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
26package sun.java2d.xr;
27
28import java.awt.*;
29import 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
41public 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}
312