Windows2003-3790/windows/advcore/gdiplus/test/simpsons/palmap.cpp
2020-09-30 16:53:55 +02:00

517 lines
16 KiB
C++

// File: PalMap.cpp
// Author: Michael Marr (mikemarr)
//
// History:
// -@- 09/23/97 (mikemarr) copied to DXCConv from d2d\mmimage
#include "stdafx.h"
#include "PalMap.h"
#include "Blt.h"
#include "ddhelper.h"
char gs_szPMPrefix[] = "palette map error";
CPaletteMap::CPaletteMap()
{
m_rgIndexMap = NULL;
m_nConvertCode = cvcInvalid;
m_cSrcBPP = m_cDstBPP = 0;
m_bIdentity = FALSE;
}
CPaletteMap::~CPaletteMap()
{
MMDELETE(m_rgIndexMap);
}
// Function: CreateMap
// This function creates a new mapping from a src palette to a destination color model.
HRESULT
CPaletteMap::CreateMap(BYTE nBPPSrcPixels, BYTE nBPPSrcPalette, LPPALETTEENTRY rgpeSrc,
const CPixelInfo &pixiDst, LPDIRECTDRAWPALETTE pddpDst)
{
MMTRACE("CPaletteMap::CreateMap\n");
HRESULT hr;
PALETTEENTRY rgpeDst[256];
DWORD dwDstCaps;
// verify arguments
if (rgpeSrc == NULL)
return E_INVALIDARG;
// delete the old index map, if it exists
MMDELETE(m_rgIndexMap);
// store the bit depths for mapping verification
// REVIEW: perhaps the maps should be created with at least 256 entries always
m_cSrcBPP = nBPPSrcPixels;
m_cDstBPP = pixiDst.nBPP;
// figure out what kind of conversion we are doing
if ((m_nConvertCode = static_cast<BYTE>(GetConvertCode(m_cSrcBPP, m_cDstBPP))) == cvcInvalid) {
MMTRACE("%s: can't convert from %d bit to %d bit\n",
gs_szPMPrefix, (int) m_cSrcBPP, (int) m_cDstBPP);
return E_INVALIDARG;
}
if (pddpDst == NULL) {
// destination is RGB
switch (m_cDstBPP) {
case 16: return DoPalTo16BitMap(nBPPSrcPalette, pixiDst, rgpeSrc); break;
case 24: return DoPalTo24BitMap(nBPPSrcPalette, pixiDst, rgpeSrc); break;
case 32: return DoPalTo32BitMap(nBPPSrcPalette, pixiDst, rgpeSrc); break;
default:
return E_INVALIDARG;
break;
}
} else {
// destination is 8 bit palettized
hr = E_INVALIDARG;
if ((m_cDstBPP != 8) ||
// get the caps
FAILED(pddpDst->GetCaps(&dwDstCaps)) ||
// verify we have True Color entries
(dwDstCaps & DDPCAPS_8BITENTRIES) ||
// make sure the number of palette entries from the caps is 8 bits
(!(dwDstCaps & DDPCAPS_8BIT)) ||
// get the palette entries
FAILED(hr = pddpDst->GetEntries(0, 0, 1 << m_cDstBPP, rgpeDst)))
{
MMTRACE("%s: invalid dst palette for map\n", gs_szPMPrefix);
return hr;
}
// create map for palette to palette
return DoPalToPalMap(nBPPSrcPalette, m_cDstBPP, rgpeSrc, rgpeDst);
}
}
HRESULT
CPaletteMap::CreateMap(LPDIRECTDRAWPALETTE pddpSrc, const CPixelInfo &pixiDst,
LPDIRECTDRAWPALETTE pddpDst)
{
// MMTRACE("CPaletteMap::CreateMap\n");
PALETTEENTRY rgpeSrc[256];
BYTE nBPPSrc;
DWORD dwSrcCaps;
// sanitize the src palette and get the srcBPP
HRESULT hr = E_INVALIDARG;
if ((pddpSrc == NULL) ||
// get the caps
FAILED(pddpSrc->GetCaps(&dwSrcCaps)) ||
// verify we have True Color entries
(dwSrcCaps & DDPCAPS_8BITENTRIES) ||
// get the number of palette entries from the caps
((nBPPSrc = BYTE(PaletteFlagsToBPP(dwSrcCaps))) == 0) ||
// get the palette entries
FAILED(hr = pddpSrc->GetEntries(0, 0, (1 << nBPPSrc), rgpeSrc)))
{
MMTRACE("%s: invalid src palette for map\n", gs_szPMPrefix);
return hr;
}
return CreateMap(nBPPSrc, nBPPSrc, rgpeSrc, pixiDst, pddpDst);
}
/*
HRESULT
CPaletteMap::CreateSortedMap(BYTE nBPP, const RGB *rgrgbSrc, BYTE nBPPUsed, DWORD iTransColor,
DWORD dwFlags, LPPALETTEENTRY rgpeDst)
{
MMTRACE("CPaletteMap::CreateSortedMap\n");
MMASSERT(nBPP <= nBPPUsed);
DWORD i, j, imin;
if ((rgrgbSrc == NULL) || (nBPPUsed > 8))
return E_INVALIDARG;
struct {
DWORD nPos;
int nLuminance;
} rgSortMap[nMAXPALETTEENTRIES], minLuminance;
// allocate the index map
MMDELETE(m_rgIndexMap);
m_rgIndexMap = (BYTE *) new BYTE[1 << nBPPUsed];
if (m_rgIndexMap == NULL)
return E_OUTOFMEMORY;
m_nConvertCode = GetConvertCode(nBPPUsed, nBPPUsed);
MMASSERT(m_nConvertCode == cvc8To8);
m_cSrcBPP = nBPPUsed;
m_cDstBPP = nBPPUsed;
// m_pixiDst.Init(nBPPUsed);
// initialize the sort map (compute luminance values)
DWORD cMapLength = (1 << nBPP), cTotalEntries = (1 << nBPPUsed);
for (i = 0; i < cMapLength; i++) {
const RGB &rgbTmp = rgrgbSrc[i];
rgSortMap[i].nPos = i;
rgSortMap[i].nLuminance = nREDWEIGHT * rgbTmp.r + nGREENWEIGHT * rgbTmp.g +
nBLUEWEIGHT * rgbTmp.b;
}
// if transparency exists, change its luminance to -1 so it will
// become the zeroth index
if (dwFlags & flagTRANSPARENT) {
if (iTransColor > cMapLength)
return E_INVALIDARG;
rgSortMap[iTransColor].nLuminance = -1;
}
// sort the entries by luminance
// REVIEW: use naive insertion sort for now
for (i = 0; i < cMapLength; i++) {
imin = i;
minLuminance = rgSortMap[imin];
for (j = i + 1; j < cMapLength; j++) {
if (minLuminance.nLuminance > rgSortMap[j].nLuminance) {
imin = j;
minLuminance = rgSortMap[imin];
}
}
rgSortMap[imin] = rgSortMap[i];
rgSortMap[i] = minLuminance;
}
// fill in the index map (sorting generates an "inverse" map)
for (i = 0; i < cMapLength; i++) {
m_rgIndexMap[rgSortMap[i].nPos] = (BYTE) i;
}
for (; i < cTotalEntries; i++)
m_rgIndexMap[i] = (BYTE) i;
// sort to a palette entry array based on this mapping
if (rgpeDst) {
for (i = 0; i < cMapLength; i++) {
PALETTEENTRY &pe = rgpeDst[i];
const RGB &rgb = rgrgbSrc[rgSortMap[i].nPos];
pe.peRed = rgb.r; pe.peGreen = rgb.g; pe.peBlue = rgb.b; pe.peFlags = 0;
}
PALETTEENTRY peZero = {0, 0, 0, 0};
for (; i < cTotalEntries; i++)
rgpeDst[i] = peZero;
}
return S_OK;
}
*/
HRESULT
CPaletteMap::DoPalTo16BitMap(BYTE cSrcBPP, const CPixelInfo &pixiDst, const PALETTEENTRY *ppeSrc)
{
MMASSERT(ppeSrc);
DWORD cEntries = (1 << cSrcBPP);
MapEntry16 *pIndexMap = new MapEntry16[cEntries];
if (pIndexMap == NULL)
return E_OUTOFMEMORY;
for (DWORD i = 0; i < cEntries; i++) {
pIndexMap[i] = pixiDst.Pack16(ppeSrc[i]);
}
m_rgIndexMap = (BYTE *) pIndexMap;
return S_OK;
}
HRESULT
CPaletteMap::DoPalTo24BitMap(BYTE cSrcBPP, const CPixelInfo &pixiDst, const PALETTEENTRY *ppeSrc)
{
MMASSERT(ppeSrc);
if ((pixiDst.nRedResidual | pixiDst.nGreenResidual | pixiDst.nBlueResidual) != 0)
return DDERR_INVALIDPIXELFORMAT;
DWORD cEntries = (1 << cSrcBPP);
MapEntry24 *pIndexMap = new MapEntry24[cEntries];
if (pIndexMap == NULL)
return E_OUTOFMEMORY;
for (DWORD i = 0; i < cEntries; i++) {
pIndexMap[i] = pixiDst.Pack(ppeSrc[i]);
}
m_rgIndexMap = (BYTE *) pIndexMap;
return S_OK;
}
HRESULT
CPaletteMap::DoPalTo32BitMap(BYTE cSrcBPP, const CPixelInfo &pixiDst, const PALETTEENTRY *ppeSrc)
{
// REVIEW: since PALETTEENTRY does not have an alpha field,
// this should be the same as 24 bit
return DoPalTo24BitMap(cSrcBPP, pixiDst, ppeSrc);
}
// blue is assumed to have a weight of 1.f
#define fSimpleRedWeight 2.1f
#define fSimpleGreenWeight 2.4f
#define fMaxColorDistance ((1.f + fSimpleRedWeight + fSimpleGreenWeight) * float(257 * 256))
static inline float
_ColorDistance(const PALETTEENTRY &pe, BYTE r, BYTE g, BYTE b)
{
float fTotal, fTmpR, fTmpG, fTmpB;
fTmpR = (float) (pe.peRed - r);
fTotal = fSimpleRedWeight * fTmpR * fTmpR;
fTmpG = (float) (pe.peGreen - g);
fTotal += fSimpleGreenWeight * fTmpG * fTmpG;
fTmpB = (float) (pe.peBlue - b);
// blue is assumed to have a weight of 1.f
fTotal += fTmpB * fTmpB;
return fTotal;
}
DWORD
_SimpleFindClosestIndex(const PALETTEENTRY *rgpePalette, DWORD cEntries, BYTE r, BYTE g, BYTE b)
{
MMASSERT(rgpePalette);
MMASSERT(cEntries <= nMAXPALETTEENTRIES);
float fTmp, fMinDistance = fMaxColorDistance;
DWORD nMinIndex = cEntries;
for (DWORD i = 0; i < cEntries; i++) {
const PALETTEENTRY &peTmp = rgpePalette[i];
if (!(peTmp.peFlags & (PC_RESERVED | PC_EXPLICIT))) {
if ((fTmp = _ColorDistance(peTmp, r, g, b)) < fMinDistance) {
// check for exact match
if (fTmp == 0.f)
return i;
nMinIndex = i;
fMinDistance = fTmp;
}
}
}
MMASSERT(nMinIndex < cEntries);
return nMinIndex;
}
// Function: DoPalToPalMap
// Compute a mapping from one palette to another and store in the palette map.
HRESULT
CPaletteMap::DoPalToPalMap(BYTE cSrcBPP, BYTE cDstBPP, const PALETTEENTRY *ppeSrc,
const PALETTEENTRY *ppeDst)
{
MMASSERT(ppeSrc && ppeDst);
DWORD cSrcEntries = (1 << cSrcBPP), cDstEntries = (1 << cDstBPP);
m_rgIndexMap = new BYTE[cSrcEntries];
if (m_rgIndexMap == NULL)
return E_OUTOFMEMORY;
for (DWORD i = 0; i < cSrcEntries; i++) {
const PALETTEENTRY &pe = ppeSrc[i];
m_rgIndexMap[i] = (BYTE) _SimpleFindClosestIndex(ppeDst, cDstEntries,
pe.peRed, pe.peGreen, pe.peBlue);
}
return S_OK;
}
// Function: GetConvertCode
// This function computes the index into the function arrays for
// mapping and color conversion.
int
CPaletteMap::GetConvertCode(DWORD nSrcBPP, DWORD nDstBPP)
{
int nCode;
if ((nDstBPP < 8) || (nSrcBPP > 8) || (nSrcBPP < 4)) {
nCode = cvcInvalid;
} else {
nCode = (((nSrcBPP >> 2) - 1) << 2) | ((nDstBPP >> 3) - 1);
}
return nCode;
}
static DWORD
GetColor8To8(DWORD dwSrcColor, const BYTE *pIndexMap)
{
MMASSERT(dwSrcColor < 256);
return (DWORD) pIndexMap[dwSrcColor];
}
static DWORD
GetColor8To16(DWORD dwSrcColor, const BYTE *pIndexMap)
{
MMASSERT(dwSrcColor < 256);
MapEntry16 *pIndexMap16 = (MapEntry16 *) pIndexMap;
return (DWORD) pIndexMap16[dwSrcColor];
}
static DWORD
GetColor8To24(DWORD dwSrcColor, const BYTE *pIndexMap)
{
MMASSERT(dwSrcColor < 256);
MapEntry24 *pIndexMap24 = (MapEntry24 *) pIndexMap;
return (DWORD) pIndexMap24[dwSrcColor];
}
static DWORD
GetColor8To32(DWORD dwSrcColor, const BYTE *pIndexMap)
{
MMASSERT(dwSrcColor < 256);
MapEntry32 *pIndexMap32 = (MapEntry32 *) pIndexMap;
return (DWORD) pIndexMap32[dwSrcColor];
}
static GetColorFunction gs_rgGetColorFunctions[cvcNumCodes] = {
NULL, NULL, NULL, NULL,
GetColor8To8, GetColor8To16,
GetColor8To24, GetColor8To32
};
DWORD
CPaletteMap::GetIndexMapping(DWORD iSrcColor) const
{
MMASSERT((m_nConvertCode < cvcInvalid) && (gs_rgGetColorFunctions[m_nConvertCode] != NULL));
return gs_rgGetColorFunctions[m_nConvertCode](iSrcColor, m_rgIndexMap);
}
// Notes:
// The convert functions also fix the transparency on the destination objects.
// A better way to do this stuff might be to have Blt functions and then separate
// convert functions that cleanup the rest of the image after the Blt.
ConvertFunction g_rgConvertFunctions[cvcNumCodes] = {
NULL, NULL, NULL, NULL,
BltFast8To8T, BltFast8To16T,
BltFast8To24T, BltFast8To32T
};
// Function: BltFast
// This function takes a src dds and writes a dst dds using the
// mapping defined by the PaletteMap. The src and dst can be the
// same surface.
HRESULT
CPaletteMap::BltFast(LPDIRECTDRAWSURFACE pddsSrc, LPRECT prSrc, LPDIRECTDRAWSURFACE pddsDst,
DWORD nXPos, DWORD nYPos, DWORD dwFlags) const
{
if (m_rgIndexMap == NULL)
return E_NOTINITIALIZED;
// make sure the surfaces are valid
if (!pddsSrc || !pddsDst) {
return E_INVALIDARG;
}
ConvertFunction pfnConvertFunction;
HRESULT hr = E_INVALIDARG;
BOOL bSrcLocked = FALSE, bDstLocked = FALSE;
DDSURFACEDESC ddsdSrc, ddsdDst;
INIT_DXSTRUCT(ddsdSrc);
INIT_DXSTRUCT(ddsdDst);
long nWidth, nHeight;
//
// Lock the surfaces
//
if (pddsSrc == pddsDst) {
// REVIEW: this lock could just lock the minimum rectangle...
if (FAILED(hr = pddsDst->Lock(NULL, &ddsdDst, DDLOCK_WAIT, NULL))) {
goto e_Convert;
}
bSrcLocked = bDstLocked = TRUE;
// copy the dst info into the src info
ddsdSrc = ddsdDst;
} else {
// REVIEW: this lock could just lock the minimum rectangle...
if (FAILED(hr = pddsSrc->Lock(NULL, &ddsdSrc, DDLOCK_WAIT, NULL)))
goto e_Convert;
bSrcLocked = TRUE;
if (FAILED(hr = pddsDst->Lock(NULL, &ddsdDst, DDLOCK_WAIT, NULL)))
goto e_Convert;
bDstLocked = TRUE;
}
// verify the image information
if ((ddsdSrc.ddpfPixelFormat.dwRGBBitCount != m_cSrcBPP) ||
(ddsdDst.ddpfPixelFormat.dwRGBBitCount != m_cDstBPP)) {
hr = E_INVALIDARG;
goto e_Convert;
}
//
// clip
//
long nClipWidth, nClipHeight, nLeft, nTop;
if (prSrc == NULL) {
nWidth = ddsdSrc.dwWidth;
nHeight = ddsdSrc.dwHeight;
nLeft = 0;
nTop = 0;
} else {
nWidth = prSrc->right - prSrc->left;
nHeight = prSrc->bottom - prSrc->top;
nLeft = prSrc->left;
nTop = prSrc->top;
}
nClipWidth = long(ddsdDst.dwWidth - nXPos);
nClipHeight = long(ddsdDst.dwHeight - nYPos);
UPDATEMAX(nClipWidth, 0);
UPDATEMAX(nClipHeight, 0);
UPDATEMAX(nWidth, 0);
UPDATEMAX(nHeight, 0);
UPDATEMAX(nLeft, 0);
UPDATEMAX(nTop, 0);
UPDATEMIN(nClipWidth, nWidth);
UPDATEMIN(nClipHeight, nHeight);
if (((nLeft + nClipWidth) > long(ddsdSrc.dwWidth)) ||
((nTop + nClipHeight) > long(ddsdSrc.dwHeight))) {
hr = E_INVALIDARG;
goto e_Convert;
}
// REVIEW: for now, fail if we are not dealing with at least 8BPP
if ((ddsdSrc.ddpfPixelFormat.dwRGBBitCount < 8) || (ddsdDst.ddpfPixelFormat.dwRGBBitCount < 8)) {
hr = E_FAIL;
goto e_Convert;
}
nLeft *= (ddsdSrc.ddpfPixelFormat.dwRGBBitCount >> 3);
nXPos *= (ddsdDst.ddpfPixelFormat.dwRGBBitCount >> 3);
pfnConvertFunction = g_rgConvertFunctions[m_nConvertCode];
if (pfnConvertFunction) {
hr = pfnConvertFunction(
LPBYTE(ddsdSrc.lpSurface) + nLeft + (nTop * ddsdSrc.lPitch),
ddsdSrc.lPitch,
LPBYTE(ddsdDst.lpSurface) + nXPos + (nYPos * ddsdDst.lPitch),
ddsdDst.lPitch,
nClipWidth,
nClipHeight,
m_rgIndexMap);
} else {
hr = E_NOTIMPL;
goto e_Convert;
}
e_Convert:
// unlock the surfaces
if (pddsSrc == pddsDst) {
if (bSrcLocked)
pddsDst->Unlock(ddsdDst.lpSurface);
} else {
if (bDstLocked)
pddsDst->Unlock(ddsdDst.lpSurface);
if (bSrcLocked)
pddsSrc->Unlock(ddsdSrc.lpSurface);
}
MMASSERT(SUCCEEDED(hr));
return hr;
}