From ae2124e71c0c6a2d1b93803af65107c3c8e22f1a Mon Sep 17 00:00:00 2001 From: Joel16 Date: Sun, 3 Nov 2019 17:50:16 -0500 Subject: [PATCH] Update stb_image and add pcx loader --- dr_pcx.h | 793 ++++++++++++++++++++++++++++++++++++++++++++++++++++ glib2d.c | 35 ++- glib2d.h | 13 +- stb_image.h | 20 +- 4 files changed, 844 insertions(+), 17 deletions(-) create mode 100644 dr_pcx.h diff --git a/dr_pcx.h b/dr_pcx.h new file mode 100644 index 0000000..04f9adc --- /dev/null +++ b/dr_pcx.h @@ -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 + +#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 +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 +#include +#include + +#ifndef DR_PCX_NO_STDIO +#include + +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 +*/ + diff --git a/glib2d.c b/glib2d.c index a9c8836..881c70e 100644 --- a/glib2d.c +++ b/glib2d.c @@ -19,12 +19,15 @@ #include "glib2d.h" -#include #include +#include #include #include #include +#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) diff --git a/glib2d.h b/glib2d.h index f1b47df..fe8c0c6 100644 --- a/glib2d.h +++ b/glib2d.h @@ -54,7 +54,6 @@ extern "C" { #endif #include -#include /** * \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. diff --git a/stb_image.h b/stb_image.h index a6202a3..196dfd5 100644 --- a/stb_image.h +++ b/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