mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-22 01:39:57 +00:00
a0824205af
svn-id: r36058
630 lines
16 KiB
C++
630 lines
16 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
|
|
* This program 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 for more details.
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* $URL$
|
|
* $Id$
|
|
*
|
|
* Palette Allocator for IBM PC.
|
|
*/
|
|
|
|
#include "tinsel/dw.h" // TBLUE1 definition
|
|
#include "tinsel/graphics.h"
|
|
#include "tinsel/handle.h" // LockMem definition
|
|
#include "tinsel/palette.h" // palette allocator structures etc.
|
|
#include "tinsel/sysvar.h"
|
|
#include "tinsel/tinsel.h"
|
|
|
|
#include "common/system.h"
|
|
|
|
namespace Tinsel {
|
|
|
|
//----------------- LOCAL DEFINES --------------------
|
|
|
|
/** video DAC transfer Q structure */
|
|
struct VIDEO_DAC_Q {
|
|
union {
|
|
SCNHANDLE hRGBarray; //!< handle of palette or
|
|
COLORREF *pRGBarray; //!< list of palette colours
|
|
} pal;
|
|
bool bHandle; //!< when set - use handle of palette
|
|
int destDACindex; //!< start index of palette in video DAC
|
|
int numColours; //!< number of colours in "hRGBarray"
|
|
};
|
|
|
|
|
|
//----------------- LOCAL GLOBAL DATA --------------------
|
|
|
|
/** background colour */
|
|
static COLORREF bgndColour = BLACK;
|
|
|
|
/** palette allocator data */
|
|
static PALQ palAllocData[NUM_PALETTES];
|
|
|
|
|
|
/** video DAC transfer Q length */
|
|
#define VDACQLENGTH (NUM_PALETTES+2)
|
|
|
|
/** video DAC transfer Q */
|
|
static VIDEO_DAC_Q vidDACdata[VDACQLENGTH];
|
|
|
|
/** video DAC transfer Q head pointer */
|
|
static VIDEO_DAC_Q *pDAChead;
|
|
|
|
/** colour index of the 4 colours used for the translucent palette */
|
|
#define COL_HILIGHT TBLUE1
|
|
|
|
/** the translucent palette lookup table */
|
|
uint8 transPalette[MAX_COLOURS]; // used in graphics.cpp
|
|
|
|
uint8 ghostPalette[MAX_COLOURS];
|
|
|
|
static int translucentIndex = 228;
|
|
|
|
static int talkIndex = 233;
|
|
|
|
static COLORREF talkColRef;
|
|
|
|
static COLORREF tagColRef;
|
|
|
|
|
|
#ifdef DEBUG
|
|
// diagnostic palette counters
|
|
static int numPals = 0;
|
|
static int maxPals = 0;
|
|
static int maxDACQ = 0;
|
|
#endif
|
|
|
|
/**
|
|
* Transfer palettes in the palette Q to Video DAC.
|
|
*/
|
|
void PalettesToVideoDAC(void) {
|
|
PALQ *pPalQ; // palette Q iterator
|
|
VIDEO_DAC_Q *pDACtail = vidDACdata; // set tail pointer
|
|
bool needUpdate = false;
|
|
|
|
// while Q is not empty
|
|
while (pDAChead != pDACtail) {
|
|
PALETTE *pPalette; // pointer to hardware palette
|
|
COLORREF *pColours; // pointer to list of RGB triples
|
|
|
|
#ifdef DEBUG
|
|
// make sure palette does not overlap
|
|
assert(pDACtail->destDACindex + pDACtail->numColours <= MAX_COLOURS);
|
|
#else
|
|
// make sure palette does not overlap
|
|
if (pDACtail->destDACindex + pDACtail->numColours > MAX_COLOURS)
|
|
pDACtail->numColours = MAX_COLOURS - pDACtail->destDACindex;
|
|
#endif
|
|
|
|
if (pDACtail->bHandle) {
|
|
// we are using a palette handle
|
|
|
|
// get hardware palette pointer
|
|
pPalette = (PALETTE *)LockMem(pDACtail->pal.hRGBarray);
|
|
|
|
// get RGB pointer
|
|
pColours = pPalette->palRGB;
|
|
} else {
|
|
// we are using a palette pointer
|
|
pColours = pDACtail->pal.pRGBarray;
|
|
}
|
|
|
|
if (pDACtail->numColours > 0)
|
|
needUpdate = true;
|
|
|
|
// update the system palette
|
|
g_system->setPalette((byte *)pColours, pDACtail->destDACindex, pDACtail->numColours);
|
|
|
|
// update tail pointer
|
|
pDACtail++;
|
|
|
|
}
|
|
|
|
// reset video DAC transfer Q head pointer
|
|
pDAChead = vidDACdata;
|
|
|
|
// clear all palette moved bits
|
|
for (pPalQ = palAllocData; pPalQ < palAllocData + NUM_PALETTES; pPalQ++)
|
|
pPalQ->posInDAC &= ~PALETTE_MOVED;
|
|
|
|
if (needUpdate)
|
|
g_system->updateScreen();
|
|
}
|
|
|
|
/**
|
|
* Commpletely reset the palette allocator.
|
|
*/
|
|
void ResetPalAllocator(void) {
|
|
#ifdef DEBUG
|
|
// clear number of palettes in use
|
|
numPals = 0;
|
|
#endif
|
|
|
|
// wipe out the palette allocator data
|
|
memset(palAllocData, 0, sizeof(palAllocData));
|
|
|
|
// reset video DAC transfer Q head pointer
|
|
pDAChead = vidDACdata;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
/**
|
|
* Shows the maximum number of palettes used at once.
|
|
*/
|
|
void PaletteStats(void) {
|
|
printf("%i palettes of %i used.\n", maxPals, NUM_PALETTES);
|
|
printf("%i DAC queue entries of %i used.\n", maxDACQ, VDACQLENGTH);
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* Places a palette in the video DAC queue.
|
|
* @param posInDAC Position in video DAC
|
|
* @param numColours Number of colours in palette
|
|
* @param hPalette Handle to palette
|
|
*/
|
|
void UpdateDACqueueHandle(int posInDAC, int numColours, SCNHANDLE hPalette) {
|
|
// check Q overflow
|
|
assert(pDAChead < vidDACdata + VDACQLENGTH);
|
|
|
|
pDAChead->destDACindex = posInDAC & ~PALETTE_MOVED; // set index in video DAC
|
|
pDAChead->numColours = numColours; // set number of colours
|
|
pDAChead->pal.hRGBarray = hPalette; // set handle of palette
|
|
pDAChead->bHandle = true; // we are using a palette handle
|
|
|
|
// update head pointer
|
|
++pDAChead;
|
|
|
|
#ifdef DEBUG
|
|
if ((pDAChead-vidDACdata) > maxDACQ)
|
|
maxDACQ = pDAChead-vidDACdata;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Places a palette in the video DAC queue.
|
|
* @param posInDAC Position in video DAC
|
|
* @param numColours, Number of colours in palette
|
|
* @param pColours List of RGB triples
|
|
*/
|
|
void UpdateDACqueue(int posInDAC, int numColours, COLORREF *pColours) {
|
|
// check Q overflow
|
|
assert(pDAChead < vidDACdata + NUM_PALETTES);
|
|
|
|
pDAChead->destDACindex = posInDAC & ~PALETTE_MOVED; // set index in video DAC
|
|
pDAChead->numColours = numColours; // set number of colours
|
|
pDAChead->pal.pRGBarray = pColours; // set addr of palette
|
|
pDAChead->bHandle = false; // we are not using a palette handle
|
|
|
|
// update head pointer
|
|
++pDAChead;
|
|
|
|
#ifdef DEBUG
|
|
if ((pDAChead-vidDACdata) > maxDACQ)
|
|
maxDACQ = pDAChead-vidDACdata;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Allocate a palette.
|
|
* @param hNewPal Palette to allocate
|
|
*/
|
|
PALQ *AllocPalette(SCNHANDLE hNewPal) {
|
|
PALQ *pPrev, *p; // walks palAllocData
|
|
int iDAC; // colour index in video DAC
|
|
PALQ *pNxtPal; // next PALQ struct in palette allocator
|
|
PALETTE *pNewPal;
|
|
|
|
// get pointer to new palette
|
|
pNewPal = (PALETTE *)LockMem(hNewPal);
|
|
|
|
// search all structs in palette allocator - see if palette already allocated
|
|
for (p = palAllocData; p < palAllocData + NUM_PALETTES; p++) {
|
|
if (p->hPal == hNewPal) {
|
|
// found the desired palette in palette allocator
|
|
p->objCount++; // update number of objects using palette
|
|
return p; // return palette queue position
|
|
}
|
|
}
|
|
|
|
// search all structs in palette allocator - find a free slot
|
|
iDAC = FGND_DAC_INDEX; // init DAC index to first available foreground colour
|
|
|
|
for (p = palAllocData; p < palAllocData + NUM_PALETTES; p++) {
|
|
if (p->hPal == 0) {
|
|
// found a free slot in palette allocator
|
|
p->objCount = 1; // init number of objects using palette
|
|
p->posInDAC = iDAC; // set palettes start pos in video DAC
|
|
p->hPal = hNewPal; // set hardware palette data
|
|
p->numColours = FROM_LE_32(pNewPal->numColours); // set number of colours in palette
|
|
|
|
if (TinselV2)
|
|
// Copy all the colours
|
|
memcpy(p->palRGB, pNewPal->palRGB, FROM_LE_32(pNewPal->numColours) * sizeof(COLORREF));
|
|
|
|
#ifdef DEBUG
|
|
// one more palette in use
|
|
if (++numPals > maxPals)
|
|
maxPals = numPals;
|
|
#endif
|
|
|
|
// Q the change to the video DAC
|
|
if (TinselV2)
|
|
UpdateDACqueue(p->posInDAC, FROM_LE_32(pNewPal->numColours), p->palRGB);
|
|
else
|
|
UpdateDACqueueHandle(p->posInDAC, p->numColours, p->hPal);
|
|
|
|
// move all palettes after this one down (if necessary)
|
|
for (pPrev = p, pNxtPal = pPrev + 1; pNxtPal < palAllocData + NUM_PALETTES; pNxtPal++) {
|
|
if (pNxtPal->hPal != 0) {
|
|
// palette slot is in use
|
|
if (pNxtPal->posInDAC >= pPrev->posInDAC + pPrev->numColours)
|
|
// no need to move palettes down
|
|
break;
|
|
|
|
// move palette down - indicate change
|
|
pNxtPal->posInDAC = pPrev->posInDAC
|
|
+ pPrev->numColours | PALETTE_MOVED;
|
|
|
|
// Q the palette change in position to the video DAC
|
|
if (!TinselV2)
|
|
UpdateDACqueueHandle(pNxtPal->posInDAC,
|
|
pNxtPal->numColours,
|
|
pNxtPal->hPal);
|
|
else if (!pNxtPal->bFading)
|
|
UpdateDACqueue(pNxtPal->posInDAC,
|
|
pNxtPal->numColours,
|
|
pNxtPal->palRGB);
|
|
|
|
// update previous palette to current palette
|
|
pPrev = pNxtPal;
|
|
}
|
|
}
|
|
|
|
// return palette pointer
|
|
return p;
|
|
}
|
|
|
|
// set new DAC index
|
|
iDAC = p->posInDAC + p->numColours;
|
|
}
|
|
|
|
// no free palettes
|
|
error("AllocPalette(): formally 'assert(0)!'");
|
|
}
|
|
|
|
/**
|
|
* Free a palette allocated with "AllocPalette".
|
|
* @param pFreePal Palette queue entry to free
|
|
*/
|
|
void FreePalette(PALQ *pFreePal) {
|
|
// validate palette Q pointer
|
|
assert(pFreePal >= palAllocData && pFreePal <= palAllocData + NUM_PALETTES - 1);
|
|
|
|
// reduce the palettes object reference count
|
|
pFreePal->objCount--;
|
|
|
|
// make sure palette has not been deallocated too many times
|
|
assert(pFreePal->objCount >= 0);
|
|
|
|
if (pFreePal->objCount == 0) {
|
|
pFreePal->hPal = 0; // palette is no longer in use
|
|
|
|
#ifdef DEBUG
|
|
// one less palette in use
|
|
--numPals;
|
|
assert(numPals >= 0);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Find the specified palette.
|
|
* @param hSrchPal Hardware palette to search for
|
|
*/
|
|
PALQ *FindPalette(SCNHANDLE hSrchPal) {
|
|
PALQ *pPal; // palette allocator iterator
|
|
|
|
// search all structs in palette allocator
|
|
for (pPal = palAllocData; pPal < palAllocData + NUM_PALETTES; pPal++) {
|
|
if (pPal->hPal == hSrchPal)
|
|
// found palette in palette allocator
|
|
return pPal;
|
|
}
|
|
|
|
// palette not found
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Swaps the palettes at the specified palette queue position.
|
|
* @param pPalQ Palette queue position
|
|
* @param hNewPal New palette
|
|
*/
|
|
void SwapPalette(PALQ *pPalQ, SCNHANDLE hNewPal) {
|
|
// convert handle to palette pointer
|
|
PALETTE *pNewPal = (PALETTE *)LockMem(hNewPal);
|
|
|
|
// validate palette Q pointer
|
|
assert(pPalQ >= palAllocData && pPalQ <= palAllocData + NUM_PALETTES - 1);
|
|
|
|
if (pPalQ->numColours >= (int)FROM_LE_32(pNewPal->numColours)) {
|
|
// new palette will fit the slot
|
|
|
|
// install new palette
|
|
pPalQ->hPal = hNewPal;
|
|
|
|
if (TinselV2) {
|
|
pPalQ->numColours = pNewPal->numColours;
|
|
|
|
// Copy all the colours
|
|
memcpy(pPalQ->palRGB, pNewPal->palRGB, pNewPal->numColours * sizeof(COLORREF));
|
|
|
|
if (!pPalQ->bFading)
|
|
// Q the change to the video DAC
|
|
UpdateDACqueue(pPalQ->posInDAC, FROM_LE_32(pNewPal->numColours), pPalQ->palRGB);
|
|
} else {
|
|
// Q the change to the video DAC
|
|
UpdateDACqueueHandle(pPalQ->posInDAC, FROM_LE_32(pNewPal->numColours), hNewPal);
|
|
}
|
|
} else {
|
|
// # colours are different - will have to update all following palette entries
|
|
assert(!TinselV2); // Fatal error for Tinsel 2
|
|
|
|
PALQ *pNxtPalQ; // next palette queue position
|
|
|
|
for (pNxtPalQ = pPalQ + 1; pNxtPalQ < palAllocData + NUM_PALETTES; pNxtPalQ++) {
|
|
if (pNxtPalQ->posInDAC >= pPalQ->posInDAC + pPalQ->numColours)
|
|
// no need to move palettes down
|
|
break;
|
|
|
|
// move palette down
|
|
pNxtPalQ->posInDAC = pPalQ->posInDAC
|
|
+ pPalQ->numColours | PALETTE_MOVED;
|
|
|
|
// Q the palette change in position to the video DAC
|
|
UpdateDACqueueHandle(pNxtPalQ->posInDAC,
|
|
pNxtPalQ->numColours,
|
|
pNxtPalQ->hPal);
|
|
|
|
// update previous palette to current palette
|
|
pPalQ = pNxtPalQ;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Statless palette iterator. Returns the next palette in the list
|
|
* @param pStrtPal Palette to start from - when NULL will start from beginning of list
|
|
*/
|
|
PALQ *GetNextPalette(PALQ *pStrtPal) {
|
|
if (pStrtPal == NULL) {
|
|
// start of palette iteration - return 1st palette
|
|
return (palAllocData[0].objCount) ? palAllocData : NULL;
|
|
}
|
|
|
|
// validate palette Q pointer
|
|
assert(pStrtPal >= palAllocData && pStrtPal <= palAllocData + NUM_PALETTES - 1);
|
|
|
|
// return next active palette in list
|
|
while (++pStrtPal < palAllocData + NUM_PALETTES) {
|
|
if (pStrtPal->objCount)
|
|
// active palette found
|
|
return pStrtPal;
|
|
}
|
|
|
|
// non found
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Sets the current background colour.
|
|
* @param colour Colour to set the background to
|
|
*/
|
|
void SetBgndColour(COLORREF colour) {
|
|
// update background colour struct
|
|
bgndColour = colour;
|
|
|
|
// Q the change to the video DAC
|
|
UpdateDACqueue(BGND_DAC_INDEX, 1, &bgndColour);
|
|
}
|
|
|
|
/**
|
|
* Note whether a palette is being faded.
|
|
* @param pPalQ Palette queue position
|
|
* @param bFading Whether it is fading
|
|
*/
|
|
void FadingPalette(PPALQ pPalQ, bool bFading) {
|
|
// validate palette Q pointer
|
|
assert(pPalQ >= palAllocData && pPalQ <= palAllocData + NUM_PALETTES - 1);
|
|
|
|
// validate that this is a change
|
|
assert(pPalQ->bFading != bFading);
|
|
|
|
pPalQ->bFading = bFading;
|
|
}
|
|
|
|
/**
|
|
* All fading processes have just been killed, so none of the
|
|
* palettes are fading.
|
|
*/
|
|
void NoFadingPalettes(void) {
|
|
PPALQ pPalQ;
|
|
|
|
for (pPalQ = palAllocData; pPalQ <= palAllocData + NUM_PALETTES - 1; pPalQ++) {
|
|
pPalQ->bFading = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Builds the translucent palette from the current backgrounds palette.
|
|
* @param hPalette Handle to current background palette
|
|
*/
|
|
void CreateTranslucentPalette(SCNHANDLE hPalette) {
|
|
// get a pointer to the palette
|
|
PALETTE *pPal = (PALETTE *)LockMem(hPalette);
|
|
|
|
// leave background colour alone
|
|
transPalette[0] = 0;
|
|
|
|
for (uint i = 0; i < FROM_LE_32(pPal->numColours); i++) {
|
|
// get the RGB colour model values
|
|
uint8 red = GetRValue(pPal->palRGB[i]);
|
|
uint8 green = GetGValue(pPal->palRGB[i]);
|
|
uint8 blue = GetBValue(pPal->palRGB[i]);
|
|
|
|
// calculate the Value field of the HSV colour model
|
|
unsigned val = (red > green) ? red : green;
|
|
val = (val > blue) ? val : blue;
|
|
|
|
// map the Value field to one of the 4 colours reserved for the translucent palette
|
|
val /= 63;
|
|
transPalette[i + 1] = (uint8)((val == 0) ? 0 : val +
|
|
(TinselV2 ? TranslucentColour() : COL_HILIGHT) - 1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates the ghost palette
|
|
*/
|
|
void CreateGhostPalette(SCNHANDLE hPalette) {
|
|
// get a pointer to the palette
|
|
PALETTE *pPal = (PALETTE *)LockMem(hPalette);
|
|
int i;
|
|
|
|
// leave background colour alone
|
|
ghostPalette[0] = 0;
|
|
|
|
for (i = 0; i < pPal->numColours; i++) {
|
|
// get the RGB colour model values
|
|
uint8 red = GetRValue(pPal->palRGB[i]);
|
|
uint8 green = GetGValue(pPal->palRGB[i]);
|
|
uint8 blue = GetBValue(pPal->palRGB[i]);
|
|
|
|
// calculate the Value field of the HSV colour model
|
|
unsigned val = (red > green) ? red : green;
|
|
val = (val > blue) ? val : blue;
|
|
|
|
// map the Value field to one of the 4 colours reserved for the translucent palette
|
|
val /= 64;
|
|
assert(/*val >= 0 &&*/ val <= 3);
|
|
ghostPalette[i + 1] = (uint8)(val + SysVar(ISV_GHOST_BASE));
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns an adjusted colour RGB
|
|
* @param colour Colour to scale
|
|
*/
|
|
static COLORREF DimColour(COLORREF colour, int factor) {
|
|
uint32 red, green, blue;
|
|
|
|
if (factor == 10) {
|
|
// No change
|
|
return colour;
|
|
} else if (factor == 0) {
|
|
// No brightness
|
|
return 0;
|
|
} else {
|
|
// apply multiplier to RGB components
|
|
red = GetRValue(colour) * factor / 10;
|
|
green = GetGValue(colour) * factor / 10;
|
|
blue = GetBValue(colour) * factor / 10;
|
|
|
|
// return new colour
|
|
return RGB(red, green, blue);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* DimPartPalette
|
|
*/
|
|
void DimPartPalette(SCNHANDLE hDimPal, int startColour, int length, int brightness) {
|
|
PALQ *pPalQ;
|
|
PALETTE *pDimPal;
|
|
int iColour;
|
|
|
|
pPalQ = FindPalette(hDimPal);
|
|
assert(pPalQ);
|
|
|
|
// get pointer to dim palette
|
|
pDimPal = (PALETTE *)LockMem(hDimPal);
|
|
|
|
// Adjust for the fact that palettes don't contain colour 0
|
|
startColour -= 1;
|
|
|
|
// Check some other things
|
|
if (startColour + length > pPalQ->numColours)
|
|
error("DimPartPalette(): colour overrun");
|
|
|
|
for (iColour = startColour ; iColour < startColour + length; iColour++) {
|
|
pPalQ->palRGB[iColour] = DimColour(pDimPal->palRGB[iColour], brightness);
|
|
}
|
|
|
|
if (!pPalQ->bFading) {
|
|
// Q the change to the video DAC
|
|
UpdateDACqueue(pPalQ->posInDAC + startColour, length, &pPalQ->palRGB[startColour]);
|
|
}
|
|
}
|
|
|
|
int TranslucentColour(void) {
|
|
return translucentIndex;
|
|
}
|
|
|
|
int HighlightColour(void) {
|
|
static COLORREF cRef;
|
|
|
|
cRef = (COLORREF)SysVar(SYS_HighlightRGB);
|
|
UpdateDACqueue(talkIndex, 1, &cRef);
|
|
|
|
return talkIndex;
|
|
}
|
|
|
|
int TalkColour(void) {
|
|
return TinselV2 ? talkIndex : TALKFONT_COL;
|
|
}
|
|
|
|
void SetTalkColourRef(COLORREF colRef) {
|
|
talkColRef = colRef;
|
|
}
|
|
|
|
COLORREF GetTalkColourRef(void) {
|
|
return talkColRef;
|
|
}
|
|
|
|
void SetTagColorRef(COLORREF colRef) {
|
|
tagColRef = colRef;
|
|
}
|
|
|
|
COLORREF GetTagColorRef(void) {
|
|
return tagColRef;
|
|
}
|
|
|
|
void SetTranslucencyOffset(int offset) {
|
|
translucentIndex = offset;
|
|
}
|
|
|
|
void SetTalkTextOffset(int offset) {
|
|
talkIndex = offset;
|
|
}
|
|
|
|
} // end of namespace Tinsel
|