uniform color quantization (#1868)

* rename I_GetPaletteIndex->I_GetNearestColor
This commit is contained in:
Roman Fomin 2024-08-27 09:02:05 +07:00 committed by GitHub
parent af359bac43
commit b5070cb54f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 155 additions and 36 deletions

View File

@ -2369,8 +2369,8 @@ void AM_ColorPreset(void)
if (mapcolor_preset == AM_PRESET_CRISPY)
{
byte *playpal = W_CacheLumpName("PLAYPAL", PU_CACHE);
mapcolor_secr = I_GetPaletteIndex(playpal, 255, 0, 255);
mapcolor_revsecr = I_GetPaletteIndex(playpal, 119, 255, 111);
mapcolor_secr = I_GetNearestColor(playpal, 255, 0, 255);
mapcolor_revsecr = I_GetNearestColor(playpal, 119, 255, 111);
}
}

View File

@ -1025,7 +1025,7 @@ void I_SetPalette(byte *palette)
// Taken from Chocolate Doom chocolate-doom/src/i_video.c:L841-867
byte I_GetPaletteIndex(byte *palette, int r, int g, int b)
byte I_GetNearestColor(byte *palette, int r, int g, int b)
{
byte best;
int best_diff, diff;

View File

@ -82,7 +82,7 @@ extern boolean correct_aspect_ratio;
extern boolean screenvisible;
extern int gamma2;
byte I_GetPaletteIndex(byte *palette, int r, int g, int b);
byte I_GetNearestColor(byte *palette, int r, int g, int b);
boolean I_WritePNGfile(char *filename); // [FG] screenshots in PNG format

View File

@ -111,7 +111,7 @@ boolean MN_LoadFon2(const byte *gfx_data, int size)
int r = *p++;
int g = *p++;
int b = *p++;
translate[i] = I_GetPaletteIndex(playpal, r, g, b);
translate[i] = I_GetNearestColor(playpal, r, g, b);
}
// 0 is transparent, last is border color

View File

@ -111,7 +111,7 @@ static byte R_SkyBlendColor(int tex)
b = colors[width/3].b;
Z_Free(colors);
return I_GetPaletteIndex(pal, r, g, b);
return I_GetNearestColor(pal, r, g, b);
}
typedef struct skycolor_s

View File

@ -370,7 +370,7 @@ static void ST_DrawSolidBackground(int st_x)
b /= 2 * depth * (v1 - v0);
// [FG] tune down to half saturation (for empiric reasons)
col = I_GetPaletteIndex(pal, r/2, g/2, b/2);
col = I_GetNearestColor(pal, r/2, g/2, b/2);
V_FillRect(0, v0, video.unscaledw, v1 - v0, col);
}

View File

@ -71,7 +71,7 @@ void V_InitFlexTranTable(void)
{
for (b = 0; b < 32; ++b)
{
RGB32k[r][g][b] = I_GetPaletteIndex(palette, MAKECOLOR(r),
RGB32k[r][g][b] = I_GetNearestColor(palette, MAKECOLOR(r),
MAKECOLOR(g), MAKECOLOR(b));
}
}

View File

@ -268,6 +268,82 @@ static void *DummyFlat(int lump, pu_tag tag)
return lumpcache[lump];
}
// Uniform Color Quantization
//
// Each color component axis (red, green and blue) is divided into a few fixed
// segments (8-8-4 in 256 colors). Each found color is placed into a
// corresponding segment slot. After all the colors are added, an average
// color is calculated for each slot. Those are the colors of the palette.
typedef struct
{
int value;
int pixel_count;
} color_slot_t;
static void AddValue(color_slot_t *s, int component)
{
s->value += component;
s->pixel_count++;
}
static int GetAverage(color_slot_t *s)
{
int result = 0;
if (s->pixel_count > 0)
{
result = (s->pixel_count == 1) ? s->value : (s->value / s->pixel_count);
}
return result;
}
typedef struct
{
color_slot_t red_slots[8];
color_slot_t green_slots[8];
color_slot_t blue_slots[4];
byte palette[768];
} uniform_quantizer_t;
static void AddColor(uniform_quantizer_t *q, int r, int g, int b)
{
int red_index = r >> 5;
int green_index = g >> 5;
int blue_index = b >> 6;
AddValue(&q->red_slots[red_index], r);
AddValue(&q->green_slots[green_index], g);
AddValue(&q->blue_slots[blue_index], b);
}
static void GetPalette(uniform_quantizer_t *q)
{
byte *roller = q->palette;
for (int rs = 0; rs < arrlen(q->red_slots); ++rs)
{
for (int gs = 0; gs < arrlen(q->green_slots); ++gs)
{
for (int bs = 0; bs < arrlen(q->blue_slots); ++bs)
{
*roller++ = GetAverage(&q->red_slots[rs]);
*roller++ = GetAverage(&q->green_slots[gs]);
*roller++ = GetAverage(&q->blue_slots[bs]);
}
}
}
}
static int GetPaletteIndex(int r, int g, int b)
{
int red_index = r >> 5;
int green_index = g >> 5;
int blue_index = b >> 6;
return (red_index << 5) + (green_index << 2) + blue_index;
}
typedef struct
{
spng_ctx *ctx;
@ -340,8 +416,7 @@ static boolean DecodePNG(png_t *png)
if (ret)
{
I_Printf(VB_ERROR, "DecodeImage: spng_get_ihdr %s\n",
spng_strerror(ret));
I_Printf(VB_ERROR, "DecodePNG: spng_get_ihdr %s\n", spng_strerror(ret));
return false;
}
@ -368,7 +443,7 @@ static boolean DecodePNG(png_t *png)
if (ret)
{
I_Printf(VB_ERROR, "DecodeImage: spng_decoded_image_size %s",
I_Printf(VB_ERROR, "DecodePNG: spng_decoded_image_size %s",
spng_strerror(ret));
return false;
}
@ -378,7 +453,7 @@ static boolean DecodePNG(png_t *png)
if (ret)
{
I_Printf(VB_ERROR, "DecodeImage: spng_decode_image %s",
I_Printf(VB_ERROR, "DecodePNG: spng_decode_image %s",
spng_strerror(ret));
free(image);
return false;
@ -391,6 +466,8 @@ static boolean DecodePNG(png_t *png)
int indexed_size = image_size / 3;
byte *indexed_image = malloc(indexed_size);
uniform_quantizer_t q = {0};
byte *roller = image;
for (int i = 0; i < indexed_size; ++i)
@ -399,7 +476,31 @@ static boolean DecodePNG(png_t *png)
int g = *roller++;
int b = *roller++;
indexed_image[i] = I_GetPaletteIndex(playpal, r, g, b);
AddColor(&q, r, g, b);
}
GetPalette(&q);
byte translate[256];
byte *palette = q.palette;
for (int i = 0; i < 256; ++i)
{
int r = *palette++;
int g = *palette++;
int b = *palette++;
translate[i] = I_GetNearestColor(playpal, r, g, b);
}
roller = image;
for (int i = 0; i < indexed_size; ++i)
{
int r = *roller++;
int g = *roller++;
int b = *roller++;
indexed_image[i] = translate[GetPaletteIndex(r, g, b)];
}
free(image);
@ -412,6 +513,8 @@ static boolean DecodePNG(png_t *png)
int indexed_size = image_size / 4;
byte *indexed_image = malloc(indexed_size);
uniform_quantizer_t q = {0};
byte *roller = image;
byte used_colors[256] = {0};
@ -423,22 +526,34 @@ static boolean DecodePNG(png_t *png)
int g = *roller++;
int b = *roller++;
int a = *roller++;
if (a < 255)
{
has_alpha = true;
continue;
}
byte c = I_GetPaletteIndex(playpal, r, g, b);
used_colors[c] = 1;
indexed_image[i] = c;
AddColor(&q, r, g, b);
}
GetPalette(&q);
byte translate[256];
byte *palette = q.palette;
for (int i = 0; i < 256; ++i)
{
int r = *palette++;
int g = *palette++;
int b = *palette++;
byte c = I_GetNearestColor(playpal, r, g, b);
used_colors[c] = 1;
translate[i] = c;
}
int color_key = NO_COLOR_KEY;
if (has_alpha)
{
int color_key = NO_COLOR_KEY;
for (int i = 0; i < 256; ++i)
{
if (used_colors[i] == 0)
@ -447,20 +562,24 @@ static boolean DecodePNG(png_t *png)
break;
}
}
png->color_key = color_key;
}
if (color_key != NO_COLOR_KEY)
roller = image;
for (int i = 0; i < indexed_size; ++i)
{
int r = *roller++;
int g = *roller++;
int b = *roller++;
int a = *roller++;
if (a < 255)
{
roller = image;
for (int i = 0; i < indexed_size; ++i)
{
roller += 3;
if (*roller++ < 255)
{
indexed_image[i] = color_key;
}
}
png->color_key = color_key;
indexed_image[i] = color_key;
continue;
}
indexed_image[i] = translate[GetPaletteIndex(r, g, b)];
}
free(image);
@ -475,7 +594,7 @@ static boolean DecodePNG(png_t *png)
if (ret)
{
I_Printf(VB_ERROR, "DecodeImage: spng_get_plte %s\n",
I_Printf(VB_ERROR, "DecodePNG: spng_get_plte %s\n",
spng_strerror(ret));
return false;
}
@ -500,7 +619,7 @@ static boolean DecodePNG(png_t *png)
need_translation = true;
translate[i] =
I_GetPaletteIndex(playpal, e->red, e->green, e->blue);
I_GetNearestColor(playpal, e->red, e->green, e->blue);
}
if (need_translation)

View File

@ -285,5 +285,5 @@ byte V_Colorize(byte *playpal, int cr, byte source)
rgb.y *= 255.0;
rgb.z *= 255.0;
return I_GetPaletteIndex(playpal, (int)rgb.x, (int)rgb.y, (int)rgb.z);
return I_GetNearestColor(playpal, (int)rgb.x, (int)rgb.y, (int)rgb.z);
}

View File

@ -200,8 +200,8 @@ void V_InitColorTranslation(void)
cr_bright[i] = V_Colorize(playpal, CR_BRIGHT, (byte)i);
}
v_lightest_color = I_GetPaletteIndex(playpal, 0xFF, 0xFF, 0xFF);
v_darkest_color = I_GetPaletteIndex(playpal, 0x00, 0x00, 0x00);
v_lightest_color = I_GetNearestColor(playpal, 0xFF, 0xFF, 0xFF);
v_darkest_color = I_GetNearestColor(playpal, 0x00, 0x00, 0x00);
byte *palsrc = playpal;
for (int i = 0; i < 256; ++i)
@ -213,7 +213,7 @@ void V_InitColorTranslation(void)
// formula is taken from dcolors.c preseving "Carmack's typo"
// https://doomwiki.org/wiki/Carmack%27s_typo
int gray = (red * 0.299 + green * 0.587 + blue * 0.144) * 255;
invul_gray[i] = I_GetPaletteIndex(playpal, gray, gray, gray);
invul_gray[i] = I_GetNearestColor(playpal, gray, gray, gray);
}
}