1 /*
2  * Copyright (c) 1999, 2021, 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             final Region clip = sg2d.getCompClip();
265             sr.setOutputArea(clip);
266             sr.setRule(PathIterator.WIND_NON_ZERO);
267 
268             BasicStroke bs = (BasicStroke) sg2d.stroke;
269             boolean thin = (sg2d.strokeState <= SunGraphics2D.STROKE_THINDASHED);
270             boolean normalize =
271                 (sg2d.strokeHint != SunHints.INTVAL_STROKE_PURE);
272 
273             RenderEngine.strokeTo(s,
274                                   sg2d.transform, clip, bs,
275                                   thin, normalize, false, sr);
276         } catch (Throwable t) {
277             sr.dispose();
278             sr = null;
279             throw new InternalError("Unable to Stroke shape ("+
280                                     t.getMessage()+")", t);
281         }
282         return sr;
283     }
284 
fill(SunGraphics2D sg2d, Shape s)285     public void fill(SunGraphics2D sg2d, Shape s) {
286         if (sg2d.strokeState == SunGraphics2D.STROKE_THIN) {
287             Path2D.Float p2df;
288             int transX;
289             int transY;
290             if (sg2d.transformState <= SunGraphics2D.TRANSFORM_INT_TRANSLATE) {
291                 if (s instanceof Path2D.Float) {
292                     p2df = (Path2D.Float)s;
293                 } else {
294                     p2df = new Path2D.Float(s);
295                 }
296                 transX = sg2d.transX;
297                 transY = sg2d.transY;
298             } else {
299                 p2df = new Path2D.Float(s, sg2d.transform);
300                 transX = 0;
301                 transY = 0;
302             }
303             sg2d.loops.fillPathLoop.FillPath(sg2d, sg2d.getSurfaceData(),
304                                              transX, transY, p2df);
305             return;
306         }
307 
308         ShapeSpanIterator sr = getFillSSI(sg2d);
309         try {
310             sr.setOutputArea(sg2d.getCompClip());
311             AffineTransform at =
312                 ((sg2d.transformState == SunGraphics2D.TRANSFORM_ISIDENT)
313                  ? null
314                  : sg2d.transform);
315             sr.appendPath(s.getPathIterator(at));
316             fillSpans(sg2d, sr);
317         } finally {
318             sr.dispose();
319         }
320     }
321 
fillSpans(SunGraphics2D sg2d, SpanIterator si)322     private static void fillSpans(SunGraphics2D sg2d, SpanIterator si) {
323         // REMIND: Eventually, the plan is that it will not be possible for
324         // fs to be null since the FillSpans loop will be the fundamental
325         // loop implemented for any destination type...
326         if (sg2d.clipState == SunGraphics2D.CLIP_SHAPE) {
327             si = sg2d.clipRegion.filter(si);
328             // REMIND: Region.filter produces a Java-only iterator
329             // with no native counterpart...
330         } else {
331             sun.java2d.loops.FillSpans fs = sg2d.loops.fillSpansLoop;
332             if (fs != null) {
333                 fs.FillSpans(sg2d, sg2d.getSurfaceData(), si);
334                 return;
335             }
336         }
337         int[] spanbox = new int[4];
338         SurfaceData sd = sg2d.getSurfaceData();
339         while (si.nextSpan(spanbox)) {
340             int x = spanbox[0];
341             int y = spanbox[1];
342             int w = spanbox[2] - x;
343             int h = spanbox[3] - y;
344             sg2d.loops.fillRectLoop.FillRect(sg2d, sd, x, y, w, h);
345         }
346     }
347 
fillParallelogram(SunGraphics2D sg2d, double ux1, double uy1, double ux2, double uy2, double x, double y, double dx1, double dy1, double dx2, double dy2)348     public void fillParallelogram(SunGraphics2D sg2d,
349                                   double ux1, double uy1,
350                                   double ux2, double uy2,
351                                   double x, double y,
352                                   double dx1, double dy1,
353                                   double dx2, double dy2)
354     {
355         FillParallelogram fp = sg2d.loops.fillParallelogramLoop;
356         fp.FillParallelogram(sg2d, sg2d.getSurfaceData(),
357                              x, y, dx1, dy1, dx2, dy2);
358     }
359 
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)360     public void drawParallelogram(SunGraphics2D sg2d,
361                                   double ux1, double uy1,
362                                   double ux2, double uy2,
363                                   double x, double y,
364                                   double dx1, double dy1,
365                                   double dx2, double dy2,
366                                   double lw1, double lw2)
367     {
368         DrawParallelogram dp = sg2d.loops.drawParallelogramLoop;
369         dp.DrawParallelogram(sg2d, sg2d.getSurfaceData(),
370                              x, y, dx1, dy1, dx2, dy2, lw1, lw2);
371     }
372 }
373