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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 }