/*
* Copyright (c) 2010, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.xr;
import java.awt.*;
import java.util.*;
/**
* We render non-antialiased geometry (consisting of rectangles) into a mask,
* which is later used in a composition step.
* To avoid mask-allocations of large size, MaskTileManager splits
* geometry larger than MASK_SIZE into several tiles,
* and stores the geometry in instances of MaskTile.
*
* @author Clemens Eisserer
*/
public class MaskTileManager {
public static final int MASK_SIZE = 256;
MaskTile mainTile = new MaskTile();
ArrayList<MaskTile> tileList;
int allocatedTiles = 0;
int xTiles, yTiles;
XRCompositeManager xrMgr;
XRBackend con;
int maskPixmap;
int maskPicture;
long maskGC;
int lineMaskPixmap;
int lineMaskPicture;
long drawLineGC;
long clearLineGC;
public MaskTileManager(XRCompositeManager xrMgr, int parentXid) {
tileList = new ArrayList<MaskTile>();
this.xrMgr = xrMgr;
this.con = xrMgr.getBackend();
maskPixmap = con.createPixmap(parentXid, 8, MASK_SIZE, MASK_SIZE);
maskPicture = con.createPicture(maskPixmap, XRUtils.PictStandardA8);
con.renderRectangle(maskPicture, XRUtils.PictOpClear,
new XRColor(Color.black),
0, 0, MASK_SIZE, MASK_SIZE);
maskGC = con.createGC(maskPixmap);
con.setGCExposures(maskGC, false);
lineMaskPixmap = con.createPixmap(parentXid, 8, MASK_SIZE, MASK_SIZE);
lineMaskPicture =
con.createPicture(lineMaskPixmap, XRUtils.PictStandardA8);
con.renderRectangle(lineMaskPicture, XRUtils.PictOpClear,
new XRColor(Color.black), 0, 0, MASK_SIZE, MASK_SIZE);
drawLineGC = con.createGC(lineMaskPixmap);
con.setGCExposures(drawLineGC, false);
con.setGCForeground(drawLineGC, 255);
clearLineGC = con.createGC(lineMaskPixmap);
con.setGCExposures(clearLineGC, false);
con.setGCForeground(clearLineGC, 0);
}
/**
* Adds a rectangle to the mask.
*/
public void addRect(int x, int y, int width, int height) {
mainTile.addRect(x, y, width, height);
}
/**
* Adds a line to the mask.
*/
public void addLine(int x1, int y1, int x2, int y2) {
mainTile.addLine(x1, y1, x2, y2);
}
/**
* Transfers the geometry stored (rectangles, lines) to one or more masks,
* and renders the result to the destination surface.
*/
public void fillMask(XRSurfaceData dst) {
boolean maskRequired = xrMgr.maskRequired();
if (maskRequired) {
mainTile.calculateDirtyAreas();
DirtyRegion dirtyArea = mainTile.getDirtyArea().cloneRegion();
mainTile.translate(-dirtyArea.x, -dirtyArea.y);
XRColor maskColor = xrMgr.getMaskColor();
// We don't need tiling if all geometry fits in a single tile
if (dirtyArea.getWidth() <= MASK_SIZE &&
dirtyArea.getHeight() <= MASK_SIZE)
{
compositeSingleTile(dst, mainTile, dirtyArea,
maskRequired, 0, 0, maskColor);
} else {
allocTiles(dirtyArea);
tileRects();
for (int i = 0; i < yTiles; i++) {
for (int m = 0; m < xTiles; m++) {
MaskTile tile = tileList.get(i * xTiles + m);
int tileStartX = m * MASK_SIZE;
int tileStartY = i * MASK_SIZE;
compositeSingleTile(dst, tile, dirtyArea, maskRequired,
tileStartX, tileStartY, maskColor);
}
}
}
} else {
xrMgr.XRRenderRectangles(dst, mainTile.getRects());
}
mainTile.reset();
}
/**
* Uploads aa geometry generated for maskblit/fill into the mask pixmap.
*/
public int uploadMask(int w, int h, int maskscan, int maskoff, byte[] mask) {
int maskPic = XRUtils.None;
if (mask != null) {
float maskAlpha =
xrMgr.isTexturePaintActive() ? xrMgr.getExtraAlpha() : 1.0f;
con.putMaskImage(maskPixmap, maskGC, mask, 0, 0, 0, 0,
w, h, maskoff, maskscan, maskAlpha);
maskPic = maskPicture;
} else if (xrMgr.isTexturePaintActive()) {
maskPic = xrMgr.getExtraAlphaMask();
}
return maskPic;
}
/**
* Clears the area of the mask-pixmap used for uploading aa coverage values.
*/
public void clearUploadMask(int mask, int w, int h) {
if (mask == maskPicture) {
con.renderRectangle(maskPicture, XRUtils.PictOpClear,
XRColor.NO_ALPHA, 0, 0, w, h);
}
}
/**
* Renders the rectangles provided to the mask, and does a composition
* operation with the properties set inXRCompositeManager.
*/
protected void compositeSingleTile(XRSurfaceData dst, MaskTile tile,
DirtyRegion dirtyArea,
boolean maskRequired,
int tileStartX, int tileStartY,
XRColor maskColor) {
if (tile.rects.getSize() > 0) {
DirtyRegion tileDirtyArea = tile.getDirtyArea();
int x = tileDirtyArea.x + tileStartX + dirtyArea.x;
int y = tileDirtyArea.y + tileStartY + dirtyArea.y;
int width = tileDirtyArea.x2 - tileDirtyArea.x;
int height = tileDirtyArea.y2 - tileDirtyArea.y;
width = Math.min(width, MASK_SIZE);
height = Math.min(height, MASK_SIZE);
int rectCnt = tile.rects.getSize();
if (maskRequired) {
int mask = XRUtils.None;
/*
* Optimization: When the tile only contains one rectangle, the
* composite-operation boundaries can be used as geometry
*/
if (rectCnt > 1) {
con.renderRectangles(maskPicture, XRUtils.PictOpSrc,
maskColor, tile.rects);
mask = maskPicture;
} else {
if (xrMgr.isTexturePaintActive()) {
mask = xrMgr.getExtraAlphaMask();
}
}
xrMgr.XRComposite(XRUtils.None, mask, dst.getPicture(),
x, y, tileDirtyArea.x, tileDirtyArea.y,
x, y, width, height);
/* Clear dirty rectangle of the rect-mask */
if (rectCnt > 1) {
con.renderRectangle(maskPicture, XRUtils.PictOpClear,
XRColor.NO_ALPHA,
tileDirtyArea.x, tileDirtyArea.y,
width, height);
}
tile.reset();
} else if (rectCnt > 0) {
tile.rects.translateRects(tileStartX + dirtyArea.x,
tileStartY + dirtyArea.y);
xrMgr.XRRenderRectangles(dst, tile.rects);
}
}
}
/**
* Allocates enough MaskTile instances, to cover the whole
* mask area, or resets existing ones.
*/
protected void allocTiles(DirtyRegion maskArea) {
xTiles = (maskArea.getWidth() / MASK_SIZE) + 1;
yTiles = (maskArea.getHeight() / MASK_SIZE) + 1;
int tileCnt = xTiles * yTiles;
if (tileCnt > allocatedTiles) {
for (int i = 0; i < tileCnt; i++) {
if (i < allocatedTiles) {
tileList.get(i).reset();
} else {
tileList.add(new MaskTile());
}
}
allocatedTiles = tileCnt;
}
}
/**
* Tiles the stored rectangles, if they are larger than the MASK_SIZE
*/
protected void tileRects() {
GrowableRectArray rects = mainTile.rects;
for (int i = 0; i < rects.getSize(); i++) {
int tileXStartIndex = rects.getX(i) / MASK_SIZE;
int tileYStartIndex = rects.getY(i) / MASK_SIZE;
int tileXLength =
((rects.getX(i) + rects.getWidth(i)) / MASK_SIZE + 1) -
tileXStartIndex;
int tileYLength =
((rects.getY(i) + rects.getHeight(i)) / MASK_SIZE + 1) -
tileYStartIndex;
for (int n = 0; n < tileYLength; n++) {
for (int m = 0; m < tileXLength; m++) {
int tileIndex =
xTiles * (tileYStartIndex + n) + tileXStartIndex + m;
MaskTile tile = tileList.get(tileIndex);
GrowableRectArray rectTileList = tile.getRects();
int tileArrayIndex = rectTileList.getNextIndex();
int tileStartPosX = (tileXStartIndex + m) * MASK_SIZE;
int tileStartPosY = (tileYStartIndex + n) * MASK_SIZE;
rectTileList.setX(tileArrayIndex, rects.getX(i) - tileStartPosX);
rectTileList.setY(tileArrayIndex, rects.getY(i) - tileStartPosY);
rectTileList.setWidth(tileArrayIndex, rects.getWidth(i));
rectTileList.setHeight(tileArrayIndex, rects.getHeight(i));
limitRectCoords(rectTileList, tileArrayIndex);
tile.getDirtyArea().growDirtyRegion
(rectTileList.getX(tileArrayIndex),
rectTileList.getY(tileArrayIndex),
rectTileList.getWidth(tileArrayIndex) +
rectTileList.getX(tileArrayIndex),
rectTileList.getHeight(tileArrayIndex) +
rectTileList.getY(tileArrayIndex));
}
}
}
}
/**
* Limits the rect's coordinates to the mask coordinates. The result is used
* by growDirtyRegion.
*/
private void limitRectCoords(GrowableRectArray rects, int index) {
if ((rects.getX(index) + rects.getWidth(index)) > MASK_SIZE) {
rects.setWidth(index, MASK_SIZE - rects.getX(index));
}
if ((rects.getY(index) + rects.getHeight(index)) > MASK_SIZE) {
rects.setHeight(index, MASK_SIZE - rects.getY(index));
}
if (rects.getX(index) < 0) {
rects.setWidth(index, rects.getWidth(index) + rects.getX(index));
rects.setX(index, 0);
}
if (rects.getY(index) < 0) {
rects.setHeight(index, rects.getHeight(index) + rects.getY(index));
rects.setY(index, 0);
}
}
}