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 }