cin: support 24bit pcx images

This commit is contained in:
Denis Pauk 2024-07-30 08:23:05 +03:00
parent 87c0029b2b
commit f688ea599f
6 changed files with 479 additions and 139 deletions

View file

@ -423,6 +423,7 @@ set(Client-Source
${CLIENT_SRC_DIR}/cl_effects.c
${CLIENT_SRC_DIR}/cl_entities.c
${CLIENT_SRC_DIR}/cl_input.c
${CLIENT_SRC_DIR}/cl_image.c
${CLIENT_SRC_DIR}/cl_inventory.c
${CLIENT_SRC_DIR}/cl_keyboard.c
${CLIENT_SRC_DIR}/cl_lights.c

View file

@ -856,6 +856,7 @@ GAME_OBJS_ = \
CLIENT_OBJS_ := \
src/backends/generic/misc.o \
src/client/cl_cin.o \
src/client/cl_image.o \
src/client/cl_console.o \
src/client/cl_download.o \
src/client/cl_effects.o \

View file

@ -30,20 +30,6 @@
#include "header/client.h"
#include "input/header/input.h"
// don't need HDR stuff
#define STBI_NO_LINEAR
#define STBI_NO_HDR
// make sure STB_image uses standard malloc(), as we'll use standard free() to deallocate
#define STBI_MALLOC(sz) malloc(sz)
#define STBI_REALLOC(p,sz) realloc(p,sz)
#define STBI_FREE(p) free(p)
// Switch of the thread local stuff. Breaks mingw under Windows.
#define STBI_NO_THREAD_LOCALS
// include implementation part of stb_image into this file
#define STB_IMAGE_IMPLEMENTATION
#include "refresh/files/stb_image.h"
extern cvar_t *vid_renderer;
cvar_t *cin_force43;
@ -80,109 +66,34 @@ typedef struct
cinematics_t cin;
void
SCR_LoadPCX(char *filename, byte **pic, byte **palette, int *width, int *height)
static void
SCR_LoadPCX(char *filename, byte **pic, byte **palette, int *width, int *height,
int *bitesPerPixel)
{
byte *raw;
pcx_t *pcx;
int x, y, bpl;
int len, full_size;
int dataByte, runLength;
byte *out, *pix;
byte *data, *palette_in;
*pic = NULL;
*palette = NULL;
/* load the file */
len = FS_LoadFile(filename, (void **)&raw);
SCR_LoadImageWithPalette(filename, &data, &palette_in, width, height, bitesPerPixel);
if (!raw || len < sizeof(pcx_t))
if (data)
{
return;
*pic = Z_Malloc((*width) * (*height) * (*bitesPerPixel) / 8);
memcpy(*pic, data, (*height) * (*width) * (*bitesPerPixel) / 8);
free(data);
}
/* parse the PCX file */
pcx = (pcx_t *)raw;
raw = &pcx->data;
if ((pcx->manufacturer != 0x0a) ||
(pcx->version != 5) ||
(pcx->encoding != 1) ||
(pcx->bits_per_pixel != 8) ||
(pcx->xmax >= 640) ||
(pcx->ymax >= 480))
else
{
Com_Printf("Bad pcx file %s\n", filename);
return;
}
bpl = LittleShort(pcx->bytes_per_line);
if (bpl <= pcx->xmax)
{
bpl = pcx->xmax + 1;
}
full_size = (pcx->ymax + 1) * (pcx->xmax + 1);
out = Z_Malloc(full_size);
*pic = out;
pix = out;
if (palette)
if (palette_in)
{
*palette = Z_Malloc(768);
memcpy(*palette, (byte *)pcx + len - 768, 768);
memcpy(*palette, palette_in, 768);
free(palette_in);
}
if (width)
{
*width = pcx->xmax + 1;
}
if (height)
{
*height = pcx->ymax + 1;
}
for (y = 0; y <= pcx->ymax; y++, pix += pcx->xmax + 1)
{
for (x = 0; x < bpl; )
{
dataByte = *raw++;
if ((dataByte & 0xC0) == 0xC0)
{
runLength = dataByte & 0x3F;
dataByte = *raw++;
}
else
{
runLength = 1;
}
while (runLength-- > 0)
{
if ((*pic + full_size) <= (pix + x))
{
x += runLength;
runLength = 0;
}
else
{
pix[x++] = dataByte;
}
}
}
}
if (raw - (byte *)pcx > len)
{
Com_Printf("PCX file %s was malformed", filename);
Z_Free(*pic);
*pic = NULL;
}
FS_FreeFile(pcx);
}
void
@ -236,7 +147,7 @@ SCR_FinishCinematic(void)
SZ_Print(&cls.netchan.message, va("nextserver %i\n", cl.servercount));
}
int
static int
SmallestNode1(int numhnodes)
{
int i;
@ -276,7 +187,7 @@ SmallestNode1(int numhnodes)
/*
* Reads the 64k counts table and initializes the node trees
*/
void
static void
Huff1TableInit(void)
{
int prev;
@ -333,7 +244,7 @@ Huff1TableInit(void)
}
}
cblock_t
static cblock_t
Huff1Decompress(cblock_t in)
{
byte *input;
@ -592,15 +503,16 @@ SCR_DrawCinematic(void)
return true;
}
if (cin_force43->value)
if (cin_force43->value && cin.height && cin.width)
{
w = viddef.height * 4 / 3;
/* Try to left original aspect ratio */
w = viddef.height * cin.width / cin.height;
if (w > viddef.width)
{
w = viddef.width;
}
w &= ~3;
h = w * 3 / 4;
h = w * cin.height / cin.width;
x = (viddef.width - w) / 2;
y = (viddef.height - h) / 2;
}
@ -653,36 +565,32 @@ SCR_DrawCinematic(void)
return true;
}
byte *
SCR_LoadHiColor(const char* namewe, const char *ext, int *width, int *height)
static byte *
SCR_LoadHiColor(const char* namewe, const char *ext, int *width, int *height,
int *bitesPerPixel)
{
byte *pic, *data = NULL, *palette = NULL;
char filename[256];
int bytesPerPixel;
byte *pic, *data = NULL;
void *rawdata;
size_t len;
Q_strlcpy(filename, namewe, sizeof(filename));
Q_strlcat(filename, ".", sizeof(filename));
Q_strlcat(filename, ext, sizeof(filename));
len = FS_LoadFile(filename, &rawdata);
if (!rawdata || len <=0)
{
return NULL;
}
data = stbi_load_from_memory(rawdata, len, width, height,
&bytesPerPixel, STBI_rgb_alpha);
SCR_LoadImageWithPalette(filename, &data, &palette,
width, height, bitesPerPixel);
if (data == NULL)
{
FS_FreeFile(rawdata);
return NULL;
}
pic = Z_Malloc(cin.height * cin.width * 4);
memcpy(pic, data, cin.height * cin.width * 4);
if (palette)
{
/* strange, here should be no palleted image */
free(palette);
}
pic = Z_Malloc(cin.height * cin.width * (*bitesPerPixel) / 8);
memcpy(pic, data, cin.height * cin.width * (*bitesPerPixel) / 8);
free(data);
return pic;
@ -705,7 +613,10 @@ SCR_PlayCinematic(char *arg)
dot = strstr(arg, ".");
/* static pcx image */
if (dot && !strcmp(dot, ".pcx"))
if (dot && (!strcmp(dot, ".pcx") ||
!strcmp(dot, ".tga") ||
!strcmp(dot, ".jpg") ||
!strcmp(dot, ".png")))
{
cvar_t *r_retexturing;
@ -721,23 +632,26 @@ SCR_PlayCinematic(char *arg)
/* Remove the extension */
memset(namewe, 0, 256);
memcpy(namewe, name, strlen(name) - strlen(dot));
cin.pic = SCR_LoadHiColor(namewe, "tga", &cin.width, &cin.height);
cin.pic = SCR_LoadHiColor(namewe, "tga", &cin.width, &cin.height,
&cin.color_bits);
if (!cin.pic)
{
cin.pic = SCR_LoadHiColor(namewe, "png", &cin.width, &cin.height);
cin.pic = SCR_LoadHiColor(namewe, "png", &cin.width, &cin.height,
&cin.color_bits);
}
if (!cin.pic)
{
cin.pic = SCR_LoadHiColor(namewe, "jpg", &cin.width, &cin.height);
cin.pic = SCR_LoadHiColor(namewe, "jpg", &cin.width, &cin.height,
&cin.color_bits);
}
}
if (!cin.pic)
{
SCR_LoadPCX(name, &cin.pic, &palette, &cin.width, &cin.height);
cin.color_bits = 8;
SCR_LoadPCX(name, &cin.pic, &palette, &cin.width, &cin.height,
&cin.color_bits);
}
cl.cinematicframe = -1;
@ -793,4 +707,3 @@ SCR_PlayCinematic(char *arg)
cin.pic = SCR_ReadNextFrame();
cl.cinematictime = Sys_Milliseconds();
}

420
src/client/cl_image.c Normal file
View file

@ -0,0 +1,420 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
*
* 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 the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* This file implements .pcx bitmap decoder.
*
* =======================================================================
*/
#include "header/client.h"
#define PCX_IDENT ((0x05 << 8) + 0x0a)
// don't need HDR stuff
#define STBI_NO_LINEAR
#define STBI_NO_HDR
// make sure STB_image uses standard malloc(), as we'll use standard free() to deallocate
#define STBI_MALLOC(sz) malloc(sz)
#define STBI_REALLOC(p,sz) realloc(p,sz)
#define STBI_FREE(p) free(p)
// Switch of the thread local stuff. Breaks mingw under Windows.
#define STBI_NO_THREAD_LOCALS
// include implementation part of stb_image into this file
#define STB_IMAGE_IMPLEMENTATION
#include "refresh/files/stb_image.h"
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)
{
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 *bitesPerPixel)
{
const pcx_t *pcx;
int full_size;
int pcx_width, pcx_height, bytes_per_line;
qboolean image_issues = false;
byte *out, *pix;
const byte *data;
*pic = NULL;
*bitesPerPixel = 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))
{
Com_Printf("%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;
*bitesPerPixel = 32;
}
out = malloc(full_size);
if (!out)
{
Com_Printf("%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))
{
Com_Printf("%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))
{
Com_Printf("%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))
{
Com_Printf("%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
{
Com_Printf("%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)
{
Com_DPrintf("%s: %s file was malformed\n", __func__, name);
free(*pic);
*pic = NULL;
}
if (image_issues)
{
Com_Printf("%s: %s file has possible size issues.\n", __func__, name);
}
}
void
SCR_LoadImageWithPalette(char *filename, byte **pic, byte **palette,
int *width, int *height, int *bitesPerPixel)
{
const char* ext;
int len, ident;
byte *raw;
ext = COM_FileExtension(filename);
*pic = NULL;
/* load the file */
len = FS_LoadFile(filename, (void **)&raw);
if (!raw || len <= 0)
{
return;
}
if (len <= sizeof(int))
{
FS_FreeFile(raw);
return;
}
ident = LittleShort(*((short*)raw));
if (!strcmp(ext, "pcx") && (ident == PCX_IDENT))
{
PCX_Decode(filename, raw, len, pic, palette, width, height, bitesPerPixel);
}
else
{
int sourcebitesPerPixel = 0;
/* other formats does not have palette directly */
if (palette)
{
*palette = NULL;
}
*pic = stbi_load_from_memory(raw, len, width, height,
&sourcebitesPerPixel, STBI_rgb_alpha);
if (*pic == NULL)
{
Com_DPrintf("%s couldn't load data from %s: %s!\n",
__func__, filename, stbi_failure_reason());
}
*bitesPerPixel = 32;
}
FS_FreeFile(raw);
}

View file

@ -59,6 +59,8 @@ extern int crosshair_width, crosshair_height;
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);
void SCR_PlayCinematic(char *name);
qboolean SCR_DrawCinematic(void);
void SCR_RunCinematic(void);

View file

@ -33,7 +33,7 @@
server_static_t svs; /* persistant server info */
server_t sv; /* local server */
int
static int
SV_FindIndex(char *name, int start, int max, qboolean create)
{
int i;
@ -98,7 +98,7 @@ SV_ImageIndex(char *name)
* to the clients -- only the fields that differ from the
* baseline will be transmitted
*/
void
static void
SV_CreateBaseline(void)
{
edict_t *svent;
@ -126,7 +126,7 @@ SV_CreateBaseline(void)
}
}
void
static void
SV_CheckForSavegame(qboolean isautosave)
{
char name[MAX_OSPATH];
@ -181,7 +181,7 @@ SV_CheckForSavegame(qboolean isautosave)
* Change the server to a new map, taking all connected
* clients along with it.
*/
void
static void
SV_SpawnServer(char *server, char *spawnpoint, server_state_t serverstate,
qboolean attractloop, qboolean loadgame, qboolean isautosave)
{
@ -560,7 +560,10 @@ SV_Map(qboolean attractloop, char *levelstring, qboolean loadgame, qboolean isau
SV_BroadcastCommand("changing\n");
SV_SpawnServer(level, spawnpoint, ss_demo, attractloop, loadgame, isautosave);
}
else if ((l > 4) && !strcmp(level + l - 4, ".pcx"))
else if ((l > 4) && (!strcmp(level + l - 4, ".pcx") ||
!strcmp(level + l - 4, ".tga") ||
!strcmp(level + l - 4, ".jpg") ||
!strcmp(level + l - 4, ".png")))
{
#ifndef DEDICATED_ONLY
SCR_BeginLoadingPlaque(); /* for local system */