1/*
2 * Copyright (c) 1999, 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 */
25
26package sun.java2d.pipe;
27
28import java.awt.Shape;
29import java.awt.BasicStroke;
30import java.awt.geom.AffineTransform;
31import java.awt.geom.PathIterator;
32import java.awt.geom.RoundRectangle2D;
33import java.awt.geom.Ellipse2D;
34import java.awt.geom.Arc2D;
35import java.awt.geom.Path2D;
36import sun.java2d.SunGraphics2D;
37import sun.java2d.SurfaceData;
38import sun.java2d.loops.FillParallelogram;
39import sun.java2d.loops.DrawParallelogram;
40import sun.awt.SunHints;
41
42public class LoopPipe
43    implements PixelDrawPipe,
44               PixelFillPipe,
45               ParallelogramPipe,
46               ShapeDrawPipe,
47               LoopBasedPipe
48{
49    static final RenderingEngine RenderEngine = RenderingEngine.getInstance();
50
51    public void drawLine(SunGraphics2D sg2d,
52                         int x1, int y1, int x2, int y2)
53    {
54        int tX = sg2d.transX;
55        int tY = sg2d.transY;
56        sg2d.loops.drawLineLoop.DrawLine(sg2d, sg2d.getSurfaceData(),
57                                         x1 + tX, y1 + tY,
58                                         x2 + tX, y2 + tY);
59    }
60
61    public void drawRect(SunGraphics2D sg2d,
62                         int x, int y, int width, int height)
63    {
64        sg2d.loops.drawRectLoop.DrawRect(sg2d, sg2d.getSurfaceData(),
65                                         x + sg2d.transX,
66                                         y + sg2d.transY,
67                                         width, height);
68    }
69
70    public void drawRoundRect(SunGraphics2D sg2d,
71                              int x, int y, int width, int height,
72                              int arcWidth, int arcHeight)
73    {
74        sg2d.shapepipe.draw(sg2d,
75                            new RoundRectangle2D.Float(x, y, width, height,
76                                                       arcWidth, arcHeight));
77    }
78
79    public void drawOval(SunGraphics2D sg2d,
80                         int x, int y, int width, int height)
81    {
82        sg2d.shapepipe.draw(sg2d, new Ellipse2D.Float(x, y, width, height));
83    }
84
85    public void drawArc(SunGraphics2D sg2d,
86                        int x, int y, int width, int height,
87                        int startAngle, int arcAngle)
88    {
89        sg2d.shapepipe.draw(sg2d, new Arc2D.Float(x, y, width, height,
90                                                  startAngle, arcAngle,
91                                                  Arc2D.OPEN));
92    }
93
94    public void drawPolyline(SunGraphics2D sg2d,
95                             int xPoints[], int yPoints[],
96                             int nPoints)
97    {
98        int nPointsArray[] = { nPoints };
99        sg2d.loops.drawPolygonsLoop.DrawPolygons(sg2d, sg2d.getSurfaceData(),
100                                                 xPoints, yPoints,
101                                                 nPointsArray, 1,
102                                                 sg2d.transX, sg2d.transY,
103                                                 false);
104    }
105
106    public void drawPolygon(SunGraphics2D sg2d,
107                            int xPoints[], int yPoints[],
108                            int nPoints)
109    {
110        int nPointsArray[] = { nPoints };
111        sg2d.loops.drawPolygonsLoop.DrawPolygons(sg2d, sg2d.getSurfaceData(),
112                                                 xPoints, yPoints,
113                                                 nPointsArray, 1,
114                                                 sg2d.transX, sg2d.transY,
115                                                 true);
116    }
117
118    public void fillRect(SunGraphics2D sg2d,
119                         int x, int y, int width, int height)
120    {
121        sg2d.loops.fillRectLoop.FillRect(sg2d, sg2d.getSurfaceData(),
122                                         x + sg2d.transX,
123                                         y + sg2d.transY,
124                                         width, height);
125    }
126
127    public void fillRoundRect(SunGraphics2D sg2d,
128                              int x, int y, int width, int height,
129                              int arcWidth, int arcHeight)
130    {
131        sg2d.shapepipe.fill(sg2d,
132                            new RoundRectangle2D.Float(x, y, width, height,
133                                                       arcWidth, arcHeight));
134    }
135
136    public void fillOval(SunGraphics2D sg2d,
137                         int x, int y, int width, int height)
138    {
139        sg2d.shapepipe.fill(sg2d, new Ellipse2D.Float(x, y, width, height));
140    }
141
142    public void fillArc(SunGraphics2D sg2d,
143                        int x, int y, int width, int height,
144                        int startAngle, int arcAngle)
145    {
146        sg2d.shapepipe.fill(sg2d, new Arc2D.Float(x, y, width, height,
147                                                  startAngle, arcAngle,
148                                                  Arc2D.PIE));
149    }
150
151    public void fillPolygon(SunGraphics2D sg2d,
152                            int xPoints[], int yPoints[],
153                            int nPoints)
154    {
155        ShapeSpanIterator sr = getFillSSI(sg2d);
156
157        try {
158            sr.setOutputArea(sg2d.getCompClip());
159            sr.appendPoly(xPoints, yPoints, nPoints, sg2d.transX, sg2d.transY);
160            fillSpans(sg2d, sr);
161        } finally {
162            sr.dispose();
163        }
164    }
165
166
167    public void draw(SunGraphics2D sg2d, Shape s) {
168        if (sg2d.strokeState == SunGraphics2D.STROKE_THIN) {
169            Path2D.Float p2df;
170            int transX;
171            int transY;
172            if (sg2d.transformState <= SunGraphics2D.TRANSFORM_INT_TRANSLATE) {
173                if (s instanceof Path2D.Float) {
174                    p2df = (Path2D.Float)s;
175                } else {
176                    p2df = new Path2D.Float(s);
177                }
178                transX = sg2d.transX;
179                transY = sg2d.transY;
180            } else {
181                p2df = new Path2D.Float(s, sg2d.transform);
182                transX = 0;
183                transY = 0;
184            }
185            sg2d.loops.drawPathLoop.DrawPath(sg2d, sg2d.getSurfaceData(),
186                                             transX, transY, p2df);
187            return;
188        }
189
190        if (sg2d.strokeState == SunGraphics2D.STROKE_CUSTOM) {
191            fill(sg2d, sg2d.stroke.createStrokedShape(s));
192            return;
193        }
194
195        ShapeSpanIterator sr = getStrokeSpans(sg2d, s);
196
197        try {
198            fillSpans(sg2d, sr);
199        } finally {
200            sr.dispose();
201        }
202    }
203
204    /**
205     * Return a ShapeSpanIterator instance that normalizes as
206     * appropriate for a fill operation as per the settings in
207     * the specified SunGraphics2D object.
208     *
209     * The ShapeSpanIterator will be newly constructed and ready
210     * to start taking in geometry.
211     *
212     * Note that the caller is responsible for calling dispose()
213     * on the returned ShapeSpanIterator inside a try/finally block:
214     * <pre>
215     *     ShapeSpanIterator ssi = LoopPipe.getFillSSI(sg2d);
216     *     try {
217     *         ssi.setOutputArea(clip);
218     *         ssi.appendPath(...); // or appendPoly
219     *         // iterate the spans from ssi and operate on them
220     *     } finally {
221     *         ssi.dispose();
222     *     }
223     * </pre>
224     */
225    public static ShapeSpanIterator getFillSSI(SunGraphics2D sg2d) {
226        boolean adjust = ((sg2d.stroke instanceof BasicStroke) &&
227                          sg2d.strokeHint != SunHints.INTVAL_STROKE_PURE);
228        return new ShapeSpanIterator(adjust);
229    }
230
231    /*
232     * Return a ShapeSpanIterator ready to iterate the spans of the wide
233     * outline of Shape s using the attributes of the SunGraphics2D
234     * object.
235     *
236     * The ShapeSpanIterator returned will be fully constructed
237     * and filled with the geometry from the Shape widened by the
238     * appropriate BasicStroke and normalization parameters taken
239     * from the SunGraphics2D object and be ready to start returning
240     * spans.
241     *
242     * Note that the caller is responsible for calling dispose()
243     * on the returned ShapeSpanIterator inside a try/finally block.
244     * <pre>
245     *     ShapeSpanIterator ssi = LoopPipe.getStrokeSpans(sg2d, s);
246     *     try {
247     *         // iterate the spans from ssi and operate on them
248     *     } finally {
249     *         ssi.dispose();
250     *     }
251     * </pre>
252     *
253     * REMIND: This should return a SpanIterator interface object
254     * but the caller needs to dispose() the object and that method
255     * is only on ShapeSpanIterator.
256     * TODO: Add a dispose() method to the SpanIterator interface.
257     */
258    public static ShapeSpanIterator getStrokeSpans(SunGraphics2D sg2d,
259                                                   Shape s)
260    {
261        ShapeSpanIterator sr = new ShapeSpanIterator(false);
262
263        try {
264            sr.setOutputArea(sg2d.getCompClip());
265            sr.setRule(PathIterator.WIND_NON_ZERO);
266
267            BasicStroke bs = (BasicStroke) sg2d.stroke;
268            boolean thin = (sg2d.strokeState <= SunGraphics2D.STROKE_THINDASHED);
269            boolean normalize =
270                (sg2d.strokeHint != SunHints.INTVAL_STROKE_PURE);
271
272            RenderEngine.strokeTo(s,
273                                  sg2d.transform, bs,
274                                  thin, normalize, false, sr);
275        } catch (Throwable t) {
276            sr.dispose();
277            sr = null;
278            throw new InternalError("Unable to Stroke shape ("+
279                                    t.getMessage()+")", t);
280        }
281        return sr;
282    }
283
284    public void fill(SunGraphics2D sg2d, Shape s) {
285        if (sg2d.strokeState == SunGraphics2D.STROKE_THIN) {
286            Path2D.Float p2df;
287            int transX;
288            int transY;
289            if (sg2d.transformState <= SunGraphics2D.TRANSFORM_INT_TRANSLATE) {
290                if (s instanceof Path2D.Float) {
291                    p2df = (Path2D.Float)s;
292                } else {
293                    p2df = new Path2D.Float(s);
294                }
295                transX = sg2d.transX;
296                transY = sg2d.transY;
297            } else {
298                p2df = new Path2D.Float(s, sg2d.transform);
299                transX = 0;
300                transY = 0;
301            }
302            sg2d.loops.fillPathLoop.FillPath(sg2d, sg2d.getSurfaceData(),
303                                             transX, transY, p2df);
304            return;
305        }
306
307        ShapeSpanIterator sr = getFillSSI(sg2d);
308        try {
309            sr.setOutputArea(sg2d.getCompClip());
310            AffineTransform at =
311                ((sg2d.transformState == SunGraphics2D.TRANSFORM_ISIDENT)
312                 ? null
313                 : sg2d.transform);
314            sr.appendPath(s.getPathIterator(at));
315            fillSpans(sg2d, sr);
316        } finally {
317            sr.dispose();
318        }
319    }
320
321    private static void fillSpans(SunGraphics2D sg2d, SpanIterator si) {
322        // REMIND: Eventually, the plan is that it will not be possible for
323        // fs to be null since the FillSpans loop will be the fundamental
324        // loop implemented for any destination type...
325        if (sg2d.clipState == SunGraphics2D.CLIP_SHAPE) {
326            si = sg2d.clipRegion.filter(si);
327            // REMIND: Region.filter produces a Java-only iterator
328            // with no native counterpart...
329        } else {
330            sun.java2d.loops.FillSpans fs = sg2d.loops.fillSpansLoop;
331            if (fs != null) {
332                fs.FillSpans(sg2d, sg2d.getSurfaceData(), si);
333                return;
334            }
335        }
336        int spanbox[] = new int[4];
337        SurfaceData sd = sg2d.getSurfaceData();
338        while (si.nextSpan(spanbox)) {
339            int x = spanbox[0];
340            int y = spanbox[1];
341            int w = spanbox[2] - x;
342            int h = spanbox[3] - y;
343            sg2d.loops.fillRectLoop.FillRect(sg2d, sd, x, y, w, h);
344        }
345    }
346
347    public void fillParallelogram(SunGraphics2D sg2d,
348                                  double ux1, double uy1,
349                                  double ux2, double uy2,
350                                  double x, double y,
351                                  double dx1, double dy1,
352                                  double dx2, double dy2)
353    {
354        FillParallelogram fp = sg2d.loops.fillParallelogramLoop;
355        fp.FillParallelogram(sg2d, sg2d.getSurfaceData(),
356                             x, y, dx1, dy1, dx2, dy2);
357    }
358
359    public void drawParallelogram(SunGraphics2D sg2d,
360                                  double ux1, double uy1,
361                                  double ux2, double uy2,
362                                  double x, double y,
363                                  double dx1, double dy1,
364                                  double dx2, double dy2,
365                                  double lw1, double lw2)
366    {
367        DrawParallelogram dp = sg2d.loops.drawParallelogramLoop;
368        dp.DrawParallelogram(sg2d, sg2d.getSurfaceData(),
369                             x, y, dx1, dy1, dx2, dy2, lw1, lw2);
370    }
371}
372