1 /*
2  * Copyright (c) 1999, 2018, 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.pipe;
27 
28 import java.awt.Shape;
29 import java.awt.BasicStroke;
30 import java.awt.geom.AffineTransform;
31 import java.awt.geom.PathIterator;
32 import java.awt.geom.RoundRectangle2D;
33 import java.awt.geom.Ellipse2D;
34 import java.awt.geom.Arc2D;
35 import java.awt.geom.Path2D;
36 import sun.java2d.SunGraphics2D;
37 import sun.java2d.SurfaceData;
38 import sun.java2d.loops.FillParallelogram;
39 import sun.java2d.loops.DrawParallelogram;
40 import sun.awt.SunHints;
41 
42 public class LoopPipe
43     implements PixelDrawPipe,
44                PixelFillPipe,
45                ParallelogramPipe,
46                ShapeDrawPipe,
47                LoopBasedPipe
48 {
49     static final RenderingEngine RenderEngine = RenderingEngine.getInstance();
50 
drawLine(SunGraphics2D sg2d, int x1, int y1, int x2, int y2)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 
drawRect(SunGraphics2D sg2d, int x, int y, int width, int height)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 
drawRoundRect(SunGraphics2D sg2d, int x, int y, int width, int height, int arcWidth, int arcHeight)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 
drawOval(SunGraphics2D sg2d, int x, int y, int width, int height)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 
drawArc(SunGraphics2D sg2d, int x, int y, int width, int height, int startAngle, int arcAngle)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 
drawPolyline(SunGraphics2D sg2d, int[] xPoints, int[] yPoints, int nPoints)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 
drawPolygon(SunGraphics2D sg2d, int[] xPoints, int[] yPoints, int nPoints)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 
fillRect(SunGraphics2D sg2d, int x, int y, int width, int height)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 
fillRoundRect(SunGraphics2D sg2d, int x, int y, int width, int height, int arcWidth, int arcHeight)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 
fillOval(SunGraphics2D sg2d, int x, int y, int width, int height)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 
fillArc(SunGraphics2D sg2d, int x, int y, int width, int height, int startAngle, int arcAngle)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 
fillPolygon(SunGraphics2D sg2d, int[] xPoints, int[] yPoints, int nPoints)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 
draw(SunGraphics2D sg2d, Shape s)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      */
getFillSSI(SunGraphics2D sg2d)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      */
getStrokeSpans(SunGraphics2D sg2d, Shape s)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 
fill(SunGraphics2D sg2d, Shape s)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 
fillSpans(SunGraphics2D sg2d, SpanIterator si)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 
fillParallelogram(SunGraphics2D sg2d, double ux1, double uy1, double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2)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 
drawParallelogram(SunGraphics2D sg2d, double ux1, double uy1, double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2, double lw1, double lw2)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