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