Path: blob/master/src/java.desktop/share/classes/sun/java2d/marlin/MarlinTileGenerator.java
41159 views
/*1* Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.java2d.marlin;2627import java.util.Arrays;28import sun.java2d.pipe.AATileGenerator;29import jdk.internal.misc.Unsafe;3031final class MarlinTileGenerator implements AATileGenerator, MarlinConst {3233private static final boolean DISABLE_BLEND = false;3435private static final int MAX_TILE_ALPHA_SUM = TILE_W * TILE_H * MAX_AA_ALPHA;3637private static final int TH_AA_ALPHA_FILL_EMPTY = ((MAX_AA_ALPHA + 1) / 3); // 33%38private static final int TH_AA_ALPHA_FILL_FULL = ((MAX_AA_ALPHA + 1) * 2 / 3); // 66%3940private static final int FILL_TILE_W = TILE_W >> 1; // half tile width4142static {43if (MAX_TILE_ALPHA_SUM <= 0) {44throw new IllegalStateException("Invalid MAX_TILE_ALPHA_SUM: " + MAX_TILE_ALPHA_SUM);45}46if (DO_TRACE) {47MarlinUtils.logInfo("MAX_AA_ALPHA : " + MAX_AA_ALPHA);48MarlinUtils.logInfo("TH_AA_ALPHA_FILL_EMPTY : " + TH_AA_ALPHA_FILL_EMPTY);49MarlinUtils.logInfo("TH_AA_ALPHA_FILL_FULL : " + TH_AA_ALPHA_FILL_FULL);50MarlinUtils.logInfo("FILL_TILE_W : " + FILL_TILE_W);51}52}5354private final Renderer renderer;55private final MarlinCache cache;56private int x, y;5758// per-thread renderer stats59final RendererStats rdrStats;6061MarlinTileGenerator(final RendererStats stats, final Renderer r,62final MarlinCache cache)63{64this.rdrStats = stats;65this.renderer = r;66this.cache = cache;67}6869MarlinTileGenerator init() {70this.x = cache.bboxX0;71this.y = cache.bboxY0;7273return this; // fluent API74}7576/**77* Disposes this tile generator:78* clean up before reusing this instance79*/80@Override81public void dispose() {82if (DO_MONITORS) {83// called from AAShapePipe.renderTiles() (render tiles end):84rdrStats.mon_pipe_renderTiles.stop();85}86// dispose cache:87cache.dispose();88// dispose renderer and recycle the RendererContext instance:89renderer.dispose();90}9192void getBbox(int[] bbox) {93bbox[0] = cache.bboxX0;94bbox[1] = cache.bboxY0;95bbox[2] = cache.bboxX1;96bbox[3] = cache.bboxY1;97}9899/**100* Gets the width of the tiles that the generator batches output into.101* @return the width of the standard alpha tile102*/103@Override104public int getTileWidth() {105if (DO_MONITORS) {106// called from AAShapePipe.renderTiles() (render tiles start):107rdrStats.mon_pipe_renderTiles.start();108}109return TILE_W;110}111112/**113* Gets the height of the tiles that the generator batches output into.114* @return the height of the standard alpha tile115*/116@Override117public int getTileHeight() {118return TILE_H;119}120121/**122* Gets the typical alpha value that will characterize the current123* tile.124* The answer may be 0x00 to indicate that the current tile has125* no coverage in any of its pixels, or it may be 0xff to indicate126* that the current tile is completely covered by the path, or any127* other value to indicate non-trivial coverage cases.128* @return 0x00 for no coverage, 0xff for total coverage, or any other129* value for partial coverage of the tile130*/131@Override132public int getTypicalAlpha() {133if (DISABLE_BLEND) {134// always return empty tiles to disable blending operations135return 0x00;136}137int al = cache.alphaSumInTile(x);138// Note: if we have a filled rectangle that doesn't end on a tile139// border, we could still return 0xff, even though al!=maxTileAlphaSum140// This is because if we return 0xff, our users will fill a rectangle141// starting at x,y that has width = Math.min(TILE_SIZE, bboxX1-x),142// and height min(TILE_SIZE,bboxY1-y), which is what should happen.143// However, to support this, we would have to use 2 Math.min's144// and 2 multiplications per tile, instead of just 2 multiplications145// to compute maxTileAlphaSum. The savings offered would probably146// not be worth it, considering how rare this case is.147// Note: I have not tested this, so in the future if it is determined148// that it is worth it, it should be implemented. Perhaps this method's149// interface should be changed to take arguments the width and height150// of the current tile. This would eliminate the 2 Math.min calls that151// would be needed here, since our caller needs to compute these 2152// values anyway.153final int alpha = (al == 0x00 ? 0x00154: (al == MAX_TILE_ALPHA_SUM ? 0xff : 0x80));155if (DO_STATS) {156rdrStats.hist_tile_generator_alpha.add(alpha);157}158return alpha;159}160161/**162* Skips the current tile and moves on to the next tile.163* Either this method, or the getAlpha() method should be called164* once per tile, but not both.165*/166@Override167public void nextTile() {168if ((x += TILE_W) >= cache.bboxX1) {169x = cache.bboxX0;170y += TILE_H;171172if (y < cache.bboxY1) {173// compute for the tile line174// [ y; max(y + TILE_SIZE, bboxY1) ]175renderer.endRendering(y);176}177}178}179180/**181* Gets the alpha coverage values for the current tile.182* Either this method, or the nextTile() method should be called183* once per tile, but not both.184*/185@Override186public void getAlpha(final byte[] tile, final int offset,187final int rowstride)188{189if (cache.useRLE) {190getAlphaRLE(tile, offset, rowstride);191} else {192getAlphaNoRLE(tile, offset, rowstride);193}194}195196/**197* Gets the alpha coverage values for the current tile.198* Either this method, or the nextTile() method should be called199* once per tile, but not both.200*/201private void getAlphaNoRLE(final byte[] tile, final int offset,202final int rowstride)203{204if (DO_MONITORS) {205rdrStats.mon_ptg_getAlpha.start();206}207208// local vars for performance:209final MarlinCache _cache = this.cache;210final long[] rowAAChunkIndex = _cache.rowAAChunkIndex;211final int[] rowAAx0 = _cache.rowAAx0;212final int[] rowAAx1 = _cache.rowAAx1;213214final int x0 = this.x;215final int x1 = FloatMath.min(x0 + TILE_W, _cache.bboxX1);216217// note: process tile line [0 - 32[218final int y0 = 0;219final int y1 = FloatMath.min(this.y + TILE_H, _cache.bboxY1) - this.y;220221if (DO_LOG_BOUNDS) {222MarlinUtils.logInfo("getAlpha = [" + x0 + " ... " + x1223+ "[ [" + y0 + " ... " + y1 + "[");224}225226final Unsafe _unsafe = OffHeapArray.UNSAFE;227final long SIZE = 1L;228final long addr_rowAA = _cache.rowAAChunk.address;229long addr;230231final int skipRowPixels = (rowstride - (x1 - x0));232233int aax0, aax1, end;234int idx = offset;235236for (int cy = y0, cx; cy < y1; cy++) {237// empty line (default)238cx = x0;239240aax1 = rowAAx1[cy]; // exclusive241242// quick check if there is AA data243// corresponding to this tile [x0; x1[244if (aax1 > x0) {245aax0 = rowAAx0[cy]; // inclusive246247if (aax0 < x1) {248// note: cx is the cursor pointer in the tile array249// (left to right)250cx = aax0;251252// ensure cx >= x0253if (cx <= x0) {254cx = x0;255} else {256// fill line start until first AA pixel rowAA exclusive:257for (end = x0; end < cx; end++) {258tile[idx++] = 0;259}260}261262// now: cx >= x0 and cx >= aax0263264// Copy AA data (sum alpha data):265addr = addr_rowAA + rowAAChunkIndex[cy] + (cx - aax0);266267for (end = (aax1 <= x1) ? aax1 : x1; cx < end; cx++) {268// cx inside tile[x0; x1[ :269tile[idx++] = _unsafe.getByte(addr); // [0-255]270addr += SIZE;271}272}273}274275// fill line end276while (cx < x1) {277tile[idx++] = 0;278cx++;279}280281if (DO_TRACE) {282for (int i = idx - (x1 - x0); i < idx; i++) {283System.out.print(hex(tile[i], 2));284}285System.out.println();286}287288idx += skipRowPixels;289}290291nextTile();292293if (DO_MONITORS) {294rdrStats.mon_ptg_getAlpha.stop();295}296}297298/**299* Gets the alpha coverage values for the current tile.300* Either this method, or the nextTile() method should be called301* once per tile, but not both.302*/303private void getAlphaRLE(final byte[] tile, final int offset,304final int rowstride)305{306if (DO_MONITORS) {307rdrStats.mon_ptg_getAlpha.start();308}309310// Decode run-length encoded alpha mask data311// The data for row j begins at cache.rowOffsetsRLE[j]312// and is encoded as a set of 2-byte pairs (val, runLen)313// terminated by a (0, 0) pair.314315// local vars for performance:316final MarlinCache _cache = this.cache;317final long[] rowAAChunkIndex = _cache.rowAAChunkIndex;318final int[] rowAAx0 = _cache.rowAAx0;319final int[] rowAAx1 = _cache.rowAAx1;320final int[] rowAAEnc = _cache.rowAAEnc;321final long[] rowAALen = _cache.rowAALen;322final long[] rowAAPos = _cache.rowAAPos;323324final int x0 = this.x;325final int x1 = FloatMath.min(x0 + TILE_W, _cache.bboxX1);326final int w = x1 - x0;327328// note: process tile line [0 - 32[329final int y0 = 0;330final int y1 = FloatMath.min(this.y + TILE_H, _cache.bboxY1) - this.y;331332if (DO_LOG_BOUNDS) {333MarlinUtils.logInfo("getAlpha = [" + x0 + " ... " + x1334+ "[ [" + y0 + " ... " + y1 + "[");335}336337// avoid too small area: fill is not faster !338final int clearTile;339final byte refVal;340final int area;341342if ((w >= FILL_TILE_W) && (area = w * y1) > 64) { // 64 / 4 ie 16 words min (faster)343final int alphaSum = cache.alphaSumInTile(x0);344345if (alphaSum < area * TH_AA_ALPHA_FILL_EMPTY) {346clearTile = 1;347refVal = 0;348} else if (alphaSum > area * TH_AA_ALPHA_FILL_FULL) {349clearTile = 2;350refVal = (byte)0xff;351} else {352clearTile = 0;353refVal = 0;354}355} else {356clearTile = 0;357refVal = 0;358}359360final Unsafe _unsafe = OffHeapArray.UNSAFE;361final long SIZE_BYTE = 1L;362final long SIZE_INT = 4L;363final long addr_rowAA = _cache.rowAAChunk.address;364long addr, addr_row, last_addr, addr_end;365366final int skipRowPixels = (rowstride - w);367368int cx, cy, cx1;369int rx0, rx1, runLen, end;370int packed;371byte val;372int idx = offset;373374switch (clearTile) {375case 1: // 0x00376// Clear full tile rows:377Arrays.fill(tile, offset, offset + (y1 * rowstride), refVal);378379for (cy = y0; cy < y1; cy++) {380// empty line (default)381cx = x0;382383if (rowAAEnc[cy] == 0) {384// Raw encoding:385386final int aax1 = rowAAx1[cy]; // exclusive387388// quick check if there is AA data389// corresponding to this tile [x0; x1[390if (aax1 > x0) {391final int aax0 = rowAAx0[cy]; // inclusive392393if (aax0 < x1) {394// note: cx is the cursor pointer in the tile array395// (left to right)396cx = aax0;397398// ensure cx >= x0399if (cx <= x0) {400cx = x0;401} else {402// skip line start until first AA pixel rowAA exclusive:403idx += (cx - x0); // > 0404}405406// now: cx >= x0 and cx >= aax0407408// Copy AA data (sum alpha data):409addr = addr_rowAA + rowAAChunkIndex[cy] + (cx - aax0);410411for (end = (aax1 <= x1) ? aax1 : x1; cx < end; cx++) {412tile[idx++] = _unsafe.getByte(addr); // [0-255]413addr += SIZE_BYTE;414}415}416}417} else {418// RLE encoding:419420// quick check if there is AA data421// corresponding to this tile [x0; x1[422if (rowAAx1[cy] > x0) { // last pixel exclusive423424cx = rowAAx0[cy]; // inclusive425if (cx > x1) {426cx = x1;427}428429// skip line start until first AA pixel rowAA exclusive:430if (cx > x0) {431idx += (cx - x0); // > 0432}433434// get row address:435addr_row = addr_rowAA + rowAAChunkIndex[cy];436// get row end address:437addr_end = addr_row + rowAALen[cy]; // coded length438439// reuse previous iteration position:440addr = addr_row + rowAAPos[cy];441442last_addr = 0L;443444while ((cx < x1) && (addr < addr_end)) {445// keep current position:446last_addr = addr;447448// packed value:449packed = _unsafe.getInt(addr);450451// last exclusive pixel x-coordinate:452cx1 = (packed >> 8);453// as bytes:454addr += SIZE_INT;455456rx0 = cx;457if (rx0 < x0) {458rx0 = x0;459}460rx1 = cx = cx1;461if (rx1 > x1) {462rx1 = x1;463cx = x1; // fix last x464}465// adjust runLen:466runLen = rx1 - rx0;467468// ensure rx1 > rx0:469if (runLen > 0) {470packed &= 0xFF; // [0-255]471472if (packed == 0)473{474idx += runLen;475continue;476}477val = (byte) packed; // [0-255]478do {479tile[idx++] = val;480} while (--runLen > 0);481}482}483484// Update last position in RLE entries:485if (last_addr != 0L) {486// Fix x0:487rowAAx0[cy] = cx; // inclusive488// Fix position:489rowAAPos[cy] = (last_addr - addr_row);490}491}492}493494// skip line end495if (cx < x1) {496idx += (x1 - cx); // > 0497}498499if (DO_TRACE) {500for (int i = idx - (x1 - x0); i < idx; i++) {501System.out.print(hex(tile[i], 2));502}503System.out.println();504}505506idx += skipRowPixels;507}508break;509510case 0:511default:512for (cy = y0; cy < y1; cy++) {513// empty line (default)514cx = x0;515516if (rowAAEnc[cy] == 0) {517// Raw encoding:518519final int aax1 = rowAAx1[cy]; // exclusive520521// quick check if there is AA data522// corresponding to this tile [x0; x1[523if (aax1 > x0) {524final int aax0 = rowAAx0[cy]; // inclusive525526if (aax0 < x1) {527// note: cx is the cursor pointer in the tile array528// (left to right)529cx = aax0;530531// ensure cx >= x0532if (cx <= x0) {533cx = x0;534} else {535for (end = x0; end < cx; end++) {536tile[idx++] = 0;537}538}539540// now: cx >= x0 and cx >= aax0541542// Copy AA data (sum alpha data):543addr = addr_rowAA + rowAAChunkIndex[cy] + (cx - aax0);544545for (end = (aax1 <= x1) ? aax1 : x1; cx < end; cx++) {546tile[idx++] = _unsafe.getByte(addr); // [0-255]547addr += SIZE_BYTE;548}549}550}551} else {552// RLE encoding:553554// quick check if there is AA data555// corresponding to this tile [x0; x1[556if (rowAAx1[cy] > x0) { // last pixel exclusive557558cx = rowAAx0[cy]; // inclusive559if (cx > x1) {560cx = x1;561}562563// fill line start until first AA pixel rowAA exclusive:564for (end = x0; end < cx; end++) {565tile[idx++] = 0;566}567568// get row address:569addr_row = addr_rowAA + rowAAChunkIndex[cy];570// get row end address:571addr_end = addr_row + rowAALen[cy]; // coded length572573// reuse previous iteration position:574addr = addr_row + rowAAPos[cy];575576last_addr = 0L;577578while ((cx < x1) && (addr < addr_end)) {579// keep current position:580last_addr = addr;581582// packed value:583packed = _unsafe.getInt(addr);584585// last exclusive pixel x-coordinate:586cx1 = (packed >> 8);587// as bytes:588addr += SIZE_INT;589590rx0 = cx;591if (rx0 < x0) {592rx0 = x0;593}594rx1 = cx = cx1;595if (rx1 > x1) {596rx1 = x1;597cx = x1; // fix last x598}599// adjust runLen:600runLen = rx1 - rx0;601602// ensure rx1 > rx0:603if (runLen > 0) {604packed &= 0xFF; // [0-255]605606val = (byte) packed; // [0-255]607do {608tile[idx++] = val;609} while (--runLen > 0);610}611}612613// Update last position in RLE entries:614if (last_addr != 0L) {615// Fix x0:616rowAAx0[cy] = cx; // inclusive617// Fix position:618rowAAPos[cy] = (last_addr - addr_row);619}620}621}622623// fill line end624while (cx < x1) {625tile[idx++] = 0;626cx++;627}628629if (DO_TRACE) {630for (int i = idx - (x1 - x0); i < idx; i++) {631System.out.print(hex(tile[i], 2));632}633System.out.println();634}635636idx += skipRowPixels;637}638break;639640case 2: // 0xFF641// Fill full tile rows:642Arrays.fill(tile, offset, offset + (y1 * rowstride), refVal);643644for (cy = y0; cy < y1; cy++) {645// empty line (default)646cx = x0;647648if (rowAAEnc[cy] == 0) {649// Raw encoding:650651final int aax1 = rowAAx1[cy]; // exclusive652653// quick check if there is AA data654// corresponding to this tile [x0; x1[655if (aax1 > x0) {656final int aax0 = rowAAx0[cy]; // inclusive657658if (aax0 < x1) {659// note: cx is the cursor pointer in the tile array660// (left to right)661cx = aax0;662663// ensure cx >= x0664if (cx <= x0) {665cx = x0;666} else {667// fill line start until first AA pixel rowAA exclusive:668for (end = x0; end < cx; end++) {669tile[idx++] = 0;670}671}672673// now: cx >= x0 and cx >= aax0674675// Copy AA data (sum alpha data):676addr = addr_rowAA + rowAAChunkIndex[cy] + (cx - aax0);677678for (end = (aax1 <= x1) ? aax1 : x1; cx < end; cx++) {679tile[idx++] = _unsafe.getByte(addr); // [0-255]680addr += SIZE_BYTE;681}682}683}684} else {685// RLE encoding:686687// quick check if there is AA data688// corresponding to this tile [x0; x1[689if (rowAAx1[cy] > x0) { // last pixel exclusive690691cx = rowAAx0[cy]; // inclusive692if (cx > x1) {693cx = x1;694}695696// fill line start until first AA pixel rowAA exclusive:697for (end = x0; end < cx; end++) {698tile[idx++] = 0;699}700701// get row address:702addr_row = addr_rowAA + rowAAChunkIndex[cy];703// get row end address:704addr_end = addr_row + rowAALen[cy]; // coded length705706// reuse previous iteration position:707addr = addr_row + rowAAPos[cy];708709last_addr = 0L;710711while ((cx < x1) && (addr < addr_end)) {712// keep current position:713last_addr = addr;714715// packed value:716packed = _unsafe.getInt(addr);717718// last exclusive pixel x-coordinate:719cx1 = (packed >> 8);720// as bytes:721addr += SIZE_INT;722723rx0 = cx;724if (rx0 < x0) {725rx0 = x0;726}727rx1 = cx = cx1;728if (rx1 > x1) {729rx1 = x1;730cx = x1; // fix last x731}732// adjust runLen:733runLen = rx1 - rx0;734735// ensure rx1 > rx0:736if (runLen > 0) {737packed &= 0xFF; // [0-255]738739if (packed == 0xFF)740{741idx += runLen;742continue;743}744val = (byte) packed; // [0-255]745do {746tile[idx++] = val;747} while (--runLen > 0);748}749}750751// Update last position in RLE entries:752if (last_addr != 0L) {753// Fix x0:754rowAAx0[cy] = cx; // inclusive755// Fix position:756rowAAPos[cy] = (last_addr - addr_row);757}758}759}760761// fill line end762while (cx < x1) {763tile[idx++] = 0;764cx++;765}766767if (DO_TRACE) {768for (int i = idx - (x1 - x0); i < idx; i++) {769System.out.print(hex(tile[i], 2));770}771System.out.println();772}773774idx += skipRowPixels;775}776}777778nextTile();779780if (DO_MONITORS) {781rdrStats.mon_ptg_getAlpha.stop();782}783}784785static String hex(int v, int d) {786String s = Integer.toHexString(v);787while (s.length() < d) {788s = "0" + s;789}790return s.substring(0, d);791}792}793794795