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 }