1/* 2 * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package sun.java2d.marlin; 27 28import jdk.internal.misc.Unsafe; 29 30/** 31 * An object used to cache pre-rendered complex paths. 32 * 33 * @see Renderer 34 */ 35public final class MarlinCache implements MarlinConst { 36 37 static final boolean FORCE_RLE = MarlinProperties.isForceRLE(); 38 static final boolean FORCE_NO_RLE = MarlinProperties.isForceNoRLE(); 39 // minimum width to try using RLE encoding: 40 static final int RLE_MIN_WIDTH 41 = Math.max(BLOCK_SIZE, MarlinProperties.getRLEMinWidth()); 42 // maximum width for RLE encoding: 43 // values are stored as int [x|alpha] where alpha is 8 bits 44 static final int RLE_MAX_WIDTH = 1 << (24 - 1); 45 46 // 2048 (pixelSize) alpha values (width) x 32 rows (tile) = 64K bytes 47 // x1 instead of 4 bytes (RLE) ie 1/4 capacity or average good RLE compression 48 static final long INITIAL_CHUNK_ARRAY = TILE_SIZE * INITIAL_PIXEL_DIM; // 64K 49 50 // The alpha map used by this object (taken out of our map cache) to convert 51 // pixel coverage counts gotten from MarlinCache (which are in the range 52 // [0, maxalpha]) into alpha values, which are in [0,256). 53 static final byte[] ALPHA_MAP; 54 55 static final OffHeapArray ALPHA_MAP_UNSAFE; 56 57 static { 58 final byte[] _ALPHA_MAP = buildAlphaMap(MAX_AA_ALPHA); 59 60 ALPHA_MAP_UNSAFE = new OffHeapArray(_ALPHA_MAP, _ALPHA_MAP.length); // 1K 61 ALPHA_MAP =_ALPHA_MAP; 62 63 final Unsafe _unsafe = OffHeapArray.UNSAFE; 64 final long addr = ALPHA_MAP_UNSAFE.address; 65 66 for (int i = 0; i < _ALPHA_MAP.length; i++) { 67 _unsafe.putByte(addr + i, _ALPHA_MAP[i]); 68 } 69 } 70 71 int bboxX0, bboxY0, bboxX1, bboxY1; 72 73 // 1D dirty arrays 74 // row index in rowAAChunk[] 75 final long[] rowAAChunkIndex = new long[TILE_SIZE]; 76 // first pixel (inclusive) for each row 77 final int[] rowAAx0 = new int[TILE_SIZE]; 78 // last pixel (exclusive) for each row 79 final int[] rowAAx1 = new int[TILE_SIZE]; 80 // encoding mode (0=raw, 1=RLE encoding) for each row 81 final int[] rowAAEnc = new int[TILE_SIZE]; 82 // coded length (RLE encoding) for each row 83 final long[] rowAALen = new long[TILE_SIZE]; 84 // last position in RLE decoding for each row (getAlpha): 85 final long[] rowAAPos = new long[TILE_SIZE]; 86 87 // dirty off-heap array containing pixel coverages for (32) rows (packed) 88 // if encoding=raw, it contains alpha coverage values (val) as integer 89 // if encoding=RLE, it contains tuples (val, last x-coordinate exclusive) 90 // use rowAAx0/rowAAx1 to get row indices within this chunk 91 final OffHeapArray rowAAChunk; 92 93 // current position in rowAAChunk array 94 long rowAAChunkPos; 95 96 // touchedTile[i] is the sum of all the alphas in the tile with 97 // x=j*TILE_SIZE+bboxX0. 98 int[] touchedTile; 99 100 // per-thread renderer context 101 final RendererContext rdrCtx; 102 103 // touchedTile ref (clean) 104 private final IntArrayCache.Reference touchedTile_ref; 105 106 int tileMin, tileMax; 107 108 boolean useRLE = false; 109 110 MarlinCache(final RendererContext rdrCtx) { 111 this.rdrCtx = rdrCtx; 112 113 rowAAChunk = rdrCtx.newOffHeapArray(INITIAL_CHUNK_ARRAY); // 64K 114 115 touchedTile_ref = rdrCtx.newCleanIntArrayRef(INITIAL_ARRAY); // 1K = 1 tile line 116 touchedTile = touchedTile_ref.initial; 117 118 // tile used marks: 119 tileMin = Integer.MAX_VALUE; 120 tileMax = Integer.MIN_VALUE; 121 } 122 123 void init(int minx, int miny, int maxx, int maxy, int edgeSumDeltaY) 124 { 125 // assert maxy >= miny && maxx >= minx; 126 bboxX0 = minx; 127 bboxY0 = miny; 128 bboxX1 = maxx; 129 bboxY1 = maxy; 130 131 final int width = (maxx - minx); 132 133 if (FORCE_NO_RLE) { 134 useRLE = false; 135 } else if (FORCE_RLE) { 136 useRLE = true; 137 } else { 138 // heuristics: use both bbox area and complexity 139 // ie number of primitives: 140 141 // fast check min and max width (maxx < 23bits): 142 if (width <= RLE_MIN_WIDTH || width >= RLE_MAX_WIDTH) { 143 useRLE = false; 144 } else { 145 // perimeter approach: how fit the total length into given height: 146 147 // if stroking: meanCrossings /= 2 => divide edgeSumDeltaY by 2 148 final int heightSubPixel 149 = (((maxy - miny) << SUBPIXEL_LG_POSITIONS_Y) << rdrCtx.stroking); 150 151 // check meanDist > block size: 152 // check width / (meanCrossings - 1) >= RLE_THRESHOLD 153 154 // fast case: (meanCrossingPerPixel <= 2) means 1 span only 155 useRLE = (edgeSumDeltaY <= (heightSubPixel << 1)) 156 // note: already checked (meanCrossingPerPixel <= 2) 157 // rewritten to avoid division: 158 || (width * heightSubPixel) > 159 ((edgeSumDeltaY - heightSubPixel) << BLOCK_SIZE_LG); 160 161 if (DO_TRACE && !useRLE) { 162 final float meanCrossings 163 = ((float) edgeSumDeltaY) / heightSubPixel; 164 final float meanDist = width / (meanCrossings - 1); 165 166 System.out.println("High complexity: " 167 + " for bbox[width = " + width 168 + " height = " + (maxy - miny) 169 + "] edgeSumDeltaY = " + edgeSumDeltaY 170 + " heightSubPixel = " + heightSubPixel 171 + " meanCrossings = "+ meanCrossings 172 + " meanDist = " + meanDist 173 + " width = " + (width * heightSubPixel) 174 + " <= criteria: " + ((edgeSumDeltaY - heightSubPixel) << BLOCK_SIZE_LG) 175 ); 176 } 177 } 178 } 179 180 // the ceiling of (maxy - miny + 1) / TILE_SIZE; 181 final int nxTiles = (width + TILE_SIZE) >> TILE_SIZE_LG; 182 183 if (nxTiles > INITIAL_ARRAY) { 184 if (DO_STATS) { 185 rdrCtx.stats.stat_array_marlincache_touchedTile.add(nxTiles); 186 } 187 touchedTile = touchedTile_ref.getArray(nxTiles); 188 } 189 } 190 191 /** 192 * Disposes this cache: 193 * clean up before reusing this instance 194 */ 195 void dispose() { 196 // Reset touchedTile if needed: 197 resetTileLine(0); 198 199 if (DO_STATS) { 200 rdrCtx.stats.totalOffHeap += rowAAChunk.length; 201 } 202 203 // Return arrays: 204 touchedTile = touchedTile_ref.putArray(touchedTile, 0, 0); // already zero filled 205 206 // At last: resize back off-heap rowAA to initial size 207 if (rowAAChunk.length != INITIAL_CHUNK_ARRAY) { 208 // note: may throw OOME: 209 rowAAChunk.resize(INITIAL_CHUNK_ARRAY); 210 } 211 if (DO_CLEAN_DIRTY) { 212 // Force zero-fill dirty arrays: 213 rowAAChunk.fill(BYTE_0); 214 } 215 } 216 217 void resetTileLine(final int pminY) { 218 // update bboxY0 to process a complete tile line [0 - 32] 219 bboxY0 = pminY; 220 221 // reset current pos 222 if (DO_STATS) { 223 rdrCtx.stats.stat_cache_rowAAChunk.add(rowAAChunkPos); 224 } 225 rowAAChunkPos = 0L; 226 227 // Reset touchedTile: 228 if (tileMin != Integer.MAX_VALUE) { 229 if (DO_STATS) { 230 rdrCtx.stats.stat_cache_tiles.add(tileMax - tileMin); 231 } 232 // clean only dirty touchedTile: 233 if (tileMax == 1) { 234 touchedTile[0] = 0; 235 } else { 236 IntArrayCache.fill(touchedTile, tileMin, tileMax, 0); 237 } 238 // reset tile used marks: 239 tileMin = Integer.MAX_VALUE; 240 tileMax = Integer.MIN_VALUE; 241 } 242 243 if (DO_CLEAN_DIRTY) { 244 // Force zero-fill dirty arrays: 245 rowAAChunk.fill(BYTE_0); 246 } 247 } 248 249 void clearAARow(final int y) { 250 // process tile line [0 - 32] 251 final int row = y - bboxY0; 252 253 // update pixel range: 254 rowAAx0[row] = 0; // first pixel inclusive 255 rowAAx1[row] = 0; // last pixel exclusive 256 rowAAEnc[row] = 0; // raw encoding 257 258 // note: leave rowAAChunkIndex[row] undefined 259 // and rowAALen[row] & rowAAPos[row] (RLE) 260 } 261 262 /** 263 * Copy the given alpha data into the rowAA cache 264 * @param alphaRow alpha data to copy from 265 * @param y y pixel coordinate 266 * @param px0 first pixel inclusive x0 267 * @param px1 last pixel exclusive x1 268 */ 269 void copyAARowNoRLE(final int[] alphaRow, final int y, 270 final int px0, final int px1) 271 { 272 if (DO_MONITORS) { 273 rdrCtx.stats.mon_rdr_copyAARow.start(); 274 } 275 276 // skip useless pixels above boundary 277 final int px_bbox1 = FloatMath.min(px1, bboxX1); 278 279 if (DO_LOG_BOUNDS) { 280 MarlinUtils.logInfo("row = [" + px0 + " ... " + px_bbox1 281 + " (" + px1 + ") [ for y=" + y); 282 } 283 284 final int row = y - bboxY0; 285 286 // update pixel range: 287 rowAAx0[row] = px0; // first pixel inclusive 288 rowAAx1[row] = px_bbox1; // last pixel exclusive 289 rowAAEnc[row] = 0; // raw encoding 290 291 // get current position (bytes): 292 final long pos = rowAAChunkPos; 293 // update row index to current position: 294 rowAAChunkIndex[row] = pos; 295 296 // determine need array size: 297 // for RLE encoding, position must be aligned to 4 bytes (int): 298 // align - 1 = 3 so add +3 and round-off by mask ~3 = -4 299 final long needSize = pos + ((px_bbox1 - px0 + 3) & -4); 300 301 // update next position (bytes): 302 rowAAChunkPos = needSize; 303 304 // update row data: 305 final OffHeapArray _rowAAChunk = rowAAChunk; 306 // ensure rowAAChunk capacity: 307 if (_rowAAChunk.length < needSize) { 308 expandRowAAChunk(needSize); 309 } 310 if (DO_STATS) { 311 rdrCtx.stats.stat_cache_rowAA.add(px_bbox1 - px0); 312 } 313 314 // rowAA contains only alpha values for range[x0; x1[ 315 final int[] _touchedTile = touchedTile; 316 final int _TILE_SIZE_LG = TILE_SIZE_LG; 317 318 final int from = px0 - bboxX0; // first pixel inclusive 319 final int to = px_bbox1 - bboxX0; // last pixel exclusive 320 321 final Unsafe _unsafe = OffHeapArray.UNSAFE; 322 final long SIZE_BYTE = 1L; 323 final long addr_alpha = ALPHA_MAP_UNSAFE.address; 324 long addr_off = _rowAAChunk.address + pos; 325 326 // compute alpha sum into rowAA: 327 for (int x = from, val = 0; x < to; x++) { 328 // alphaRow is in [0; MAX_COVERAGE] 329 val += alphaRow[x]; // [from; to[ 330 331 // ensure values are in [0; MAX_AA_ALPHA] range 332 if (DO_AA_RANGE_CHECK) { 333 if (val < 0) { 334 System.out.println("Invalid coverage = " + val); 335 val = 0; 336 } 337 if (val > MAX_AA_ALPHA) { 338 System.out.println("Invalid coverage = " + val); 339 val = MAX_AA_ALPHA; 340 } 341 } 342 343 // store alpha sum (as byte): 344 if (val == 0) { 345 _unsafe.putByte(addr_off, (byte)0); // [0..255] 346 } else { 347 _unsafe.putByte(addr_off, _unsafe.getByte(addr_alpha + val)); // [0..255] 348 349 // update touchedTile 350 _touchedTile[x >> _TILE_SIZE_LG] += val; 351 } 352 addr_off += SIZE_BYTE; 353 } 354 355 // update tile used marks: 356 int tx = from >> _TILE_SIZE_LG; // inclusive 357 if (tx < tileMin) { 358 tileMin = tx; 359 } 360 361 tx = ((to - 1) >> _TILE_SIZE_LG) + 1; // exclusive (+1 to be sure) 362 if (tx > tileMax) { 363 tileMax = tx; 364 } 365 366 if (DO_LOG_BOUNDS) { 367 MarlinUtils.logInfo("clear = [" + from + " ... " + to + "["); 368 } 369 370 // Clear alpha row for reuse: 371 IntArrayCache.fill(alphaRow, from, px1 - bboxX0, 0); 372 373 if (DO_MONITORS) { 374 rdrCtx.stats.mon_rdr_copyAARow.stop(); 375 } 376 } 377 378 void copyAARowRLE_WithBlockFlags(final int[] blkFlags, final int[] alphaRow, 379 final int y, final int px0, final int px1) 380 { 381 if (DO_MONITORS) { 382 rdrCtx.stats.mon_rdr_copyAARow.start(); 383 } 384 385 // Copy rowAA data into the piscesCache if one is present 386 final int _bboxX0 = bboxX0; 387 388 // process tile line [0 - 32] 389 final int row = y - bboxY0; 390 final int from = px0 - _bboxX0; // first pixel inclusive 391 392 // skip useless pixels above boundary 393 final int px_bbox1 = FloatMath.min(px1, bboxX1); 394 final int to = px_bbox1 - _bboxX0; // last pixel exclusive 395 396 if (DO_LOG_BOUNDS) { 397 MarlinUtils.logInfo("row = [" + px0 + " ... " + px_bbox1 398 + " (" + px1 + ") [ for y=" + y); 399 } 400 401 // get current position: 402 final long initialPos = startRLERow(row, px0, px_bbox1); 403 404 // determine need array size: 405 // pessimistic: max needed size = deltaX x 4 (1 int) 406 final long needSize = initialPos + ((to - from) << 2); 407 408 // update row data: 409 OffHeapArray _rowAAChunk = rowAAChunk; 410 // ensure rowAAChunk capacity: 411 if (_rowAAChunk.length < needSize) { 412 expandRowAAChunk(needSize); 413 } 414 415 final Unsafe _unsafe = OffHeapArray.UNSAFE; 416 final long SIZE_INT = 4L; 417 final long addr_alpha = ALPHA_MAP_UNSAFE.address; 418 long addr_off = _rowAAChunk.address + initialPos; 419 420 final int[] _touchedTile = touchedTile; 421 final int _TILE_SIZE_LG = TILE_SIZE_LG; 422 final int _BLK_SIZE_LG = BLOCK_SIZE_LG; 423 424 // traverse flagged blocks: 425 final int blkW = (from >> _BLK_SIZE_LG); 426 final int blkE = (to >> _BLK_SIZE_LG) + 1; 427 428 // Perform run-length encoding and store results in the piscesCache 429 int val = 0; 430 int cx0 = from; 431 int runLen; 432 433 final int _MAX_VALUE = Integer.MAX_VALUE; 434 int last_t0 = _MAX_VALUE; 435 436 int skip = 0; 437 438 for (int t = blkW, blk_x0, blk_x1, cx, delta; t <= blkE; t++) { 439 if (blkFlags[t] != 0) { 440 blkFlags[t] = 0; 441 442 if (last_t0 == _MAX_VALUE) { 443 last_t0 = t; 444 } 445 continue; 446 } 447 if (last_t0 != _MAX_VALUE) { 448 // emit blocks: 449 blk_x0 = FloatMath.max(last_t0 << _BLK_SIZE_LG, from); 450 last_t0 = _MAX_VALUE; 451 452 // (last block pixel+1) inclusive => +1 453 blk_x1 = FloatMath.min((t << _BLK_SIZE_LG) + 1, to); 454 455 for (cx = blk_x0; cx < blk_x1; cx++) { 456 if ((delta = alphaRow[cx]) != 0) { 457 alphaRow[cx] = 0; 458 459 // not first rle entry: 460 if (cx != cx0) { 461 runLen = cx - cx0; 462 463 // store alpha coverage (ensure within bounds): 464 // as [absX|val] where: 465 // absX is the absolute x-coordinate: 466 // note: last pixel exclusive (>= 0) 467 // note: it should check X is smaller than 23bits (overflow)! 468 469 // check address alignment to 4 bytes: 470 if (DO_CHECK_UNSAFE) { 471 if ((addr_off & 3) != 0) { 472 MarlinUtils.logInfo("Misaligned Unsafe address: " + addr_off); 473 } 474 } 475 476 // special case to encode entries into a single int: 477 if (val == 0) { 478 _unsafe.putInt(addr_off, 479 ((_bboxX0 + cx) << 8) 480 ); 481 } else { 482 _unsafe.putInt(addr_off, 483 ((_bboxX0 + cx) << 8) 484 | (((int) _unsafe.getByte(addr_alpha + val)) & 0xFF) // [0..255] 485 ); 486 487 if (runLen == 1) { 488 _touchedTile[cx0 >> _TILE_SIZE_LG] += val; 489 } else { 490 touchTile(cx0, val, cx, runLen, _touchedTile); 491 } 492 } 493 addr_off += SIZE_INT; 494 495 if (DO_STATS) { 496 rdrCtx.stats.hist_tile_generator_encoding_runLen 497 .add(runLen); 498 } 499 cx0 = cx; 500 } 501 502 // alpha value = running sum of coverage delta: 503 val += delta; 504 505 // ensure values are in [0; MAX_AA_ALPHA] range 506 if (DO_AA_RANGE_CHECK) { 507 if (val < 0) { 508 System.out.println("Invalid coverage = " + val); 509 val = 0; 510 } 511 if (val > MAX_AA_ALPHA) { 512 System.out.println("Invalid coverage = " + val); 513 val = MAX_AA_ALPHA; 514 } 515 } 516 } 517 } 518 } else if (DO_STATS) { 519 skip++; 520 } 521 } 522 523 // Process remaining RLE run: 524 runLen = to - cx0; 525 526 // store alpha coverage (ensure within bounds): 527 // as (int)[absX|val] where: 528 // absX is the absolute x-coordinate in bits 31 to 8 and val in bits 0..7 529 // note: last pixel exclusive (>= 0) 530 // note: it should check X is smaller than 23bits (overflow)! 531 532 // check address alignment to 4 bytes: 533 if (DO_CHECK_UNSAFE) { 534 if ((addr_off & 3) != 0) { 535 MarlinUtils.logInfo("Misaligned Unsafe address: " + addr_off); 536 } 537 } 538 539 // special case to encode entries into a single int: 540 if (val == 0) { 541 _unsafe.putInt(addr_off, 542 ((_bboxX0 + to) << 8) 543 ); 544 } else { 545 _unsafe.putInt(addr_off, 546 ((_bboxX0 + to) << 8) 547 | (((int) _unsafe.getByte(addr_alpha + val)) & 0xFF) // [0..255] 548 ); 549 550 if (runLen == 1) { 551 _touchedTile[cx0 >> _TILE_SIZE_LG] += val; 552 } else { 553 touchTile(cx0, val, to, runLen, _touchedTile); 554 } 555 } 556 addr_off += SIZE_INT; 557 558 if (DO_STATS) { 559 rdrCtx.stats.hist_tile_generator_encoding_runLen.add(runLen); 560 } 561 562 long len = (addr_off - _rowAAChunk.address); 563 564 // update coded length as bytes: 565 rowAALen[row] = (len - initialPos); 566 567 // update current position: 568 rowAAChunkPos = len; 569 570 if (DO_STATS) { 571 rdrCtx.stats.stat_cache_rowAA.add(rowAALen[row]); 572 rdrCtx.stats.hist_tile_generator_encoding_ratio.add( 573 (100 * skip) / (blkE - blkW) 574 ); 575 } 576 577 // update tile used marks: 578 int tx = from >> _TILE_SIZE_LG; // inclusive 579 if (tx < tileMin) { 580 tileMin = tx; 581 } 582 583 tx = ((to - 1) >> _TILE_SIZE_LG) + 1; // exclusive (+1 to be sure) 584 if (tx > tileMax) { 585 tileMax = tx; 586 } 587 588 // Clear alpha row for reuse: 589 if (px1 > bboxX1) { 590 alphaRow[to ] = 0; 591 alphaRow[to + 1] = 0; 592 } 593 if (DO_CHECKS) { 594 IntArrayCache.check(blkFlags, blkW, blkE, 0); 595 IntArrayCache.check(alphaRow, from, px1 - bboxX0, 0); 596 } 597 598 if (DO_MONITORS) { 599 rdrCtx.stats.mon_rdr_copyAARow.stop(); 600 } 601 } 602 603 long startRLERow(final int row, final int x0, final int x1) { 604 // rows are supposed to be added by increasing y. 605 rowAAx0[row] = x0; // first pixel inclusive 606 rowAAx1[row] = x1; // last pixel exclusive 607 rowAAEnc[row] = 1; // RLE encoding 608 rowAAPos[row] = 0L; // position = 0 609 610 // update row index to current position: 611 return (rowAAChunkIndex[row] = rowAAChunkPos); 612 } 613 614 private void expandRowAAChunk(final long needSize) { 615 if (DO_STATS) { 616 rdrCtx.stats.stat_array_marlincache_rowAAChunk.add(needSize); 617 } 618 619 // note: throw IOOB if neededSize > 2Gb: 620 final long newSize = ArrayCacheConst.getNewLargeSize(rowAAChunk.length, 621 needSize); 622 623 rowAAChunk.resize(newSize); 624 } 625 626 private void touchTile(final int x0, final int val, final int x1, 627 final int runLen, 628 final int[] _touchedTile) 629 { 630 // the x and y of the current row, minus bboxX0, bboxY0 631 // process tile line [0 - 32] 632 final int _TILE_SIZE_LG = TILE_SIZE_LG; 633 634 // update touchedTile 635 int tx = (x0 >> _TILE_SIZE_LG); 636 637 // handle trivial case: same tile (x0, x0+runLen) 638 if (tx == (x1 >> _TILE_SIZE_LG)) { 639 // same tile: 640 _touchedTile[tx] += val * runLen; 641 return; 642 } 643 644 final int tx1 = (x1 - 1) >> _TILE_SIZE_LG; 645 646 if (tx <= tx1) { 647 final int nextTileXCoord = (tx + 1) << _TILE_SIZE_LG; 648 _touchedTile[tx++] += val * (nextTileXCoord - x0); 649 } 650 if (tx < tx1) { 651 // don't go all the way to tx1 - we need to handle the last 652 // tile as a special case (just like we did with the first 653 final int tileVal = (val << _TILE_SIZE_LG); 654 for (; tx < tx1; tx++) { 655 _touchedTile[tx] += tileVal; 656 } 657 } 658 // they will be equal unless x0 >> TILE_SIZE_LG == tx1 659 if (tx == tx1) { 660 final int txXCoord = tx << _TILE_SIZE_LG; 661 final int nextTileXCoord = (tx + 1) << _TILE_SIZE_LG; 662 663 final int lastXCoord = (nextTileXCoord <= x1) ? nextTileXCoord : x1; 664 _touchedTile[tx] += val * (lastXCoord - txXCoord); 665 } 666 } 667 668 int alphaSumInTile(final int x) { 669 return touchedTile[(x - bboxX0) >> TILE_SIZE_LG]; 670 } 671 672 @Override 673 public String toString() { 674 return "bbox = [" 675 + bboxX0 + ", " + bboxY0 + " => " 676 + bboxX1 + ", " + bboxY1 + "]\n"; 677 } 678 679 private static byte[] buildAlphaMap(final int maxalpha) { 680 // double size ! 681 final byte[] alMap = new byte[maxalpha << 1]; 682 final int halfmaxalpha = maxalpha >> 2; 683 for (int i = 0; i <= maxalpha; i++) { 684 alMap[i] = (byte) ((i * 255 + halfmaxalpha) / maxalpha); 685// System.out.println("alphaMap[" + i + "] = " 686// + Byte.toUnsignedInt(alMap[i])); 687 } 688 return alMap; 689 } 690} 691