renders: support 24bit pcx images

Based on ffmpeg code and checked with
https://samples.ffmpeg.org/image-samples/pcx/
This commit is contained in:
Denis Pauk 2024-07-30 09:04:34 +03:00
parent f688ea599f
commit 8520285a10
6 changed files with 361 additions and 171 deletions

View File

@ -68,19 +68,19 @@ cinematics_t cin;
static void
SCR_LoadPCX(char *filename, byte **pic, byte **palette, int *width, int *height,
int *bitesPerPixel)
int *bitsPerPixel)
{
byte *data, *palette_in;
*pic = NULL;
*palette = NULL;
SCR_LoadImageWithPalette(filename, &data, &palette_in, width, height, bitesPerPixel);
SCR_LoadImageWithPalette(filename, &data, &palette_in, width, height, bitsPerPixel);
if (data)
{
*pic = Z_Malloc((*width) * (*height) * (*bitesPerPixel) / 8);
memcpy(*pic, data, (*height) * (*width) * (*bitesPerPixel) / 8);
*pic = Z_Malloc((*width) * (*height) * (*bitsPerPixel) / 8);
memcpy(*pic, data, (*height) * (*width) * (*bitsPerPixel) / 8);
free(data);
}
else
@ -567,7 +567,7 @@ SCR_DrawCinematic(void)
static byte *
SCR_LoadHiColor(const char* namewe, const char *ext, int *width, int *height,
int *bitesPerPixel)
int *bitsPerPixel)
{
byte *pic, *data = NULL, *palette = NULL;
char filename[256];
@ -577,7 +577,7 @@ SCR_LoadHiColor(const char* namewe, const char *ext, int *width, int *height,
Q_strlcat(filename, ext, sizeof(filename));
SCR_LoadImageWithPalette(filename, &data, &palette,
width, height, bitesPerPixel);
width, height, bitsPerPixel);
if (data == NULL)
{
return NULL;
@ -589,8 +589,8 @@ SCR_LoadHiColor(const char* namewe, const char *ext, int *width, int *height,
free(palette);
}
pic = Z_Malloc(cin.height * cin.width * (*bitesPerPixel) / 8);
memcpy(pic, data, cin.height * cin.width * (*bitesPerPixel) / 8);
pic = Z_Malloc(cin.height * cin.width * (*bitsPerPixel) / 8);
memcpy(pic, data, cin.height * cin.width * (*bitsPerPixel) / 8);
free(data);
return pic;

View File

@ -95,7 +95,7 @@ PCX_RLE_Decode(byte *pix, byte *pix_max, const byte *raw, const byte *raw_max,
static void
PCX_Decode(const char *name, const byte *raw, int len, byte **pic, byte **palette,
int *width, int *height, int *bitesPerPixel)
int *width, int *height, int *bitsPerPixel)
{
const pcx_t *pcx;
int full_size;
@ -105,7 +105,7 @@ PCX_Decode(const char *name, const byte *raw, int len, byte **pic, byte **palett
const byte *data;
*pic = NULL;
*bitesPerPixel = 8;
*bitsPerPixel = 8;
if (palette)
{
@ -144,7 +144,7 @@ PCX_Decode(const char *name, const byte *raw, int len, byte **pic, byte **palett
if (pcx->color_planes == 3 && pcx->bits_per_pixel == 8)
{
full_size *= 4;
*bitesPerPixel = 32;
*bitsPerPixel = 32;
}
out = malloc(full_size);
@ -366,7 +366,7 @@ PCX_Decode(const char *name, const byte *raw, int len, byte **pic, byte **palett
void
SCR_LoadImageWithPalette(char *filename, byte **pic, byte **palette,
int *width, int *height, int *bitesPerPixel)
int *width, int *height, int *bitsPerPixel)
{
const char* ext;
int len, ident;
@ -392,11 +392,11 @@ SCR_LoadImageWithPalette(char *filename, byte **pic, byte **palette,
ident = LittleShort(*((short*)raw));
if (!strcmp(ext, "pcx") && (ident == PCX_IDENT))
{
PCX_Decode(filename, raw, len, pic, palette, width, height, bitesPerPixel);
PCX_Decode(filename, raw, len, pic, palette, width, height, bitsPerPixel);
}
else
{
int sourcebitesPerPixel = 0;
int sourcebitsPerPixel = 0;
/* other formats does not have palette directly */
if (palette)
@ -405,7 +405,7 @@ SCR_LoadImageWithPalette(char *filename, byte **pic, byte **palette,
}
*pic = stbi_load_from_memory(raw, len, width, height,
&sourcebitesPerPixel, STBI_rgb_alpha);
&sourcebitsPerPixel, STBI_rgb_alpha);
if (*pic == NULL)
{
@ -413,7 +413,7 @@ SCR_LoadImageWithPalette(char *filename, byte **pic, byte **palette,
__func__, filename, stbi_failure_reason());
}
*bitesPerPixel = 32;
*bitsPerPixel = 32;
}
FS_FreeFile(raw);

View File

@ -60,7 +60,7 @@ void SCR_AddDirtyPoint(int x, int y);
void SCR_DirtyScreen(void);
void SCR_LoadImageWithPalette(char *filename, byte **pic, byte **palette,
int *width, int *height, int *bitesPerPixel);
int *width, int *height, int *bitsPerPixel);
void SCR_PlayCinematic(char *name);
qboolean SCR_DrawCinematic(void);
void SCR_RunCinematic(void);

View File

@ -116,18 +116,338 @@ fixQuitScreen(byte* px)
}
}
void
LoadPCX(const char *origname, byte **pic, byte **palette, int *width, int *height)
static const byte *
PCX_RLE_Decode(byte *pix, byte *pix_max, const byte *raw, const byte *raw_max,
int bytes_per_line, qboolean *image_issues)
{
byte *raw;
pcx_t *pcx;
int x, y;
int len, full_size;
int pcx_width, pcx_height;
int x;
for (x = 0; x < bytes_per_line; )
{
int runLength;
byte dataByte;
if (raw >= raw_max)
{
// no place for read
*image_issues = true;
return raw;
}
dataByte = *raw++;
if ((dataByte & 0xC0) == 0xC0)
{
runLength = dataByte & 0x3F;
if (raw >= raw_max)
{
// no place for read
*image_issues = true;
return raw;
}
dataByte = *raw++;
}
else
{
runLength = 1;
}
while (runLength-- > 0)
{
if (pix_max <= (pix + x))
{
// no place for write
*image_issues = true;
return raw;
}
else
{
pix[x++] = dataByte;
}
}
}
return raw;
}
static void
PCX_Decode(const char *name, const byte *raw, int len, byte **pic, byte **palette,
int *width, int *height, int *bitsPerPixel)
{
const pcx_t *pcx;
int full_size;
int pcx_width, pcx_height, bytes_per_line;
qboolean image_issues = false;
int dataByte, runLength;
byte *out, *pix;
const byte *data;
*pic = NULL;
*bitsPerPixel = 8;
if (palette)
{
*palette = NULL;
}
if (len < sizeof(pcx_t))
{
return;
}
/* parse the PCX file */
pcx = (const pcx_t *)raw;
data = &pcx->data;
bytes_per_line = LittleShort(pcx->bytes_per_line);
pcx_width = LittleShort(pcx->xmax) - LittleShort(pcx->xmin);
pcx_height = LittleShort(pcx->ymax) - LittleShort(pcx->ymin);
if ((pcx->manufacturer != 0x0a) ||
(pcx->version != 5) ||
(pcx->encoding != 1) ||
(pcx_width <= 0) ||
(pcx_height <= 0) ||
(bytes_per_line <= 0) ||
(pcx->color_planes <= 0) ||
(pcx->bits_per_pixel <= 0))
{
R_Printf(PRINT_ALL, "%s: Bad pcx file %s: version: %d:%d, encoding: %d\n",
__func__, name, pcx->manufacturer, pcx->version, pcx->encoding);
return;
}
full_size = (pcx_height + 1) * (pcx_width + 1);
if (pcx->color_planes == 3 && pcx->bits_per_pixel == 8)
{
full_size *= 4;
*bitsPerPixel = 32;
}
out = malloc(full_size);
if (!out)
{
R_Printf(PRINT_ALL, "%s: Can't allocate for %s\n", __func__, name);
return;
}
*pic = out;
pix = out;
if (width)
{
*width = pcx_width + 1;
}
if (height)
{
*height = pcx_height + 1;
}
if ((pcx->color_planes == 3 || pcx->color_planes == 4)
&& pcx->bits_per_pixel == 8)
{
int x, y, linesize;
byte *line;
if (bytes_per_line <= pcx_width)
{
image_issues = true;
}
/* clean image alpha */
memset(pix, 255, full_size);
linesize = Q_max(bytes_per_line, pcx_width + 1) * pcx->color_planes;
line = malloc(linesize);
for (y = 0; y <= pcx_height; y++, pix += (pcx_width + 1) * 4)
{
data = PCX_RLE_Decode(line, line + linesize,
data, (byte *)pcx + len,
bytes_per_line * pcx->color_planes, &image_issues);
for (x = 0; x <= pcx_width; x++) {
int j;
for (j = 0; j < pcx->color_planes; j++)
{
pix[4 * x + j] = line[x + bytes_per_line * j];
}
}
}
free(line);
}
else if (pcx->bits_per_pixel == 1)
{
byte *line;
int y;
if (palette)
{
*palette = malloc(768);
if (!(*palette))
{
R_Printf(PRINT_ALL, "%s: Can't allocate for %s\n", __func__, name);
free(out);
return;
}
memcpy(*palette, pcx->palette, sizeof(pcx->palette));
}
line = malloc(bytes_per_line * pcx->color_planes);
for (y = 0; y <= pcx_height; y++, pix += pcx_width + 1)
{
int x;
data = PCX_RLE_Decode(line, line + bytes_per_line * pcx->color_planes,
data, (byte *)pcx + len,
bytes_per_line * pcx->color_planes, &image_issues);
for (x = 0; x <= pcx_width; x++)
{
int m, i, v;
m = 0x80 >> (x & 7);
v = 0;
for (i = pcx->color_planes - 1; i >= 0; i--) {
v <<= 1;
v += (line[i * bytes_per_line + (x >> 3)] & m) ? 1 : 0;
}
pix[x] = v;
}
}
free(line);
}
else if (pcx->color_planes == 1 && pcx->bits_per_pixel == 8)
{
int y, linesize;
byte *line;
if (palette)
{
*palette = malloc(768);
if (!(*palette))
{
R_Printf(PRINT_ALL, "%s: Can't allocate for %s\n", __func__, name);
free(out);
return;
}
if ((len > 768) && (((byte *)pcx)[len - 769] == 0x0C))
{
memcpy(*palette, (byte *)pcx + len - 768, 768);
}
else
{
image_issues = true;
}
}
if (bytes_per_line <= pcx_width)
{
image_issues = true;
}
linesize = Q_max(bytes_per_line, pcx_width + 1);
line = malloc(linesize);
for (y = 0; y <= pcx_height; y++, pix += pcx_width + 1)
{
data = PCX_RLE_Decode(line, line + linesize,
data, (byte *)pcx + len,
bytes_per_line, &image_issues);
/* copy only visible part */
memcpy(pix, line, pcx_width + 1);
}
free(line);
}
else if (pcx->color_planes == 1 &&
(pcx->bits_per_pixel == 2 || pcx->bits_per_pixel == 4))
{
int y;
byte *line;
if (palette)
{
*palette = malloc(768);
if (!(*palette))
{
R_Printf(PRINT_ALL, "%s: Can't allocate for %s\n", __func__, name);
free(out);
return;
}
memcpy(*palette, pcx->palette, sizeof(pcx->palette));
}
line = malloc(bytes_per_line);
for (y = 0; y <= pcx_height; y++, pix += pcx_width + 1)
{
int x, mask, div;
data = PCX_RLE_Decode(line, line + bytes_per_line,
data, (byte *)pcx + len,
bytes_per_line, &image_issues);
mask = (1 << pcx->bits_per_pixel) - 1;
div = 8 / pcx->bits_per_pixel;
for (x = 0; x <= pcx_width; x++)
{
unsigned v, shift;
v = line[x / div] & 0xFF;
/* for 2 bits:
* 0 -> 6
* 1 -> 4
* 3 -> 2
* 4 -> 0
*/
shift = pcx->bits_per_pixel * ((div - 1) - x % div);
pix[x] = (v >> shift) & mask;
}
}
free(line);
}
else
{
R_Printf(PRINT_ALL, "%s: Bad pcx file %s: planes: %d, bits: %d\n",
__func__, name, pcx->color_planes, pcx->bits_per_pixel);
free(*pic);
*pic = NULL;
}
if (data - (byte *)pcx > len)
{
R_Printf(PRINT_DEVELOPER, "%s: %s file was malformed\n",
__func__, name);
free(*pic);
*pic = NULL;
}
if (image_issues)
{
R_Printf(PRINT_ALL, "%s: %s file has possible size issues.\n",
__func__, name);
}
}
void
LoadPCX(const char *origname, byte **pic, byte **palette, int *width, int *height,
int *bitsPerPixel)
{
char filename[256];
byte *raw;
int len;
FixFileExt(origname, "pcx", filename, sizeof(filename));
@ -140,156 +460,25 @@ LoadPCX(const char *origname, byte **pic, byte **palette, int *width, int *heigh
/* load the file */
len = ri.FS_LoadFile(filename, (void **)&raw);
if (!raw || len < sizeof(pcx_t))
if (!raw)
{
R_Printf(PRINT_DEVELOPER, "Bad pcx file %s\n", filename);
/* no such file */
return;
}
/* parse the PCX file */
pcx = (pcx_t *)raw;
PCX_Decode(filename, raw, len, pic, palette, width, height, bitsPerPixel);
pcx->xmin = LittleShort(pcx->xmin);
pcx->ymin = LittleShort(pcx->ymin);
pcx->xmax = LittleShort(pcx->xmax);
pcx->ymax = LittleShort(pcx->ymax);
pcx->hres = LittleShort(pcx->hres);
pcx->vres = LittleShort(pcx->vres);
pcx->bytes_per_line = LittleShort(pcx->bytes_per_line);
pcx->palette_type = LittleShort(pcx->palette_type);
raw = &pcx->data;
pcx_width = pcx->xmax - pcx->xmin;
pcx_height = pcx->ymax - pcx->ymin;
if ((pcx->manufacturer != 0x0a) || (pcx->version != 5) ||
(pcx->encoding != 1) || (pcx->bits_per_pixel != 8) ||
(pcx_width >= 4096) || (pcx_height >= 4096))
{
R_Printf(PRINT_ALL, "Bad pcx file %s\n", filename);
ri.FS_FreeFile(pcx);
return;
}
if (pcx->bytes_per_line <= pcx_width)
{
pcx->bytes_per_line = pcx_width + 1;
image_issues = true;
}
full_size = (pcx_height + 1) * (pcx_width + 1);
out = malloc(full_size);
if (!out)
{
R_Printf(PRINT_ALL, "Can't allocate\n");
ri.FS_FreeFile(pcx);
return;
}
*pic = out;
pix = out;
if (palette)
{
*palette = malloc(768);
if (!(*palette))
{
R_Printf(PRINT_ALL, "Can't allocate\n");
free(out);
ri.FS_FreeFile(pcx);
return;
}
if (len > 768)
{
memcpy(*palette, (byte *)pcx + len - 768, 768);
}
else
{
image_issues = true;
}
}
if (width)
{
*width = pcx_width + 1;
}
if (height)
{
*height = pcx_height + 1;
}
for (y = 0; y <= pcx_height; y++, pix += pcx_width + 1)
{
for (x = 0; x < pcx->bytes_per_line; )
{
if (raw - (byte *)pcx > len)
{
// no place for read
image_issues = true;
x = pcx_width;
break;
}
dataByte = *raw++;
if ((dataByte & 0xC0) == 0xC0)
{
runLength = dataByte & 0x3F;
if (raw - (byte *)pcx > len)
{
// no place for read
image_issues = true;
x = pcx_width;
break;
}
dataByte = *raw++;
}
else
{
runLength = 1;
}
while (runLength-- > 0)
{
if ((*pic + full_size) <= (pix + x))
{
// no place for write
image_issues = true;
x += runLength;
runLength = 0;
}
else
{
pix[x++] = dataByte;
}
}
}
}
if (raw - (byte *)pcx > len)
{
R_Printf(PRINT_DEVELOPER, "PCX file %s was malformed", filename);
free(*pic);
*pic = NULL;
}
else if(pcx_width == 319 && pcx_height == 239
if(*pic && *bitsPerPixel == 8 && width && height
&& *width == 319 && *height == 239
&& Q_strcasecmp(filename, "pics/quit.pcx") == 0
&& Com_BlockChecksum(pcx, len) == 3329419434u)
&& Com_BlockChecksum(raw, len) == 3329419434u)
{
// it's the quit screen, and the baseq2 one (identified by checksum)
// so fix it
fixQuitScreen(*pic);
}
if (image_issues)
{
R_Printf(PRINT_ALL, "PCX file %s has possible size issues.\n", filename);
}
ri.FS_FreeFile(pcx);
ri.FS_FreeFile(raw);
}
void
@ -327,11 +516,11 @@ void
GetPCXPalette (byte **colormap, unsigned *d_8to24table)
{
byte *pal;
int i;
int i, bitsPerPixel;
/* get the palette and colormap */
LoadPCX ("pics/colormap.pcx", colormap, &pal, NULL, NULL);
if (!*colormap || !pal)
LoadPCX ("pics/colormap.pcx", colormap, &pal, NULL, NULL, &bitsPerPixel);
if (!*colormap || !pal || bitsPerPixel != 8)
{
ri.Sys_Error (ERR_FATAL, "%s: Couldn't load pics/colormap.pcx",
__func__);

View File

@ -509,16 +509,16 @@ R_LoadImage(const char *name, const char* namewe, const char *ext, imagetype_t t
{
byte *pic = NULL;
byte *palette = NULL;
int width = 0, height = 0;
int width = 0, height = 0, bitsPerPixel = 8;
LoadPCX (namewe, &pic, &palette, &width, &height);
LoadPCX (namewe, &pic, &palette, &width, &height, &bitsPerPixel);
if (!pic)
return NULL;
image = load_image(name, pic,
width, width,
height, height,
width * height, type, 8);
width * height, type, bitsPerPixel);
if (palette)
{

View File

@ -85,7 +85,8 @@ extern struct image_s* LoadM8(const char *origname, imagetype_t type, loadimage_
extern struct image_s* LoadM32(const char *origname, imagetype_t type, loadimage_t load_image);
extern void FixFileExt(const char *origname, const char *ext, char *filename, size_t size);
extern void GetPCXPalette(byte **colormap, unsigned *d_8to24table);
extern void LoadPCX(const char *origname, byte **pic, byte **palette, int *width, int *height);
extern void LoadPCX(const char *origname, byte **pic, byte **palette, int *width, int *height,
int *bitsPerPixel);
extern void GetPCXInfo(const char *origname, int *width, int *height);
extern void GetWalInfo(const char *name, int *width, int *height);
extern void GetM8Info(const char *name, int *width, int *height);