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.xr;
27
28 import java.awt.*;
29 import java.util.*;
30
31 /**
32 * We render non-antialiased geometry (consisting of rectangles) into a mask,
33 * which is later used in a composition step.
34 * To avoid mask-allocations of large size, MaskTileManager splits
35 * geometry larger than MASK_SIZE into several tiles,
36 * and stores the geometry in instances of MaskTile.
37 *
38 * @author Clemens Eisserer
39 */
40
41 public class MaskTileManager {
42
43 public static final int MASK_SIZE = 256;
44
45 MaskTile mainTile = new MaskTile();
46
47 ArrayList<MaskTile> tileList;
48 int allocatedTiles = 0;
49 int xTiles, yTiles;
50
51 XRCompositeManager xrMgr;
52 XRBackend con;
53
54 int maskPixmap;
55 int maskPicture;
56 long maskGC;
57
58 public MaskTileManager(XRCompositeManager xrMgr, int parentXid) {
59 tileList = new ArrayList<MaskTile>();
60 this.xrMgr = xrMgr;
61 this.con = xrMgr.getBackend();
62
63 maskPixmap = con.createPixmap(parentXid, 8, MASK_SIZE, MASK_SIZE);
64 maskPicture = con.createPicture(maskPixmap, XRUtils.PictStandardA8);
65 con.renderRectangle(maskPicture, XRUtils.PictOpClear,
66 new XRColor(Color.black),
67 0, 0, MASK_SIZE, MASK_SIZE);
68 maskGC = con.createGC(maskPixmap);
69 con.setGCExposures(maskGC, false);
70 }
71
72 /**
73 * Transfers the geometry stored (rectangles, lines) to one or more masks,
74 * and renders the result to the destination surface.
75 */
76 public void fillMask(XRSurfaceData dst) {
77
78 boolean maskRequired = xrMgr.maskRequired();
79 boolean maskEvaluated = XRUtils.isMaskEvaluated(xrMgr.compRule);
80
81 if (maskRequired && maskEvaluated) {
82 mainTile.calculateDirtyAreas();
83 DirtyRegion dirtyArea = mainTile.getDirtyArea().cloneRegion();
84 mainTile.translate(-dirtyArea.x, -dirtyArea.y);
85
86 XRColor maskColor = xrMgr.getMaskColor();
87
88 // We don't need tiling if all geometry fits in a single tile
89 if (dirtyArea.getWidth() <= MASK_SIZE &&
90 dirtyArea.getHeight() <= MASK_SIZE)
91 {
92 compositeSingleTile(dst, mainTile, dirtyArea,
93 maskRequired, 0, 0, maskColor);
94 } else {
95 allocTiles(dirtyArea);
96 tileRects();
97
98 for (int i = 0; i < yTiles; i++) {
99 for (int m = 0; m < xTiles; m++) {
100 MaskTile tile = tileList.get(i * xTiles + m);
101
102 int tileStartX = m * MASK_SIZE;
103 int tileStartY = i * MASK_SIZE;
104 compositeSingleTile(dst, tile, dirtyArea, maskRequired,
105 tileStartX, tileStartY, maskColor);
106 }
107 }
108 }
109 } else {
110 /*
111 * If a mask would be required to store geometry (maskRequired)
112 * composition has to be done rectangle-by-rectagle.
113 */
114 if(xrMgr.isSolidPaintActive()) {
115 xrMgr.XRRenderRectangles(dst, mainTile.getRects());
116 } else {
117 xrMgr.XRCompositeRectangles(dst, mainTile.getRects());
118 }
119 }
120
121 mainTile.reset();
122 }
123
124 /**
125 * Uploads aa geometry generated for maskblit/fill into the mask pixmap.
126 */
127 public int uploadMask(int w, int h, int maskscan, int maskoff, byte[] mask) {
128 int maskPic = XRUtils.None;
129
130 if (mask != null) {
131 float maskAlpha =
132 xrMgr.isTexturePaintActive() ? xrMgr.getExtraAlpha() : 1.0f;
133 con.putMaskImage(maskPixmap, maskGC, mask, 0, 0, 0, 0,
134 w, h, maskoff, maskscan, maskAlpha);
135 maskPic = maskPicture;
136 } else if (xrMgr.isTexturePaintActive()) {
137 maskPic = xrMgr.getExtraAlphaMask();
138 }
139
140 return maskPic;
141 }
142
143 /**
144 * Clears the area of the mask-pixmap used for uploading aa coverage values.
145 */
146 public void clearUploadMask(int mask, int w, int h) {
147 if (mask == maskPicture) {
148 con.renderRectangle(maskPicture, XRUtils.PictOpClear,
149 XRColor.NO_ALPHA, 0, 0, w, h);
150 }
151 }
152
153
154 /**
155 * Renders the rectangles provided to the mask, and does a composition
156 * operation with the properties set inXRCompositeManager.
157 */
158 protected void compositeSingleTile(XRSurfaceData dst, MaskTile tile,
159 DirtyRegion dirtyArea,
160 boolean maskRequired,
161 int tileStartX, int tileStartY,
162 XRColor maskColor) {
163 if (tile.rects.getSize() > 0) {
164 DirtyRegion tileDirtyArea = tile.getDirtyArea();
165
166 int x = tileDirtyArea.x + tileStartX + dirtyArea.x;
167 int y = tileDirtyArea.y + tileStartY + dirtyArea.y;
168 int width = tileDirtyArea.x2 - tileDirtyArea.x;
169 int height = tileDirtyArea.y2 - tileDirtyArea.y;
170 width = Math.min(width, MASK_SIZE);
171 height = Math.min(height, MASK_SIZE);
172
173 int rectCnt = tile.rects.getSize();
174
175 if (maskRequired) {
176 int mask = XRUtils.None;
177
178 /*
179 * Optimization: When the tile only contains one rectangle, the
180 * composite-operation boundaries can be used as geometry
181 */
182 if (rectCnt > 1) {
183 con.renderRectangles(maskPicture, XRUtils.PictOpSrc,
184 maskColor, tile.rects);
185 mask = maskPicture;
186 } else {
187 if (xrMgr.isTexturePaintActive()) {
188 mask = xrMgr.getExtraAlphaMask();
189 }
190 }
191
192 xrMgr.XRComposite(XRUtils.None, mask, dst.getPicture(),
193 x, y, tileDirtyArea.x, tileDirtyArea.y,
194 x, y, width, height);
195
196 /* Clear dirty rectangle of the rect-mask */
197 if (rectCnt > 1) {
198 con.renderRectangle(maskPicture, XRUtils.PictOpClear,
199 XRColor.NO_ALPHA,
200 tileDirtyArea.x, tileDirtyArea.y,
201 width, height);
202 }
203
204 tile.reset();
205 } else if (rectCnt > 0) {
206 tile.rects.translateRects(tileStartX + dirtyArea.x,
207 tileStartY + dirtyArea.y);
208 xrMgr.XRRenderRectangles(dst, tile.rects);
209 }
210 }
211 }
212
213
214 /**
215 * Allocates enough MaskTile instances, to cover the whole
216 * mask area, or resets existing ones.
217 */
218 protected void allocTiles(DirtyRegion maskArea) {
219 xTiles = (maskArea.getWidth() / MASK_SIZE) + 1;
220 yTiles = (maskArea.getHeight() / MASK_SIZE) + 1;
221 int tileCnt = xTiles * yTiles;
222
223 if (tileCnt > allocatedTiles) {
224 for (int i = 0; i < tileCnt; i++) {
225 if (i < allocatedTiles) {
226 tileList.get(i).reset();
227 } else {
228 tileList.add(new MaskTile());
229 }
230 }
231
232 allocatedTiles = tileCnt;
233 }
234 }
235
236 /**
237 * Tiles the stored rectangles, if they are larger than the MASK_SIZE
238 */
239 protected void tileRects() {
240 GrowableRectArray rects = mainTile.rects;
241
242 for (int i = 0; i < rects.getSize(); i++) {
243 int tileXStartIndex = rects.getX(i) / MASK_SIZE;
244 int tileYStartIndex = rects.getY(i) / MASK_SIZE;
245 int tileXLength =
246 ((rects.getX(i) + rects.getWidth(i)) / MASK_SIZE + 1) -
247 tileXStartIndex;
248 int tileYLength =
249 ((rects.getY(i) + rects.getHeight(i)) / MASK_SIZE + 1) -
250 tileYStartIndex;
251
252 for (int n = 0; n < tileYLength; n++) {
253 for (int m = 0; m < tileXLength; m++) {
254
255 int tileIndex =
256 xTiles * (tileYStartIndex + n) + tileXStartIndex + m;
257 MaskTile tile = tileList.get(tileIndex);
258
259 GrowableRectArray rectTileList = tile.getRects();
260 int tileArrayIndex = rectTileList.getNextIndex();
261
262 int tileStartPosX = (tileXStartIndex + m) * MASK_SIZE;
263 int tileStartPosY = (tileYStartIndex + n) * MASK_SIZE;
264
265 rectTileList.setX(tileArrayIndex, rects.getX(i) - tileStartPosX);
266 rectTileList.setY(tileArrayIndex, rects.getY(i) - tileStartPosY);
267 rectTileList.setWidth(tileArrayIndex, rects.getWidth(i));
268 rectTileList.setHeight(tileArrayIndex, rects.getHeight(i));
269
270 limitRectCoords(rectTileList, tileArrayIndex);
271
272 tile.getDirtyArea().growDirtyRegion
273 (rectTileList.getX(tileArrayIndex),
274 rectTileList.getY(tileArrayIndex),
275 rectTileList.getWidth(tileArrayIndex) +
276 rectTileList.getX(tileArrayIndex),
277 rectTileList.getHeight(tileArrayIndex) +
278 rectTileList.getY(tileArrayIndex));
279 }
280 }
281 }
282 }
283
284 /**
285 * Limits the rect's coordinates to the mask coordinates. The result is used
286 * by growDirtyRegion.
287 */
288 private void limitRectCoords(GrowableRectArray rects, int index) {
289 if ((rects.getX(index) + rects.getWidth(index)) > MASK_SIZE) {
290 rects.setWidth(index, MASK_SIZE - rects.getX(index));
291 }
292 if ((rects.getY(index) + rects.getHeight(index)) > MASK_SIZE) {
293 rects.setHeight(index, MASK_SIZE - rects.getY(index));
294 }
295 if (rects.getX(index) < 0) {
296 rects.setWidth(index, rects.getWidth(index) + rects.getX(index));
297 rects.setX(index, 0);
298 }
299 if (rects.getY(index) < 0) {
300 rects.setHeight(index, rects.getHeight(index) + rects.getY(index));
301 rects.setY(index, 0);
302 }
303 }
304
305 /**
306 * @return MainTile to which rectangles are added before composition.
307 */
308 public MaskTile getMainTile() {
309 return mainTile;
310 }
311 }