solvespace/src/resource.cpp

125 lines
3.8 KiB
C++

//-----------------------------------------------------------------------------
// Discovery and loading of our resources (icons, fonts, templates, etc).
//
// Copyright 2016 whitequark
//-----------------------------------------------------------------------------
#include "solvespace.h"
#include <png.h>
namespace SolveSpace {
//-----------------------------------------------------------------------------
// Resource loading functions
//-----------------------------------------------------------------------------
std::string LoadString(const std::string &name) {
size_t size;
const void *data = LoadResource(name, &size);
if(data == NULL) oops();
return std::string(static_cast<const char *>(data), size);
}
Pixmap LoadPNG(const std::string &name) {
size_t size;
const void *data = LoadResource(name, &size);
if(data == NULL) oops();
Pixmap pixmap = Pixmap::FromPNG(static_cast<const uint8_t *>(data), size);
if(pixmap.IsEmpty()) oops();
return pixmap;
}
//-----------------------------------------------------------------------------
// Pixmap manipulation
//-----------------------------------------------------------------------------
void Pixmap::Clear() {
*this = {};
}
static Pixmap ReadPNGIntoPixmap(png_struct *png_ptr, png_info *info_ptr) {
png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_GRAY_TO_RGB, NULL);
Pixmap pixmap = {};
pixmap.width = png_get_image_width(png_ptr, info_ptr);
pixmap.height = png_get_image_height(png_ptr, info_ptr);
pixmap.hasAlpha = png_get_color_type(png_ptr, info_ptr) & PNG_COLOR_MASK_ALPHA;
size_t stride = pixmap.width * pixmap.GetBytesPerPixel();
if(stride % 4 != 0) stride += 4 - stride % 4;
pixmap.stride = stride;
pixmap.data = std::unique_ptr<uint8_t[]>(new uint8_t[pixmap.stride * pixmap.height]);
uint8_t **rows = png_get_rows(png_ptr, info_ptr);
for(size_t y = 0; y < pixmap.height; y++) {
memcpy(&pixmap.data[pixmap.stride * y], rows[y],
pixmap.width * pixmap.GetBytesPerPixel());
}
return pixmap;
}
Pixmap Pixmap::FromPNG(const uint8_t *data, size_t size) {
Pixmap pixmap = {};
struct Slice { const uint8_t *data; size_t size; };
Slice dataSlice = { data, size };
png_struct *png_ptr = NULL;
png_info *info_ptr = NULL;
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if(!png_ptr) goto exit;
info_ptr = png_create_info_struct(png_ptr);
if(!info_ptr) goto exit;
if(setjmp(png_jmpbuf(png_ptr))) goto exit;
png_set_read_fn(png_ptr, &dataSlice,
[](png_struct *png_ptr, uint8_t *data, size_t size) {
Slice *dataSlice = (Slice *)png_get_io_ptr(png_ptr);
if(size <= dataSlice->size) {
memcpy(data, dataSlice->data, size);
dataSlice->data += size;
dataSlice->size -= size;
} else {
png_error(png_ptr, "EOF");
}
});
pixmap = ReadPNGIntoPixmap(png_ptr, info_ptr);
exit:
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return pixmap;
}
Pixmap Pixmap::FromPNG(FILE *f) {
Pixmap pixmap = {};
png_struct *png_ptr = NULL;
png_info *info_ptr = NULL;
uint8_t header[8];
if(fread(header, 1, sizeof(header), f) != sizeof(header)) goto exit;
if(png_sig_cmp(header, 0, sizeof(header))) goto exit;
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if(!png_ptr) goto exit;
info_ptr = png_create_info_struct(png_ptr);
if(!info_ptr) goto exit;
if(setjmp(png_jmpbuf(png_ptr))) goto exit;
png_init_io(png_ptr, f);
png_set_sig_bytes(png_ptr, sizeof(header));
pixmap = ReadPNGIntoPixmap(png_ptr, info_ptr);
exit:
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return pixmap;
}
}