view src/java.desktop/windows/native/libawt/windows/awt_Palette.cpp @ 17373:2f60047b6f6d

8179990: Cleaner palette entry handling Reviewed-by: prr, mschoene, rhalade
author serb
date Wed, 17 May 2017 18:22:54 -0700
parents 678faa7d1a6a
children
line wrap: on
line source
/*
 * Copyright (c) 2001, 2017, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

#include "awt.h"
#include "awt_Palette.h"
#include "awt_Component.h"
#include "img_util_md.h"
#include "awt_CustomPaletteDef.h"
#include "Trace.h"

BOOL AwtPalette::m_useCustomPalette = TRUE;

#define ERROR_GRAY (-1)
#define NON_GRAY 0
#define LINEAR_STATIC_GRAY 1
#define NON_LINEAR_STATIC_GRAY 2

/**
 * Select the palette into the given HDC.  This will
 * allow operations using this HDC to access the palette
 * colors/indices.
 */
HPALETTE AwtPalette::Select(HDC hDC)
{
    HPALETTE prevPalette = NULL;
    if (logicalPalette) {
        BOOL background = !(m_useCustomPalette);
        prevPalette = ::SelectPalette(hDC, logicalPalette, background);
    }
    return prevPalette;
}

/**
 * Realize the palette of the given HDC.  This will attempt to
 * install the palette of the HDC onto the device associated with
 * that HDC.
 */
void AwtPalette::Realize(HDC hDC)
{
    if (logicalPalette) {
        if (!m_useCustomPalette ||
            AwtComponent::QueryNewPaletteCalled() ||
            AwtToolkit::GetInstance().HasDisplayChanged()) {
            // Fix for bug 4178909, workaround for Windows bug.  Shouldn't
            // do a RealizePalette until the first QueryNewPalette message
            // has been processed.
            // But if we are switching the primary monitor from non-8bpp
            // to 8bpp mode, we may not get any palette messages during
            // the display change event.  Go ahead and realize the palette
            // now anyway in this situation.  This was especially noticeable
            // on win2k in multimon.  Note that there still seems to be some
            // problem with actually setting the palette on the primary
            // screen until after QNP is called, but at least the
            // secondary devices can correctly realize the palette.
            ::RealizePalette(hDC);
        }
    }
}

/**
 * Disable the use of our custom palette.  This method is called
 * during initialization if we detect that we are running inside
 * the plugin; we do not want to clobber our parent application's
 * palette with our own in that situation.
 */
void AwtPalette::DisableCustomPalette()
{
    m_useCustomPalette = FALSE;
}

/**
 * Returns whether we are currently using a custom palette.  Used
 * by AwtWin32GraphicsDevice when creating the colorModel of the
 * device.
 */
BOOL AwtPalette::UseCustomPalette()
{
    return m_useCustomPalette;
}


/**
 * Constructor.  Initialize the system and logical palettes.
 * used by this object.
 */
AwtPalette::AwtPalette(AwtWin32GraphicsDevice *device)
{
    this->device = device;
    Update();
    UpdateLogical();
}

/**
 * Retrieves system palette entries. Includes a workaround for some
 * video drivers which may not support the GSPE call but may return
 * valid values from this procedure.
 */
int AwtPalette::FetchPaletteEntries(HDC hDC, PALETTEENTRY* pPalEntries)
{
    LOGPALETTE* pLogPal = 0;
    HPALETTE hPal = 0;
    HPALETTE hPalOld = 0;
    int numEntries;

    numEntries = ::GetSystemPaletteEntries(hDC, 0, 256, pPalEntries);

    if (numEntries > 0) {
        return numEntries;
    }
    // Workaround: some drivers do not support GetSysPalEntries

    pLogPal = (LOGPALETTE*) new char[sizeof(LOGPALETTE)
                                    + 256*sizeof(PALETTEENTRY)];
    if (pLogPal == NULL) {
        return 0;
    }

    pLogPal->palVersion = 0x300;
    pLogPal->palNumEntries = 256;
    int iEntry;
    PALETTEENTRY* pEntry;
    for (iEntry = 0; iEntry < 256; iEntry++) {
        pEntry = pLogPal->palPalEntry + iEntry;
        pEntry->peRed = iEntry;
        pEntry->peGreen = pEntry->peBlue = 0;
        pEntry->peFlags = PC_EXPLICIT;
    }
    hPal = ::CreatePalette(pLogPal);
    delete[] pLogPal;
    if ( hPal == 0 ) {
        return 0;
    }

    hPalOld = ::SelectPalette(hDC, hPal, 1);
    if (hPalOld == 0) {
        ::DeleteObject(hPal);
        return 0;
    }
    ::RealizePalette(hDC);

    COLORREF rgb;
    for (iEntry = 0; iEntry < 256; iEntry++) {
        rgb = ::GetNearestColor(hDC, PALETTEINDEX(iEntry));
        pPalEntries[iEntry].peRed = GetRValue(rgb);
        pPalEntries[iEntry].peGreen = GetGValue(rgb);
        pPalEntries[iEntry].peBlue = GetBValue(rgb);
    }

    ::SelectPalette(hDC, hPalOld, 0 );
    ::DeleteObject(hPal);
    ::RealizePalette(hDC);

    return 256;
}

int AwtPalette::GetGSType(PALETTEENTRY* pPalEntries)
{
    int isGray = 1;
    int isLinearStaticGray = 1;
    int isNonLinearStaticGray = 1;
    int iEntry;
    char bUsed[256];
    BYTE r, g, b;

    memset(bUsed, 0, sizeof(bUsed));
    for (iEntry = 0; iEntry < 256; iEntry++) {
        r = pPalEntries[iEntry].peRed;
        g = pPalEntries[iEntry].peGreen;
        b = pPalEntries[iEntry].peBlue;
        if (r != g || r != b) {
            isGray = 0;
            break;
        } else {
            // the values are gray
            if (r != iEntry) {
                // it's not linear
                // but it could be non-linear static gray
                isLinearStaticGray = 0;
            }
            bUsed[r] = 1;
        }
    }

    if (isGray && !isLinearStaticGray) {
        // check if all 256 grays are there
        // if that's the case, it's non-linear static gray
        for (iEntry = 0; iEntry < 256; iEntry++ ) {
            if (!bUsed[iEntry]) {
                // not non-linear (not all 256 colors are used)
                isNonLinearStaticGray = 0;
                break;
            }
        }
    }

    if (!isGray) {
        J2dTraceLn(J2D_TRACE_INFO,
                   "Detected palette: NON_GRAY/USER-MODIFIABLE");
        return NON_GRAY;
    }
    if (isLinearStaticGray) {
        J2dTraceLn(J2D_TRACE_INFO,
                   "Detected palette: LINEAR_STATIC_GRAY");
        return LINEAR_STATIC_GRAY;
    }
    if (isNonLinearStaticGray) {
        J2dTraceLn(J2D_TRACE_INFO,
                   "Detected palette: NON_LINEAR_STATIC_GRAY");
        return NON_LINEAR_STATIC_GRAY;
    }

    J2dTraceLn(J2D_TRACE_ERROR,
               "Unable to detect palette type, non-gray is assumed");
    // not supposed to be here, error
    return ERROR_GRAY;
}

/**
 * Updates our system palette variables to make sure they match
 * the current state of the actual system palette.  This method
 * is called during AwtPalette creation and after palette changes.
 * Return whether there were any palette changes from the previous
 * system palette.
 */
BOOL AwtPalette::Update()
{
    PALETTEENTRY pe[256];
    int numEntries = 0;
    int bitsPerPixel;
    int i;
    HDC hDC;

    hDC = device->GetDC();
    if (!hDC) {
        return FALSE;
    }
    bitsPerPixel = ::GetDeviceCaps(hDC, BITSPIXEL);
    device->ReleaseDC(hDC);
    if (8 != bitsPerPixel) {
        return FALSE;
    }

    hDC = device->GetDC();
    numEntries = FetchPaletteEntries(hDC, pe);

    device->ReleaseDC(hDC);

    if ((numEntries == numSystemEntries) &&
        (0 == memcmp(pe, systemEntriesWin32, numEntries * sizeof(PALETTEENTRY))))
    {
        return FALSE;
    }

    // make this system palette the new cached win32 palette
    numEntries = (numEntries > 256)? 256: numEntries;
    memcpy(systemEntriesWin32, pe, numEntries * sizeof(PALETTEENTRY));
    numSystemEntries = numEntries;

    // Create jdk-style system palette
    int startIndex = 0, endIndex = numEntries-1;
    int staticGrayType = GetGSType(systemEntriesWin32);

    if (staticGrayType == LINEAR_STATIC_GRAY) {
        device->SetGrayness(GS_STATICGRAY);
    } else if (staticGrayType == NON_LINEAR_STATIC_GRAY) {
        device->SetGrayness(GS_NONLINGRAY);
    } else if (getenv("FORCEGRAY")) {
        J2dTraceLn(J2D_TRACE_INFO,
                    "Gray Palette Forced via FORCEGRAY");
        // Need to zero first and last ten
        // palette entries. Otherwise in UpdateDynamicColorModel
        // we could set non-gray values to the palette.
        for (i = 0; i < 10; i++) {
            systemEntries[i] = 0x00000000;
            systemEntries[i+246] = 0x00000000;
        }
        numEntries -= 20;
        startIndex = 10;
        endIndex -= 10;
        device->SetGrayness(GS_INDEXGRAY);
    } else {
        device->SetGrayness(GS_NOTGRAY);
    }

    for (i = startIndex; i <= endIndex; i++) {
        systemEntries[i] =  0xff000000
                        | (pe[i].peRed << 16)
                        | (pe[i].peGreen << 8)
                        | (pe[i].peBlue);
    }

    systemInverseLUT =
        initCubemap((int *)systemEntries, numEntries, 32);

    ColorData *cData = device->GetColorData();
    if ((device->GetGrayness() == GS_NONLINGRAY ||
         device->GetGrayness() == GS_INDEXGRAY) &&
        cData != NULL) {

        if (cData->pGrayInverseLutData != NULL) {
            free(cData->pGrayInverseLutData);
            cData->pGrayInverseLutData = NULL;
        }
        initInverseGrayLut((int*)systemEntries, 256, device->GetColorData());
    }

    return TRUE;
}


/**
 * Creates our custom palette based on: the current system palette,
 * the grayscale-ness of the system palette, and the state of the
 * primary device.
 */
void AwtPalette::UpdateLogical()
{
    // Create and initialize a palette
    int nEntries = 256;
    char *buf = NULL;
    buf = new char[sizeof(LOGPALETTE) + nEntries *
        sizeof(PALETTEENTRY)];

    LOGPALETTE *pLogPal = (LOGPALETTE*)buf;
    PALETTEENTRY *pPalEntries = (PALETTEENTRY *)(&(pLogPal->palPalEntry[0]));

    memcpy(pPalEntries, systemEntriesWin32, 256 * sizeof(PALETTEENTRY));

    PALETTEENTRY *pPal = pPalEntries;
    int i;
    int staticGrayType = device->GetGrayness();
    if (staticGrayType == GS_INDEXGRAY) {
        float m = 255.0f / 235.0f;
        float g = 0.5f;
        pPal = &pPalEntries[10];
        for (i = 10; i < 246; i++, pPal++) {
            pPal->peRed = pPal->peGreen = pPal->peBlue =
                (int)g;
            g += m;
            pPal->peFlags = PC_NOCOLLAPSE;
        }
    } else if (staticGrayType == GS_NOTGRAY) {
        for (i = 10; i < 246; i++) {
            pPalEntries[i] = customPalette[i-10];
        }
    }
    pLogPal->palNumEntries = 256;
    pLogPal->palVersion = 0x300;
    logicalPalette = ::CreatePalette(pLogPal);

    for (i = 0; i < nEntries; i++) {
        logicalEntries[i] =  0xff000000
                        | (pPalEntries[i].peRed << 16)
                        | (pPalEntries[i].peGreen << 8)
                        | (pPalEntries[i].peBlue);
    }
    delete [] buf;
}