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