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) if (mapcolor_preset == AM_PRESET_CRISPY)
{ {
byte *playpal = W_CacheLumpName("PLAYPAL", PU_CACHE); byte *playpal = W_CacheLumpName("PLAYPAL", PU_CACHE);
mapcolor_secr = I_GetPaletteIndex(playpal, 255, 0, 255); mapcolor_secr = I_GetNearestColor(playpal, 255, 0, 255);
mapcolor_revsecr = I_GetPaletteIndex(playpal, 119, 255, 111); 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 // 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; byte best;
int best_diff, diff; int best_diff, diff;

View File

@ -82,7 +82,7 @@ extern boolean correct_aspect_ratio;
extern boolean screenvisible; extern boolean screenvisible;
extern int gamma2; 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 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 r = *p++;
int g = *p++; int g = *p++;
int b = *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 // 0 is transparent, last is border color

View File

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

View File

@ -370,7 +370,7 @@ static void ST_DrawSolidBackground(int st_x)
b /= 2 * depth * (v1 - v0); b /= 2 * depth * (v1 - v0);
// [FG] tune down to half saturation (for empiric reasons) // [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); 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) 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)); MAKECOLOR(g), MAKECOLOR(b));
} }
} }

View File

@ -268,6 +268,82 @@ static void *DummyFlat(int lump, pu_tag tag)
return lumpcache[lump]; 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 typedef struct
{ {
spng_ctx *ctx; spng_ctx *ctx;
@ -340,8 +416,7 @@ static boolean DecodePNG(png_t *png)
if (ret) if (ret)
{ {
I_Printf(VB_ERROR, "DecodeImage: spng_get_ihdr %s\n", I_Printf(VB_ERROR, "DecodePNG: spng_get_ihdr %s\n", spng_strerror(ret));
spng_strerror(ret));
return false; return false;
} }
@ -368,7 +443,7 @@ static boolean DecodePNG(png_t *png)
if (ret) 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)); spng_strerror(ret));
return false; return false;
} }
@ -378,7 +453,7 @@ static boolean DecodePNG(png_t *png)
if (ret) if (ret)
{ {
I_Printf(VB_ERROR, "DecodeImage: spng_decode_image %s", I_Printf(VB_ERROR, "DecodePNG: spng_decode_image %s",
spng_strerror(ret)); spng_strerror(ret));
free(image); free(image);
return false; return false;
@ -391,6 +466,8 @@ static boolean DecodePNG(png_t *png)
int indexed_size = image_size / 3; int indexed_size = image_size / 3;
byte *indexed_image = malloc(indexed_size); byte *indexed_image = malloc(indexed_size);
uniform_quantizer_t q = {0};
byte *roller = image; byte *roller = image;
for (int i = 0; i < indexed_size; ++i) for (int i = 0; i < indexed_size; ++i)
@ -399,7 +476,31 @@ static boolean DecodePNG(png_t *png)
int g = *roller++; int g = *roller++;
int b = *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); free(image);
@ -412,6 +513,8 @@ static boolean DecodePNG(png_t *png)
int indexed_size = image_size / 4; int indexed_size = image_size / 4;
byte *indexed_image = malloc(indexed_size); byte *indexed_image = malloc(indexed_size);
uniform_quantizer_t q = {0};
byte *roller = image; byte *roller = image;
byte used_colors[256] = {0}; byte used_colors[256] = {0};
@ -423,22 +526,34 @@ static boolean DecodePNG(png_t *png)
int g = *roller++; int g = *roller++;
int b = *roller++; int b = *roller++;
int a = *roller++; int a = *roller++;
if (a < 255) if (a < 255)
{ {
has_alpha = true; has_alpha = true;
continue; continue;
} }
byte c = I_GetPaletteIndex(playpal, r, g, b); AddColor(&q, r, g, b);
used_colors[c] = 1;
indexed_image[i] = c;
} }
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) if (has_alpha)
{ {
int color_key = NO_COLOR_KEY;
for (int i = 0; i < 256; ++i) for (int i = 0; i < 256; ++i)
{ {
if (used_colors[i] == 0) if (used_colors[i] == 0)
@ -447,20 +562,24 @@ static boolean DecodePNG(png_t *png)
break; break;
} }
} }
if (color_key != NO_COLOR_KEY)
{
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; png->color_key = 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)
{
indexed_image[i] = color_key;
continue;
}
indexed_image[i] = translate[GetPaletteIndex(r, g, b)];
} }
free(image); free(image);
@ -475,7 +594,7 @@ static boolean DecodePNG(png_t *png)
if (ret) 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)); spng_strerror(ret));
return false; return false;
} }
@ -500,7 +619,7 @@ static boolean DecodePNG(png_t *png)
need_translation = true; need_translation = true;
translate[i] = translate[i] =
I_GetPaletteIndex(playpal, e->red, e->green, e->blue); I_GetNearestColor(playpal, e->red, e->green, e->blue);
} }
if (need_translation) if (need_translation)

View File

@ -285,5 +285,5 @@ byte V_Colorize(byte *playpal, int cr, byte source)
rgb.y *= 255.0; rgb.y *= 255.0;
rgb.z *= 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); cr_bright[i] = V_Colorize(playpal, CR_BRIGHT, (byte)i);
} }
v_lightest_color = I_GetPaletteIndex(playpal, 0xFF, 0xFF, 0xFF); v_lightest_color = I_GetNearestColor(playpal, 0xFF, 0xFF, 0xFF);
v_darkest_color = I_GetPaletteIndex(playpal, 0x00, 0x00, 0x00); v_darkest_color = I_GetNearestColor(playpal, 0x00, 0x00, 0x00);
byte *palsrc = playpal; byte *palsrc = playpal;
for (int i = 0; i < 256; ++i) 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" // formula is taken from dcolors.c preseving "Carmack's typo"
// https://doomwiki.org/wiki/Carmack%27s_typo // https://doomwiki.org/wiki/Carmack%27s_typo
int gray = (red * 0.299 + green * 0.587 + blue * 0.144) * 255; 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);
} }
} }