mirror of
https://github.com/AquariaOSE/Aquaria.git
synced 2024-11-29 03:33:48 +00:00
138 lines
3 KiB
C++
138 lines
3 KiB
C++
|
#include "Image.h"
|
||
|
#include "stb_image.h"
|
||
|
#include "stb_image_write.h"
|
||
|
#include "DeflateCompressor.h"
|
||
|
#include "ttvfs_stdio.h"
|
||
|
#include "Base.h"
|
||
|
|
||
|
bool pngSaveRGBA(const char *filename, size_t width, size_t height, unsigned char *data)
|
||
|
{
|
||
|
return !!stbi_write_png(filename, (int)width, (int)height, 4, data, width * 4);
|
||
|
}
|
||
|
|
||
|
bool tgaSaveRGBA(const char *filename, size_t width, size_t height, unsigned char *data)
|
||
|
{
|
||
|
return !!stbi_write_tga(filename, (int)width, (int)height, 4, data);
|
||
|
}
|
||
|
|
||
|
|
||
|
// Aquaria special: zlib-compressed TGA
|
||
|
bool zgaSaveRGBA(const char *filename, size_t w, size_t h, unsigned char *data)
|
||
|
{
|
||
|
ByteBuffer::uint8 type ,mode,aux, pixelDepth = 32;
|
||
|
ByteBuffer::uint8 cGarbage = 0;
|
||
|
ByteBuffer::uint16 iGarbage = 0;
|
||
|
ByteBuffer::uint16 width = w, height = h;
|
||
|
|
||
|
// open file and check for errors
|
||
|
FILE *file = fopen(filename, "wb");
|
||
|
if (file == NULL)
|
||
|
return false;
|
||
|
|
||
|
// compute image type: 2 for RGB(A), 3 for greyscale
|
||
|
mode = pixelDepth / 8;
|
||
|
if ((pixelDepth == 24) || (pixelDepth == 32))
|
||
|
type = 2;
|
||
|
else
|
||
|
type = 3;
|
||
|
|
||
|
// convert the image data from RGBA to BGRA
|
||
|
if (mode >= 3)
|
||
|
for (int i=0; i < width * height * mode ; i+= mode)
|
||
|
{
|
||
|
aux = data[i];
|
||
|
data[i] = data[i+2];
|
||
|
data[i+2] = aux;
|
||
|
}
|
||
|
|
||
|
ZlibCompressor z;
|
||
|
z.SetForceCompression(true);
|
||
|
z.reserve(width * height * mode + 30);
|
||
|
z << cGarbage
|
||
|
<< cGarbage
|
||
|
<< type
|
||
|
<< iGarbage
|
||
|
<< iGarbage
|
||
|
<< cGarbage
|
||
|
<< iGarbage
|
||
|
<< iGarbage
|
||
|
<< width
|
||
|
<< height
|
||
|
<< pixelDepth
|
||
|
<< cGarbage;
|
||
|
|
||
|
z.append(data, width * height * mode);
|
||
|
z.Compress(3);
|
||
|
|
||
|
bool ok = fwrite(z.contents(), 1, z.size(), file) == z.size();
|
||
|
|
||
|
fclose(file);
|
||
|
|
||
|
return ok;
|
||
|
}
|
||
|
|
||
|
|
||
|
static VFILE *imageLoadOpenFile(const char *filename)
|
||
|
{
|
||
|
return vfopen(filename, "rb");
|
||
|
}
|
||
|
|
||
|
// fill 'data' with 'size' bytes. return number of bytes actually read
|
||
|
static int cbread(void *user,char *data,int size)
|
||
|
{
|
||
|
VFILE *vf = (VFILE*)user;
|
||
|
return (int)vfread(data, 1, size, vf);
|
||
|
}
|
||
|
|
||
|
// skip the next 'n' bytes, or 'unget' the last -n bytes if negative
|
||
|
void cbskip(void *user,int n)
|
||
|
{
|
||
|
VFILE *vf = (VFILE*)user;
|
||
|
vfseek(vf, n, SEEK_CUR);
|
||
|
}
|
||
|
|
||
|
// returns nonzero if we are at end of file/data
|
||
|
int cbeof(void *user)
|
||
|
{
|
||
|
VFILE *vf = (VFILE*)user;
|
||
|
return vfeof(vf);
|
||
|
}
|
||
|
|
||
|
static const stbi_io_callbacks iocb = { cbread, cbskip, cbeof };
|
||
|
|
||
|
ImageData imageLoadGeneric(const char *filename, bool forceRGBA)
|
||
|
{
|
||
|
ImageData ret = {};
|
||
|
VFILE *fh = imageLoadOpenFile(filename);
|
||
|
if(fh)
|
||
|
{
|
||
|
int x = 0, y = 0, comp = 0;
|
||
|
stbi_uc *pix = stbi_load_from_callbacks(&iocb, fh, &x, &y, &comp, forceRGBA ? 4 : 0);
|
||
|
ret.pixels = pix;
|
||
|
ret.channels = forceRGBA ? 4 : comp;
|
||
|
ret.w = x;
|
||
|
ret.h = y;
|
||
|
vfclose(fh);
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
// this actually loads anything that looks fine after uncompressing the data
|
||
|
ImageData imageLoadZGA(const char *filename)
|
||
|
{
|
||
|
ImageData ret = {};
|
||
|
size_t size = 0;
|
||
|
char *buf = readCompressedFile(filename, &size);
|
||
|
if(buf)
|
||
|
{
|
||
|
int x = 0, y = 0, comp = 0;
|
||
|
stbi_uc *pix = stbi_load_from_memory((stbi_uc*)buf, size, &x, &y, &comp, 0);
|
||
|
delete [] buf;
|
||
|
ret.pixels = pix;
|
||
|
ret.channels = comp;
|
||
|
ret.w = x;
|
||
|
ret.h = y;
|
||
|
}
|
||
|
return ret;
|
||
|
}
|