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 }