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 #include "jni_util.h"
27 #include "awt.h"
28 #include "sun_java2d_windows_GDIRenderer.h"
29 #include "java_awt_geom_PathIterator.h"
30 
31 #include "GDIWindowSurfaceData.h"
32 #include "awt_Component.h"
33 #include "awt_Pen.h"
34 #include "awt_Brush.h"
35 
36 #include "GraphicsPrimitiveMgr.h"
37 
38 #include <math.h>                /* for cos(), sin(), etc */
39 
40 #define MAX_CLAMP_BND (1<<26)
41 #define MIN_CLAMP_BND (-MAX_CLAMP_BND)
42 
43 #define CLAMP(x) (((x) > MAX_CLAMP_BND) ?   \
44     MAX_CLAMP_BND : ((x) < MIN_CLAMP_BND) ? \
45         MIN_CLAMP_BND : (x))
46 
47 
48 extern "C" {
49 
50 #define POLYTEMPSIZE    (512 / sizeof(POINT))
51 
AngleToCoord(jint angle,jint w,jint h,jint * x,jint * y)52 static void AngleToCoord(jint angle, jint w, jint h, jint *x, jint *y)
53 {
54     const double pi = 3.1415926535;
55     const double toRadians = 2 * pi / 360;
56 
57     *x = (long)(cos((double)angle * toRadians) * w);
58     *y = -(long)(sin((double)angle * toRadians) * h);
59 }
60 
TransformPoly(jint * xpoints,jint * ypoints,jint transx,jint transy,POINT * pPoints,jint * pNpoints,BOOL close,BOOL fixend)61 static POINT *TransformPoly(jint *xpoints, jint *ypoints,
62                             jint transx, jint transy,
63                             POINT *pPoints, jint *pNpoints,
64                             BOOL close, BOOL fixend)
65 {
66     int npoints = *pNpoints;
67     int outpoints = npoints;
68     jint x, y;
69 
70     // Fix for 4298688 - draw(Line) and Polygon omit last pixel
71     // We will need to add a point if we need to close it off or
72     // if we need to fix the endpoint to accommodate the Windows
73     // habit of never drawing the last pixel of a Polyline.  Note
74     // that if the polyline is already closed then neither fix
75     // is needed because the last pixel is also the first pixel
76     // and so will be drawn just fine.
77     // Clarification for 4298688 - regression bug 4678208 points
78     // out that we still need to fix the endpoint if the closed
79     // polygon never went anywhere (all vertices on same coordinate).
80     jint mx = xpoints[0];
81     jint my = ypoints[0];
82     BOOL isclosed = (xpoints[npoints-1] == mx && ypoints[npoints-1] == my);
83     if ((close && !isclosed) || fixend) {
84         outpoints++;
85         *pNpoints = outpoints;
86     }
87     if (outpoints > POLYTEMPSIZE) {
88         try {
89             pPoints = (POINT *) SAFE_SIZE_ARRAY_ALLOC(safe_Malloc, sizeof(POINT), outpoints);
90         } catch (const std::bad_alloc&) {
91             return NULL;
92         }
93     }
94     BOOL isempty = fixend;
95     for (int i = 0; i < npoints; i++) {
96         x = xpoints[i];
97         y = ypoints[i];
98         isempty = isempty && (x == mx && y == my);
99         pPoints[i].x = CLAMP(x + transx);
100         pPoints[i].y = CLAMP(y + transy);
101     }
102     if (close && !isclosed) {
103         pPoints[npoints] = pPoints[0];
104     } else if (fixend) {
105         if (!close || isempty) {
106             // Fix for 4298688 - draw(Line) and Polygon omit last pixel
107             // Fix up the last segment by adding another segment after
108             // it that is only 1 pixel long.  The first pixel of that
109             // segment will be drawn, but the second pixel is the one
110             // that Windows omits.
111             pPoints[npoints] = pPoints[npoints-1];
112             pPoints[npoints].x++;
113         } else {
114             outpoints--;
115             *pNpoints = outpoints;
116         }
117     }
118 
119     return pPoints;
120 }
121 
122 /*
123  * Class:     sun_java2d_windows_GDIRenderer
124  * Method:    doDrawLine
125  * Signature: (Lsun/java2d/windows/GDIWindowSurfaceData;Lsun/java2d/pipe/Region;Ljava/awt/Composite;IIIII)V
126  */
127 JNIEXPORT void JNICALL
Java_sun_java2d_windows_GDIRenderer_doDrawLine(JNIEnv * env,jobject wr,jobject sData,jobject clip,jobject comp,jint color,jint x1,jint y1,jint x2,jint y2)128 Java_sun_java2d_windows_GDIRenderer_doDrawLine
129     (JNIEnv *env, jobject wr,
130      jobject sData,
131      jobject clip, jobject comp, jint color,
132      jint x1, jint y1, jint x2, jint y2)
133 {
134     J2dTraceLn(J2D_TRACE_INFO, "GDIRenderer_doDrawLine");
135     J2dTraceLn5(J2D_TRACE_VERBOSE,
136                 "  color=0x%x x1=%-4d y1=%-4d x2=%-4d y2=%-4d",
137                 color, x1, y1, x2, y2);
138     GDIWinSDOps *wsdo = GDIWindowSurfaceData_GetOps(env, sData);
139     if (wsdo == NULL) {
140         return;
141     }
142 
143     HDC hdc;
144     jint patrop;
145     if (x1 == x2 || y1 == y2) {
146         if (x1 > x2) {
147             jint t = x1; x1 = x2; x2 = t;
148         }
149         if (y1 > y2) {
150             jint t = y1; y1 = y2; y2 = t;
151         }
152         hdc = wsdo->GetDC(env, wsdo, BRUSH, &patrop, clip, comp, color);
153         if (hdc == NULL) {
154             return;
155         }
156         ::PatBlt(hdc, x1, y1, x2-x1+1, y2-y1+1, patrop);
157     } else {
158         hdc = wsdo->GetDC(env, wsdo, PENBRUSH, &patrop, clip, comp, color);
159         if (hdc == NULL) {
160             return;
161         }
162         ::MoveToEx(hdc, x1, y1, NULL);
163         ::LineTo(hdc, x2, y2);
164         ::PatBlt(hdc, x2, y2, 1, 1, patrop);
165     }
166     wsdo->ReleaseDC(env, wsdo, hdc);
167 }
168 
169 /*
170  * Class:     sun_java2d_windows_GDIRenderer
171  * Method:    doDrawRect
172  * Signature: (Lsun/java2d/windows/GDIWindowSurfaceData;Lsun/java2d/pipe/Region;Ljava/awt/Composite;IIIII)V
173  */
174 JNIEXPORT void JNICALL
Java_sun_java2d_windows_GDIRenderer_doDrawRect(JNIEnv * env,jobject wr,jobject sData,jobject clip,jobject comp,jint color,jint x,jint y,jint w,jint h)175 Java_sun_java2d_windows_GDIRenderer_doDrawRect
176     (JNIEnv *env, jobject wr,
177      jobject sData,
178      jobject clip, jobject comp, jint color,
179      jint x, jint y, jint w, jint h)
180 {
181     J2dTraceLn(J2D_TRACE_INFO, "GDIRenderer_doDrawRect");
182     J2dTraceLn5(J2D_TRACE_VERBOSE,
183                 "  color=0x%x x=%-4d y=%-4d w=%-4d h=%-4d",
184                 color, x, y, w, h);
185     if (w < 0 || h < 0) {
186         return;
187     }
188 
189     GDIWinSDOps *wsdo = GDIWindowSurfaceData_GetOps(env, sData);
190     if (wsdo == NULL) {
191         return;
192     }
193     jint patrop;
194     HDC hdc = wsdo->GetDC(env, wsdo, BRUSH, &patrop, clip, comp, color);
195     if (hdc == NULL) {
196         return;
197     }
198     if (w < 2 || h < 2) {
199         // If one dimension is less than 2 then there is no
200         // gap in the middle - draw a solid filled rectangle.
201         ::PatBlt(hdc, x, y, w+1, h+1, patrop);
202     } else {
203         // Avoid drawing the endpoints twice.
204         // Also prefer including the endpoints in the
205         // horizontal sections which draw pixels faster.
206         ::PatBlt(hdc,  x,   y,  w+1,  1,  patrop);
207         ::PatBlt(hdc,  x,  y+1,  1,  h-1, patrop);
208         ::PatBlt(hdc, x+w, y+1,  1,  h-1, patrop);
209         ::PatBlt(hdc,  x,  y+h, w+1,  1,  patrop);
210     }
211     wsdo->ReleaseDC(env, wsdo, hdc);
212 }
213 
214 /*
215  * Class:     sun_java2d_windows_GDIRenderer
216  * Method:    doDrawRoundRect
217  * Signature: (Lsun/java2d/windows/GDIWindowSurfaceData;Lsun/java2d/pipe/Region;Ljava/awt/Composite;IIIIIII)V
218  */
219 JNIEXPORT void JNICALL
Java_sun_java2d_windows_GDIRenderer_doDrawRoundRect(JNIEnv * env,jobject wr,jobject sData,jobject clip,jobject comp,jint color,jint x,jint y,jint w,jint h,jint arcW,jint arcH)220 Java_sun_java2d_windows_GDIRenderer_doDrawRoundRect
221     (JNIEnv *env, jobject wr,
222      jobject sData,
223      jobject clip, jobject comp, jint color,
224      jint x, jint y, jint w, jint h, jint arcW, jint arcH)
225 {
226     J2dTraceLn(J2D_TRACE_INFO, "GDIRenderer_doDrawRoundRect");
227     J2dTraceLn5(J2D_TRACE_VERBOSE,
228                 "  color=0x%x x=%-4d y=%-4d w=%-4d h=%-4d",
229                 color, x, y, w, h);
230     J2dTraceLn2(J2D_TRACE_VERBOSE, "  arcW=%-4d arcH=%-4d",
231                 arcW, arcH);
232     if (w < 2 || h < 2 || arcW <= 0 || arcH <= 0) {
233         // Fix for 4524760 - drawRoundRect0 test case fails on Windows 98
234         // Thin round rects degenerate into regular rectangles
235         // because there is no room for the arc sections.  Also
236         // if there is no arc dimension then the roundrect must
237         // be a simple rectangle.  Defer to the DrawRect function
238         // which handles degenerate sizes better.
239         Java_sun_java2d_windows_GDIRenderer_doDrawRect(env, wr,
240                                                        sData, clip,
241                                                        comp, color,
242                                                        x, y, w, h);
243         return;
244     }
245 
246     GDIWinSDOps *wsdo = GDIWindowSurfaceData_GetOps(env, sData);
247     if (wsdo == NULL) {
248         return;
249     }
250     HDC hdc = wsdo->GetDC(env, wsdo, PENONLY, NULL, clip, comp, color);
251     if (hdc == NULL) {
252         return;
253     }
254     ::RoundRect(hdc, x, y, x+w+1, y+h+1, arcW, arcH);
255     wsdo->ReleaseDC(env, wsdo, hdc);
256 }
257 
258 /*
259  * Class:     sun_java2d_windows_GDIRenderer
260  * Method:    doDrawOval
261  * Signature: (Lsun/java2d/windows/GDIWindowSurfaceData;Lsun/java2d/pipe/Region;Ljava/awt/Composite;IIIII)V
262  */
263 JNIEXPORT void JNICALL
Java_sun_java2d_windows_GDIRenderer_doDrawOval(JNIEnv * env,jobject wr,jobject sData,jobject clip,jobject comp,jint color,jint x,jint y,jint w,jint h)264 Java_sun_java2d_windows_GDIRenderer_doDrawOval
265     (JNIEnv *env, jobject wr,
266      jobject sData,
267      jobject clip, jobject comp, jint color,
268      jint x, jint y, jint w, jint h)
269 {
270     J2dTraceLn(J2D_TRACE_INFO, "GDIRenderer_doDrawOval");
271     J2dTraceLn5(J2D_TRACE_VERBOSE,
272                 "  color=0x%x x=%-4d y=%-4d w=%-4d h=%-4d",
273                 color, x, y, w, h);
274     if (w < 2 || h < 2) {
275         // Thin enough ovals have no room for curvature.  Defer to
276         // the DrawRect method which handles degenerate sizes better.
277         Java_sun_java2d_windows_GDIRenderer_doDrawRect(env, wr,
278                                                        sData, clip,
279                                                        comp, color,
280                                                        x, y, w, h);
281         return;
282     }
283 
284     GDIWinSDOps *wsdo = GDIWindowSurfaceData_GetOps(env, sData);
285     if (wsdo == NULL) {
286         return;
287     }
288     HDC hdc = wsdo->GetDC(env, wsdo, PENONLY, NULL, clip, comp, color);
289     if (hdc == NULL) {
290         return;
291     }
292     ::Ellipse(hdc, x, y, x+w+1, y+h+1);
293     wsdo->ReleaseDC(env, wsdo, hdc);
294 }
295 
296 /*
297  * Class:     sun_java2d_windows_GDIRenderer
298  * Method:    doDrawArc
299  * Signature: (Lsun/java2d/windows/GDIWindowSurfaceData;Lsun/java2d/pipe/Region;Ljava/awt/Composite;IIIIIII)V
300  */
301 JNIEXPORT void JNICALL
Java_sun_java2d_windows_GDIRenderer_doDrawArc(JNIEnv * env,jobject wr,jobject sData,jobject clip,jobject comp,jint color,jint x,jint y,jint w,jint h,jint angleStart,jint angleExtent)302 Java_sun_java2d_windows_GDIRenderer_doDrawArc
303     (JNIEnv *env, jobject wr,
304      jobject sData,
305      jobject clip, jobject comp, jint color,
306      jint x, jint y, jint w, jint h,
307      jint angleStart, jint angleExtent)
308 {
309     J2dTraceLn(J2D_TRACE_INFO, "GDIRenderer_doDrawArc");
310     J2dTraceLn5(J2D_TRACE_VERBOSE,
311                 "  color=0x%x x=%-4d y=%-4d w=%-4d h=%-4d",
312                 color, x, y, w, h);
313     J2dTraceLn2(J2D_TRACE_VERBOSE,
314                 "  angleStart=%-4d angleExtent=%-4d",
315                 angleStart, angleExtent);
316     if (w < 0 || h < 0 || angleExtent == 0) {
317         return;
318     }
319 
320     GDIWinSDOps *wsdo = GDIWindowSurfaceData_GetOps(env, sData);
321     if (wsdo == NULL) {
322         return;
323     }
324 
325     long sx, sy, ex, ey;
326     if (angleExtent >= 360 || angleExtent <= -360) {
327         sx = ex = x + w;
328         sy = ey = y + h/2;
329     } else {
330         int angleEnd;
331         if (angleExtent < 0) {
332             angleEnd = angleStart;
333             angleStart += angleExtent;
334         } else {
335             angleEnd = angleStart + angleExtent;
336         }
337         AngleToCoord(angleStart, w, h, &sx, &sy);
338         sx += x + w/2;
339         sy += y + h/2;
340         AngleToCoord(angleEnd, w, h, &ex, &ey);
341         ex += x + w/2;
342         ey += y + h/2;
343     }
344     HDC hdc = wsdo->GetDC(env, wsdo, PEN, NULL, clip, comp, color);
345     if (hdc == NULL) {
346         return;
347     }
348     ::Arc(hdc, x, y, x+w+1, y+h+1, sx, sy, ex, ey);
349     wsdo->ReleaseDC(env, wsdo, hdc);
350 }
351 
352 /*
353  * Class:     sun_java2d_windows_GDIRenderer
354  * Method:    doDrawPoly
355  * Signature: (Lsun/java2d/windows/GDIWindowSurfaceData;Lsun/java2d/pipe/Region;Ljava/awt/Composite;III[I[IIZ)V
356  */
357 JNIEXPORT void JNICALL
Java_sun_java2d_windows_GDIRenderer_doDrawPoly(JNIEnv * env,jobject wr,jobject sData,jobject clip,jobject comp,jint color,jint transx,jint transy,jintArray xpointsarray,jintArray ypointsarray,jint npoints,jboolean isclosed)358 Java_sun_java2d_windows_GDIRenderer_doDrawPoly
359     (JNIEnv *env, jobject wr,
360      jobject sData,
361      jobject clip, jobject comp, jint color,
362      jint transx, jint transy,
363      jintArray xpointsarray, jintArray ypointsarray,
364      jint npoints, jboolean isclosed)
365 {
366     J2dTraceLn(J2D_TRACE_INFO, "GDIRenderer_doDrawPoly");
367     J2dTraceLn5(J2D_TRACE_VERBOSE,
368                 "  color=0x%x transx=%-4d transy=%-4d "\
369                 "npoints=%-4d isclosed=%-4d",
370                 color, transx, transy, npoints, isclosed);
371     if (JNU_IsNull(env, xpointsarray) || JNU_IsNull(env, ypointsarray)) {
372         JNU_ThrowNullPointerException(env, "coordinate array");
373         return;
374     }
375     if (env->GetArrayLength(xpointsarray) < npoints ||
376         env->GetArrayLength(ypointsarray) < npoints)
377     {
378         JNU_ThrowArrayIndexOutOfBoundsException(env, "coordinate array");
379         return;
380     }
381     if (npoints < 2) {
382         // Fix for 4067534 - assertion failure in 1.3.1 for degenerate polys
383         // Not enough points for a line.
384         // Note that this would be ignored later anyway, but returning
385         // here saves us from mistakes in TransformPoly and seeing bad
386         // return values from the Windows Polyline function.
387         return;
388     }
389 
390     GDIWinSDOps *wsdo = GDIWindowSurfaceData_GetOps(env, sData);
391     if (wsdo == NULL) {
392         return;
393     }
394 
395     POINT tmpPts[POLYTEMPSIZE], *pPoints = NULL;
396 
397     jint *xpoints = (jint *) env->GetPrimitiveArrayCritical(xpointsarray, NULL);
398 
399     if (xpoints != NULL) {
400         jint *ypoints = (jint *) env->GetPrimitiveArrayCritical(ypointsarray, NULL);
401         if (ypoints != NULL) {
402             pPoints = TransformPoly(xpoints, ypoints, transx, transy,
403                                     tmpPts, &npoints, isclosed, TRUE);
404             env->ReleasePrimitiveArrayCritical(ypointsarray, ypoints, JNI_ABORT);
405         }
406         env->ReleasePrimitiveArrayCritical(xpointsarray, xpoints, JNI_ABORT);
407     }
408 
409     if (pPoints == NULL) {
410         return;
411     }
412 
413     HDC hdc = wsdo->GetDC(env, wsdo, PEN, NULL, clip, comp, color);
414     if (hdc == NULL) {
415         return;
416     }
417     ::Polyline(hdc, pPoints, npoints);
418     wsdo->ReleaseDC(env, wsdo, hdc);
419     if (pPoints != tmpPts) {
420         free(pPoints);
421     }
422 }
423 
424 /*
425  * Class:     sun_java2d_windows_GDIRenderer
426  * Method:    doFillRect
427  * Signature: (Lsun/java2d/windows/GDIWindowSurfaceData;Lsun/java2d/pipe/Region;Ljava/awt/Composite;IIIII)V
428  */
429 JNIEXPORT void JNICALL
Java_sun_java2d_windows_GDIRenderer_doFillRect(JNIEnv * env,jobject wr,jobject sData,jobject clip,jobject comp,jint color,jint x,jint y,jint w,jint h)430 Java_sun_java2d_windows_GDIRenderer_doFillRect
431     (JNIEnv *env, jobject wr,
432      jobject sData,
433      jobject clip, jobject comp, jint color,
434      jint x, jint y, jint w, jint h)
435 {
436     J2dTraceLn(J2D_TRACE_INFO, "GDIRenderer_doFillRect");
437     J2dTraceLn5(J2D_TRACE_VERBOSE,
438                 "  color=0x%x x=%-4d y=%-4d w=%-4d h=%-4d",
439                 color, x, y, w, h);
440     if (w <= 0 || h <= 0) {
441         return;
442     }
443 
444     GDIWinSDOps *wsdo = GDIWindowSurfaceData_GetOps(env, sData);
445     if (wsdo == NULL) {
446         return;
447     }
448     jint patrop;
449     HDC hdc = wsdo->GetDC(env, wsdo, BRUSH, &patrop, clip, comp, color);
450     if (hdc == NULL) {
451         return;
452     }
453     ::PatBlt(hdc, x, y, w, h, patrop);
454     wsdo->ReleaseDC(env, wsdo, hdc);
455 }
456 
457 /*
458  * Class:     sun_java2d_windows_GDIRenderer
459  * Method:    doFillRoundRect
460  * Signature: (Lsun/java2d/windows/GDIWindowSurfaceData;Lsun/java2d/pipe/Region;Ljava/awt/Composite;IIIIIII)V
461  */
462 JNIEXPORT void JNICALL
Java_sun_java2d_windows_GDIRenderer_doFillRoundRect(JNIEnv * env,jobject wr,jobject sData,jobject clip,jobject comp,jint color,jint x,jint y,jint w,jint h,jint arcW,jint arcH)463 Java_sun_java2d_windows_GDIRenderer_doFillRoundRect
464     (JNIEnv *env, jobject wr,
465      jobject sData,
466      jobject clip, jobject comp, jint color,
467      jint x, jint y, jint w, jint h, jint arcW, jint arcH)
468 {
469     J2dTraceLn(J2D_TRACE_INFO, "GDIRenderer_doFillRoundRect");
470     J2dTraceLn5(J2D_TRACE_VERBOSE,
471                 "  color=0x%x x=%-4d y=%-4d w=%-4d h=%-4d",
472                 color, x, y, w, h);
473     J2dTraceLn2(J2D_TRACE_VERBOSE, "  arcW=%-4d arcH=%-4d",
474                 arcW, arcH);
475     if (w < 2 || h < 2 || arcW <= 0 || arcH <= 0) {
476         // Fix related to 4524760 - drawRoundRect0 fails on Windows 98
477         // Thin round rects have no room for curvature.  Also, if
478         // the curvature is empty then the primitive has degenerated
479         // into a simple rectangle.  Defer to the FillRect method
480         // which deals with degenerate sizes better.
481         Java_sun_java2d_windows_GDIRenderer_doFillRect(env, wr,
482                                                        sData, clip,
483                                                        comp, color,
484                                                        x, y, w, h);
485         return;
486     }
487 
488     GDIWinSDOps *wsdo = GDIWindowSurfaceData_GetOps(env, sData);
489     if (wsdo == NULL) {
490         return;
491     }
492     HDC hdc = wsdo->GetDC(env, wsdo, BRUSHONLY, NULL, clip, comp, color);
493     if (hdc == NULL) {
494         return;
495     }
496     ::RoundRect(hdc, x, y, x+w+1, y+h+1, arcW, arcH);
497     wsdo->ReleaseDC(env, wsdo, hdc);
498 }
499 
500 /*
501  * Class:     sun_java2d_windows_GDIRenderer
502  * Method:    doFillOval
503  * Signature: (Lsun/java2d/windows/GDIWindowSurfaceData;Lsun/java2d/pipe/Region;Ljava/awt/Composite;IIIII)V
504  */
505 JNIEXPORT void JNICALL
Java_sun_java2d_windows_GDIRenderer_doFillOval(JNIEnv * env,jobject wr,jobject sData,jobject clip,jobject comp,jint color,jint x,jint y,jint w,jint h)506 Java_sun_java2d_windows_GDIRenderer_doFillOval
507     (JNIEnv *env, jobject wr,
508      jobject sData,
509      jobject clip, jobject comp, jint color,
510      jint x, jint y, jint w, jint h)
511 {
512     J2dTraceLn(J2D_TRACE_INFO, "GDIRenderer_doFillOval");
513     J2dTraceLn5(J2D_TRACE_VERBOSE,
514                 "  color=0x%x x=%-4d y=%-4d w=%-4d h=%-4d",
515                 color, x, y, w, h);
516     if (w < 3 || h < 3) {
517         // Fix for 4411814 - small ovals do not draw anything
518         // (related to 4205762 on Solaris platform)
519         // Most platform graphics packages have poor rendering
520         // for thin ellipses and the rendering is most strikingly
521         // different from our theoretical arcs.  Ideally we should
522         // trap all ovals less than some fairly large size and
523         // try to draw aesthetically pleasing ellipses, but that
524         // would require considerably more work to get the corresponding
525         // drawArc variants to match pixel for pixel.
526         // Thin ovals of girth 1 pixel are simple rectangles.
527         // Thin ovals of girth 2 pixels are simple rectangles with
528         // potentially smaller lengths.  Determine the correct length
529         // by calculating .5*.5 + scaledlen*scaledlen == 1.0 which
530         // means that scaledlen is the sqrt(0.75).  Scaledlen is
531         // relative to the true length (w or h) and needs to be
532         // adjusted by half a pixel in different ways for odd or
533         // even lengths.
534 #define SQRT_3_4 0.86602540378443864676
535         if (w > 2 && h > 1) {
536             int adjw = (int) ((SQRT_3_4 * w - ((w&1)-1)) * 0.5);
537             adjw = adjw * 2 + (w&1);
538             x += (w-adjw)/2;
539             w = adjw;
540         } else if (h > 2 && w > 1) {
541             int adjh = (int) ((SQRT_3_4 * h - ((h&1)-1)) * 0.5);
542             adjh = adjh * 2 + (h&1);
543             y += (h-adjh)/2;
544             h = adjh;
545         }
546 #undef SQRT_3_4
547         if (w > 0 && h > 0) {
548             Java_sun_java2d_windows_GDIRenderer_doFillRect(env, wr, sData,
549                                                            clip, comp, color,
550                                                            x, y, w, h);
551         }
552         return;
553     }
554 
555     GDIWinSDOps *wsdo = GDIWindowSurfaceData_GetOps(env, sData);
556     if (wsdo == NULL) {
557         return;
558     }
559     HDC hdc = wsdo->GetDC(env, wsdo, BRUSHONLY, NULL, clip, comp, color);
560     if (hdc == NULL) {
561         return;
562     }
563     ::Ellipse(hdc, x, y, x+w+1, y+h+1);
564     wsdo->ReleaseDC(env, wsdo, hdc);
565 }
566 
567 /*
568  * Class:     sun_java2d_windows_GDIRenderer
569  * Method:    doFillArc
570  * Signature: (Lsun/java2d/windows/GDIWindowSurfaceData;Lsun/java2d/pipe/Region;Ljava/awt/Composite;IIIIIII)V
571  */
572 JNIEXPORT void JNICALL
Java_sun_java2d_windows_GDIRenderer_doFillArc(JNIEnv * env,jobject wr,jobject sData,jobject clip,jobject comp,jint color,jint x,jint y,jint w,jint h,jint angleStart,jint angleExtent)573 Java_sun_java2d_windows_GDIRenderer_doFillArc
574     (JNIEnv *env, jobject wr,
575      jobject sData,
576      jobject clip, jobject comp, jint color,
577      jint x, jint y, jint w, jint h,
578      jint angleStart, jint angleExtent)
579 {
580     J2dTraceLn(J2D_TRACE_INFO, "GDIRenderer_doFillArc");
581     J2dTraceLn5(J2D_TRACE_VERBOSE,
582                 "  color=0x%x x=%-4d y=%-4d w=%-4d h=%-4d",
583                 color, x, y, w, h);
584     J2dTraceLn2(J2D_TRACE_VERBOSE,
585                 "  angleStart=%-4d angleExtent=%-4d",
586                 angleStart, angleExtent);
587     if (w <= 0 || h <= 0 || angleExtent == 0) {
588         return;
589     }
590     if (angleExtent >= 360 || angleExtent <= -360) {
591         // Fix related to 4411814 - small ovals (and arcs) do not draw
592         // If the arc is a full circle, let the Oval method handle it
593         // since that method can deal with degenerate sizes better.
594         Java_sun_java2d_windows_GDIRenderer_doFillOval(env, wr,
595                                                        sData, clip,
596                                                        comp, color,
597                                                        x, y, w, h);
598         return;
599     }
600 
601     GDIWinSDOps *wsdo = GDIWindowSurfaceData_GetOps(env, sData);
602     if (wsdo == NULL) {
603         return;
604     }
605     long sx, sy, ex, ey;
606     int angleEnd;
607     if (angleExtent < 0) {
608         angleEnd = angleStart;
609         angleStart += angleExtent;
610     } else {
611         angleEnd = angleStart + angleExtent;
612     }
613     AngleToCoord(angleStart, w, h, &sx, &sy);
614     sx += x + w/2;
615     sy += y + h/2;
616     AngleToCoord(angleEnd, w, h, &ex, &ey);
617     ex += x + w/2;
618     ey += y + h/2;
619     HDC hdc = wsdo->GetDC(env, wsdo, BRUSHONLY, NULL, clip, comp, color);
620     if (hdc == NULL) {
621         return;
622     }
623     ::Pie(hdc, x, y, x+w+1, y+h+1, sx, sy, ex, ey);
624     wsdo->ReleaseDC(env, wsdo, hdc);
625 }
626 
627 /*
628  * Class:     sun_java2d_windows_GDIRenderer
629  * Method:    doFillPoly
630  * Signature: (Lsun/java2d/windows/GDIWindowSurfaceData;Lsun/java2d/pipe/Region;Ljava/awt/Composite;III[I[II)V
631  */
632 JNIEXPORT void JNICALL
Java_sun_java2d_windows_GDIRenderer_doFillPoly(JNIEnv * env,jobject wr,jobject sData,jobject clip,jobject comp,jint color,jint transx,jint transy,jintArray xpointsarray,jintArray ypointsarray,jint npoints)633 Java_sun_java2d_windows_GDIRenderer_doFillPoly
634     (JNIEnv *env, jobject wr,
635      jobject sData,
636      jobject clip, jobject comp, jint color,
637      jint transx, jint transy,
638      jintArray xpointsarray, jintArray ypointsarray,
639      jint npoints)
640 {
641     J2dTraceLn(J2D_TRACE_INFO, "GDIRenderer_doFillPoly");
642     J2dTraceLn4(J2D_TRACE_VERBOSE,
643                 "  color=0x%x transx=%-4d transy=%-4d npoints=%-4d",
644                 color, transx, transy, npoints);
645     if (JNU_IsNull(env, xpointsarray) || JNU_IsNull(env, ypointsarray)) {
646         JNU_ThrowNullPointerException(env, "coordinate array");
647         return;
648     }
649     if (env->GetArrayLength(xpointsarray) < npoints ||
650         env->GetArrayLength(ypointsarray) < npoints)
651     {
652         JNU_ThrowArrayIndexOutOfBoundsException(env, "coordinate array");
653         return;
654     }
655     if (npoints < 3) {
656         // Fix for 4067534 - assertion failure in 1.3.1 for degenerate polys
657         // Not enough points for a triangle.
658         // Note that this would be ignored later anyway, but returning
659         // here saves us from mistakes in TransformPoly and seeing bad
660         // return values from the Windows Polyline function.
661         return;
662     }
663 
664     GDIWinSDOps *wsdo = GDIWindowSurfaceData_GetOps(env, sData);
665     if (wsdo == NULL) {
666         return;
667     }
668 
669     POINT tmpPts[POLYTEMPSIZE], *pPoints = NULL;
670 
671     jint *xpoints = (jint *) env->GetPrimitiveArrayCritical(xpointsarray, NULL);
672     if (xpoints != NULL) {
673         jint *ypoints = (jint *) env->GetPrimitiveArrayCritical(ypointsarray, NULL);
674         if (ypoints != NULL) {
675             pPoints = TransformPoly(xpoints, ypoints, transx, transy,
676                                 tmpPts, &npoints, FALSE, FALSE);
677             env->ReleasePrimitiveArrayCritical(ypointsarray, ypoints, JNI_ABORT);
678         }
679         env->ReleasePrimitiveArrayCritical(xpointsarray, xpoints, JNI_ABORT);
680     }
681 
682     if (pPoints == NULL) {
683         return;
684     }
685 
686     HDC hdc = wsdo->GetDC(env, wsdo, BRUSHONLY, NULL, clip, comp, color);
687     if (hdc == NULL) {
688         return;
689     }
690     ::SetPolyFillMode(hdc, ALTERNATE);
691     ::Polygon(hdc, pPoints, npoints);
692     wsdo->ReleaseDC(env, wsdo, hdc);
693     if (pPoints != tmpPts) {
694         free(pPoints);
695     }
696 }
697 
698 /*
699  * Class:     sun_java2d_windows_GDIRenderer
700  * Method:    doShape
701  * Signature:  (Lsun/java2d/windows/GDIWindowSurfaceData;Lsun/java2d/pipe/Region;
702  *              Ljava/awt/Composite;IIILjava/awt/geom/Path2D.Float;Z)V
703  */
704 JNIEXPORT void JNICALL
Java_sun_java2d_windows_GDIRenderer_doShape(JNIEnv * env,jobject wr,jobject sData,jobject clip,jobject comp,jint color,jint transX,jint transY,jobject p2df,jboolean isfill)705 Java_sun_java2d_windows_GDIRenderer_doShape
706     (JNIEnv *env, jobject wr,
707      jobject sData,
708      jobject clip, jobject comp, jint color,
709      jint transX, jint transY,
710      jobject p2df, jboolean isfill)
711 {
712     J2dTraceLn(J2D_TRACE_INFO, "GDIRenderer_doShape");
713     J2dTraceLn4(J2D_TRACE_VERBOSE,
714                 "  color=0x%x transx=%-4d transy=%-4d isfill=%-4d",
715                 color, transX, transY, isfill);
716     GDIWinSDOps *wsdo = GDIWindowSurfaceData_GetOps(env, sData);
717     if (wsdo == NULL) {
718         return;
719     }
720 
721     jarray typesarray = (jarray) env->GetObjectField(p2df, path2DTypesID);
722     jarray coordsarray = (jarray) env->GetObjectField(p2df,
723                                                       path2DFloatCoordsID);
724     if (coordsarray == NULL) {
725         JNU_ThrowNullPointerException(env, "coordinates array");
726         return;
727     }
728     jint numtypes = env->GetIntField(p2df, path2DNumTypesID);
729     if (env->GetArrayLength(typesarray) < numtypes) {
730         JNU_ThrowArrayIndexOutOfBoundsException(env, "types array");
731         return;
732     }
733     jint maxcoords = env->GetArrayLength(coordsarray);
734     jint rule = env->GetIntField(p2df, path2DWindingRuleID);
735 
736     HDC hdc = wsdo->GetDC(env, wsdo, (isfill ? BRUSH : PEN), NULL,
737                           clip, comp, color);
738     if (hdc == NULL) {
739         return;
740     }
741 
742     jbyte *types = (jbyte *) env->GetPrimitiveArrayCritical(typesarray,
743                                                             NULL);
744     if (types == NULL) {
745         wsdo->ReleaseDC(env, wsdo, hdc);
746         return;
747     }
748 
749     jfloat *coords = (jfloat *) env->GetPrimitiveArrayCritical(coordsarray,
750                                                                NULL);
751     if (coords == NULL) {
752         env->ReleasePrimitiveArrayCritical(typesarray, types, JNI_ABORT);
753         wsdo->ReleaseDC(env, wsdo, hdc);
754         return;
755     }
756 
757     ::SetPolyFillMode(hdc, (rule == java_awt_geom_PathIterator_WIND_NON_ZERO
758                             ? WINDING : ALTERNATE));
759     ::BeginPath(hdc);
760 
761     int index = 0;
762     BOOL ok = TRUE;
763     BOOL isempty = TRUE;
764     BOOL isapoint = TRUE;
765     int mx = 0, my = 0, x1 = 0, y1 = 0;
766     POINT ctrlpts[3];
767     for (int i = 0; ok && i < numtypes; i++) {
768         switch (types[i]) {
769         case java_awt_geom_PathIterator_SEG_MOVETO:
770             if (!isfill && !isempty) {
771                 // Fix for 4298688 - draw(Line) omits last pixel
772                 // Windows omits the last pixel of a path when stroking.
773                 // Fix up the last segment of the previous subpath by
774                 // adding another segment after it that is only 1 pixel
775                 // long.  The first pixel of that segment will be drawn,
776                 // but the second pixel is the one that Windows omits.
777                 ::LineTo(hdc, x1+1, y1);
778             }
779             if (index + 2 <= maxcoords) {
780                 mx = x1 = transX + (int) floor(coords[index++]);
781                 my = y1 = transY + (int) floor(coords[index++]);
782                 ::MoveToEx(hdc, x1, y1, NULL);
783                 isempty = TRUE;
784                 isapoint = TRUE;
785             } else {
786                 ok = FALSE;
787             }
788             break;
789         case java_awt_geom_PathIterator_SEG_LINETO:
790             if (index + 2 <= maxcoords) {
791                 x1 = transX + (int) floor(coords[index++]);
792                 y1 = transY + (int) floor(coords[index++]);
793                 ::LineTo(hdc, x1, y1);
794                 isapoint = isapoint && (x1 == mx && y1 == my);
795                 isempty = FALSE;
796             } else {
797                 ok = FALSE;
798             }
799             break;
800         case java_awt_geom_PathIterator_SEG_QUADTO:
801             if (index + 4 <= maxcoords) {
802                 ctrlpts[0].x = transX + (int) floor(coords[index++]);
803                 ctrlpts[0].y = transY + (int) floor(coords[index++]);
804                 ctrlpts[2].x = transX + (int) floor(coords[index++]);
805                 ctrlpts[2].y = transY + (int) floor(coords[index++]);
806                 ctrlpts[1].x = (ctrlpts[0].x * 2 + ctrlpts[2].x) / 3;
807                 ctrlpts[1].y = (ctrlpts[0].y * 2 + ctrlpts[2].y) / 3;
808                 ctrlpts[0].x = (ctrlpts[0].x * 2 + x1) / 3;
809                 ctrlpts[0].y = (ctrlpts[0].y * 2 + y1) / 3;
810                 x1 = ctrlpts[2].x;
811                 y1 = ctrlpts[2].y;
812                 ::PolyBezierTo(hdc, ctrlpts, 3);
813                 isapoint = isapoint && (x1 == mx && y1 == my);
814                 isempty = FALSE;
815             } else {
816                 ok = FALSE;
817             }
818             break;
819         case java_awt_geom_PathIterator_SEG_CUBICTO:
820             if (index + 6 <= maxcoords) {
821                 ctrlpts[0].x = transX + (int) floor(coords[index++]);
822                 ctrlpts[0].y = transY + (int) floor(coords[index++]);
823                 ctrlpts[1].x = transX + (int) floor(coords[index++]);
824                 ctrlpts[1].y = transY + (int) floor(coords[index++]);
825                 ctrlpts[2].x = transX + (int) floor(coords[index++]);
826                 ctrlpts[2].y = transY + (int) floor(coords[index++]);
827                 x1 = ctrlpts[2].x;
828                 y1 = ctrlpts[2].y;
829                 ::PolyBezierTo(hdc, ctrlpts, 3);
830                 isapoint = isapoint && (x1 == mx && y1 == my);
831                 isempty = FALSE;
832             } else {
833                 ok = FALSE;
834             }
835             break;
836         case java_awt_geom_PathIterator_SEG_CLOSE:
837             ::CloseFigure(hdc);
838             if (x1 != mx || y1 != my) {
839                 x1 = mx;
840                 y1 = my;
841                 ::MoveToEx(hdc, x1, y1, NULL);
842                 isempty = TRUE;
843                 isapoint = TRUE;
844             } else if (!isfill && !isempty && isapoint) {
845                 ::LineTo(hdc, x1+1, y1);
846                 ::MoveToEx(hdc, x1, y1, NULL);
847                 isempty = TRUE;
848                 isapoint = TRUE;
849             }
850             break;
851         }
852     }
853     env->ReleasePrimitiveArrayCritical(typesarray, types, JNI_ABORT);
854     env->ReleasePrimitiveArrayCritical(coordsarray, coords, JNI_ABORT);
855     if (ok) {
856         if (!isfill && !isempty) {
857             // Fix for 4298688 - draw(Line) omits last pixel
858             // Windows omits the last pixel of a path when stroking.
859             // Fix up the last segment of the previous subpath by
860             // adding another segment after it that is only 1 pixel
861             // long.  The first pixel of that segment will be drawn,
862             // but the second pixel is the one that Windows omits.
863             ::LineTo(hdc, x1+1, y1);
864         }
865         ::EndPath(hdc);
866         if (isfill) {
867             ::FillPath(hdc);
868         } else {
869             ::StrokePath(hdc);
870         }
871     } else {
872         ::AbortPath(hdc);
873         JNU_ThrowArrayIndexOutOfBoundsException(env, "coords array");
874     }
875     wsdo->ReleaseDC(env, wsdo, hdc);
876 }
877 
878 } /* extern "C" */
879 
RectInMonitorRect(RECT * rCheck,RECT * rContainer)880 INLINE BOOL RectInMonitorRect(RECT *rCheck, RECT *rContainer)
881 {
882     // Assumption: left <= right, top <= bottom
883     if (rCheck->left >= rContainer->left &&
884         rCheck->right <= rContainer->right &&
885         rCheck->top >= rContainer->top &&
886         rCheck->bottom <= rContainer->bottom)
887     {
888         return TRUE;
889     } else {
890         return FALSE;
891     }
892 }
893 
894 /*
895  * Class:     sun_java2d_windows_GDIRenderer
896  * Method:    devCopyArea
897  * Signature: (Lsun/java2d/windows/GDIWindowSurfaceData;IIIIII)V
898  */
899 JNIEXPORT void JNICALL
Java_sun_java2d_windows_GDIRenderer_devCopyArea(JNIEnv * env,jobject wr,jobject wsd,jint srcx,jint srcy,jint dx,jint dy,jint width,jint height)900 Java_sun_java2d_windows_GDIRenderer_devCopyArea
901     (JNIEnv *env, jobject wr,
902      jobject wsd,
903      jint srcx, jint srcy,
904      jint dx, jint dy,
905      jint width, jint height)
906 {
907     GDIWinSDOps *wsdo = GDIWindowSurfaceData_GetOps(env, wsd);
908     J2dTraceLn(J2D_TRACE_INFO, "GDIWindowSurfaceData_devCopyArea");
909     J2dTraceLn4(J2D_TRACE_VERBOSE, "   srcx=%-4d srcy=%-4d dx=%-4d dy=%-4d",
910                 srcx, srcy, dx, dy);
911     J2dTraceLn2(J2D_TRACE_VERBOSE, "     w=%-4d h=%-4d", width, height);
912     if (wsdo == NULL) {
913         return;
914     }
915     if (wsdo->invalid) {
916         SurfaceData_ThrowInvalidPipeException(env,
917             "GDIRenderer_devCopyArea: invalid surface data");
918         return;
919     }
920 
921     HDC hDC = wsdo->GetDC(env, wsdo, 0, NULL, NULL, NULL, 0);
922     if (hDC == NULL) {
923         return;
924     }
925 
926     RECT r;
927     ::SetRect(&r, srcx, srcy, srcx + width, srcy + height);
928     HRGN rgnUpdate = ::CreateRectRgn(0, 0, 0, 0);
929     VERIFY(::ScrollDC(hDC, dx, dy, &r, NULL, rgnUpdate, NULL));
930 
931     // ScrollDC invalidates the part of the source rectangle that
932     // is outside of the destination rectangle on the assumption
933     // that you wanted to "move" the pixels from source to dest,
934     // and so now you will want to paint new pixels in the source.
935     // Since our copyarea operation involves no such semantics we
936     // are only interested in the part of the update region that
937     // corresponds to unavailable source pixels - i.e. the part
938     // that falls within the destination rectangle.
939 
940     // The update region will be in client relative coordinates
941     // but the destination rect will be in window relative coordinates
942     ::OffsetRect(&r, dx-wsdo->insets.left, dy-wsdo->insets.top);
943     HRGN rgnDst = ::CreateRectRgnIndirect(&r);
944     int result = ::CombineRgn(rgnUpdate, rgnUpdate, rgnDst, RGN_AND);
945 
946     // Invalidate the exposed area.
947     if (result != NULLREGION) {
948         ::InvalidateRgn(wsdo->window, rgnUpdate, TRUE);
949     }
950     ::DeleteObject(rgnUpdate);
951     ::DeleteObject(rgnDst);
952 
953     wsdo->ReleaseDC(env, wsdo, hDC);
954 }
955