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 }