TextureData:encode returns Blob; rm core/png;

This commit is contained in:
bjorn 2020-10-26 02:22:43 -06:00
parent 1e7749a58a
commit 827dfda748
7 changed files with 137 additions and 159 deletions

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -1,136 +0,0 @@
#include "png.h"
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
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;
}

View File

@ -1,8 +0,0 @@
#include <stdint.h>
#include <stddef.h>
#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);

View File

@ -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) {

View File

@ -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);