mirror of https://github.com/bjornbytes/lovr.git
TextureData:encode returns Blob; rm core/png;
This commit is contained in:
parent
1e7749a58a
commit
827dfda748
|
@ -330,7 +330,6 @@ set(LOVR_SRC
|
||||||
src/core/arr.c
|
src/core/arr.c
|
||||||
src/core/fs.c
|
src/core/fs.c
|
||||||
src/core/map.c
|
src/core/map.c
|
||||||
src/core/png.c
|
|
||||||
src/core/ref.c
|
src/core/ref.c
|
||||||
src/core/utf.c
|
src/core/utf.c
|
||||||
src/core/util.c
|
src/core/util.c
|
||||||
|
|
1
Tupfile
1
Tupfile
|
@ -10,7 +10,6 @@ SRC += src/core/map.c
|
||||||
ifneq (@(PICO),y)
|
ifneq (@(PICO),y)
|
||||||
SRC += src/core/os_$(PLATFORM).c
|
SRC += src/core/os_$(PLATFORM).c
|
||||||
endif
|
endif
|
||||||
SRC += src/core/png.c
|
|
||||||
SRC += src/core/ref.c
|
SRC += src/core/ref.c
|
||||||
SRC += src/core/utf.c
|
SRC += src/core/utf.c
|
||||||
SRC += src/core/util.c
|
SRC += src/core/util.c
|
||||||
|
|
|
@ -3,9 +3,8 @@
|
||||||
|
|
||||||
static int l_lovrTextureDataEncode(lua_State* L) {
|
static int l_lovrTextureDataEncode(lua_State* L) {
|
||||||
TextureData* textureData = luax_checktype(L, 1, TextureData);
|
TextureData* textureData = luax_checktype(L, 1, TextureData);
|
||||||
const char* filename = luaL_checkstring(L, 2);
|
Blob* blob = lovrTextureDataEncode(textureData);
|
||||||
bool success = lovrTextureDataEncode(textureData, filename);
|
luax_pushtype(L, Blob, blob);
|
||||||
lua_pushboolean(L, success);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
136
src/core/png.c
136
src/core/png.c
|
@ -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;
|
|
||||||
}
|
|
|
@ -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);
|
|
|
@ -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");
|
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;
|
uint32_t w = textureData->width;
|
||||||
int32_t stride = -1 * (int) (textureData->width * 4);
|
uint32_t h = textureData->height;
|
||||||
size_t size;
|
uint8_t* pixels = (uint8_t*) textureData->blob->data + (h - 1) * w * 4;
|
||||||
void* data = png_encode(pixels, textureData->width, textureData->height, stride, &size);
|
int32_t stride = -1 * (int) (w * 4);
|
||||||
if (!data) return false;
|
|
||||||
lovrFilesystemWrite(filename, data, size, false);
|
// The world's worst png encoder
|
||||||
free(data);
|
// Encoding uses one unfiltered IDAT chunk, each row is an uncompressed huffman block
|
||||||
return true;
|
// 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) {
|
void lovrTextureDataPaste(TextureData* textureData, TextureData* source, uint32_t dx, uint32_t dy, uint32_t sx, uint32_t sy, uint32_t w, uint32_t h) {
|
||||||
|
|
|
@ -66,6 +66,6 @@ TextureData* lovrTextureDataInitFromBlob(TextureData* textureData, Blob* blob, b
|
||||||
#define lovrTextureDataCreateFromBlob(...) lovrTextureDataInitFromBlob(lovrAlloc(TextureData), __VA_ARGS__)
|
#define lovrTextureDataCreateFromBlob(...) lovrTextureDataInitFromBlob(lovrAlloc(TextureData), __VA_ARGS__)
|
||||||
Color lovrTextureDataGetPixel(TextureData* textureData, uint32_t x, uint32_t y);
|
Color lovrTextureDataGetPixel(TextureData* textureData, uint32_t x, uint32_t y);
|
||||||
void lovrTextureDataSetPixel(TextureData* textureData, uint32_t x, uint32_t y, Color color);
|
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 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);
|
void lovrTextureDataDestroy(void* ref);
|
||||||
|
|
Loading…
Reference in New Issue