mirror of
https://bitbucket.org/CPMADevs/cnq3
synced 2024-11-10 06:31:48 +00:00
using stb_image to load png and tga images
libjpeg errors no longer shut down the engine
This commit is contained in:
parent
4c98469f3f
commit
bec4101854
8 changed files with 7348 additions and 153 deletions
|
@ -119,6 +119,7 @@ all: $(TARGETDIR) $(OBJDIR) prebuild prelink $(TARGET)
|
|||
endif
|
||||
|
||||
OBJECTS := \
|
||||
$(OBJDIR)/stb_image.o \
|
||||
$(OBJDIR)/tr_backend.o \
|
||||
$(OBJDIR)/tr_bsp.o \
|
||||
$(OBJDIR)/tr_cmds.o \
|
||||
|
@ -198,6 +199,9 @@ $(GCH): $(PCH)
|
|||
$(SILENT) $(CXX) -x c++-header $(ALL_CXXFLAGS) -o "$@" -MF "$(@:%.gch=%.d)" -c "$<"
|
||||
endif
|
||||
|
||||
$(OBJDIR)/stb_image.o: ../../code/renderer/stb_image.cpp
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/tr_backend.o: ../../code/renderer/tr_backend.cpp
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
|
|
|
@ -297,6 +297,7 @@ copy "..\..\..\.bin\release_x64\cnq3-x64.pdb" "$(QUAKE3DIR)"</Command>
|
|||
<ClInclude Include="..\..\code\qcommon\vm_shim.h" />
|
||||
<ClInclude Include="..\..\code\renderer\qgl.h" />
|
||||
<ClInclude Include="..\..\code\renderer\qgl_linked.h" />
|
||||
<ClInclude Include="..\..\code\renderer\stb_image.h" />
|
||||
<ClInclude Include="..\..\code\renderer\tr_local.h" />
|
||||
<ClInclude Include="..\..\code\renderer\tr_public.h" />
|
||||
<ClInclude Include="..\..\code\server\server.h" />
|
||||
|
|
|
@ -204,6 +204,9 @@
|
|||
<ClInclude Include="..\..\code\renderer\qgl_linked.h">
|
||||
<Filter>renderer</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\code\renderer\stb_image.h">
|
||||
<Filter>renderer</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\code\renderer\tr_local.h">
|
||||
<Filter>renderer</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
@ -207,10 +207,12 @@
|
|||
<ItemGroup>
|
||||
<ClInclude Include="..\..\code\renderer\qgl.h" />
|
||||
<ClInclude Include="..\..\code\renderer\qgl_linked.h" />
|
||||
<ClInclude Include="..\..\code\renderer\stb_image.h" />
|
||||
<ClInclude Include="..\..\code\renderer\tr_local.h" />
|
||||
<ClInclude Include="..\..\code\renderer\tr_public.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\code\renderer\stb_image.cpp" />
|
||||
<ClCompile Include="..\..\code\renderer\tr_backend.cpp" />
|
||||
<ClCompile Include="..\..\code\renderer\tr_bsp.cpp" />
|
||||
<ClCompile Include="..\..\code\renderer\tr_cmds.cpp" />
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
|
||||
DD Mmm 17 - 1.49
|
||||
|
||||
chg: using stb_image to load png and tga images
|
||||
|
||||
fix: libjpeg errors no longer shut down the engine
|
||||
|
||||
add: m_accelStyle to control the mouse accel style and m_accelOffset for style 1
|
||||
m_accelStyle 0 (default) = legacy CNQ3 accel style
|
||||
m_accelStyle 1 = new accel style, similar to quake3e's cl_mouseAccelStyle 1
|
||||
|
|
70
code/renderer/stb_image.cpp
Normal file
70
code/renderer/stb_image.cpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
#include "tr_local.h"
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(disable: 4505) // unreferenced local function
|
||||
#endif
|
||||
|
||||
static void* q_malloc( size_t );
|
||||
static void q_free( void* );
|
||||
static void* q_realloc_sized( void*, size_t, size_t );
|
||||
|
||||
// we only support png and tga
|
||||
#define STBI_MALLOC q_malloc
|
||||
#define STBI_REALLOC_SIZED q_realloc_sized
|
||||
#define STBI_FREE q_free
|
||||
#define STBI_NO_STDIO
|
||||
#define STBI_FAILURE_USERMSG
|
||||
#define STBI_NO_JPEG
|
||||
#define STBI_NO_BMP
|
||||
#define STBI_NO_PSD
|
||||
#define STBI_NO_GIF
|
||||
#define STBI_NO_HDR
|
||||
#define STBI_NO_PIC
|
||||
#define STBI_NO_PNM
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
|
||||
|
||||
static void* q_malloc( size_t bytes )
|
||||
{
|
||||
return ri.Malloc((int)bytes);
|
||||
}
|
||||
|
||||
|
||||
static void q_free( void* buffer )
|
||||
{
|
||||
if (buffer != NULL)
|
||||
ri.Free(buffer);
|
||||
}
|
||||
|
||||
|
||||
static void* q_realloc_sized( void* ptr, size_t oldSize, size_t newSize )
|
||||
{
|
||||
if (ptr == NULL)
|
||||
return q_malloc(newSize);
|
||||
|
||||
if (newSize <= oldSize)
|
||||
return ptr;
|
||||
|
||||
void* ptr2 = q_malloc(newSize);
|
||||
memcpy(ptr2, ptr, oldSize);
|
||||
q_free(ptr);
|
||||
|
||||
return ptr2;
|
||||
}
|
||||
|
||||
|
||||
qbool LoadSTB( const char* fileName, byte* buffer, int len, byte** pic, int* w, int* h, GLenum* format )
|
||||
{
|
||||
int comp;
|
||||
*pic = (byte*)stbi_load_from_memory(buffer, len, w, h, &comp, 4);
|
||||
if (*pic == NULL) {
|
||||
ri.Printf(PRINT_WARNING, "stb_image: couldn't load %s: %s\n", fileName, stbi_failure_reason());
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
*format = comp == 4 ? GL_RGBA : GL_RGB;
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
7177
code/renderer/stb_image.h
Normal file
7177
code/renderer/stb_image.h
Normal file
File diff suppressed because it is too large
Load diff
|
@ -21,6 +21,12 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|||
*/
|
||||
// tr_image.c
|
||||
#include "tr_local.h"
|
||||
#include <setjmp.h>
|
||||
|
||||
|
||||
#if defined (_MSC_VER)
|
||||
# pragma warning (disable: 4611) // setjmp and C++ destructors
|
||||
#endif
|
||||
|
||||
|
||||
#define IMAGE_HASH_SIZE 1024
|
||||
|
@ -478,127 +484,15 @@ image_t* R_CreateImage( const char* name, byte* pic, int width, int height, GLen
|
|||
}
|
||||
|
||||
|
||||
static qbool LoadTGA( const char* name, byte** pic, int* w, int* h, GLenum* format )
|
||||
{
|
||||
*pic = NULL;
|
||||
|
||||
byte* buffer;
|
||||
ri.FS_ReadFile( name, (void**)&buffer );
|
||||
if (!buffer)
|
||||
return qfalse;
|
||||
|
||||
byte* p = buffer;
|
||||
|
||||
TargaHeader targa_header;
|
||||
targa_header.id_length = p[0];
|
||||
targa_header.colormap_type = p[1];
|
||||
targa_header.image_type = p[2];
|
||||
targa_header.width = LittleShort( *(short*)&p[12] );
|
||||
targa_header.height = LittleShort( *(short*)&p[14] );
|
||||
targa_header.pixel_size = p[16];
|
||||
targa_header.attributes = p[17];
|
||||
|
||||
p += sizeof(TargaHeader);
|
||||
|
||||
if ((targa_header.image_type != 2) && (targa_header.image_type != 10) && (targa_header.image_type != 3))
|
||||
ri.Error( ERR_DROP, "LoadTGA %s: Only type 2 and 10 (RGB/A) and 3 (gray) images supported\n", name );
|
||||
|
||||
if ( targa_header.colormap_type )
|
||||
ri.Error( ERR_DROP, "LoadTGA %s: Colormaps not supported\n", name );
|
||||
|
||||
if ( ( targa_header.pixel_size != 32 && targa_header.pixel_size != 24 ) && targa_header.image_type != 3 )
|
||||
ri.Error( ERR_DROP, "LoadTGA %s: Only 32 or 24 bit color images supported\n", name );
|
||||
|
||||
*format = (targa_header.pixel_size == 32) ? GL_RGBA : GL_RGB;
|
||||
|
||||
int columns = targa_header.width;
|
||||
int rows = targa_header.height;
|
||||
unsigned numBytes = columns * rows * 4;
|
||||
|
||||
*w = columns;
|
||||
*h = rows;
|
||||
|
||||
if ( !columns || !rows || (numBytes > 0x7FFFFFFF) || (numBytes / columns / 4 != rows) )
|
||||
ri.Error( ERR_DROP, "LoadTGA: %s has an invalid image size\n", name );
|
||||
|
||||
*pic = (byte*)ri.Malloc(numBytes);
|
||||
byte* dst;
|
||||
|
||||
if (targa_header.id_length != 0)
|
||||
p += targa_header.id_length; // skip TARGA image comment
|
||||
|
||||
byte pixel[4] = { 0, 0, 0, 255 };
|
||||
int bpp = (targa_header.pixel_size >> 3);
|
||||
|
||||
// uncompressed greyscale - expand it into fake-RGB
|
||||
if (targa_header.image_type == 3) {
|
||||
for (int y = rows-1; y >= 0; --y) {
|
||||
dst = *pic + y*columns*4;
|
||||
for (int x = 0; x < columns; ++x) {
|
||||
pixel[0] = pixel[1] = pixel[2] = *p++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// one of these days we'll actually just use GL_BGRA_EXT, but until then...
|
||||
#define UNMUNGE_TGA_PIXEL { *dst++ = pixel[2]; *dst++ = pixel[1]; *dst++ = pixel[0]; *dst++ = pixel[3]; }
|
||||
|
||||
// uncompressed BGR(A)
|
||||
if (targa_header.image_type == 2) {
|
||||
for (int y = rows-1; y >= 0; --y) {
|
||||
dst = *pic + y*columns*4;
|
||||
for (int x = 0; x < columns; ++x) {
|
||||
for (int i = 0; i < bpp; ++i)
|
||||
pixel[i] = *p++;
|
||||
UNMUNGE_TGA_PIXEL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define WRAP_TGA if ((++x == columns) && y--) { x = 0; dst = *pic + y*columns*4; }
|
||||
|
||||
// RLE BGR(A)
|
||||
if (targa_header.image_type == 10) {
|
||||
int y = rows-1;
|
||||
while (y >= 0) {
|
||||
dst = *pic + y*columns*4;
|
||||
int x = 0;
|
||||
while (x < columns) {
|
||||
int rle = *p++;
|
||||
int n = 1 + (rle & 0x7F);
|
||||
if (rle & 0x80) { // RLE packet, 1 pixel n times
|
||||
for (int i = 0; i < bpp; ++i)
|
||||
pixel[i] = *p++;
|
||||
while (n--) {
|
||||
UNMUNGE_TGA_PIXEL;
|
||||
WRAP_TGA;
|
||||
}
|
||||
}
|
||||
else while (n--) { // n distinct pixels
|
||||
for (int i = 0; i < bpp; ++i)
|
||||
pixel[i] = *p++;
|
||||
UNMUNGE_TGA_PIXEL;
|
||||
WRAP_TGA;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef WRAP_TGA
|
||||
|
||||
#undef UNMUNGE_TGA_PIXEL
|
||||
|
||||
if (targa_header.attributes & 0x20)
|
||||
ri.Printf( PRINT_WARNING, "WARNING: '%s' TGA file header declares top-down image, ignoring\n", name );
|
||||
|
||||
ri.FS_FreeFile( buffer );
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
typedef struct {
|
||||
jmp_buf jumpBuffer;
|
||||
const char* fileName;
|
||||
qbool load;
|
||||
} engineJPEGInfo_t;
|
||||
|
||||
// The only memory allocation function pointers we can override are the ones exposed in jpeg_memory_mgr.
|
||||
// The problem is that it's the wrong layer for us: we want to replace malloc and free,
|
||||
// not change how the pooling of allocations works.
|
||||
|
@ -623,35 +517,39 @@ extern "C"
|
|||
{
|
||||
char buffer[JMSG_LENGTH_MAX];
|
||||
(*cinfo->err->format_message)(cinfo, buffer);
|
||||
engineJPEGInfo_t* const extra = (engineJPEGInfo_t*)cinfo->client_data;
|
||||
ri.Printf(PRINT_WARNING, "libjpeg-turbo: couldn't %s %s: %s\n", extra->load ? "load" : "save", extra->fileName, buffer);
|
||||
jpeg_destroy(cinfo);
|
||||
ri.Error( ERR_FATAL, "%s\n", buffer );
|
||||
longjmp(extra->jumpBuffer, -1);
|
||||
}
|
||||
|
||||
void output_message( j_common_ptr cinfo )
|
||||
{
|
||||
char buffer[JMSG_LENGTH_MAX];
|
||||
(*cinfo->err->format_message)(cinfo, buffer);
|
||||
ri.Printf(PRINT_ALL, "%s\n", buffer);
|
||||
const engineJPEGInfo_t* const extra = (const engineJPEGInfo_t*)cinfo->client_data;
|
||||
ri.Printf(PRINT_ALL, "libjpeg-turbo: while %s %s: %s\n", extra->load ? "loading" : "saving", extra->fileName, buffer);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static qbool LoadJPG( const char* name, byte** pic, int* w, int* h )
|
||||
static qbool LoadJPG( const char* fileName, byte* buffer, int len, byte** pic, int* w, int* h, GLenum* format )
|
||||
{
|
||||
jpeg_decompress_struct cinfo;
|
||||
jpeg_error_mgr jerr;
|
||||
engineJPEGInfo_t extra;
|
||||
|
||||
if (setjmp(extra.jumpBuffer))
|
||||
return qfalse;
|
||||
|
||||
extra.load = qtrue;
|
||||
extra.fileName = fileName;
|
||||
cinfo.err = jpeg_std_error( &jerr );
|
||||
cinfo.err->error_exit = &error_exit;
|
||||
cinfo.err->output_message = &output_message;
|
||||
|
||||
cinfo.client_data = &extra;
|
||||
jpeg_create_decompress( &cinfo );
|
||||
|
||||
byte* buffer;
|
||||
const int len = ri.FS_ReadFile( name, (void**)&buffer );
|
||||
if (!buffer)
|
||||
return qfalse;
|
||||
|
||||
jpeg_mem_src( &cinfo, buffer, len );
|
||||
|
||||
jpeg_read_header( &cinfo, TRUE );
|
||||
|
@ -682,19 +580,29 @@ static qbool LoadJPG( const char* name, byte** pic, int* w, int* h )
|
|||
jpeg_finish_decompress( &cinfo );
|
||||
jpeg_destroy_decompress( &cinfo );
|
||||
|
||||
ri.FS_FreeFile( buffer );
|
||||
*format = GL_RGB;
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
|
||||
int SaveJPGToBuffer( byte* out, int quality, int image_width, int image_height, byte* image_buffer )
|
||||
{
|
||||
static const char* fileName = "memory buffer";
|
||||
|
||||
jpeg_compress_struct cinfo;
|
||||
jpeg_error_mgr jerr;
|
||||
engineJPEGInfo_t extra;
|
||||
|
||||
if (setjmp(extra.jumpBuffer))
|
||||
return qfalse;
|
||||
|
||||
extra.load = qfalse;
|
||||
extra.fileName = fileName;
|
||||
cinfo.err = jpeg_std_error( &jerr );
|
||||
cinfo.err->error_exit = &error_exit;
|
||||
cinfo.err->output_message = &output_message;
|
||||
cinfo.client_data = &extra;
|
||||
jpeg_create_compress( &cinfo );
|
||||
|
||||
// jpeg_mem_dest only calls malloc if both outSize and outBuffer are 0
|
||||
|
@ -732,40 +640,66 @@ int SaveJPGToBuffer( byte* out, int quality, int image_width, int image_height,
|
|||
///////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
extern qbool LoadSTB( const char* fileName, byte* buffer, int len, byte** pic, int* w, int* h, GLenum* format );
|
||||
|
||||
typedef qbool (*imageLoaderFunc)( const char* fileName, byte* buffer, int len, byte** pic, int* w, int* h, GLenum* format );
|
||||
|
||||
typedef struct {
|
||||
const char* extension;
|
||||
imageLoaderFunc function;
|
||||
} imageLoader_t;
|
||||
|
||||
static const imageLoader_t imageLoaders[] = {
|
||||
{ ".jpg", &LoadJPG },
|
||||
{ ".tga", &LoadSTB },
|
||||
{ ".png", &LoadSTB },
|
||||
{ ".jpeg", &LoadJPG }
|
||||
};
|
||||
|
||||
|
||||
static void R_LoadImage( const char* name, byte** pic, int* w, int* h, GLenum* format )
|
||||
{
|
||||
*pic = NULL;
|
||||
|
||||
*w = 0;
|
||||
*h = 0;
|
||||
|
||||
int len = strlen(name);
|
||||
if (len < 5) {
|
||||
ri.Printf( PRINT_ALL, "ERROR: invalid image name %s\n", name );
|
||||
return;
|
||||
const int loaderCount = ARRAY_LEN( imageLoaders );
|
||||
char altName[MAX_QPATH];
|
||||
|
||||
byte* buffer;
|
||||
int bufferSize = ri.FS_ReadFile( name, (void**)&buffer );
|
||||
if ( buffer == NULL ) {
|
||||
const char* lastDot = strrchr( name, '.' );
|
||||
const int nameLength = lastDot != NULL ? (int)(lastDot - name) : (int)strlen( name );
|
||||
if ( nameLength >= MAX_QPATH )
|
||||
return;
|
||||
|
||||
for ( int i = 0; i < loaderCount; ++i ) {
|
||||
memcpy( altName, name, nameLength );
|
||||
altName[nameLength] = '\0';
|
||||
Q_strcat( altName, sizeof(altName), imageLoaders[i].extension );
|
||||
bufferSize = ri.FS_ReadFile( altName, (void**)&buffer );
|
||||
if ( buffer != NULL ) {
|
||||
name = altName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( buffer == NULL )
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Q_stricmp( name+len-4, ".tga" ) && LoadTGA( name, pic, w, h, format ))
|
||||
return;
|
||||
const int nameLength = (int)strlen( name );
|
||||
for ( int i = 0; i < loaderCount; ++i ) {
|
||||
const int extLength = (int)strlen( imageLoaders[i].extension );
|
||||
if ( extLength < nameLength &&
|
||||
Q_stricmp(name + nameLength - extLength, imageLoaders[i].extension) == 0 ) {
|
||||
(*imageLoaders[i].function)( name, buffer, bufferSize, pic, w, h, format );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*format = GL_RGB;
|
||||
#if defined(_DEBUG)
|
||||
// either this is REALLY a jpg, or just as likely some moron got the extension wrong
|
||||
if (!Q_stricmp( name+len-4, ".jpg" ) && LoadJPG( name, pic, w, h ))
|
||||
return;
|
||||
|
||||
ri.Printf( PRINT_DEVELOPER, "WARNING: idiot has misnamed %s\n", name );
|
||||
#endif
|
||||
|
||||
char altname[MAX_QPATH];
|
||||
Q_strncpyz( altname, name, sizeof(altname) );
|
||||
len = strlen( altname );
|
||||
altname[len-3] = 'j';
|
||||
altname[len-2] = 'p';
|
||||
altname[len-1] = 'g';
|
||||
LoadJPG( altname, pic, w, h );
|
||||
|
||||
return;
|
||||
ri.FS_FreeFile( buffer );
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue