mirror of
https://github.com/joel16/gLib2D.git
synced 2024-11-23 09:59:58 +00:00
Update stb_image and add pcx loader
This commit is contained in:
parent
3d86193279
commit
ae2124e71c
793
dr_pcx.h
Normal file
793
dr_pcx.h
Normal file
@ -0,0 +1,793 @@
|
||||
// PCX image loader. Public domain. See "unlicense" statement at the end of this file.
|
||||
// dr_pcx - v0.3.1 - 2018-09-11
|
||||
//
|
||||
// David Reid - mackron@gmail.com
|
||||
|
||||
// USAGE
|
||||
//
|
||||
// dr_pcx is a single-file library. To use it, do something like the following in one .c file.
|
||||
// #define DR_PCX_IMPLEMENTATION
|
||||
// #include "dr_pcx.h"
|
||||
//
|
||||
// You can then #include this file in other parts of the program as you would with any other header file. Do something like
|
||||
// the following to load and decode an image:
|
||||
//
|
||||
// int width;
|
||||
// int height;
|
||||
// int components
|
||||
// drpcx_uint8* pImageData = drpcx_load_file("my_image.pcx", DRPCX_FALSE, &width, &height, &components, 0);
|
||||
// if (pImageData == NULL) {
|
||||
// // Failed to load image.
|
||||
// }
|
||||
//
|
||||
// ...
|
||||
//
|
||||
// drpcx_free(pImageData);
|
||||
//
|
||||
// The boolean parameter (second argument in the above example) is whether or not the image should be flipped upside down.
|
||||
//
|
||||
//
|
||||
//
|
||||
// OPTIONS
|
||||
// #define these options before including this file.
|
||||
//
|
||||
// #define DR_PCX_NO_STDIO
|
||||
// Disable drpcx_load_file().
|
||||
//
|
||||
//
|
||||
//
|
||||
// QUICK NOTES
|
||||
// - 2-bpp/4-plane and 4-bpp/1-plane formats have not been tested.
|
||||
|
||||
#ifndef dr_pcx_h
|
||||
#define dr_pcx_h
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1600
|
||||
typedef signed char drpcx_int8;
|
||||
typedef unsigned char drpcx_uint8;
|
||||
typedef signed short drpcx_int16;
|
||||
typedef unsigned short drpcx_uint16;
|
||||
typedef signed int drpcx_int32;
|
||||
typedef unsigned int drpcx_uint32;
|
||||
typedef signed __int64 drpcx_int64;
|
||||
typedef unsigned __int64 drpcx_uint64;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
typedef int8_t drpcx_int8;
|
||||
typedef uint8_t drpcx_uint8;
|
||||
typedef int16_t drpcx_int16;
|
||||
typedef uint16_t drpcx_uint16;
|
||||
typedef int32_t drpcx_int32;
|
||||
typedef uint32_t drpcx_uint32;
|
||||
typedef int64_t drpcx_int64;
|
||||
typedef uint64_t drpcx_uint64;
|
||||
#endif
|
||||
typedef drpcx_uint8 drpcx_bool8;
|
||||
typedef drpcx_uint32 drpcx_bool32;
|
||||
#define DRPCX_TRUE 1
|
||||
#define DRPCX_FALSE 0
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Callback for when data is read. Return value is the number of bytes actually read.
|
||||
typedef size_t (* drpcx_read_proc)(void* userData, void* bufferOut, size_t bytesToRead);
|
||||
|
||||
|
||||
// Loads a PCX file using the given callbacks.
|
||||
drpcx_uint8* drpcx_load(drpcx_read_proc onRead, void* pUserData, drpcx_bool32 flipped, int* x, int* y, int* internalComponents, int desiredComponents);
|
||||
|
||||
// Frees memory returned by drpcx_load() and family.
|
||||
void drpcx_free(void* pReturnValueFromLoad);
|
||||
|
||||
|
||||
#ifndef DR_PCX_NO_STDIO
|
||||
// Loads an PCX file from an actual file.
|
||||
drpcx_uint8* drpcx_load_file(const char* filename, drpcx_bool32 flipped, int* x, int* y, int* internalComponents, int desiredComponents);
|
||||
#endif
|
||||
|
||||
// Helper for loading an PCX file from a block of memory.
|
||||
drpcx_uint8* drpcx_load_memory(const void* data, size_t dataSize, drpcx_bool32 flipped, int* x, int* y, int* internalComponents, int desiredComponents);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // dr_pcx_h
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPLEMENTATION
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifdef DR_PCX_IMPLEMENTATION
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#ifndef DR_PCX_NO_STDIO
|
||||
#include <stdio.h>
|
||||
|
||||
static size_t drpcx__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead)
|
||||
{
|
||||
return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData);
|
||||
}
|
||||
|
||||
drpcx_uint8* drpcx_load_file(const char* filename, drpcx_bool32 flipped, int* x, int* y, int* internalComponents, int desiredComponents)
|
||||
{
|
||||
FILE* pFile;
|
||||
#ifdef _MSC_VER
|
||||
if (fopen_s(&pFile, filename, "rb") != 0) {
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
pFile = fopen(filename, "rb");
|
||||
if (pFile == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
drpcx_uint8* pImageData = drpcx_load(drpcx__on_read_stdio, pFile, flipped, x, y, internalComponents, desiredComponents);
|
||||
|
||||
fclose(pFile);
|
||||
return pImageData;
|
||||
}
|
||||
#endif // DR_PCX_NO_STDIO
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// A pointer to the beginning of the data. We use a char as the type here for easy offsetting.
|
||||
const unsigned char* data;
|
||||
size_t dataSize;
|
||||
size_t currentReadPos;
|
||||
} drpcx_memory;
|
||||
|
||||
static size_t drpcx__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead)
|
||||
{
|
||||
drpcx_memory* memory = (drpcx_memory*)pUserData;
|
||||
assert(memory != NULL);
|
||||
assert(memory->dataSize >= memory->currentReadPos);
|
||||
|
||||
size_t bytesRemaining = memory->dataSize - memory->currentReadPos;
|
||||
if (bytesToRead > bytesRemaining) {
|
||||
bytesToRead = bytesRemaining;
|
||||
}
|
||||
|
||||
if (bytesToRead > 0) {
|
||||
memcpy(bufferOut, memory->data + memory->currentReadPos, bytesToRead);
|
||||
memory->currentReadPos += bytesToRead;
|
||||
}
|
||||
|
||||
return bytesToRead;
|
||||
}
|
||||
|
||||
drpcx_uint8* drpcx_load_memory(const void* data, size_t dataSize, drpcx_bool32 flipped, int* x, int* y, int* internalComponents, int desiredComponents)
|
||||
{
|
||||
drpcx_memory memory;
|
||||
memory.data = (const unsigned char*)data;
|
||||
memory.dataSize = dataSize;
|
||||
memory.currentReadPos = 0;
|
||||
return drpcx_load(drpcx__on_read_memory, &memory, flipped, x, y, internalComponents, desiredComponents);
|
||||
}
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
drpcx_uint8 header;
|
||||
drpcx_uint8 version;
|
||||
drpcx_uint8 encoding;
|
||||
drpcx_uint8 bpp;
|
||||
drpcx_uint16 left;
|
||||
drpcx_uint16 top;
|
||||
drpcx_uint16 right;
|
||||
drpcx_uint16 bottom;
|
||||
drpcx_uint16 hres;
|
||||
drpcx_uint16 vres;
|
||||
drpcx_uint8 palette16[48];
|
||||
drpcx_uint8 reserved1;
|
||||
drpcx_uint8 bitPlanes;
|
||||
drpcx_uint16 bytesPerLine;
|
||||
drpcx_uint16 paletteType;
|
||||
drpcx_uint16 screenSizeH;
|
||||
drpcx_uint16 screenSizeV;
|
||||
drpcx_uint8 reserved2[54];
|
||||
} drpcx_header;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
drpcx_read_proc onRead;
|
||||
void* pUserData;
|
||||
drpcx_bool32 flipped;
|
||||
drpcx_header header;
|
||||
|
||||
drpcx_uint32 width;
|
||||
drpcx_uint32 height;
|
||||
drpcx_uint32 components; // 3 = RGB; 4 = RGBA. Only 3 and 4 are supported.
|
||||
drpcx_uint8* pImageData;
|
||||
} drpcx;
|
||||
|
||||
|
||||
static drpcx_uint8 drpcx__read_byte(drpcx* pPCX)
|
||||
{
|
||||
drpcx_uint8 byte = 0;
|
||||
pPCX->onRead(pPCX->pUserData, &byte, 1);
|
||||
|
||||
return byte;
|
||||
}
|
||||
|
||||
static drpcx_uint8* drpcx__row_ptr(drpcx* pPCX, drpcx_uint32 row)
|
||||
{
|
||||
drpcx_uint32 stride = pPCX->width * pPCX->components;
|
||||
|
||||
drpcx_uint8* pRow = pPCX->pImageData;
|
||||
if (pPCX->flipped) {
|
||||
pRow += (pPCX->height - row - 1) * stride;
|
||||
} else {
|
||||
pRow += row * stride;
|
||||
}
|
||||
|
||||
return pRow;
|
||||
}
|
||||
|
||||
static drpcx_uint8 drpcx__rle(drpcx* pPCX, drpcx_uint8* pRLEValueOut)
|
||||
{
|
||||
drpcx_uint8 rleCount;
|
||||
drpcx_uint8 rleValue;
|
||||
|
||||
rleValue = drpcx__read_byte(pPCX);
|
||||
if ((rleValue & 0xC0) == 0xC0) {
|
||||
rleCount = rleValue & 0x3F;
|
||||
rleValue = drpcx__read_byte(pPCX);
|
||||
} else {
|
||||
rleCount = 1;
|
||||
}
|
||||
|
||||
|
||||
*pRLEValueOut = rleValue;
|
||||
return rleCount;
|
||||
}
|
||||
|
||||
|
||||
drpcx_bool32 drpcx__decode_1bit(drpcx* pPCX)
|
||||
{
|
||||
drpcx_uint8 rleCount = 0;
|
||||
drpcx_uint8 rleValue = 0;
|
||||
|
||||
switch (pPCX->header.bitPlanes)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
|
||||
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
|
||||
for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
|
||||
if (rleCount == 0) {
|
||||
rleCount = drpcx__rle(pPCX, &rleValue);
|
||||
}
|
||||
rleCount -= 1;
|
||||
|
||||
for (int bit = 0; (bit < 8) && ((x*8 + bit) < pPCX->width); ++bit) {
|
||||
drpcx_uint8 mask = (1 << (7 - bit));
|
||||
drpcx_uint8 paletteIndex = (rleValue & mask) >> (7 - bit);
|
||||
|
||||
pRow[0] = paletteIndex * 255;
|
||||
pRow[1] = paletteIndex * 255;
|
||||
pRow[2] = paletteIndex * 255;
|
||||
pRow += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return DRPCX_TRUE;
|
||||
|
||||
} break;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
{
|
||||
for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
|
||||
for (drpcx_uint32 c = 0; c < pPCX->header.bitPlanes; ++c) {
|
||||
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
|
||||
for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
|
||||
if (rleCount == 0) {
|
||||
rleCount = drpcx__rle(pPCX, &rleValue);
|
||||
}
|
||||
rleCount -= 1;
|
||||
|
||||
for (int bit = 0; (bit < 8) && ((x*8 + bit) < pPCX->width); ++bit) {
|
||||
drpcx_uint8 mask = (1 << (7 - bit));
|
||||
drpcx_uint8 paletteIndex = (rleValue & mask) >> (7 - bit);
|
||||
|
||||
pRow[0] |= ((paletteIndex & 0x01) << c);
|
||||
pRow += pPCX->components;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
|
||||
for (drpcx_uint32 x = 0; x < pPCX->width; ++x) {
|
||||
drpcx_uint8 paletteIndex = pRow[0];
|
||||
for (drpcx_uint32 c = 0; c < pPCX->components; ++c) {
|
||||
pRow[c] = pPCX->header.palette16[paletteIndex*3 + c];
|
||||
}
|
||||
|
||||
pRow += pPCX->components;
|
||||
}
|
||||
}
|
||||
|
||||
return DRPCX_TRUE;
|
||||
}
|
||||
|
||||
default: return DRPCX_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
drpcx_bool32 drpcx__decode_2bit(drpcx* pPCX)
|
||||
{
|
||||
drpcx_uint8 rleCount = 0;
|
||||
drpcx_uint8 rleValue = 0;
|
||||
|
||||
switch (pPCX->header.bitPlanes)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
drpcx_uint8 paletteCGA[48];
|
||||
paletteCGA[ 0] = 0x00; paletteCGA[ 1] = 0x00; paletteCGA[ 2] = 0x00; // #000000
|
||||
paletteCGA[ 3] = 0x00; paletteCGA[ 4] = 0x00; paletteCGA[ 5] = 0xAA; // #0000AA
|
||||
paletteCGA[ 6] = 0x00; paletteCGA[ 7] = 0xAA; paletteCGA[ 8] = 0x00; // #00AA00
|
||||
paletteCGA[ 9] = 0x00; paletteCGA[10] = 0xAA; paletteCGA[11] = 0xAA; // #00AAAA
|
||||
paletteCGA[12] = 0xAA; paletteCGA[13] = 0x00; paletteCGA[14] = 0x00; // #AA0000
|
||||
paletteCGA[15] = 0xAA; paletteCGA[16] = 0x00; paletteCGA[17] = 0xAA; // #AA00AA
|
||||
paletteCGA[18] = 0xAA; paletteCGA[19] = 0x55; paletteCGA[20] = 0x00; // #AA5500
|
||||
paletteCGA[21] = 0xAA; paletteCGA[22] = 0xAA; paletteCGA[23] = 0xAA; // #AAAAAA
|
||||
paletteCGA[24] = 0x55; paletteCGA[25] = 0x55; paletteCGA[26] = 0x55; // #555555
|
||||
paletteCGA[27] = 0x55; paletteCGA[28] = 0x55; paletteCGA[29] = 0xFF; // #5555FF
|
||||
paletteCGA[30] = 0x55; paletteCGA[31] = 0xFF; paletteCGA[32] = 0x55; // #55FF55
|
||||
paletteCGA[33] = 0x55; paletteCGA[34] = 0xFF; paletteCGA[35] = 0xFF; // #55FFFF
|
||||
paletteCGA[36] = 0xFF; paletteCGA[37] = 0x55; paletteCGA[38] = 0x55; // #FF5555
|
||||
paletteCGA[39] = 0xFF; paletteCGA[40] = 0x55; paletteCGA[41] = 0xFF; // #FF55FF
|
||||
paletteCGA[42] = 0xFF; paletteCGA[43] = 0xFF; paletteCGA[44] = 0x55; // #FFFF55
|
||||
paletteCGA[45] = 0xFF; paletteCGA[46] = 0xFF; paletteCGA[47] = 0xFF; // #FFFFFF
|
||||
|
||||
drpcx_uint8 cgaBGColor = pPCX->header.palette16[0] >> 4;
|
||||
drpcx_uint8 i = (pPCX->header.palette16[3] & 0x20) >> 5;
|
||||
drpcx_uint8 p = (pPCX->header.palette16[3] & 0x40) >> 6;
|
||||
//drpcx_uint8 c = (pPCX->header.palette16[3] & 0x80) >> 7; // Color or monochrome. How is monochrome handled?
|
||||
|
||||
for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
|
||||
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
|
||||
for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
|
||||
if (rleCount == 0) {
|
||||
rleCount = drpcx__rle(pPCX, &rleValue);
|
||||
}
|
||||
rleCount -= 1;
|
||||
|
||||
for (int bit = 0; bit < 4; ++bit) {
|
||||
if (x*4 + bit < pPCX->width) {
|
||||
drpcx_uint8 mask = (3 << ((3 - bit) * 2));
|
||||
drpcx_uint8 paletteIndex = (rleValue & mask) >> ((3 - bit) * 2);
|
||||
|
||||
drpcx_uint8 cgaIndex;
|
||||
if (paletteIndex == 0) { // Background.
|
||||
cgaIndex = cgaBGColor;
|
||||
} else { // Foreground
|
||||
cgaIndex = (((paletteIndex << 1) + p) + (i << 3));
|
||||
}
|
||||
|
||||
pRow[0] = paletteCGA[cgaIndex*3 + 0];
|
||||
pRow[1] = paletteCGA[cgaIndex*3 + 1];
|
||||
pRow[2] = paletteCGA[cgaIndex*3 + 2];
|
||||
pRow += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: According to http://www.fysnet.net/pcxfile.htm, we should use the palette at the end of the file
|
||||
// instead of the standard CGA palette if the version is equal to 5. With my test files the palette
|
||||
// at the end of the file does not exist. Research this one.
|
||||
if (pPCX->header.version == 5) {
|
||||
drpcx_uint8 paletteMarker = drpcx__read_byte(pPCX);
|
||||
if (paletteMarker == 0x0C) {
|
||||
// TODO: Implement Me.
|
||||
}
|
||||
}
|
||||
|
||||
return DRPCX_TRUE;
|
||||
};
|
||||
|
||||
case 4:
|
||||
{
|
||||
// NOTE: This is completely untested. If anybody knows where I can get a test file please let me know or send it through to me!
|
||||
// TODO: Test Me.
|
||||
|
||||
for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
|
||||
for (drpcx_uint32 c = 0; c < pPCX->header.bitPlanes; ++c) {
|
||||
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
|
||||
for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
|
||||
if (rleCount == 0) {
|
||||
rleCount = drpcx__rle(pPCX, &rleValue);
|
||||
}
|
||||
rleCount -= 1;
|
||||
|
||||
for (int bitpair = 0; (bitpair < 4) && ((x*4 + bitpair) < pPCX->width); ++bitpair) {
|
||||
drpcx_uint8 mask = (4 << (3 - bitpair));
|
||||
drpcx_uint8 paletteIndex = (rleValue & mask) >> (3 - bitpair);
|
||||
|
||||
pRow[0] |= ((paletteIndex & 0x03) << (c*2));
|
||||
pRow += pPCX->components;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
|
||||
for (drpcx_uint32 x = 0; x < pPCX->width; ++x) {
|
||||
drpcx_uint8 paletteIndex = pRow[0];
|
||||
for (drpcx_uint32 c = 0; c < pPCX->header.bitPlanes; ++c) {
|
||||
pRow[c] = pPCX->header.palette16[paletteIndex*3 + c];
|
||||
}
|
||||
|
||||
pRow += pPCX->components;
|
||||
}
|
||||
}
|
||||
|
||||
return DRPCX_TRUE;
|
||||
};
|
||||
|
||||
default: return DRPCX_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
drpcx_bool32 drpcx__decode_4bit(drpcx* pPCX)
|
||||
{
|
||||
// NOTE: This is completely untested. If anybody knows where I can get a test file please let me know or send it through to me!
|
||||
// TODO: Test Me.
|
||||
|
||||
if (pPCX->header.bitPlanes > 1) {
|
||||
return DRPCX_FALSE;
|
||||
}
|
||||
|
||||
drpcx_uint8 rleCount = 0;
|
||||
drpcx_uint8 rleValue = 0;
|
||||
|
||||
for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
|
||||
for (drpcx_uint32 c = 0; c < pPCX->header.bitPlanes; ++c) {
|
||||
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
|
||||
for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
|
||||
if (rleCount == 0) {
|
||||
rleCount = drpcx__rle(pPCX, &rleValue);
|
||||
}
|
||||
rleCount -= 1;
|
||||
|
||||
for (int nibble = 0; (nibble < 2) && ((x*2 + nibble) < pPCX->width); ++nibble)
|
||||
{
|
||||
drpcx_uint8 mask = (4 << (1 - nibble));
|
||||
drpcx_uint8 paletteIndex = (rleValue & mask) >> (1 - nibble);
|
||||
|
||||
pRow[0] |= ((paletteIndex & 0x0F) << (c*4));
|
||||
pRow += pPCX->components;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
|
||||
for (drpcx_uint32 x = 0; x < pPCX->width; ++x) {
|
||||
drpcx_uint8 paletteIndex = pRow[0];
|
||||
for (drpcx_uint32 c = 0; c < pPCX->components; ++c) {
|
||||
pRow[c] = pPCX->header.palette16[paletteIndex*3 + c];
|
||||
}
|
||||
|
||||
pRow += pPCX->components;
|
||||
}
|
||||
}
|
||||
|
||||
return DRPCX_TRUE;
|
||||
}
|
||||
|
||||
drpcx_bool32 drpcx__decode_8bit(drpcx* pPCX)
|
||||
{
|
||||
drpcx_uint8 rleCount = 0;
|
||||
drpcx_uint8 rleValue = 0;
|
||||
drpcx_uint32 stride = pPCX->width * pPCX->components;
|
||||
|
||||
switch (pPCX->header.bitPlanes)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
|
||||
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
|
||||
for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
|
||||
if (rleCount == 0) {
|
||||
rleCount = drpcx__rle(pPCX, &rleValue);
|
||||
}
|
||||
rleCount -= 1;
|
||||
|
||||
if (x < pPCX->width) {
|
||||
pRow[0] = rleValue;
|
||||
pRow[1] = rleValue;
|
||||
pRow[2] = rleValue;
|
||||
pRow += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// At this point we can know if we are dealing with a palette or a grayscale image by checking the next byte. If it's equal to 0x0C, we
|
||||
// need to do a simple palette lookup.
|
||||
drpcx_uint8 paletteMarker = drpcx__read_byte(pPCX);
|
||||
if (paletteMarker == 0x0C) {
|
||||
// A palette is present - we need to do a second pass.
|
||||
drpcx_uint8 palette256[768];
|
||||
if (pPCX->onRead(pPCX->pUserData, palette256, sizeof(palette256)) != sizeof(palette256)) {
|
||||
return DRPCX_FALSE;
|
||||
}
|
||||
|
||||
for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
|
||||
drpcx_uint8* pRow = pPCX->pImageData + (y * stride);
|
||||
for (drpcx_uint32 x = 0; x < pPCX->width; ++x) {
|
||||
drpcx_uint8 index = pRow[0];
|
||||
pRow[0] = palette256[index*3 + 0];
|
||||
pRow[1] = palette256[index*3 + 1];
|
||||
pRow[2] = palette256[index*3 + 2];
|
||||
pRow += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return DRPCX_TRUE;
|
||||
}
|
||||
|
||||
case 3:
|
||||
case 4:
|
||||
{
|
||||
for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
|
||||
for (drpcx_uint32 c = 0; c < pPCX->components; ++c) {
|
||||
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
|
||||
for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
|
||||
if (rleCount == 0) {
|
||||
rleCount = drpcx__rle(pPCX, &rleValue);
|
||||
}
|
||||
rleCount -= 1;
|
||||
|
||||
if (x < pPCX->width) {
|
||||
pRow[c] = rleValue;
|
||||
pRow += pPCX->components;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return DRPCX_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return DRPCX_TRUE;
|
||||
}
|
||||
|
||||
drpcx_uint8* drpcx_load(drpcx_read_proc onRead, void* pUserData, drpcx_bool32 flipped, int* x, int* y, int* internalComponents, int desiredComponents)
|
||||
{
|
||||
if (onRead == NULL) return NULL;
|
||||
if (desiredComponents > 4) return NULL;
|
||||
|
||||
drpcx pcx;
|
||||
pcx.onRead = onRead;
|
||||
pcx.pUserData = pUserData;
|
||||
pcx.flipped = flipped;
|
||||
if (onRead(pUserData, &pcx.header, sizeof(pcx.header)) != sizeof(pcx.header)) {
|
||||
return NULL; // Failed to read the header.
|
||||
}
|
||||
|
||||
if (pcx.header.header != 10) {
|
||||
return NULL; // Not a PCX file.
|
||||
}
|
||||
|
||||
if (pcx.header.encoding != 1) {
|
||||
return NULL; // Not supporting non-RLE encoding. Would assume a value of 0 indicates raw, unencoded, but that is apparently never used.
|
||||
}
|
||||
|
||||
if (pcx.header.bpp != 1 && pcx.header.bpp != 2 && pcx.header.bpp != 4 && pcx.header.bpp != 8) {
|
||||
return NULL; // Unsupported pixel format.
|
||||
}
|
||||
|
||||
|
||||
if (pcx.header.left > pcx.header.right) {
|
||||
drpcx_uint16 temp = pcx.header.left;
|
||||
pcx.header.left = pcx.header.right;
|
||||
pcx.header.right = temp;
|
||||
}
|
||||
if (pcx.header.top > pcx.header.bottom) {
|
||||
drpcx_uint16 temp = pcx.header.top;
|
||||
pcx.header.top = pcx.header.bottom;
|
||||
pcx.header.bottom = temp;
|
||||
}
|
||||
|
||||
pcx.width = pcx.header.right - pcx.header.left + 1;
|
||||
pcx.height = pcx.header.bottom - pcx.header.top + 1;
|
||||
pcx.components = (pcx.header.bpp == 8 && pcx.header.bitPlanes == 4) ? 4 : 3;
|
||||
|
||||
size_t dataSize = pcx.width * pcx.height * pcx.components;
|
||||
pcx.pImageData = (drpcx_uint8*)calloc(1, dataSize); // <-- Clearing to zero is important! Required for proper decoding.
|
||||
if (pcx.pImageData == NULL) {
|
||||
return NULL; // Failed to allocate memory.
|
||||
}
|
||||
|
||||
drpcx_bool32 result = DRPCX_FALSE;
|
||||
switch (pcx.header.bpp)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
result = drpcx__decode_1bit(&pcx);
|
||||
} break;
|
||||
|
||||
case 2:
|
||||
{
|
||||
result = drpcx__decode_2bit(&pcx);
|
||||
} break;
|
||||
|
||||
case 4:
|
||||
{
|
||||
result = drpcx__decode_4bit(&pcx);
|
||||
} break;
|
||||
|
||||
case 8:
|
||||
{
|
||||
result = drpcx__decode_8bit(&pcx);
|
||||
} break;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
free(pcx.pImageData);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// There's an annoying amount of branching when loading PCX files so for simplicity I'm doing the component conversion as
|
||||
// a second pass.
|
||||
if (desiredComponents == 0) desiredComponents = pcx.components;
|
||||
if (desiredComponents != (int)pcx.components) {
|
||||
drpcx_uint8* pNewImageData = (drpcx_uint8*)malloc(pcx.width * pcx.height * desiredComponents);
|
||||
if (pNewImageData == NULL) {
|
||||
free(pcx.pImageData);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
drpcx_uint8* pSrcData = pcx.pImageData;
|
||||
drpcx_uint8* pDstData = pNewImageData;
|
||||
if (desiredComponents < (int)pcx.components) {
|
||||
// We're reducing the number of components. Just drop the excess.
|
||||
for (drpcx_uint32 i = 0; i < pcx.width*pcx.height; ++i) {
|
||||
for (int c = 0; c < desiredComponents; ++c) {
|
||||
pDstData[c] = pSrcData[c];
|
||||
}
|
||||
|
||||
pSrcData += pcx.components;
|
||||
pDstData += desiredComponents;
|
||||
}
|
||||
} else {
|
||||
// We're increasing the number of components. Always ensure the alpha channel is set to 0xFF.
|
||||
if (pcx.components == 1) {
|
||||
for (drpcx_uint32 i = 0; i < pcx.width*pcx.height; ++i) {
|
||||
for (int c = 0; c < desiredComponents; ++c) {
|
||||
pDstData[c] = pSrcData[0];
|
||||
}
|
||||
|
||||
pSrcData += pcx.components;
|
||||
pDstData += desiredComponents;
|
||||
}
|
||||
} else if (pcx.components == 2) {
|
||||
for (drpcx_uint32 i = 0; i < pcx.width*pcx.height; ++i) {
|
||||
pDstData[0] = pSrcData[0];
|
||||
pDstData[1] = pSrcData[1];
|
||||
pDstData[2] = 0x00;
|
||||
if (desiredComponents == 4) pDstData[3] = 0xFF;
|
||||
|
||||
pSrcData += pcx.components;
|
||||
pDstData += desiredComponents;
|
||||
}
|
||||
} else {
|
||||
assert(pcx.components == 3);
|
||||
assert(desiredComponents == 4);
|
||||
for (drpcx_uint32 i = 0; i < pcx.width*pcx.height; ++i) {
|
||||
pDstData[0] = pSrcData[0];
|
||||
pDstData[1] = pSrcData[1];
|
||||
pDstData[2] = pSrcData[2];
|
||||
pDstData[3] = 0xFF;
|
||||
|
||||
pSrcData += pcx.components;
|
||||
pDstData += desiredComponents;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(pcx.pImageData);
|
||||
pcx.pImageData = pNewImageData;
|
||||
}
|
||||
|
||||
if (x) *x = pcx.width;
|
||||
if (y) *y = pcx.height;
|
||||
if (internalComponents) *internalComponents = pcx.components;
|
||||
return pcx.pImageData;
|
||||
}
|
||||
|
||||
void drpcx_free(void* pReturnValueFromLoad)
|
||||
{
|
||||
free(pReturnValueFromLoad);
|
||||
}
|
||||
|
||||
#endif // DR_PCX_IMPLEMENTATION
|
||||
|
||||
|
||||
// REVISION HISTORY
|
||||
//
|
||||
// v0.3.1 - 2018-09-11
|
||||
// - Styling fixes.
|
||||
// - Fix a typo.
|
||||
//
|
||||
// v0.3 - 2018-02-08
|
||||
// - API CHANGE: Rename dr_* types to drpcx_*.
|
||||
//
|
||||
// v0.2c - 2018-02-07
|
||||
// - Fix a crash.
|
||||
//
|
||||
// v0.2b - 2018-02-02
|
||||
// - Fix compilation error.
|
||||
//
|
||||
// v0.2a - 2017-07-16
|
||||
// - Change underlying type for booleans to unsigned.
|
||||
//
|
||||
// v0.2 - 2016-10-28
|
||||
// - API CHANGE: Add a parameter to drpcx_load() and family to control the number of output components.
|
||||
// - Use custom sized types rather than built-in ones to improve support for older MSVC compilers.
|
||||
//
|
||||
// v0.1c - 2016-10-23
|
||||
// - A minor change to drpcx_bool8 and drpcx_bool32 types.
|
||||
//
|
||||
// v0.1b - 2016-10-11
|
||||
// - Use drpcx_bool32 instead of the built-in "bool" type. The reason for this change is that it helps maintain API/ABI consistency
|
||||
// between C and C++ builds.
|
||||
//
|
||||
// v0.1a - 2016-09-18
|
||||
// - Change date format to ISO 8601 (YYYY-MM-DD)
|
||||
//
|
||||
// v0.1 - 2016-05-04
|
||||
// - Initial versioned release.
|
||||
|
||||
|
||||
// TODO
|
||||
// - Test 2-bpp/4-plane and 4-bpp/1-plane formats.
|
||||
|
||||
|
||||
/*
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
*/
|
||||
|
35
glib2d.c
35
glib2d.c
@ -19,12 +19,15 @@
|
||||
|
||||
#include "glib2d.h"
|
||||
|
||||
#include <pspkernel.h>
|
||||
#include <pspdisplay.h>
|
||||
#include <pspkernel.h>
|
||||
#include <pspgu.h>
|
||||
#include <vram.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#define DR_PCX_IMPLEMENTATION
|
||||
#include "dr_pcx.h"
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STBI_NO_HDR
|
||||
#define STBI_NO_PIC
|
||||
@ -1028,6 +1031,24 @@ void g2dTexFree(g2dTexture **tex) {
|
||||
*tex = NULL;
|
||||
}
|
||||
|
||||
static g2dTexture *_g2dTexLoadFilePCX(const char *path) {
|
||||
g2dTexture *tex = NULL;
|
||||
g2dColor *line = NULL;
|
||||
int width = 0, height = 0;
|
||||
u32 row = 0, col = 0;
|
||||
|
||||
line = (g2dColor *)drpcx_load_file(path, DRPCX_FALSE, &width, &height, NULL, PIXEL_SIZE);
|
||||
tex = g2dTexCreate(width, height);
|
||||
|
||||
for (row = 0; row < tex->w; row++) {
|
||||
for (col = 0; col < tex->h; col++)
|
||||
tex->data[row + col * tex->tw] = line[(row + col * tex->w)];
|
||||
}
|
||||
|
||||
drpcx_free(line);
|
||||
return tex;
|
||||
}
|
||||
|
||||
static g2dTexture *_g2dTexLoadFile(const char *path) {
|
||||
g2dTexture *tex = NULL;
|
||||
g2dColor *line = NULL;
|
||||
@ -1046,7 +1067,7 @@ static g2dTexture *_g2dTexLoadFile(const char *path) {
|
||||
return tex;
|
||||
}
|
||||
|
||||
static g2dTexture *_g2dTexLoadMemory(void *data, size_t size) {
|
||||
static g2dTexture *_g2dTexLoadMemory(void *data, int size) {
|
||||
g2dTexture *tex = NULL;
|
||||
g2dColor *line = NULL;
|
||||
int width = 0, height = 0;
|
||||
@ -1069,8 +1090,14 @@ g2dTexture *g2dTexLoad(char *path, g2dTex_Mode mode) {
|
||||
|
||||
if (path == NULL)
|
||||
return NULL;
|
||||
|
||||
char extension[5] = {0};
|
||||
strncpy(extension, &path[strlen(path) - 4], 4);
|
||||
|
||||
tex = _g2dTexLoadFile(path);
|
||||
if (!strncasecmp(extension, ".pcx", 4))
|
||||
tex = _g2dTexLoadFilePCX(path);
|
||||
else
|
||||
tex = _g2dTexLoadFile(path);
|
||||
|
||||
if (tex == NULL)
|
||||
goto error;
|
||||
@ -1100,7 +1127,7 @@ error:
|
||||
return tex;
|
||||
}
|
||||
|
||||
g2dTexture *g2dTexLoadMemory(void *data, size_t size, g2dTex_Mode mode) {
|
||||
g2dTexture *g2dTexLoadMemory(void *data, int size, g2dTex_Mode mode) {
|
||||
g2dTexture *tex = NULL;
|
||||
|
||||
if (data == NULL)
|
||||
|
13
glib2d.h
13
glib2d.h
@ -54,7 +54,6 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
* \def G2D_SCR_W
|
||||
@ -374,12 +373,10 @@ void g2dTexFree(g2dTexture **tex);
|
||||
* @param tex_mode A g2dTex_Mode constant.
|
||||
* @returns Pointer to the generated texture.
|
||||
*
|
||||
* This function loads an image file. There is support for PNG & JPEG files
|
||||
* (if USE_PNG and USE_JPEG are defined). Swizzling is enabled only for 16*16+
|
||||
* textures (useless on small textures), pass G2D_SWIZZLE to enable it.
|
||||
* Swizzling is enabled only for 16*16+ textures (useless on small textures), pass G2D_SWIZZLE to enable it.
|
||||
* Texture supported up to 512*512 in size only (hardware limitation).
|
||||
*/
|
||||
g2dTexture* g2dTexLoad(char path[], g2dTex_Mode mode);
|
||||
g2dTexture *g2dTexLoad(char path[], g2dTex_Mode mode);
|
||||
|
||||
/**
|
||||
* \brief Loads an image from memory.
|
||||
@ -388,12 +385,10 @@ g2dTexture* g2dTexLoad(char path[], g2dTex_Mode mode);
|
||||
* @param tex_mode A g2dTex_Mode constant.
|
||||
* @returns Pointer to the generated texture.
|
||||
*
|
||||
* This function loads an image file. There is support for PNG & JPEG files
|
||||
* (if USE_PNG and USE_JPEG are defined). Swizzling is enabled only for 16*16+
|
||||
* textures (useless on small textures), pass G2D_SWIZZLE to enable it.
|
||||
* Swizzling is enabled only for 16*16+ textures (useless on small textures), pass G2D_SWIZZLE to enable it.
|
||||
* Texture supported up to 512*512 in size only (hardware limitation).
|
||||
*/
|
||||
g2dTexture *g2dTexLoadMemory(void *data, size_t size, g2dTex_Mode mode);
|
||||
g2dTexture *g2dTexLoadMemory(void *data, int size, g2dTex_Mode mode);
|
||||
|
||||
/**
|
||||
* \brief Resets the current coordinates.
|
||||
|
20
stb_image.h
20
stb_image.h
@ -1,4 +1,4 @@
|
||||
/* stb_image - v2.22 - public domain image loader - http://nothings.org/stb
|
||||
/* stb_image - v2.23 - public domain image loader - http://nothings.org/stb
|
||||
no warranty implied; use at your own risk
|
||||
|
||||
Do this:
|
||||
@ -48,6 +48,7 @@ LICENSE
|
||||
|
||||
RECENT REVISION HISTORY:
|
||||
|
||||
2.23 (2019-08-11) fix clang static analysis warning
|
||||
2.22 (2019-03-04) gif fixes, fix warnings
|
||||
2.21 (2019-02-25) fix typo in comment
|
||||
2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs
|
||||
@ -5079,7 +5080,7 @@ static int stbi__high_bit(unsigned int z)
|
||||
if (z >= 0x00100) { n += 8; z >>= 8; }
|
||||
if (z >= 0x00010) { n += 4; z >>= 4; }
|
||||
if (z >= 0x00004) { n += 2; z >>= 2; }
|
||||
if (z >= 0x00002) { n += 1; z >>= 1; }
|
||||
if (z >= 0x00002) { n += 1;/* >>= 1;*/ }
|
||||
return n;
|
||||
}
|
||||
|
||||
@ -5237,7 +5238,10 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req
|
||||
psize = (info.offset - 14 - info.hsz) >> 2;
|
||||
}
|
||||
|
||||
s->img_n = ma ? 4 : 3;
|
||||
if (info.bpp == 24 && ma == 0xff000000)
|
||||
s->img_n = 3;
|
||||
else
|
||||
s->img_n = ma ? 4 : 3;
|
||||
if (req_comp && req_comp >= 3) // we can directly decode 3 or 4
|
||||
target = req_comp;
|
||||
else
|
||||
@ -5547,6 +5551,8 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req
|
||||
int RLE_repeating = 0;
|
||||
int read_next_pixel = 1;
|
||||
STBI_NOTUSED(ri);
|
||||
STBI_NOTUSED(tga_x_origin); // @TODO
|
||||
STBI_NOTUSED(tga_y_origin); // @TODO
|
||||
|
||||
// do a tiny bit of precessing
|
||||
if ( tga_image_type >= 8 )
|
||||
@ -5710,6 +5716,7 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req
|
||||
// Microsoft's C compilers happy... [8^(
|
||||
tga_palette_start = tga_palette_len = tga_palette_bits =
|
||||
tga_x_origin = tga_y_origin = 0;
|
||||
STBI_NOTUSED(tga_palette_start);
|
||||
// OK, done
|
||||
return tga_data;
|
||||
}
|
||||
@ -6936,7 +6943,12 @@ static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp)
|
||||
return 0;
|
||||
if (x) *x = s->img_x;
|
||||
if (y) *y = s->img_y;
|
||||
if (comp) *comp = info.ma ? 4 : 3;
|
||||
if (comp) {
|
||||
if (info.bpp == 24 && info.ma == 0xff000000)
|
||||
*comp = 3;
|
||||
else
|
||||
*comp = info.ma ? 4 : 3;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user