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