mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-31 13:38:47 +00:00
Clipping implemented in Dasher (curve subdivision at clip edges) + higher quality(curve, subpixels) + new path simplifier Reviewed-by: prr, serb
637 lines
22 KiB
Java
637 lines
22 KiB
Java
/*
|
|
* Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
package sun.java2d.marlin;
|
|
|
|
import jdk.internal.misc.Unsafe;
|
|
|
|
/**
|
|
* An object used to cache pre-rendered complex paths.
|
|
*
|
|
* @see Renderer
|
|
*/
|
|
public final class MarlinCache implements MarlinConst {
|
|
|
|
static final boolean FORCE_RLE = MarlinProperties.isForceRLE();
|
|
static final boolean FORCE_NO_RLE = MarlinProperties.isForceNoRLE();
|
|
// minimum width to try using RLE encoding:
|
|
static final int RLE_MIN_WIDTH
|
|
= Math.max(BLOCK_SIZE, MarlinProperties.getRLEMinWidth());
|
|
// maximum width for RLE encoding:
|
|
// values are stored as int [x|alpha] where alpha is 8 bits
|
|
static final int RLE_MAX_WIDTH = 1 << (24 - 1);
|
|
|
|
// 4096 (pixels) alpha values (width) x 64 rows / 4 (tile) = 64K bytes
|
|
// x1 instead of 4 bytes (RLE) ie 1/4 capacity or average good RLE compression
|
|
static final long INITIAL_CHUNK_ARRAY = TILE_H * INITIAL_PIXEL_WIDTH >> 2; // 64K
|
|
|
|
// The alpha map used by this object (taken out of our map cache) to convert
|
|
// pixel coverage counts gotten from MarlinCache (which are in the range
|
|
// [0, maxalpha]) into alpha values, which are in [0,256).
|
|
static final byte[] ALPHA_MAP;
|
|
|
|
static final OffHeapArray ALPHA_MAP_UNSAFE;
|
|
|
|
static {
|
|
final byte[] _ALPHA_MAP = buildAlphaMap(MAX_AA_ALPHA);
|
|
|
|
ALPHA_MAP_UNSAFE = new OffHeapArray(_ALPHA_MAP, _ALPHA_MAP.length); // 1K
|
|
ALPHA_MAP =_ALPHA_MAP;
|
|
|
|
final Unsafe _unsafe = OffHeapArray.UNSAFE;
|
|
final long addr = ALPHA_MAP_UNSAFE.address;
|
|
|
|
for (int i = 0; i < _ALPHA_MAP.length; i++) {
|
|
_unsafe.putByte(addr + i, _ALPHA_MAP[i]);
|
|
}
|
|
}
|
|
|
|
int bboxX0, bboxY0, bboxX1, bboxY1;
|
|
|
|
// 1D dirty arrays
|
|
// row index in rowAAChunk[]
|
|
final long[] rowAAChunkIndex = new long[TILE_H];
|
|
// first pixel (inclusive) for each row
|
|
final int[] rowAAx0 = new int[TILE_H];
|
|
// last pixel (exclusive) for each row
|
|
final int[] rowAAx1 = new int[TILE_H];
|
|
// encoding mode (0=raw, 1=RLE encoding) for each row
|
|
final int[] rowAAEnc = new int[TILE_H];
|
|
// coded length (RLE encoding) for each row
|
|
final long[] rowAALen = new long[TILE_H];
|
|
// last position in RLE decoding for each row (getAlpha):
|
|
final long[] rowAAPos = new long[TILE_H];
|
|
|
|
// dirty off-heap array containing pixel coverages for (32) rows (packed)
|
|
// if encoding=raw, it contains alpha coverage values (val) as integer
|
|
// if encoding=RLE, it contains tuples (val, last x-coordinate exclusive)
|
|
// use rowAAx0/rowAAx1 to get row indices within this chunk
|
|
final OffHeapArray rowAAChunk;
|
|
|
|
// current position in rowAAChunk array
|
|
long rowAAChunkPos;
|
|
|
|
// touchedTile[i] is the sum of all the alphas in the tile with
|
|
// x=j*TILE_SIZE+bboxX0.
|
|
int[] touchedTile;
|
|
|
|
// per-thread renderer stats
|
|
final RendererStats rdrStats;
|
|
|
|
// touchedTile ref (clean)
|
|
private final IntArrayCache.Reference touchedTile_ref;
|
|
|
|
int tileMin, tileMax;
|
|
|
|
boolean useRLE = false;
|
|
|
|
MarlinCache(final IRendererContext rdrCtx) {
|
|
this.rdrStats = rdrCtx.stats();
|
|
|
|
rowAAChunk = rdrCtx.newOffHeapArray(INITIAL_CHUNK_ARRAY); // 64K
|
|
|
|
touchedTile_ref = rdrCtx.newCleanIntArrayRef(INITIAL_ARRAY); // 1K = 1 tile line
|
|
touchedTile = touchedTile_ref.initial;
|
|
|
|
// tile used marks:
|
|
tileMin = Integer.MAX_VALUE;
|
|
tileMax = Integer.MIN_VALUE;
|
|
}
|
|
|
|
void init(int minx, int miny, int maxx, int maxy)
|
|
{
|
|
// assert maxy >= miny && maxx >= minx;
|
|
bboxX0 = minx;
|
|
bboxY0 = miny;
|
|
bboxX1 = maxx;
|
|
bboxY1 = maxy;
|
|
|
|
final int width = (maxx - minx);
|
|
|
|
if (FORCE_NO_RLE) {
|
|
useRLE = false;
|
|
} else if (FORCE_RLE) {
|
|
useRLE = true;
|
|
} else {
|
|
// heuristics: use both bbox area and complexity
|
|
// ie number of primitives:
|
|
|
|
// fast check min and max width (maxx < 23bits):
|
|
useRLE = (width > RLE_MIN_WIDTH && width < RLE_MAX_WIDTH);
|
|
}
|
|
|
|
// the ceiling of (maxy - miny + 1) / TILE_SIZE;
|
|
final int nxTiles = (width + TILE_W) >> TILE_W_LG;
|
|
|
|
if (nxTiles > INITIAL_ARRAY) {
|
|
if (DO_STATS) {
|
|
rdrStats.stat_array_marlincache_touchedTile.add(nxTiles);
|
|
}
|
|
touchedTile = touchedTile_ref.getArray(nxTiles);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Disposes this cache:
|
|
* clean up before reusing this instance
|
|
*/
|
|
void dispose() {
|
|
// Reset touchedTile if needed:
|
|
resetTileLine(0);
|
|
|
|
if (DO_STATS) {
|
|
rdrStats.totalOffHeap += rowAAChunk.length;
|
|
}
|
|
|
|
// Return arrays:
|
|
touchedTile = touchedTile_ref.putArray(touchedTile, 0, 0); // already zero filled
|
|
|
|
// At last: resize back off-heap rowAA to initial size
|
|
if (rowAAChunk.length != INITIAL_CHUNK_ARRAY) {
|
|
// note: may throw OOME:
|
|
rowAAChunk.resize(INITIAL_CHUNK_ARRAY);
|
|
}
|
|
if (DO_CLEAN_DIRTY) {
|
|
// Force zero-fill dirty arrays:
|
|
rowAAChunk.fill(BYTE_0);
|
|
}
|
|
}
|
|
|
|
void resetTileLine(final int pminY) {
|
|
// update bboxY0 to process a complete tile line [0 - 32]
|
|
bboxY0 = pminY;
|
|
|
|
// reset current pos
|
|
if (DO_STATS) {
|
|
rdrStats.stat_cache_rowAAChunk.add(rowAAChunkPos);
|
|
}
|
|
rowAAChunkPos = 0L;
|
|
|
|
// Reset touchedTile:
|
|
if (tileMin != Integer.MAX_VALUE) {
|
|
if (DO_STATS) {
|
|
rdrStats.stat_cache_tiles.add(tileMax - tileMin);
|
|
}
|
|
// clean only dirty touchedTile:
|
|
if (tileMax == 1) {
|
|
touchedTile[0] = 0;
|
|
} else {
|
|
IntArrayCache.fill(touchedTile, tileMin, tileMax, 0);
|
|
}
|
|
// reset tile used marks:
|
|
tileMin = Integer.MAX_VALUE;
|
|
tileMax = Integer.MIN_VALUE;
|
|
}
|
|
|
|
if (DO_CLEAN_DIRTY) {
|
|
// Force zero-fill dirty arrays:
|
|
rowAAChunk.fill(BYTE_0);
|
|
}
|
|
}
|
|
|
|
void clearAARow(final int y) {
|
|
// process tile line [0 - 32]
|
|
final int row = y - bboxY0;
|
|
|
|
// update pixel range:
|
|
rowAAx0[row] = 0; // first pixel inclusive
|
|
rowAAx1[row] = 0; // last pixel exclusive
|
|
rowAAEnc[row] = 0; // raw encoding
|
|
|
|
// note: leave rowAAChunkIndex[row] undefined
|
|
// and rowAALen[row] & rowAAPos[row] (RLE)
|
|
}
|
|
|
|
/**
|
|
* Copy the given alpha data into the rowAA cache
|
|
* @param alphaRow alpha data to copy from
|
|
* @param y y pixel coordinate
|
|
* @param px0 first pixel inclusive x0
|
|
* @param px1 last pixel exclusive x1
|
|
*/
|
|
void copyAARowNoRLE(final int[] alphaRow, final int y,
|
|
final int px0, final int px1)
|
|
{
|
|
// skip useless pixels above boundary
|
|
final int px_bbox1 = FloatMath.min(px1, bboxX1);
|
|
|
|
if (DO_LOG_BOUNDS) {
|
|
MarlinUtils.logInfo("row = [" + px0 + " ... " + px_bbox1
|
|
+ " (" + px1 + ") [ for y=" + y);
|
|
}
|
|
|
|
final int row = y - bboxY0;
|
|
|
|
// update pixel range:
|
|
rowAAx0[row] = px0; // first pixel inclusive
|
|
rowAAx1[row] = px_bbox1; // last pixel exclusive
|
|
rowAAEnc[row] = 0; // raw encoding
|
|
|
|
// get current position (bytes):
|
|
final long pos = rowAAChunkPos;
|
|
// update row index to current position:
|
|
rowAAChunkIndex[row] = pos;
|
|
|
|
// determine need array size:
|
|
// for RLE encoding, position must be aligned to 4 bytes (int):
|
|
// align - 1 = 3 so add +3 and round-off by mask ~3 = -4
|
|
final long needSize = pos + ((px_bbox1 - px0 + 3) & -4);
|
|
|
|
// update next position (bytes):
|
|
rowAAChunkPos = needSize;
|
|
|
|
// update row data:
|
|
final OffHeapArray _rowAAChunk = rowAAChunk;
|
|
// ensure rowAAChunk capacity:
|
|
if (_rowAAChunk.length < needSize) {
|
|
expandRowAAChunk(needSize);
|
|
}
|
|
if (DO_STATS) {
|
|
rdrStats.stat_cache_rowAA.add(px_bbox1 - px0);
|
|
}
|
|
|
|
// rowAA contains only alpha values for range[x0; x1[
|
|
final int[] _touchedTile = touchedTile;
|
|
final int _TILE_SIZE_LG = TILE_W_LG;
|
|
|
|
final int from = px0 - bboxX0; // first pixel inclusive
|
|
final int to = px_bbox1 - bboxX0; // last pixel exclusive
|
|
|
|
final Unsafe _unsafe = OffHeapArray.UNSAFE;
|
|
final long SIZE_BYTE = 1L;
|
|
final long addr_alpha = ALPHA_MAP_UNSAFE.address;
|
|
long addr_off = _rowAAChunk.address + pos;
|
|
|
|
// compute alpha sum into rowAA:
|
|
for (int x = from, val = 0; x < to; x++) {
|
|
// alphaRow is in [0; MAX_COVERAGE]
|
|
val += alphaRow[x]; // [from; to[
|
|
|
|
// ensure values are in [0; MAX_AA_ALPHA] range
|
|
if (DO_AA_RANGE_CHECK) {
|
|
if (val < 0) {
|
|
MarlinUtils.logInfo("Invalid coverage = " + val);
|
|
val = 0;
|
|
}
|
|
if (val > MAX_AA_ALPHA) {
|
|
MarlinUtils.logInfo("Invalid coverage = " + val);
|
|
val = MAX_AA_ALPHA;
|
|
}
|
|
}
|
|
|
|
// store alpha sum (as byte):
|
|
if (val == 0) {
|
|
_unsafe.putByte(addr_off, (byte)0); // [0-255]
|
|
} else {
|
|
_unsafe.putByte(addr_off, _unsafe.getByte(addr_alpha + val)); // [0-255]
|
|
|
|
// update touchedTile
|
|
_touchedTile[x >> _TILE_SIZE_LG] += val;
|
|
}
|
|
addr_off += SIZE_BYTE;
|
|
}
|
|
|
|
// update tile used marks:
|
|
int tx = from >> _TILE_SIZE_LG; // inclusive
|
|
if (tx < tileMin) {
|
|
tileMin = tx;
|
|
}
|
|
|
|
tx = ((to - 1) >> _TILE_SIZE_LG) + 1; // exclusive (+1 to be sure)
|
|
if (tx > tileMax) {
|
|
tileMax = tx;
|
|
}
|
|
|
|
if (DO_LOG_BOUNDS) {
|
|
MarlinUtils.logInfo("clear = [" + from + " ... " + to + "[");
|
|
}
|
|
|
|
// Clear alpha row for reuse:
|
|
IntArrayCache.fill(alphaRow, from, px1 + 1 - bboxX0, 0);
|
|
}
|
|
|
|
void copyAARowRLE_WithBlockFlags(final int[] blkFlags, final int[] alphaRow,
|
|
final int y, final int px0, final int px1)
|
|
{
|
|
// Copy rowAA data into the piscesCache if one is present
|
|
final int _bboxX0 = bboxX0;
|
|
|
|
// process tile line [0 - 32]
|
|
final int row = y - bboxY0;
|
|
final int from = px0 - _bboxX0; // first pixel inclusive
|
|
|
|
// skip useless pixels above boundary
|
|
final int px_bbox1 = FloatMath.min(px1, bboxX1);
|
|
final int to = px_bbox1 - _bboxX0; // last pixel exclusive
|
|
|
|
if (DO_LOG_BOUNDS) {
|
|
MarlinUtils.logInfo("row = [" + px0 + " ... " + px_bbox1
|
|
+ " (" + px1 + ") [ for y=" + y);
|
|
}
|
|
|
|
// get current position:
|
|
final long initialPos = startRLERow(row, px0, px_bbox1);
|
|
|
|
// determine need array size:
|
|
// pessimistic: max needed size = deltaX x 4 (1 int)
|
|
final long needSize = initialPos + ((to - from) << 2);
|
|
|
|
// update row data:
|
|
OffHeapArray _rowAAChunk = rowAAChunk;
|
|
// ensure rowAAChunk capacity:
|
|
if (_rowAAChunk.length < needSize) {
|
|
expandRowAAChunk(needSize);
|
|
}
|
|
|
|
final Unsafe _unsafe = OffHeapArray.UNSAFE;
|
|
final long SIZE_INT = 4L;
|
|
final long addr_alpha = ALPHA_MAP_UNSAFE.address;
|
|
long addr_off = _rowAAChunk.address + initialPos;
|
|
|
|
final int[] _touchedTile = touchedTile;
|
|
final int _TILE_SIZE_LG = TILE_W_LG;
|
|
final int _BLK_SIZE_LG = BLOCK_SIZE_LG;
|
|
|
|
// traverse flagged blocks:
|
|
final int blkW = (from >> _BLK_SIZE_LG);
|
|
final int blkE = (to >> _BLK_SIZE_LG) + 1;
|
|
// ensure last block flag = 0 to process final block:
|
|
blkFlags[blkE] = 0;
|
|
|
|
// Perform run-length encoding and store results in the piscesCache
|
|
int val = 0;
|
|
int cx0 = from;
|
|
int runLen;
|
|
|
|
final int _MAX_VALUE = Integer.MAX_VALUE;
|
|
int last_t0 = _MAX_VALUE;
|
|
|
|
int skip = 0;
|
|
|
|
for (int t = blkW, blk_x0, blk_x1, cx, delta; t <= blkE; t++) {
|
|
if (blkFlags[t] != 0) {
|
|
blkFlags[t] = 0;
|
|
|
|
if (last_t0 == _MAX_VALUE) {
|
|
last_t0 = t;
|
|
}
|
|
continue;
|
|
}
|
|
if (last_t0 != _MAX_VALUE) {
|
|
// emit blocks:
|
|
blk_x0 = FloatMath.max(last_t0 << _BLK_SIZE_LG, from);
|
|
last_t0 = _MAX_VALUE;
|
|
|
|
// (last block pixel+1) inclusive => +1
|
|
blk_x1 = FloatMath.min((t << _BLK_SIZE_LG) + 1, to);
|
|
|
|
for (cx = blk_x0; cx < blk_x1; cx++) {
|
|
if ((delta = alphaRow[cx]) != 0) {
|
|
alphaRow[cx] = 0;
|
|
|
|
// not first rle entry:
|
|
if (cx != cx0) {
|
|
runLen = cx - cx0;
|
|
|
|
// store alpha coverage (ensure within bounds):
|
|
// as [absX|val] where:
|
|
// absX is the absolute x-coordinate:
|
|
// note: last pixel exclusive (>= 0)
|
|
// note: it should check X is smaller than 23bits (overflow)!
|
|
|
|
// check address alignment to 4 bytes:
|
|
if (DO_CHECK_UNSAFE) {
|
|
if ((addr_off & 3) != 0) {
|
|
MarlinUtils.logInfo("Misaligned Unsafe address: " + addr_off);
|
|
}
|
|
}
|
|
|
|
// special case to encode entries into a single int:
|
|
if (val == 0) {
|
|
_unsafe.putInt(addr_off,
|
|
((_bboxX0 + cx) << 8)
|
|
);
|
|
} else {
|
|
_unsafe.putInt(addr_off,
|
|
((_bboxX0 + cx) << 8)
|
|
| (((int) _unsafe.getByte(addr_alpha + val)) & 0xFF) // [0-255]
|
|
);
|
|
|
|
if (runLen == 1) {
|
|
_touchedTile[cx0 >> _TILE_SIZE_LG] += val;
|
|
} else {
|
|
touchTile(cx0, val, cx, runLen, _touchedTile);
|
|
}
|
|
}
|
|
addr_off += SIZE_INT;
|
|
|
|
if (DO_STATS) {
|
|
rdrStats.hist_tile_generator_encoding_runLen
|
|
.add(runLen);
|
|
}
|
|
cx0 = cx;
|
|
}
|
|
|
|
// alpha value = running sum of coverage delta:
|
|
val += delta;
|
|
|
|
// ensure values are in [0; MAX_AA_ALPHA] range
|
|
if (DO_AA_RANGE_CHECK) {
|
|
if (val < 0) {
|
|
MarlinUtils.logInfo("Invalid coverage = " + val);
|
|
val = 0;
|
|
}
|
|
if (val > MAX_AA_ALPHA) {
|
|
MarlinUtils.logInfo("Invalid coverage = " + val);
|
|
val = MAX_AA_ALPHA;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (DO_STATS) {
|
|
skip++;
|
|
}
|
|
}
|
|
|
|
// Process remaining RLE run:
|
|
runLen = to - cx0;
|
|
|
|
// store alpha coverage (ensure within bounds):
|
|
// as (int)[absX|val] where:
|
|
// absX is the absolute x-coordinate in bits 31 to 8 and val in bits 0..7
|
|
// note: last pixel exclusive (>= 0)
|
|
// note: it should check X is smaller than 23bits (overflow)!
|
|
|
|
// check address alignment to 4 bytes:
|
|
if (DO_CHECK_UNSAFE) {
|
|
if ((addr_off & 3) != 0) {
|
|
MarlinUtils.logInfo("Misaligned Unsafe address: " + addr_off);
|
|
}
|
|
}
|
|
|
|
// special case to encode entries into a single int:
|
|
if (val == 0) {
|
|
_unsafe.putInt(addr_off,
|
|
((_bboxX0 + to) << 8)
|
|
);
|
|
} else {
|
|
_unsafe.putInt(addr_off,
|
|
((_bboxX0 + to) << 8)
|
|
| (((int) _unsafe.getByte(addr_alpha + val)) & 0xFF) // [0-255]
|
|
);
|
|
|
|
if (runLen == 1) {
|
|
_touchedTile[cx0 >> _TILE_SIZE_LG] += val;
|
|
} else {
|
|
touchTile(cx0, val, to, runLen, _touchedTile);
|
|
}
|
|
}
|
|
addr_off += SIZE_INT;
|
|
|
|
if (DO_STATS) {
|
|
rdrStats.hist_tile_generator_encoding_runLen.add(runLen);
|
|
}
|
|
|
|
long len = (addr_off - _rowAAChunk.address);
|
|
|
|
// update coded length as bytes:
|
|
rowAALen[row] = (len - initialPos);
|
|
|
|
// update current position:
|
|
rowAAChunkPos = len;
|
|
|
|
if (DO_STATS) {
|
|
rdrStats.stat_cache_rowAA.add(rowAALen[row]);
|
|
rdrStats.hist_tile_generator_encoding_ratio.add(
|
|
(100 * skip) / (blkE - blkW)
|
|
);
|
|
}
|
|
|
|
// update tile used marks:
|
|
int tx = from >> _TILE_SIZE_LG; // inclusive
|
|
if (tx < tileMin) {
|
|
tileMin = tx;
|
|
}
|
|
|
|
tx = ((to - 1) >> _TILE_SIZE_LG) + 1; // exclusive (+1 to be sure)
|
|
if (tx > tileMax) {
|
|
tileMax = tx;
|
|
}
|
|
|
|
// Clear alpha row for reuse:
|
|
alphaRow[to] = 0;
|
|
if (DO_CHECKS) {
|
|
IntArrayCache.check(blkFlags, blkW, blkE, 0);
|
|
IntArrayCache.check(alphaRow, from, px1 + 1 - bboxX0, 0);
|
|
}
|
|
}
|
|
|
|
long startRLERow(final int row, final int x0, final int x1) {
|
|
// rows are supposed to be added by increasing y.
|
|
rowAAx0[row] = x0; // first pixel inclusive
|
|
rowAAx1[row] = x1; // last pixel exclusive
|
|
rowAAEnc[row] = 1; // RLE encoding
|
|
rowAAPos[row] = 0L; // position = 0
|
|
|
|
// update row index to current position:
|
|
return (rowAAChunkIndex[row] = rowAAChunkPos);
|
|
}
|
|
|
|
private void expandRowAAChunk(final long needSize) {
|
|
if (DO_STATS) {
|
|
rdrStats.stat_array_marlincache_rowAAChunk.add(needSize);
|
|
}
|
|
|
|
// note: throw IOOB if neededSize > 2Gb:
|
|
final long newSize = ArrayCacheConst.getNewLargeSize(rowAAChunk.length,
|
|
needSize);
|
|
|
|
rowAAChunk.resize(newSize);
|
|
}
|
|
|
|
private void touchTile(final int x0, final int val, final int x1,
|
|
final int runLen,
|
|
final int[] _touchedTile)
|
|
{
|
|
// the x and y of the current row, minus bboxX0, bboxY0
|
|
// process tile line [0 - 32]
|
|
final int _TILE_SIZE_LG = TILE_W_LG;
|
|
|
|
// update touchedTile
|
|
int tx = (x0 >> _TILE_SIZE_LG);
|
|
|
|
// handle trivial case: same tile (x0, x0+runLen)
|
|
if (tx == (x1 >> _TILE_SIZE_LG)) {
|
|
// same tile:
|
|
_touchedTile[tx] += val * runLen;
|
|
return;
|
|
}
|
|
|
|
final int tx1 = (x1 - 1) >> _TILE_SIZE_LG;
|
|
|
|
if (tx <= tx1) {
|
|
final int nextTileXCoord = (tx + 1) << _TILE_SIZE_LG;
|
|
_touchedTile[tx++] += val * (nextTileXCoord - x0);
|
|
}
|
|
if (tx < tx1) {
|
|
// don't go all the way to tx1 - we need to handle the last
|
|
// tile as a special case (just like we did with the first
|
|
final int tileVal = (val << _TILE_SIZE_LG);
|
|
for (; tx < tx1; tx++) {
|
|
_touchedTile[tx] += tileVal;
|
|
}
|
|
}
|
|
// they will be equal unless x0 >> TILE_SIZE_LG == tx1
|
|
if (tx == tx1) {
|
|
final int txXCoord = tx << _TILE_SIZE_LG;
|
|
final int nextTileXCoord = (tx + 1) << _TILE_SIZE_LG;
|
|
|
|
final int lastXCoord = (nextTileXCoord <= x1) ? nextTileXCoord : x1;
|
|
_touchedTile[tx] += val * (lastXCoord - txXCoord);
|
|
}
|
|
}
|
|
|
|
int alphaSumInTile(final int x) {
|
|
return touchedTile[(x - bboxX0) >> TILE_W_LG];
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "bbox = ["
|
|
+ bboxX0 + ", " + bboxY0 + " => "
|
|
+ bboxX1 + ", " + bboxY1 + "]\n";
|
|
}
|
|
|
|
private static byte[] buildAlphaMap(final int maxalpha) {
|
|
// double size !
|
|
final byte[] alMap = new byte[maxalpha << 1];
|
|
final int halfmaxalpha = maxalpha >> 2;
|
|
for (int i = 0; i <= maxalpha; i++) {
|
|
alMap[i] = (byte) ((i * 255 + halfmaxalpha) / maxalpha);
|
|
}
|
|
return alMap;
|
|
}
|
|
}
|