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.awt.geom.*;
30
31import java.security.AccessController;
32import java.security.PrivilegedAction;
33
34import sun.font.*;
35import sun.java2d.*;
36import sun.java2d.jules.*;
37import sun.java2d.loops.*;
38
39/**
40 * Manages per-application resources, e.g. the 1x1 pixmap used for solid color
41 * fill as well as per-application state e.g. the currently set source picture
42 * used for composition .
43 *
44 * @author Clemens Eisserer
45 */
46
47public class XRCompositeManager {
48    private static boolean enableGradCache = true;
49    private static XRCompositeManager instance;
50
51    private static final int SOLID = 0;
52    private static final int TEXTURE = 1;
53    private static final int GRADIENT = 2;
54
55    int srcType;
56    XRSolidSrcPict solidSrc32;
57    XRSurfaceData texture;
58    XRSurfaceData gradient;
59    int alphaMask = XRUtils.None;
60
61    XRColor solidColor = new XRColor();
62    float extraAlpha = 1.0f;
63    byte compRule = XRUtils.PictOpOver;
64    XRColor alphaColor = new XRColor();
65
66    XRSurfaceData solidSrcPict;
67    int alphaMaskPict;
68    int gradCachePixmap;
69    int gradCachePicture;
70
71    boolean xorEnabled = false;
72    int validatedPixel = 0;
73    Composite validatedComp;
74    Paint validatedPaint;
75    float validatedExtraAlpha = 1.0f;
76
77    XRBackend con;
78    MaskTileManager maskBuffer;
79    XRTextRenderer textRenderer;
80    XRMaskImage maskImage;
81
82    public static synchronized XRCompositeManager getInstance(
83            XRSurfaceData surface) {
84        if (instance == null) {
85            instance = new XRCompositeManager(surface);
86        }
87        return instance;
88    }
89
90    private XRCompositeManager(XRSurfaceData surface) {
91        con = new XRBackendNative();
92
93        String gradProp =
94            AccessController.doPrivileged(new PrivilegedAction<String>() {
95                public String run() {
96                    return System.getProperty("sun.java2d.xrgradcache");
97                }
98            });
99
100        enableGradCache = gradProp == null ||
101                          !(gradProp.equalsIgnoreCase("false") ||
102                          gradProp.equalsIgnoreCase("f"));
103
104        XRPaints.register(this);
105
106        initResources(surface);
107
108        maskBuffer = new MaskTileManager(this, surface.getXid());
109        textRenderer = new XRTextRenderer(this);
110        maskImage = new XRMaskImage(this, surface.getXid());
111    }
112
113    public void initResources(XRSurfaceData surface) {
114        int parentXid = surface.getXid();
115
116        solidSrc32 = new XRSolidSrcPict(con, parentXid);
117        setForeground(0);
118
119        int extraAlphaMask = con.createPixmap(parentXid, 8, 1, 1);
120        alphaMaskPict = con.createPicture(extraAlphaMask,
121                XRUtils.PictStandardA8);
122        con.setPictureRepeat(alphaMaskPict, XRUtils.RepeatNormal);
123        con.renderRectangle(alphaMaskPict, XRUtils.PictOpClear,
124                XRColor.NO_ALPHA, 0, 0, 1, 1);
125
126        if (enableGradCache) {
127            gradCachePixmap = con.createPixmap(parentXid, 32,
128                    MaskTileManager.MASK_SIZE, MaskTileManager.MASK_SIZE);
129            gradCachePicture = con.createPicture(gradCachePixmap,
130                    XRUtils.PictStandardARGB32);
131        }
132    }
133
134    public void setForeground(int pixel) {
135        solidColor.setColorValues(pixel, true);
136    }
137
138    public void setGradientPaint(XRSurfaceData gradient) {
139        if (this.gradient != null) {
140            con.freePicture(this.gradient.picture);
141        }
142        this.gradient = gradient;
143        srcType = GRADIENT;
144    }
145
146    public void setTexturePaint(XRSurfaceData texture) {
147        this.texture = texture;
148        this.srcType = TEXTURE;
149    }
150
151    public void XRResetPaint() {
152        srcType = SOLID;
153    }
154
155    public void validateCompositeState(Composite comp, AffineTransform xform,
156            Paint paint, SunGraphics2D sg2d) {
157        boolean updatePaint = (paint != validatedPaint) || paint == null;
158
159        // validate composite
160        if ((comp != validatedComp)) {
161            if (comp != null) {
162                setComposite(comp);
163            } else {
164                comp = AlphaComposite.getInstance(AlphaComposite.SRC_OVER);
165                setComposite(comp);
166            }
167            // the paint state is dependent on the composite state, so make
168            // sure we update the color below
169            updatePaint = true;
170            validatedComp = comp;
171        }
172
173        if (sg2d != null && (validatedPixel != sg2d.pixel  || updatePaint)) {
174            validatedPixel = sg2d.pixel;
175            setForeground(validatedPixel);
176        }
177
178        // validate paint
179        if (updatePaint) {
180            if (paint != null && sg2d != null
181                    && sg2d.paintState >= SunGraphics2D.PAINT_GRADIENT) {
182                XRPaints.setPaint(sg2d, paint);
183            } else {
184                XRResetPaint();
185            }
186            validatedPaint = paint;
187        }
188
189        if (srcType != SOLID) {
190            AffineTransform at = (AffineTransform) xform.clone();
191            try {
192                at.invert();
193            } catch (NoninvertibleTransformException e) {
194                at.setToIdentity();
195            }
196            getCurrentSource().validateAsSource(at, -1, XRUtils.ATransOpToXRQuality(sg2d.interpolationType));
197        }
198    }
199
200    private void setComposite(Composite comp) {
201        if (comp instanceof AlphaComposite) {
202            AlphaComposite aComp = (AlphaComposite) comp;
203            validatedExtraAlpha = aComp.getAlpha();
204
205            this.compRule = XRUtils.j2dAlphaCompToXR(aComp.getRule());
206            this.extraAlpha = validatedExtraAlpha;
207
208            if (extraAlpha == 1.0f) {
209                alphaMask = XRUtils.None;
210                alphaColor.alpha = XRColor.FULL_ALPHA.alpha;
211            } else {
212                alphaColor.alpha = XRColor
213                        .byteToXRColorValue((int) (extraAlpha * 255));
214                alphaMask = alphaMaskPict;
215                con.renderRectangle(alphaMaskPict, XRUtils.PictOpSrc,
216                        alphaColor, 0, 0, 1, 1);
217            }
218
219            xorEnabled = false;
220        } else if (comp instanceof XORComposite) {
221            /* XOR composite validation is handled in XRSurfaceData */
222            xorEnabled = true;
223        } else {
224            throw new InternalError(
225                    "Composite accaleration not implemented for: "
226                            + comp.getClass().getName());
227        }
228    }
229
230    public boolean maskRequired() {
231        return (!xorEnabled)
232                && ((srcType != SOLID)
233                        || (srcType == SOLID && (solidColor.alpha != 0xffff) || (extraAlpha != 1.0f)));
234    }
235
236    public void XRComposite(int src, int mask, int dst, int srcX, int srcY,
237            int maskX, int maskY, int dstX, int dstY, int width, int height) {
238        int cachedSrc = (src == XRUtils.None) ? getCurrentSource().picture : src;
239        int cachedX = srcX;
240        int cachedY = srcY;
241
242        if (enableGradCache && gradient != null
243                && cachedSrc == gradient.picture) {
244            con.renderComposite(XRUtils.PictOpSrc, gradient.picture,
245                    XRUtils.None, gradCachePicture, srcX, srcY, 0, 0, 0, 0,
246                    width, height);
247            cachedX = 0;
248            cachedY = 0;
249            cachedSrc = gradCachePicture;
250        }
251
252        con.renderComposite(compRule, cachedSrc, mask, dst, cachedX, cachedY,
253                maskX, maskY, dstX, dstY, width, height);
254    }
255
256    public void XRCompositeTraps(int dst, int srcX, int srcY,
257            TrapezoidList trapList) {
258        int renderReferenceX = 0;
259        int renderReferenceY = 0;
260
261        if (trapList.getP1YLeft(0) < trapList.getP2YLeft(0)) {
262            renderReferenceX = trapList.getP1XLeft(0);
263            renderReferenceY = trapList.getP1YLeft(0);
264        } else {
265            renderReferenceX = trapList.getP2XLeft(0);
266            renderReferenceY = trapList.getP2YLeft(0);
267        }
268
269        renderReferenceX = (int) Math.floor(XRUtils
270                .XFixedToDouble(renderReferenceX));
271        renderReferenceY = (int) Math.floor(XRUtils
272                .XFixedToDouble(renderReferenceY));
273
274        con.renderCompositeTrapezoids(compRule, getCurrentSource().picture,
275                XRUtils.PictStandardA8, dst, renderReferenceX,
276                renderReferenceY, trapList);
277    }
278
279    public void XRRenderRectangles(XRSurfaceData dst, GrowableRectArray rects) {
280        if (xorEnabled) {
281            con.GCRectangles(dst.getXid(), dst.getGC(), rects);
282        } else {
283            if (rects.getSize() == 1) {
284                con.renderRectangle(dst.getPicture(), compRule, solidColor,
285                        rects.getX(0), rects.getY(0), rects.getWidth(0), rects.getHeight(0));
286            } else {
287                con.renderRectangles(dst.getPicture(), compRule, solidColor, rects);
288            }
289        }
290    }
291
292    public void XRCompositeRectangles(XRSurfaceData dst, GrowableRectArray rects) {
293        int srcPict = getCurrentSource().picture;
294
295        for(int i=0; i < rects.getSize(); i++) {
296            int x = rects.getX(i);
297            int y = rects.getY(i);
298            int width = rects.getWidth(i);
299            int height = rects.getHeight(i);
300
301            con.renderComposite(compRule, srcPict, XRUtils.None, dst.picture, x, y, 0, 0, x, y, width, height);
302        }
303    }
304
305    protected XRSurfaceData getCurrentSource() {
306        switch(srcType) {
307        case SOLID:
308            return solidSrc32.prepareSrcPict(validatedPixel);
309        case TEXTURE:
310            return texture;
311        case GRADIENT:
312            return gradient;
313        }
314
315        return null;
316    }
317
318    public void compositeBlit(XRSurfaceData src, XRSurfaceData dst, int sx,
319            int sy, int dx, int dy, int w, int h) {
320        con.renderComposite(compRule, src.picture, alphaMask, dst.picture, sx,
321                sy, 0, 0, dx, dy, w, h);
322    }
323
324    public void compositeText(XRSurfaceData dst, int sx, int sy, int glyphSet,
325            int maskFormat, GrowableEltArray elts) {
326        /*
327         * Try to emulate the SRC blend mode with SRC_OVER.
328         * We bail out during pipe validation for cases where this is not possible.
329         */
330        byte textCompRule = (compRule != XRUtils.PictOpSrc) ? compRule : XRUtils.PictOpOver;
331        con.XRenderCompositeText(textCompRule, getCurrentSource().picture, dst.picture,
332                maskFormat, sx, sy, 0, 0, glyphSet, elts);
333    }
334
335    public XRColor getMaskColor() {
336        return !isTexturePaintActive() ? XRColor.FULL_ALPHA : getAlphaColor();
337    }
338
339    public int getExtraAlphaMask() {
340        return alphaMask;
341    }
342
343    public boolean isTexturePaintActive() {
344        return srcType == TEXTURE;
345    }
346
347    public boolean isSolidPaintActive() {
348        return srcType == SOLID;
349    }
350
351    public XRColor getAlphaColor() {
352        return alphaColor;
353    }
354
355    public XRBackend getBackend() {
356        return con;
357    }
358
359    public float getExtraAlpha() {
360        return validatedExtraAlpha;
361    }
362
363    public byte getCompRule() {
364        return compRule;
365    }
366
367    public XRTextRenderer getTextRenderer() {
368        return textRenderer;
369    }
370
371    public MaskTileManager getMaskBuffer() {
372        return maskBuffer;
373    }
374
375    public XRMaskImage getMaskImage() {
376        return maskImage;
377    }
378}
379