From 827dfda748d8b8a08a791ec5054eb9ce2f8795e9 Mon Sep 17 00:00:00 2001 From: bjorn Date: Mon, 26 Oct 2020 02:22:43 -0600 Subject: [PATCH] TextureData:encode returns Blob; rm core/png; --- CMakeLists.txt | 1 - Tupfile | 1 - src/api/l_data_textureData.c | 5 +- src/core/png.c | 136 ------------------------------- src/core/png.h | 8 -- src/modules/data/textureData.c | 143 ++++++++++++++++++++++++++++++--- src/modules/data/textureData.h | 2 +- 7 files changed, 137 insertions(+), 159 deletions(-) delete mode 100644 src/core/png.c delete mode 100644 src/core/png.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ed0c7f53..2fc9a562 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -330,7 +330,6 @@ set(LOVR_SRC src/core/arr.c src/core/fs.c src/core/map.c - src/core/png.c src/core/ref.c src/core/utf.c src/core/util.c diff --git a/Tupfile b/Tupfile index 9af51f3c..ca8fb6b1 100644 --- a/Tupfile +++ b/Tupfile @@ -10,7 +10,6 @@ SRC += src/core/map.c ifneq (@(PICO),y) SRC += src/core/os_$(PLATFORM).c endif -SRC += src/core/png.c SRC += src/core/ref.c SRC += src/core/utf.c SRC += src/core/util.c diff --git a/src/api/l_data_textureData.c b/src/api/l_data_textureData.c index 29f2a15a..1e4c80e8 100644 --- a/src/api/l_data_textureData.c +++ b/src/api/l_data_textureData.c @@ -3,9 +3,8 @@ static int l_lovrTextureDataEncode(lua_State* L) { TextureData* textureData = luax_checktype(L, 1, TextureData); - const char* filename = luaL_checkstring(L, 2); - bool success = lovrTextureDataEncode(textureData, filename); - lua_pushboolean(L, success); + Blob* blob = lovrTextureDataEncode(textureData); + luax_pushtype(L, Blob, blob); return 1; } diff --git a/src/core/png.c b/src/core/png.c deleted file mode 100644 index 211d9962..00000000 --- a/src/core/png.c +++ /dev/null @@ -1,136 +0,0 @@ -#include "png.h" -#include -#include -#include -#include - -static uint32_t crc_lookup[256]; -static bool crc_ready = false; -static void crc_init(void) { - if (!crc_ready) { - crc_ready = true; - for (uint32_t i = 0; i < 256; i++) { - uint32_t x = i; - for (uint32_t b = 0; b < 8; b++) { - if (x & 1) { - x = 0xedb88320L ^ (x >> 1); - } else { - x >>= 1; - } - crc_lookup[i] = x; - } - } - } -} - -static uint32_t crc32(uint8_t* data, size_t length) { - uint32_t c = 0xffffffff; - for (size_t i = 0; i < length; i++) c = crc_lookup[(c ^ data[i]) & 0xff] ^ (c >> 8); - return c ^ 0xffffffff; -} - -void* png_encode(uint8_t* pixels, uint32_t w, uint32_t h, int32_t stride, size_t* outputSize) { - uint8_t signature[] = { 137, 80, 78, 71, 13, 10, 26, 10 }; - uint8_t header[13] = { - w >> 24, w >> 16, w >> 8, w >> 0, - h >> 24, h >> 16, h >> 8, h >> 0, - 8, 6, 0, 0, 0 - }; - - // The png is encoded using one unfiltered IDAT chunk, each row is an uncompressed huffman block - // The total IDAT chunk data size is - // - 2 bytes for the zlib header - // - 6 bytes per block (5 bytes for the huffman block header + 1 byte scanline filter prefix) - // - n bytes for the image data itself (width * height * 4) - // - 4 bytes for the adler32 checksum - size_t rowSize = w * 4; - size_t imageSize = rowSize * h; - size_t blockSize = rowSize + 1; - size_t idatSize = 2 + (h * (5 + 1)) + imageSize + 4; - - *outputSize = sizeof(signature); // Signature - *outputSize += 4 + strlen("IHDR") + sizeof(header) + 4; - *outputSize += 4 + strlen("IDAT") + idatSize + 4; - *outputSize += 4 + strlen("IEND") + 4; - uint8_t* data = malloc(*outputSize); - if (data == NULL) { - return NULL; - } - - crc_init(); - uint32_t crc; - - // Signature - memcpy(data, signature, sizeof(signature)); - data += sizeof(signature); - - // IHDR - memcpy(data, (uint8_t[4]) { 0, 0, 0, sizeof(header) }, 4); - memcpy(data + 4, "IHDR", 4); - memcpy(data + 8, header, sizeof(header)); - crc = crc32(data + 4, 4 + sizeof(header)); - memcpy(data + 8 + sizeof(header), (uint8_t[4]) { crc >> 24, crc >> 16, crc >> 8, crc >> 0 }, 4); - data += 8 + sizeof(header) + 4; - - // IDAT - memcpy(data, (uint8_t[4]) { idatSize >> 24 & 0xff, idatSize >> 16 & 0xff, idatSize >> 8 & 0xff, idatSize >> 0 & 0xff }, 4); - memcpy(data + 4, "IDAT", 4); - - { - uint8_t* p = data + 8; - size_t length = imageSize; - - // adler32 counters - uint64_t s1 = 1, s2 = 0; - - // zlib header - *p++ = (7 << 4) + (8 << 0); - *p++ = 1; - - while (length >= rowSize) { - - // 1 indicates the final block - *p++ = (length == rowSize); - - // Write length and negated length - memcpy(p + 0, &(uint16_t) { blockSize & 0xffff }, 2); - memcpy(p + 2, &(uint16_t) { ~blockSize & 0xffff }, 2); - p += 4; - - // Write the filter method (0) and the row data - *p++ = 0x00; - memcpy(p, pixels, rowSize); - - // Update adler32 - s1 += 0; - s2 += s1; - for (size_t i = 0; i < rowSize; i++) { - s1 = (s1 + pixels[i]); - s2 = (s2 + s1); - } - s1 %= 65521; - s2 %= 65521; - - // Update cursors - p += rowSize; - pixels += stride; - length -= rowSize; - } - - // Write adler32 checksum - memcpy(p, (uint8_t[4]) { s2 >> 8, s2 >> 0, s1 >> 8, s1 >> 0 }, 4); - } - - crc = crc32(data + 4, idatSize + 4); - memcpy(data + 8 + idatSize, (uint8_t[4]) { crc >> 24, crc >> 16, crc >> 8, crc }, 4); - data += 8 + idatSize + 4; - - // IEND - memcpy(data, (uint8_t[4]) { 0 }, 4); - memcpy(data + 4, "IEND", 4); - crc = crc32(data + 4, 4); - memcpy(data + 8, (uint8_t[4]) { crc >> 24, crc >> 16, crc >> 8, crc >> 0 }, 4); - data += 8 + 4; - - return data - *outputSize; -} diff --git a/src/core/png.h b/src/core/png.h deleted file mode 100644 index 961a39e5..00000000 --- a/src/core/png.h +++ /dev/null @@ -1,8 +0,0 @@ -#include -#include - -#pragma once - -// The world's worst png encoder (uncompressed), for now just free the data when you're done - -void* png_encode(uint8_t* pixels, uint32_t width, uint32_t height, int32_t stride, size_t* outputSize); diff --git a/src/modules/data/textureData.c b/src/modules/data/textureData.c index f0cf7685..4950f70c 100644 --- a/src/modules/data/textureData.c +++ b/src/modules/data/textureData.c @@ -594,16 +594,141 @@ void lovrTextureDataSetPixel(TextureData* textureData, uint32_t x, uint32_t y, C } } -bool lovrTextureDataEncode(TextureData* textureData, const char* filename) { +static uint32_t crc_lookup[256]; +static bool crc_ready = false; +static void crc_init(void) { + if (!crc_ready) { + crc_ready = true; + for (uint32_t i = 0; i < 256; i++) { + uint32_t x = i; + for (uint32_t b = 0; b < 8; b++) { + if (x & 1) { + x = 0xedb88320L ^ (x >> 1); + } else { + x >>= 1; + } + crc_lookup[i] = x; + } + } + } +} + +static uint32_t crc32(uint8_t* data, size_t length) { + uint32_t c = 0xffffffff; + for (size_t i = 0; i < length; i++) c = crc_lookup[(c ^ data[i]) & 0xff] ^ (c >> 8); + return c ^ 0xffffffff; +} + +Blob* lovrTextureDataEncode(TextureData* textureData) { lovrAssert(textureData->format == FORMAT_RGBA, "Only RGBA TextureData can be encoded"); - uint8_t* pixels = (uint8_t*) textureData->blob->data + (textureData->height - 1) * textureData->width * 4; - int32_t stride = -1 * (int) (textureData->width * 4); - size_t size; - void* data = png_encode(pixels, textureData->width, textureData->height, stride, &size); - if (!data) return false; - lovrFilesystemWrite(filename, data, size, false); - free(data); - return true; + uint32_t w = textureData->width; + uint32_t h = textureData->height; + uint8_t* pixels = (uint8_t*) textureData->blob->data + (h - 1) * w * 4; + int32_t stride = -1 * (int) (w * 4); + + // The world's worst png encoder + // Encoding uses one unfiltered IDAT chunk, each row is an uncompressed huffman block + // The total IDAT chunk data size is + // - 2 bytes for the zlib header + // - 6 bytes per block (5 bytes for the huffman block header + 1 byte scanline filter prefix) + // - n bytes for the image data itself (width * height * 4) + // - 4 bytes for the adler32 checksum + + uint8_t signature[] = { 137, 80, 78, 71, 13, 10, 26, 10 }; + uint8_t header[13] = { + w >> 24, w >> 16, w >> 8, w >> 0, + h >> 24, h >> 16, h >> 8, h >> 0, + 8, 6, 0, 0, 0 + }; + + size_t rowSize = w * 4; + size_t imageSize = rowSize * h; + size_t blockSize = rowSize + 1; + size_t idatSize = 2 + (h * (5 + 1)) + imageSize + 4; + + size_t size = sizeof(signature); + size += 4 + strlen("IHDR") + sizeof(header) + 4; + size += 4 + strlen("IDAT") + idatSize + 4; + size += 4 + strlen("IEND") + 4; + uint8_t* data = malloc(size); + lovrAssert(data, "Out of memory"); + + crc_init(); + uint32_t crc; + + // Signature + memcpy(data, signature, sizeof(signature)); + data += sizeof(signature); + + // IHDR + memcpy(data, (uint8_t[4]) { 0, 0, 0, sizeof(header) }, 4); + memcpy(data + 4, "IHDR", 4); + memcpy(data + 8, header, sizeof(header)); + crc = crc32(data + 4, 4 + sizeof(header)); + memcpy(data + 8 + sizeof(header), (uint8_t[4]) { crc >> 24, crc >> 16, crc >> 8, crc >> 0 }, 4); + data += 8 + sizeof(header) + 4; + + // IDAT + memcpy(data, (uint8_t[4]) { idatSize >> 24 & 0xff, idatSize >> 16 & 0xff, idatSize >> 8 & 0xff, idatSize >> 0 & 0xff }, 4); + memcpy(data + 4, "IDAT", 4); + + { + uint8_t* p = data + 8; + size_t length = imageSize; + + // adler32 counters + uint64_t s1 = 1, s2 = 0; + + // zlib header + *p++ = (7 << 4) + (8 << 0); + *p++ = 1; + + while (length >= rowSize) { + + // 1 indicates the final block + *p++ = (length == rowSize); + + // Write length and negated length + memcpy(p + 0, &(uint16_t) { blockSize & 0xffff }, 2); + memcpy(p + 2, &(uint16_t) { ~blockSize & 0xffff }, 2); + p += 4; + + // Write the filter method (0) and the row data + *p++ = 0x00; + memcpy(p, pixels, rowSize); + + // Update adler32 + s1 += 0; + s2 += s1; + for (size_t i = 0; i < rowSize; i++) { + s1 = (s1 + pixels[i]); + s2 = (s2 + s1); + } + s1 %= 65521; + s2 %= 65521; + + // Update cursors + p += rowSize; + pixels += stride; + length -= rowSize; + } + + // Write adler32 checksum + memcpy(p, (uint8_t[4]) { s2 >> 8, s2 >> 0, s1 >> 8, s1 >> 0 }, 4); + } + + crc = crc32(data + 4, idatSize + 4); + memcpy(data + 8 + idatSize, (uint8_t[4]) { crc >> 24, crc >> 16, crc >> 8, crc }, 4); + data += 8 + idatSize + 4; + + // IEND + memcpy(data, (uint8_t[4]) { 0 }, 4); + memcpy(data + 4, "IEND", 4); + crc = crc32(data + 4, 4); + memcpy(data + 8, (uint8_t[4]) { crc >> 24, crc >> 16, crc >> 8, crc >> 0 }, 4); + data += 8 + 4; + + return lovrBlobCreate(data - size, size, "Encoded TextureData"); } void lovrTextureDataPaste(TextureData* textureData, TextureData* source, uint32_t dx, uint32_t dy, uint32_t sx, uint32_t sy, uint32_t w, uint32_t h) { diff --git a/src/modules/data/textureData.h b/src/modules/data/textureData.h index 9e2d62f2..a66dce20 100644 --- a/src/modules/data/textureData.h +++ b/src/modules/data/textureData.h @@ -66,6 +66,6 @@ TextureData* lovrTextureDataInitFromBlob(TextureData* textureData, Blob* blob, b #define lovrTextureDataCreateFromBlob(...) lovrTextureDataInitFromBlob(lovrAlloc(TextureData), __VA_ARGS__) Color lovrTextureDataGetPixel(TextureData* textureData, uint32_t x, uint32_t y); void lovrTextureDataSetPixel(TextureData* textureData, uint32_t x, uint32_t y, Color color); -bool lovrTextureDataEncode(TextureData* textureData, const char* filename); +Blob* lovrTextureDataEncode(TextureData* textureData); void lovrTextureDataPaste(TextureData* textureData, TextureData* source, uint32_t dx, uint32_t dy, uint32_t sx, uint32_t sy, uint32_t w, uint32_t h); void lovrTextureDataDestroy(void* ref);