1 /* 2 * Copyright (c) 2007, 2015, 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 sun.java2d.pipe.AATileGenerator; 29 import jdk.internal.misc.Unsafe; 30 31 final class MarlinTileGenerator implements AATileGenerator, MarlinConst { 32 33 private static final int MAX_TILE_ALPHA_SUM = TILE_SIZE * TILE_SIZE 34 * MAX_AA_ALPHA; 35 36 private final Renderer rdr; 37 private final MarlinCache cache; 38 private int x, y; 39 40 MarlinTileGenerator(Renderer r) { 41 this.rdr = r; 42 this.cache = r.cache; 43 } 44 45 MarlinTileGenerator init() { 46 this.x = cache.bboxX0; 47 this.y = cache.bboxY0; 48 49 return this; // fluent API 50 } 51 52 /** 53 * Disposes this tile generator: 54 * clean up before reusing this instance 55 */ 56 @Override 57 public void dispose() { 58 if (doMonitors) { 59 // called from AAShapePipe.renderTiles() (render tiles end): 60 RendererContext.stats.mon_pipe_renderTiles.stop(); 61 } 62 // dispose cache: 63 cache.dispose(); 64 // dispose renderer: 65 rdr.dispose(); 66 // recycle the RendererContext instance 67 MarlinRenderingEngine.returnRendererContext(rdr.rdrCtx); 68 } 69 70 void getBbox(int bbox[]) { 71 bbox[0] = cache.bboxX0; 72 bbox[1] = cache.bboxY0; 73 bbox[2] = cache.bboxX1; 74 bbox[3] = cache.bboxY1; 75 } 76 77 /** 78 * Gets the width of the tiles that the generator batches output into. 79 * @return the width of the standard alpha tile 80 */ 81 @Override 82 public int getTileWidth() { 83 if (doMonitors) { 84 // called from AAShapePipe.renderTiles() (render tiles start): 85 RendererContext.stats.mon_pipe_renderTiles.start(); 86 } 87 return TILE_SIZE; 88 } 89 90 /** 91 * Gets the height of the tiles that the generator batches output into. 92 * @return the height of the standard alpha tile 93 */ 94 @Override 95 public int getTileHeight() { 96 return TILE_SIZE; 97 } 98 99 /** 100 * Gets the typical alpha value that will characterize the current 101 * tile. 102 * The answer may be 0x00 to indicate that the current tile has 103 * no coverage in any of its pixels, or it may be 0xff to indicate 104 * that the current tile is completely covered by the path, or any 105 * other value to indicate non-trivial coverage cases. 106 * @return 0x00 for no coverage, 0xff for total coverage, or any other 107 * value for partial coverage of the tile 108 */ 109 @Override 110 public int getTypicalAlpha() { 111 int al = cache.alphaSumInTile(x); 112 // Note: if we have a filled rectangle that doesn't end on a tile 113 // border, we could still return 0xff, even though al!=maxTileAlphaSum 114 // This is because if we return 0xff, our users will fill a rectangle 115 // starting at x,y that has width = Math.min(TILE_SIZE, bboxX1-x), 116 // and height min(TILE_SIZE,bboxY1-y), which is what should happen. 117 // However, to support this, we would have to use 2 Math.min's 118 // and 2 multiplications per tile, instead of just 2 multiplications 119 // to compute maxTileAlphaSum. The savings offered would probably 120 // not be worth it, considering how rare this case is. 121 // Note: I have not tested this, so in the future if it is determined 122 // that it is worth it, it should be implemented. Perhaps this method's 123 // interface should be changed to take arguments the width and height 124 // of the current tile. This would eliminate the 2 Math.min calls that 125 // would be needed here, since our caller needs to compute these 2 126 // values anyway. 127 final int alpha = (al == 0x00 ? 0x00 128 : (al == MAX_TILE_ALPHA_SUM ? 0xff : 0x80)); 129 if (doStats) { 130 RendererContext.stats.hist_tile_generator_alpha.add(alpha); 131 } 132 return alpha; 133 } 134 135 /** 136 * Skips the current tile and moves on to the next tile. 137 * Either this method, or the getAlpha() method should be called 138 * once per tile, but not both. 139 */ 140 @Override 141 public void nextTile() { 142 if ((x += TILE_SIZE) >= cache.bboxX1) { 143 x = cache.bboxX0; 144 y += TILE_SIZE; 145 146 if (y < cache.bboxY1) { 147 // compute for the tile line 148 // [ y; max(y + TILE_SIZE, bboxY1) ] 149 this.rdr.endRendering(y); 150 } 151 } 152 } 153 154 /** 155 * Gets the alpha coverage values for the current tile. 156 * Either this method, or the nextTile() method should be called 157 * once per tile, but not both. 158 */ 159 @Override 160 public void getAlpha(final byte tile[], final int offset, 161 final int rowstride) 162 { 163 if (cache.useRLE) { 164 getAlphaRLE(tile, offset, rowstride); 165 } else { 166 getAlphaNoRLE(tile, offset, rowstride); 167 } 168 } 169 170 /** 171 * Gets the alpha coverage values for the current tile. 172 * Either this method, or the nextTile() method should be called 173 * once per tile, but not both. 174 */ 175 private void getAlphaNoRLE(final byte tile[], final int offset, 176 final int rowstride) 177 { 178 if (doMonitors) { 179 RendererContext.stats.mon_ptg_getAlpha.start(); 180 } 181 182 // local vars for performance: 183 final MarlinCache _cache = this.cache; 184 final long[] rowAAChunkIndex = _cache.rowAAChunkIndex; 185 final int[] rowAAx0 = _cache.rowAAx0; 186 final int[] rowAAx1 = _cache.rowAAx1; 187 188 final int x0 = this.x; 189 final int x1 = FloatMath.min(x0 + TILE_SIZE, _cache.bboxX1); 190 191 // note: process tile line [0 - 32[ 192 final int y0 = 0; 193 final int y1 = FloatMath.min(this.y + TILE_SIZE, _cache.bboxY1) - this.y; 194 195 if (doLogBounds) { 196 MarlinUtils.logInfo("getAlpha = [" + x0 + " ... " + x1 197 + "[ [" + y0 + " ... " + y1 + "["); 198 } 199 200 final Unsafe _unsafe = OffHeapArray.unsafe; 201 final long SIZE = 1L; 202 final long addr_rowAA = _cache.rowAAChunk.address; 203 long addr; 204 205 final int skipRowPixels = (rowstride - (x1 - x0)); 206 207 int aax0, aax1, end; 208 int idx = offset; 209 210 for (int cy = y0, cx; cy < y1; cy++) { 211 // empty line (default) 212 cx = x0; 213 214 aax1 = rowAAx1[cy]; // exclusive 215 216 // quick check if there is AA data 217 // corresponding to this tile [x0; x1[ 218 if (aax1 > x0) { 219 aax0 = rowAAx0[cy]; // inclusive 220 221 if (aax0 < x1) { 222 // note: cx is the cursor pointer in the tile array 223 // (left to right) 224 cx = aax0; 225 226 // ensure cx >= x0 227 if (cx <= x0) { 228 cx = x0; 229 } else { 230 // fill line start until first AA pixel rowAA exclusive: 231 for (end = x0; end < cx; end++) { 232 tile[idx++] = 0; 233 } 234 } 235 236 // now: cx >= x0 but cx < aax0 (x1 < aax0) 237 238 // Copy AA data (sum alpha data): 239 addr = addr_rowAA + rowAAChunkIndex[cy] + (cx - aax0); 240 241 for (end = (aax1 <= x1) ? aax1 : x1; cx < end; cx++) { 242 // cx inside tile[x0; x1[ : 243 tile[idx++] = _unsafe.getByte(addr); // [0..255] 244 addr += SIZE; 245 } 246 } 247 } 248 249 // fill line end 250 while (cx < x1) { 251 tile[idx++] = 0; 252 cx++; 253 } 254 255 if (doTrace) { 256 for (int i = idx - (x1 - x0); i < idx; i++) { 257 System.out.print(hex(tile[i], 2)); 258 } 259 System.out.println(); 260 } 261 262 idx += skipRowPixels; 263 } 264 265 nextTile(); 266 267 if (doMonitors) { 268 RendererContext.stats.mon_ptg_getAlpha.stop(); 269 } 270 } 271 272 /** 273 * Gets the alpha coverage values for the current tile. 274 * Either this method, or the nextTile() method should be called 275 * once per tile, but not both. 276 */ 277 private void getAlphaRLE(final byte tile[], final int offset, 278 final int rowstride) 279 { 280 if (doMonitors) { 281 RendererContext.stats.mon_ptg_getAlpha.start(); 282 } 283 284 // Decode run-length encoded alpha mask data 285 // The data for row j begins at cache.rowOffsetsRLE[j] 286 // and is encoded as a set of 2-byte pairs (val, runLen) 287 // terminated by a (0, 0) pair. 288 289 // local vars for performance: 290 final MarlinCache _cache = this.cache; 291 final long[] rowAAChunkIndex = _cache.rowAAChunkIndex; 292 final int[] rowAAx0 = _cache.rowAAx0; 293 final int[] rowAAx1 = _cache.rowAAx1; 294 final int[] rowAAEnc = _cache.rowAAEnc; 295 final long[] rowAALen = _cache.rowAALen; 296 final long[] rowAAPos = _cache.rowAAPos; 297 298 final int x0 = this.x; 299 final int x1 = FloatMath.min(x0 + TILE_SIZE, _cache.bboxX1); 300 301 // note: process tile line [0 - 32[ 302 final int y0 = 0; 303 final int y1 = FloatMath.min(this.y + TILE_SIZE, _cache.bboxY1) - this.y; 304 305 if (doLogBounds) { 306 MarlinUtils.logInfo("getAlpha = [" + x0 + " ... " + x1 307 + "[ [" + y0 + " ... " + y1 + "["); 308 } 309 310 final Unsafe _unsafe = OffHeapArray.unsafe; 311 final long SIZE_BYTE = 1L; 312 final long SIZE_INT = 4L; 313 final long addr_rowAA = _cache.rowAAChunk.address; 314 long addr, addr_row, last_addr, addr_end; 315 316 final int skipRowPixels = (rowstride - (x1 - x0)); 317 318 int cx, cy, cx1; 319 int rx0, rx1, runLen, end; 320 int packed; 321 byte val; 322 int idx = offset; 323 324 for (cy = y0; cy < y1; cy++) { 325 // empty line (default) 326 cx = x0; 327 328 if (rowAAEnc[cy] == 0) { 329 // Raw encoding: 330 331 final int aax1 = rowAAx1[cy]; // exclusive 332 333 // quick check if there is AA data 334 // corresponding to this tile [x0; x1[ 335 if (aax1 > x0) { 336 final int aax0 = rowAAx0[cy]; // inclusive 337 338 if (aax0 < x1) { 339 // note: cx is the cursor pointer in the tile array 340 // (left to right) 341 cx = aax0; 342 343 // ensure cx >= x0 344 if (cx <= x0) { 345 cx = x0; 346 } else { 347 // fill line start until first AA pixel rowAA exclusive: 348 for (end = x0; end < cx; end++) { 349 tile[idx++] = 0; 350 } 351 } 352 353 // now: cx >= x0 but cx < aax0 (x1 < aax0) 354 355 // Copy AA data (sum alpha data): 356 addr = addr_rowAA + rowAAChunkIndex[cy] + (cx - aax0); 357 358 for (end = (aax1 <= x1) ? aax1 : x1; cx < end; cx++) { 359 tile[idx++] = _unsafe.getByte(addr); // [0..255] 360 addr += SIZE_BYTE; 361 } 362 } 363 } 364 } else { 365 // RLE encoding: 366 367 // quick check if there is AA data 368 // corresponding to this tile [x0; x1[ 369 if (rowAAx1[cy] > x0) { // last pixel exclusive 370 371 cx = rowAAx0[cy]; // inclusive 372 if (cx > x1) { 373 cx = x1; 374 } 375 376 // fill line start until first AA pixel rowAA exclusive: 377 for (int i = x0; i < cx; i++) { 378 tile[idx++] = 0; 379 } 380 381 // get row address: 382 addr_row = addr_rowAA + rowAAChunkIndex[cy]; 383 // get row end address: 384 addr_end = addr_row + rowAALen[cy]; // coded length 385 386 // reuse previous iteration position: 387 addr = addr_row + rowAAPos[cy]; 388 389 last_addr = 0L; 390 391 while ((cx < x1) && (addr < addr_end)) { 392 // keep current position: 393 last_addr = addr; 394 395 // packed value: 396 packed = _unsafe.getInt(addr); 397 398 // last exclusive pixel x-coordinate: 399 cx1 = (packed >> 8); 400 // as bytes: 401 addr += SIZE_INT; 402 403 rx0 = cx; 404 if (rx0 < x0) { 405 rx0 = x0; 406 } 407 rx1 = cx = cx1; 408 if (rx1 > x1) { 409 rx1 = x1; 410 cx = x1; // fix last x 411 } 412 // adjust runLen: 413 runLen = rx1 - rx0; 414 415 // ensure rx1 > rx0: 416 if (runLen > 0) { 417 val = (byte)(packed & 0xFF); // [0..255] 418 419 do { 420 tile[idx++] = val; 421 } while (--runLen > 0); 422 } 423 } 424 425 // Update last position in RLE entries: 426 if (last_addr != 0L) { 427 // Fix x0: 428 rowAAx0[cy] = cx; // inclusive 429 // Fix position: 430 rowAAPos[cy] = (last_addr - addr_row); 431 } 432 } 433 } 434 435 // fill line end 436 while (cx < x1) { 437 tile[idx++] = 0; 438 cx++; 439 } 440 441 if (doTrace) { 442 for (int i = idx - (x1 - x0); i < idx; i++) { 443 System.out.print(hex(tile[i], 2)); 444 } 445 System.out.println(); 446 } 447 448 idx += skipRowPixels; 449 } 450 451 nextTile(); 452 453 if (doMonitors) { 454 RendererContext.stats.mon_ptg_getAlpha.stop(); 455 } 456 } 457 458 static String hex(int v, int d) { 459 String s = Integer.toHexString(v); 460 while (s.length() < d) { 461 s = "0" + s; 462 } 463 return s.substring(0, d); 464 } 465 }