1/*
2 * Copyright (c) 1997, 2016, 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 */
25package sun.java2d.pipe;
26
27import java.awt.BasicStroke;
28import java.awt.Rectangle;
29import java.awt.Shape;
30import java.awt.geom.Rectangle2D;
31import sun.awt.SunHints;
32import sun.java2d.ReentrantContext;
33import sun.java2d.ReentrantContextProvider;
34import sun.java2d.ReentrantContextProviderTL;
35import sun.java2d.SunGraphics2D;
36
37/**
38 * This class is used to convert raw geometry into 8-bit alpha tiles
39 * using an AATileGenerator for application by the next stage of
40 * the pipeline.
41 * This class sets up the Generator and computes the alpha tiles
42 * and then passes them on to a CompositePipe object for painting.
43 */
44public final class AAShapePipe
45    implements ShapeDrawPipe, ParallelogramPipe
46{
47    static final RenderingEngine RDR_ENGINE = RenderingEngine.getInstance();
48
49    // Per-thread TileState (~1K very small so do not use any Weak Reference)
50    private static final ReentrantContextProvider<TileState> TILE_STATE_PROVIDER =
51            new ReentrantContextProviderTL<TileState>(
52                    ReentrantContextProvider.REF_HARD)
53            {
54                @Override
55                protected TileState newContext() {
56                    return new TileState();
57                }
58            };
59
60    final CompositePipe outpipe;
61
62    public AAShapePipe(CompositePipe pipe) {
63        outpipe = pipe;
64    }
65
66    @Override
67    public void draw(SunGraphics2D sg, Shape s) {
68        final BasicStroke bs;
69
70        if (sg.stroke instanceof BasicStroke) {
71            bs = (BasicStroke) sg.stroke;
72        } else {
73            s = sg.stroke.createStrokedShape(s);
74            bs = null;
75        }
76
77        renderPath(sg, s, bs);
78    }
79
80    @Override
81    public void fill(SunGraphics2D sg, Shape s) {
82        renderPath(sg, s, null);
83    }
84
85    @Override
86    public void fillParallelogram(SunGraphics2D sg,
87                                  double ux1, double uy1,
88                                  double ux2, double uy2,
89                                  double x, double y,
90                                  double dx1, double dy1,
91                                  double dx2, double dy2)
92    {
93        final TileState ts = TILE_STATE_PROVIDER.acquire();
94        try {
95            final int[] abox = ts.abox;
96
97            final AATileGenerator aatg =
98                RDR_ENGINE.getAATileGenerator(x, y, dx1, dy1, dx2, dy2, 0, 0,
99                                                sg.getCompClip(), abox);
100            if (aatg != null) {
101                renderTiles(sg, ts.computeBBox(ux1, uy1, ux2, uy2),
102                            aatg, abox, ts);
103            }
104        } finally {
105            TILE_STATE_PROVIDER.release(ts);
106        }
107    }
108
109    @Override
110    public void drawParallelogram(SunGraphics2D sg,
111                                  double ux1, double uy1,
112                                  double ux2, double uy2,
113                                  double x, double y,
114                                  double dx1, double dy1,
115                                  double dx2, double dy2,
116                                  double lw1, double lw2)
117    {
118        final TileState ts = TILE_STATE_PROVIDER.acquire();
119        try {
120            final int[] abox = ts.abox;
121
122            final AATileGenerator aatg =
123                RDR_ENGINE.getAATileGenerator(x, y, dx1, dy1, dx2, dy2, lw1,
124                                                lw2, sg.getCompClip(), abox);
125            if (aatg != null) {
126                // Note that bbox is of the original shape, not the wide path.
127                // This is appropriate for handing to Paint methods...
128                renderTiles(sg, ts.computeBBox(ux1, uy1, ux2, uy2),
129                            aatg, abox, ts);
130            }
131        } finally {
132            TILE_STATE_PROVIDER.release(ts);
133        }
134    }
135
136    public void renderPath(SunGraphics2D sg, Shape s, BasicStroke bs) {
137        final boolean adjust = (bs != null &&
138                          sg.strokeHint != SunHints.INTVAL_STROKE_PURE);
139        final boolean thin = (sg.strokeState <= SunGraphics2D.STROKE_THINDASHED);
140
141        final TileState ts = TILE_STATE_PROVIDER.acquire();
142        try {
143            final int[] abox = ts.abox;
144
145            final AATileGenerator aatg =
146                RDR_ENGINE.getAATileGenerator(s, sg.transform, sg.getCompClip(),
147                                                bs, thin, adjust, abox);
148            if (aatg != null) {
149                renderTiles(sg, s, aatg, abox, ts);
150            }
151        } finally {
152            TILE_STATE_PROVIDER.release(ts);
153        }
154    }
155
156    public void renderTiles(SunGraphics2D sg, Shape s,
157                            final AATileGenerator aatg,
158                            final int[] abox, final TileState ts)
159    {
160        Object context = null;
161        try {
162            // reentrance: outpipe may also use AAShapePipe:
163            context = outpipe.startSequence(sg, s,
164                                            ts.computeDevBox(abox),
165                                            abox);
166
167            // copy of int[] abox as local variables for performance:
168            final int x0 = abox[0];
169            final int y0 = abox[1];
170            final int x1 = abox[2];
171            final int y1 = abox[3];
172
173            final int tw = aatg.getTileWidth();
174            final int th = aatg.getTileHeight();
175
176            // get tile from thread local storage:
177            final byte[] alpha = ts.getAlphaTile(tw * th);
178            byte[] atile;
179
180            for (int y = y0; y < y1; y += th) {
181                final int h = Math.min(th, y1 - y);
182
183                for (int x = x0; x < x1; x += tw) {
184                    final int w = Math.min(tw, x1 - x);
185
186                    final int a = aatg.getTypicalAlpha();
187
188                    if (a == 0x00 || !outpipe.needTile(context, x, y, w, h)) {
189                        aatg.nextTile();
190                        outpipe.skipTile(context, x, y);
191                        continue;
192                    }
193                    if (a == 0xff) {
194                        atile = null;
195                        aatg.nextTile();
196                    } else {
197                        atile = alpha;
198                        aatg.getAlpha(alpha, 0, tw);
199                    }
200
201                    outpipe.renderPathTile(context, atile, 0, tw, x, y, w, h);
202                }
203            }
204        } finally {
205            aatg.dispose();
206            if (context != null) {
207                outpipe.endSequence(context);
208            }
209        }
210    }
211
212    // Tile state used by AAShapePipe
213    static final class TileState extends ReentrantContext {
214        // cached tile (32 x 32 tile by default)
215        private byte[] theTile = new byte[32 * 32];
216        // dirty aabox array
217        final int[] abox = new int[4];
218        // dirty bbox rectangle
219        private final Rectangle dev = new Rectangle();
220        // dirty bbox rectangle2D.Double
221        private final Rectangle2D.Double bbox2D = new Rectangle2D.Double();
222
223        byte[] getAlphaTile(int len) {
224            byte[] t = theTile;
225            if (t.length < len) {
226                // create a larger tile and may free current theTile (too small)
227                theTile = t = new byte[len];
228            }
229            return t;
230        }
231
232        Rectangle computeDevBox(final int[] abox) {
233            final Rectangle box = this.dev;
234            box.x = abox[0];
235            box.y = abox[1];
236            box.width = abox[2] - abox[0];
237            box.height = abox[3] - abox[1];
238            return box;
239        }
240
241        Rectangle2D computeBBox(double ux1, double uy1,
242                                double ux2, double uy2)
243        {
244            if ((ux2 -= ux1) < 0.0) {
245                ux1 += ux2;
246                ux2 = -ux2;
247            }
248            if ((uy2 -= uy1) < 0.0) {
249                uy1 += uy2;
250                uy2 = -uy2;
251            }
252            final Rectangle2D.Double box = this.bbox2D;
253            box.x = ux1;
254            box.y = uy1;
255            box.width = ux2;
256            box.height = uy2;
257            return box;
258        }
259    }
260}
261