1/*
2 * Copyright (c) 2001, 2008, 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 "awt.h"
27#include "awt_Palette.h"
28#include "awt_Component.h"
29#include "img_util_md.h"
30#include "awt_CustomPaletteDef.h"
31#include "Trace.h"
32
33BOOL AwtPalette::m_useCustomPalette = TRUE;
34
35#define ERROR_GRAY (-1)
36#define NON_GRAY 0
37#define LINEAR_STATIC_GRAY 1
38#define NON_LINEAR_STATIC_GRAY 2
39
40/**
41 * Select the palette into the given HDC.  This will
42 * allow operations using this HDC to access the palette
43 * colors/indices.
44 */
45HPALETTE AwtPalette::Select(HDC hDC)
46{
47    HPALETTE prevPalette = NULL;
48    if (logicalPalette) {
49        BOOL background = !(m_useCustomPalette);
50        prevPalette = ::SelectPalette(hDC, logicalPalette, background);
51    }
52    return prevPalette;
53}
54
55/**
56 * Realize the palette of the given HDC.  This will attempt to
57 * install the palette of the HDC onto the device associated with
58 * that HDC.
59 */
60void AwtPalette::Realize(HDC hDC)
61{
62    if (logicalPalette) {
63        if (!m_useCustomPalette ||
64            AwtComponent::QueryNewPaletteCalled() ||
65            AwtToolkit::GetInstance().HasDisplayChanged()) {
66            // Fix for bug 4178909, workaround for Windows bug.  Shouldn't
67            // do a RealizePalette until the first QueryNewPalette message
68            // has been processed.
69            // But if we are switching the primary monitor from non-8bpp
70            // to 8bpp mode, we may not get any palette messages during
71            // the display change event.  Go ahead and realize the palette
72            // now anyway in this situation.  This was especially noticeable
73            // on win2k in multimon.  Note that there still seems to be some
74            // problem with actually setting the palette on the primary
75            // screen until after QNP is called, but at least the
76            // secondary devices can correctly realize the palette.
77            ::RealizePalette(hDC);
78        }
79    }
80}
81
82/**
83 * Disable the use of our custom palette.  This method is called
84 * during initialization if we detect that we are running inside
85 * the plugin; we do not want to clobber our parent application's
86 * palette with our own in that situation.
87 */
88void AwtPalette::DisableCustomPalette()
89{
90    m_useCustomPalette = FALSE;
91}
92
93/**
94 * Returns whether we are currently using a custom palette.  Used
95 * by AwtWin32GraphicsDevice when creating the colorModel of the
96 * device.
97 */
98BOOL AwtPalette::UseCustomPalette()
99{
100    return m_useCustomPalette;
101}
102
103
104/**
105 * Constructor.  Initialize the system and logical palettes.
106 * used by this object.
107 */
108AwtPalette::AwtPalette(AwtWin32GraphicsDevice *device)
109{
110    this->device = device;
111    Update();
112    UpdateLogical();
113}
114
115/**
116 * Retrieves system palette entries. Includes a workaround for some
117 * video drivers which may not support the GSPE call but may return
118 * valid values from this procedure.
119 */
120int AwtPalette::FetchPaletteEntries(HDC hDC, PALETTEENTRY* pPalEntries)
121{
122    LOGPALETTE* pLogPal = 0;
123    HPALETTE hPal = 0;
124    HPALETTE hPalOld = 0;
125    int numEntries;
126
127    numEntries = ::GetSystemPaletteEntries(hDC, 0, 256, pPalEntries);
128
129    if (numEntries > 0) {
130        return numEntries;
131    }
132    // Workaround: some drivers do not support GetSysPalEntries
133
134    pLogPal = (LOGPALETTE*) new char[sizeof(LOGPALETTE)
135                                    + 256*sizeof(PALETTEENTRY)];
136    if (pLogPal == NULL) {
137        return 0;
138    }
139
140    pLogPal->palVersion = 0x300;
141    pLogPal->palNumEntries = 256;
142    int iEntry;
143    PALETTEENTRY* pEntry;
144    for (iEntry = 0; iEntry < 256; iEntry++) {
145        pEntry = pLogPal->palPalEntry + iEntry;
146        pEntry->peRed = iEntry;
147        pEntry->peGreen = pEntry->peBlue = 0;
148        pEntry->peFlags = PC_EXPLICIT;
149    }
150    hPal = ::CreatePalette(pLogPal);
151    delete pLogPal;
152    if ( hPal == 0 ) {
153        return 0;
154    }
155
156    hPalOld = ::SelectPalette(hDC, hPal, 1);
157    if (hPalOld == 0) {
158        ::DeleteObject(hPal);
159        return 0;
160    }
161    ::RealizePalette(hDC);
162
163    COLORREF rgb;
164    for (iEntry = 0; iEntry < 256; iEntry++) {
165        rgb = ::GetNearestColor(hDC, PALETTEINDEX(iEntry));
166        pPalEntries[iEntry].peRed = GetRValue(rgb);
167        pPalEntries[iEntry].peGreen = GetGValue(rgb);
168        pPalEntries[iEntry].peBlue = GetBValue(rgb);
169    }
170
171    ::SelectPalette(hDC, hPalOld, 0 );
172    ::DeleteObject(hPal);
173    ::RealizePalette(hDC);
174
175    return 256;
176}
177
178int AwtPalette::GetGSType(PALETTEENTRY* pPalEntries)
179{
180    int isGray = 1;
181    int isLinearStaticGray = 1;
182    int isNonLinearStaticGray = 1;
183    int iEntry;
184    char bUsed[256];
185    BYTE r, g, b;
186
187    memset(bUsed, 0, sizeof(bUsed));
188    for (iEntry = 0; iEntry < 256; iEntry++) {
189        r = pPalEntries[iEntry].peRed;
190        g = pPalEntries[iEntry].peGreen;
191        b = pPalEntries[iEntry].peBlue;
192        if (r != g || r != b) {
193            isGray = 0;
194            break;
195        } else {
196            // the values are gray
197            if (r != iEntry) {
198                // it's not linear
199                // but it could be non-linear static gray
200                isLinearStaticGray = 0;
201            }
202            bUsed[r] = 1;
203        }
204    }
205
206    if (isGray && !isLinearStaticGray) {
207        // check if all 256 grays are there
208        // if that's the case, it's non-linear static gray
209        for (iEntry = 0; iEntry < 256; iEntry++ ) {
210            if (!bUsed[iEntry]) {
211                // not non-linear (not all 256 colors are used)
212                isNonLinearStaticGray = 0;
213                break;
214            }
215        }
216    }
217
218    if (!isGray) {
219        J2dTraceLn(J2D_TRACE_INFO,
220                   "Detected palette: NON_GRAY/USER-MODIFIABLE");
221        return NON_GRAY;
222    }
223    if (isLinearStaticGray) {
224        J2dTraceLn(J2D_TRACE_INFO,
225                   "Detected palette: LINEAR_STATIC_GRAY");
226        return LINEAR_STATIC_GRAY;
227    }
228    if (isNonLinearStaticGray) {
229        J2dTraceLn(J2D_TRACE_INFO,
230                   "Detected palette: NON_LINEAR_STATIC_GRAY");
231        return NON_LINEAR_STATIC_GRAY;
232    }
233
234    J2dTraceLn(J2D_TRACE_ERROR,
235               "Unable to detect palette type, non-gray is assumed");
236    // not supposed to be here, error
237    return ERROR_GRAY;
238}
239
240/**
241 * Updates our system palette variables to make sure they match
242 * the current state of the actual system palette.  This method
243 * is called during AwtPalette creation and after palette changes.
244 * Return whether there were any palette changes from the previous
245 * system palette.
246 */
247BOOL AwtPalette::Update()
248{
249    PALETTEENTRY pe[256];
250    int numEntries = 0;
251    int bitsPerPixel;
252    int i;
253    HDC hDC;
254
255    hDC = device->GetDC();
256    if (!hDC) {
257        return FALSE;
258    }
259    bitsPerPixel = ::GetDeviceCaps(hDC, BITSPIXEL);
260    device->ReleaseDC(hDC);
261    if (8 != bitsPerPixel) {
262        return FALSE;
263    }
264
265    hDC = device->GetDC();
266    numEntries = FetchPaletteEntries(hDC, pe);
267
268    device->ReleaseDC(hDC);
269
270    if ((numEntries == numSystemEntries) &&
271        (0 == memcmp(pe, systemEntriesWin32, numEntries * sizeof(PALETTEENTRY))))
272    {
273        return FALSE;
274    }
275
276    // make this system palette the new cached win32 palette
277    numEntries = (numEntries > 256)? 256: numEntries;
278    memcpy(systemEntriesWin32, pe, numEntries * sizeof(PALETTEENTRY));
279    numSystemEntries = numEntries;
280
281    // Create jdk-style system palette
282    int startIndex = 0, endIndex = numEntries-1;
283    int staticGrayType = GetGSType(systemEntriesWin32);
284
285    if (staticGrayType == LINEAR_STATIC_GRAY) {
286        device->SetGrayness(GS_STATICGRAY);
287    } else if (staticGrayType == NON_LINEAR_STATIC_GRAY) {
288        device->SetGrayness(GS_NONLINGRAY);
289    } else if (getenv("FORCEGRAY")) {
290        J2dTraceLn(J2D_TRACE_INFO,
291                    "Gray Palette Forced via FORCEGRAY");
292        // Need to zero first and last ten
293        // palette entries. Otherwise in UpdateDynamicColorModel
294        // we could set non-gray values to the palette.
295        for (i = 0; i < 10; i++) {
296            systemEntries[i] = 0x00000000;
297            systemEntries[i+246] = 0x00000000;
298        }
299        numEntries -= 20;
300        startIndex = 10;
301        endIndex -= 10;
302        device->SetGrayness(GS_INDEXGRAY);
303    } else {
304        device->SetGrayness(GS_NOTGRAY);
305    }
306
307    for (i = startIndex; i <= endIndex; i++) {
308        systemEntries[i] =  0xff000000
309                        | (pe[i].peRed << 16)
310                        | (pe[i].peGreen << 8)
311                        | (pe[i].peBlue);
312    }
313
314    systemInverseLUT =
315        initCubemap((int *)systemEntries, numEntries, 32);
316
317    ColorData *cData = device->GetColorData();
318    if ((device->GetGrayness() == GS_NONLINGRAY ||
319         device->GetGrayness() == GS_INDEXGRAY) &&
320        cData != NULL) {
321
322        if (cData->pGrayInverseLutData != NULL) {
323            free(cData->pGrayInverseLutData);
324            cData->pGrayInverseLutData = NULL;
325        }
326        initInverseGrayLut((int*)systemEntries, 256, device->GetColorData());
327    }
328
329    return TRUE;
330}
331
332
333/**
334 * Creates our custom palette based on: the current system palette,
335 * the grayscale-ness of the system palette, and the state of the
336 * primary device.
337 */
338void AwtPalette::UpdateLogical()
339{
340    // Create and initialize a palette
341    int nEntries = 256;
342    char *buf = NULL;
343    buf = new char[sizeof(LOGPALETTE) + nEntries *
344        sizeof(PALETTEENTRY)];
345
346    LOGPALETTE *pLogPal = (LOGPALETTE*)buf;
347    PALETTEENTRY *pPalEntries = (PALETTEENTRY *)(&(pLogPal->palPalEntry[0]));
348
349    memcpy(pPalEntries, systemEntriesWin32, 256 * sizeof(PALETTEENTRY));
350
351    PALETTEENTRY *pPal = pPalEntries;
352    int i;
353    int staticGrayType = device->GetGrayness();
354    if (staticGrayType == GS_INDEXGRAY) {
355        float m = 255.0f / 235.0f;
356        float g = 0.5f;
357        pPal = &pPalEntries[10];
358        for (i = 10; i < 246; i++, pPal++) {
359            pPal->peRed = pPal->peGreen = pPal->peBlue =
360                (int)g;
361            g += m;
362            pPal->peFlags = PC_NOCOLLAPSE;
363        }
364    } else if (staticGrayType == GS_NOTGRAY) {
365        for (i = 10; i < 246; i++) {
366            pPalEntries[i] = customPalette[i-10];
367        }
368    }
369    pLogPal->palNumEntries = 256;
370    pLogPal->palVersion = 0x300;
371    logicalPalette = ::CreatePalette(pLogPal);
372
373    for (i = 0; i < nEntries; i++) {
374        logicalEntries[i] =  0xff000000
375                        | (pPalEntries[i].peRed << 16)
376                        | (pPalEntries[i].peGreen << 8)
377                        | (pPalEntries[i].peBlue);
378    }
379    delete [] buf;
380}
381