1+ /* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
2+
3+ /*
4+ Part of the Processing project - http://processing.org
5+
6+ Copyright (c) 2015 The Processing Foundation
7+
8+ This library is free software; you can redistribute it and/or
9+ modify it under the terms of the GNU Lesser General Public
10+ License version 2.1 as published by the Free Software Foundation.
11+
12+ This library is distributed in the hope that it will be useful,
13+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+ Lesser General Public License for more details.
16+
17+ You should have received a copy of the GNU Lesser General
18+ Public License along with this library; if not, write to the
19+ Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20+ Boston, MA 02111-1307 USA
21+ */
22+
23+ package processing .awt ;
24+
25+ import java .awt .Paint ;
26+ import java .awt .PaintContext ;
27+ import java .awt .Rectangle ;
28+ import java .awt .RenderingHints ;
29+ import java .awt .geom .AffineTransform ;
30+ import java .awt .geom .Point2D ;
31+ import java .awt .geom .Rectangle2D ;
32+ import java .awt .image .ColorModel ;
33+ import java .awt .image .Raster ;
34+ import java .awt .image .WritableRaster ;
35+
36+ import processing .core .PApplet ;
37+ import processing .core .PGraphics ;
38+ import processing .core .PShapeSVG ;
39+ import processing .data .*;
40+
41+
42+ /**
43+ * Implements features for PShape that are specific to AWT and Java2D.
44+ * At the moment, this is gradients and java.awt.Paint handling.
45+ */
46+ public class PShapeJava2D extends PShapeSVG {
47+ Paint strokeGradientPaint ;
48+ Paint fillGradientPaint ;
49+
50+
51+ public PShapeJava2D (XML svg ) {
52+ super (svg );
53+ }
54+
55+
56+ @ Override
57+ protected void setParent (PShapeSVG parent ) {
58+ if (parent instanceof PShapeJava2D ) {
59+ PShapeJava2D pj = (PShapeJava2D ) parent ;
60+ fillGradientPaint = pj .fillGradientPaint ;
61+ strokeGradientPaint = pj .strokeGradientPaint ;
62+
63+ } else { // parent is null or not Java2D
64+ fillGradientPaint = null ;
65+ strokeGradientPaint = null ;
66+ }
67+ }
68+
69+
70+ static class LinearGradientPaint implements Paint {
71+ float x1 , y1 , x2 , y2 ;
72+ float [] offset ;
73+ int [] color ;
74+ int count ;
75+ float opacity ;
76+
77+ public LinearGradientPaint (float x1 , float y1 , float x2 , float y2 ,
78+ float [] offset , int [] color , int count ,
79+ float opacity ) {
80+ this .x1 = x1 ;
81+ this .y1 = y1 ;
82+ this .x2 = x2 ;
83+ this .y2 = y2 ;
84+ this .offset = offset ;
85+ this .color = color ;
86+ this .count = count ;
87+ this .opacity = opacity ;
88+ }
89+
90+ public PaintContext createContext (ColorModel cm ,
91+ Rectangle deviceBounds , Rectangle2D userBounds ,
92+ AffineTransform xform , RenderingHints hints ) {
93+ Point2D t1 = xform .transform (new Point2D .Float (x1 , y1 ), null );
94+ Point2D t2 = xform .transform (new Point2D .Float (x2 , y2 ), null );
95+ return new LinearGradientContext ((float ) t1 .getX (), (float ) t1 .getY (),
96+ (float ) t2 .getX (), (float ) t2 .getY ());
97+ }
98+
99+ public int getTransparency () {
100+ return TRANSLUCENT ; // why not.. rather than checking each color
101+ }
102+
103+ public class LinearGradientContext implements PaintContext {
104+ int ACCURACY = 2 ;
105+ float tx1 , ty1 , tx2 , ty2 ;
106+
107+ public LinearGradientContext (float tx1 , float ty1 , float tx2 , float ty2 ) {
108+ this .tx1 = tx1 ;
109+ this .ty1 = ty1 ;
110+ this .tx2 = tx2 ;
111+ this .ty2 = ty2 ;
112+ }
113+
114+ public void dispose () { }
115+
116+ public ColorModel getColorModel () { return ColorModel .getRGBdefault (); }
117+
118+ public Raster getRaster (int x , int y , int w , int h ) {
119+ WritableRaster raster =
120+ getColorModel ().createCompatibleWritableRaster (w , h );
121+
122+ int [] data = new int [w * h * 4 ];
123+
124+ // make normalized version of base vector
125+ float nx = tx2 - tx1 ;
126+ float ny = ty2 - ty1 ;
127+ float len = (float ) Math .sqrt (nx *nx + ny *ny );
128+ if (len != 0 ) {
129+ nx /= len ;
130+ ny /= len ;
131+ }
132+
133+ int span = (int ) PApplet .dist (tx1 , ty1 , tx2 , ty2 ) * ACCURACY ;
134+ if (span <= 0 ) {
135+ //System.err.println("span is too small");
136+ // annoying edge case where the gradient isn't legit
137+ int index = 0 ;
138+ for (int j = 0 ; j < h ; j ++) {
139+ for (int i = 0 ; i < w ; i ++) {
140+ data [index ++] = 0 ;
141+ data [index ++] = 0 ;
142+ data [index ++] = 0 ;
143+ data [index ++] = 255 ;
144+ }
145+ }
146+
147+ } else {
148+ int [][] interp = new int [span ][4 ];
149+ int prev = 0 ;
150+ for (int i = 1 ; i < count ; i ++) {
151+ int c0 = color [i -1 ];
152+ int c1 = color [i ];
153+ int last = (int ) (offset [i ] * (span -1 ));
154+ //System.out.println("last is " + last);
155+ for (int j = prev ; j <= last ; j ++) {
156+ float btwn = PApplet .norm (j , prev , last );
157+ interp [j ][0 ] = (int ) PApplet .lerp ((c0 >> 16 ) & 0xff , (c1 >> 16 ) & 0xff , btwn );
158+ interp [j ][1 ] = (int ) PApplet .lerp ((c0 >> 8 ) & 0xff , (c1 >> 8 ) & 0xff , btwn );
159+ interp [j ][2 ] = (int ) PApplet .lerp (c0 & 0xff , c1 & 0xff , btwn );
160+ interp [j ][3 ] = (int ) (PApplet .lerp ((c0 >> 24 ) & 0xff , (c1 >> 24 ) & 0xff , btwn ) * opacity );
161+ //System.out.println(j + " " + interp[j][0] + " " + interp[j][1] + " " + interp[j][2]);
162+ }
163+ prev = last ;
164+ }
165+
166+ int index = 0 ;
167+ for (int j = 0 ; j < h ; j ++) {
168+ for (int i = 0 ; i < w ; i ++) {
169+ //float distance = 0; //PApplet.dist(cx, cy, x + i, y + j);
170+ //int which = PApplet.min((int) (distance * ACCURACY), interp.length-1);
171+ float px = (x + i ) - tx1 ;
172+ float py = (y + j ) - ty1 ;
173+ // distance up the line is the dot product of the normalized
174+ // vector of the gradient start/stop by the point being tested
175+ int which = (int ) ((px *nx + py *ny ) * ACCURACY );
176+ if (which < 0 ) which = 0 ;
177+ if (which > interp .length -1 ) which = interp .length -1 ;
178+ //if (which > 138) System.out.println("grabbing " + which);
179+
180+ data [index ++] = interp [which ][0 ];
181+ data [index ++] = interp [which ][1 ];
182+ data [index ++] = interp [which ][2 ];
183+ data [index ++] = interp [which ][3 ];
184+ }
185+ }
186+ }
187+ raster .setPixels (0 , 0 , w , h , data );
188+
189+ return raster ;
190+ }
191+ }
192+ }
193+
194+
195+ static class RadialGradientPaint implements Paint {
196+ float cx , cy , radius ;
197+ float [] offset ;
198+ int [] color ;
199+ int count ;
200+ float opacity ;
201+
202+ public RadialGradientPaint (float cx , float cy , float radius ,
203+ float [] offset , int [] color , int count ,
204+ float opacity ) {
205+ this .cx = cx ;
206+ this .cy = cy ;
207+ this .radius = radius ;
208+ this .offset = offset ;
209+ this .color = color ;
210+ this .count = count ;
211+ this .opacity = opacity ;
212+ }
213+
214+ public PaintContext createContext (ColorModel cm ,
215+ Rectangle deviceBounds , Rectangle2D userBounds ,
216+ AffineTransform xform , RenderingHints hints ) {
217+ return new RadialGradientContext ();
218+ }
219+
220+ public int getTransparency () {
221+ return TRANSLUCENT ;
222+ }
223+
224+ public class RadialGradientContext implements PaintContext {
225+ int ACCURACY = 5 ;
226+
227+ public void dispose () {}
228+
229+ public ColorModel getColorModel () { return ColorModel .getRGBdefault (); }
230+
231+ public Raster getRaster (int x , int y , int w , int h ) {
232+ WritableRaster raster =
233+ getColorModel ().createCompatibleWritableRaster (w , h );
234+
235+ int span = (int ) radius * ACCURACY ;
236+ int [][] interp = new int [span ][4 ];
237+ int prev = 0 ;
238+ for (int i = 1 ; i < count ; i ++) {
239+ int c0 = color [i -1 ];
240+ int c1 = color [i ];
241+ int last = (int ) (offset [i ] * (span - 1 ));
242+ for (int j = prev ; j <= last ; j ++) {
243+ float btwn = PApplet .norm (j , prev , last );
244+ interp [j ][0 ] = (int ) PApplet .lerp ((c0 >> 16 ) & 0xff , (c1 >> 16 ) & 0xff , btwn );
245+ interp [j ][1 ] = (int ) PApplet .lerp ((c0 >> 8 ) & 0xff , (c1 >> 8 ) & 0xff , btwn );
246+ interp [j ][2 ] = (int ) PApplet .lerp (c0 & 0xff , c1 & 0xff , btwn );
247+ interp [j ][3 ] = (int ) (PApplet .lerp ((c0 >> 24 ) & 0xff , (c1 >> 24 ) & 0xff , btwn ) * opacity );
248+ }
249+ prev = last ;
250+ }
251+
252+ int [] data = new int [w * h * 4 ];
253+ int index = 0 ;
254+ for (int j = 0 ; j < h ; j ++) {
255+ for (int i = 0 ; i < w ; i ++) {
256+ float distance = PApplet .dist (cx , cy , x + i , y + j );
257+ int which = PApplet .min ((int ) (distance * ACCURACY ), interp .length -1 );
258+
259+ data [index ++] = interp [which ][0 ];
260+ data [index ++] = interp [which ][1 ];
261+ data [index ++] = interp [which ][2 ];
262+ data [index ++] = interp [which ][3 ];
263+ }
264+ }
265+ raster .setPixels (0 , 0 , w , h , data );
266+
267+ return raster ;
268+ }
269+ }
270+ }
271+
272+
273+ protected Paint calcGradientPaint (Gradient gradient ) {
274+ if (gradient instanceof LinearGradient ) {
275+ LinearGradient grad = (LinearGradient ) gradient ;
276+ return new LinearGradientPaint (grad .x1 , grad .y1 , grad .x2 , grad .y2 ,
277+ grad .offset , grad .color , grad .count ,
278+ opacity );
279+
280+ } else if (gradient instanceof RadialGradient ) {
281+ RadialGradient grad = (RadialGradient ) gradient ;
282+ return new RadialGradientPaint (grad .cx , grad .cy , grad .r ,
283+ grad .offset , grad .color , grad .count ,
284+ opacity );
285+ }
286+ return null ;
287+ }
288+
289+
290+ // protected Paint calcGradientPaint(Gradient gradient,
291+ // float x1, float y1, float x2, float y2) {
292+ // if (gradient instanceof LinearGradient) {
293+ // LinearGradient grad = (LinearGradient) gradient;
294+ // return new LinearGradientPaint(x1, y1, x2, y2,
295+ // grad.offset, grad.color, grad.count,
296+ // opacity);
297+ // }
298+ // throw new RuntimeException("Not a linear gradient.");
299+ // }
300+
301+
302+ // protected Paint calcGradientPaint(Gradient gradient,
303+ // float cx, float cy, float r) {
304+ // if (gradient instanceof RadialGradient) {
305+ // RadialGradient grad = (RadialGradient) gradient;
306+ // return new RadialGradientPaint(cx, cy, r,
307+ // grad.offset, grad.color, grad.count,
308+ // opacity);
309+ // }
310+ // throw new RuntimeException("Not a radial gradient.");
311+ // }
312+
313+
314+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
315+
316+
317+ @ Override
318+ protected void styles (PGraphics g ) {
319+ super .styles (g );
320+
321+ //if (g instanceof PGraphicsJava2D) {
322+ PGraphicsJava2D p2d = (PGraphicsJava2D ) g ;
323+
324+ if (strokeGradient != null ) {
325+ p2d .strokeGradient = true ;
326+ if (strokeGradientPaint == null ) {
327+ strokeGradientPaint = calcGradientPaint (strokeGradient );
328+ }
329+ p2d .strokeGradientObject = strokeGradientPaint ;
330+ } else {
331+ // need to shut off, in case parent object has a gradient applied
332+ //p2d.strokeGradient = false;
333+ }
334+ if (fillGradient != null ) {
335+ p2d .fillGradient = true ;
336+ if (fillGradientPaint == null ) {
337+ fillGradientPaint = calcGradientPaint (fillGradient );
338+ }
339+ p2d .fillGradientObject = fillGradientPaint ;
340+ } else {
341+ // need to shut off, in case parent object has a gradient applied
342+ //p2d.fillGradient = false;
343+ }
344+ //}
345+ }
346+ }
0 commit comments