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 }