/* convert.c Image/color conversion routins (RGB to paletted) Copyright (C) 2013 Bill Currie Author: Bill Currie Date: 2013/5/10 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: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "QF/alloc.h" #include "QF/hash.h" #include "QF/image.h" #include "QF/mathlib.h" struct colcache_s { struct colcache_s *next; hashtab_t *tab; }; typedef struct colcache_color_s { struct colcache_color_s *next; byte rgb[3]; byte col; } colcache_color_t; ALLOC_STATE (colcache_t, colcache); ALLOC_STATE (colcache_color_t, colcache_color); static colcache_color_t * colcache_new_color (const byte *rgb, byte ind) { colcache_color_t *col; ALLOC (256, colcache_color_t, colcache_color, col); VectorCopy (rgb, col->rgb); col->col = ind; return col; } static void colcache_free_color (void *_col, void *unused) { colcache_color_t *col = (colcache_color_t *) _col; FREE (colcache_color, col); } static uintptr_t colcache_get_hash (const void *_col, void *unused) { colcache_color_t *col = (colcache_color_t *) _col; uintptr_t r, g, b; r = col->rgb[0]; g = col->rgb[1]; b = col->rgb[2]; return (r << 8) ^ (g << 4) ^ b; } static int colcache_compare (const void *_cola, const void *_colb, void *unused) { colcache_color_t *cola = (colcache_color_t *) _cola; colcache_color_t *colb = (colcache_color_t *) _colb; return VectorCompare (cola->rgb, colb->rgb); } colcache_t * ColorCache_New (void) { colcache_t *cache; ALLOC (16, colcache_t, colcache, cache); cache->tab = Hash_NewTable (1023, 0, colcache_free_color, 0, 0); Hash_SetHashCompare (cache->tab, colcache_get_hash, colcache_compare); return cache; } void ColorCache_Delete (colcache_t *cache) { Hash_DelTable (cache->tab); FREE (colcache, cache); } void ColorCache_Shutdown (void) { ALLOC_FREE_BLOCKS (colcache); ALLOC_FREE_BLOCKS (colcache_color); } byte ConvertColor (const byte *rgb, const byte *pal, colcache_t *cache) { //FIXME slow! int dist[3]; int d, bestd = 256 * 256 * 3, bestc = -1; int i; colcache_color_t *col; if (cache) { colcache_color_t search; VectorCopy (rgb, search.rgb); col = Hash_FindElement (cache->tab, &search); if (col) return col->col; } for (i = 0; i < 256; i++) { VectorSubtract (pal + i * 3, rgb, dist); d = DotProduct (dist, dist); if (d < bestd) { bestd = d; bestc = i; } } if (cache) { col = colcache_new_color (rgb, bestc); Hash_AddElement (cache->tab, col); } return bestc; } tex_t * ConvertImage (const tex_t *tex, const byte *pal) { tex_t *new; int pixels; int bpp = 3; int i; colcache_t *cache; pixels = tex->width * tex->height; new = malloc (sizeof (tex_t) + pixels); new->data = (byte *) (new + 1); new->width = tex->width; new->height = tex->height; new->format = tex_palette; new->palette = pal; switch (tex->format) { case tex_palette: case tex_l: // will not work as expected FIXME case tex_a: // will not work as expected FIXME case tex_frgba: // will not work as expected FIXME memcpy (new->data, tex->data, pixels); break; case tex_la: // will not work as expected FIXME for (i = 0; i < pixels; i++) new->data[i] = tex->data[i * 2]; break; case tex_rgba: bpp = 4; case tex_rgb: cache = ColorCache_New (); for (i = 0; i < pixels; i++) new->data[i] = ConvertColor (tex->data + i * bpp, pal, cache); ColorCache_Delete (cache); break; } return new; }