484 lines
14 KiB
C
484 lines
14 KiB
C
// license:BSD-3-Clause
|
|
// copyright-holders:Aaron Giles
|
|
/******************************************************************************
|
|
|
|
palette.c
|
|
|
|
Palette handling functions.
|
|
|
|
******************************************************************************/
|
|
|
|
#include "palette.h"
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
|
|
|
|
|
|
//**************************************************************************
|
|
// INLINE FUNCTIONS
|
|
//**************************************************************************
|
|
|
|
//-------------------------------------------------
|
|
// adjust_palette_entry - adjust a palette
|
|
// entry for brightness
|
|
//-------------------------------------------------
|
|
|
|
inline rgb_t palette_t::adjust_palette_entry(rgb_t entry, float brightness, float contrast, const UINT8 *gamma_map)
|
|
{
|
|
int r = rgb_clamp(float(gamma_map[RGB_RED(entry)]) * contrast + brightness);
|
|
int g = rgb_clamp(float(gamma_map[RGB_GREEN(entry)]) * contrast + brightness);
|
|
int b = rgb_clamp(float(gamma_map[RGB_BLUE(entry)]) * contrast + brightness);
|
|
int a = RGB_ALPHA(entry);
|
|
return MAKE_ARGB(a,r,g,b);
|
|
}
|
|
|
|
|
|
|
|
//**************************************************************************
|
|
// CLIENT DIRTY LIST MANAGEMENT
|
|
//**************************************************************************
|
|
|
|
//-------------------------------------------------
|
|
// dirty_state - constructor
|
|
//-------------------------------------------------
|
|
|
|
palette_client::dirty_state::dirty_state()
|
|
: m_mindirty(0),
|
|
m_maxdirty(0)
|
|
{
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// dirty_list - return the current list and
|
|
// min/max values
|
|
//-------------------------------------------------
|
|
|
|
const UINT32 *palette_client::dirty_state::dirty_list(UINT32 &mindirty, UINT32 &maxdirty)
|
|
{
|
|
// fill in the mindirty/maxdirty
|
|
mindirty = m_mindirty;
|
|
maxdirty = m_maxdirty;
|
|
|
|
// if nothing to report, report nothing
|
|
return (m_mindirty > m_maxdirty) ? NULL : &m_dirty[0];
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// resize - resize the dirty array and mark all
|
|
// dirty
|
|
//-------------------------------------------------
|
|
|
|
void palette_client::dirty_state::resize(UINT32 colors)
|
|
{
|
|
// resize to the correct number of dwords
|
|
UINT32 dirty_dwords = (colors + 31) / 32;
|
|
m_dirty.resize(dirty_dwords);
|
|
|
|
// mark all entries dirty
|
|
memset(&m_dirty[0], 0xff, dirty_dwords * sizeof(UINT32));
|
|
m_dirty[dirty_dwords - 1] &= (1 << (colors % 32)) - 1;
|
|
|
|
// set min/max
|
|
m_mindirty = 0;
|
|
m_maxdirty = colors - 1;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// mark_dirty - mark a single entry dirty
|
|
//-------------------------------------------------
|
|
|
|
void palette_client::dirty_state::mark_dirty(UINT32 index)
|
|
{
|
|
m_dirty[index / 32] |= 1 << (index % 32);
|
|
m_mindirty = MIN(m_mindirty, index);
|
|
m_maxdirty = MAX(m_maxdirty, index);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// reset - clear the dirty array to mark all
|
|
// entries as clean
|
|
//-------------------------------------------------
|
|
|
|
void palette_client::dirty_state::reset()
|
|
{
|
|
// erase relevant entries in the new live one
|
|
memset(&m_dirty[m_mindirty / 32], 0, ((m_maxdirty / 32) + 1 - (m_mindirty / 32)) * sizeof(UINT32));
|
|
m_mindirty = m_dirty.count() * 32 - 1;
|
|
m_maxdirty = 0;
|
|
}
|
|
|
|
|
|
|
|
//**************************************************************************
|
|
// PALETTE CLIENT
|
|
//**************************************************************************
|
|
|
|
//-------------------------------------------------
|
|
// palette_client - constructor
|
|
//-------------------------------------------------
|
|
|
|
palette_client::palette_client(palette_t &palette)
|
|
: m_palette(palette),
|
|
m_next(NULL),
|
|
m_live(&m_dirty[0]),
|
|
m_previous(&m_dirty[1])
|
|
{
|
|
// add a reference to the palette
|
|
palette.ref();
|
|
|
|
// resize the dirty lists
|
|
UINT32 total_colors = palette.num_colors() * palette.num_groups();
|
|
m_dirty[0].resize(total_colors);
|
|
m_dirty[1].resize(total_colors);
|
|
|
|
// now add us to the list of clients
|
|
m_next = palette.m_client_list;
|
|
palette.m_client_list = this;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// ~palette_client - destructor
|
|
//-------------------------------------------------
|
|
|
|
palette_client::~palette_client()
|
|
{
|
|
// first locate and remove ourself from our palette's list
|
|
for (palette_client **curptr = &m_palette.m_client_list; *curptr != NULL; curptr = &(*curptr)->m_next)
|
|
if (*curptr == this)
|
|
{
|
|
*curptr = m_next;
|
|
break;
|
|
}
|
|
|
|
// now deref the palette
|
|
m_palette.deref();
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// dirty_list - atomically get the current dirty
|
|
// list for a client
|
|
//-------------------------------------------------
|
|
|
|
const UINT32 *palette_client::dirty_list(UINT32 &mindirty, UINT32 &maxdirty)
|
|
{
|
|
// if nothing to report, report nothing and don't swap
|
|
const UINT32 *result = m_live->dirty_list(mindirty, maxdirty);
|
|
if (result == NULL)
|
|
return NULL;
|
|
|
|
// swap the live and previous lists
|
|
dirty_state *temp = m_live;
|
|
m_live = m_previous;
|
|
m_previous = temp;
|
|
|
|
// reset new live one and return the pointer to the previous
|
|
m_live->reset();
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
//**************************************************************************
|
|
// PALETTE_T
|
|
//**************************************************************************
|
|
|
|
//-------------------------------------------------
|
|
// palette_t - constructor
|
|
//-------------------------------------------------
|
|
|
|
palette_t::palette_t(UINT32 numcolors, UINT32 numgroups)
|
|
: m_refcount(1),
|
|
m_numcolors(numcolors),
|
|
m_numgroups(numgroups),
|
|
m_brightness(0.0f),
|
|
m_contrast(1.0f),
|
|
m_gamma(1.0f),
|
|
m_entry_color(numcolors),
|
|
m_entry_contrast(numcolors),
|
|
m_adjusted_color(numcolors * numgroups + 2),
|
|
m_adjusted_rgb15(numcolors * numgroups + 2),
|
|
m_group_bright(numgroups),
|
|
m_group_contrast(numgroups),
|
|
m_client_list(NULL)
|
|
{
|
|
// initialize gamma map
|
|
for (UINT32 index = 0; index < 256; index++)
|
|
m_gamma_map[index] = index;
|
|
|
|
// initialize the per-entry data
|
|
for (UINT32 index = 0; index < numcolors; index++)
|
|
{
|
|
m_entry_color[index] = RGB_BLACK;
|
|
m_entry_contrast[index] = 1.0f;
|
|
}
|
|
|
|
// initialize the per-group data
|
|
for (UINT32 index = 0; index < numgroups; index++)
|
|
{
|
|
m_group_bright[index] = 0.0f;
|
|
m_group_contrast[index] = 1.0f;
|
|
}
|
|
|
|
// initialize the expanded data
|
|
for (UINT32 index = 0; index < numcolors * numgroups; index++)
|
|
{
|
|
m_adjusted_color[index] = RGB_BLACK;
|
|
m_adjusted_rgb15[index] = rgb_to_rgb15(RGB_BLACK);
|
|
}
|
|
|
|
// add black and white as the last two colors
|
|
m_adjusted_color[numcolors * numgroups + 0] = RGB_BLACK;
|
|
m_adjusted_rgb15[numcolors * numgroups + 0] = rgb_to_rgb15(RGB_BLACK);
|
|
m_adjusted_color[numcolors * numgroups + 1] = RGB_WHITE;
|
|
m_adjusted_rgb15[numcolors * numgroups + 1] = rgb_to_rgb15(RGB_WHITE);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// palette_t - destructor
|
|
//-------------------------------------------------
|
|
|
|
palette_t::~palette_t()
|
|
{
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// deref - dereference a palette object; if the
|
|
// reference count goes to 0, it is freed
|
|
//-------------------------------------------------
|
|
|
|
void palette_t::deref()
|
|
{
|
|
// if the reference count goes to 0, free
|
|
if (--m_refcount == 0)
|
|
delete this;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// set_brightness - set the overall brightness
|
|
// for the palette
|
|
//-------------------------------------------------
|
|
|
|
void palette_t::set_brightness(float brightness)
|
|
{
|
|
// convert incoming value to normalized result
|
|
brightness = (brightness - 1.0f) * 256.0f;
|
|
|
|
// set the global brightness if changed
|
|
if (m_brightness == brightness)
|
|
return;
|
|
m_brightness = brightness;
|
|
|
|
// update across all indices in all groups
|
|
for (int groupnum = 0; groupnum < m_numgroups; groupnum++)
|
|
for (int index = 0; index < m_numcolors; index++)
|
|
update_adjusted_color(groupnum, index);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// set_contrast - set the overall contrast for
|
|
// the palette
|
|
//-------------------------------------------------
|
|
|
|
void palette_t::set_contrast(float contrast)
|
|
{
|
|
// set the global contrast if changed
|
|
if (m_contrast == contrast)
|
|
return;
|
|
m_contrast = contrast;
|
|
|
|
// update across all indices in all groups
|
|
for (int groupnum = 0; groupnum < m_numgroups; groupnum++)
|
|
for (int index = 0; index < m_numcolors; index++)
|
|
update_adjusted_color(groupnum, index);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// set_gamma - set the overall gamma for the
|
|
// palette
|
|
//-------------------------------------------------
|
|
|
|
void palette_t::set_gamma(float gamma)
|
|
{
|
|
// set the global gamma if changed
|
|
if (m_gamma == gamma)
|
|
return;
|
|
m_gamma = gamma;
|
|
|
|
// recompute the gamma map
|
|
gamma = 1.0f / gamma;
|
|
for (int index = 0; index < 256; index++)
|
|
{
|
|
float fval = float(index) * (1.0f / 255.0f);
|
|
float fresult = pow(fval, gamma);
|
|
m_gamma_map[index] = rgb_clamp(255.0f * fresult);
|
|
}
|
|
|
|
// update across all indices in all groups
|
|
for (int groupnum = 0; groupnum < m_numgroups; groupnum++)
|
|
for (int index = 0; index < m_numcolors; index++)
|
|
update_adjusted_color(groupnum, index);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// entry_set_color - set the raw RGB color for a
|
|
// given palette index
|
|
//-------------------------------------------------
|
|
|
|
void palette_t::entry_set_color(UINT32 index, rgb_t rgb)
|
|
{
|
|
// if out of range, or unchanged, ignore
|
|
if (index >= m_numcolors || m_entry_color[index] == rgb)
|
|
return;
|
|
|
|
// set the color
|
|
m_entry_color[index] = rgb;
|
|
|
|
// update across all groups
|
|
for (int groupnum = 0; groupnum < m_numgroups; groupnum++)
|
|
update_adjusted_color(groupnum, index);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// entry_set_contrast - set the contrast
|
|
// adjustment for a single palette index
|
|
//-------------------------------------------------
|
|
|
|
void palette_t::entry_set_contrast(UINT32 index, float contrast)
|
|
{
|
|
// if out of range, or unchanged, ignore
|
|
if (index >= m_numcolors || m_entry_contrast[index] == contrast)
|
|
return;
|
|
|
|
// set the contrast
|
|
m_entry_contrast[index] = contrast;
|
|
|
|
// update across all groups
|
|
for (int groupnum = 0; groupnum < m_numgroups; groupnum++)
|
|
update_adjusted_color(groupnum, index);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// group_set_brightness - configure overall
|
|
// brightness for a palette group
|
|
//-------------------------------------------------
|
|
|
|
void palette_t::group_set_brightness(UINT32 group, float brightness)
|
|
{
|
|
// convert incoming value to normalized result
|
|
brightness = (brightness - 1.0f) * 256.0f;
|
|
|
|
// if out of range, or unchanged, ignore
|
|
if (group >= m_numgroups || m_group_bright[group] == brightness)
|
|
return;
|
|
|
|
// set the contrast
|
|
m_group_bright[group] = brightness;
|
|
|
|
// update across all colors
|
|
for (int index = 0; index < m_numcolors; index++)
|
|
update_adjusted_color(group, index);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// group_set_contrast - configure overall
|
|
// contrast for a palette group
|
|
//-------------------------------------------------
|
|
|
|
void palette_t::group_set_contrast(UINT32 group, float contrast)
|
|
{
|
|
// if out of range, or unchanged, ignore
|
|
if (group >= m_numgroups || m_group_contrast[group] == contrast)
|
|
return;
|
|
|
|
// set the contrast
|
|
m_group_contrast[group] = contrast;
|
|
|
|
// update across all colors
|
|
for (int index = 0; index < m_numcolors; index++)
|
|
update_adjusted_color(group, index);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// normalize_range - normalize a range of palette
|
|
// entries
|
|
//-------------------------------------------------
|
|
|
|
void palette_t::normalize_range(UINT32 start, UINT32 end, int lum_min, int lum_max)
|
|
{
|
|
// clamp within range
|
|
start = MAX(start, 0);
|
|
end = MIN(end, m_numcolors - 1);
|
|
|
|
// find the minimum and maximum brightness of all the colors in the range
|
|
INT32 ymin = 1000 * 255, ymax = 0;
|
|
for (UINT32 index = start; index <= end; index++)
|
|
{
|
|
rgb_t rgb = m_entry_color[index];
|
|
UINT32 y = 299 * RGB_RED(rgb) + 587 * RGB_GREEN(rgb) + 114 * RGB_BLUE(rgb);
|
|
ymin = MIN(ymin, y);
|
|
ymax = MAX(ymax, y);
|
|
}
|
|
|
|
// determine target minimum/maximum
|
|
INT32 tmin = (lum_min < 0) ? ((ymin + 500) / 1000) : lum_min;
|
|
INT32 tmax = (lum_max < 0) ? ((ymax + 500) / 1000) : lum_max;
|
|
|
|
// now normalize the palette
|
|
for (UINT32 index = start; index <= end; index++)
|
|
{
|
|
rgb_t rgb = m_entry_color[index];
|
|
INT32 y = 299 * RGB_RED(rgb) + 587 * RGB_GREEN(rgb) + 114 * RGB_BLUE(rgb);
|
|
INT32 u = ((INT32)RGB_BLUE(rgb)-y /1000)*492 / 1000;
|
|
INT32 v = ((INT32)RGB_RED(rgb)-y / 1000)*877 / 1000;
|
|
INT32 target = tmin + ((y - ymin) * (tmax - tmin + 1)) / (ymax - ymin);
|
|
UINT8 r = rgb_clamp(target + 1140 * v / 1000);
|
|
UINT8 g = rgb_clamp(target - 395 * u / 1000 - 581 * v / 1000);
|
|
UINT8 b = rgb_clamp(target + 2032 * u / 1000);
|
|
entry_set_color(index, MAKE_RGB(r, g, b));
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// update_adjusted_color - update a color index
|
|
// by group and index pair
|
|
//-------------------------------------------------
|
|
|
|
void palette_t::update_adjusted_color(UINT32 group, UINT32 index)
|
|
{
|
|
// compute the adjusted value
|
|
rgb_t adjusted = adjust_palette_entry(m_entry_color[index],
|
|
m_group_bright[group] + m_brightness,
|
|
m_group_contrast[group] * m_entry_contrast[index] * m_contrast,
|
|
m_gamma_map);
|
|
|
|
// if not different, ignore
|
|
UINT32 finalindex = group * m_numcolors + index;
|
|
if (m_adjusted_color[finalindex] == adjusted)
|
|
return;
|
|
|
|
// otherwise, modify the adjusted color array
|
|
m_adjusted_color[finalindex] = adjusted;
|
|
m_adjusted_rgb15[finalindex] = rgb_to_rgb15(adjusted);
|
|
|
|
// mark dirty in all clients
|
|
for (palette_client *client = m_client_list; client != NULL; client = client->next())
|
|
client->mark_dirty(finalindex);
|
|
}
|