/*
 * Copyright (c) 2010, 2013, 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
/** * 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; 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); }
Transfers the geometry stored (rectangles, lines) to one or more masks, and renders the result to the destination surface.
/** * 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(); boolean maskEvaluated = XRUtils.isMaskEvaluated(xrMgr.compRule); if (maskRequired && maskEvaluated) { 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 { /* * If a mask would be required to store geometry (maskRequired) * composition has to be done rectangle-by-rectagle. */ if(xrMgr.isSolidPaintActive()) { xrMgr.XRRenderRectangles(dst, mainTile.getRects()); } else { xrMgr.XRCompositeRectangles(dst, mainTile.getRects()); } } mainTile.reset(); }
Uploads aa geometry generated for maskblit/fill into the mask pixmap.
/** * 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.
/** * 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.
/** * 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.
/** * 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
/** * 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.
/** * 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); } }
Returns:MainTile to which rectangles are added before composition.
/** * @return MainTile to which rectangles are added before composition. */
public MaskTile getMainTile() { return mainTile; } }