jdk/src/solaris/classes/sun/java2d/jules/JulesAATileGenerator.java
changeset 5579 1a5e995a710b
child 6374 e214162c907e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/java2d/jules/JulesAATileGenerator.java	Fri May 28 11:37:44 2010 -0700
@@ -0,0 +1,349 @@
+/*
+ * Copyright 2010 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.java2d.jules;
+
+import java.awt.*;
+import java.awt.geom.*;
+import java.util.concurrent.*;
+import sun.java2d.pipe.*;
+import sun.java2d.xr.*;
+
+public class JulesAATileGenerator implements AATileGenerator {
+    /* Threading stuff */
+    final static ExecutorService rasterThreadPool =
+                                          Executors.newCachedThreadPool();
+    final static int CPU_CNT = Runtime.getRuntime().availableProcessors();
+
+    final static boolean ENABLE_THREADING = false;
+    final static int THREAD_MIN = 16;
+    final static int THREAD_BEGIN = 16;
+
+    IdleTileCache tileCache;
+    TileWorker worker;
+    boolean threaded = false;
+    int rasterTileCnt;
+
+    /* Tiling */
+    final static int TILE_SIZE = 32;
+    final static int TILE_SIZE_FP = 32 << 16;
+    int left, right, top, bottom, width, height;
+    int leftFP, topFP;
+    int tileCnt, tilesX, tilesY;
+    int currTilePos = 0;
+    TrapezoidList traps;
+    TileTrapContainer[] tiledTrapArray;
+    JulesTile mainTile;
+
+    public JulesAATileGenerator(Shape s, AffineTransform at, Region clip,
+                                BasicStroke bs, boolean thin,
+                                boolean normalize, int[] bbox) {
+        JulesPathBuf buf = new JulesPathBuf();
+
+        if (bs == null) {
+            traps = buf.tesselateFill(s, at, clip);
+        } else {
+            traps = buf.tesselateStroke(s, bs, thin, false, true, at, clip);
+        }
+
+        calculateArea(bbox);
+        bucketSortTraps();
+        calculateTypicalAlpha();
+
+        threaded = ENABLE_THREADING &&
+                   rasterTileCnt >= THREAD_MIN && CPU_CNT >= 2;
+        if (threaded) {
+            tileCache = new IdleTileCache();
+            worker = new TileWorker(this, THREAD_BEGIN, tileCache);
+            rasterThreadPool.execute(worker);
+        }
+
+        mainTile = new JulesTile();
+    }
+
+    private static native long
+        rasterizeTrapezoidsNative(long pixmanImagePtr, int[] traps,
+                                  int[] trapPos, int trapCnt,
+                                  byte[] buffer, int xOff, int yOff);
+
+    private static native void freePixmanImgPtr(long pixmanImgPtr);
+
+    private void calculateArea(int[] bbox) {
+        tilesX = 0;
+        tilesY = 0;
+        tileCnt = 0;
+        bbox[0] = 0;
+        bbox[1] = 0;
+        bbox[2] = 0;
+        bbox[3] = 0;
+
+        if (traps.getSize() > 0) {
+            left = traps.getLeft();
+            right = traps.getRight();
+            top = traps.getTop();
+            bottom = traps.getBottom();
+            leftFP = left << 16;
+            topFP = top << 16;
+
+            bbox[0] = left;
+            bbox[1] = top;
+            bbox[2] = right;
+            bbox[3] = bottom;
+
+            width = right - left;
+            height = bottom - top;
+
+            if (width > 0 && height > 0) {
+                tilesX = (int) Math.ceil(((double) width) / TILE_SIZE);
+                tilesY = (int) Math.ceil(((double) height) / TILE_SIZE);
+                tileCnt = tilesY * tilesX;
+                tiledTrapArray = new TileTrapContainer[tileCnt];
+            } else {
+                // If there is no area touched by the traps, don't
+                // render them.
+                traps.setSize(0);
+            }
+        }
+    }
+
+
+    private void bucketSortTraps() {
+
+        for (int i = 0; i < traps.getSize(); i++) {
+            int top = traps.getTop(i) - XRUtils.XDoubleToFixed(this.top);
+            int bottom = traps.getBottom(i) - topFP;
+            int p1xLeft = traps.getP1XLeft(i) - leftFP;
+            int p2xLeft = traps.getP2XLeft(i) - leftFP;
+            int p1xRight = traps.getP1XRight(i) - leftFP;
+            int p2xRight = traps.getP2XRight(i) - leftFP;
+
+            int minLeft = Math.min(p1xLeft, p2xLeft);
+            int maxRight = Math.max(p1xRight, p2xRight);
+
+            maxRight = maxRight > 0 ? maxRight - 1 : maxRight;
+            bottom = bottom > 0 ? bottom - 1 : bottom;
+
+            int startTileY = top / TILE_SIZE_FP;
+            int endTileY = bottom / TILE_SIZE_FP;
+            int startTileX = minLeft / TILE_SIZE_FP;
+            int endTileX = maxRight / TILE_SIZE_FP;
+
+            for (int n = startTileY; n <= endTileY; n++) {
+
+                for (int m = startTileX; m <= endTileX; m++) {
+                    int trapArrayPos = n * tilesX + m;
+                    TileTrapContainer trapTileList = tiledTrapArray[trapArrayPos];
+                    if (trapTileList == null) {
+                        trapTileList = new TileTrapContainer(new GrowableIntArray(1, 16));
+                        tiledTrapArray[trapArrayPos] = trapTileList;
+                    }
+
+                    trapTileList.getTraps().addInt(i);
+                }
+            }
+        }
+    }
+
+    public void getAlpha(byte[] tileBuffer, int offset, int rowstride) {
+        JulesTile tile = null;
+
+        if (threaded) {
+            tile = worker.getPreRasterizedTile(currTilePos);
+        }
+
+        if (tile != null) {
+            System.arraycopy(tile.getImgBuffer(), 0,
+                             tileBuffer, 0, tileBuffer.length);
+            tileCache.releaseTile(tile);
+        } else {
+            mainTile.setImgBuffer(tileBuffer);
+            rasterizeTile(currTilePos, mainTile);
+        }
+
+        nextTile();
+    }
+
+    public void calculateTypicalAlpha() {
+        rasterTileCnt = 0;
+
+        for (int index = 0; index < tileCnt; index++) {
+
+            TileTrapContainer trapCont = tiledTrapArray[index];
+            if (trapCont != null) {
+                GrowableIntArray trapList = trapCont.getTraps();
+
+                int tileAlpha = 127;
+                if (trapList == null || trapList.getSize() == 0) {
+                    tileAlpha = 0;
+                } else if (doTrapsCoverTile(trapList, index)) {
+                    tileAlpha = 0xff;
+                }
+
+                if (tileAlpha == 127 || tileAlpha == 0xff) {
+                    rasterTileCnt++;
+                }
+
+                trapCont.setTileAlpha(tileAlpha);
+            }
+        }
+    }
+
+    /*
+     * Optimization for large fills. Foutunatly cairo does generate an y-sorted
+     * list of trapezoids. This makes it quite simple to check wether a tile is
+     * fully covered by traps by: - Checking wether the tile is fully covered by
+     * traps vertically (trap 2 starts where trap 1 ended) - Checking wether all
+     * traps cover the tile horizontally This also works, when a single tile
+     * coveres the whole tile.
+     */
+    protected boolean doTrapsCoverTile(GrowableIntArray trapList, int tileIndex) {
+
+        // Don't bother optimizing tiles with lots of traps, usually it won't
+        // succeed anyway.
+        if (trapList.getSize() > TILE_SIZE) {
+            return false;
+        }
+
+        int tileStartX = getXPos(tileIndex) * TILE_SIZE_FP + leftFP;
+        int tileStartY = getYPos(tileIndex) * TILE_SIZE_FP + topFP;
+        int tileEndX = tileStartX + TILE_SIZE_FP;
+        int tileEndY = tileStartY + TILE_SIZE_FP;
+
+        // Check wether first tile covers the beginning of the tile vertically
+        int firstTop = traps.getTop(trapList.getInt(0));
+        int firstBottom = traps.getBottom(trapList.getInt(0));
+        if (firstTop > tileStartY || firstBottom < tileStartY) {
+            return false;
+        }
+
+        // Initialize lastBottom with top, in order to pass the checks for the
+        // first iteration
+        int lastBottom = firstTop;
+
+        for (int i = 0; i < trapList.getSize(); i++) {
+            int trapPos = trapList.getInt(i);
+            if (traps.getP1XLeft(trapPos) > tileStartX ||
+                traps.getP2XLeft(trapPos) > tileStartX ||
+                traps.getP1XRight(trapPos) < tileEndX  ||
+                traps.getP2XRight(trapPos) < tileEndX  ||
+                 traps.getTop(trapPos) != lastBottom)
+            {
+                return false;
+            }
+            lastBottom = traps.getBottom(trapPos);
+        }
+
+        // When the last trap covered the tileEnd vertically, the tile is fully
+        // covered
+        return lastBottom >= tileEndY;
+    }
+
+    public int getTypicalAlpha() {
+        if (tiledTrapArray[currTilePos] == null) {
+            return 0;
+        } else {
+            return tiledTrapArray[currTilePos].getTileAlpha();
+        }
+    }
+
+    public void dispose() {
+        freePixmanImgPtr(mainTile.getPixmanImgPtr());
+
+        if (threaded) {
+            tileCache.disposeConsumerResources();
+            worker.disposeConsumerResources();
+        }
+    }
+
+    protected JulesTile rasterizeTile(int tileIndex, JulesTile tile) {
+        int tileOffsetX = left + getXPos(tileIndex) * TILE_SIZE;
+        int tileOffsetY = top + getYPos(tileIndex) * TILE_SIZE;
+        TileTrapContainer trapCont = tiledTrapArray[tileIndex];
+        GrowableIntArray trapList = trapCont.getTraps();
+
+        if (trapCont.getTileAlpha() == 127) {
+            long pixmanImgPtr =
+                 rasterizeTrapezoidsNative(tile.getPixmanImgPtr(),
+                                           traps.getTrapArray(),
+                                           trapList.getArray(),
+                                           trapList.getSize(),
+                                           tile.getImgBuffer(),
+                                           tileOffsetX, tileOffsetY);
+            tile.setPixmanImgPtr(pixmanImgPtr);
+        }
+
+        tile.setTilePos(tileIndex);
+        return tile;
+    }
+
+    protected int getXPos(int arrayPos) {
+        return arrayPos % tilesX;
+    }
+
+    protected int getYPos(int arrayPos) {
+        return arrayPos / tilesX;
+    }
+
+    public void nextTile() {
+        currTilePos++;
+    }
+
+    public int getTileHeight() {
+        return TILE_SIZE;
+    }
+
+    public int getTileWidth() {
+        return TILE_SIZE;
+    }
+
+    public int getTileCount() {
+        return tileCnt;
+    }
+
+    public TileTrapContainer getTrapContainer(int index) {
+        return tiledTrapArray[index];
+    }
+}
+
+class TileTrapContainer {
+    int tileAlpha;
+    GrowableIntArray traps;
+
+    public TileTrapContainer(GrowableIntArray traps) {
+        this.traps = traps;
+    }
+
+    public void setTileAlpha(int tileAlpha) {
+        this.tileAlpha = tileAlpha;
+    }
+
+    public int getTileAlpha() {
+        return tileAlpha;
+    }
+
+    public GrowableIntArray getTraps() {
+        return traps;
+    }
+}