|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.java2d.marlin; |
|
|
|
import sun.java2d.pipe.AATileGenerator; |
|
import sun.misc.Unsafe; |
|
|
|
final class MarlinTileGenerator implements AATileGenerator, MarlinConst { |
|
|
|
private static final int MAX_TILE_ALPHA_SUM = TILE_SIZE * TILE_SIZE |
|
* MAX_AA_ALPHA; |
|
|
|
private final Renderer rdr; |
|
private final MarlinCache cache; |
|
private int x, y; |
|
|
|
MarlinTileGenerator(Renderer r) { |
|
this.rdr = r; |
|
this.cache = r.cache; |
|
} |
|
|
|
MarlinTileGenerator init() { |
|
this.x = cache.bboxX0; |
|
this.y = cache.bboxY0; |
|
|
|
return this; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public void dispose() { |
|
if (doMonitors) { |
|
|
|
RendererContext.stats.mon_pipe_renderTiles.stop(); |
|
} |
|
|
|
cache.dispose(); |
|
|
|
rdr.dispose(); |
|
|
|
MarlinRenderingEngine.returnRendererContext(rdr.rdrCtx); |
|
} |
|
|
|
void getBbox(int bbox[]) { |
|
bbox[0] = cache.bboxX0; |
|
bbox[1] = cache.bboxY0; |
|
bbox[2] = cache.bboxX1; |
|
bbox[3] = cache.bboxY1; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public int getTileWidth() { |
|
if (doMonitors) { |
|
|
|
RendererContext.stats.mon_pipe_renderTiles.start(); |
|
} |
|
return TILE_SIZE; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public int getTileHeight() { |
|
return TILE_SIZE; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public int getTypicalAlpha() { |
|
int al = cache.alphaSumInTile(x); |
|
// Note: if we have a filled rectangle that doesn't end on a tile |
|
// border, we could still return 0xff, even though al!=maxTileAlphaSum |
|
// This is because if we return 0xff, our users will fill a rectangle |
|
// starting at x,y that has width = Math.min(TILE_SIZE, bboxX1-x), |
|
// and height min(TILE_SIZE,bboxY1-y), which is what should happen. |
|
// However, to support this, we would have to use 2 Math.min's |
|
// and 2 multiplications per tile, instead of just 2 multiplications |
|
// to compute maxTileAlphaSum. The savings offered would probably |
|
// not be worth it, considering how rare this case is. |
|
// Note: I have not tested this, so in the future if it is determined |
|
// that it is worth it, it should be implemented. Perhaps this method's |
|
// interface should be changed to take arguments the width and height |
|
// of the current tile. This would eliminate the 2 Math.min calls that |
|
// would be needed here, since our caller needs to compute these 2 |
|
|
|
final int alpha = (al == 0x00 ? 0x00 |
|
: (al == MAX_TILE_ALPHA_SUM ? 0xff : 0x80)); |
|
if (doStats) { |
|
RendererContext.stats.hist_tile_generator_alpha.add(alpha); |
|
} |
|
return alpha; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public void nextTile() { |
|
if ((x += TILE_SIZE) >= cache.bboxX1) { |
|
x = cache.bboxX0; |
|
y += TILE_SIZE; |
|
|
|
if (y < cache.bboxY1) { |
|
// compute for the tile line |
|
|
|
this.rdr.endRendering(y); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public void getAlpha(final byte tile[], final int offset, |
|
final int rowstride) |
|
{ |
|
if (cache.useRLE) { |
|
getAlphaRLE(tile, offset, rowstride); |
|
} else { |
|
getAlphaNoRLE(tile, offset, rowstride); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void getAlphaNoRLE(final byte tile[], final int offset, |
|
final int rowstride) |
|
{ |
|
if (doMonitors) { |
|
RendererContext.stats.mon_ptg_getAlpha.start(); |
|
} |
|
|
|
|
|
final MarlinCache _cache = this.cache; |
|
final long[] rowAAChunkIndex = _cache.rowAAChunkIndex; |
|
final int[] rowAAx0 = _cache.rowAAx0; |
|
final int[] rowAAx1 = _cache.rowAAx1; |
|
|
|
final int x0 = this.x; |
|
final int x1 = FloatMath.min(x0 + TILE_SIZE, _cache.bboxX1); |
|
|
|
|
|
final int y0 = 0; |
|
final int y1 = FloatMath.min(this.y + TILE_SIZE, _cache.bboxY1) - this.y; |
|
|
|
if (doLogBounds) { |
|
MarlinUtils.logInfo("getAlpha = [" + x0 + " ... " + x1 |
|
+ "[ [" + y0 + " ... " + y1 + "["); |
|
} |
|
|
|
final Unsafe _unsafe = OffHeapArray.unsafe; |
|
final long SIZE = 1L; |
|
final long addr_rowAA = _cache.rowAAChunk.address; |
|
long addr; |
|
|
|
final int skipRowPixels = (rowstride - (x1 - x0)); |
|
|
|
int aax0, aax1, end; |
|
int idx = offset; |
|
|
|
for (int cy = y0, cx; cy < y1; cy++) { |
|
|
|
cx = x0; |
|
|
|
aax1 = rowAAx1[cy]; |
|
|
|
// quick check if there is AA data |
|
|
|
if (aax1 > x0) { |
|
aax0 = rowAAx0[cy]; |
|
|
|
if (aax0 < x1) { |
|
// note: cx is the cursor pointer in the tile array |
|
|
|
cx = aax0; |
|
|
|
|
|
if (cx <= x0) { |
|
cx = x0; |
|
} else { |
|
|
|
for (end = x0; end < cx; end++) { |
|
tile[idx++] = 0; |
|
} |
|
} |
|
|
|
// now: cx >= x0 but cx < aax0 (x1 < aax0) |
|
|
|
|
|
addr = addr_rowAA + rowAAChunkIndex[cy] + (cx - aax0); |
|
|
|
for (end = (aax1 <= x1) ? aax1 : x1; cx < end; cx++) { |
|
// cx inside tile[x0; x1[ : |
|
tile[idx++] = _unsafe.getByte(addr); |
|
addr += SIZE; |
|
} |
|
} |
|
} |
|
|
|
|
|
while (cx < x1) { |
|
tile[idx++] = 0; |
|
cx++; |
|
} |
|
|
|
if (doTrace) { |
|
for (int i = idx - (x1 - x0); i < idx; i++) { |
|
System.out.print(hex(tile[i], 2)); |
|
} |
|
System.out.println(); |
|
} |
|
|
|
idx += skipRowPixels; |
|
} |
|
|
|
nextTile(); |
|
|
|
if (doMonitors) { |
|
RendererContext.stats.mon_ptg_getAlpha.stop(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void getAlphaRLE(final byte tile[], final int offset, |
|
final int rowstride) |
|
{ |
|
if (doMonitors) { |
|
RendererContext.stats.mon_ptg_getAlpha.start(); |
|
} |
|
|
|
// Decode run-length encoded alpha mask data |
|
// The data for row j begins at cache.rowOffsetsRLE[j] |
|
// and is encoded as a set of 2-byte pairs (val, runLen) |
|
// terminated by a (0, 0) pair. |
|
|
|
|
|
final MarlinCache _cache = this.cache; |
|
final long[] rowAAChunkIndex = _cache.rowAAChunkIndex; |
|
final int[] rowAAx0 = _cache.rowAAx0; |
|
final int[] rowAAx1 = _cache.rowAAx1; |
|
final int[] rowAAEnc = _cache.rowAAEnc; |
|
final long[] rowAALen = _cache.rowAALen; |
|
final long[] rowAAPos = _cache.rowAAPos; |
|
|
|
final int x0 = this.x; |
|
final int x1 = FloatMath.min(x0 + TILE_SIZE, _cache.bboxX1); |
|
|
|
|
|
final int y0 = 0; |
|
final int y1 = FloatMath.min(this.y + TILE_SIZE, _cache.bboxY1) - this.y; |
|
|
|
if (doLogBounds) { |
|
MarlinUtils.logInfo("getAlpha = [" + x0 + " ... " + x1 |
|
+ "[ [" + y0 + " ... " + y1 + "["); |
|
} |
|
|
|
final Unsafe _unsafe = OffHeapArray.unsafe; |
|
final long SIZE_BYTE = 1L; |
|
final long SIZE_INT = 4L; |
|
final long addr_rowAA = _cache.rowAAChunk.address; |
|
long addr, addr_row, last_addr, addr_end; |
|
|
|
final int skipRowPixels = (rowstride - (x1 - x0)); |
|
|
|
int cx, cy, cx1; |
|
int rx0, rx1, runLen, end; |
|
int packed; |
|
byte val; |
|
int idx = offset; |
|
|
|
for (cy = y0; cy < y1; cy++) { |
|
|
|
cx = x0; |
|
|
|
if (rowAAEnc[cy] == 0) { |
|
// Raw encoding: |
|
|
|
final int aax1 = rowAAx1[cy]; |
|
|
|
// quick check if there is AA data |
|
|
|
if (aax1 > x0) { |
|
final int aax0 = rowAAx0[cy]; |
|
|
|
if (aax0 < x1) { |
|
// note: cx is the cursor pointer in the tile array |
|
|
|
cx = aax0; |
|
|
|
|
|
if (cx <= x0) { |
|
cx = x0; |
|
} else { |
|
|
|
for (end = x0; end < cx; end++) { |
|
tile[idx++] = 0; |
|
} |
|
} |
|
|
|
// now: cx >= x0 but cx < aax0 (x1 < aax0) |
|
|
|
|
|
addr = addr_rowAA + rowAAChunkIndex[cy] + (cx - aax0); |
|
|
|
for (end = (aax1 <= x1) ? aax1 : x1; cx < end; cx++) { |
|
tile[idx++] = _unsafe.getByte(addr); |
|
addr += SIZE_BYTE; |
|
} |
|
} |
|
} |
|
} else { |
|
// RLE encoding: |
|
|
|
// quick check if there is AA data |
|
|
|
if (rowAAx1[cy] > x0) { // last pixel exclusive |
|
|
|
cx = rowAAx0[cy]; |
|
if (cx > x1) { |
|
cx = x1; |
|
} |
|
|
|
|
|
for (int i = x0; i < cx; i++) { |
|
tile[idx++] = 0; |
|
} |
|
|
|
|
|
addr_row = addr_rowAA + rowAAChunkIndex[cy]; |
|
// get row end address: |
|
addr_end = addr_row + rowAALen[cy]; |
|
|
|
|
|
addr = addr_row + rowAAPos[cy]; |
|
|
|
last_addr = 0L; |
|
|
|
while ((cx < x1) && (addr < addr_end)) { |
|
|
|
last_addr = addr; |
|
|
|
|
|
packed = _unsafe.getInt(addr); |
|
|
|
|
|
cx1 = (packed >> 8); |
|
|
|
addr += SIZE_INT; |
|
|
|
rx0 = cx; |
|
if (rx0 < x0) { |
|
rx0 = x0; |
|
} |
|
rx1 = cx = cx1; |
|
if (rx1 > x1) { |
|
rx1 = x1; |
|
cx = x1; |
|
} |
|
|
|
runLen = rx1 - rx0; |
|
|
|
|
|
if (runLen > 0) { |
|
val = (byte)(packed & 0xFF); |
|
|
|
do { |
|
tile[idx++] = val; |
|
} while (--runLen > 0); |
|
} |
|
} |
|
|
|
|
|
if (last_addr != 0L) { |
|
// Fix x0: |
|
rowAAx0[cy] = cx; |
|
|
|
rowAAPos[cy] = (last_addr - addr_row); |
|
} |
|
} |
|
} |
|
|
|
|
|
while (cx < x1) { |
|
tile[idx++] = 0; |
|
cx++; |
|
} |
|
|
|
if (doTrace) { |
|
for (int i = idx - (x1 - x0); i < idx; i++) { |
|
System.out.print(hex(tile[i], 2)); |
|
} |
|
System.out.println(); |
|
} |
|
|
|
idx += skipRowPixels; |
|
} |
|
|
|
nextTile(); |
|
|
|
if (doMonitors) { |
|
RendererContext.stats.mon_ptg_getAlpha.stop(); |
|
} |
|
} |
|
|
|
static String hex(int v, int d) { |
|
String s = Integer.toHexString(v); |
|
while (s.length() < d) { |
|
s = "0" + s; |
|
} |
|
return s.substring(0, d); |
|
} |
|
} |