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