core/png; rm stb_image_write;

This commit is contained in:
bjorn 2020-02-16 04:11:55 -08:00
parent 7f205d2fb1
commit 47f4319ba8
7 changed files with 156 additions and 1639 deletions

View File

@ -325,6 +325,7 @@ set(LOVR_SRC
src/core/fs.c
src/core/maf.c
src/core/map.c
src/core/png.c
src/core/ref.c
src/core/utf.c
src/core/util.c
@ -391,7 +392,6 @@ if(LOVR_ENABLE_DATA)
src/api/l_data_soundData.c
src/api/l_data_textureData.c
src/lib/stb/stb_image.c
src/lib/stb/stb_image_write.c
src/lib/stb/stb_truetype.c
src/lib/stb/stb_vorbis.c
src/lib/jsmn/jsmn.c

View File

@ -7,6 +7,7 @@ SRC += src/core/fs.c
SRC += src/core/maf.c
SRC += src/core/map.c
SRC += src/core/os_$(PLATFORM).c
SRC += src/core/png.c
SRC += src/core/ref.c
SRC += src/core/utf.c
SRC += src/core/util.c

136
src/core/png.c Normal file
View File

@ -0,0 +1,136 @@
#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, idatSize >> 16, idatSize >> 8, idatSize >> 0 }, 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}, 2);
memcpy(p + 2, &(uint16_t) {~blockSize}, 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 + 8, idatSize);
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;
}

8
src/core/png.h Normal file
View File

@ -0,0 +1,8 @@
#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

@ -1,3 +0,0 @@
#define STB_IMAGE_WRITE_IMPLEMENTATION
#define STBI_WRITE_NO_STDIO
#include "stb_image_write.h"

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
#include "data/textureData.h"
#include "filesystem/filesystem.h"
#include "core/png.h"
#include "core/ref.h"
#include "lib/stb/stb_image.h"
#include "lib/stb/stb_image_write.h"
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
@ -565,19 +565,16 @@ void lovrTextureDataSetPixel(TextureData* textureData, uint32_t x, uint32_t y, C
}
}
static void writeCallback(void* context, void* data, int size) {
const char* filename = context;
lovrFilesystemWrite(filename, data, size, false);
}
bool lovrTextureDataEncode(TextureData* textureData, const char* filename) {
lovrAssert(textureData->format == FORMAT_RGB || textureData->format == FORMAT_RGBA, "Only RGB and RGBA TextureData can be encoded");
int components = textureData->format == FORMAT_RGB ? 3 : 4;
int width = textureData->width;
int height = textureData->height;
void* data = (uint8_t*) textureData->blob->data + (textureData->height - 1) * textureData->width * components;
int stride = -1 * (int) (textureData->width * components);
return stbi_write_png_to_func(writeCallback, (void*) filename, width, height, components, data, stride);
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;
}
void lovrTextureDataPaste(TextureData* textureData, TextureData* source, uint32_t dx, uint32_t dy, uint32_t sx, uint32_t sy, uint32_t w, uint32_t h) {