diff options
Diffstat (limited to 'trunk/src/lib/util/palette.c')
-rw-r--r-- | trunk/src/lib/util/palette.c | 789 |
1 files changed, 789 insertions, 0 deletions
diff --git a/trunk/src/lib/util/palette.c b/trunk/src/lib/util/palette.c new file mode 100644 index 00000000000..6ba369d8bc6 --- /dev/null +++ b/trunk/src/lib/util/palette.c @@ -0,0 +1,789 @@ +/****************************************************************************** + + palette.c + + Palette handling functions. + +**************************************************************************** + + Copyright Aaron Giles + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name 'MAME' nor the names of its contributors may be + used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +******************************************************************************/ + +#include "palette.h" +#include <stdlib.h> +#include <math.h> + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +/* object to track dirty states */ +typedef struct _dirty_state dirty_state; +struct _dirty_state +{ + UINT32 * dirty; /* bitmap of dirty entries */ + UINT32 mindirty; /* minimum dirty entry */ + UINT32 maxdirty; /* minimum dirty entry */ +}; + + +/* a single palette client */ +struct _palette_client +{ + palette_client *next; /* pointer to next client */ + palette_t * palette; /* reference to the palette */ + dirty_state live; /* live dirty state */ + dirty_state previous; /* previous dirty state */ +}; + + +/* a palette object */ +struct _palette_t +{ + UINT32 refcount; /* reference count on the palette */ + UINT32 numcolors; /* number of colors in the palette */ + UINT32 numgroups; /* number of groups in the palette */ + + float brightness; /* overall brightness value */ + float contrast; /* overall contrast value */ + float gamma; /* overall gamma value */ + UINT8 gamma_map[256]; /* gamma map */ + + rgb_t * entry_color; /* array of raw colors */ + float * entry_contrast; /* contrast value for each entry */ + rgb_t * adjusted_color; /* array of adjusted colors */ + rgb_t * adjusted_rgb15; /* array of adjusted colors as RGB15 */ + + float * group_bright; /* brightness value for each group */ + float * group_contrast; /* contrast value for each group */ + + palette_client *client_list; /* list of clients for this palette */ +}; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +static void internal_palette_free(palette_t *palette); +static void update_adjusted_color(palette_t *palette, UINT32 group, UINT32 index); + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + adjust_palette_entry - adjust a palette + entry for brightness +-------------------------------------------------*/ + +INLINE rgb_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); +} + + + +/*************************************************************************** + PALETTE ALLOCATION +***************************************************************************/ + +/*------------------------------------------------- + palette_alloc - allocate a new palette object + and take a single reference on it +-------------------------------------------------*/ + +palette_t *palette_alloc(UINT32 numcolors, UINT32 numgroups) +{ + palette_t *palette; + UINT32 index; + + /* allocate memory */ + palette = (palette_t *)malloc(sizeof(*palette)); + if (palette == NULL) + goto error; + memset(palette, 0, sizeof(*palette)); + + /* initialize overall controls */ + palette->brightness = 0.0f; + palette->contrast = 1.0f; + palette->gamma = 1.0f; + for (index = 0; index < 256; index++) + palette->gamma_map[index] = index; + + /* allocate an array of palette entries and individual contrasts for each */ + palette->entry_color = (rgb_t *)malloc(sizeof(*palette->entry_color) * numcolors); + palette->entry_contrast = (float *)malloc(sizeof(*palette->entry_contrast) * numcolors); + if (palette->entry_color == NULL || palette->entry_contrast == NULL) + goto error; + + /* initialize the entries */ + for (index = 0; index < numcolors; index++) + { + palette->entry_color[index] = RGB_BLACK; + palette->entry_contrast[index] = 1.0f; + } + + /* allocate an array of brightness and contrast for each group */ + palette->group_bright = (float *)malloc(sizeof(*palette->group_bright) * numgroups); + palette->group_contrast = (float *)malloc(sizeof(*palette->group_contrast) * numgroups); + if (palette->group_bright == NULL || palette->group_contrast == NULL) + goto error; + + /* initialize the entries */ + for (index = 0; index < numgroups; index++) + { + palette->group_bright[index] = 0.0f; + palette->group_contrast[index] = 1.0f; + } + + /* allocate arrays for the adjusted colors */ + palette->adjusted_color = (rgb_t *)malloc(sizeof(*palette->adjusted_color) * (numcolors * numgroups + 2)); + palette->adjusted_rgb15 = (rgb_t *)malloc(sizeof(*palette->adjusted_rgb15) * (numcolors * numgroups + 2)); + if (palette->adjusted_color == NULL || palette->adjusted_rgb15 == NULL) + goto error; + + /* initialize the arrays */ + for (index = 0; index < numcolors * numgroups; index++) + { + palette->adjusted_color[index] = RGB_BLACK; + palette->adjusted_rgb15[index] = rgb_to_rgb15(RGB_BLACK); + } + + /* add black and white as the last two colors */ + palette->adjusted_color[index] = RGB_BLACK; + palette->adjusted_rgb15[index++] = rgb_to_rgb15(RGB_BLACK); + palette->adjusted_color[index] = RGB_WHITE; + palette->adjusted_rgb15[index++] = rgb_to_rgb15(RGB_WHITE); + + /* initialize the remainder of the structure */ + palette->refcount = 1; + palette->numcolors = numcolors; + palette->numgroups = numgroups; + return palette; + +error: + if (palette != NULL) + internal_palette_free(palette); + + return NULL; +} + + +/*------------------------------------------------- + palette_ref - reference a palette object, + incrementing its reference count +-------------------------------------------------*/ + +void palette_ref(palette_t *palette) +{ + palette->refcount++; +} + + +/*------------------------------------------------- + palette_deref - dereference a palette object; + if the reference count goes to 0, it is freed +-------------------------------------------------*/ + +void palette_deref(palette_t *palette) +{ + /* if the reference count goes to 0, free */ + if (--palette->refcount == 0) + internal_palette_free(palette); +} + + + +/*************************************************************************** + PALETTE INFORMATION +***************************************************************************/ + +/*------------------------------------------------- + palette_get_num_colors - return the number of + colors allocated in the palette +-------------------------------------------------*/ + +int palette_get_num_colors(palette_t *palette) +{ + return palette->numcolors; +} + + +/*------------------------------------------------- + palette_get_num_groups - return the number of + groups managed by the palette +-------------------------------------------------*/ + +int palette_get_num_groups(palette_t *palette) +{ + return palette->numgroups; +} + + +/*------------------------------------------------- + palette_get_max_index - return the maximum + allowed index (i.e., length of arrays returned + by palette_entry_list*) +-------------------------------------------------*/ + +int palette_get_max_index(palette_t *palette) +{ + return palette->numcolors * palette->numgroups + 2; +} + + +/*------------------------------------------------- + palette_get_black_entry - return the index of + the black entry +-------------------------------------------------*/ + +UINT32 palette_get_black_entry(palette_t *palette) +{ + return palette->numcolors * palette->numgroups + 0; +} + + +/*------------------------------------------------- + palette_get_white_entry - return the index of + the white entry +-------------------------------------------------*/ + +UINT32 palette_get_white_entry(palette_t *palette) +{ + return palette->numcolors * palette->numgroups + 1; +} + + + +/*************************************************************************** + PALETTE CLIENTS +***************************************************************************/ + +/*------------------------------------------------- + palette_client_alloc - add a new client to a + palette +-------------------------------------------------*/ + +palette_client *palette_client_alloc(palette_t *palette) +{ + UINT32 total_colors = palette->numcolors * palette->numgroups; + UINT32 dirty_dwords = (total_colors + 31) / 32; + palette_client *client; + + /* allocate memory for the client */ + client = (palette_client *)malloc(sizeof(*client)); + if (client == NULL) + goto error; + memset(client, 0, sizeof(*client)); + + /* allocate dirty lists */ + client->live.dirty = (UINT32 *)malloc(dirty_dwords * sizeof(UINT32)); + client->previous.dirty = (UINT32 *)malloc(dirty_dwords * sizeof(UINT32)); + if (client->live.dirty == NULL || client->previous.dirty == NULL) + goto error; + + /* mark everything dirty to start with */ + memset(client->live.dirty, 0xff, dirty_dwords * sizeof(UINT32)); + memset(client->previous.dirty, 0xff, dirty_dwords * sizeof(UINT32)); + client->live.dirty[dirty_dwords - 1] &= (1 << (total_colors % 32)) - 1; + client->previous.dirty[dirty_dwords - 1] &= (1 << (total_colors % 32)) - 1; + + /* initialize the rest of the structure and add a reference to a palette */ + client->palette = palette; + palette_ref(palette); + client->live.mindirty = 0; + client->live.maxdirty = total_colors - 1; + + /* now add us to the list of clients */ + client->next = palette->client_list; + palette->client_list = client; + return client; + +error: + if (client != NULL) + { + if (client->live.dirty != NULL) + free(client->live.dirty); + if (client->previous.dirty != NULL) + free(client->previous.dirty); + free(client); + } + return NULL; +} + + +/*------------------------------------------------- + palette_client_free - remove a client from a + palette +-------------------------------------------------*/ + +void palette_client_free(palette_client *client) +{ + palette_client **curptr; + + /* first locate and remove ourself from the palette's list */ + for (curptr = &client->palette->client_list; *curptr != NULL; curptr = &(*curptr)->next) + if (*curptr == client) + { + *curptr = client->next; + break; + } + + /* now deref the palette */ + palette_deref(client->palette); + + /* free our data */ + if (client->live.dirty != NULL) + free(client->live.dirty); + if (client->previous.dirty != NULL) + free(client->previous.dirty); + free(client); +} + + +/*------------------------------------------------- + palette_client_get_palette - return a pointer + to the palette for this client +-------------------------------------------------*/ + +palette_t *palette_client_get_palette(palette_client *client) +{ + return client->palette; +} + + +/*------------------------------------------------- + palette_client_get_dirty_list - atomically get + the current dirty list for a client +-------------------------------------------------*/ + +const UINT32 *palette_client_get_dirty_list(palette_client *client, UINT32 *mindirty, UINT32 *maxdirty) +{ + dirty_state temp; + + /* fill in the mindirty/maxdirty */ + if (mindirty != NULL) + *mindirty = client->live.mindirty; + if (maxdirty != NULL) + *maxdirty = client->live.maxdirty; + + /* if nothing to report, report nothing and don't swap */ + if (client->live.mindirty > client->live.maxdirty) + return NULL; + + /* swap the live and previous lists */ + temp = client->live; + client->live = client->previous; + client->previous = temp; + + /* erase relevant entries in the new live one */ + if (client->live.mindirty <= client->live.maxdirty) + memset(client->live.dirty, client->live.mindirty / 8, (client->live.maxdirty / 8) + 1 - (client->live.mindirty / 8)); + client->live.mindirty = client->palette->numcolors * client->palette->numgroups; + client->live.maxdirty = 0; + + /* return a pointer to the previous table */ + return client->previous.dirty; +} + + + +/*************************************************************************** + PALETTE COLOR MANAGEMENT +***************************************************************************/ + +/*------------------------------------------------- + palette_entry_set_color - set the raw RGB + color for a given palette index +-------------------------------------------------*/ + +void palette_entry_set_color(palette_t *palette, UINT32 index, rgb_t rgb) +{ + int groupnum; + + /* if out of range, or unchanged, ignore */ + if (index >= palette->numcolors || palette->entry_color[index] == rgb) + return; + + /* set the color */ + palette->entry_color[index] = rgb; + + /* update across all groups */ + for (groupnum = 0; groupnum < palette->numgroups; groupnum++) + update_adjusted_color(palette, groupnum, index); +} + + +/*------------------------------------------------- + palette_entry_get_color - return the raw RGB + color for a given palette index +-------------------------------------------------*/ + +rgb_t palette_entry_get_color(palette_t *palette, UINT32 index) +{ + return (index < palette->numcolors) ? palette->entry_color[index] : RGB_BLACK; +} + + +/*------------------------------------------------- + palette_entry_get_adjusted_color - return the + adjusted RGB color (after all adjustments) for + a given palette index +-------------------------------------------------*/ + +rgb_t palette_entry_get_adjusted_color(palette_t *palette, UINT32 index) +{ + return (index < palette->numcolors * palette->numgroups) ? palette->adjusted_color[index] : RGB_BLACK; +} + + +/*------------------------------------------------- + palette_entry_list_raw - return the entire + palette as an array of raw RGB values +-------------------------------------------------*/ + +const rgb_t *palette_entry_list_raw(palette_t *palette) +{ + return palette->entry_color; +} + + +/*------------------------------------------------- + palette_entry_list_adjusted - return the + entire palette as an array of adjusted RGB + values +-------------------------------------------------*/ + +const rgb_t *palette_entry_list_adjusted(palette_t *palette) +{ + return palette->adjusted_color; +} + + +/*------------------------------------------------- + palette_entry_list_adjusted_rgb15 - return the + entire palette as an array of adjusted RGB-15 + values +-------------------------------------------------*/ + +const rgb_t *palette_entry_list_adjusted_rgb15(palette_t *palette) +{ + return palette->adjusted_rgb15; +} + + + +/*************************************************************************** + PALETTE ADJUSTMENTS +***************************************************************************/ + +/*------------------------------------------------- + palette_set_brightness - set the overall + brightness for the palette +-------------------------------------------------*/ + +void palette_set_brightness(palette_t *palette, float brightness) +{ + int groupnum, index; + + /* convert incoming value to normalized result */ + brightness = (brightness - 1.0f) * 256.0f; + + /* set the global brightness if changed */ + if (palette->brightness == brightness) + return; + palette->brightness = brightness; + + /* update across all indices in all groups */ + for (groupnum = 0; groupnum < palette->numgroups; groupnum++) + for (index = 0; index < palette->numcolors; index++) + update_adjusted_color(palette, groupnum, index); +} + + +/*------------------------------------------------- + palette_set_contrast - set the overall + contrast for the palette +-------------------------------------------------*/ + +void palette_set_contrast(palette_t *palette, float contrast) +{ + int groupnum, index; + + /* set the global contrast if changed */ + if (palette->contrast == contrast) + return; + palette->contrast = contrast; + + /* update across all indices in all groups */ + for (groupnum = 0; groupnum < palette->numgroups; groupnum++) + for (index = 0; index < palette->numcolors; index++) + update_adjusted_color(palette, groupnum, index); +} + + +/*------------------------------------------------- + palette_set_gamma - set the overall + gamma for the palette +-------------------------------------------------*/ + +void palette_set_gamma(palette_t *palette, float gamma) +{ + int groupnum, index; + + /* set the global gamma if changed */ + if (palette->gamma == gamma) + return; + palette->gamma = gamma; + + /* recompute the gamma map */ + gamma = 1.0f / gamma; + for (index = 0; index < 256; index++) + { + float fval = (float)index * (1.0f / 255.0f); + float fresult = pow(fval, gamma); + palette->gamma_map[index] = rgb_clamp(255.0f * fresult); + } + + /* update across all indices in all groups */ + for (groupnum = 0; groupnum < palette->numgroups; groupnum++) + for (index = 0; index < palette->numcolors; index++) + update_adjusted_color(palette, groupnum, index); +} + + +/*------------------------------------------------- + palette_entry_set_contrast - set the contrast + adjustment for a single palette index +-------------------------------------------------*/ + +void palette_entry_set_contrast(palette_t *palette, UINT32 index, float contrast) +{ + int groupnum; + + /* if out of range, or unchanged, ignore */ + if (index >= palette->numcolors || palette->entry_contrast[index] == contrast) + return; + + /* set the contrast */ + palette->entry_contrast[index] = contrast; + + /* update across all groups */ + for (groupnum = 0; groupnum < palette->numgroups; groupnum++) + update_adjusted_color(palette, groupnum, index); +} + + +/*------------------------------------------------- + palette_entry_get_contrast - return the + contrast adjustment for a single palette index +-------------------------------------------------*/ + +float palette_entry_get_contrast(palette_t *palette, UINT32 index) +{ + return (index < palette->numcolors) ? palette->entry_contrast[index] : 1.0f; +} + + +/*------------------------------------------------- + palette_group_set_brightness - configure + overall brightness for a palette group +-------------------------------------------------*/ + +void palette_group_set_brightness(palette_t *palette, UINT32 group, float brightness) +{ + int index; + + /* convert incoming value to normalized result */ + brightness = (brightness - 1.0f) * 256.0f; + + /* if out of range, or unchanged, ignore */ + if (group >= palette->numgroups || palette->group_bright[group] == brightness) + return; + + /* set the contrast */ + palette->group_bright[group] = brightness; + + /* update across all colors */ + for (index = 0; index < palette->numcolors; index++) + update_adjusted_color(palette, group, index); +} + + +/*------------------------------------------------- + palette_group_set_contrast - configure + overall contrast for a palette group +-------------------------------------------------*/ + +void palette_group_set_contrast(palette_t *palette, UINT32 group, float contrast) +{ + int index; + + /* if out of range, or unchanged, ignore */ + if (group >= palette->numgroups || palette->group_contrast[group] == contrast) + return; + + /* set the contrast */ + palette->group_contrast[group] = contrast; + + /* update across all colors */ + for (index = 0; index < palette->numcolors; index++) + update_adjusted_color(palette, group, index); +} + + + +/*************************************************************************** + PALETTE UTILITIES +***************************************************************************/ + +/*------------------------------------------------- + palette_normalize_range - normalize a range + of palette entries +-------------------------------------------------*/ + +void palette_normalize_range(palette_t *palette, UINT32 start, UINT32 end, int lum_min, int lum_max) +{ + INT32 ymin = 1000 * 255, ymax = 0; + INT32 tmin, tmax; + UINT32 index; + + /* clamp within range */ + start = MAX(start, 0); + end = MIN(end, palette->numcolors - 1); + + /* find the minimum and maximum brightness of all the colors in the range */ + for (index = start; index <= end; index++) + { + rgb_t rgb = palette->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 */ + tmin = (lum_min < 0) ? ((ymin + 500) / 1000) : lum_min; + tmax = (lum_max < 0) ? ((ymax + 500) / 1000) : lum_max; + + /* now normalize the palette */ + for (index = start; index <= end; index++) + { + rgb_t rgb = palette->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); + palette_entry_set_color(palette, index, MAKE_RGB(r, g, b)); + } +} + + + +/*************************************************************************** + INTERNAL ROUTINES +***************************************************************************/ + +/*------------------------------------------------- + internal_palette_free - free all allocations + from a palette +-------------------------------------------------*/ + +static void internal_palette_free(palette_t *palette) +{ + /* free per-color data */ + if (palette->entry_color != NULL) + free(palette->entry_color); + if (palette->entry_contrast != NULL) + free(palette->entry_contrast); + + /* free per-group data */ + if (palette->group_bright != NULL) + free(palette->group_bright); + if (palette->group_contrast != NULL) + free(palette->group_contrast); + + /* free adjusted data */ + if (palette->adjusted_color != NULL) + free(palette->adjusted_color); + if (palette->adjusted_rgb15 != NULL) + free(palette->adjusted_rgb15); + + /* and the palette itself */ + free(palette); +} + + +/*------------------------------------------------- + update_adjusted_color - update a color index + by group and index pair +-------------------------------------------------*/ + +static void update_adjusted_color(palette_t *palette, UINT32 group, UINT32 index) +{ + UINT32 finalindex = group * palette->numcolors + index; + palette_client *client; + rgb_t adjusted; + + /* compute the adjusted value */ + adjusted = adjust_palette_entry(palette->entry_color[index], + palette->group_bright[group] + palette->brightness, + palette->group_contrast[group] * palette->entry_contrast[index] * palette->contrast, + palette->gamma_map); + + /* if not different, ignore */ + if (palette->adjusted_color[finalindex] == adjusted) + return; + + /* otherwise, modify the adjusted color array */ + palette->adjusted_color[finalindex] = adjusted; + palette->adjusted_rgb15[finalindex] = rgb_to_rgb15(adjusted); + + /* mark dirty in all clients */ + for (client = palette->client_list; client != NULL; client = client->next) + { + client->live.dirty[finalindex / 32] |= 1 << (finalindex % 32); + client->live.mindirty = MIN(client->live.mindirty, finalindex); + client->live.maxdirty = MAX(client->live.maxdirty, finalindex); + } +} |