1/*
2 * Copyright (c) 1999, 2011, 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
27
28/**
29 * This class holds the information for a particular graphics device.
30 * Since a display change can cause the creation of new devices
31 * at any time, there is no referencing of the devices array allowed.
32 * Instead, anyone wishing to reference a device in the array (e.g.,
33 * the current default device or a device for a given hWnd) must
34 * call one of the static methods of this class with the index of
35 * the device in question.  Those methods will then lock the devices
36 * array and forward the request to the current device at that
37 * array index.
38 */
39
40#include <awt.h>
41#include <sun_awt_Win32GraphicsDevice.h>
42#include "awt_Canvas.h"
43#include "awt_Win32GraphicsDevice.h"
44#include "awt_Window.h"
45#include "java_awt_Transparency.h"
46#include "java_awt_color_ColorSpace.h"
47#include "sun_awt_Win32GraphicsDevice.h"
48#include "java_awt_image_DataBuffer.h"
49#include "dither.h"
50#include "img_util_md.h"
51#include "Devices.h"
52#include <d2d1.h>
53#pragma comment(lib, "d2d1")
54#include "systemScale.h"
55
56uns_ordered_dither_array img_oda_alpha;
57
58jclass      AwtWin32GraphicsDevice::indexCMClass;
59jclass      AwtWin32GraphicsDevice::wToolkitClass;
60jfieldID    AwtWin32GraphicsDevice::dynamicColorModelID;
61jfieldID    AwtWin32GraphicsDevice::indexCMrgbID;
62jfieldID    AwtWin32GraphicsDevice::indexCMcacheID;
63jmethodID   AwtWin32GraphicsDevice::paletteChangedMID;
64BOOL        AwtWin32GraphicsDevice::primaryPalettized;
65int         AwtWin32GraphicsDevice::primaryIndex = 0;
66
67
68/**
69 * Construct this device.  Store the screen (index into the devices
70 * array of this object), the array (used in static references via
71 * particular device indices), the monitor/pMonitorInfo (which other
72 * classes will inquire of this device), the bits per pixel of this
73 * device, and information on whether the primary device is palettized.
74 */
75AwtWin32GraphicsDevice::AwtWin32GraphicsDevice(int screen,
76                                               HMONITOR mhnd, Devices *arr)
77{
78    this->screen  = screen;
79    this->devicesArray = arr;
80    this->scaleX = 1;
81    this->scaleY = 1;
82    javaDevice = NULL;
83    colorData = new ImgColorData;
84    colorData->grayscale = GS_NOTGRAY;
85    palette = NULL;
86    cData = NULL;
87    gpBitmapInfo = NULL;
88    monitor = mhnd;
89    pMonitorInfo = new MONITORINFOEX;
90    pMonitorInfo->cbSize = sizeof(MONITORINFOEX);
91    ::GetMonitorInfo(monitor, pMonitorInfo);
92
93    // Set primary device info: other devices will need to know
94    // whether the primary is palettized during the initialization
95    // process
96    HDC hDC = this->GetDC();
97    colorData->bitsperpixel = ::GetDeviceCaps(hDC, BITSPIXEL);
98    this->ReleaseDC(hDC);
99    if (MONITORINFOF_PRIMARY & pMonitorInfo->dwFlags) {
100        primaryIndex = screen;
101        if (colorData->bitsperpixel > 8) {
102            primaryPalettized = FALSE;
103        } else {
104            primaryPalettized = TRUE;
105        }
106    }
107}
108
109AwtWin32GraphicsDevice::~AwtWin32GraphicsDevice()
110{
111    delete colorData;
112    if (gpBitmapInfo) {
113        free(gpBitmapInfo);
114    }
115    if (palette) {
116        delete palette;
117    }
118    if (pMonitorInfo) {
119        delete pMonitorInfo;
120    }
121    if (javaDevice) {
122        JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
123        env->DeleteWeakGlobalRef(javaDevice);
124    }
125    if (cData != NULL) {
126        freeICMColorData(cData);
127    }
128}
129
130HDC AwtWin32GraphicsDevice::MakeDCFromMonitor(HMONITOR hmMonitor) {
131    HDC retCode = NULL;
132    if (NULL != hmMonitor) {
133        MONITORINFOEX mieInfo;
134
135        memset((void*)(&mieInfo), 0, sizeof(MONITORINFOEX));
136        mieInfo.cbSize = sizeof(MONITORINFOEX);
137
138        if (TRUE == ::GetMonitorInfo(hmMonitor, (LPMONITORINFOEX)(&mieInfo))) {
139            HDC hDC = CreateDC(mieInfo.szDevice, NULL, NULL, NULL);
140            if (NULL != hDC) {
141                retCode = hDC;
142            }
143        }
144    }
145    return retCode;
146}
147
148HDC AwtWin32GraphicsDevice::GetDC()
149{
150    return MakeDCFromMonitor(monitor);
151}
152
153void AwtWin32GraphicsDevice::ReleaseDC(HDC hDC)
154{
155    if (hDC != NULL) {
156        ::DeleteDC(hDC);
157    }
158}
159
160/**
161 * Init this device.  This creates the bitmap structure
162 * used to hold the device color data and initializes any
163 * appropriate palette structures.
164 */
165void AwtWin32GraphicsDevice::Initialize()
166{
167    unsigned int ri, gi, bi;
168    if (colorData->bitsperpixel < 8) {
169        // REMIND: how to handle?
170    }
171
172    // Create a BitmapInfo object for color data
173    if (!gpBitmapInfo) {
174        try {
175            gpBitmapInfo = (BITMAPINFO *)
176                safe_Malloc(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
177        } catch (std::bad_alloc&) {
178            throw;
179        }
180        gpBitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
181    }
182    gpBitmapInfo->bmiHeader.biBitCount = 0;
183    HDC hBMDC = this->GetDC();
184    HBITMAP hBM = ::CreateCompatibleBitmap(hBMDC, 1, 1);
185    VERIFY(::GetDIBits(hBMDC, hBM, 0, 1, NULL, gpBitmapInfo, DIB_RGB_COLORS));
186
187    if (colorData->bitsperpixel > 8) {
188        if (MONITORINFOF_PRIMARY & pMonitorInfo->dwFlags) {
189            primaryPalettized = FALSE;
190        }
191        if (colorData->bitsperpixel != 24) { // 15, 16, or 32 bpp
192            int foo;
193            gpBitmapInfo->bmiHeader.biCompression = BI_BITFIELDS;
194            if (::GetDIBits(hBMDC, hBM, 0, 1, &foo, gpBitmapInfo,
195                            DIB_RGB_COLORS) == 0)
196            {
197                // Bug 4684966: If GetDIBits returns an error, we could
198                // get stuck in an infinite loop setting the colorData
199                // fields.  Hardcode bitColors to reasonable values instead.
200                // These values are picked according to standard masks
201                // for these bit depths on win9x, according to MSDN docs.
202                switch (colorData->bitsperpixel) {
203                case 15:
204                    ((int *)gpBitmapInfo->bmiColors)[0] = 0x7c00;
205                    ((int *)gpBitmapInfo->bmiColors)[1] = 0x03e0;
206                    ((int *)gpBitmapInfo->bmiColors)[2] = 0x001f;
207                    break;
208                case 16:
209                    ((int *)gpBitmapInfo->bmiColors)[0] = 0xf800;
210                    ((int *)gpBitmapInfo->bmiColors)[1] = 0x07e0;
211                    ((int *)gpBitmapInfo->bmiColors)[2] = 0x001f;
212                    break;
213                case 32:
214                default:
215                    ((int *)gpBitmapInfo->bmiColors)[0] = 0xff0000;
216                    ((int *)gpBitmapInfo->bmiColors)[1] = 0x00ff00;
217                    ((int *)gpBitmapInfo->bmiColors)[2] = 0x0000ff;
218                    break;
219                }
220            }
221            ri = ((unsigned int *)gpBitmapInfo->bmiColors)[0];
222            colorData->rOff = 0;
223            while ((ri & 1) == 0) {
224                colorData->rOff++;
225                ri >>= 1;
226            }
227            colorData->rScale = 0;
228            while (ri < 0x80) {
229                colorData->rScale++;
230                ri <<= 1;
231            }
232            gi = ((unsigned int *)gpBitmapInfo->bmiColors)[1];
233            colorData->gOff = 0;
234            while ((gi & 1) == 0) {
235                colorData->gOff++;
236                gi >>= 1;
237            }
238            colorData->gScale = 0;
239            while (gi < 0x80) {
240                colorData->gScale++;
241                gi <<= 1;
242            }
243            bi = ((unsigned int *)gpBitmapInfo->bmiColors)[2];
244            colorData->bOff = 0;
245            while ((bi & 1) == 0) {
246                colorData->bOff++;
247                bi >>= 1;
248            }
249            colorData->bScale = 0;
250            while (bi < 0x80) {
251                colorData->bScale++;
252                bi <<= 1;
253            }
254            if (   (0 == colorData->bOff)
255                && (5 == colorData->gOff)
256                && (10 == colorData->rOff)
257                && (3 == colorData->bScale)
258                && (3 == colorData->gScale)
259                && (3 == colorData->rScale)) {
260                colorData->bitsperpixel = 15;
261                gpBitmapInfo->bmiHeader.biCompression = BI_RGB;
262            }
263        } else {    // 24 bpp
264            gpBitmapInfo->bmiHeader.biBitCount = 24;
265            gpBitmapInfo->bmiHeader.biCompression = BI_RGB;
266
267            // Fill these values in as a convenience for the screen
268            // ColorModel construction code below (see getColorModel())
269            ((int *)gpBitmapInfo->bmiColors)[0] = 0x0000ff;
270            ((int *)gpBitmapInfo->bmiColors)[1] = 0x00ff00;
271            ((int *)gpBitmapInfo->bmiColors)[2] = 0xff0000;
272        }
273    } else {
274        if (MONITORINFOF_PRIMARY & pMonitorInfo->dwFlags) {
275            primaryPalettized = TRUE;
276        }
277        gpBitmapInfo->bmiHeader.biBitCount = 8;
278        gpBitmapInfo->bmiHeader.biCompression = BI_RGB;
279        gpBitmapInfo->bmiHeader.biClrUsed = 256;
280        gpBitmapInfo->bmiHeader.biClrImportant = 256;
281
282        // The initialization of cData is done prior to
283        // calling palette->Update() since we need it
284        // for calculating inverseGrayLut
285        if (cData == NULL) {
286            cData = (ColorData*)safe_Calloc(1, sizeof(ColorData));
287            memset(cData, 0, sizeof(ColorData));
288            initDitherTables(cData);
289        }
290
291        if (!palette) {
292            palette = new AwtPalette(this);
293        } else {
294            palette->Update();
295        }
296        palette->UpdateLogical();
297    }
298    VERIFY(::DeleteObject(hBM));
299    VERIFY(::DeleteDC(hBMDC));
300}
301
302/**
303 * Creates a new colorModel given the current device configuration.
304 * The dynamic flag determines whether we use the system palette
305 * (dynamic == TRUE) or our custom palette in creating a new
306 * IndexedColorModel.
307 */
308jobject AwtWin32GraphicsDevice::GetColorModel(JNIEnv *env, jboolean dynamic)
309{
310    jobject awt_colormodel;
311    int i;
312    if (colorData->bitsperpixel == 24) {
313        awt_colormodel =
314            JNU_NewObjectByName(env, "sun/awt/Win32ColorModel24", "()V");
315    } else if (colorData->bitsperpixel > 8) {
316        int *masks = (int *)gpBitmapInfo->bmiColors;
317        int numbits = 0;
318        unsigned int bits = (masks[0] | masks[1] | masks[2]);
319        while (bits) {
320            numbits++;
321            bits >>= 1;
322        }
323        awt_colormodel = JNU_NewObjectByName(env,
324                                             "java/awt/image/DirectColorModel",
325                                             "(IIII)V", numbits,
326                                             masks[0], masks[1], masks[2]);
327    } else if (colorData->grayscale == GS_STATICGRAY) {
328        jclass clazz;
329        jclass clazz1;
330        jmethodID mid;
331        jobject cspace = NULL;
332        jint bits[1];
333        jintArray bitsArray;
334
335        clazz1 = env->FindClass("java/awt/color/ColorSpace");
336        CHECK_NULL_RETURN(clazz1, NULL);
337        mid = env->GetStaticMethodID(clazz1, "getInstance",
338              "(I)Ljava/awt/color/ColorSpace;");
339        CHECK_NULL_RETURN(mid, NULL);
340        cspace = env->CallStaticObjectMethod(clazz1, mid,
341            java_awt_color_ColorSpace_CS_GRAY);
342        CHECK_NULL_RETURN(cspace, NULL);
343
344        bits[0] = 8;
345        bitsArray = env->NewIntArray(1);
346        if (bitsArray == 0) {
347            return NULL;
348        } else {
349            env->SetIntArrayRegion(bitsArray, 0, 1, bits);
350        }
351
352        clazz = env->FindClass("java/awt/image/ComponentColorModel");
353        CHECK_NULL_RETURN(clazz, NULL);
354        mid = env->GetMethodID(clazz,"<init>",
355            "(Ljava/awt/color/ColorSpace;[IZZII)V");
356        CHECK_NULL_RETURN(mid, NULL);
357
358        awt_colormodel = env->NewObject(clazz, mid,
359                                        cspace,
360                                        bitsArray,
361                                        JNI_FALSE,
362                                        JNI_FALSE,
363                                        java_awt_Transparency_OPAQUE,
364                                        java_awt_image_DataBuffer_TYPE_BYTE);
365    } else {
366        jintArray hRGB = env->NewIntArray(256);
367        unsigned int *rgb = NULL, *rgbP = NULL;
368        jboolean allvalid = JNI_TRUE;
369        jbyte vbits[256/8];
370        jobject validBits = NULL;
371
372        CHECK_NULL_RETURN(hRGB, NULL);
373        /* Create the LUT from the color map */
374        try {
375            rgb = (unsigned int *) env->GetPrimitiveArrayCritical(hRGB, 0);
376            CHECK_NULL_RETURN(rgb, NULL);
377            rgbP = rgb;
378            if (!palette) {
379                palette = new AwtPalette(this);
380                palette->UpdateLogical();
381            }
382            if (colorData->grayscale == GS_INDEXGRAY) {
383                /* For IndexColorModel, pretend first 10 colors and last
384                   10 colors are transparent black.  This makes
385                   ICM.allgrayopaque true.
386                */
387                unsigned int *logicalEntries = palette->GetLogicalEntries();
388
389                for (i=0; i < 10; i++) {
390                    rgbP[i] = 0x00000000;
391                    rgbP[i+246] = 0x00000000;
392                }
393                memcpy(&rgbP[10], &logicalEntries[10], 236 * sizeof(RGBQUAD));
394                // We need to specify which entries in the colormap are
395                // valid so that the transparent black entries we have
396                // created do not affect the Transparency setting of the
397                // IndexColorModel.  The vbits array is used to construct
398                // a BigInteger such that the most significant bit of vbits[0]
399                // indicates the validity of the last color (#256) and the
400                // least significant bit of vbits[256/8] indicates the
401                // validity of the first color (#0).  We need to fill vbits
402                // with all 1's and then turn off the first and last 10 bits.
403                memset(vbits, 0xff, sizeof(vbits));
404                vbits[0] = 0;
405                vbits[1] = (jbyte) (0xff >> 2);
406                vbits[sizeof(vbits)-2] = (jbyte) (0xff << 2);
407                vbits[sizeof(vbits)-1] = 0;
408                allvalid = JNI_FALSE;
409            } else {
410                if (AwtPalette::UseCustomPalette() && !dynamic) {
411                    // If we plan to use our custom palette (i.e., we are
412                    // not running inside another app and we are not creating
413                    // a dynamic colorModel object), then setup ICM with
414                    // custom palette entries
415                    unsigned int *logicalEntries = palette->GetLogicalEntries();
416                    memcpy(rgbP, logicalEntries, 256 * sizeof(int));
417                } else {
418                    // Else, use current system palette entries.
419                    // REMIND: This may not give the result we want if
420                    // we are running inside another app and that
421                    // parent app is running in the background when we
422                    // reach here.  We could at least cache an "ideal" set of
423                    // system palette entries from the first time we are
424                    // running in the foreground and then future ICM's will
425                    // use that set instead.
426                    unsigned int *systemEntries = palette->GetSystemEntries();
427                    memcpy(rgbP, systemEntries, 256 * sizeof(int));
428                }
429            }
430        } catch (...) {
431            env->ReleasePrimitiveArrayCritical(hRGB, rgb, 0);
432            throw;
433        }
434
435        env->ReleasePrimitiveArrayCritical(hRGB, rgb, 0);
436
437        // Construct a new color model
438        if (!allvalid) {
439            jbyteArray bArray = env->NewByteArray(sizeof(vbits));
440            CHECK_NULL_RETURN(bArray, NULL);
441            env->SetByteArrayRegion(bArray, 0, sizeof(vbits), vbits);
442            validBits = JNU_NewObjectByName(env,
443                                            "java/math/BigInteger",
444                                            "([B)V", bArray);
445            JNU_CHECK_EXCEPTION_RETURN(env, NULL);
446        }
447        awt_colormodel =
448            JNU_NewObjectByName(env,
449                                "java/awt/image/IndexColorModel",
450                                "(II[IIILjava/math/BigInteger;)V",
451                                8, 256,
452                                hRGB, 0,
453                                java_awt_image_DataBuffer_TYPE_BYTE,
454                                validBits);
455    }
456    return awt_colormodel;
457}
458
459/**
460 * Called from AwtPalette code when it is determined what grayscale
461 * value (if any) the current logical palette has
462 */
463void AwtWin32GraphicsDevice::SetGrayness(int grayValue)
464{
465    colorData->grayscale = grayValue;
466}
467
468
469/**
470 * Update our dynamic IndexedColorModel.  This happens after
471 * a change to the system palette.  Any surfaces stored in vram
472 * (Win32OffScreenSurfaceData and GDIWindowSurfaceData objects)
473 * refer to this colorModel and use its lookup table and inverse
474 * lookup to calculate correct index values for rgb colors.  So
475 * the colorModel must always reflect the current state of the
476 * system palette.
477 */
478void AwtWin32GraphicsDevice::UpdateDynamicColorModel()
479{
480    if (!javaDevice) {
481        // javaDevice may not be set yet.  If not, return.  In
482        // this situation, we probably don't need an update anyway
483        // since the colorModel will be created with the correct
484        // info when the java side is initialized.
485        return;
486    }
487    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
488    jobject colorModel = env->GetObjectField(javaDevice,
489        dynamicColorModelID);
490    if (!colorModel) {
491        return;
492    }
493    if (env->IsInstanceOf(colorModel, indexCMClass)) {
494        // If colorModel not of type ICM then we're not in 8-bit mode and
495        // don't need to update it
496        jboolean isCopy;
497        unsigned int *newEntries = palette->GetSystemEntries();
498        jintArray rgbArray = (jintArray)env->GetObjectField(colorModel,
499            AwtWin32GraphicsDevice::indexCMrgbID);
500        jintArray cacheArray = (jintArray)env->GetObjectField(colorModel,
501            AwtWin32GraphicsDevice::indexCMcacheID);
502        if (!rgbArray || !cacheArray) {
503            JNU_ThrowInternalError(env, "rgb or lookupcache array of IndexColorModel null");
504            return;
505        }
506        int rgbLength = env->GetArrayLength(rgbArray);
507        int cacheLength = env->GetArrayLength(cacheArray);
508        jint *cmEntries = (jint *)env->GetPrimitiveArrayCritical(rgbArray, &isCopy);
509        if (!cmEntries) {
510            env->ExceptionClear();
511            JNU_ThrowInternalError(env, "Problem retrieving rgb critical array");
512            return;
513        }
514        jint *cache = (jint *)env->GetPrimitiveArrayCritical(cacheArray, &isCopy);
515        if (!cache) {
516            env->ExceptionClear();
517            env->ReleasePrimitiveArrayCritical(rgbArray, cmEntries, JNI_ABORT);
518            JNU_ThrowInternalError(env, "Problem retrieving cache critical array");
519            return;
520        }
521        // Set the new rgb values
522    int i;
523    for (i = 0; i < rgbLength; ++i) {
524            cmEntries[i] = newEntries[i];
525        }
526        // clear out the old cache
527        for (i = 0; i < cacheLength; ++i) {
528            cache[i] = 0;
529        }
530        env->ReleasePrimitiveArrayCritical(cacheArray, cache, 0);
531        env->ReleasePrimitiveArrayCritical(rgbArray, cmEntries, 0);
532
533        // Call WToolkit::paletteChanged() method; this will invalidate
534        // the offscreen surfaces dependent on this dynamic colorModel
535        // to ensure that they get redrawn with the correct color indices
536        env->CallStaticVoidMethod(AwtWin32GraphicsDevice::wToolkitClass,
537            paletteChangedMID);
538    }
539}
540
541unsigned int *AwtWin32GraphicsDevice::GetSystemPaletteEntries()
542{
543    // REMIND: What to do if palette NULL?  Need to throw
544    // some kind of exception?
545    return palette->GetSystemEntries();
546}
547
548unsigned char *AwtWin32GraphicsDevice::GetSystemInverseLUT()
549{
550    // REMIND: What to do if palette NULL?  Need to throw
551    // some kind of exception?
552    return palette->GetSystemInverseLUT();
553}
554
555
556BOOL AwtWin32GraphicsDevice::UpdateSystemPalette()
557{
558    if (colorData->bitsperpixel > 8) {
559        return FALSE;
560    } else {
561        return palette->Update();
562    }
563}
564
565HPALETTE AwtWin32GraphicsDevice::SelectPalette(HDC hDC)
566{
567    if (palette) {
568        return palette->Select(hDC);
569    } else {
570        return NULL;
571    }
572}
573
574void AwtWin32GraphicsDevice::RealizePalette(HDC hDC)
575{
576    if (palette) {
577        palette->Realize(hDC);
578    }
579}
580
581/**
582 * Deterine which device the HWND exists on and return the
583 * appropriate index into the devices array.
584 */
585int AwtWin32GraphicsDevice::DeviceIndexForWindow(HWND hWnd)
586{
587    HMONITOR mon = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
588    int screen = AwtWin32GraphicsDevice::GetScreenFromHMONITOR(mon);
589    return screen;
590}
591
592/**
593 * Get the HPALETTE associated with this device
594 */
595HPALETTE AwtWin32GraphicsDevice::GetPalette()
596{
597    if (palette) {
598        return palette->GetPalette();
599    } else {
600        return NULL;
601    }
602}
603
604/**
605 * Object referring to this device is releasing that reference.
606 * This allows the array holding all devices to be released (once
607 * all references to the array have gone away).
608 */
609void AwtWin32GraphicsDevice::Release()
610{
611    devicesArray->Release();
612}
613
614/**
615 * Links this native object with its java Win32GraphicsDevice.
616 * Need this link because the colorModel of the java device
617 * may be updated from native code.
618 */
619void AwtWin32GraphicsDevice::SetJavaDevice(JNIEnv *env, jobject objPtr)
620{
621    javaDevice = env->NewWeakGlobalRef(objPtr);
622}
623
624/**
625 * Sets horizontal and vertical scale factors
626 */
627void AwtWin32GraphicsDevice::SetScale(float sx, float sy)
628{
629    scaleX = sx;
630    scaleY = sy;
631}
632
633int AwtWin32GraphicsDevice::ScaleUpX(int x)
634{
635    return (int)ceil(x * scaleX);
636}
637
638int AwtWin32GraphicsDevice::ScaleUpY(int y)
639{
640    return (int)ceil(y * scaleY);
641}
642
643int AwtWin32GraphicsDevice::ScaleDownX(int x)
644{
645    return (int)ceil(x / scaleX);
646}
647
648int AwtWin32GraphicsDevice::ScaleDownY(int y)
649{
650    return (int)ceil(y / scaleY);
651}
652
653void AwtWin32GraphicsDevice::InitDesktopScales()
654{
655    float dpiX = -1.0f;
656    float dpiY = -1.0f;
657    GetScreenDpi(GetMonitor(), &dpiX, &dpiY);
658    if (dpiX > 0 && dpiY > 0) {
659        SetScale(dpiX / 96, dpiY / 96);
660    }
661}
662
663float AwtWin32GraphicsDevice::GetScaleX()
664{
665    return scaleX;
666}
667
668float AwtWin32GraphicsDevice::GetScaleY()
669{
670    return scaleY;
671}
672
673/**
674 * Disables offscreen acceleration for this device.  This
675 * sets a flag in the java object that is used to determine
676 * whether offscreen surfaces can be created on the device.
677 */
678void AwtWin32GraphicsDevice::DisableOffscreenAcceleration()
679{
680    // REMIND: noop for now
681}
682
683/**
684 * Invalidates the GraphicsDevice object associated with this
685 * device by disabling offscreen acceleration and calling
686 * invalidate(defIndex) on the java object.
687 */
688void AwtWin32GraphicsDevice::Invalidate(JNIEnv *env)
689{
690    int defIndex = AwtWin32GraphicsDevice::GetDefaultDeviceIndex();
691    DisableOffscreenAcceleration();
692    jobject javaDevice = GetJavaDevice();
693    if (!JNU_IsNull(env, javaDevice)) {
694        JNU_CallMethodByName(env, NULL, javaDevice, "invalidate",
695                             "(I)V", defIndex);
696    }
697}
698
699/**
700 * Static deviceIndex-based methods
701 *
702 * The following methods take a deviceIndex for the list of devices
703 * and perform the appropriate action on that device.  This way of
704 * dereferencing the list of devices allows us to do appropriate
705 * locks around the list to ensure multi-threaded safety.
706 */
707
708
709jobject AwtWin32GraphicsDevice::GetColorModel(JNIEnv *env, jboolean dynamic,
710                                              int deviceIndex)
711{
712    Devices::InstanceAccess devices;
713    return devices->GetDevice(deviceIndex)->GetColorModel(env, dynamic);
714}
715
716LPMONITORINFO AwtWin32GraphicsDevice::GetMonitorInfo(int deviceIndex)
717{
718    Devices::InstanceAccess devices;
719    return devices->GetDevice(deviceIndex)->GetMonitorInfo();
720}
721
722/**
723 * This function updates the data in the MONITORINFOEX structure pointed to by
724 * pMonitorInfo for all monitors on the system.  Added for 4654713.
725 */
726void AwtWin32GraphicsDevice::ResetAllMonitorInfo()
727{
728    //IE in some circumstances generates WM_SETTINGCHANGE message on appearance
729    //and thus triggers this method
730    //but we may not have the devices list initialized yet.
731    if (!Devices::GetInstance()){
732        return;
733    }
734    Devices::InstanceAccess devices;
735    int devicesNum = devices->GetNumDevices();
736    for (int deviceIndex = 0; deviceIndex < devicesNum; deviceIndex++) {
737        HMONITOR monitor = devices->GetDevice(deviceIndex)->GetMonitor();
738        ::GetMonitorInfo(monitor,
739                         devices->GetDevice(deviceIndex)->pMonitorInfo);
740    }
741}
742
743void AwtWin32GraphicsDevice::DisableOffscreenAccelerationForDevice(
744    HMONITOR hMonitor)
745{
746    Devices::InstanceAccess devices;
747    if (hMonitor == NULL) {
748        devices->GetDevice(0)->DisableOffscreenAcceleration();
749    } else {
750        int devicesNum = devices->GetNumDevices();
751        for (int i = 0; i < devicesNum; ++i) {
752            if (devices->GetDevice(i)->GetMonitor() == hMonitor) {
753                devices->GetDevice(i)->DisableOffscreenAcceleration();
754            }
755        }
756    }
757}
758
759HMONITOR AwtWin32GraphicsDevice::GetMonitor(int deviceIndex)
760{
761    Devices::InstanceAccess devices;
762    return devices->GetDevice(deviceIndex)->GetMonitor();
763}
764
765HPALETTE AwtWin32GraphicsDevice::GetPalette(int deviceIndex)
766{
767    Devices::InstanceAccess devices;
768    return devices->GetDevice(deviceIndex)->GetPalette();
769}
770
771void AwtWin32GraphicsDevice::UpdateDynamicColorModel(int deviceIndex)
772{
773    Devices::InstanceAccess devices;
774    devices->GetDevice(deviceIndex)->UpdateDynamicColorModel();
775}
776
777BOOL AwtWin32GraphicsDevice::UpdateSystemPalette(int deviceIndex)
778{
779    Devices::InstanceAccess devices;
780    return devices->GetDevice(deviceIndex)->UpdateSystemPalette();
781}
782
783HPALETTE AwtWin32GraphicsDevice::SelectPalette(HDC hDC, int deviceIndex)
784{
785    Devices::InstanceAccess devices;
786    return devices->GetDevice(deviceIndex)->SelectPalette(hDC);
787}
788
789void AwtWin32GraphicsDevice::RealizePalette(HDC hDC, int deviceIndex)
790{
791    Devices::InstanceAccess devices;
792    devices->GetDevice(deviceIndex)->RealizePalette(hDC);
793}
794
795ColorData *AwtWin32GraphicsDevice::GetColorData(int deviceIndex)
796{
797    Devices::InstanceAccess devices;
798    return devices->GetDevice(deviceIndex)->GetColorData();
799}
800
801/**
802 * Return the grayscale value for the indicated device.
803 */
804int AwtWin32GraphicsDevice::GetGrayness(int deviceIndex)
805{
806    Devices::InstanceAccess devices;
807    return devices->GetDevice(deviceIndex)->GetGrayness();
808}
809
810HDC AwtWin32GraphicsDevice::GetDCFromScreen(int screen) {
811    J2dTraceLn1(J2D_TRACE_INFO,
812                "AwtWin32GraphicsDevice::GetDCFromScreen screen=%d", screen);
813    Devices::InstanceAccess devices;
814    AwtWin32GraphicsDevice *dev = devices->GetDevice(screen);
815    return MakeDCFromMonitor(dev->GetMonitor());
816}
817
818/** Compare elements of MONITORINFOEX structures for the given HMONITORs.
819 * If equal, return TRUE
820 */
821BOOL AwtWin32GraphicsDevice::AreSameMonitors(HMONITOR mon1, HMONITOR mon2) {
822    J2dTraceLn2(J2D_TRACE_INFO,
823                "AwtWin32GraphicsDevice::AreSameMonitors mhnd1=%x mhnd2=%x",
824                mon1, mon2);
825    DASSERT(mon1 != NULL);
826    DASSERT(mon2 != NULL);
827
828    MONITORINFOEX mi1;
829    MONITORINFOEX mi2;
830
831    memset((void*)(&mi1), 0, sizeof(MONITORINFOEX));
832    mi1.cbSize = sizeof(MONITORINFOEX);
833    memset((void*)(&mi2), 0, sizeof(MONITORINFOEX));
834    mi2.cbSize = sizeof(MONITORINFOEX);
835
836    if (::GetMonitorInfo(mon1, &mi1) != 0 &&
837        ::GetMonitorInfo(mon2, &mi2) != 0 )
838    {
839        if (::EqualRect(&mi1.rcMonitor, &mi2.rcMonitor) &&
840            ::EqualRect(&mi1.rcWork, &mi2.rcWork) &&
841            (mi1.dwFlags  == mi1.dwFlags))
842        {
843
844            J2dTraceLn(J2D_TRACE_VERBOSE, "  the monitors are the same");
845            return TRUE;
846        }
847    }
848    J2dTraceLn(J2D_TRACE_VERBOSE, "  the monitors are not the same");
849    return FALSE;
850}
851
852int AwtWin32GraphicsDevice::GetScreenFromHMONITOR(HMONITOR mon) {
853    J2dTraceLn1(J2D_TRACE_INFO,
854                "AwtWin32GraphicsDevice::GetScreenFromHMONITOR mhnd=%x", mon);
855
856    DASSERT(mon != NULL);
857    JNIEnv *env = (JNIEnv*) JNU_GetEnv(jvm, JNI_VERSION_1_2);
858    if (!Devices::GetInstance()) {
859       Devices::UpdateInstance(env);
860    }
861    Devices::InstanceAccess devices;
862
863    for (int i = 0; i < devices->GetNumDevices(); i++) {
864        HMONITOR mhnd = devices->GetDevice(i)->GetMonitor();
865        if (AreSameMonitors(mon, mhnd)) {
866            J2dTraceLn1(J2D_TRACE_VERBOSE, "  Found device: %d", i);
867            return i;
868        }
869    }
870
871    J2dTraceLn1(J2D_TRACE_WARNING,
872                "AwtWin32GraphicsDevice::GetScreenFromHMONITOR(): "\
873                "couldn't find screen for HMONITOR %x, returning default", mon);
874    return AwtWin32GraphicsDevice::GetDefaultDeviceIndex();
875}
876
877
878/**
879 * End of static deviceIndex-based methods
880 */
881
882
883    const DWORD REQUIRED_FLAGS = (   //Flags which must be set in
884     PFD_SUPPORT_GDI |               //in the PixelFormatDescriptor.
885     PFD_DRAW_TO_WINDOW);            //Used to choose the default config
886                                     //and to check formats in
887                                     //isPixFmtSupported()
888extern "C" {
889
890JNIEXPORT void JNICALL
891Java_sun_awt_Win32GraphicsDevice_initIDs(JNIEnv *env, jclass cls)
892{
893    TRY;
894
895    /* class ids */
896    jclass iCMClass = env->FindClass("java/awt/image/IndexColorModel");
897    CHECK_NULL(iCMClass);
898    AwtWin32GraphicsDevice::indexCMClass = (jclass) env->NewGlobalRef(iCMClass);
899    env->DeleteLocalRef(iCMClass);
900    DASSERT(AwtWin32GraphicsDevice::indexCMClass);
901    CHECK_NULL(AwtWin32GraphicsDevice::indexCMClass);
902
903    jclass wTClass = env->FindClass("sun/awt/windows/WToolkit");
904    CHECK_NULL(wTClass);
905    AwtWin32GraphicsDevice::wToolkitClass = (jclass)env->NewGlobalRef(wTClass);
906    env->DeleteLocalRef(wTClass);
907    DASSERT(AwtWin32GraphicsDevice::wToolkitClass);
908    CHECK_NULL(AwtWin32GraphicsDevice::wToolkitClass);
909
910    /* field ids */
911    AwtWin32GraphicsDevice::dynamicColorModelID = env->GetFieldID(cls,
912        "dynamicColorModel", "Ljava/awt/image/ColorModel;");
913    DASSERT(AwtWin32GraphicsDevice::dynamicColorModelID);
914    CHECK_NULL(AwtWin32GraphicsDevice::dynamicColorModelID);
915
916    AwtWin32GraphicsDevice::indexCMrgbID =
917        env->GetFieldID(AwtWin32GraphicsDevice::indexCMClass, "rgb", "[I");
918    DASSERT(AwtWin32GraphicsDevice::indexCMrgbID);
919    CHECK_NULL(AwtWin32GraphicsDevice::indexCMrgbID);
920
921    AwtWin32GraphicsDevice::indexCMcacheID =
922        env->GetFieldID(AwtWin32GraphicsDevice::indexCMClass,
923        "lookupcache", "[I");
924    DASSERT(AwtWin32GraphicsDevice::indexCMcacheID);
925    CHECK_NULL(AwtWin32GraphicsDevice::indexCMcacheID);
926
927    /* method ids */
928    AwtWin32GraphicsDevice::paletteChangedMID = env->GetStaticMethodID(
929        AwtWin32GraphicsDevice::wToolkitClass, "paletteChanged", "()V");
930    DASSERT(AwtWin32GraphicsDevice::paletteChangedMID);
931    CHECK_NULL(AwtWin32GraphicsDevice::paletteChangedMID);
932
933    // Only want to call this once per session
934    make_uns_ordered_dither_array(img_oda_alpha, 256);
935
936    // workaround JDK-6477756, ignore return value to keep dll in memory
937    JDK_LoadSystemLibrary("opengl32.dll");
938
939    CATCH_BAD_ALLOC;
940}
941
942} /* extern "C" */
943
944
945/*
946 * Class:     sun_awt_Win32GraphicsDevice
947 * Method:    getMaxConfigsImpl
948 * Signature: ()I
949 */
950
951JNIEXPORT jint JNICALL Java_sun_awt_Win32GraphicsDevice_getMaxConfigsImpl
952    (JNIEnv* jniEnv, jobject theThis, jint screen) {
953        TRY;
954    HDC hDC = AwtWin32GraphicsDevice::GetDCFromScreen(screen);
955
956    PIXELFORMATDESCRIPTOR pfd;
957    int max = ::DescribePixelFormat(hDC, 1, sizeof(PIXELFORMATDESCRIPTOR),
958        &pfd);
959    if (hDC != NULL) {
960        VERIFY(::DeleteDC(hDC));
961        hDC = NULL;
962    }
963    //If ::DescribePixelFormat() fails, max = 0
964    //In this case, we return 1 config with visual number 0
965    if (max == 0) {
966        max = 1;
967    }
968    return (jint)max;
969        CATCH_BAD_ALLOC_RET(0);
970}
971
972/*
973 * Class:     sun_awt_Win32GraphicsDevice
974 * Method:    isPixFmtSupported
975 * Signature: (I)Z
976 */
977
978JNIEXPORT jboolean JNICALL Java_sun_awt_Win32GraphicsDevice_isPixFmtSupported
979    (JNIEnv* env, jobject theThis, jint pixFmtID, jint screen) {
980        TRY;
981    jboolean suppColor = JNI_TRUE;
982    HDC hDC = AwtWin32GraphicsDevice::GetDCFromScreen(screen);
983
984    if (pixFmtID == 0) {
985        return true;
986    }
987
988    PIXELFORMATDESCRIPTOR pfd;
989    int max = ::DescribePixelFormat(hDC, (int)pixFmtID,
990        sizeof(PIXELFORMATDESCRIPTOR), &pfd);
991    DASSERT(max);
992
993    //Check for supported ColorModel
994    if ((pfd.cColorBits < 8) ||
995       ((pfd.cColorBits == 8) && (pfd.iPixelType != PFD_TYPE_COLORINDEX))) {
996        //Note: this still allows for PixelFormats with > 8 color bits
997        //which use COLORINDEX instead of RGB.  This seems to work fine,
998        //although issues may crop up involving PFD_NEED_PALETTE, which
999        //is not currently taken into account.
1000        //If changes are made, they should also be reflected in
1001        //getDefaultPixID.
1002        suppColor = JNI_FALSE;
1003    }
1004
1005    if (hDC != NULL) {
1006        VERIFY(::DeleteDC(hDC));
1007        hDC = NULL;
1008    }
1009    return (((pfd.dwFlags & REQUIRED_FLAGS) == REQUIRED_FLAGS) && suppColor) ?
1010     JNI_TRUE : JNI_FALSE;
1011        CATCH_BAD_ALLOC_RET(FALSE);
1012}
1013
1014/*
1015 * Class:     sun_awt_Win32GraphicsDevice
1016 * Method:    getDefaultPixIDImpl
1017 * Signature: (I)I
1018 */
1019
1020JNIEXPORT jint JNICALL Java_sun_awt_Win32GraphicsDevice_getDefaultPixIDImpl
1021    (JNIEnv* env, jobject theThis, jint screen) {
1022        TRY;
1023    int pixFmtID = 0;
1024    HDC hDC = AwtWin32GraphicsDevice::GetDCFromScreen(screen);
1025
1026    PIXELFORMATDESCRIPTOR pfd = {
1027        sizeof(PIXELFORMATDESCRIPTOR),
1028        1,               //version
1029        REQUIRED_FLAGS,  //flags
1030        0,               //iPixelType
1031        0,               //cColorBits
1032        0,0,0,0,0,0,0,0, //cRedBits, cRedShift, green, blue, alpha
1033        0,0,0,0,0,       //cAccumBits, cAccumRedBits, green, blue, alpha
1034        0,0,0,0,0,0,0,0  //etc.
1035    };
1036
1037    //If 8-bit mode, must use Indexed mode
1038    if (8 == ::GetDeviceCaps(hDC, BITSPIXEL)) {
1039        pfd.iPixelType = PFD_TYPE_COLORINDEX;
1040    }
1041
1042    pixFmtID = ::ChoosePixelFormat(hDC, &pfd);
1043    if (pixFmtID == 0) {
1044        //Return 0 if GDI call fails.
1045        if (hDC != NULL) {
1046            VERIFY(::DeleteDC(hDC));
1047            hDC = NULL;
1048        }
1049        return pixFmtID;
1050    }
1051
1052    if (JNI_FALSE == Java_sun_awt_Win32GraphicsDevice_isPixFmtSupported(
1053     env, theThis, pixFmtID, screen)) {
1054        /* Can't find a suitable pixel format ID.  Fall back on 0. */
1055        pixFmtID = 0;
1056    }
1057
1058    VERIFY(::DeleteDC(hDC));
1059    hDC = NULL;
1060    return (jint)pixFmtID;
1061        CATCH_BAD_ALLOC_RET(0);
1062}
1063
1064/*
1065 * Class:     sun_awt_Win32GraphicsDevice
1066 * Method:    enterFullScreenExclusive
1067 * Signature: (Ljava/awt/peer/WindowPeer;)V
1068 */
1069
1070JNIEXPORT void JNICALL
1071Java_sun_awt_Win32GraphicsDevice_enterFullScreenExclusive(
1072        JNIEnv* env, jobject graphicsDevice,
1073        jint screen, jobject windowPeer) {
1074
1075    TRY;
1076
1077    PDATA pData;
1078    JNI_CHECK_PEER_RETURN(windowPeer);
1079
1080    AwtWindow *window = (AwtWindow *)pData;  // safe cast since we are called
1081                                             // with the WWindowPeer object
1082    HWND hWnd = window->GetHWnd();
1083
1084    if (!::SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0,
1085                        SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOSIZE))
1086    {
1087        J2dTraceLn1(J2D_TRACE_ERROR,
1088                    "Error %d setting topmost attribute to fs window",
1089                    ::GetLastError());
1090    }
1091
1092    CATCH_BAD_ALLOC;
1093}
1094
1095/*
1096 * Class:     sun_awt_Win32GraphicsDevice
1097 * Method:    exitFullScreenExclusive
1098 * Signature: (Ljava/awt/peer/WindowPeer;)V
1099 */
1100
1101JNIEXPORT void JNICALL
1102Java_sun_awt_Win32GraphicsDevice_exitFullScreenExclusive(
1103        JNIEnv* env, jobject graphicsDevice,
1104        jint screen, jobject windowPeer) {
1105
1106    TRY;
1107
1108    PDATA pData;
1109    JNI_CHECK_PEER_RETURN(windowPeer);
1110
1111    AwtWindow *window = (AwtWindow *)pData;  // safe cast since we are called
1112                                             // with the WWindowPeer object
1113    HWND hWnd = window->GetHWnd();
1114
1115    jobject target = env->GetObjectField(windowPeer, AwtObject::targetID);
1116    jboolean alwaysOnTop = JNU_GetFieldByName(env, NULL, target, "alwaysOnTop", "Z").z;
1117    env->DeleteLocalRef(target);
1118
1119    if (!::SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1120                        SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOSIZE))
1121    {
1122        J2dTraceLn1(J2D_TRACE_ERROR,
1123                    "Error %d unsetting topmost attribute to fs window",
1124                    ::GetLastError());
1125    }
1126
1127    // We should restore alwaysOnTop state as it's anyway dropped here
1128    Java_sun_awt_windows_WWindowPeer_setAlwaysOnTopNative(env, windowPeer, alwaysOnTop);
1129
1130    CATCH_BAD_ALLOC;
1131}
1132
1133jobject CreateDisplayMode(JNIEnv* env, jint width, jint height,
1134    jint bitDepth, jint refreshRate) {
1135
1136    TRY;
1137
1138    jclass displayModeClass = env->FindClass("java/awt/DisplayMode");
1139    if (JNU_IsNull(env, displayModeClass)) {
1140        env->ExceptionClear();
1141        JNU_ThrowInternalError(env, "Could not get display mode class");
1142        return NULL;
1143    }
1144
1145    jmethodID cid = env->GetMethodID(displayModeClass, "<init>", "(IIII)V");
1146    if (cid == NULL) {
1147        env->ExceptionClear();
1148        JNU_ThrowInternalError(env, "Could not get display mode constructor");
1149        return NULL;
1150    }
1151
1152    jobject displayMode = env->NewObject(displayModeClass, cid, width,
1153        height, bitDepth, refreshRate);
1154    return displayMode;
1155
1156    CATCH_BAD_ALLOC_RET(NULL);
1157}
1158
1159/**
1160 * A utility function which retrieves a DISPLAY_DEVICE information
1161 * given a screen number.
1162 *
1163 * If the function was able to find an attached device for the given screen
1164 * number, the lpDisplayDevice will be initialized with the data and
1165 * the function will return TRUE, otherwise it returns FALSE and contents
1166 * of the structure pointed to by lpDisplayDevice is undefined.
1167 */
1168static BOOL
1169GetAttachedDisplayDevice(int screen, DISPLAY_DEVICE *lpDisplayDevice)
1170{
1171    DWORD dwDeviceNum = 0;
1172    lpDisplayDevice->cb = sizeof(DISPLAY_DEVICE);
1173    while (EnumDisplayDevices(NULL, dwDeviceNum, lpDisplayDevice, 0) &&
1174           dwDeviceNum < 20) // avoid infinite loop with buggy drivers
1175    {
1176        if (lpDisplayDevice->StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) {
1177            Devices::InstanceAccess devices;
1178            MONITORINFOEX *pMonInfo =
1179                (LPMONITORINFOEX)devices->GetDevice(screen)->GetMonitorInfo();
1180            // make sure the device names match
1181            if (wcscmp(pMonInfo->szDevice, lpDisplayDevice->DeviceName) == 0) {
1182                return TRUE;
1183            }
1184        }
1185        dwDeviceNum++;
1186    }
1187    return FALSE;
1188}
1189
1190/*
1191 * Class:     sun_awt_Win32GraphicsDevice
1192 * Method:    getCurrentDisplayMode
1193 * Signature: (IZ)Ljava/awt/DisplayMode;
1194 */
1195JNIEXPORT jobject JNICALL
1196Java_sun_awt_Win32GraphicsDevice_getCurrentDisplayMode
1197    (JNIEnv* env, jobject graphicsDevice, jint screen)
1198{
1199    TRY;
1200
1201    DEVMODE dm;
1202    LPTSTR pName = NULL;
1203
1204    dm.dmSize = sizeof(dm);
1205    dm.dmDriverExtra = 0;
1206
1207    DISPLAY_DEVICE displayDevice;
1208    if (GetAttachedDisplayDevice(screen, &displayDevice)) {
1209        pName = displayDevice.DeviceName;
1210    }
1211    if (!EnumDisplaySettings(pName, ENUM_CURRENT_SETTINGS, &dm))
1212    {
1213        return NULL;
1214    }
1215
1216    return CreateDisplayMode(env, dm.dmPelsWidth,
1217        dm.dmPelsHeight, dm.dmBitsPerPel, dm.dmDisplayFrequency);
1218
1219    CATCH_BAD_ALLOC_RET(NULL);
1220}
1221
1222/*
1223 * Class:     sun_awt_Win32GraphicsDevice
1224 * Method:    configDisplayMode
1225 * Signature: (IIIIZ)V
1226 */
1227JNIEXPORT void JNICALL
1228Java_sun_awt_Win32GraphicsDevice_configDisplayMode
1229    (JNIEnv* env, jobject graphicsDevice, jint screen, jobject windowPeer,
1230     jint width, jint height, jint bitDepth, jint refreshRate)
1231{
1232    TRY;
1233
1234        DEVMODE dm;
1235
1236    dm.dmSize = sizeof(dm);
1237    dm.dmDriverExtra = 0;
1238    dm.dmPelsWidth = width;
1239    dm.dmPelsHeight = height;
1240    dm.dmBitsPerPel = bitDepth;
1241    dm.dmDisplayFrequency = refreshRate;
1242    dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT |
1243        DM_BITSPERPEL | DM_DISPLAYFREQUENCY;
1244
1245    // ChangeDisplaySettings works only on the primary screen.
1246    // ChangeDisplaySettingsEx is not available on NT,
1247    // so it'd be nice not to break it if we can help it.
1248    if (screen == AwtWin32GraphicsDevice::GetDefaultDeviceIndex()) {
1249        if (::ChangeDisplaySettings(&dm, CDS_FULLSCREEN) !=
1250            DISP_CHANGE_SUCCESSFUL)
1251        {
1252            JNU_ThrowInternalError(env,
1253                                   "Could not set display mode");
1254        }
1255        return;
1256    }
1257
1258    DISPLAY_DEVICE displayDevice;
1259    if (!GetAttachedDisplayDevice(screen, &displayDevice) ||
1260        (::ChangeDisplaySettingsEx(displayDevice.DeviceName, &dm, NULL, CDS_FULLSCREEN, NULL) !=
1261          DISP_CHANGE_SUCCESSFUL))
1262    {
1263        JNU_ThrowInternalError(env,
1264                               "Could not set display mode");
1265    }
1266
1267    CATCH_BAD_ALLOC;
1268}
1269
1270class EnumDisplayModeParam {
1271public:
1272    EnumDisplayModeParam(JNIEnv* e, jobject a) : env(e), arrayList(a) {}
1273    JNIEnv* env;
1274    jobject arrayList;
1275};
1276
1277void addDisplayMode(JNIEnv* env, jobject arrayList, jint width,
1278    jint height, jint bitDepth, jint refreshRate) {
1279
1280    TRY;
1281
1282    jobject displayMode = CreateDisplayMode(env, width, height,
1283        bitDepth, refreshRate);
1284    if (!JNU_IsNull(env, displayMode)) {
1285        jclass arrayListClass = env->GetObjectClass(arrayList);
1286        if (JNU_IsNull(env, arrayListClass)) {
1287            JNU_ThrowInternalError(env,
1288                "Could not get class java.util.ArrayList");
1289            return;
1290        }
1291        jmethodID mid = env->GetMethodID(arrayListClass, "add",
1292        "(Ljava/lang/Object;)Z");
1293        if (mid == NULL) {
1294            env->ExceptionClear();
1295            JNU_ThrowInternalError(env,
1296                "Could not get method java.util.ArrayList.add()");
1297            return;
1298        }
1299        env->CallObjectMethod(arrayList, mid, displayMode);
1300        env->DeleteLocalRef(displayMode);
1301    }
1302
1303    CATCH_BAD_ALLOC;
1304}
1305
1306/*
1307 * Class:     sun_awt_Win32GraphicsDevice
1308 * Method:    enumDisplayModes
1309 * Signature: (Ljava/util/ArrayList;Z)V
1310 */
1311JNIEXPORT void JNICALL Java_sun_awt_Win32GraphicsDevice_enumDisplayModes
1312    (JNIEnv* env, jobject graphicsDevice, jint screen, jobject arrayList)
1313{
1314
1315    TRY;
1316
1317    DEVMODE dm;
1318    LPTSTR pName = NULL;
1319    DISPLAY_DEVICE displayDevice;
1320
1321
1322    if (GetAttachedDisplayDevice(screen, &displayDevice)) {
1323        pName = displayDevice.DeviceName;
1324    }
1325
1326    dm.dmSize = sizeof(dm);
1327    dm.dmDriverExtra = 0;
1328
1329    BOOL bContinue = TRUE;
1330    for (int i = 0; bContinue; i++) {
1331        bContinue = EnumDisplaySettings(pName, i, &dm);
1332        if (dm.dmBitsPerPel >= 8) {
1333            addDisplayMode(env, arrayList, dm.dmPelsWidth, dm.dmPelsHeight,
1334                           dm.dmBitsPerPel, dm.dmDisplayFrequency);
1335            JNU_CHECK_EXCEPTION(env);
1336        }
1337    }
1338
1339    CATCH_BAD_ALLOC;
1340}
1341
1342/*
1343 * Class:     sun_awt_Win32GraphicsDevice
1344 * Method:    makeColorModel
1345 * Signature: ()Ljava/awt/image/ColorModel
1346 */
1347
1348JNIEXPORT jobject JNICALL
1349    Java_sun_awt_Win32GraphicsDevice_makeColorModel
1350    (JNIEnv *env, jobject thisPtr, jint screen, jboolean dynamic)
1351{
1352    Devices::InstanceAccess devices;
1353    return devices->GetDevice(screen)->GetColorModel(env, dynamic);
1354}
1355
1356/*
1357 * Class:     sun_awt_Win32GraphicsDevice
1358 * Method:    initDevice
1359 * Signature: (I)V
1360 */
1361JNIEXPORT void JNICALL
1362    Java_sun_awt_Win32GraphicsDevice_initDevice
1363    (JNIEnv *env, jobject thisPtr, jint screen)
1364{
1365    Devices::InstanceAccess devices;
1366    devices->GetDevice(screen)->SetJavaDevice(env, thisPtr);
1367}
1368
1369/*
1370 * Class:     sun_awt_Win32GraphicsDevice
1371 * Method:    setNativeScale
1372 * Signature: (I,F,F)V
1373 */
1374JNIEXPORT void JNICALL
1375    Java_sun_awt_Win32GraphicsDevice_setNativeScale
1376    (JNIEnv *env, jobject thisPtr, jint screen, jfloat scaleX, jfloat scaleY)
1377{
1378    Devices::InstanceAccess devices;
1379    AwtWin32GraphicsDevice *device = devices->GetDevice(screen);
1380
1381    if (device != NULL ) {
1382        device->SetScale(scaleX, scaleY);
1383    }
1384}
1385
1386/*
1387 * Class:     sun_awt_Win32GraphicsDevice
1388 * Method:    getNativeScaleX
1389 * Signature: (I)F
1390 */
1391JNIEXPORT jfloat JNICALL
1392    Java_sun_awt_Win32GraphicsDevice_getNativeScaleX
1393    (JNIEnv *env, jobject thisPtr, jint screen)
1394{
1395    Devices::InstanceAccess devices;
1396    AwtWin32GraphicsDevice *device = devices->GetDevice(screen);
1397    return (device == NULL) ? 1 : device->GetScaleX();
1398}
1399
1400/*
1401 * Class:     sun_awt_Win32GraphicsDevice
1402 * Method:    getNativeScaleY
1403 * Signature: (I)F
1404 */
1405JNIEXPORT jfloat JNICALL
1406    Java_sun_awt_Win32GraphicsDevice_getNativeScaleY
1407    (JNIEnv *env, jobject thisPtr, jint screen)
1408{
1409    Devices::InstanceAccess devices;
1410    AwtWin32GraphicsDevice *device = devices->GetDevice(screen);
1411    return (device == NULL) ? 1 : device->GetScaleY();
1412}
1413
1414/*
1415* Class:     sun_awt_Win32GraphicsDevice
1416* Method:    initNativeScale
1417* Signature: (I)V;
1418*/
1419JNIEXPORT void JNICALL
1420Java_sun_awt_Win32GraphicsDevice_initNativeScale
1421(JNIEnv *env, jobject thisPtr, jint screen)
1422{
1423    Devices::InstanceAccess devices;
1424    AwtWin32GraphicsDevice *device = devices->GetDevice(screen);
1425
1426    if (device != NULL) {
1427        device->InitDesktopScales();
1428    }
1429}
1430
1431