1 /*
   2  * Copyright (c) 2007, 2015, 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 
  26 package sun.java2d.marlin;
  27 
  28 import jdk.internal.misc.Unsafe;
  29 
  30 /**
  31  * An object used to cache pre-rendered complex paths.
  32  *
  33  * @see Renderer
  34  */
  35 public 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     // large cached touchedTile (dirty)
 104     final int[] touchedTile_initial = new int[INITIAL_ARRAY]; // 1 tile line
 105 
 106     int tileMin, tileMax;
 107 
 108     boolean useRLE = false;
 109 
 110     MarlinCache(final RendererContext rdrCtx) {
 111         this.rdrCtx = rdrCtx;
 112 
 113         rowAAChunk = new OffHeapArray(rdrCtx.cleanerObj, INITIAL_CHUNK_ARRAY); // 64K
 114 
 115         touchedTile = touchedTile_initial;
 116 
 117         // tile used marks:
 118         tileMin = Integer.MAX_VALUE;
 119         tileMax = Integer.MIN_VALUE;
 120     }
 121 
 122     void init(int minx, int miny, int maxx, int maxy, int edgeSumDeltaY)
 123     {
 124         // assert maxy >= miny && maxx >= minx;
 125         bboxX0 = minx;
 126         bboxY0 = miny;
 127         bboxX1 = maxx;
 128         bboxY1 = maxy;
 129 
 130         final int width = (maxx - minx);
 131 
 132         if (FORCE_NO_RLE) {
 133             useRLE = false;
 134         } else if (FORCE_RLE) {
 135             useRLE = true;
 136         } else {
 137             // heuristics: use both bbox area and complexity
 138             // ie number of primitives:
 139 
 140             // fast check min and max width (maxx < 23bits):
 141             if (width <= RLE_MIN_WIDTH || width >= RLE_MAX_WIDTH) {
 142                 useRLE = false;
 143             } else {
 144                 // perimeter approach: how fit the total length into given height:
 145 
 146                 // if stroking: meanCrossings /= 2 => divide edgeSumDeltaY by 2
 147                 final int heightSubPixel
 148                     = (((maxy - miny) << SUBPIXEL_LG_POSITIONS_Y) << rdrCtx.stroking);
 149 
 150                 // check meanDist > block size:
 151                 // check width / (meanCrossings - 1) >= RLE_THRESHOLD
 152 
 153                 // fast case: (meanCrossingPerPixel <= 2) means 1 span only
 154                 useRLE = (edgeSumDeltaY <= (heightSubPixel << 1))
 155                     // note: already checked (meanCrossingPerPixel <= 2)
 156                     // rewritten to avoid division:
 157                     || (width * heightSubPixel) >
 158                             ((edgeSumDeltaY - heightSubPixel) << BLOCK_SIZE_LG);
 159 
 160                 if (doTrace && !useRLE) {
 161                     final float meanCrossings
 162                         = ((float) edgeSumDeltaY) / heightSubPixel;
 163                     final float meanDist = width / (meanCrossings - 1);
 164 
 165                     System.out.println("High complexity: "
 166                         + " for bbox[width = " + width
 167                         + " height = " + (maxy - miny)
 168                         + "] edgeSumDeltaY = " + edgeSumDeltaY
 169                         + " heightSubPixel = " + heightSubPixel
 170                         + " meanCrossings = "+ meanCrossings
 171                         + " meanDist = " + meanDist
 172                         + " width =  " + (width * heightSubPixel)
 173                         + " <= criteria:  " + ((edgeSumDeltaY - heightSubPixel) << BLOCK_SIZE_LG)
 174                     );
 175                 }
 176             }
 177         }
 178 
 179         // the ceiling of (maxy - miny + 1) / TILE_SIZE;
 180         final int nxTiles = (width + TILE_SIZE) >> TILE_SIZE_LG;
 181 
 182         if (nxTiles > INITIAL_ARRAY) {
 183             if (doStats) {
 184                 RendererContext.stats.stat_array_marlincache_touchedTile
 185                     .add(nxTiles);
 186             }
 187             touchedTile = rdrCtx.getIntArray(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         // Return arrays:
 200         if (touchedTile != touchedTile_initial) {
 201             rdrCtx.putIntArray(touchedTile, 0, 0); // already zero filled
 202             touchedTile = touchedTile_initial;
 203         }
 204         // At last: resize back off-heap rowAA to initial size
 205         if (rowAAChunk.length != INITIAL_CHUNK_ARRAY) {
 206             // note: may throw OOME:
 207             rowAAChunk.resize(INITIAL_CHUNK_ARRAY);
 208         }
 209         if (doCleanDirty) {
 210             // Force zero-fill dirty arrays:
 211             rowAAChunk.fill(BYTE_0);
 212         }
 213     }
 214 
 215     void resetTileLine(final int pminY) {
 216         // update bboxY0 to process a complete tile line [0 - 32]
 217         bboxY0 = pminY;
 218 
 219         // reset current pos
 220         if (doStats) {
 221             RendererContext.stats.stat_cache_rowAAChunk.add(rowAAChunkPos);
 222         }
 223         rowAAChunkPos = 0L;
 224 
 225         // Reset touchedTile:
 226         if (tileMin != Integer.MAX_VALUE) {
 227             if (doStats) {
 228                 RendererContext.stats.stat_cache_tiles.add(tileMax - tileMin);
 229             }
 230             // clean only dirty touchedTile:
 231             if (tileMax == 1) {
 232                 touchedTile[0] = 0;
 233             } else {
 234                 IntArrayCache.fill(touchedTile, tileMin, tileMax, 0);
 235             }
 236             // reset tile used marks:
 237             tileMin = Integer.MAX_VALUE;
 238             tileMax = Integer.MIN_VALUE;
 239         }
 240 
 241         if (doCleanDirty) {
 242             // Force zero-fill dirty arrays:
 243             rowAAChunk.fill(BYTE_0);
 244         }
 245     }
 246 
 247     void clearAARow(final int y) {
 248         // process tile line [0 - 32]
 249         final int row = y - bboxY0;
 250 
 251         // update pixel range:
 252         rowAAx0[row]  = 0; // first pixel inclusive
 253         rowAAx1[row]  = 0; //  last pixel exclusive
 254         rowAAEnc[row] = 0; // raw encoding
 255 
 256         // note: leave rowAAChunkIndex[row] undefined
 257         // and rowAALen[row] & rowAAPos[row] (RLE)
 258     }
 259 
 260     /**
 261      * Copy the given alpha data into the rowAA cache
 262      * @param alphaRow alpha data to copy from
 263      * @param y y pixel coordinate
 264      * @param px0 first pixel inclusive x0
 265      * @param px1 last pixel exclusive x1
 266      */
 267     void copyAARowNoRLE(final int[] alphaRow, final int y,
 268                    final int px0, final int px1)
 269     {
 270         if (doMonitors) {
 271             RendererContext.stats.mon_rdr_copyAARow.start();
 272         }
 273 
 274         // skip useless pixels above boundary
 275         final int px_bbox1 = FloatMath.min(px1, bboxX1);
 276 
 277         if (doLogBounds) {
 278             MarlinUtils.logInfo("row = [" + px0 + " ... " + px_bbox1
 279                                 + " (" + px1 + ") [ for y=" + y);
 280         }
 281 
 282         final int row = y - bboxY0;
 283 
 284         // update pixel range:
 285         rowAAx0[row]  = px0;      // first pixel inclusive
 286         rowAAx1[row]  = px_bbox1; //  last pixel exclusive
 287         rowAAEnc[row] = 0; // raw encoding
 288 
 289         // get current position (bytes):
 290         final long pos = rowAAChunkPos;
 291         // update row index to current position:
 292         rowAAChunkIndex[row] = pos;
 293 
 294         // determine need array size:
 295         // for RLE encoding, position must be aligned to 4 bytes (int):
 296         // align - 1 = 3 so add +3 and round-off by mask ~3 = -4
 297         final long needSize = pos + ((px_bbox1 - px0 + 3) & -4);
 298 
 299         // update next position (bytes):
 300         rowAAChunkPos = needSize;
 301 
 302         // update row data:
 303         final OffHeapArray _rowAAChunk = rowAAChunk;
 304         // ensure rowAAChunk capacity:
 305         if (_rowAAChunk.length < needSize) {
 306             expandRowAAChunk(needSize);
 307         }
 308         if (doStats) {
 309             RendererContext.stats.stat_cache_rowAA.add(px_bbox1 - px0);
 310         }
 311 
 312         // rowAA contains only alpha values for range[x0; x1[
 313         final int[] _touchedTile = touchedTile;
 314         final int _TILE_SIZE_LG = TILE_SIZE_LG;
 315 
 316         final int from = px0      - bboxX0; // first pixel inclusive
 317         final int to   = px_bbox1 - bboxX0; //  last pixel exclusive
 318 
 319         final Unsafe _unsafe = OffHeapArray.unsafe;
 320         final long SIZE_BYTE = 1L;
 321         final long addr_alpha = ALPHA_MAP_UNSAFE.address;
 322         long addr_off = _rowAAChunk.address + pos;
 323 
 324         // compute alpha sum into rowAA:
 325         for (int x = from, val = 0; x < to; x++) {
 326             // alphaRow is in [0; MAX_COVERAGE]
 327             val += alphaRow[x]; // [from; to[
 328 
 329             // ensure values are in [0; MAX_AA_ALPHA] range
 330             if (DO_AA_RANGE_CHECK) {
 331                 if (val < 0) {
 332                     System.out.println("Invalid coverage = " + val);
 333                     val = 0;
 334                 }
 335                 if (val > MAX_AA_ALPHA) {
 336                     System.out.println("Invalid coverage = " + val);
 337                     val = MAX_AA_ALPHA;
 338                 }
 339             }
 340 
 341             // store alpha sum (as byte):
 342             if (val == 0) {
 343                 _unsafe.putByte(addr_off, (byte)0); // [0..255]
 344             } else {
 345                 _unsafe.putByte(addr_off, _unsafe.getByte(addr_alpha + val)); // [0..255]
 346 
 347                 // update touchedTile
 348                 _touchedTile[x >> _TILE_SIZE_LG] += val;
 349             }
 350             addr_off += SIZE_BYTE;
 351         }
 352 
 353         // update tile used marks:
 354         int tx = from >> _TILE_SIZE_LG; // inclusive
 355         if (tx < tileMin) {
 356             tileMin = tx;
 357         }
 358 
 359         tx = ((to - 1) >> _TILE_SIZE_LG) + 1; // exclusive (+1 to be sure)
 360         if (tx > tileMax) {
 361             tileMax = tx;
 362         }
 363 
 364         if (doLogBounds) {
 365             MarlinUtils.logInfo("clear = [" + from + " ... " + to + "[");
 366         }
 367 
 368         // Clear alpha row for reuse:
 369         IntArrayCache.fill(alphaRow, from, px1 - bboxX0, 0);
 370 
 371         if (doMonitors) {
 372             RendererContext.stats.mon_rdr_copyAARow.stop();
 373         }
 374     }
 375 
 376     void copyAARowRLE_WithBlockFlags(final int[] blkFlags, final int[] alphaRow,
 377                       final int y, final int px0, final int px1)
 378     {
 379         if (doMonitors) {
 380             RendererContext.stats.mon_rdr_copyAARow.start();
 381         }
 382 
 383         // Copy rowAA data into the piscesCache if one is present
 384         final int _bboxX0 = bboxX0;
 385 
 386         // process tile line [0 - 32]
 387         final int row  = y - bboxY0;
 388         final int from = px0 - _bboxX0; // first pixel inclusive
 389 
 390         // skip useless pixels above boundary
 391         final int px_bbox1 = FloatMath.min(px1, bboxX1);
 392         final int to       = px_bbox1 - _bboxX0; //  last pixel exclusive
 393 
 394         if (doLogBounds) {
 395             MarlinUtils.logInfo("row = [" + px0 + " ... " + px_bbox1
 396                                 + " (" + px1 + ") [ for y=" + y);
 397         }
 398 
 399         // get current position:
 400         final long initialPos = startRLERow(row, px0, px_bbox1);
 401 
 402         // determine need array size:
 403         // pessimistic: max needed size = deltaX x 4 (1 int)
 404         final long needSize = initialPos + ((to - from) << 2);
 405 
 406         // update row data:
 407         OffHeapArray _rowAAChunk = rowAAChunk;
 408         // ensure rowAAChunk capacity:
 409         if (_rowAAChunk.length < needSize) {
 410             expandRowAAChunk(needSize);
 411         }
 412 
 413         final Unsafe _unsafe = OffHeapArray.unsafe;
 414         final long SIZE_INT = 4L;
 415         final long addr_alpha = ALPHA_MAP_UNSAFE.address;
 416         long addr_off = _rowAAChunk.address + initialPos;
 417 
 418         final int[] _touchedTile = touchedTile;
 419         final int _TILE_SIZE_LG = TILE_SIZE_LG;
 420         final int _BLK_SIZE_LG  = BLOCK_SIZE_LG;
 421 
 422         // traverse flagged blocks:
 423         final int blkW = (from >> _BLK_SIZE_LG);
 424         final int blkE = (to   >> _BLK_SIZE_LG) + 1;
 425 
 426         // Perform run-length encoding and store results in the piscesCache
 427         int val = 0;
 428         int cx0 = from;
 429         int runLen;
 430 
 431         final int _MAX_VALUE = Integer.MAX_VALUE;
 432         int last_t0 = _MAX_VALUE;
 433 
 434         int skip = 0;
 435 
 436         for (int t = blkW, blk_x0, blk_x1, cx, delta; t <= blkE; t++) {
 437             if (blkFlags[t] != 0) {
 438                 blkFlags[t] = 0;
 439 
 440                 if (last_t0 == _MAX_VALUE) {
 441                     last_t0 = t;
 442                 }
 443                 continue;
 444             }
 445             if (last_t0 != _MAX_VALUE) {
 446                 // emit blocks:
 447                 blk_x0 = FloatMath.max(last_t0 << _BLK_SIZE_LG, from);
 448                 last_t0 = _MAX_VALUE;
 449 
 450                 // (last block pixel+1) inclusive => +1
 451                 blk_x1 = FloatMath.min((t << _BLK_SIZE_LG) + 1, to);
 452 
 453                 for (cx = blk_x0; cx < blk_x1; cx++) {
 454                     if ((delta = alphaRow[cx]) != 0) {
 455                         alphaRow[cx] = 0;
 456 
 457                         // not first rle entry:
 458                         if (cx != cx0) {
 459                             runLen = cx - cx0;
 460 
 461                             // store alpha coverage (ensure within bounds):
 462                             // as [absX|val] where:
 463                             // absX is the absolute x-coordinate:
 464                             // note: last pixel exclusive (>= 0)
 465                             // note: it should check X is smaller than 23bits (overflow)!
 466 
 467                             // check address alignment to 4 bytes:
 468                             if (doCheckUnsafe) {
 469                                 if ((addr_off & 3) != 0) {
 470                                     MarlinUtils.logInfo("Misaligned Unsafe address: " + addr_off);
 471                                 }
 472                             }
 473 
 474                             // special case to encode entries into a single int:
 475                             if (val == 0) {
 476                                 _unsafe.putInt(addr_off,
 477                                     ((_bboxX0 + cx) << 8)
 478                                 );
 479                             } else {
 480                                 _unsafe.putInt(addr_off,
 481                                     ((_bboxX0 + cx) << 8)
 482                                     | (((int) _unsafe.getByte(addr_alpha + val)) & 0xFF) // [0..255]
 483                                 );
 484 
 485                                 if (runLen == 1) {
 486                                     _touchedTile[cx0 >> _TILE_SIZE_LG] += val;
 487                                 } else {
 488                                     touchTile(cx0, val, cx, runLen, _touchedTile);
 489                                 }
 490                             }
 491                             addr_off += SIZE_INT;
 492 
 493                             if (doStats) {
 494                                 RendererContext.stats.hist_tile_generator_encoding_runLen
 495                                     .add(runLen);
 496                             }
 497                             cx0 = cx;
 498                         }
 499 
 500                         // alpha value = running sum of coverage delta:
 501                         val += delta;
 502 
 503                         // ensure values are in [0; MAX_AA_ALPHA] range
 504                         if (DO_AA_RANGE_CHECK) {
 505                             if (val < 0) {
 506                                 System.out.println("Invalid coverage = " + val);
 507                                 val = 0;
 508                             }
 509                             if (val > MAX_AA_ALPHA) {
 510                                 System.out.println("Invalid coverage = " + val);
 511                                 val = MAX_AA_ALPHA;
 512                             }
 513                         }
 514                     }
 515                 }
 516             } else if (doStats) {
 517                 skip++;
 518             }
 519         }
 520 
 521         // Process remaining RLE run:
 522         runLen = to - cx0;
 523 
 524         // store alpha coverage (ensure within bounds):
 525         // as (int)[absX|val] where:
 526         // absX is the absolute x-coordinate in bits 31 to 8 and val in bits 0..7
 527         // note: last pixel exclusive (>= 0)
 528         // note: it should check X is smaller than 23bits (overflow)!
 529 
 530         // check address alignment to 4 bytes:
 531         if (doCheckUnsafe) {
 532             if ((addr_off & 3) != 0) {
 533                 MarlinUtils.logInfo("Misaligned Unsafe address: " + addr_off);
 534             }
 535         }
 536 
 537         // special case to encode entries into a single int:
 538         if (val == 0) {
 539             _unsafe.putInt(addr_off,
 540                 ((_bboxX0 + to) << 8)
 541             );
 542         } else {
 543             _unsafe.putInt(addr_off,
 544                 ((_bboxX0 + to) << 8)
 545                 | (((int) _unsafe.getByte(addr_alpha + val)) & 0xFF) // [0..255]
 546             );
 547 
 548             if (runLen == 1) {
 549                 _touchedTile[cx0 >> _TILE_SIZE_LG] += val;
 550             } else {
 551                 touchTile(cx0, val, to, runLen, _touchedTile);
 552             }
 553         }
 554         addr_off += SIZE_INT;
 555 
 556         if (doStats) {
 557             RendererContext.stats.hist_tile_generator_encoding_runLen
 558                 .add(runLen);
 559         }
 560 
 561         long len = (addr_off - _rowAAChunk.address);
 562 
 563         // update coded length as bytes:
 564         rowAALen[row] = (len - initialPos);
 565 
 566         // update current position:
 567         rowAAChunkPos = len;
 568 
 569         if (doStats) {
 570             RendererContext.stats.stat_cache_rowAA.add(rowAALen[row]);
 571             RendererContext.stats.hist_tile_generator_encoding_ratio.add(
 572                 (100 * skip) / (blkE - blkW)
 573             );
 574         }
 575 
 576         // update tile used marks:
 577         int tx = from >> _TILE_SIZE_LG; // inclusive
 578         if (tx < tileMin) {
 579             tileMin = tx;
 580         }
 581 
 582         tx = ((to - 1) >> _TILE_SIZE_LG) + 1; // exclusive (+1 to be sure)
 583         if (tx > tileMax) {
 584             tileMax = tx;
 585         }
 586 
 587         // Clear alpha row for reuse:
 588         if (px1 > bboxX1) {
 589             alphaRow[to    ] = 0;
 590             alphaRow[to + 1] = 0;
 591         }
 592         if (doChecks) {
 593             IntArrayCache.check(blkFlags, 0, blkFlags.length, 0);
 594             IntArrayCache.check(alphaRow, 0, alphaRow.length, 0);
 595         }
 596 
 597         if (doMonitors) {
 598             RendererContext.stats.mon_rdr_copyAARow.stop();
 599         }
 600     }
 601 
 602     long startRLERow(final int row, final int x0, final int x1) {
 603         // rows are supposed to be added by increasing y.
 604         rowAAx0[row]  = x0; // first pixel inclusive
 605         rowAAx1[row]  = x1; // last pixel exclusive
 606         rowAAEnc[row] = 1; // RLE encoding
 607         rowAAPos[row] = 0L; // position = 0
 608 
 609         // update row index to current position:
 610         return (rowAAChunkIndex[row] = rowAAChunkPos);
 611     }
 612 
 613     private void expandRowAAChunk(final long needSize) {
 614         if (doStats) {
 615             RendererContext.stats.stat_array_marlincache_rowAAChunk
 616                 .add(needSize);
 617         }
 618 
 619         // note: throw IOOB if neededSize > 2Gb:
 620         final long newSize = ArrayCache.getNewLargeSize(rowAAChunk.length, needSize);
 621 
 622         rowAAChunk.resize(newSize);
 623     }
 624 
 625     private void touchTile(final int x0, final int val, final int x1,
 626                            final int runLen,
 627                            final int[] _touchedTile)
 628     {
 629         // the x and y of the current row, minus bboxX0, bboxY0
 630         // process tile line [0 - 32]
 631         final int _TILE_SIZE_LG = TILE_SIZE_LG;
 632 
 633         // update touchedTile
 634         int tx = (x0 >> _TILE_SIZE_LG);
 635 
 636         // handle trivial case: same tile (x0, x0+runLen)
 637         if (tx == (x1 >> _TILE_SIZE_LG)) {
 638             // same tile:
 639             _touchedTile[tx] += val * runLen;
 640             return;
 641         }
 642 
 643         final int tx1 = (x1 - 1) >> _TILE_SIZE_LG;
 644 
 645         if (tx <= tx1) {
 646             final int nextTileXCoord = (tx + 1) << _TILE_SIZE_LG;
 647             _touchedTile[tx++] += val * (nextTileXCoord - x0);
 648         }
 649         if (tx < tx1) {
 650             // don't go all the way to tx1 - we need to handle the last
 651             // tile as a special case (just like we did with the first
 652             final int tileVal = (val << _TILE_SIZE_LG);
 653             for (; tx < tx1; tx++) {
 654                 _touchedTile[tx] += tileVal;
 655             }
 656         }
 657         // they will be equal unless x0 >> TILE_SIZE_LG == tx1
 658         if (tx == tx1) {
 659             final int txXCoord       =  tx      << _TILE_SIZE_LG;
 660             final int nextTileXCoord = (tx + 1) << _TILE_SIZE_LG;
 661 
 662             final int lastXCoord = (nextTileXCoord <= x1) ? nextTileXCoord : x1;
 663             _touchedTile[tx] += val * (lastXCoord - txXCoord);
 664         }
 665     }
 666 
 667     int alphaSumInTile(final int x) {
 668         return touchedTile[(x - bboxX0) >> TILE_SIZE_LG];
 669     }
 670 
 671     @Override
 672     public String toString() {
 673         return "bbox = ["
 674             + bboxX0 + ", " + bboxY0 + " => "
 675             + bboxX1 + ", " + bboxY1 + "]\n";
 676     }
 677 
 678     private static byte[] buildAlphaMap(final int maxalpha) {
 679         // double size !
 680         final byte[] alMap = new byte[maxalpha << 1];
 681         final int halfmaxalpha = maxalpha >> 2;
 682         for (int i = 0; i <= maxalpha; i++) {
 683             alMap[i] = (byte) ((i * 255 + halfmaxalpha) / maxalpha);
 684 //            System.out.println("alphaMap[" + i + "] = "
 685 //                               + Byte.toUnsignedInt(alMap[i]));
 686         }
 687         return alMap;
 688     }
 689 }