add documentation for issue 4148

Per Claude:

The key things the new comments add that the old ones were missing:

1. Palette RGB colors are completely ignored — only the index value is used as alpha. This is the non-obvious invariant that @notimaginative described.
2. Exact mapping table — makes the * 18 / * 17 math immediately readable, and clarifies the ANI vs PCX difference (> 15 clamps to opaque in ANI but transparent in PCX).
3. The broken-palette consequence — explicitly states that a reversed palette causes a white square, linking to #4148 so future readers understand the context.
This commit is contained in:
Goober5000 committed 2026-04-30 00:35:19 -04:00
1 parent ad4424984a
commit e2707549f4
2 files changed
+28 -15

No files matched your search

+18 -10
View File
@@ -142,11 +142,15 @@ int unpack_pixel(anim_instance *ai, ubyte *data, ubyte pix, int aabitmap, int bp
bit_16 = (ushort)pix;
break;
case 8:
// 8 bit-per-pixel aa bitmaps are a bit special since they only use a palette index value in the range [0, 15]. These
// palette indexes must be remapped to alpha values between [0, 255] which is what graphics code expects. Palette
// range [0, 14] is a gradient from black to white, and palette index 15 is a special color which indicates the background
// area of a HUD gauge. Retail code uses the final alpha value for index 1 for this special index to give gauges a dark
// transparent background.
// 8-bit-per-pixel HUD gauge ANI aabitmaps use the palette index directly as an alpha value —
// the actual RGB colors stored in the palette are completely ignored. The valid range is [0, 15]:
// [0]: fully transparent (alpha 0)
// [1-14]: black-to-white gradient (alpha = index * 18)
// [15]: special HUD background (alpha 18, matching index 1, for a dark transparent tint)
// [>15]: clamped to fully opaque (alpha 255)
// Because only the index value matters, the palette in the source file must be ordered so that
// index 0 is the transparent end and index 14 is the opaque end. A reversed palette causes the
// gauge to render as a solid white square (see GitHub issue #4148).
if (pix > 15) {
bit_8 = 255;
}
@@ -240,11 +244,15 @@ int unpack_pixel_count(anim_instance *ai, ubyte *data, ubyte pix, int count = 0,
bit_16 = (ushort)pix;
break;
case 8 :
// 8 bit-per-pixel aa bitmaps are a bit special since they only use a palette index value in the range [0, 15]. These
// palette indexes must be remapped to alpha values between [0, 255] which is what graphics code expects. Palette
// range [0, 14] is a gradient from black to white, and palette index 15 is a special color which indicates the background
// area of a HUD gauge. Retail code uses the final alpha value for index 1 for this special index to give gauges a dark
// transparent background.
// 8-bit-per-pixel HUD gauge ANI aabitmaps use the palette index directly as an alpha value —
// the actual RGB colors stored in the palette are completely ignored. The valid range is [0, 15]:
// [0]: fully transparent (alpha 0)
// [1-14]: black-to-white gradient (alpha = index * 18)
// [15]: special HUD background (alpha 18, matching index 1, for a dark transparent tint)
// [>15]: clamped to fully opaque (alpha 255)
// Because only the index value matters, the palette in the source file must be ordered so that
// index 0 is the transparent end and index 14 is the opaque end. A reversed palette causes the
// gauge to render as a solid white square (see GitHub issue #4148).
if (pix > 15) {
bit_8 = 255;
}
+10 -5
View File
@@ -312,11 +312,16 @@ int pcx_read_bitmap( const char * real_filename, ubyte *org_data, ubyte * /*pal*
if ( byte_size == 1 ) {
auto pixel_val = data;
if (!mask_bitmap) {
// 8 bit-per-pixel aa bitmaps are a bit special since they only use values in the range [0, 15] where 15 wraps
// around back to 0. Since the rest of the code expects the value to be in the range [0, 255] the pixel value
// needs to be adjusted here. By multiplying the value with 17 the original range [0, 15] is mapped to [0, 255]
// This only applies to bitmaps that are not used as masks since mask bitmaps use higher values
// to indicate their mask area
// 8-bit-per-pixel HUD gauge PCX aabitmaps use the pixel value directly as an alpha level —
// the actual RGB colors in the palette are completely ignored. The valid range is [0, 15]:
// [0]: fully transparent (alpha 0)
// [1-14]: black-to-white gradient (alpha = value * 17)
// [15]: special HUD background (alpha 17, matching index 1, for a dark transparent tint)
// [>15]: clamped to fully transparent (alpha 0)
// Because only the pixel value matters, the palette in the source file must be ordered so that
// index 0 is the transparent end and index 14 is the opaque end. A reversed palette causes the
// gauge to render as a solid white square (see GitHub issue #4148).
// Mask bitmaps skip this block because they use the full value range for the mask area.
if (data > 15) {
pixel_val = 0;
} else if (data == 15) {