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 }