mirror of
https://github.com/Shpoike/Quakespasm.git
synced 2024-11-10 07:21:58 +00:00
1169 lines
41 KiB
C
1169 lines
41 KiB
C
/*
|
|
Copyright (C) 1996-2001 Id Software, Inc.
|
|
Copyright (C) 2002-2009 John Fitzgibbons and others
|
|
Copyright (C) 2010-2014 QuakeSpasm developers
|
|
|
|
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.
|
|
|
|
*/
|
|
//image.c -- image loading
|
|
|
|
#include "quakedef.h"
|
|
|
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
|
#define STB_IMAGE_WRITE_STATIC
|
|
#include "stb_image_write.h"
|
|
|
|
#define LODEPNG_NO_COMPILE_DECODER
|
|
#define LODEPNG_NO_COMPILE_CPP
|
|
#define LODEPNG_NO_COMPILE_ANCILLARY_CHUNKS
|
|
#define LODEPNG_NO_COMPILE_ERROR_TEXT
|
|
#include "lodepng.h"
|
|
#include "lodepng.c"
|
|
|
|
static char loadfilename[MAX_OSPATH]; //file scope so that error messages can use it
|
|
|
|
typedef struct stdio_buffer_s {
|
|
FILE *f;
|
|
unsigned char buffer[1024];
|
|
int size;
|
|
int pos;
|
|
} stdio_buffer_t;
|
|
|
|
static stdio_buffer_t *Buf_Alloc(FILE *f)
|
|
{
|
|
stdio_buffer_t *buf = (stdio_buffer_t *) calloc(1, sizeof(stdio_buffer_t));
|
|
buf->f = f;
|
|
return buf;
|
|
}
|
|
|
|
static void Buf_Free(stdio_buffer_t *buf)
|
|
{
|
|
free(buf);
|
|
}
|
|
|
|
static inline int Buf_GetC(stdio_buffer_t *buf)
|
|
{
|
|
if (buf->pos >= buf->size)
|
|
{
|
|
buf->size = fread(buf->buffer, 1, sizeof(buf->buffer), buf->f);
|
|
buf->pos = 0;
|
|
|
|
if (buf->size == 0)
|
|
return EOF;
|
|
}
|
|
|
|
return buf->buffer[buf->pos++];
|
|
}
|
|
|
|
/*small function to read files with stb_image - single-file image loader library.
|
|
** downloaded from: https://raw.githubusercontent.com/nothings/stb/master/stb_image.h
|
|
** only use jpeg+png formats, because tbh there's not much need for the others.
|
|
** */
|
|
#define STB_IMAGE_IMPLEMENTATION
|
|
#define STBI_ONLY_JPEG
|
|
#ifdef LODEPNG_NO_COMPILE_DECODER
|
|
#define STBI_ONLY_PNG
|
|
#endif
|
|
#include "stb_image.h"
|
|
static byte *Image_LoadSTBI(FILE *f, int *width, int *height)
|
|
{
|
|
int bytesPerPixel;
|
|
byte *heap = stbi_load_from_file(f, width, height, &bytesPerPixel, 4);
|
|
fclose(f);
|
|
if (heap)
|
|
{ //this is silly, but we do it for consistency.
|
|
//frankly, most people should be using tga-inside-pk3.
|
|
byte *hunk = Hunk_Alloc(*width**height*4);
|
|
memcpy(hunk, heap, *width**height*4);
|
|
free(heap);
|
|
return hunk;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
byte *Image_LoadPNG(FILE *f, int *width, int *height, qboolean *malloced)
|
|
{
|
|
#ifdef LODEPNG_NO_COMPILE_DECODER
|
|
return Image_LoadSTBI (f, width, height);
|
|
#else
|
|
unsigned w, h;
|
|
unsigned char *out = NULL, *in;
|
|
size_t insize = com_filesize;
|
|
|
|
in = malloc(com_filesize);
|
|
if (!in)
|
|
return NULL;
|
|
if (com_filesize == fread(in, 1, com_filesize, f))
|
|
{
|
|
*malloced = true;
|
|
lodepng_decode32(&out, &w, &h, in, insize);
|
|
}
|
|
free(in);
|
|
return out;
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
/*spike -- start of dds loader
|
|
note that support for loading compressed mipchains is elsewhere. not all those listed here will work.
|
|
formats are dependant upon the user's hardware.
|
|
mip chains must be complete (no single-level images)
|
|
non-2d images are not supported.
|
|
dx10 dds files ARE supported (but probably won't be generated by your tools...), but bc7 is there if you want it (and the user's gpu drivers support it).
|
|
*/
|
|
typedef struct {
|
|
unsigned int dwSize;
|
|
unsigned int dwFlags;
|
|
unsigned int dwFourCC;
|
|
|
|
unsigned int bitcount;
|
|
unsigned int redmask;
|
|
unsigned int greenmask;
|
|
unsigned int bluemask;
|
|
unsigned int alphamask;
|
|
} ddspixelformat_t;
|
|
|
|
typedef struct {
|
|
unsigned int magic;
|
|
unsigned int dwSize;
|
|
unsigned int dwFlags;
|
|
unsigned int dwHeight;
|
|
unsigned int dwWidth;
|
|
unsigned int dwPitchOrLinearSize;
|
|
unsigned int dwDepth;
|
|
unsigned int dwMipMapCount;
|
|
unsigned int dwReserved1[11];
|
|
ddspixelformat_t ddpfPixelFormat;
|
|
unsigned int ddsCaps[4];
|
|
unsigned int dwReserved2;
|
|
} ddsheader_t;
|
|
typedef struct {
|
|
unsigned int dxgiformat;
|
|
unsigned int resourcetype; //0=unknown, 1=buffer, 2=1d, 3=2d, 4=3d
|
|
unsigned int miscflag; //singular... yeah. 4=cubemap.
|
|
unsigned int arraysize;
|
|
unsigned int miscflags2;
|
|
} dds10header_t;
|
|
|
|
static byte *Image_LoadDDS(FILE *f, int *width, int *height, enum srcformat *fmt)
|
|
{
|
|
const char *fname = loadfilename; //parameters as globals is a shitty practise, but I'm too lazy to fix up the other loaders too.
|
|
int nummips;
|
|
int mipnum;
|
|
int datasize;
|
|
byte *ret;
|
|
enum srcformat encoding = SRC_EXTERNAL;
|
|
|
|
ddsheader_t fmtheader;
|
|
dds10header_t fmt10header;
|
|
|
|
fread(&fmtheader, 1, sizeof(fmtheader), f);
|
|
|
|
if (fmtheader.magic != (('D'<<0)|('D'<<8)|('S'<<16)|(' '<<24)))
|
|
return NULL;
|
|
|
|
fmtheader.dwSize += sizeof(fmtheader.magic);
|
|
if (fmtheader.dwSize != sizeof(fmtheader))
|
|
return NULL; //corrupt/different version
|
|
memset(&fmt10header, 0, sizeof(fmt10header));
|
|
|
|
fmt10header.arraysize = (fmtheader.ddsCaps[1] & 0x200)?6:1; //cubemaps need 6 faces...
|
|
|
|
nummips = fmtheader.dwMipMapCount;
|
|
if (nummips < 1)
|
|
nummips = 1;
|
|
|
|
if (!(fmtheader.ddpfPixelFormat.dwFlags & 4))
|
|
{
|
|
#define IsPacked(bits,r,g,b,a) fmtheader.ddpfPixelFormat.bitcount==bits&&fmtheader.ddpfPixelFormat.redmask==r&&fmtheader.ddpfPixelFormat.greenmask==g&&fmtheader.ddpfPixelFormat.bluemask==b&&fmtheader.ddpfPixelFormat.alphamask==a
|
|
if (IsPacked(24, 0xff0000, 0x00ff00, 0x0000ff, 0))
|
|
encoding = TexMgr_FormatForName("BGR8");
|
|
else if (IsPacked(24, 0x000000, 0x00ff00, 0xff0000, 0))
|
|
encoding = TexMgr_FormatForName("RGB8");
|
|
else if (IsPacked(32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000))
|
|
encoding = TexMgr_FormatForName("BGRA8");
|
|
else if (IsPacked(32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000))
|
|
encoding = TexMgr_FormatForName("RGBA8");
|
|
else if (IsPacked(32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0))
|
|
encoding = TexMgr_FormatForName("BGRX8");
|
|
else if (IsPacked(32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0))
|
|
encoding = TexMgr_FormatForName("RGBX8");
|
|
else if (IsPacked(32, 0x000003ff, 0x000ffc00, 0x3ff00000, 0xc0000000))
|
|
encoding = TexMgr_FormatForName("A2BGR10");
|
|
else if (IsPacked(16, 0xf800, 0x07e0, 0x001f, 0))
|
|
encoding = TexMgr_FormatForName("RGB565");
|
|
else if (IsPacked(16, 0xf800, 0x07c0, 0x003e, 0x0001))
|
|
encoding = TexMgr_FormatForName("RGBA5551");
|
|
else if (IsPacked(16, 0x7c00, 0x03e0, 0x001f, 0x8000))
|
|
encoding = TexMgr_FormatForName("ARGB1555");
|
|
else if (IsPacked(16, 0xf000, 0x0f00, 0x00f0, 0x000f))
|
|
encoding = TexMgr_FormatForName("RGBA4444");
|
|
else if (IsPacked(16, 0x0f00, 0x00f0, 0x000f, 0xf000))
|
|
encoding = TexMgr_FormatForName("ARGB4444");
|
|
else if (IsPacked( 8, 0x000000ff, 0x00000000, 0x00000000, 0x00000000))
|
|
encoding = (fmtheader.ddpfPixelFormat.dwFlags&0x20000)?TexMgr_FormatForName("L8"):TexMgr_FormatForName("R8");
|
|
else if (IsPacked(16, 0x000000ff, 0x00000000, 0x00000000, 0x0000ff00))
|
|
encoding = TexMgr_FormatForName("L8A8");
|
|
if (encoding == SRC_EXTERNAL) //used as an error code
|
|
{
|
|
Con_Printf("Unsupported non-fourcc dds in %s\n", fname);
|
|
Con_Printf(" bits: %u\n", fmtheader.ddpfPixelFormat.bitcount);
|
|
Con_Printf(" red: %08x\n", fmtheader.ddpfPixelFormat.redmask);
|
|
Con_Printf("green: %08x\n", fmtheader.ddpfPixelFormat.greenmask);
|
|
Con_Printf(" blue: %08x\n", fmtheader.ddpfPixelFormat.bluemask);
|
|
Con_Printf("alpha: %08x\n", fmtheader.ddpfPixelFormat.alphamask);
|
|
Con_Printf(" used: %08x\n", fmtheader.ddpfPixelFormat.redmask^fmtheader.ddpfPixelFormat.greenmask^fmtheader.ddpfPixelFormat.bluemask^fmtheader.ddpfPixelFormat.alphamask);
|
|
return NULL;
|
|
}
|
|
#undef IsPacked
|
|
}
|
|
else if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('T'<<16)|('1'<<24)))
|
|
encoding = TexMgr_FormatForName("BC1_RGBA"); //alpha or not? Assume yes, and let the drivers decide.
|
|
else if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('T'<<16)|('2'<<24))) //dx3 with premultiplied alpha
|
|
{
|
|
// if (!(tex->flags & IF_PREMULTIPLYALPHA))
|
|
// return false;
|
|
encoding = TexMgr_FormatForName("BC2_RGBA");
|
|
}
|
|
else if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('T'<<16)|('3'<<24)))
|
|
{
|
|
// if (tex->flags & IF_PREMULTIPLYALPHA)
|
|
// return false;
|
|
encoding = TexMgr_FormatForName("BC2_RGBA");
|
|
}
|
|
else if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('T'<<16)|('4'<<24))) //dx5 with premultiplied alpha
|
|
{
|
|
// if (!(tex->flags & IF_PREMULTIPLYALPHA))
|
|
// return false;
|
|
encoding = TexMgr_FormatForName("BC3_RGBA");
|
|
}
|
|
else if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('T'<<16)|('5'<<24)))
|
|
{
|
|
// if (tex->flags & IF_PREMULTIPLYALPHA)
|
|
// return false;
|
|
encoding = TexMgr_FormatForName("BC3_RGBA");
|
|
}
|
|
else if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('A'<<0)|('T'<<8)|('I'<<16)|('1'<<24))
|
|
|| *(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('B'<<0)|('C'<<8)|('4'<<16)|('U'<<24)))
|
|
encoding = TexMgr_FormatForName("BC4_R");
|
|
else if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('A'<<0)|('T'<<8)|('I'<<16)|('2'<<24))
|
|
|| *(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('B'<<0)|('C'<<8)|('5'<<16)|('U'<<24)))
|
|
encoding = TexMgr_FormatForName("BC5_RG");
|
|
else if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('B'<<0)|('C'<<8)|('4'<<16)|('S'<<24)))
|
|
encoding = TexMgr_FormatForName("BC4_R_SNORM");
|
|
else if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('B'<<0)|('C'<<8)|('5'<<16)|('S'<<24)))
|
|
encoding = TexMgr_FormatForName("BC5_RG_SNORM");
|
|
else if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('E'<<0)|('T'<<8)|('C'<<16)|('2'<<24)))
|
|
encoding = TexMgr_FormatForName("ETC2_RGB");
|
|
else if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('1'<<16)|('0'<<24)))
|
|
{
|
|
//this has some weird extra header with dxgi format types.
|
|
fread(&fmt10header, 1, sizeof(fmt10header), f);
|
|
switch(fmt10header.dxgiformat)
|
|
{
|
|
// case 0x0/*DXGI_FORMAT_UNKNOWN*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
// case 0x1/*DXGI_FORMAT_R32G32B32A32_TYPELESS*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
case 0x2/*DXGI_FORMAT_R32G32B32A32_FLOAT*/: encoding = TexMgr_FormatForName("RGBA32F"); break;
|
|
// case 0x3/*DXGI_FORMAT_R32G32B32A32_UINT*/: encoding = TexMgr_FormatForName("RGBA32_UINT"); break;
|
|
// case 0x4/*DXGI_FORMAT_R32G32B32A32_SINT*/: encoding = TexMgr_FormatForName("RGBA32_SINT"); break;
|
|
// case 0x5/*DXGI_FORMAT_R32G32B32_TYPELESS*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
case 0x6/*DXGI_FORMAT_R32G32B32_FLOAT*/: encoding = TexMgr_FormatForName("RGB32F"); break;
|
|
// case 0x7/*DXGI_FORMAT_R32G32B32_UINT*/: encoding = TexMgr_FormatForName("RGB32_UINT"); break;
|
|
// case 0x8/*DXGI_FORMAT_R32G32B32_SINT*/: encoding = TexMgr_FormatForName("RGB32_SINT"); break;
|
|
// case 0x9/*DXGI_FORMAT_R16G16B16A16_TYPELESS*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
case 0xa/*DXGI_FORMAT_R16G16B16A16_FLOAT*/: encoding = TexMgr_FormatForName("RGBA16F"); break;
|
|
case 0xb/*DXGI_FORMAT_R16G16B16A16_UNORM*/: encoding = TexMgr_FormatForName("RGBA16"); break;
|
|
// case 0xc/*DXGI_FORMAT_R16G16B16A16_UINT*/: encoding = TexMgr_FormatForName("RGBA16_UINT"); break;
|
|
// case 0xd/*DXGI_FORMAT_R16G16B16A16_SNORM*/: encoding = TexMgr_FormatForName("RGBA16_SNORM"); break;
|
|
// case 0xe/*DXGI_FORMAT_R16G16B16A16_SINT*/: encoding = TexMgr_FormatForName("RGBA16_SINT"); break;
|
|
// case 0xf/*DXGI_FORMAT_R32G32_TYPELESS*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
// case 0x10/*DXGI_FORMAT_R32G32_FLOAT*/: encoding = TexMgr_FormatForName("RG32F"); break;
|
|
// case 0x11/*DXGI_FORMAT_R32G32_UINT*/: encoding = TexMgr_FormatForName("RG32_UINT"); break;
|
|
// case 0x12/*DXGI_FORMAT_R32G32_SINT*/: encoding = TexMgr_FormatForName("RG32_SINT"); break;
|
|
// case 0x13/*DXGI_FORMAT_R32G8X24_TYPELESS*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
// case 0x14/*DXGI_FORMAT_D32_FLOAT_S8X24_UINT*/: encoding = TexMgr_FormatForName("DEPTH32_8"); break;
|
|
// case 0x15/*DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS*/:encoding = TexMgr_FormatForName("INVALID"); break;
|
|
// case 0x16/*DXGI_FORMAT_X32_TYPELESS_G8X24_UINT*/:encoding = TexMgr_FormatForName("INVALID"); break;
|
|
// case 0x17/*DXGI_FORMAT_R10G10B10A2_TYPELESS*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
case 0x18/*DXGI_FORMAT_R10G10B10A2_UNORM*/: encoding = TexMgr_FormatForName("A2BGR10"); break;
|
|
// case 0x19/*DXGI_FORMAT_R10G10B10A2_UINT*/: encoding = TexMgr_FormatForName("A2BGR10_UINT"); break;
|
|
case 0x1a/*DXGI_FORMAT_R11G11B10_FLOAT*/: encoding = TexMgr_FormatForName("B10G11R11F"); break;
|
|
// case 0x1b/*DXGI_FORMAT_R8G8B8A8_TYPELESS*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
case 0x1c/*DXGI_FORMAT_R8G8B8A8_UNORM*/: encoding = TexMgr_FormatForName("RGBA8"); break;
|
|
case 0x1d/*DXGI_FORMAT_R8G8B8A8_UNORM_SRGB*/: encoding = TexMgr_FormatForName("RGBA8_SRGB"); break;
|
|
// case 0x1e/*DXGI_FORMAT_R8G8B8A8_UINT*/: encoding = TexMgr_FormatForName("RGBA8_UINT"); break;
|
|
// case 0x1f/*DXGI_FORMAT_R8G8B8A8_SNORM*/: encoding = TexMgr_FormatForName("RGBA8_SNORM"); break;
|
|
// case 0x20/*DXGI_FORMAT_R8G8B8A8_SINT*/: encoding = TexMgr_FormatForName("RGBA8_SINT"); break;
|
|
// case 0x21/*DXGI_FORMAT_R16G16_TYPELESS*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
// case 0x22/*DXGI_FORMAT_R16G16_FLOAT*/: encoding = TexMgr_FormatForName("RG16F"); break;
|
|
// case 0x23/*DXGI_FORMAT_R16G16_UNORM*/: encoding = TexMgr_FormatForName("RG16"); break;
|
|
// case 0x24/*DXGI_FORMAT_R16G16_UINT*/: encoding = TexMgr_FormatForName("RG16_UINT"); break;
|
|
// case 0x25/*DXGI_FORMAT_R16G16_SNORM*/: encoding = TexMgr_FormatForName("RG16_SNORM"); break;
|
|
// case 0x26/*DXGI_FORMAT_R16G16_SINT*/: encoding = TexMgr_FormatForName("RG16_SINT"); break;
|
|
// case 0x27/*DXGI_FORMAT_R32_TYPELESS*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
case 0x28/*DXGI_FORMAT_D32_FLOAT*/: encoding = TexMgr_FormatForName("DEPTH32"); break;
|
|
case 0x29/*DXGI_FORMAT_R32_FLOAT*/: encoding = TexMgr_FormatForName("R32F"); break;
|
|
// case 0x2a/*DXGI_FORMAT_R32_UINT*/: encoding = TexMgr_FormatForName("R32_UINT"); break;
|
|
// case 0x2b/*DXGI_FORMAT_R32_SINT*/: encoding = TexMgr_FormatForName("R32_SINT"); break;
|
|
// case 0x2c/*DXGI_FORMAT_R24G8_TYPELESS*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
// case 0x2d/*DXGI_FORMAT_D24_UNORM_S8_UINT*/: encoding = TexMgr_FormatForName("DEPTH24_8"); break;
|
|
// case 0x2e/*DXGI_FORMAT_R24_UNORM_X8_TYPELESS*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
// case 0x2f/*DXGI_FORMAT_X24_TYPELESS_G8_UINT*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
// case 0x30/*DXGI_FORMAT_R8G8_TYPELESS*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
case 0x31/*DXGI_FORMAT_R8G8_UNORM*/: encoding = TexMgr_FormatForName("RG8"); break;
|
|
// case 0x32/*DXGI_FORMAT_R8G8_UINT*/: encoding = TexMgr_FormatForName("RG8_UINT"); break;
|
|
case 0x33/*DXGI_FORMAT_R8G8_SNORM*/: encoding = TexMgr_FormatForName("RG8_SNORM"); break;
|
|
// case 0x34/*DXGI_FORMAT_R8G8_SINT*/: encoding = TexMgr_FormatForName("RG8_SINT"); break;
|
|
// case 0x35/*DXGI_FORMAT_R16_TYPELESS*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
case 0x36/*DXGI_FORMAT_R16_FLOAT*/: encoding = TexMgr_FormatForName("R16F"); break;
|
|
case 0x37/*DXGI_FORMAT_D16_UNORM*/: encoding = TexMgr_FormatForName("DEPTH16"); break;
|
|
case 0x38/*DXGI_FORMAT_R16_UNORM*/: encoding = TexMgr_FormatForName("R16"); break;
|
|
// case 0x39/*DXGI_FORMAT_R16_UINT*/: encoding = TexMgr_FormatForName("R16_UINT"); break;
|
|
// case 0x3a/*DXGI_FORMAT_R16_SNORM*/: encoding = TexMgr_FormatForName("R16_SNORM"); break;
|
|
// case 0x3b/*DXGI_FORMAT_R16_SINT*/: encoding = TexMgr_FormatForName("R16_SINT"); break;
|
|
// case 0x3c/*DXGI_FORMAT_R8_TYPELESS*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
case 0x3d/*DXGI_FORMAT_R8_UNORM*/: encoding = TexMgr_FormatForName("R8"); break;
|
|
// case 0x3e/*DXGI_FORMAT_R8_UINT*/: encoding = TexMgr_FormatForName("R8_UINT"); break;
|
|
case 0x3f/*DXGI_FORMAT_R8_SNORM*/: encoding = TexMgr_FormatForName("R8_SNORM"); break;
|
|
// case 0x40/*DXGI_FORMAT_R8_SINT*/: encoding = TexMgr_FormatForName("R8_SINT"); break;
|
|
case 0x41/*DXGI_FORMAT_A8_UNORM*/: encoding = TexMgr_FormatForName("A8"); break;
|
|
// case 0x42/*DXGI_FORMAT_R1_UNORM*/: encoding = TexMgr_FormatForName("R1"); break;
|
|
case 0x43/*DXGI_FORMAT_R9G9B9E5_SHAREDEXP*/: encoding = TexMgr_FormatForName("E5BGR9"); break;
|
|
// case 0x44/*DXGI_FORMAT_R8G8_B8G8_UNORM*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
// case 0x45/*DXGI_FORMAT_G8R8_G8B8_UNORM*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
// case 0x46/*DXGI_FORMAT_BC1_TYPELESS*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
case 0x47/*DXGI_FORMAT_BC1_UNORM*/: encoding = TexMgr_FormatForName("BC1_RGBA"); break;
|
|
case 0x48/*DXGI_FORMAT_BC1_UNORM_SRGB*/: encoding = TexMgr_FormatForName("BC1_RGBA_SRGB"); break;
|
|
// case 0x49/*DXGI_FORMAT_BC2_TYPELESS*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
case 0x4a/*DXGI_FORMAT_BC2_UNORM*/: encoding = TexMgr_FormatForName("BC2_RGBA"); break;
|
|
case 0x4b/*DXGI_FORMAT_BC2_UNORM_SRGB*/: encoding = TexMgr_FormatForName("BC2_RGBA_SRGB"); break;
|
|
// case 0x4c/*DXGI_FORMAT_BC3_TYPELESS*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
case 0x4d/*DXGI_FORMAT_BC3_UNORM*/: encoding = TexMgr_FormatForName("BC3_RGBA"); break;
|
|
case 0x4e/*DXGI_FORMAT_BC3_UNORM_SRGB*/: encoding = TexMgr_FormatForName("BC3_RGBA_SRGB"); break;
|
|
// case 0x4f/*DXGI_FORMAT_BC4_TYPELESS*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
case 0x50/*DXGI_FORMAT_BC4_UNORM*/: encoding = TexMgr_FormatForName("BC4_R"); break;
|
|
case 0x51/*DXGI_FORMAT_BC4_SNORM*/: encoding = TexMgr_FormatForName("BC4_R_SNORM"); break;
|
|
// case 0x52/*DXGI_FORMAT_BC5_TYPELESS*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
case 0x53/*DXGI_FORMAT_BC5_UNORM*/: encoding = TexMgr_FormatForName("BC5_RG"); break;
|
|
case 0x54/*DXGI_FORMAT_BC5_SNORM*/: encoding = TexMgr_FormatForName("BC5_RG_SNORM"); break;
|
|
case 0x55/*DXGI_FORMAT_B5G6R5_UNORM*/: encoding = TexMgr_FormatForName("RGB565"); break;
|
|
case 0x56/*DXGI_FORMAT_B5G5R5A1_UNORM*/: encoding = TexMgr_FormatForName("ARGB1555"); break;
|
|
case 0x57/*DXGI_FORMAT_B8G8R8A8_UNORM*/: encoding = TexMgr_FormatForName("BGRA8"); break;
|
|
case 0x58/*DXGI_FORMAT_B8G8R8X8_UNORM*/: encoding = TexMgr_FormatForName("BGRX8"); break;
|
|
// case 0x59/*DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM*/:encoding = TexMgr_FormatForName("INVALID"); break;
|
|
// case 0x5a/*DXGI_FORMAT_B8G8R8A8_TYPELESS*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
case 0x5b/*DXGI_FORMAT_B8G8R8A8_UNORM_SRGB*/: encoding = TexMgr_FormatForName("BGRA8_SRGB"); break;
|
|
// case 0x5c/*DXGI_FORMAT_B8G8R8X8_TYPELESS*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
case 0x5d/*DXGI_FORMAT_B8G8R8X8_UNORM_SRGB*/: encoding = TexMgr_FormatForName("BGRX8_SRGB"); break;
|
|
// case 0x5e/*DXGI_FORMAT_BC6H_TYPELESS*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
case 0x5f/*DXGI_FORMAT_BC6H_UF16*/: encoding = TexMgr_FormatForName("BC6_RGB_UFLOAT"); break;
|
|
case 0x60/*DXGI_FORMAT_BC6H_SF16*/: encoding = TexMgr_FormatForName("BC6_RGB_SFLOAT"); break;
|
|
// case 0x61/*DXGI_FORMAT_BC7_TYPELESS*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
case 0x62/*DXGI_FORMAT_BC7_UNORM*/: encoding = TexMgr_FormatForName("BC7_RGBA"); break;
|
|
case 0x63/*DXGI_FORMAT_BC7_UNORM_SRGB*/: encoding = TexMgr_FormatForName("BC7_RGBA_SRGB"); break;
|
|
// case 0x64/*DXGI_FORMAT_AYUV*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
// case 0x65/*DXGI_FORMAT_Y410*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
// case 0x66/*DXGI_FORMAT_Y416*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
// case 0x67/*DXGI_FORMAT_NV12*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
// case 0x68/*DXGI_FORMAT_P010*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
// case 0x69/*DXGI_FORMAT_P016*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
// case 0x6a/*DXGI_FORMAT_420_OPAQUE*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
// case 0x6b/*DXGI_FORMAT_YUY2*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
// case 0x6c/*DXGI_FORMAT_Y210*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
// case 0x6d/*DXGI_FORMAT_Y216*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
// case 0x6e/*DXGI_FORMAT_NV11*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
// case 0x6f/*DXGI_FORMAT_AI44*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
// case 0x70/*DXGI_FORMAT_IA44*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
// case 0x71/*DXGI_FORMAT_P8*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
// case 0x72/*DXGI_FORMAT_A8P8*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
case 0x73/*DXGI_FORMAT_B4G4R4A4_UNORM*/: encoding = TexMgr_FormatForName("ARGB4444"); break;
|
|
// case 0x82/*DXGI_FORMAT_P208*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
// case 0x83/*DXGI_FORMAT_V208*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
// case 0x84/*DXGI_FORMAT_V408*/: encoding = TexMgr_FormatForName("INVALID"); break;
|
|
case 134: encoding = TexMgr_FormatForName("ASTC_4X4"); break;
|
|
case 135: encoding = TexMgr_FormatForName("ASTC_4X4_SRGB"); break;
|
|
case 138: encoding = TexMgr_FormatForName("ASTC_5X4"); break;
|
|
case 139: encoding = TexMgr_FormatForName("ASTC_5X4_SRGB"); break;
|
|
case 142: encoding = TexMgr_FormatForName("ASTC_5X5"); break;
|
|
case 143: encoding = TexMgr_FormatForName("ASTC_5X5_SRGB"); break;
|
|
case 146: encoding = TexMgr_FormatForName("ASTC_6X5"); break;
|
|
case 147: encoding = TexMgr_FormatForName("ASTC_6X5_SRGB"); break;
|
|
case 150: encoding = TexMgr_FormatForName("ASTC_6X6"); break;
|
|
case 151: encoding = TexMgr_FormatForName("ASTC_6X6_SRGB"); break;
|
|
case 154: encoding = TexMgr_FormatForName("ASTC_8X5"); break;
|
|
case 155: encoding = TexMgr_FormatForName("ASTC_8X5_SRGB"); break;
|
|
case 158: encoding = TexMgr_FormatForName("ASTC_8X6"); break;
|
|
case 159: encoding = TexMgr_FormatForName("ASTC_8X6_SRGB"); break;
|
|
case 162: encoding = TexMgr_FormatForName("ASTC_8X8"); break;
|
|
case 163: encoding = TexMgr_FormatForName("ASTC_8X8_SRGB"); break;
|
|
case 166: encoding = TexMgr_FormatForName("ASTC_10X5"); break;
|
|
case 167: encoding = TexMgr_FormatForName("ASTC_10X5_SRGB"); break;
|
|
case 170: encoding = TexMgr_FormatForName("ASTC_10X6"); break;
|
|
case 171: encoding = TexMgr_FormatForName("ASTC_10X6_SRGB"); break;
|
|
case 174: encoding = TexMgr_FormatForName("ASTC_10X8"); break;
|
|
case 175: encoding = TexMgr_FormatForName("ASTC_10X8_SRGB"); break;
|
|
case 178: encoding = TexMgr_FormatForName("ASTC_10X10"); break;
|
|
case 179: encoding = TexMgr_FormatForName("ASTC_10X10_SRGB"); break;
|
|
case 182: encoding = TexMgr_FormatForName("ASTC_12X10"); break;
|
|
case 183: encoding = TexMgr_FormatForName("ASTC_12X10_SRGB"); break;
|
|
case 186: encoding = TexMgr_FormatForName("ASTC_12X12"); break;
|
|
case 187: encoding = TexMgr_FormatForName("ASTC_12X12_SRGB"); break;
|
|
|
|
default:
|
|
Con_Printf("Unsupported dds10 dxgi in %s - %u\n", fname, fmt10header.dxgiformat);
|
|
return NULL;
|
|
}
|
|
}
|
|
if (encoding == SRC_EXTERNAL) //used as an error code
|
|
{
|
|
Con_Printf("Unsupported encoding in %s - \"%c%c%c%c\"\n", fname,
|
|
((char*)&fmtheader.ddpfPixelFormat.dwFourCC)[0],
|
|
((char*)&fmtheader.ddpfPixelFormat.dwFourCC)[1],
|
|
((char*)&fmtheader.ddpfPixelFormat.dwFourCC)[2],
|
|
((char*)&fmtheader.ddpfPixelFormat.dwFourCC)[3]);
|
|
return NULL;
|
|
}
|
|
|
|
if ((fmtheader.ddsCaps[1] & 0x200) && (fmtheader.ddsCaps[1] & 0xfc00) != 0xfc00)
|
|
return NULL; //cubemap without all 6 faces defined.
|
|
|
|
if (fmtheader.ddsCaps[1] & 0x200)
|
|
{
|
|
if (fmt10header.arraysize % 6) //weird number of faces.
|
|
return NULL;
|
|
|
|
if (fmt10header.arraysize == 6)
|
|
{
|
|
//layers = 6;
|
|
return NULL; //don't try to load cubemaps.
|
|
}
|
|
else
|
|
{
|
|
//layers = fmt10header.arraysize;
|
|
return NULL; //don't try to load cubemap arrays.
|
|
}
|
|
}
|
|
else if (fmtheader.ddsCaps[1] & 0x200000)
|
|
{
|
|
if (fmt10header.arraysize != 1) //no 2d arrays
|
|
return NULL;
|
|
return NULL; //don't try to load 3d textures.
|
|
}
|
|
else
|
|
{
|
|
if (fmt10header.arraysize == 1)
|
|
; //yay, we can load 2d images.
|
|
else
|
|
return NULL; //don't try to load 2d arrays.
|
|
//layers = fmt10header.arraysize;
|
|
}
|
|
|
|
for (mipnum = 0; ; mipnum++)
|
|
{
|
|
int mipwidth = fmtheader.dwWidth >> mipnum;
|
|
int mipheight = fmtheader.dwHeight >> mipnum;
|
|
if (!mipwidth && !mipheight)
|
|
break;
|
|
mipwidth = q_max(1,mipwidth); //include the 1*1 mip with non-square textures.
|
|
mipheight = q_max(1,mipheight);
|
|
}
|
|
if (mipnum != nummips)
|
|
{
|
|
Con_Printf("%s: dds with incomplete mip chain\n", fname);
|
|
return NULL;
|
|
}
|
|
|
|
datasize = TexMgr_ImageSize(fmtheader.dwWidth, fmtheader.dwHeight, encoding);
|
|
if (!datasize)
|
|
return NULL; //werid/unsupported
|
|
|
|
//just read the mipchain into a new bit of memory and return that.
|
|
//note that layers and mips are awkward, but we don't support layers here so its just a densely packed pyramid.
|
|
ret = (byte *) Hunk_Alloc (datasize);
|
|
fread(ret, 1, datasize, f);
|
|
|
|
*width = fmtheader.dwWidth;
|
|
*height = fmtheader.dwHeight;
|
|
*fmt = encoding;
|
|
return ret;
|
|
}
|
|
/*spike -- end of dds loader*/
|
|
|
|
|
|
/*
|
|
============
|
|
Image_LoadImage
|
|
|
|
returns a pointer to hunk allocated data. either RGBA8 or a compressed mip chain.
|
|
|
|
TODO: search order: tga png jpg pcx lmp
|
|
============
|
|
*/
|
|
byte *Image_LoadImage (const char *name, int *width, int *height, enum srcformat *fmt, qboolean *malloced)
|
|
{
|
|
FILE *f;
|
|
char *prefixes[3] = {"", "textures/", "textures/"};
|
|
int i;
|
|
|
|
const char *origname = name;
|
|
|
|
*malloced = false;
|
|
*fmt = SRC_RGBA;
|
|
|
|
for (i = 0; i < sizeof(prefixes)/sizeof(prefixes[0]); i++)
|
|
{
|
|
if (i == 2) //last resort...
|
|
{
|
|
name = COM_SkipPath(name);
|
|
if (origname == name)
|
|
continue; //no point trying.
|
|
}
|
|
|
|
q_snprintf (loadfilename, sizeof(loadfilename), "%s%s.dds", prefixes[i], name);
|
|
COM_FOpenFile (loadfilename, &f, NULL);
|
|
if (f)
|
|
return Image_LoadDDS (f, width, height, fmt);
|
|
|
|
q_snprintf (loadfilename, sizeof(loadfilename), "%s%s.tga", prefixes[i], name);
|
|
COM_FOpenFile (loadfilename, &f, NULL);
|
|
if (f)
|
|
return Image_LoadTGA (f, width, height);
|
|
|
|
q_snprintf (loadfilename, sizeof(loadfilename), "%s%s.png", prefixes[i], name);
|
|
COM_FOpenFile (loadfilename, &f, NULL);
|
|
if (f)
|
|
return Image_LoadPNG (f, width, height, malloced);
|
|
|
|
q_snprintf (loadfilename, sizeof(loadfilename), "%s%s.jpeg", prefixes[i], name);
|
|
COM_FOpenFile (loadfilename, &f, NULL);
|
|
if (f)
|
|
return Image_LoadSTBI (f, width, height);
|
|
|
|
q_snprintf (loadfilename, sizeof(loadfilename), "%s%s.jpg", prefixes[i], name);
|
|
COM_FOpenFile (loadfilename, &f, NULL);
|
|
if (f)
|
|
return Image_LoadSTBI (f, width, height);
|
|
|
|
q_snprintf (loadfilename, sizeof(loadfilename), "%s%s.pcx", prefixes[i], name);
|
|
COM_FOpenFile (loadfilename, &f, NULL);
|
|
if (f)
|
|
return Image_LoadPCX (f, width, height);
|
|
}
|
|
|
|
name = origname;
|
|
q_snprintf (loadfilename, sizeof(loadfilename), "%s%s.lmp", "", name);
|
|
COM_FOpenFile (loadfilename, &f, NULL);
|
|
if (f)
|
|
return Image_LoadLMP (f, width, height);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//==============================================================================
|
|
//
|
|
// TGA
|
|
//
|
|
//==============================================================================
|
|
|
|
typedef struct targaheader_s {
|
|
unsigned char id_length, colormap_type, image_type;
|
|
unsigned short colormap_index, colormap_length;
|
|
unsigned char colormap_size;
|
|
unsigned short x_origin, y_origin, width, height;
|
|
unsigned char pixel_size, attributes;
|
|
} targaheader_t;
|
|
|
|
#define TARGAHEADERSIZE 18 //size on disk
|
|
|
|
targaheader_t targa_header;
|
|
|
|
int fgetLittleShort (FILE *f)
|
|
{
|
|
byte b1, b2;
|
|
|
|
b1 = fgetc(f);
|
|
b2 = fgetc(f);
|
|
|
|
return (short)(b1 + b2*256);
|
|
}
|
|
|
|
int fgetLittleLong (FILE *f)
|
|
{
|
|
byte b1, b2, b3, b4;
|
|
|
|
b1 = fgetc(f);
|
|
b2 = fgetc(f);
|
|
b3 = fgetc(f);
|
|
b4 = fgetc(f);
|
|
|
|
return b1 + (b2<<8) + (b3<<16) + (b4<<24);
|
|
}
|
|
|
|
/*
|
|
============
|
|
Image_WriteTGA -- writes RGB or RGBA data to a TGA file
|
|
|
|
returns true if successful
|
|
|
|
TODO: support BGRA and BGR formats (since opengl can return them, and we don't have to swap)
|
|
============
|
|
*/
|
|
qboolean Image_WriteTGA (const char *name, byte *data, int width, int height, int bpp, qboolean upsidedown)
|
|
{
|
|
int handle, i, size, temp, bytes;
|
|
char pathname[MAX_OSPATH];
|
|
byte header[TARGAHEADERSIZE];
|
|
|
|
Sys_mkdir (com_gamedir); //if we've switched to a nonexistant gamedir, create it now so we don't crash
|
|
q_snprintf (pathname, sizeof(pathname), "%s/%s", com_gamedir, name);
|
|
handle = Sys_FileOpenWrite (pathname);
|
|
if (handle == -1)
|
|
return false;
|
|
|
|
Q_memset (header, 0, TARGAHEADERSIZE);
|
|
header[2] = 2; // uncompressed type
|
|
header[12] = width&255;
|
|
header[13] = width>>8;
|
|
header[14] = height&255;
|
|
header[15] = height>>8;
|
|
header[16] = bpp; // pixel size
|
|
if (upsidedown)
|
|
header[17] = 0x20; //upside-down attribute
|
|
|
|
// swap red and blue bytes
|
|
bytes = bpp/8;
|
|
size = width*height*bytes;
|
|
for (i=0; i<size; i+=bytes)
|
|
{
|
|
temp = data[i];
|
|
data[i] = data[i+2];
|
|
data[i+2] = temp;
|
|
}
|
|
|
|
Sys_FileWrite (handle, header, TARGAHEADERSIZE);
|
|
Sys_FileWrite (handle, data, size);
|
|
Sys_FileClose (handle);
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
Image_LoadTGA
|
|
=============
|
|
*/
|
|
byte *Image_LoadTGA (FILE *fin, int *width, int *height)
|
|
{
|
|
int columns, rows, numPixels;
|
|
byte *pixbuf;
|
|
int row, column;
|
|
byte *targa_rgba;
|
|
int realrow; //johnfitz -- fix for upside-down targas
|
|
qboolean upside_down; //johnfitz -- fix for upside-down targas
|
|
stdio_buffer_t *buf;
|
|
|
|
targa_header.id_length = fgetc(fin);
|
|
targa_header.colormap_type = fgetc(fin);
|
|
targa_header.image_type = fgetc(fin);
|
|
|
|
targa_header.colormap_index = fgetLittleShort(fin);
|
|
targa_header.colormap_length = fgetLittleShort(fin);
|
|
targa_header.colormap_size = fgetc(fin);
|
|
targa_header.x_origin = fgetLittleShort(fin);
|
|
targa_header.y_origin = fgetLittleShort(fin);
|
|
targa_header.width = fgetLittleShort(fin);
|
|
targa_header.height = fgetLittleShort(fin);
|
|
targa_header.pixel_size = fgetc(fin);
|
|
targa_header.attributes = fgetc(fin);
|
|
|
|
if (targa_header.image_type==1)
|
|
{
|
|
if (targa_header.pixel_size != 8 || targa_header.colormap_size != 24 || targa_header.colormap_length > 256)
|
|
Sys_Error ("Image_LoadTGA: %s has an %ibit palette\n", loadfilename, targa_header.colormap_type);
|
|
}
|
|
else
|
|
{
|
|
if (targa_header.image_type!=2 && targa_header.image_type!=10)
|
|
Sys_Error ("Image_LoadTGA: %s is not a type 2 or type 10 targa (%i)\n", loadfilename, targa_header.image_type);
|
|
|
|
if (targa_header.colormap_type !=0 || (targa_header.pixel_size!=32 && targa_header.pixel_size!=24))
|
|
Sys_Error ("Image_LoadTGA: %s is not a 24bit or 32bit targa\n", loadfilename);
|
|
}
|
|
|
|
columns = targa_header.width;
|
|
rows = targa_header.height;
|
|
numPixels = columns * rows;
|
|
upside_down = !(targa_header.attributes & 0x20); //johnfitz -- fix for upside-down targas
|
|
|
|
targa_rgba = (byte *) Hunk_Alloc (numPixels*4);
|
|
|
|
if (targa_header.id_length != 0)
|
|
fseek(fin, targa_header.id_length, SEEK_CUR); // skip TARGA image comment
|
|
|
|
buf = Buf_Alloc(fin);
|
|
|
|
if (targa_header.image_type==1) // Uncompressed, paletted images
|
|
{
|
|
byte palette[256*4];
|
|
int i;
|
|
//palette data comes first
|
|
for (i = 0; i < targa_header.colormap_length; i++)
|
|
{ //this palette data is bgr.
|
|
palette[i*3+2] = Buf_GetC(buf);
|
|
palette[i*3+1] = Buf_GetC(buf);
|
|
palette[i*3+0] = Buf_GetC(buf);
|
|
palette[i*3+3] = 255;
|
|
}
|
|
for (i = targa_header.colormap_length*4; i < sizeof(palette); i++)
|
|
palette[i] = 0;
|
|
for(row=rows-1; row>=0; row--)
|
|
{
|
|
realrow = upside_down ? row : rows - 1 - row;
|
|
pixbuf = targa_rgba + realrow*columns*4;
|
|
|
|
for(column=0; column<columns; column++)
|
|
{
|
|
i = Buf_GetC(buf);
|
|
*pixbuf++= palette[i*3+0];
|
|
*pixbuf++= palette[i*3+1];
|
|
*pixbuf++= palette[i*3+2];
|
|
*pixbuf++= palette[i*3+3];
|
|
}
|
|
}
|
|
}
|
|
else if (targa_header.image_type==2) // Uncompressed, RGB images
|
|
{
|
|
for(row=rows-1; row>=0; row--)
|
|
{
|
|
//johnfitz -- fix for upside-down targas
|
|
realrow = upside_down ? row : rows - 1 - row;
|
|
pixbuf = targa_rgba + realrow*columns*4;
|
|
//johnfitz
|
|
for(column=0; column<columns; column++)
|
|
{
|
|
unsigned char red,green,blue,alphabyte;
|
|
switch (targa_header.pixel_size)
|
|
{
|
|
case 24:
|
|
blue = Buf_GetC(buf);
|
|
green = Buf_GetC(buf);
|
|
red = Buf_GetC(buf);
|
|
*pixbuf++ = red;
|
|
*pixbuf++ = green;
|
|
*pixbuf++ = blue;
|
|
*pixbuf++ = 255;
|
|
break;
|
|
case 32:
|
|
blue = Buf_GetC(buf);
|
|
green = Buf_GetC(buf);
|
|
red = Buf_GetC(buf);
|
|
alphabyte = Buf_GetC(buf);
|
|
*pixbuf++ = red;
|
|
*pixbuf++ = green;
|
|
*pixbuf++ = blue;
|
|
*pixbuf++ = alphabyte;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (targa_header.image_type==10) // Runlength encoded RGB images
|
|
{
|
|
unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j;
|
|
for(row=rows-1; row>=0; row--)
|
|
{
|
|
//johnfitz -- fix for upside-down targas
|
|
realrow = upside_down ? row : rows - 1 - row;
|
|
pixbuf = targa_rgba + realrow*columns*4;
|
|
//johnfitz
|
|
for(column=0; column<columns; )
|
|
{
|
|
packetHeader=Buf_GetC(buf);
|
|
packetSize = 1 + (packetHeader & 0x7f);
|
|
if (packetHeader & 0x80) // run-length packet
|
|
{
|
|
switch (targa_header.pixel_size)
|
|
{
|
|
case 24:
|
|
blue = Buf_GetC(buf);
|
|
green = Buf_GetC(buf);
|
|
red = Buf_GetC(buf);
|
|
alphabyte = 255;
|
|
break;
|
|
case 32:
|
|
blue = Buf_GetC(buf);
|
|
green = Buf_GetC(buf);
|
|
red = Buf_GetC(buf);
|
|
alphabyte = Buf_GetC(buf);
|
|
break;
|
|
default: /* avoid compiler warnings */
|
|
blue = red = green = alphabyte = 0;
|
|
}
|
|
|
|
for(j=0;j<packetSize;j++)
|
|
{
|
|
*pixbuf++=red;
|
|
*pixbuf++=green;
|
|
*pixbuf++=blue;
|
|
*pixbuf++=alphabyte;
|
|
column++;
|
|
if (column==columns) // run spans across rows
|
|
{
|
|
column=0;
|
|
if (row>0)
|
|
row--;
|
|
else
|
|
goto breakOut;
|
|
//johnfitz -- fix for upside-down targas
|
|
realrow = upside_down ? row : rows - 1 - row;
|
|
pixbuf = targa_rgba + realrow*columns*4;
|
|
//johnfitz
|
|
}
|
|
}
|
|
}
|
|
else // non run-length packet
|
|
{
|
|
for(j=0;j<packetSize;j++)
|
|
{
|
|
switch (targa_header.pixel_size)
|
|
{
|
|
case 24:
|
|
blue = Buf_GetC(buf);
|
|
green = Buf_GetC(buf);
|
|
red = Buf_GetC(buf);
|
|
*pixbuf++ = red;
|
|
*pixbuf++ = green;
|
|
*pixbuf++ = blue;
|
|
*pixbuf++ = 255;
|
|
break;
|
|
case 32:
|
|
blue = Buf_GetC(buf);
|
|
green = Buf_GetC(buf);
|
|
red = Buf_GetC(buf);
|
|
alphabyte = Buf_GetC(buf);
|
|
*pixbuf++ = red;
|
|
*pixbuf++ = green;
|
|
*pixbuf++ = blue;
|
|
*pixbuf++ = alphabyte;
|
|
break;
|
|
default: /* avoid compiler warnings */
|
|
blue = red = green = alphabyte = 0;
|
|
}
|
|
column++;
|
|
if (column==columns) // pixel packet run spans across rows
|
|
{
|
|
column=0;
|
|
if (row>0)
|
|
row--;
|
|
else
|
|
goto breakOut;
|
|
//johnfitz -- fix for upside-down targas
|
|
realrow = upside_down ? row : rows - 1 - row;
|
|
pixbuf = targa_rgba + realrow*columns*4;
|
|
//johnfitz
|
|
}
|
|
}
|
|
}
|
|
}
|
|
breakOut:;
|
|
}
|
|
}
|
|
|
|
Buf_Free(buf);
|
|
fclose(fin);
|
|
|
|
*width = (int)(targa_header.width);
|
|
*height = (int)(targa_header.height);
|
|
return targa_rgba;
|
|
}
|
|
|
|
//==============================================================================
|
|
//
|
|
// PCX
|
|
//
|
|
//==============================================================================
|
|
|
|
typedef struct
|
|
{
|
|
char signature;
|
|
char version;
|
|
char encoding;
|
|
char bits_per_pixel;
|
|
unsigned short xmin,ymin,xmax,ymax;
|
|
unsigned short hdpi,vdpi;
|
|
byte colortable[48];
|
|
char reserved;
|
|
char color_planes;
|
|
unsigned short bytes_per_line;
|
|
unsigned short palette_type;
|
|
char filler[58];
|
|
} pcxheader_t;
|
|
|
|
/*
|
|
============
|
|
Image_LoadPCX
|
|
============
|
|
*/
|
|
byte *Image_LoadPCX (FILE *f, int *width, int *height)
|
|
{
|
|
pcxheader_t pcx;
|
|
int x, y, w, h, readbyte, runlength, start;
|
|
byte *p, *data;
|
|
byte palette[768];
|
|
stdio_buffer_t *buf;
|
|
|
|
start = ftell (f); //save start of file (since we might be inside a pak file, SEEK_SET might not be the start of the pcx)
|
|
|
|
fread(&pcx, sizeof(pcx), 1, f);
|
|
pcx.xmin = (unsigned short)LittleShort (pcx.xmin);
|
|
pcx.ymin = (unsigned short)LittleShort (pcx.ymin);
|
|
pcx.xmax = (unsigned short)LittleShort (pcx.xmax);
|
|
pcx.ymax = (unsigned short)LittleShort (pcx.ymax);
|
|
pcx.bytes_per_line = (unsigned short)LittleShort (pcx.bytes_per_line);
|
|
|
|
if (pcx.signature != 0x0A)
|
|
Sys_Error ("'%s' is not a valid PCX file", loadfilename);
|
|
|
|
if (pcx.version != 5)
|
|
Sys_Error ("'%s' is version %i, should be 5", loadfilename, pcx.version);
|
|
|
|
if (pcx.encoding != 1 || pcx.bits_per_pixel != 8 || pcx.color_planes != 1)
|
|
Sys_Error ("'%s' has wrong encoding or bit depth", loadfilename);
|
|
|
|
w = pcx.xmax - pcx.xmin + 1;
|
|
h = pcx.ymax - pcx.ymin + 1;
|
|
|
|
data = (byte *) Hunk_Alloc((w*h+1)*4); //+1 to allow reading padding byte on last line
|
|
|
|
//load palette
|
|
fseek (f, start + com_filesize - 768, SEEK_SET);
|
|
fread (palette, 1, 768, f);
|
|
|
|
//back to start of image data
|
|
fseek (f, start + sizeof(pcx), SEEK_SET);
|
|
|
|
buf = Buf_Alloc(f);
|
|
|
|
for (y=0; y<h; y++)
|
|
{
|
|
p = data + y * w * 4;
|
|
|
|
for (x=0; x<(pcx.bytes_per_line); ) //read the extra padding byte if necessary
|
|
{
|
|
readbyte = Buf_GetC(buf);
|
|
|
|
if(readbyte >= 0xC0)
|
|
{
|
|
runlength = readbyte & 0x3F;
|
|
readbyte = Buf_GetC(buf);
|
|
}
|
|
else
|
|
runlength = 1;
|
|
|
|
while(runlength--)
|
|
{
|
|
p[0] = palette[readbyte*3];
|
|
p[1] = palette[readbyte*3+1];
|
|
p[2] = palette[readbyte*3+2];
|
|
p[3] = 255;
|
|
p += 4;
|
|
x++;
|
|
}
|
|
}
|
|
}
|
|
|
|
Buf_Free(buf);
|
|
fclose(f);
|
|
|
|
*width = w;
|
|
*height = h;
|
|
return data;
|
|
}
|
|
|
|
//==============================================================================
|
|
//
|
|
// QPIC (aka '.lmp')
|
|
//
|
|
//==============================================================================
|
|
|
|
typedef struct
|
|
{
|
|
unsigned int width, height;
|
|
} lmpheader_t;
|
|
|
|
/*
|
|
============
|
|
Image_LoadLMP
|
|
============
|
|
*/
|
|
byte *Image_LoadLMP (FILE *f, int *width, int *height)
|
|
{
|
|
lmpheader_t qpic;
|
|
size_t pix;
|
|
void *data;
|
|
byte *src;
|
|
unsigned int *dest;
|
|
|
|
fread(&qpic, sizeof(qpic), 1, f);
|
|
qpic.width = LittleLong (qpic.width);
|
|
qpic.height = LittleLong (qpic.height);
|
|
|
|
pix = qpic.width*qpic.height;
|
|
|
|
if (com_filesize != 8+pix)
|
|
{
|
|
fclose(f);
|
|
return NULL;
|
|
}
|
|
|
|
data = (byte *) Hunk_Alloc(pix*4); //+1 to allow reading padding byte on last line
|
|
dest = data;
|
|
src = (byte *)data + pix*(4-1);
|
|
|
|
fread(src, 1, pix, f);
|
|
|
|
while(pix --> 0)
|
|
*dest++ = d_8to24table[*src++];
|
|
|
|
fclose(f);
|
|
|
|
*width = qpic.width;
|
|
*height = qpic.height;
|
|
return data;
|
|
}
|
|
|
|
//==============================================================================
|
|
//
|
|
// STB_IMAGE_WRITE
|
|
//
|
|
//==============================================================================
|
|
|
|
static byte *CopyFlipped(const byte *data, int width, int height, int bpp)
|
|
{
|
|
int y, rowsize;
|
|
byte *flipped;
|
|
|
|
rowsize = width * (bpp / 8);
|
|
flipped = (byte *) malloc(height * rowsize);
|
|
if (!flipped)
|
|
return NULL;
|
|
|
|
for (y=0; y<height; y++)
|
|
{
|
|
memcpy(&flipped[y * rowsize], &data[(height - 1 - y) * rowsize], rowsize);
|
|
}
|
|
return flipped;
|
|
}
|
|
|
|
/*
|
|
============
|
|
Image_WriteJPG -- writes using stb_image_write
|
|
|
|
returns true if successful
|
|
============
|
|
*/
|
|
qboolean Image_WriteJPG (const char *name, byte *data, int width, int height, int bpp, int quality, qboolean upsidedown)
|
|
{
|
|
unsigned error;
|
|
char pathname[MAX_OSPATH];
|
|
byte *flipped;
|
|
int bytes_per_pixel;
|
|
|
|
if (!(bpp == 32 || bpp == 24))
|
|
Sys_Error ("bpp not 24 or 32");
|
|
|
|
bytes_per_pixel = bpp / 8;
|
|
|
|
Sys_mkdir (com_gamedir); //if we've switched to a nonexistant gamedir, create it now so we don't crash
|
|
q_snprintf (pathname, sizeof(pathname), "%s/%s", com_gamedir, name);
|
|
|
|
if (!upsidedown)
|
|
{
|
|
flipped = CopyFlipped (data, width, height, bpp);
|
|
if (!flipped)
|
|
return false;
|
|
}
|
|
else
|
|
flipped = data;
|
|
|
|
error = stbi_write_jpg (pathname, width, height, bytes_per_pixel, flipped, quality);
|
|
if (!upsidedown)
|
|
free (flipped);
|
|
|
|
return (error != 0);
|
|
}
|
|
|
|
qboolean Image_WritePNG (const char *name, byte *data, int width, int height, int bpp, qboolean upsidedown)
|
|
{
|
|
unsigned error;
|
|
char pathname[MAX_OSPATH];
|
|
byte *flipped;
|
|
unsigned char *filters;
|
|
unsigned char *png;
|
|
size_t pngsize;
|
|
LodePNGState state;
|
|
|
|
if (!(bpp == 32 || bpp == 24))
|
|
Sys_Error("bpp not 24 or 32");
|
|
|
|
Sys_mkdir (com_gamedir); //if we've switched to a nonexistant gamedir, create it now so we don't crash
|
|
q_snprintf (pathname, sizeof(pathname), "%s/%s", com_gamedir, name);
|
|
|
|
flipped = (!upsidedown)? CopyFlipped (data, width, height, bpp) : data;
|
|
filters = (unsigned char *) malloc (height);
|
|
if (!filters || !flipped)
|
|
{
|
|
if (!upsidedown)
|
|
free (flipped);
|
|
free (filters);
|
|
return false;
|
|
}
|
|
|
|
// set some options for faster compression
|
|
lodepng_state_init(&state);
|
|
state.encoder.zlibsettings.use_lz77 = 0;
|
|
state.encoder.auto_convert = 0;
|
|
state.encoder.filter_strategy = LFS_PREDEFINED;
|
|
memset(filters, 1, height); //use filter 1; see https://www.w3.org/TR/PNG-Filters.html
|
|
state.encoder.predefined_filters = filters;
|
|
|
|
if (bpp == 24)
|
|
{
|
|
state.info_raw.colortype = LCT_RGB;
|
|
state.info_png.color.colortype = LCT_RGB;
|
|
}
|
|
else
|
|
{
|
|
state.info_raw.colortype = LCT_RGBA;
|
|
state.info_png.color.colortype = LCT_RGBA;
|
|
}
|
|
|
|
error = lodepng_encode (&png, &pngsize, flipped, width, height, &state);
|
|
if (error == 0) lodepng_save_file (png, pngsize, pathname);
|
|
#ifdef LODEPNG_COMPILE_ERROR_TEXT
|
|
else Con_Printf("WritePNG: %s\n", lodepng_error_text (error));
|
|
#endif
|
|
|
|
lodepng_state_cleanup (&state);
|
|
free (png);
|
|
free (filters);
|
|
if (!upsidedown)
|
|
free (flipped);
|
|
|
|
return (error == 0);
|
|
}
|