1 /* 2 * Copyright (c) 2010, 2013, 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.jules; 27 28 import java.awt.*; 29 import java.awt.geom.*; 30 import java.util.concurrent.*; 31 import sun.java2d.pipe.*; 32 import sun.java2d.xr.*; 33 34 public class JulesAATileGenerator implements AATileGenerator { 35 /* Threading stuff */ 36 static final ExecutorService rasterThreadPool = 37 Executors.newCachedThreadPool(); 38 static final int CPU_CNT = Runtime.getRuntime().availableProcessors(); 39 40 static final boolean ENABLE_THREADING = false; 41 static final int THREAD_MIN = 16; 42 static final int THREAD_BEGIN = 16; 43 44 IdleTileCache tileCache; 45 TileWorker worker; 46 boolean threaded = false; 47 int rasterTileCnt; 48 49 /* Tiling */ 50 static final int TILE_SIZE = 32; 51 static final int TILE_SIZE_FP = 32 << 16; 52 int left, right, top, bottom, width, height; 53 int leftFP, topFP; 54 int tileCnt, tilesX, tilesY; 55 int currTilePos = 0; 56 TrapezoidList traps; 57 TileTrapContainer[] tiledTrapArray; 58 JulesTile mainTile; 59 60 public JulesAATileGenerator(Shape s, AffineTransform at, Region clip, 61 BasicStroke bs, boolean thin, 62 boolean normalize, int[] bbox) { 63 JulesPathBuf buf = new JulesPathBuf(); 64 65 if (bs == null) { 66 traps = buf.tesselateFill(s, at, clip); 67 } else { 68 traps = buf.tesselateStroke(s, bs, thin, false, true, at, clip); 69 } 70 71 calculateArea(bbox); 72 bucketSortTraps(); 73 calculateTypicalAlpha(); 74 75 threaded = ENABLE_THREADING && 76 rasterTileCnt >= THREAD_MIN && CPU_CNT >= 2; 77 if (threaded) { 78 tileCache = new IdleTileCache(); 79 worker = new TileWorker(this, THREAD_BEGIN, tileCache); 80 rasterThreadPool.execute(worker); 81 } 82 83 mainTile = new JulesTile(); 84 } 85 86 private static native long 87 rasterizeTrapezoidsNative(long pixmanImagePtr, int[] traps, 88 int[] trapPos, int trapCnt, 89 byte[] buffer, int xOff, int yOff); 90 91 private static native void freePixmanImgPtr(long pixmanImgPtr); 92 93 private void calculateArea(int[] bbox) { 94 tilesX = 0; 95 tilesY = 0; 96 tileCnt = 0; 97 bbox[0] = 0; 98 bbox[1] = 0; 99 bbox[2] = 0; 100 bbox[3] = 0; 101 102 if (traps.getSize() > 0) { 103 left = traps.getLeft(); 104 right = traps.getRight(); 105 top = traps.getTop(); 106 bottom = traps.getBottom(); 107 leftFP = left << 16; 108 topFP = top << 16; 109 110 bbox[0] = left; 111 bbox[1] = top; 112 bbox[2] = right; 113 bbox[3] = bottom; 114 115 width = right - left; 116 height = bottom - top; 117 118 if (width > 0 && height > 0) { 119 tilesX = (int) Math.ceil(((double) width) / TILE_SIZE); 120 tilesY = (int) Math.ceil(((double) height) / TILE_SIZE); 121 tileCnt = tilesY * tilesX; 122 tiledTrapArray = new TileTrapContainer[tileCnt]; 123 } else { 124 // If there is no area touched by the traps, don't 125 // render them. 126 traps.setSize(0); 127 } 128 } 129 } 130 131 132 private void bucketSortTraps() { 133 134 for (int i = 0; i < traps.getSize(); i++) { 135 int top = traps.getTop(i) - XRUtils.XDoubleToFixed(this.top); 136 int bottom = traps.getBottom(i) - topFP; 137 int p1xLeft = traps.getP1XLeft(i) - leftFP; 138 int p2xLeft = traps.getP2XLeft(i) - leftFP; 139 int p1xRight = traps.getP1XRight(i) - leftFP; 140 int p2xRight = traps.getP2XRight(i) - leftFP; 141 142 int minLeft = Math.min(p1xLeft, p2xLeft); 143 int maxRight = Math.max(p1xRight, p2xRight); 144 145 maxRight = maxRight > 0 ? maxRight - 1 : maxRight; 146 bottom = bottom > 0 ? bottom - 1 : bottom; 147 148 int startTileY = top / TILE_SIZE_FP; 149 int endTileY = bottom / TILE_SIZE_FP; 150 int startTileX = minLeft / TILE_SIZE_FP; 151 int endTileX = maxRight / TILE_SIZE_FP; 152 153 for (int n = startTileY; n <= endTileY; n++) { 154 155 for (int m = startTileX; m <= endTileX; m++) { 156 int trapArrayPos = n * tilesX + m; 157 TileTrapContainer trapTileList = tiledTrapArray[trapArrayPos]; 158 if (trapTileList == null) { 159 trapTileList = new TileTrapContainer(new GrowableIntArray(1, 16)); 160 tiledTrapArray[trapArrayPos] = trapTileList; 161 } 162 163 trapTileList.getTraps().addInt(i); 164 } 165 } 166 } 167 } 168 169 public void getAlpha(byte[] tileBuffer, int offset, int rowstride) { 170 JulesTile tile = null; 171 172 if (threaded) { 173 tile = worker.getPreRasterizedTile(currTilePos); 174 } 175 176 if (tile != null) { 177 System.arraycopy(tile.getImgBuffer(), 0, 178 tileBuffer, 0, tileBuffer.length); 179 tileCache.releaseTile(tile); 180 } else { 181 mainTile.setImgBuffer(tileBuffer); 182 rasterizeTile(currTilePos, mainTile); 183 } 184 185 nextTile(); 186 } 187 188 public void calculateTypicalAlpha() { 189 rasterTileCnt = 0; 190 191 for (int index = 0; index < tileCnt; index++) { 192 193 TileTrapContainer trapCont = tiledTrapArray[index]; 194 if (trapCont != null) { 195 GrowableIntArray trapList = trapCont.getTraps(); 196 197 int tileAlpha = 127; 198 if (trapList == null || trapList.getSize() == 0) { 199 tileAlpha = 0; 200 } else if (doTrapsCoverTile(trapList, index)) { 201 tileAlpha = 0xff; 202 } 203 204 if (tileAlpha == 127 || tileAlpha == 0xff) { 205 rasterTileCnt++; 206 } 207 208 trapCont.setTileAlpha(tileAlpha); 209 } 210 } 211 } 212 213 /* 214 * Optimization for large fills. Foutunatly cairo does generate an y-sorted 215 * list of trapezoids. This makes it quite simple to check whether a tile is 216 * fully covered by traps by: - Checking whether the tile is fully covered by 217 * traps vertically (trap 2 starts where trap 1 ended) - Checking whether all 218 * traps cover the tile horizontally This also works, when a single tile 219 * coveres the whole tile. 220 */ 221 protected boolean doTrapsCoverTile(GrowableIntArray trapList, int tileIndex) { 222 223 // Don't bother optimizing tiles with lots of traps, usually it won't 224 // succeed anyway. 225 if (trapList.getSize() > TILE_SIZE) { 226 return false; 227 } 228 229 int tileStartX = getXPos(tileIndex) * TILE_SIZE_FP + leftFP; 230 int tileStartY = getYPos(tileIndex) * TILE_SIZE_FP + topFP; 231 int tileEndX = tileStartX + TILE_SIZE_FP; 232 int tileEndY = tileStartY + TILE_SIZE_FP; 233 234 // Check whether first tile covers the beginning of the tile vertically 235 int firstTop = traps.getTop(trapList.getInt(0)); 236 int firstBottom = traps.getBottom(trapList.getInt(0)); 237 if (firstTop > tileStartY || firstBottom < tileStartY) { 238 return false; 239 } 240 241 // Initialize lastBottom with top, in order to pass the checks for the 242 // first iteration 243 int lastBottom = firstTop; 244 245 for (int i = 0; i < trapList.getSize(); i++) { 246 int trapPos = trapList.getInt(i); 247 if (traps.getP1XLeft(trapPos) > tileStartX || 248 traps.getP2XLeft(trapPos) > tileStartX || 249 traps.getP1XRight(trapPos) < tileEndX || 250 traps.getP2XRight(trapPos) < tileEndX || 251 traps.getTop(trapPos) != lastBottom) 252 { 253 return false; 254 } 255 lastBottom = traps.getBottom(trapPos); 256 } 257 258 // When the last trap covered the tileEnd vertically, the tile is fully 259 // covered 260 return lastBottom >= tileEndY; 261 } 262 263 public int getTypicalAlpha() { 264 if (tiledTrapArray[currTilePos] == null) { 265 return 0; 266 } else { 267 return tiledTrapArray[currTilePos].getTileAlpha(); 268 } 269 } 270 271 public void dispose() { 272 freePixmanImgPtr(mainTile.getPixmanImgPtr()); 273 274 if (threaded) { 275 tileCache.disposeConsumerResources(); 276 worker.disposeConsumerResources(); 277 } 278 } 279 280 protected JulesTile rasterizeTile(int tileIndex, JulesTile tile) { 281 int tileOffsetX = left + getXPos(tileIndex) * TILE_SIZE; 282 int tileOffsetY = top + getYPos(tileIndex) * TILE_SIZE; 283 TileTrapContainer trapCont = tiledTrapArray[tileIndex]; 284 GrowableIntArray trapList = trapCont.getTraps(); 285 286 if (trapCont.getTileAlpha() == 127) { 287 long pixmanImgPtr = 288 rasterizeTrapezoidsNative(tile.getPixmanImgPtr(), 289 traps.getTrapArray(), 290 trapList.getArray(), 291 trapList.getSize(), 292 tile.getImgBuffer(), 293 tileOffsetX, tileOffsetY); 294 tile.setPixmanImgPtr(pixmanImgPtr); 295 } 296 297 tile.setTilePos(tileIndex); 298 return tile; 299 } 300 301 protected int getXPos(int arrayPos) { 302 return arrayPos % tilesX; 303 } 304 305 protected int getYPos(int arrayPos) { 306 return arrayPos / tilesX; 307 } 308 309 public void nextTile() { 310 currTilePos++; 311 } 312 313 public int getTileHeight() { 314 return TILE_SIZE; 315 } 316 317 public int getTileWidth() { 318 return TILE_SIZE; 319 } 320 321 public int getTileCount() { 322 return tileCnt; 323 } 324 325 public TileTrapContainer getTrapContainer(int index) { 326 return tiledTrapArray[index]; 327 } 328 }