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 48 extern "C" { 49 50 #define POLYTEMPSIZE (512 / sizeof(POINT)) 51 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 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 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 */ 123 JNIEXPORT void JNICALL 124 Java_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 */ 170 JNIEXPORT void JNICALL 171 Java_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 */ 215 JNIEXPORT void JNICALL 216 Java_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 */ 259 JNIEXPORT void JNICALL 260 Java_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 */ 297 JNIEXPORT void JNICALL 298 Java_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 */ 353 JNIEXPORT void JNICALL 354 Java_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 */ 425 JNIEXPORT void JNICALL 426 Java_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 */ 458 JNIEXPORT void JNICALL 459 Java_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 */ 501 JNIEXPORT void JNICALL 502 Java_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 */ 568 JNIEXPORT void JNICALL 569 Java_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 */ 628 JNIEXPORT void JNICALL 629 Java_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 */ 700 JNIEXPORT void JNICALL 701 Java_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 876 INLINE 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 */ 895 JNIEXPORT void JNICALL 896 Java_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 }