diff options
author | 2015-11-08 12:56:12 +0100 | |
---|---|---|
committer | 2015-11-08 12:56:12 +0100 | |
commit | 7c19aac60e12d6f5ea301bdb34d7826a01e0b06f (patch) | |
tree | f310d86aa2c6bfc19d115307dedde4eb0cd52dad /src/lib/util/palette.cpp | |
parent | a57b46ae933badd7441ce1644711dbb851e2b504 (diff) |
Rename *.c -> *.cpp in our source (nw)
Diffstat (limited to 'src/lib/util/palette.cpp')
-rw-r--r-- | src/lib/util/palette.cpp | 561 |
1 files changed, 561 insertions, 0 deletions
diff --git a/src/lib/util/palette.cpp b/src/lib/util/palette.cpp new file mode 100644 index 00000000000..cafdce24ebd --- /dev/null +++ b/src/lib/util/palette.cpp @@ -0,0 +1,561 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/****************************************************************************** + + palette.c + + Palette handling functions. + +******************************************************************************/ + +#include <assert.h> + +#include "palette.h" +#include <stdlib.h> +#include <math.h> + + +const rgb_t rgb_t::black(0,0,0); +const rgb_t rgb_t::white(255,255,255); + + +//************************************************************************** +// 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_t::clamp(float(gamma_map[entry.r()]) * contrast + brightness); + int g = rgb_t::clamp(float(gamma_map[entry.g()]) * contrast + brightness); + int b = rgb_t::clamp(float(gamma_map[entry.b()]) * contrast + brightness); + int a = entry.a(); + return rgb_t(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 and mark all entries dirty + UINT32 dirty_dwords = (colors + 31) / 32; + m_dirty.resize(dirty_dwords); + memset(&m_dirty[0], 0xff, dirty_dwords*4); + + // mark all entries dirty + 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.size() * 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 +//************************************************************************** + +//------------------------------------------------- +// alloc - static allocator +//------------------------------------------------- + +palette_t *palette_t::alloc(UINT32 numcolors, UINT32 numgroups) +{ + return new palette_t(numcolors, numgroups); +} + + +//------------------------------------------------- +// 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_t::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_t::black; + m_adjusted_rgb15[index] = rgb_t::black.as_rgb15(); + } + + // add black and white as the last two colors + m_adjusted_color[numcolors * numgroups + 0] = rgb_t::black; + m_adjusted_rgb15[numcolors * numgroups + 0] = rgb_t::black.as_rgb15(); + m_adjusted_color[numcolors * numgroups + 1] = rgb_t::white; + m_adjusted_rgb15[numcolors * numgroups + 1] = rgb_t::white.as_rgb15(); +} + + +//------------------------------------------------- +// palette_t - destructor +//------------------------------------------------- + +palette_t::~palette_t() +{ +} + + +//------------------------------------------------- +// palette_t - destructor +//------------------------------------------------- + +void palette_t::deref() +{ + 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_t::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_red_level - set the red level for a +// given palette index +//------------------------------------------------- + +void palette_t::entry_set_red_level(UINT32 index, UINT8 level) +{ + // if out of range, or unchanged, ignore + if (index >= m_numcolors || m_entry_color[index].r() == level) + return; + + // set the level + m_entry_color[index].set_r(level); + + // update across all groups + for (int groupnum = 0; groupnum < m_numgroups; groupnum++) + update_adjusted_color(groupnum, index); +} + + +//------------------------------------------------- +// entry_set_green_level - set the green level for a +// given palette index +//------------------------------------------------- + +void palette_t::entry_set_green_level(UINT32 index, UINT8 level) +{ + // if out of range, or unchanged, ignore + if (index >= m_numcolors || m_entry_color[index].g() == level) + return; + + // set the level + m_entry_color[index].set_g(level); + + // update across all groups + for (int groupnum = 0; groupnum < m_numgroups; groupnum++) + update_adjusted_color(groupnum, index); +} + + +//------------------------------------------------- +// entry_set_blue_level - set the blue level for a +// given palette index +//------------------------------------------------- + +void palette_t::entry_set_blue_level(UINT32 index, UINT8 level) +{ + // if out of range, or unchanged, ignore + if (index >= m_numcolors || m_entry_color[index].b() == level) + return; + + // set the level + m_entry_color[index].set_b(level); + + // 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.r() + 587 * rgb.g() + 114 * rgb.b(); + 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.r() + 587 * rgb.g() + 114 * rgb.b(); + INT32 u = ((INT32)rgb.b()-y /1000)*492 / 1000; + INT32 v = ((INT32)rgb.r()-y / 1000)*877 / 1000; + INT32 target = tmin + ((y - ymin) * (tmax - tmin + 1)) / (ymax - ymin); + UINT8 r = rgb_t::clamp(target + 1140 * v / 1000); + UINT8 g = rgb_t::clamp(target - 395 * u / 1000 - 581 * v / 1000); + UINT8 b = rgb_t::clamp(target + 2032 * u / 1000); + entry_set_color(index, rgb_t(r, g, b)); + } +} + +/** + * @fn void palette_t::update_adjusted_color(UINT32 group, UINT32 index) + * + * @brief ------------------------------------------------- + * update_adjusted_color - update a color index by group and index pair + * -------------------------------------------------. + * + * @param group The group. + * @param index Zero-based index of the. + */ + +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] = adjusted.as_rgb15(); + + // mark dirty in all clients + for (palette_client *client = m_client_list; client != NULL; client = client->next()) + client->mark_dirty(finalindex); +} |