Move the tex conversion to libQFimage.

This is for the conversion /to/ paletted textures. The conversion is
necessary for csqc support. In the process, the conversion has been sped up
by implementing a color cache for the conversion process. I haven't
measured the difference yet, but Mr Fixit does seem to load much faster for
the sw renderer than it did before the change (many months old memory).
This commit is contained in:
Bill Currie 2013-05-11 08:02:43 +09:00
parent d39adc6a64
commit 0cae54d25d
5 changed files with 190 additions and 61 deletions

View file

@ -29,15 +29,16 @@
#ifndef __QF_image_h
#define __QF_image_h
#include "QF/qtypes.h"
#include "QF/quakeio.h"
// could not use texture_t as that is used for models.
typedef struct tex_s {
int width;
int height;
int format;
unsigned char *palette; // 0 = 32 bit, otherwise 8
unsigned char data[4]; // variable length
int width;
int height;
int format;
const byte *palette; // 0 = 32 bit, otherwise 8
byte data[4]; // variable length
} tex_t;
#define tex_palette 0
@ -47,6 +48,12 @@ typedef struct tex_s {
#define tex_rgb 3
#define tex_rgba 4
typedef struct colcache_s colcache_t;
tex_t *LoadImage (const char *imageFile);
colcache_t *ColorCache_New (void);
void ColorCache_Delete (colcache_t *cache);
byte ConvertColor (const byte *rgb, const byte *pal, colcache_t *cache);
tex_t *ConvertImage (const tex_t *tex, const byte *pal);
#endif //__QF_image_h

View file

@ -13,6 +13,6 @@ libQFimage_la_LDFLAGS= $(lib_ldflags)
libQFimage_la_LIBADD= $(image_deps) $(PNG_LIBS)
libQFimage_la_DEPENDENCIES= $(pmage_deps)
libQFimage_la_SOURCES= \
image.c pcx.c png.c tga.c
convert.c image.c pcx.c png.c tga.c
EXTRA_DIST=

175
libs/image/convert.c Normal file
View file

@ -0,0 +1,175 @@
/*
convert.c
Image/color conversion routins (RGB to paletted)
Copyright (C) 2013 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
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;
static colcache_t *colcache_freelist;
static colcache_color_t *colcache_color_freelist;
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);
Hash_SetHashCompare (cache->tab, colcache_get_hash, colcache_compare);
return cache;
}
void
ColorCache_Delete (colcache_t *cache)
{
Hash_DelTable (cache->tab);
FREE (colcache, cache);
}
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 (field_offset (tex_t, data[pixels]));
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
case tex_a: // will not work as expected
memcpy (new->data, tex->data, pixels);
break;
case tex_la: // will not work as expected
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;
}

View file

@ -81,59 +81,6 @@ sw_iqm_clear (model_t *mod)
Mod_FreeIQM (iqm);
}
static byte
convert_color (byte *rgb)
{
//FIXME slow!
int dist[3];
int d, bestd = 256 * 256 * 3, bestc = -1;
int i;
for (i = 0; i < 256; i++) {
VectorSubtract (vid.basepal + i * 3, rgb, dist);
d = DotProduct (dist, dist);
if (d < bestd) {
bestd = d;
bestc = i;
}
}
return bestc;
}
static tex_t *
convert_tex (tex_t *tex)
{
tex_t *new;
int pixels;
int bpp = 3;
int i;
pixels = tex->width * tex->height;
new = malloc (field_offset (tex_t, data[pixels]));
new->width = tex->width;
new->height = tex->height;
new->format = tex_palette;
new->palette = 0;
switch (tex->format) {
case tex_palette:
case tex_l: // will not work as expected
case tex_a: // will not work as expected
memcpy (new->data, tex->data, pixels);
break;
case tex_la: // will not work as expected
for (i = 0; i < pixels; i++)
new->data[i] = tex->data[i * 2];
break;
case tex_rgba:
bpp = 4;
case tex_rgb:
for (i = 0; i < pixels; i++)
new->data[i] = convert_color (tex->data + i * bpp);
break;
}
return new;
}
static inline void
convert_coord (byte *tc, int size)
{
@ -166,7 +113,7 @@ sw_iqm_load_textures (iqm_t *iqm)
dstring_copystr (str, iqm->text + iqm->meshes[i].material);
QFS_StripExtension (str->str, str->str);
if ((tex = LoadImage (va ("textures/%s", str->str))))
tex = sw->skins[i] = convert_tex (tex);
tex = sw->skins[i] = ConvertImage (tex, vid.basepal);
else
tex = sw->skins[i] = &null_texture;
for (j = 0; j < (int) iqm->meshes[i].num_triangles * 3; j++) {

View file

@ -104,7 +104,7 @@ load_image (const char *name)
switch (tex->format) {
case tex_palette:
for (i = 0, s = tex->data, d = image->data; i < pixels; i++) {
byte *v = tex->palette + *s++ * 3;
const byte *v = tex->palette + *s++ * 3;
*d++ = *v++;
*d++ = *v++;
*d++ = *v++;