#include "awt_dc.h"
#include "awt_querydc.h"
#include "awt_component.h"
#include "awt_graphics.h"
#include "awt_image.h"
#include "awt_resource.h"
#include "awt_palette.h"
extern "C" {
#include "java_awt_image_ColorModel.h"
#include "java_awt_image_IndexColorModel.h"
};
//#define DEBUG_GDI
static AwtMutex AwtPaletteLock;
AwtPalette::AwtPalette()
{
m_hDC = NULL;
m_refCount = 1;
m_numColors = 0;
m_pPalEntries = NULL;
m_hPal = NULL;
m_ILTableFilled = FALSE;
}
//------------------------------------------------------------------------
//
//------------------------------------------------------------------------
AwtPalette::~AwtPalette()
{
if(m_hPal != NULL)
{
DeleteObject(m_hPal);
#ifdef DEBUG_GDI
char buf[100];
wsprintf(buf, "Delete m_hPal= 0x%x, thrid=%d\n",m_hPal, PR_GetCurrentThreadID() );
OutputDebugString(buf);
#endif
}
if(m_pPalEntries != NULL)
{
free ((char *)m_pLogPal);
}
}
//------------------------------------------------------------------------
//
//------------------------------------------------------------------------
void AwtPalette::AddRef()
{
m_hpal_lock.Lock();
m_refCount++;
m_hpal_lock.Unlock();
}
//------------------------------------------------------------------------
//
//------------------------------------------------------------------------
void AwtPalette::Release()
{
UINT nRef;
m_hpal_lock.Lock();
nRef = --m_refCount;
m_hpal_lock.Unlock();
if (nRef == 0)
{
ReleaseCachedAwtPalette();
delete this;
}
}
//------------------------------------------------------------------------
//
// Lock the AwtPalette for exclusive use.
//
//------------------------------------------------------------------------
void AwtPalette::Lock( )
{
m_hpal_lock.Lock();
}
//------------------------------------------------------------------------
//
// Unlock the AwtPalette. This allows any other thread to obtain a Lock.
//
//------------------------------------------------------------------------
void AwtPalette::Unlock()
{
m_hpal_lock.Unlock();
}
#define STATIC_COLOR_OFFSET 10 // To preserve the static colors
//------------------------------------------------------------------------
// Creates a logical palette with colors in the color model.
// Note that this is not cached as each image may have its own palette of
// colors.
//------------------------------------------------------------------------
void AwtPalette::CreateCMCompatiblePalette(HColorModel *hCM)
{
HWND hWnd = ::GetDesktopWindow();
CreateCompatiblePalette(GetDC(hWnd));
FillPaletteWithSpecialColors();
int numCMColors;
IndexColorModel *inxCM;
if ( obj_classblock(hCM)
== FindClass(EE(), "java/awt/image/IndexColorModel", TRUE)
)
{
unsigned char *cmred;
unsigned char *cmgreen;
unsigned char *cmblue;
inxCM = (Classjava_awt_image_IndexColorModel *)unhand(hCM);
cmred = (unsigned char *) unhand(inxCM->red);
cmgreen = (unsigned char *) unhand(inxCM->green);
cmblue = (unsigned char *) unhand(inxCM->blue);
numCMColors = inxCM->map_size;
m_numColors = 256;
if(m_hPal == NULL)
{
char *buf = new char[sizeof(LOGPALETTE) + m_numColors * sizeof(PALETTEENTRY)];
m_pLogPal = (LOGPALETTE*)buf;
m_pPalEntries = (PALETTEENTRY *)(&(m_pLogPal->palPalEntry[0]));
}
for(int i=STATIC_COLOR_OFFSET; i<(numCMColors+STATIC_COLOR_OFFSET); i++)
{
InitPaletteEntry(&m_pPalEntries[i++], cmred[i], cmgreen[i], cmblue[i]);
if( i > 245 )
{
break;
}
}
if(m_hPal != NULL)
{
DeleteObject(m_hPal);
#ifdef DEBUG_GDI
char buf[100];
wsprintf(buf, "Delete m_hPal= 0x%x, thrid=%d\n",m_hPal, PR_GetCurrentThreadID() );
OutputDebugString(buf);
#endif
}
m_pLogPal->palNumEntries = m_numColors;
m_pLogPal->palVersion = 0x300;
m_hPal = ::CreatePalette(m_pLogPal);
#ifdef DEBUG_GDI
char buf[100];
wsprintf(buf, "Create Palette 1 = 0x%x, thrid=%d\n",m_hPal, PR_GetCurrentThreadID() );
OutputDebugString(buf);
#endif
m_bitPerPixel = 8;
}
else
{
m_bitPerPixel = 24;
}
//PrintPalette();
}
int AwtPalette::GetBitsPerPixel()
{
return m_bitPerPixel;
}
int AwtPalette::GetNumColors()
{
return m_numColors;
}
AwtPalette *AwtPalette::Create(HColorModel *hCM)
{
AwtPalette *pNewAwtPalette = new AwtPalette();
if(pNewAwtPalette != NULL)
{
pNewAwtPalette->CreateCMCompatiblePalette(hCM);
}
return pNewAwtPalette;
}
//------------------------------------------------------------------------
// Creates a logical palette made of the same colors as the one in
// the given DC. This is cached accross many images which want to
// use a standard palette. So this wrapped in a mutex and a static
// Create function. Note that even if this device does not support
// palettes we create one and fill it with special colors.
//------------------------------------------------------------------------
void AwtPalette::CreateCompatiblePalette(HDC hDC)
{
int devcap = GetDeviceCaps(hDC, RASTERCAPS);
m_numColors = 256;
if( (devcap & RC_PALETTE) > 0)
{
// This is a palette supporting device. So, create a palette with
// size same as that of in the device palette.
m_bitPerPixel = ::GetDeviceCaps(hDC, BITSPIXEL);
if(m_bitPerPixel < 8)
{
m_bitPerPixel = 24; // We will not get any palette colors but
// create our own.
}
}
else
{
m_bitPerPixel = 24;
}
char *buf = new char[sizeof(LOGPALETTE) + m_numColors * sizeof(PALETTEENTRY)];
m_pLogPal = (LOGPALETTE*)buf;
m_pPalEntries = (PALETTEENTRY *)(&(m_pLogPal->palPalEntry[0]));
int result ;
// Get the palette colors to preserve the static colors.
// Else generate all the other colors.
if( m_bitPerPixel == 8)
{
result = ::GetSystemPaletteEntries(hDC, 0, m_numColors, m_pPalEntries);
if( result != m_numColors)
{
m_numColors = 0;
m_pPalEntries = NULL;
free(buf);
m_hPal = NULL;
return;
}
}
for(int i=0; i<m_numColors; i++)
{
m_pPalEntries[i].peFlags = PC_NOCOLLAPSE;
}
m_pLogPal->palNumEntries = m_numColors;
m_pLogPal->palVersion = 0x300;
if( m_bitPerPixel < 24)
{
m_hPal = ::CreatePalette(m_pLogPal);
#ifdef DEBUG_GDI
char buf[100];
wsprintf(buf, "Create Palette 2= 0x%x, thrid=%d\n",m_hPal, PR_GetCurrentThreadID() );
OutputDebugString(buf);
#endif
}
m_hDC = hDC;
}
BOOL AwtPalette::CreateCompatiblePalette(HPALETTE hPal)
{
//REMIND: This does not take care of 4 and 16 bit palettes. in such cases
// we will end up creating a symmetric palette in awt via DC.
char *buf = new char[sizeof(LOGPALETTE) + (256 * sizeof(PALETTEENTRY))];
m_pLogPal = (LOGPALETTE*)buf;
m_pPalEntries = (PALETTEENTRY *)(&(m_pLogPal->palPalEntry[0]));
m_numColors = ::GetPaletteEntries(
hPal, // handle of logical color palette
0, // first entry to retrieve
256, // number of entries to retrieve
m_pPalEntries // address of array receiving entries
);
if (m_numColors < 256 )
{
return FALSE;
}
// Undocumented behaviour. This gives the best result.
// All cases including transparency, animation and dithering and pink color
// drawing work only if we create the palette with these magic flags and
// realize the palette in both DC and image draw as background palettes.
// This fixes bugNo:23796.
// Applets to test.
// http://warp.mcom.com/java/applets/UnderConstruction/example1.html (transparency)
// http://www.vivids.com/java/image/Img.html (JPEG 256 color display)
// http://w3.mcom.com/systems/index.html (Animation with the digger )
// http://warp.mcom.com/java/applets/ArcTest/example1.html (Pink grid)
for(int i=0; i<256; i++)
{
m_pPalEntries[i].peFlags = PC_NOCOLLAPSE;
}
for(i=0; i<10; i++)
{
m_pPalEntries[i].peFlags = 0;
m_pPalEntries[255-i].peFlags = 0;
}
/////
m_pLogPal->palNumEntries = m_numColors;
m_pLogPal->palVersion = 0x300;
m_hPal = ::CreatePalette(m_pLogPal);
#ifdef DEBUG_GDI
char buf1[100];
wsprintf(buf1, "Create Palette 3= 0x%x, thrid=%d\n",m_hPal, PR_GetCurrentThreadID() );
OutputDebugString(buf1);
#endif
m_hPalOrig = hPal;
//PrintPalette();
return TRUE;
}
void AwtPalette::PrintPalette()
{
char buf[100];
for(int i=0; i<m_numColors; i++)
{
wsprintf(buf, "(%d,%d,%d)\n",m_pPalEntries[i].peRed,
m_pPalEntries[i].peGreen,
m_pPalEntries[i].peBlue);
OutputDebugString(buf);
}
}
void AwtPalette::GetRGBQUADS(RGBQUAD *pRgbq)
{
PALETTEENTRY *pPal = m_pPalEntries;
for(int i=0; i<m_numColors; i++)
{
pRgbq->rgbRed = pPal->peRed;
pRgbq->rgbGreen = pPal->peGreen;
pRgbq->rgbBlue = pPal->peBlue;
pRgbq->rgbReserved = 0;
pRgbq++;
pPal++;
}
}
// Not used now. but a good utility fn.
PALETTEENTRY *AwtPalette::GetPaletteEntries()
{
AwtPaletteLock.Lock();
char *buf = new char[sizeof(LOGPALETTE) + m_numColors * sizeof(PALETTEENTRY)];
LOGPALETTE *pLogPal = (LOGPALETTE*)buf;
PALETTEENTRY *pPalEntries = (PALETTEENTRY *)(&(pLogPal->palPalEntry[0]));
for(int i=0; i<m_numColors; i++)
{
InitPaletteEntry(&pPalEntries[i++], m_pPalEntries[i].peRed, m_pPalEntries[i].peGreen, m_pPalEntries[i].peBlue);
}
AwtPaletteLock.Unlock();
return pPalEntries;
}
// To update a existing set of palette entries created. Used when
// filling palette with special colors.
void AwtPalette::UpdatePaletteEntries(int startInx, int endInx, PALETTEENTRY *newEntries)
{
if( (startInx < 0) || (endInx > 255))
{
return;
}
int i=0;
for (i=0; i<=(endInx-startInx); i++)
{
m_pPalEntries[startInx+i] = newEntries[i];
m_pPalEntries[i].peFlags = PC_NOCOLLAPSE;
}
if(m_hPal != NULL)
{
DeleteObject(m_hPal);
#ifdef DEBUG_GDI
char buf[100];
wsprintf(buf, "Delete m_hPal= 0x%x, thrid=%d\n",m_hPal, PR_GetCurrentThreadID() );
OutputDebugString(buf);
#endif
}
if( startInx == 10)
{
// Undocumented behaviour. Setting the flags to NOCOLLAPSE or
// any valid entry does not give a proper mapping. The realized
// palette is shifted by 10.
for(i=0; i<10; i++)
{
m_pPalEntries[i].peFlags = 0;
m_pPalEntries[255-i].peFlags = 0;
}
}
m_hPal = ::CreatePalette(m_pLogPal);
#ifdef DEBUG_GDI
char buf[100];
wsprintf(buf, "Create Palette 4= 0x%x, thrid=%d\n",m_hPal, PR_GetCurrentThreadID() );
OutputDebugString(buf);
#endif
}
void AwtPalette::InitPaletteEntry(PALETTEENTRY *pPalEntry, int r, int g, int b)
{
pPalEntry->peRed = r;
pPalEntry->peGreen = g;
pPalEntry->peBlue = b;
pPalEntry->peFlags = PC_NOCOLLAPSE;
}
void AwtPalette::FillPaletteWithSpecialColors()
{
int numColors=((m_bitPerPixel == 24)? 256 : 236);
char *buf = new char[sizeof(LOGPALETTE) + numColors * sizeof(PALETTEENTRY)];
LOGPALETTE *pLogPal = (LOGPALETTE*)buf;
PALETTEENTRY *pPalEntries = (PALETTEENTRY *)(&(pLogPal->palPalEntry[0]));
int i=0;
//REMIND: Do we reserve for 4 and 16 color?
// 22 special colors. Only then do we get xor mode working!
InitPaletteEntry(&pPalEntries[i++],255, 255, 255);
InitPaletteEntry(&pPalEntries[i++],255, 0, 0);
InitPaletteEntry(&pPalEntries[i++],0, 255, 0);
InitPaletteEntry(&pPalEntries[i++],0, 0, 255);
InitPaletteEntry(&pPalEntries[i++],255, 255, 0);
InitPaletteEntry(&pPalEntries[i++],255, 0, 255);
InitPaletteEntry(&pPalEntries[i++],0, 255, 255);
InitPaletteEntry(&pPalEntries[i++],235, 235, 235);
InitPaletteEntry(&pPalEntries[i++],224, 224, 224);
InitPaletteEntry(&pPalEntries[i++],214, 214, 214);
InitPaletteEntry(&pPalEntries[i++],192, 192, 192);
InitPaletteEntry(&pPalEntries[i++],162, 162, 162);
InitPaletteEntry(&pPalEntries[i++],128, 128, 128);
InitPaletteEntry(&pPalEntries[i++],105, 105, 105);
InitPaletteEntry(&pPalEntries[i++],64, 64, 64);
InitPaletteEntry(&pPalEntries[i++],32, 32, 32);
InitPaletteEntry(&pPalEntries[i++],255, 128, 128);
InitPaletteEntry(&pPalEntries[i++],128, 255, 128);
InitPaletteEntry(&pPalEntries[i++],128, 128, 255);
InitPaletteEntry(&pPalEntries[i++],255, 255, 128);
InitPaletteEntry(&pPalEntries[i++],255, 128, 255);
InitPaletteEntry(&pPalEntries[i++],128, 255, 255);
// This block will allocate 225 colors along the color cube
// with b varying as 0, 64, 128, 192, 256 (5 levels)
// with g varying as 0, 64, 128, 192, 256 (5 levels)
// with r varying as 0, 32, 64, 96, 128, 160, 192, 224, 256 (9 levels)
// Total colors = 5*5*9 = 225
int ri, gi, bi;
for (bi = 0 ; bi <= CMAP_COLS ; bi += 2)
{
int b = bi << (8 - CMAP_BITS);
for (gi = 0 ; gi <= CMAP_COLS ; gi += 2)
{
int g = gi << (8 - CMAP_BITS);
for (ri = 0; ri <= CMAP_COLS ; ri += 1)
{
int r = ri << (8 - CMAP_BITS);
InitPaletteEntry(&pPalEntries[i++], r, g, b);
if ( i > numColors )
{
break;
}
}
if ( i > numColors )
{
break;
}
}
if ( i > numColors )
{
break;
}
}
int startInx=((numColors == 236)? 10 : 0);
int endInx=((numColors == 236)? 245 : 255);
// This will fill in a new set of colors distributed accross
// the cube.
UpdatePaletteEntries(startInx, endInx, pPalEntries);
delete buf;
//PrintPalette();
}
//-------------------------------------------------------------------------
// Fills the inverse lookup table associated with this palette. This allows
// for rgb to lut conversion. Right now there are only 9*9*9 = 729 colors
// in the cube. So the time to construct the table will be 729*256 = 186624
// loops. Since this is done only once per DC it may be OK.
// REMIND: To make RGB to LUT better we should need more levels on each axis
// 16. For this we would have to implement the convex polyhedron algm
// to create the mapping else numloops will be too many.
// Notes::
// ILT = Inverse Lookup Table. (Allows to map a rgb value to a lut value)
//
// LUT1 to LUT2 LUT1->RGB->(ILT)->LUT2. This is used only when
// we copy one image onto another. In this awt we do not
// allow for image editing.
// LUT1 to LUT1 (In case of blting a image to display) = No conversion
// necessary. Just copy the color table into DIB and select the
// into hDC before blting. palette
// LUT to RGB. No Conversion necessary. Copy the color table into DIB and
// blt it onto 16,24 bit screen. BitBlt will convert.
// RGB to LUT we have to manufacture a color palette by doing color quantaization.
// or give colors from a color cube. This is FillPaletteWithSpecialColors.
// In this case copy the image to a 24 bit dIB and select a special palette
// into hDC before blting.
// RGB to RGB Just copy the bitmap into a DIB and blt it as is.
//
//-------------------------------------------------------------------------
void AwtPalette::FillILTable()
{
int ri, gi, bi;
for (bi = 0 ; bi <= CMAP_COLS ; bi += 1) {
int b = bi << (8 - CMAP_BITS);
for (gi = 0 ; gi <= CMAP_COLS ; gi += 1) {
int g = gi << (8 - CMAP_BITS);
for (ri = 0 ; ri <= CMAP_COLS ; ri += 1) {
int r = ri << (8 - CMAP_BITS);
m_ILTable[ri][gi][bi] = ClosestMatchPalette(r, g, b);
}
}
}
m_ILTableFilled = TRUE;
}
int AwtPalette::ClosestMatchPalette(int r, int g, int b)
{
PALETTEENTRY *p = m_pPalEntries;
int nPaletteSize = m_numColors;
int i, besti = 0;
int32 t, d;
int32_t mindist = 256L * 256L * 256L;
// Perform initial range checking on r, g, b
if (r > 255) r = 255;
if (g > 255) g = 255;
if (b > 255) b = 255;
for (i = 0 ; i < nPaletteSize ; i++, p++) {
t = p->peRed - r;
d = t * t;
if (d >= mindist) {
continue;
}
t = p->peGreen - g;
d += t * t;
if (d >= mindist) {
continue;
}
t = p->peBlue- b;
d += t * t;
if (d >= mindist) {
continue;
}
if (d == 0) {
return i;
}
if (d < mindist) {
besti = i;
mindist = d;
}
}
return besti;
}
//-------------------------------------------------------------------------
//
// Cached AwtPalette implementation
//
//-------------------------------------------------------------------------
AwtCList AwtPalette::m_freeChain;
AwtCList AwtPalette::m_liveChain;
//-------------------------------------------------------------------------
//
// Create an AwtPalette object of the requested color.
//
// If an object already exists for the color, then return it, otherwise
// create a new one...
//
//-------------------------------------------------------------------------
AwtPalette *AwtPalette::Create(HDC hDC)
{
AwtCList *qp;
AwtPalette *pCachedAwtPalette;
AwtPalette *pCachedNewAwtPalette = NULL;
AwtPaletteLock.Lock();
//
// Scan the list of allocated brushes to see if the requested color
// has already been created...
//
qp = m_liveChain.next;
while (qp != &m_liveChain) {
pCachedAwtPalette = (AwtPalette *)RESOURCE_PTR(qp);
if (pCachedAwtPalette->m_hDC == hDC)
{
pCachedNewAwtPalette = pCachedAwtPalette;
break;
}
qp = qp->next;
}
//
// Allocate a new palette
//
if (pCachedNewAwtPalette == NULL)
{
// Allocate from the free chain if possible...
if (m_freeChain.next != m_freeChain.prev)
{
pCachedNewAwtPalette = (AwtPalette *)RESOURCE_PTR( m_freeChain.next );
//PR_REMOVE_AND_INIT_LINK( &(pCachedNewAwtPalette->m_link) );
pCachedNewAwtPalette->m_link.Remove();
}
else
{
pCachedNewAwtPalette = new AwtPalette();
pCachedNewAwtPalette->CreateCompatiblePalette(hDC);
pCachedNewAwtPalette->FillPaletteWithSpecialColors();
pCachedNewAwtPalette->FillILTable();
}
//
// Initialize the object and place it in the brush cache...
//
//PR_APPEND_LINK( &(pCachedNewAwtPalette->m_link), &m_liveChain );
m_liveChain.Append(pCachedNewAwtPalette->m_link);
}
AwtPaletteLock.Unlock();
return pCachedNewAwtPalette;
}
AwtPalette *AwtPalette::Create(HPALETTE hPal)
{
AwtCList *qp;
AwtPalette *pCachedAwtPalette;
AwtPalette *pCachedNewAwtPalette = NULL;
AwtPaletteLock.Lock();
//
// Scan the list of allocated brushes to see if the requested color
// has already been created...
//
qp = m_liveChain.next;
while (qp != &m_liveChain) {
pCachedAwtPalette = (AwtPalette *)RESOURCE_PTR(qp);
if (pCachedAwtPalette->m_hPalOrig == hPal)
{
pCachedNewAwtPalette = pCachedAwtPalette;
break;
}
qp = qp->next;
}
//
// Allocate a new brush
//
if (pCachedNewAwtPalette == NULL)
{
// Allocate from the free chain if possible...
if (m_freeChain.next != m_freeChain.prev)
{
pCachedNewAwtPalette = (AwtPalette *)RESOURCE_PTR( m_freeChain.next );
//PR_REMOVE_AND_INIT_LINK( &(pCachedNewAwtPalette->m_link) );
pCachedNewAwtPalette->m_link.Remove();
}
else
{
pCachedNewAwtPalette = new AwtPalette();
if( pCachedNewAwtPalette->CreateCompatiblePalette(hPal) == TRUE)
{
pCachedNewAwtPalette->FillILTable();
}
else
{
free(pCachedNewAwtPalette);
pCachedNewAwtPalette=NULL;
AwtPaletteLock.Unlock();
return NULL;
}
}
//
// Initialize the object and place it in the brush cache...
//
//PR_APPEND_LINK( &(pCachedNewAwtPalette->m_link), &m_liveChain );
m_liveChain.Append(pCachedNewAwtPalette->m_link);
}
AwtPaletteLock.Unlock();
return pCachedNewAwtPalette;
}
//-------------------------------------------------------------------------
//
// Delete all of the objects in the cache...
//
//-------------------------------------------------------------------------
void AwtPalette::DeleteCache(void)
{
AwtPalette *pObject;
// Lock the cache...
AwtPaletteLock.Lock();
//
// Scan the list of allocated brushes to see if the requested color
// has already been created...
//
while (!m_liveChain.IsEmpty()) {
pObject = (AwtPalette *)RESOURCE_PTR(m_liveChain.next);
pObject->m_link.Remove();
pObject->ReleaseCachedAwtPalette();
delete pObject;
}
// Unlock the cache...
AwtPaletteLock.Unlock();
}
//-------------------------------------------------------------------------
//
// Release an AwtPalette object. If this is the last reference to the
// object, then free all native resources (ie. GDI) and place the object
// on the free chain.
//
//-------------------------------------------------------------------------
void AwtPalette::ReleaseCachedAwtPalette()
{
//
// Remove the object from the cache and place it on the free chain.
//
AwtPaletteLock.Lock();
if(m_hPal != NULL)
{
DeleteObject(m_hPal);
#ifdef DEBUG_GDI
char buf[100];
wsprintf(buf, "Delete m_hPal= 0x%x, thrid=%d\n",m_hPal, PR_GetCurrentThreadID() );
OutputDebugString(buf);
#endif
m_hPal = NULL;
}
m_link.Remove();
m_freeChain.Append(m_link);
AwtPaletteLock.Unlock();
}
//REMIND Check when this obj is freed