1 /*
   2  * Copyright (c) 2007, 2017, 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 java.util.Arrays;
  29 import sun.java2d.pipe.AATileGenerator;
  30 import jdk.internal.misc.Unsafe;
  31 
  32 final class MarlinTileGenerator implements AATileGenerator, MarlinConst {
  33 
  34     private static final boolean DISABLE_BLEND = false;
  35 
  36     private static final int MAX_TILE_ALPHA_SUM = TILE_W * TILE_H * MAX_AA_ALPHA;
  37 
  38     private static final int TH_AA_ALPHA_FILL_EMPTY = ((MAX_AA_ALPHA + 1) / 3); // 33%
  39     private static final int TH_AA_ALPHA_FILL_FULL  = ((MAX_AA_ALPHA + 1) * 2 / 3); // 66%
  40 
  41     private static final int FILL_TILE_W = Math.max(16, TILE_W >> 2); // 1/4th tile width
  42 
  43     static {
  44         if (MAX_TILE_ALPHA_SUM <= 0) {
  45             throw new IllegalStateException("Invalid MAX_TILE_ALPHA_SUM: " + MAX_TILE_ALPHA_SUM);
  46         }
  47         if (DO_TRACE) {
  48             System.out.println("MAX_AA_ALPHA           : " + MAX_AA_ALPHA);
  49             System.out.println("TH_AA_ALPHA_FILL_EMPTY : " + TH_AA_ALPHA_FILL_EMPTY);
  50             System.out.println("TH_AA_ALPHA_FILL_FULL  : " + TH_AA_ALPHA_FILL_FULL);
  51             System.out.println("FILL_TILE_W            : " + FILL_TILE_W);
  52         }
  53     }
  54 
  55     private final Renderer rdrF;
  56     private final DRenderer rdrD;
  57     private final MarlinCache cache;
  58     private int x, y;
  59 
  60     // per-thread renderer stats
  61     final RendererStats rdrStats;
  62 
  63     MarlinTileGenerator(final RendererStats stats, final MarlinRenderer r,
  64                         final MarlinCache cache)
  65     {
  66         this.rdrStats = stats;
  67         if (r instanceof Renderer) {
  68             this.rdrF = (Renderer)r;
  69             this.rdrD = null;
  70         } else {
  71             this.rdrF = null;
  72             this.rdrD = (DRenderer)r;
  73         }
  74         this.cache = cache;
  75     }
  76 
  77     MarlinTileGenerator init() {
  78         this.x = cache.bboxX0;
  79         this.y = cache.bboxY0;
  80 
  81         return this; // fluent API
  82     }
  83 
  84     /**
  85      * Disposes this tile generator:
  86      * clean up before reusing this instance
  87      */
  88     @Override
  89     public void dispose() {
  90         if (DO_MONITORS) {
  91             // called from AAShapePipe.renderTiles() (render tiles end):
  92             rdrStats.mon_pipe_renderTiles.stop();
  93         }
  94         // dispose cache:
  95         cache.dispose();
  96         // dispose renderer and recycle the RendererContext instance:
  97         // bimorphic call optimization:
  98         if (rdrF != null) {
  99             rdrF.dispose();
 100         } else if (rdrD != null) {
 101             rdrD.dispose();
 102         }
 103     }
 104 
 105     void getBbox(int[] bbox) {
 106         bbox[0] = cache.bboxX0;
 107         bbox[1] = cache.bboxY0;
 108         bbox[2] = cache.bboxX1;
 109         bbox[3] = cache.bboxY1;
 110     }
 111 
 112     /**
 113      * Gets the width of the tiles that the generator batches output into.
 114      * @return the width of the standard alpha tile
 115      */
 116     @Override
 117     public int getTileWidth() {
 118         if (DO_MONITORS) {
 119             // called from AAShapePipe.renderTiles() (render tiles start):
 120             rdrStats.mon_pipe_renderTiles.start();
 121         }
 122         return TILE_W;
 123     }
 124 
 125     /**
 126      * Gets the height of the tiles that the generator batches output into.
 127      * @return the height of the standard alpha tile
 128      */
 129     @Override
 130     public int getTileHeight() {
 131         return TILE_H;
 132     }
 133 
 134     /**
 135      * Gets the typical alpha value that will characterize the current
 136      * tile.
 137      * The answer may be 0x00 to indicate that the current tile has
 138      * no coverage in any of its pixels, or it may be 0xff to indicate
 139      * that the current tile is completely covered by the path, or any
 140      * other value to indicate non-trivial coverage cases.
 141      * @return 0x00 for no coverage, 0xff for total coverage, or any other
 142      *         value for partial coverage of the tile
 143      */
 144     @Override
 145     public int getTypicalAlpha() {
 146         if (DISABLE_BLEND) {
 147             // always return empty tiles to disable blending operations
 148             return 0x00;
 149         }
 150         int al = cache.alphaSumInTile(x);
 151         // Note: if we have a filled rectangle that doesn't end on a tile
 152         // border, we could still return 0xff, even though al!=maxTileAlphaSum
 153         // This is because if we return 0xff, our users will fill a rectangle
 154         // starting at x,y that has width = Math.min(TILE_SIZE, bboxX1-x),
 155         // and height min(TILE_SIZE,bboxY1-y), which is what should happen.
 156         // However, to support this, we would have to use 2 Math.min's
 157         // and 2 multiplications per tile, instead of just 2 multiplications
 158         // to compute maxTileAlphaSum. The savings offered would probably
 159         // not be worth it, considering how rare this case is.
 160         // Note: I have not tested this, so in the future if it is determined
 161         // that it is worth it, it should be implemented. Perhaps this method's
 162         // interface should be changed to take arguments the width and height
 163         // of the current tile. This would eliminate the 2 Math.min calls that
 164         // would be needed here, since our caller needs to compute these 2
 165         // values anyway.
 166         final int alpha = (al == 0x00 ? 0x00
 167                               : (al == MAX_TILE_ALPHA_SUM ? 0xff : 0x80));
 168         if (DO_STATS) {
 169             rdrStats.hist_tile_generator_alpha.add(alpha);
 170         }
 171         return alpha;
 172     }
 173 
 174     /**
 175      * Skips the current tile and moves on to the next tile.
 176      * Either this method, or the getAlpha() method should be called
 177      * once per tile, but not both.
 178      */
 179     @Override
 180     public void nextTile() {
 181         if ((x += TILE_W) >= cache.bboxX1) {
 182             x = cache.bboxX0;
 183             y += TILE_H;
 184 
 185             if (y < cache.bboxY1) {
 186                 // compute for the tile line
 187                 // [ y; max(y + TILE_SIZE, bboxY1) ]
 188                 // bimorphic call optimization:
 189                 if (rdrF != null) {
 190                     rdrF.endRendering(y);
 191                 } else if (rdrD != null) {
 192                     rdrD.endRendering(y);
 193                 }
 194             }
 195         }
 196     }
 197 
 198     /**
 199      * Gets the alpha coverage values for the current tile.
 200      * Either this method, or the nextTile() method should be called
 201      * once per tile, but not both.
 202      */
 203     @Override
 204     public void getAlpha(final byte[] tile, final int offset,
 205                                             final int rowstride)
 206     {
 207         if (cache.useRLE) {
 208             getAlphaRLE(tile, offset, rowstride);
 209         } else {
 210             getAlphaNoRLE(tile, offset, rowstride);
 211         }
 212     }
 213 
 214     /**
 215      * Gets the alpha coverage values for the current tile.
 216      * Either this method, or the nextTile() method should be called
 217      * once per tile, but not both.
 218      */
 219     private void getAlphaNoRLE(final byte[] tile, final int offset,
 220                                final int rowstride)
 221     {
 222         if (DO_MONITORS) {
 223             rdrStats.mon_ptg_getAlpha.start();
 224         }
 225 
 226         // local vars for performance:
 227         final MarlinCache _cache = this.cache;
 228         final long[] rowAAChunkIndex = _cache.rowAAChunkIndex;
 229         final int[] rowAAx0 = _cache.rowAAx0;
 230         final int[] rowAAx1 = _cache.rowAAx1;
 231 
 232         final int x0 = this.x;
 233         final int x1 = FloatMath.min(x0 + TILE_W, _cache.bboxX1);
 234 
 235         // note: process tile line [0 - 32[
 236         final int y0 = 0;
 237         final int y1 = FloatMath.min(this.y + TILE_H, _cache.bboxY1) - this.y;
 238 
 239         if (DO_LOG_BOUNDS) {
 240             MarlinUtils.logInfo("getAlpha = [" + x0 + " ... " + x1
 241                                 + "[ [" + y0 + " ... " + y1 + "[");
 242         }
 243 
 244         final Unsafe _unsafe = OffHeapArray.UNSAFE;
 245         final long SIZE = 1L;
 246         final long addr_rowAA = _cache.rowAAChunk.address;
 247         long addr;
 248 
 249         final int skipRowPixels = (rowstride - (x1 - x0));
 250 
 251         int aax0, aax1, end;
 252         int idx = offset;
 253 
 254         for (int cy = y0, cx; cy < y1; cy++) {
 255             // empty line (default)
 256             cx = x0;
 257 
 258             aax1 = rowAAx1[cy]; // exclusive
 259 
 260             // quick check if there is AA data
 261             // corresponding to this tile [x0; x1[
 262             if (aax1 > x0) {
 263                 aax0 = rowAAx0[cy]; // inclusive
 264 
 265                 if (aax0 < x1) {
 266                     // note: cx is the cursor pointer in the tile array
 267                     // (left to right)
 268                     cx = aax0;
 269 
 270                     // ensure cx >= x0
 271                     if (cx <= x0) {
 272                         cx = x0;
 273                     } else {
 274                         // fill line start until first AA pixel rowAA exclusive:
 275                         for (end = x0; end < cx; end++) {
 276                             tile[idx++] = 0;
 277                         }
 278                     }
 279 
 280                     // now: cx >= x0 and cx >= aax0
 281 
 282                     // Copy AA data (sum alpha data):
 283                     addr = addr_rowAA + rowAAChunkIndex[cy] + (cx - aax0);
 284 
 285                     for (end = (aax1 <= x1) ? aax1 : x1; cx < end; cx++) {
 286                         // cx inside tile[x0; x1[ :
 287                         tile[idx++] = _unsafe.getByte(addr); // [0-255]
 288                         addr += SIZE;
 289                     }
 290                 }
 291             }
 292 
 293             // fill line end
 294             while (cx < x1) {
 295                 tile[idx++] = 0;
 296                 cx++;
 297             }
 298 
 299             if (DO_TRACE) {
 300                 for (int i = idx - (x1 - x0); i < idx; i++) {
 301                     System.out.print(hex(tile[i], 2));
 302                 }
 303                 System.out.println();
 304             }
 305 
 306             idx += skipRowPixels;
 307         }
 308 
 309         nextTile();
 310 
 311         if (DO_MONITORS) {
 312             rdrStats.mon_ptg_getAlpha.stop();
 313         }
 314     }
 315 
 316     /**
 317      * Gets the alpha coverage values for the current tile.
 318      * Either this method, or the nextTile() method should be called
 319      * once per tile, but not both.
 320      */
 321     private void getAlphaRLE(final byte[] tile, final int offset,
 322                              final int rowstride)
 323     {
 324         if (DO_MONITORS) {
 325             rdrStats.mon_ptg_getAlpha.start();
 326         }
 327 
 328         // Decode run-length encoded alpha mask data
 329         // The data for row j begins at cache.rowOffsetsRLE[j]
 330         // and is encoded as a set of 2-byte pairs (val, runLen)
 331         // terminated by a (0, 0) pair.
 332 
 333         // local vars for performance:
 334         final MarlinCache _cache = this.cache;
 335         final long[] rowAAChunkIndex = _cache.rowAAChunkIndex;
 336         final int[] rowAAx0 = _cache.rowAAx0;
 337         final int[] rowAAx1 = _cache.rowAAx1;
 338         final int[] rowAAEnc = _cache.rowAAEnc;
 339         final long[] rowAALen = _cache.rowAALen;
 340         final long[] rowAAPos = _cache.rowAAPos;
 341 
 342         final int x0 = this.x;
 343         final int x1 = FloatMath.min(x0 + TILE_W, _cache.bboxX1);
 344         final int w  = x1 - x0;
 345 
 346         // note: process tile line [0 - 32[
 347         final int y0 = 0;
 348         final int y1 = FloatMath.min(this.y + TILE_H, _cache.bboxY1) - this.y;
 349 
 350         if (DO_LOG_BOUNDS) {
 351             MarlinUtils.logInfo("getAlpha = [" + x0 + " ... " + x1
 352                                 + "[ [" + y0 + " ... " + y1 + "[");
 353         }
 354 
 355         // avoid too small area: fill is not faster !
 356         final int clearTile;
 357         final byte refVal;
 358         final int area;
 359 
 360         if ((w >= FILL_TILE_W) && (area = w * y1) > 64) { // 64 / 4 ie 16 words min (faster)
 361             final int alphaSum = cache.alphaSumInTile(x0);
 362 
 363             if (alphaSum < area * TH_AA_ALPHA_FILL_EMPTY) {
 364                 clearTile = 1;
 365                 refVal = 0;
 366             } else if (alphaSum > area * TH_AA_ALPHA_FILL_FULL) {
 367                 clearTile = 2;
 368                 refVal = (byte)0xff;
 369             } else {
 370                 clearTile = 0;
 371                 refVal = 0;
 372             }
 373         } else {
 374             clearTile = 0;
 375             refVal = 0;
 376         }
 377 
 378         final Unsafe _unsafe = OffHeapArray.UNSAFE;
 379         final long SIZE_BYTE = 1L;
 380         final long SIZE_INT = 4L;
 381         final long addr_rowAA = _cache.rowAAChunk.address;
 382         long addr, addr_row, last_addr, addr_end;
 383 
 384         final int skipRowPixels = (rowstride - w);
 385 
 386         int cx, cy, cx1;
 387         int rx0, rx1, runLen, end;
 388         int packed;
 389         byte val;
 390         int idx = offset;
 391 
 392         switch (clearTile) {
 393         case 1: // 0x00
 394             // Clear full tile rows:
 395             Arrays.fill(tile, offset, offset + (y1 * rowstride), refVal);
 396 
 397             for (cy = y0; cy < y1; cy++) {
 398                 // empty line (default)
 399                 cx = x0;
 400 
 401                 if (rowAAEnc[cy] == 0) {
 402                     // Raw encoding:
 403 
 404                     final int aax1 = rowAAx1[cy]; // exclusive
 405 
 406                     // quick check if there is AA data
 407                     // corresponding to this tile [x0; x1[
 408                     if (aax1 > x0) {
 409                         final int aax0 = rowAAx0[cy]; // inclusive
 410 
 411                         if (aax0 < x1) {
 412                             // note: cx is the cursor pointer in the tile array
 413                             // (left to right)
 414                             cx = aax0;
 415 
 416                             // ensure cx >= x0
 417                             if (cx <= x0) {
 418                                 cx = x0;
 419                             } else {
 420                                 // skip line start until first AA pixel rowAA exclusive:
 421                                 idx += (cx - x0); // > 0
 422                             }
 423 
 424                             // now: cx >= x0 and cx >= aax0
 425 
 426                             // Copy AA data (sum alpha data):
 427                             addr = addr_rowAA + rowAAChunkIndex[cy] + (cx - aax0);
 428 
 429                             for (end = (aax1 <= x1) ? aax1 : x1; cx < end; cx++) {
 430                                 tile[idx++] = _unsafe.getByte(addr); // [0-255]
 431                                 addr += SIZE_BYTE;
 432                             }
 433                         }
 434                     }
 435                 } else {
 436                     // RLE encoding:
 437 
 438                     // quick check if there is AA data
 439                     // corresponding to this tile [x0; x1[
 440                     if (rowAAx1[cy] > x0) { // last pixel exclusive
 441 
 442                         cx = rowAAx0[cy]; // inclusive
 443                         if (cx > x1) {
 444                             cx = x1;
 445                         }
 446 
 447                         // skip line start until first AA pixel rowAA exclusive:
 448                         if (cx > x0) {
 449                             idx += (cx - x0); // > 0
 450                         }
 451 
 452                         // get row address:
 453                         addr_row = addr_rowAA + rowAAChunkIndex[cy];
 454                         // get row end address:
 455                         addr_end = addr_row + rowAALen[cy]; // coded length
 456 
 457                         // reuse previous iteration position:
 458                         addr = addr_row + rowAAPos[cy];
 459 
 460                         last_addr = 0L;
 461 
 462                         while ((cx < x1) && (addr < addr_end)) {
 463                             // keep current position:
 464                             last_addr = addr;
 465 
 466                             // packed value:
 467                             packed = _unsafe.getInt(addr);
 468 
 469                             // last exclusive pixel x-coordinate:
 470                             cx1 = (packed >> 8);
 471                             // as bytes:
 472                             addr += SIZE_INT;
 473 
 474                             rx0 = cx;
 475                             if (rx0 < x0) {
 476                                 rx0 = x0;
 477                             }
 478                             rx1 = cx = cx1;
 479                             if (rx1 > x1) {
 480                                 rx1 = x1;
 481                                 cx  = x1; // fix last x
 482                             }
 483                             // adjust runLen:
 484                             runLen = rx1 - rx0;
 485 
 486                             // ensure rx1 > rx0:
 487                             if (runLen > 0) {
 488                                 packed &= 0xFF; // [0-255]
 489 
 490                                 if (packed == 0)
 491                                 {
 492                                     idx += runLen;
 493                                     continue;
 494                                 }
 495                                 val = (byte) packed; // [0-255]
 496                                 do {
 497                                     tile[idx++] = val;
 498                                 } while (--runLen > 0);
 499                             }
 500                         }
 501 
 502                         // Update last position in RLE entries:
 503                         if (last_addr != 0L) {
 504                             // Fix x0:
 505                             rowAAx0[cy]  = cx; // inclusive
 506                             // Fix position:
 507                             rowAAPos[cy] = (last_addr - addr_row);
 508                         }
 509                     }
 510                 }
 511 
 512                 // skip line end
 513                 if (cx < x1) {
 514                     idx += (x1 - cx); // > 0
 515                 }
 516 
 517                 if (DO_TRACE) {
 518                     for (int i = idx - (x1 - x0); i < idx; i++) {
 519                         System.out.print(hex(tile[i], 2));
 520                     }
 521                     System.out.println();
 522                 }
 523 
 524                 idx += skipRowPixels;
 525             }
 526         break;
 527 
 528         case 0:
 529         default:
 530             for (cy = y0; cy < y1; cy++) {
 531                 // empty line (default)
 532                 cx = x0;
 533 
 534                 if (rowAAEnc[cy] == 0) {
 535                     // Raw encoding:
 536 
 537                     final int aax1 = rowAAx1[cy]; // exclusive
 538 
 539                     // quick check if there is AA data
 540                     // corresponding to this tile [x0; x1[
 541                     if (aax1 > x0) {
 542                         final int aax0 = rowAAx0[cy]; // inclusive
 543 
 544                         if (aax0 < x1) {
 545                             // note: cx is the cursor pointer in the tile array
 546                             // (left to right)
 547                             cx = aax0;
 548 
 549                             // ensure cx >= x0
 550                             if (cx <= x0) {
 551                                 cx = x0;
 552                             } else {
 553                                 for (end = x0; end < cx; end++) {
 554                                     tile[idx++] = 0;
 555                                 }
 556                             }
 557 
 558                             // now: cx >= x0 and cx >= aax0
 559 
 560                             // Copy AA data (sum alpha data):
 561                             addr = addr_rowAA + rowAAChunkIndex[cy] + (cx - aax0);
 562 
 563                             for (end = (aax1 <= x1) ? aax1 : x1; cx < end; cx++) {
 564                                 tile[idx++] = _unsafe.getByte(addr); // [0-255]
 565                                 addr += SIZE_BYTE;
 566                             }
 567                         }
 568                     }
 569                 } else {
 570                     // RLE encoding:
 571 
 572                     // quick check if there is AA data
 573                     // corresponding to this tile [x0; x1[
 574                     if (rowAAx1[cy] > x0) { // last pixel exclusive
 575 
 576                         cx = rowAAx0[cy]; // inclusive
 577                         if (cx > x1) {
 578                             cx = x1;
 579                         }
 580 
 581                         // fill line start until first AA pixel rowAA exclusive:
 582                         for (end = x0; end < cx; end++) {
 583                             tile[idx++] = 0;
 584                         }
 585 
 586                         // get row address:
 587                         addr_row = addr_rowAA + rowAAChunkIndex[cy];
 588                         // get row end address:
 589                         addr_end = addr_row + rowAALen[cy]; // coded length
 590 
 591                         // reuse previous iteration position:
 592                         addr = addr_row + rowAAPos[cy];
 593 
 594                         last_addr = 0L;
 595 
 596                         while ((cx < x1) && (addr < addr_end)) {
 597                             // keep current position:
 598                             last_addr = addr;
 599 
 600                             // packed value:
 601                             packed = _unsafe.getInt(addr);
 602 
 603                             // last exclusive pixel x-coordinate:
 604                             cx1 = (packed >> 8);
 605                             // as bytes:
 606                             addr += SIZE_INT;
 607 
 608                             rx0 = cx;
 609                             if (rx0 < x0) {
 610                                 rx0 = x0;
 611                             }
 612                             rx1 = cx = cx1;
 613                             if (rx1 > x1) {
 614                                 rx1 = x1;
 615                                 cx  = x1; // fix last x
 616                             }
 617                             // adjust runLen:
 618                             runLen = rx1 - rx0;
 619 
 620                             // ensure rx1 > rx0:
 621                             if (runLen > 0) {
 622                                 packed &= 0xFF; // [0-255]
 623 
 624                                 val = (byte) packed; // [0-255]
 625                                 do {
 626                                     tile[idx++] = val;
 627                                 } while (--runLen > 0);
 628                             }
 629                         }
 630 
 631                         // Update last position in RLE entries:
 632                         if (last_addr != 0L) {
 633                             // Fix x0:
 634                             rowAAx0[cy]  = cx; // inclusive
 635                             // Fix position:
 636                             rowAAPos[cy] = (last_addr - addr_row);
 637                         }
 638                     }
 639                 }
 640 
 641                 // fill line end
 642                 while (cx < x1) {
 643                     tile[idx++] = 0;
 644                     cx++;
 645                 }
 646 
 647                 if (DO_TRACE) {
 648                     for (int i = idx - (x1 - x0); i < idx; i++) {
 649                         System.out.print(hex(tile[i], 2));
 650                     }
 651                     System.out.println();
 652                 }
 653 
 654                 idx += skipRowPixels;
 655             }
 656         break;
 657 
 658         case 2: // 0xFF
 659             // Fill full tile rows:
 660             Arrays.fill(tile, offset, offset + (y1 * rowstride), refVal);
 661 
 662             for (cy = y0; cy < y1; cy++) {
 663                 // empty line (default)
 664                 cx = x0;
 665 
 666                 if (rowAAEnc[cy] == 0) {
 667                     // Raw encoding:
 668 
 669                     final int aax1 = rowAAx1[cy]; // exclusive
 670 
 671                     // quick check if there is AA data
 672                     // corresponding to this tile [x0; x1[
 673                     if (aax1 > x0) {
 674                         final int aax0 = rowAAx0[cy]; // inclusive
 675 
 676                         if (aax0 < x1) {
 677                             // note: cx is the cursor pointer in the tile array
 678                             // (left to right)
 679                             cx = aax0;
 680 
 681                             // ensure cx >= x0
 682                             if (cx <= x0) {
 683                                 cx = x0;
 684                             } else {
 685                                 // fill line start until first AA pixel rowAA exclusive:
 686                                 for (end = x0; end < cx; end++) {
 687                                     tile[idx++] = 0;
 688                                 }
 689                             }
 690 
 691                             // now: cx >= x0 and cx >= aax0
 692 
 693                             // Copy AA data (sum alpha data):
 694                             addr = addr_rowAA + rowAAChunkIndex[cy] + (cx - aax0);
 695 
 696                             for (end = (aax1 <= x1) ? aax1 : x1; cx < end; cx++) {
 697                                 tile[idx++] = _unsafe.getByte(addr); // [0-255]
 698                                 addr += SIZE_BYTE;
 699                             }
 700                         }
 701                     }
 702                 } else {
 703                     // RLE encoding:
 704 
 705                     // quick check if there is AA data
 706                     // corresponding to this tile [x0; x1[
 707                     if (rowAAx1[cy] > x0) { // last pixel exclusive
 708 
 709                         cx = rowAAx0[cy]; // inclusive
 710                         if (cx > x1) {
 711                             cx = x1;
 712                         }
 713 
 714                         // fill line start until first AA pixel rowAA exclusive:
 715                         for (end = x0; end < cx; end++) {
 716                             tile[idx++] = 0;
 717                         }
 718 
 719                         // get row address:
 720                         addr_row = addr_rowAA + rowAAChunkIndex[cy];
 721                         // get row end address:
 722                         addr_end = addr_row + rowAALen[cy]; // coded length
 723 
 724                         // reuse previous iteration position:
 725                         addr = addr_row + rowAAPos[cy];
 726 
 727                         last_addr = 0L;
 728 
 729                         while ((cx < x1) && (addr < addr_end)) {
 730                             // keep current position:
 731                             last_addr = addr;
 732 
 733                             // packed value:
 734                             packed = _unsafe.getInt(addr);
 735 
 736                             // last exclusive pixel x-coordinate:
 737                             cx1 = (packed >> 8);
 738                             // as bytes:
 739                             addr += SIZE_INT;
 740 
 741                             rx0 = cx;
 742                             if (rx0 < x0) {
 743                                 rx0 = x0;
 744                             }
 745                             rx1 = cx = cx1;
 746                             if (rx1 > x1) {
 747                                 rx1 = x1;
 748                                 cx  = x1; // fix last x
 749                             }
 750                             // adjust runLen:
 751                             runLen = rx1 - rx0;
 752 
 753                             // ensure rx1 > rx0:
 754                             if (runLen > 0) {
 755                                 packed &= 0xFF; // [0-255]
 756 
 757                                 if (packed == 0xFF)
 758                                 {
 759                                     idx += runLen;
 760                                     continue;
 761                                 }
 762                                 val = (byte) packed; // [0-255]
 763                                 do {
 764                                     tile[idx++] = val;
 765                                 } while (--runLen > 0);
 766                             }
 767                         }
 768 
 769                         // Update last position in RLE entries:
 770                         if (last_addr != 0L) {
 771                             // Fix x0:
 772                             rowAAx0[cy]  = cx; // inclusive
 773                             // Fix position:
 774                             rowAAPos[cy] = (last_addr - addr_row);
 775                         }
 776                     }
 777                 }
 778 
 779                 // fill line end
 780                 while (cx < x1) {
 781                     tile[idx++] = 0;
 782                     cx++;
 783                 }
 784 
 785                 if (DO_TRACE) {
 786                     for (int i = idx - (x1 - x0); i < idx; i++) {
 787                         System.out.print(hex(tile[i], 2));
 788                     }
 789                     System.out.println();
 790                 }
 791 
 792                 idx += skipRowPixels;
 793             }
 794         }
 795 
 796         nextTile();
 797 
 798         if (DO_MONITORS) {
 799             rdrStats.mon_ptg_getAlpha.stop();
 800         }
 801     }
 802 
 803     static String hex(int v, int d) {
 804         String s = Integer.toHexString(v);
 805         while (s.length() < d) {
 806             s = "0" + s;
 807         }
 808         return s.substring(0, d);
 809     }
 810 }