C library to convert any image into `.paa` texture file
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

291 lines
6.9 KiB

#include <stdlib.h>
#include <string.h>
#include <ilu.h>
#include <lzo/lzo1x.h>
#include "il_paa.h"
// globals
static ILHANDLE PAAFile;
static long OFFSOffset = 0;
// internal
ILboolean ilSavePAAInternal(void);
//! Writes a PAA file
ILenum ILAPIENTRY ilSavePAA(const char *FileName)
{
PAAFile = fopen(FileName, "w");
if (PAAFile == NULL) {
return IL_FILE_WRITE_ERROR;
}
if (ilSavePAAInternal() == IL_FALSE) {
return IL_INTERNAL_ERROR;
}
fclose(PAAFile);
PAAFile = NULL;
return IL_NO_ERROR;
}
void WriteTagg(ILconst_string name, ILuint size, ILubyte* data)
{
fwrite(PAA_TAGG_SIGNATURE, sizeof(int8_t), 4, PAAFile);
fwrite(name, sizeof(int8_t), 4, PAAFile);
fwrite(&size, sizeof(int32_t), 1, PAAFile);
fwrite(data, sizeof(int8_t), size, PAAFile);
}
ILubyte* GetAvgColor()
{
ILuint Image, TempImage;
ILubyte *Data;
ILint Size;
ILenum Format, Type;
ILuint NumPixel, R, G, B, A;
TempImage = Image = ilGetInteger(IL_CUR_IMAGE);
Format = ilGetInteger(IL_IMAGE_FORMAT);
Type = ilGetInteger(IL_IMAGE_TYPE);
if ((Type != IL_UNSIGNED_BYTE && Type != IL_BYTE) || Format == IL_COLOUR_INDEX) {
TempImage = ilGenImage();
ilCopyImage(Image);
ilBindImage(TempImage);
ilConvertImage(IL_UNSIGNED_BYTE, IL_RGBA);
}
Size = ilGetInteger(IL_IMAGE_SIZE_OF_DATA);
NumPixel = ilGetInteger(IL_IMAGE_WIDTH) * ilGetInteger(IL_IMAGE_HEIGHT);
Data = ilGetData();
R = G = B = A = 0;
for (int i = 0; i < Size; i += 4) {
R += Data[i];
G += Data[i+1];
B += Data[i+2];
A += Data[i+3];
}
ILubyte* color = malloc(sizeof(int8_t) * 4);
color[0] = R / NumPixel;
color[1] = G / NumPixel;
color[2] = B / NumPixel;
color[3] = A / NumPixel;
if (TempImage != Image)
{
ilDeleteImage(TempImage);
ilBindImage(Image);
}
return color;
}
void WriteMipmap(ILushort width, ILushort height, ILuint size, ILubyte* data)
{
ILubyte SizeInt24[3];
fwrite(&width, sizeof(int16_t), 1, PAAFile);
fwrite(&height, sizeof(int16_t), 1, PAAFile);
if (width && height) {
// convert ILuint to uint24
SizeInt24[0] = (ILubyte) (size & 0xFF);
SizeInt24[1] = (ILubyte) (size >> 8);
SizeInt24[2] = (ILubyte) (size >> 16);
fwrite(SizeInt24, sizeof(int8_t), 3, PAAFile);
fwrite(data, sizeof(int8_t), size, PAAFile);
}
}
ILboolean WritePAAHeader(void)
{
ILint DXTCFormat = ilGetInteger(IL_DXTC_FORMAT);
ILenum FlagTagg = PAA_TAGG_FLAG_NONE;
if (DXTCFormat == IL_DXT1) {
WritePAAType(PAA_TYPE_DXT1)
FlagTagg = PAA_TAGG_FLAG_ALPHA_NOT_INTERPOLATED;
} else if (DXTCFormat == IL_DXT5) {
WritePAAType(PAA_TYPE_DXT5)
FlagTagg = PAA_TAGG_FLAG_BASIC_TRANSPARENCY;
} else {
// Unsupported Format
return IL_FALSE;
}
// Write Taggs
WriteAVGCTagg()
WriteMAXCTagg()
if (FlagTagg != PAA_TAGG_FLAG_NONE) {
WriteFLAGTagg(FlagTagg);
}
// Write empty OFFS Tagg
OFFSOffset = ftell(PAAFile);
ILubyte *EmptyOffs = calloc(PAA_TAGG_OFFS_OFFSET_NUM, sizeof(int32_t));
if (EmptyOffs == NULL) {
return IL_FALSE;
}
WriteTagg(PAA_TAGG_OFFS, PAA_TAGG_OFFS_SIZE, EmptyOffs);
free(EmptyOffs);
// Write empty Palette
ILushort Palette = 0;
fwrite(&Palette, sizeof(int16_t), 1, PAAFile);
return IL_TRUE;
}
void CompleteOffsets(ILuint* offsets)
{
long Pos = ftell(PAAFile);
fseek(PAAFile, OFFSOffset, SEEK_SET);
WriteTagg(PAA_TAGG_OFFS, PAA_TAGG_OFFS_SIZE, (ILubyte *) offsets);
OFFSOffset = 0;
fseek(PAAFile, Pos, SEEK_SET);
}
ILubyte* LZOCompressDXT(ILubyte* Data, ILuint Size, lzo_uint *CompressedSize)
{
int r;
lzo_voidp WrkMem;
ILubyte *Buffer;
*CompressedSize = Size + Size / 16 + 64 + 3;
if (lzo_init() != LZO_E_OK) {
// TODO: Error logging
return NULL;
}
WrkMem = malloc(LZO1X_999_MEM_COMPRESS);
if (WrkMem == NULL) {
return NULL;
}
Buffer = malloc(*CompressedSize);
if (Buffer == NULL) {
free(WrkMem);
return NULL;
}
r = lzo1x_999_compress(Data, Size, Buffer, (lzo_uint *) CompressedSize, WrkMem);
free(WrkMem);
if (r != LZO_E_OK) {
free(Buffer);
// TODO: Error logging
return NULL;
}
if (*CompressedSize >= Size) {
free(Buffer);
// Return original data because it is smaller
*CompressedSize = 0;
return Data;
}
return Buffer;
}
ILboolean WriteImages(void)
{
ILuint Image, Offsets[PAA_TAGG_OFFS_OFFSET_NUM], Width, Height, DXTCSize;
ILint NumMipmaps, DXTCFormat;
ILubyte *DXTCData, *CompressedData;
ILushort End = 0;
lzo_uint CompressedSize;
if (!iluBuildMipmaps()) {
return IL_FALSE;
}
Image = ilGetInteger(IL_CUR_IMAGE);
DXTCFormat = ilGetInteger(IL_DXTC_FORMAT);
NumMipmaps = ilGetInteger(IL_NUM_MIPMAPS);
if (NumMipmaps > PAA_TAGG_OFFS_OFFSET_NUM) {
NumMipmaps = PAA_TAGG_OFFS_OFFSET_NUM;
}
memset(Offsets, 0, sizeof(int32_t) * PAA_TAGG_OFFS_OFFSET_NUM);
// Write MipMaps
for (int i = 0; i < NumMipmaps; ++i) {
ilBindImage(Image);
ilActiveMipmap(i);
Offsets[i] = ftell(PAAFile);
Width = ilGetInteger(IL_IMAGE_WIDTH);
Height = ilGetInteger(IL_IMAGE_HEIGHT);
DXTCData = ilCompressDXT(ilGetData(), Width, Height, 1, DXTCFormat, &DXTCSize);
if (DXTCData == NULL) {
return IL_FALSE;
}
// Compress only the first (PAA_LZO_MODE_COMPRESS_FIRST) Mipmap (original size)
if (i == 0) {
CompressedData = LZOCompressDXT(DXTCData, DXTCSize, &CompressedSize);
if (CompressedData == NULL) {
return IL_FALSE;
} else if (CompressedSize > 0) {
free(DXTCData);
DXTCData = CompressedData;
DXTCSize = CompressedSize;
Width |= 0x8000u;
}
}
WriteMipmap(Width, Height, DXTCSize, DXTCData);
// TODO: Remove Offset if MipMap was not written (Width or Height == 0), Handle abort case
}
// Create empty MipMap to mark end
WriteMipmap(0, 0, -1, NULL);
// Write last 2 zero bytes to mark the end
fwrite(&End, sizeof(int16_t), 1, PAAFile);
// Write offsets, now that we know
CompleteOffsets(Offsets);
return IL_TRUE;
}
ILboolean ilSavePAAInternal(void)
{
WritePAAHeader();
WriteImages();
return IL_TRUE;
}
// internal API
ILboolean il_paa_init(void)
{
ILboolean Register;
Register = ilRegisterSave("paa", (IL_SAVEPROC) &ilSavePAA);
// Register &= ilRegisterLoad("paa", (IL_LOADPROC) &ilLoadPAA);
return Register;
}
ILboolean il_paa_destroy(void)
{
ILboolean Remove;
Remove = ilRemoveSave("paa");
// Remove &= ilRemoveLoad("paa");
return Remove;
}