pixmap: refactor
* Split conversion-related code into a separate file. * Renamed loaders to fileformat handlers. * Added API for exporting into one of the supported file formats. * Replaced serialization APIs with an "internal" fileformat capable of of storing any Pixmap. It is identical to the serialization format. * Removed PNG writing code from video_screenshot_task, now using the new pixmap export API instead.
This commit is contained in:
parent
1840c85273
commit
b4e07470d8
14 changed files with 741 additions and 522 deletions
358
src/pixmap/conversion.c
Normal file
358
src/pixmap/conversion.c
Normal file
|
@ -0,0 +1,358 @@
|
|||
/*
|
||||
* This software is licensed under the terms of the MIT License.
|
||||
* See COPYING for further information.
|
||||
* ---
|
||||
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
|
||||
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
|
||||
*/
|
||||
|
||||
#include "taisei.h"
|
||||
|
||||
#include "pixmap.h"
|
||||
#include "util.h"
|
||||
|
||||
// NOTE: this is pretty stupid and not at all optimized, patches welcome
|
||||
|
||||
#define _CONV_FUNCNAME convert_u8_to_u8
|
||||
#define _CONV_IN_MAX UINT8_MAX
|
||||
#define _CONV_IN_TYPE uint8_t
|
||||
#define _CONV_OUT_MAX UINT8_MAX
|
||||
#define _CONV_OUT_TYPE uint8_t
|
||||
#include "pixmap_conversion.inc.h"
|
||||
|
||||
#define _CONV_FUNCNAME convert_u8_to_u16
|
||||
#define _CONV_IN_MAX UINT8_MAX
|
||||
#define _CONV_IN_TYPE uint8_t
|
||||
#define _CONV_OUT_MAX UINT16_MAX
|
||||
#define _CONV_OUT_TYPE uint16_t
|
||||
#include "pixmap_conversion.inc.h"
|
||||
|
||||
#define _CONV_FUNCNAME convert_u8_to_u32
|
||||
#define _CONV_IN_MAX UINT8_MAX
|
||||
#define _CONV_IN_TYPE uint8_t
|
||||
#define _CONV_OUT_MAX UINT32_MAX
|
||||
#define _CONV_OUT_TYPE uint32_t
|
||||
#include "pixmap_conversion.inc.h"
|
||||
|
||||
#define _CONV_FUNCNAME convert_u8_to_f32
|
||||
#define _CONV_IN_MAX UINT8_MAX
|
||||
#define _CONV_IN_TYPE uint8_t
|
||||
#define _CONV_OUT_MAX 1.0f
|
||||
#define _CONV_OUT_TYPE float
|
||||
#include "pixmap_conversion.inc.h"
|
||||
|
||||
#define _CONV_FUNCNAME convert_u16_to_u8
|
||||
#define _CONV_IN_MAX UINT16_MAX
|
||||
#define _CONV_IN_TYPE uint16_t
|
||||
#define _CONV_OUT_MAX UINT8_MAX
|
||||
#define _CONV_OUT_TYPE uint8_t
|
||||
#include "pixmap_conversion.inc.h"
|
||||
|
||||
#define _CONV_FUNCNAME convert_u16_to_u16
|
||||
#define _CONV_IN_MAX UINT16_MAX
|
||||
#define _CONV_IN_TYPE uint16_t
|
||||
#define _CONV_OUT_MAX UINT16_MAX
|
||||
#define _CONV_OUT_TYPE uint16_t
|
||||
#include "pixmap_conversion.inc.h"
|
||||
|
||||
#define _CONV_FUNCNAME convert_u16_to_u32
|
||||
#define _CONV_IN_MAX UINT16_MAX
|
||||
#define _CONV_IN_TYPE uint16_t
|
||||
#define _CONV_OUT_MAX UINT32_MAX
|
||||
#define _CONV_OUT_TYPE uint32_t
|
||||
#include "pixmap_conversion.inc.h"
|
||||
|
||||
#define _CONV_FUNCNAME convert_u16_to_f32
|
||||
#define _CONV_IN_MAX UINT16_MAX
|
||||
#define _CONV_IN_TYPE uint16_t
|
||||
#define _CONV_OUT_MAX 1.0f
|
||||
#define _CONV_OUT_TYPE float
|
||||
#include "pixmap_conversion.inc.h"
|
||||
|
||||
#define _CONV_FUNCNAME convert_u32_to_u8
|
||||
#define _CONV_IN_MAX UINT32_MAX
|
||||
#define _CONV_IN_TYPE uint32_t
|
||||
#define _CONV_OUT_MAX UINT8_MAX
|
||||
#define _CONV_OUT_TYPE uint8_t
|
||||
#include "pixmap_conversion.inc.h"
|
||||
|
||||
#define _CONV_FUNCNAME convert_u32_to_u16
|
||||
#define _CONV_IN_MAX UINT32_MAX
|
||||
#define _CONV_IN_TYPE uint32_t
|
||||
#define _CONV_OUT_MAX UINT16_MAX
|
||||
#define _CONV_OUT_TYPE uint16_t
|
||||
#include "pixmap_conversion.inc.h"
|
||||
|
||||
#define _CONV_FUNCNAME convert_u32_to_u32
|
||||
#define _CONV_IN_MAX UINT32_MAX
|
||||
#define _CONV_IN_TYPE uint32_t
|
||||
#define _CONV_OUT_MAX UINT32_MAX
|
||||
#define _CONV_OUT_TYPE uint32_t
|
||||
#include "pixmap_conversion.inc.h"
|
||||
|
||||
#define _CONV_FUNCNAME convert_u32_to_f32
|
||||
#define _CONV_IN_MAX UINT32_MAX
|
||||
#define _CONV_IN_TYPE uint32_t
|
||||
#define _CONV_OUT_MAX 1.0f
|
||||
#define _CONV_OUT_TYPE float
|
||||
#include "pixmap_conversion.inc.h"
|
||||
|
||||
#define _CONV_FUNCNAME convert_f32_to_u8
|
||||
#define _CONV_IN_MAX 1.0f
|
||||
#define _CONV_IN_TYPE float
|
||||
#define _CONV_OUT_MAX UINT8_MAX
|
||||
#define _CONV_OUT_TYPE uint8_t
|
||||
#include "pixmap_conversion.inc.h"
|
||||
|
||||
#define _CONV_FUNCNAME convert_f32_to_u16
|
||||
#define _CONV_IN_MAX 1.0f
|
||||
#define _CONV_IN_TYPE float
|
||||
#define _CONV_OUT_MAX UINT16_MAX
|
||||
#define _CONV_OUT_TYPE uint16_t
|
||||
#include "pixmap_conversion.inc.h"
|
||||
|
||||
#define _CONV_FUNCNAME convert_f32_to_u32
|
||||
#define _CONV_IN_MAX 1.0f
|
||||
#define _CONV_IN_TYPE float
|
||||
#define _CONV_OUT_MAX UINT32_MAX
|
||||
#define _CONV_OUT_TYPE uint32_t
|
||||
#include "pixmap_conversion.inc.h"
|
||||
|
||||
#define _CONV_FUNCNAME convert_f32_to_f32
|
||||
#define _CONV_IN_MAX 1.0f
|
||||
#define _CONV_IN_TYPE float
|
||||
#define _CONV_OUT_MAX 1.0f
|
||||
#define _CONV_OUT_TYPE float
|
||||
#include "pixmap_conversion.inc.h"
|
||||
|
||||
#define DEPTH_FLOAT_BIT (1 << 10)
|
||||
|
||||
typedef void (*convfunc_t)(
|
||||
size_t in_elements,
|
||||
size_t out_elements,
|
||||
size_t num_pixels,
|
||||
void *vbuf_in,
|
||||
void *vbuf_out,
|
||||
int swizzle[4]
|
||||
);
|
||||
|
||||
#define CONV(in, in_depth, out, out_depth) { convert_##in##_to_##out, in_depth, out_depth }
|
||||
|
||||
struct conversion_def {
|
||||
convfunc_t func;
|
||||
uint depth_in;
|
||||
uint depth_out;
|
||||
};
|
||||
|
||||
struct conversion_def conversion_table[] = {
|
||||
CONV(u8, 8, u8, 8),
|
||||
CONV(u8, 8, u16, 16),
|
||||
CONV(u8, 8, u32, 32),
|
||||
CONV(u8, 8, f32, 32 | DEPTH_FLOAT_BIT),
|
||||
|
||||
CONV(u16, 16, u8, 8),
|
||||
CONV(u16, 16, u16, 16),
|
||||
CONV(u16, 16, u32, 32),
|
||||
CONV(u16, 16, f32, 32 | DEPTH_FLOAT_BIT),
|
||||
|
||||
CONV(u32, 32, u8, 8),
|
||||
CONV(u32, 16, u16, 16),
|
||||
CONV(u32, 32, u32, 32),
|
||||
CONV(u32, 32, f32, 32 | DEPTH_FLOAT_BIT),
|
||||
|
||||
CONV(f32, 32 | DEPTH_FLOAT_BIT, u8, 8),
|
||||
CONV(f32, 16 | DEPTH_FLOAT_BIT, u16, 16),
|
||||
CONV(f32, 32 | DEPTH_FLOAT_BIT, u32, 32),
|
||||
CONV(f32, 32 | DEPTH_FLOAT_BIT, f32, 32 | DEPTH_FLOAT_BIT),
|
||||
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static struct conversion_def* find_conversion(uint depth_in, uint depth_out) {
|
||||
for(struct conversion_def *cv = conversion_table; cv->func; ++cv) {
|
||||
if(cv->depth_in == depth_in && cv->depth_out == depth_out) {
|
||||
return cv;
|
||||
}
|
||||
}
|
||||
|
||||
log_fatal("Pixmap conversion for %upbc -> %upbc undefined, please add", depth_in, depth_out);
|
||||
}
|
||||
|
||||
static void pixmap_copy_meta(const Pixmap *src, Pixmap *dst) {
|
||||
dst->format = src->format;
|
||||
dst->width = src->width;
|
||||
dst->height = src->height;
|
||||
dst->origin = src->origin;
|
||||
}
|
||||
|
||||
void pixmap_copy(const Pixmap *src, Pixmap *dst) {
|
||||
assert(dst->data.untyped != NULL);
|
||||
assert(src->data_size > 0);
|
||||
assert(dst->data_size >= src->data_size);
|
||||
|
||||
if(!pixmap_format_is_compressed(src->format)) {
|
||||
assert(src->data_size == pixmap_data_size(src->format, src->width, src->height));
|
||||
}
|
||||
|
||||
pixmap_copy_meta(src, dst);
|
||||
memcpy(dst->data.untyped, src->data.untyped, src->data_size);
|
||||
}
|
||||
|
||||
void pixmap_copy_alloc(const Pixmap *src, Pixmap *dst) {
|
||||
dst->data.untyped = pixmap_alloc_buffer_for_copy(src, &dst->data_size);
|
||||
pixmap_copy(src, dst);
|
||||
}
|
||||
|
||||
void pixmap_convert(const Pixmap *src, Pixmap *dst, PixmapFormat format) {
|
||||
size_t num_pixels = src->width * src->height;
|
||||
size_t pixel_size = PIXMAP_FORMAT_PIXEL_SIZE(format);
|
||||
|
||||
assert(dst->data.untyped != NULL);
|
||||
pixmap_copy_meta(src, dst);
|
||||
|
||||
if(src->format == format) {
|
||||
memcpy(dst->data.untyped, src->data.untyped, num_pixels * pixel_size);
|
||||
return;
|
||||
}
|
||||
|
||||
dst->format = format;
|
||||
|
||||
struct conversion_def *cv = find_conversion(
|
||||
PIXMAP_FORMAT_DEPTH(src->format) | (PIXMAP_FORMAT_IS_FLOAT(src->format) * DEPTH_FLOAT_BIT),
|
||||
PIXMAP_FORMAT_DEPTH(dst->format) | (PIXMAP_FORMAT_IS_FLOAT(dst->format) * DEPTH_FLOAT_BIT)
|
||||
);
|
||||
|
||||
cv->func(
|
||||
PIXMAP_FORMAT_LAYOUT(src->format),
|
||||
PIXMAP_FORMAT_LAYOUT(dst->format),
|
||||
num_pixels,
|
||||
src->data.untyped,
|
||||
dst->data.untyped,
|
||||
NULL
|
||||
);
|
||||
}
|
||||
|
||||
static int swizzle_idx(char s) {
|
||||
switch(s) {
|
||||
case 'r': return 0;
|
||||
case 'g': return 1;
|
||||
case 'b': return 2;
|
||||
case 'a': return 3;
|
||||
case '0': return 4;
|
||||
case '1': return 5;
|
||||
default: UNREACHABLE;
|
||||
}
|
||||
}
|
||||
|
||||
void pixmap_swizzle_inplace(Pixmap *px, SwizzleMask swizzle) {
|
||||
uint channels = pixmap_format_layout(px->format);
|
||||
swizzle = swizzle_canonize(swizzle);
|
||||
|
||||
if(!swizzle_is_significant(swizzle, channels)) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint cvt_id = pixmap_format_depth(px->format) | (pixmap_format_is_float(px->format) * DEPTH_FLOAT_BIT);
|
||||
struct conversion_def *cv = find_conversion(cvt_id, cvt_id);
|
||||
|
||||
cv->func(
|
||||
channels,
|
||||
channels,
|
||||
px->width * px->height,
|
||||
px->data.untyped,
|
||||
px->data.untyped,
|
||||
(int[]) {
|
||||
swizzle_idx(swizzle.r),
|
||||
swizzle_idx(swizzle.g),
|
||||
swizzle_idx(swizzle.b),
|
||||
swizzle_idx(swizzle.a),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void pixmap_convert_alloc(const Pixmap *src, Pixmap *dst, PixmapFormat format) {
|
||||
dst->data.untyped = pixmap_alloc_buffer_for_conversion(src, format, &dst->data_size);
|
||||
pixmap_convert(src, dst, format);
|
||||
}
|
||||
|
||||
void pixmap_convert_inplace_realloc(Pixmap *src, PixmapFormat format) {
|
||||
assert(src->data.untyped != NULL);
|
||||
|
||||
if(src->format == format) {
|
||||
return;
|
||||
}
|
||||
|
||||
Pixmap tmp;
|
||||
pixmap_copy_meta(src, &tmp);
|
||||
pixmap_convert_alloc(src, &tmp, format);
|
||||
|
||||
free(src->data.untyped);
|
||||
*src = tmp;
|
||||
}
|
||||
|
||||
void pixmap_flip_y(const Pixmap *src, Pixmap *dst) {
|
||||
assert(dst->data.untyped != NULL);
|
||||
pixmap_copy_meta(src, dst);
|
||||
|
||||
size_t rows = src->height;
|
||||
size_t row_length = src->width * PIXMAP_FORMAT_PIXEL_SIZE(src->format);
|
||||
|
||||
if(UNLIKELY(row_length == 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
char *cdst = dst->data.untyped;
|
||||
const char *csrc = src->data.untyped;
|
||||
|
||||
for(size_t row = 0, irow = rows - 1; row < rows; ++row, --irow) {
|
||||
memcpy(cdst + irow * row_length, csrc + row * row_length, row_length);
|
||||
}
|
||||
}
|
||||
|
||||
void pixmap_flip_y_alloc(const Pixmap *src, Pixmap *dst) {
|
||||
dst->data.untyped = pixmap_alloc_buffer_for_copy(src, &dst->data_size);
|
||||
pixmap_flip_y(src, dst);
|
||||
}
|
||||
|
||||
void pixmap_flip_y_inplace(Pixmap *src) {
|
||||
size_t rows = src->height;
|
||||
size_t row_length = src->width * PIXMAP_FORMAT_PIXEL_SIZE(src->format);
|
||||
|
||||
if(UNLIKELY(row_length == 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
char *data = src->data.untyped;
|
||||
char swap_buffer[row_length];
|
||||
|
||||
for(size_t row = 0; row < rows / 2; ++row) {
|
||||
memcpy(swap_buffer, data + row * row_length, row_length);
|
||||
memcpy(data + row * row_length, data + (rows - row - 1) * row_length, row_length);
|
||||
memcpy(data + (rows - row - 1) * row_length, swap_buffer, row_length);
|
||||
}
|
||||
}
|
||||
|
||||
void pixmap_flip_to_origin(const Pixmap *src, Pixmap *dst, PixmapOrigin origin) {
|
||||
assert(dst->data.untyped != NULL);
|
||||
|
||||
if(src->origin == origin) {
|
||||
pixmap_copy(src, dst);
|
||||
} else {
|
||||
pixmap_flip_y(src, dst);
|
||||
dst->origin = origin;
|
||||
}
|
||||
}
|
||||
|
||||
void pixmap_flip_to_origin_alloc(const Pixmap *src, Pixmap *dst, PixmapOrigin origin) {
|
||||
dst->data.untyped = pixmap_alloc_buffer_for_copy(src, &dst->data_size);
|
||||
pixmap_flip_to_origin(src, dst, origin);
|
||||
}
|
||||
|
||||
void pixmap_flip_to_origin_inplace(Pixmap *src, PixmapOrigin origin) {
|
||||
if(src->origin == origin) {
|
||||
return;
|
||||
}
|
||||
|
||||
pixmap_flip_y_inplace(src);
|
||||
src->origin = origin;
|
||||
}
|
30
src/pixmap/fileformats/fileformats.h
Normal file
30
src/pixmap/fileformats/fileformats.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* This software is licensed under the terms of the MIT License.
|
||||
* See COPYING for further information.
|
||||
* ---
|
||||
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
|
||||
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
|
||||
*/
|
||||
|
||||
#ifndef IGUARD_pixmap_fileformats_fileformats_h
|
||||
#define IGUARD_pixmap_fileformats_fileformats_h
|
||||
|
||||
#include "taisei.h"
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "../pixmap.h"
|
||||
|
||||
typedef struct PixmapFileFormatHandler {
|
||||
bool (*probe)(SDL_RWops *stream);
|
||||
bool (*load)(SDL_RWops *stream, Pixmap *pixmap, PixmapFormat preferred_format);
|
||||
bool (*save)(SDL_RWops *stream, const Pixmap *pixmap, const PixmapSaveOptions *opts);
|
||||
const char **filename_exts;
|
||||
const char *name;
|
||||
} PixmapFileFormatHandler;
|
||||
|
||||
extern PixmapFileFormatHandler pixmap_fileformat_internal;
|
||||
extern PixmapFileFormatHandler pixmap_fileformat_png;
|
||||
extern PixmapFileFormatHandler pixmap_fileformat_webp;
|
||||
|
||||
#endif // IGUARD_pixmap_fileformats_fileformats_h
|
|
@ -8,15 +8,24 @@
|
|||
|
||||
#include "taisei.h"
|
||||
|
||||
#include "serialize.h"
|
||||
|
||||
#include "fileformats.h"
|
||||
#include "rwops/rwops_crc32.h"
|
||||
#include "util.h"
|
||||
|
||||
static const uint8_t pxs_magic[] = { 0xe2, 0x99, 0xa5, 0x52, 0x65, 0x69, 0x6d, 0x75 };
|
||||
#define PXS_CRC32_INIT 42069
|
||||
enum {
|
||||
PXI_VERSION = 1,
|
||||
PXI_CRC32_INIT = 42069,
|
||||
};
|
||||
|
||||
bool pixmap_serialize(SDL_RWops *stream, const Pixmap *pixmap) {
|
||||
static const uint8_t pxi_magic[] = { 0xe2, 0x99, 0xa5, 0x52, 0x65, 0x69, 0x6d, 0x75 };
|
||||
|
||||
static bool px_internal_probe(SDL_RWops *stream) {
|
||||
uint8_t magic[sizeof(pxi_magic)] = { 0 };
|
||||
SDL_RWread(stream, magic, sizeof(magic), 1);
|
||||
return !memcmp(magic, pxi_magic, sizeof(magic));
|
||||
}
|
||||
|
||||
static bool px_internal_save(SDL_RWops *stream, const Pixmap *pixmap, const PixmapSaveOptions *opts) {
|
||||
SDL_RWops *cstream = NULL;
|
||||
|
||||
#define CHECK_WRITE(expr, expected_size) \
|
||||
|
@ -32,10 +41,10 @@ bool pixmap_serialize(SDL_RWops *stream, const Pixmap *pixmap) {
|
|||
#define W_U16(stream, val) CHECK_WRITE(SDL_WriteLE16(stream, val), sizeof(uint16_t))
|
||||
#define W_U8(stream, val) CHECK_WRITE(SDL_WriteU8(stream, val), sizeof(uint8_t))
|
||||
|
||||
W_BYTES(stream, pxs_magic, sizeof(pxs_magic));
|
||||
W_U16(stream, PIXMAP_SERIALIZED_VERSION);
|
||||
W_BYTES(stream, pxi_magic, sizeof(pxi_magic));
|
||||
W_U16(stream, PXI_VERSION);
|
||||
|
||||
uint32_t crc32 = PXS_CRC32_INIT;
|
||||
uint32_t crc32 = PXI_CRC32_INIT;
|
||||
cstream = NOT_NULL(SDL_RWWrapCRC32(stream, &crc32, false));
|
||||
|
||||
W_U32(cstream, pixmap->width);
|
||||
|
@ -60,26 +69,26 @@ fail:
|
|||
return false;
|
||||
}
|
||||
|
||||
bool pixmap_deserialize(SDL_RWops *stream, Pixmap *pixmap) {
|
||||
static bool px_internal_load(SDL_RWops *stream, Pixmap *pixmap, PixmapFormat preferred_format) {
|
||||
assume(pixmap->data.untyped == NULL);
|
||||
|
||||
SDL_RWops *cstream = NULL;
|
||||
|
||||
uint8_t magic[sizeof(pxs_magic)] = { 0 };
|
||||
uint8_t magic[sizeof(pxi_magic)] = { 0 };
|
||||
SDL_RWread(stream, magic, 1, sizeof(magic));
|
||||
|
||||
if(memcmp(magic, pxs_magic, sizeof(magic))) {
|
||||
if(memcmp(magic, pxi_magic, sizeof(magic))) {
|
||||
log_error("Bad magic header");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t version = SDL_ReadLE16(stream);
|
||||
if(version != PIXMAP_SERIALIZED_VERSION) {
|
||||
log_error("Bad version %u; expected %u", version, PIXMAP_SERIALIZED_VERSION);
|
||||
if(version != PXI_VERSION) {
|
||||
log_error("Bad version %u; expected %u", version, PXI_VERSION);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t crc32 = PXS_CRC32_INIT;
|
||||
uint32_t crc32 = PXI_CRC32_INIT;
|
||||
cstream = NOT_NULL(SDL_RWWrapCRC32(stream, &crc32, false));
|
||||
|
||||
// TODO validate and verify consistency of data_size/width/height/format
|
||||
|
@ -135,3 +144,11 @@ fail:
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
PixmapFileFormatHandler pixmap_fileformat_internal = {
|
||||
.probe = px_internal_probe,
|
||||
.save = px_internal_save,
|
||||
.load = px_internal_load,
|
||||
.filename_exts = (const char*[]) { NULL },
|
||||
.name = "Taisei internal",
|
||||
};
|
6
src/pixmap/fileformats/meson.build
Normal file
6
src/pixmap/fileformats/meson.build
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
pixmap_fileformats_src = files(
|
||||
'internal.c',
|
||||
'png.c',
|
||||
'webp.c',
|
||||
)
|
|
@ -8,14 +8,14 @@
|
|||
|
||||
#include "taisei.h"
|
||||
|
||||
#include "loaders.h"
|
||||
#include "fileformats.h"
|
||||
#include "util.h"
|
||||
#include "util/pngcruft.h"
|
||||
|
||||
static uchar png_magic[] = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };
|
||||
static const uint8_t png_magic[] = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };
|
||||
|
||||
static bool px_png_probe(SDL_RWops *stream) {
|
||||
uchar magic[sizeof(png_magic)] = { 0 };
|
||||
uint8_t magic[sizeof(png_magic)] = { 0 };
|
||||
SDL_RWread(stream, magic, sizeof(magic), 1);
|
||||
return !memcmp(magic, png_magic, sizeof(magic));
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ static bool px_png_load(SDL_RWops *stream, Pixmap *pixmap, PixmapFormat preferre
|
|||
// the GL backend. This is just a slight optimization, not a hard dependency.
|
||||
pixmap->origin = PIXMAP_ORIGIN_BOTTOMLEFT;
|
||||
|
||||
size_t pixel_size = PIXMAP_FORMAT_PIXEL_SIZE(pixmap->format);
|
||||
size_t pixel_size = pixmap_format_pixel_size(pixmap->format);
|
||||
|
||||
if(pixmap->height > PIXMAP_BUFFER_MAX_SIZE / (pixmap->width * pixel_size)) {
|
||||
error = "The image is too large";
|
||||
|
@ -143,8 +143,146 @@ done:
|
|||
return true;
|
||||
}
|
||||
|
||||
PixmapLoader pixmap_loader_png = {
|
||||
static void px_png_save_apply_conversions(
|
||||
const Pixmap *src_pixmap, Pixmap *dst_pixmap
|
||||
) {
|
||||
PixmapLayout fmt_layout = pixmap_format_layout(src_pixmap->format);
|
||||
PixmapLayout fmt_depth = pixmap_format_layout(src_pixmap->format);
|
||||
|
||||
if(fmt_depth > 16) {
|
||||
fmt_depth = 16;
|
||||
} else if(fmt_depth < 16) {
|
||||
fmt_depth = 8;
|
||||
}
|
||||
|
||||
if(fmt_layout == PIXMAP_LAYOUT_RG) {
|
||||
fmt_layout = PIXMAP_LAYOUT_RGB;
|
||||
}
|
||||
|
||||
PixmapFormat fmt = PIXMAP_MAKE_FORMAT(fmt_layout, fmt_depth);
|
||||
|
||||
if(fmt == src_pixmap->format) {
|
||||
*dst_pixmap = *src_pixmap;
|
||||
} else {
|
||||
pixmap_convert_alloc(src_pixmap, dst_pixmap, fmt);
|
||||
}
|
||||
}
|
||||
|
||||
static bool px_png_save(
|
||||
SDL_RWops *stream, const Pixmap *src_pixmap, const PixmapSaveOptions *base_opts
|
||||
) {
|
||||
if(
|
||||
pixmap_format_is_compressed(src_pixmap->format) ||
|
||||
pixmap_format_is_float(src_pixmap->format)
|
||||
) {
|
||||
log_error("Can't write %s image to PNG", pixmap_format_name(src_pixmap->format));
|
||||
return false;
|
||||
}
|
||||
|
||||
const PixmapPNGSaveOptions *opts, default_opts = PIXMAP_DEFAULT_PNG_SAVE_OPTIONS;
|
||||
opts = &default_opts;
|
||||
|
||||
if(
|
||||
base_opts &&
|
||||
base_opts->file_format == PIXMAP_FILEFORMAT_PNG &&
|
||||
base_opts->struct_size > sizeof(*base_opts)
|
||||
) {
|
||||
assert(base_opts->struct_size == sizeof(*opts));
|
||||
opts = UNION_CAST(const PixmapSaveOptions*, const PixmapPNGSaveOptions*, base_opts);
|
||||
}
|
||||
|
||||
const char *error = NULL;
|
||||
png_structp png;
|
||||
png_infop info_ptr;
|
||||
|
||||
Pixmap px = { 0 };
|
||||
|
||||
png = pngutil_create_write_struct();
|
||||
|
||||
if(png == NULL) {
|
||||
error = "pngutil_create_write_struct() failed";
|
||||
goto done;
|
||||
}
|
||||
|
||||
info_ptr = png_create_info_struct(png);
|
||||
|
||||
if(info_ptr == NULL) {
|
||||
error = "png_create_info_struct() failed";
|
||||
goto done;
|
||||
}
|
||||
|
||||
if(setjmp(png_jmpbuf(png))) {
|
||||
error = "PNG error";
|
||||
goto done;
|
||||
}
|
||||
|
||||
px_png_save_apply_conversions(src_pixmap, &px);
|
||||
|
||||
size_t pixel_size = pixmap_format_pixel_size(px.format);
|
||||
|
||||
uint png_color;
|
||||
switch(pixmap_format_layout(px.format)) {
|
||||
case PIXMAP_LAYOUT_R: png_color = PNG_COLOR_TYPE_GRAY; break;
|
||||
case PIXMAP_LAYOUT_RGB: png_color = PNG_COLOR_TYPE_RGB; break;
|
||||
case PIXMAP_LAYOUT_RGBA: png_color = PNG_COLOR_TYPE_RGBA; break;
|
||||
default: UNREACHABLE;
|
||||
}
|
||||
|
||||
pngutil_init_rwops_write(png, stream);
|
||||
|
||||
png_set_IHDR(
|
||||
png, info_ptr,
|
||||
px.width, px.height, pixmap_format_depth(px.format),
|
||||
png_color,
|
||||
PNG_INTERLACE_NONE,
|
||||
PNG_COMPRESSION_TYPE_DEFAULT,
|
||||
PNG_FILTER_TYPE_DEFAULT
|
||||
);
|
||||
|
||||
if(opts->zlib_compression_level >= 0) {
|
||||
png_set_compression_level(png, opts->zlib_compression_level);
|
||||
}
|
||||
|
||||
png_write_info(png, info_ptr);
|
||||
|
||||
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
|
||||
if(pixmap_format_depth(px.format) > 8) {
|
||||
png_set_swap(png);
|
||||
}
|
||||
#endif
|
||||
|
||||
if(px.origin == PIXMAP_ORIGIN_BOTTOMLEFT) {
|
||||
for(int row = px.height - 1; row >= 0; --row) {
|
||||
png_write_row(png, px.data.r8->values + row * px.width * pixel_size);
|
||||
}
|
||||
} else {
|
||||
for(int row = 0; row < px.height; ++row) {
|
||||
png_write_row(png, px.data.r8->values + row * px.width * pixel_size);
|
||||
}
|
||||
}
|
||||
|
||||
png_write_end(png, info_ptr);
|
||||
|
||||
done:
|
||||
if(error) {
|
||||
log_error("%s", error);
|
||||
}
|
||||
|
||||
if(px.data.untyped != src_pixmap->data.untyped) {
|
||||
free(px.data.untyped);
|
||||
}
|
||||
|
||||
if(png != NULL) {
|
||||
png_destroy_write_struct(&png, info_ptr ? &info_ptr : NULL);
|
||||
}
|
||||
|
||||
return !error;
|
||||
}
|
||||
|
||||
PixmapFileFormatHandler pixmap_fileformat_png = {
|
||||
.probe = px_png_probe,
|
||||
.load = px_png_load,
|
||||
.filename_exts = (const char*[]){ "png", NULL },
|
||||
.save = px_png_save,
|
||||
.filename_exts = (const char*[]) { "png", NULL },
|
||||
.name = "PNG",
|
||||
};
|
|
@ -9,7 +9,7 @@
|
|||
#include "taisei.h"
|
||||
|
||||
#include "util.h"
|
||||
#include "loaders.h"
|
||||
#include "fileformats.h"
|
||||
|
||||
#include <webp/decode.h>
|
||||
|
||||
|
@ -130,8 +130,9 @@ static bool px_webp_load(SDL_RWops *stream, Pixmap *pixmap, PixmapFormat preferr
|
|||
return pixmap->data.untyped != NULL;
|
||||
}
|
||||
|
||||
PixmapLoader pixmap_loader_webp = {
|
||||
PixmapFileFormatHandler pixmap_fileformat_webp = {
|
||||
.probe = px_webp_probe,
|
||||
.load = px_webp_load,
|
||||
.filename_exts = (const char*[]){ "webp", NULL },
|
||||
.filename_exts = (const char*[]) { "webp", NULL },
|
||||
.name = "WebP",
|
||||
};
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* This software is licensed under the terms of the MIT License.
|
||||
* See COPYING for further information.
|
||||
* ---
|
||||
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
|
||||
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
|
||||
*/
|
||||
|
||||
#ifndef IGUARD_pixmap_loaders_loaders_h
|
||||
#define IGUARD_pixmap_loaders_loaders_h
|
||||
|
||||
#include "taisei.h"
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "../pixmap.h"
|
||||
|
||||
typedef struct PixmapLoader {
|
||||
bool (*probe)(SDL_RWops *stream);
|
||||
bool (*load)(SDL_RWops *stream, Pixmap *pixmap, PixmapFormat preferred_format);
|
||||
const char **filename_exts;
|
||||
} PixmapLoader;
|
||||
|
||||
extern PixmapLoader pixmap_loader_png;
|
||||
extern PixmapLoader pixmap_loader_webp;
|
||||
|
||||
#endif // IGUARD_pixmap_loaders_loaders_h
|
|
@ -1,5 +0,0 @@
|
|||
|
||||
pixmap_loaders_src = files(
|
||||
'loader_png.c',
|
||||
'loader_webp.c',
|
||||
)
|
|
@ -1,8 +1,8 @@
|
|||
|
||||
pixmap_src = files(
|
||||
'pixmap.c',
|
||||
'serialize.c',
|
||||
'conversion.c',
|
||||
)
|
||||
|
||||
subdir('loaders')
|
||||
pixmap_src += pixmap_loaders_src
|
||||
subdir('fileformats')
|
||||
pixmap_src += pixmap_fileformats_src
|
||||
|
|
|
@ -9,177 +9,16 @@
|
|||
#include "taisei.h"
|
||||
|
||||
#include "pixmap.h"
|
||||
#include "loaders/loaders.h"
|
||||
#include "fileformats/fileformats.h"
|
||||
#include "util.h"
|
||||
|
||||
// NOTE: this is pretty stupid and not at all optimized, patches welcome
|
||||
|
||||
#define _CONV_FUNCNAME convert_u8_to_u8
|
||||
#define _CONV_IN_MAX UINT8_MAX
|
||||
#define _CONV_IN_TYPE uint8_t
|
||||
#define _CONV_OUT_MAX UINT8_MAX
|
||||
#define _CONV_OUT_TYPE uint8_t
|
||||
#include "pixmap_conversion.inc.h"
|
||||
|
||||
#define _CONV_FUNCNAME convert_u8_to_u16
|
||||
#define _CONV_IN_MAX UINT8_MAX
|
||||
#define _CONV_IN_TYPE uint8_t
|
||||
#define _CONV_OUT_MAX UINT16_MAX
|
||||
#define _CONV_OUT_TYPE uint16_t
|
||||
#include "pixmap_conversion.inc.h"
|
||||
|
||||
#define _CONV_FUNCNAME convert_u8_to_u32
|
||||
#define _CONV_IN_MAX UINT8_MAX
|
||||
#define _CONV_IN_TYPE uint8_t
|
||||
#define _CONV_OUT_MAX UINT32_MAX
|
||||
#define _CONV_OUT_TYPE uint32_t
|
||||
#include "pixmap_conversion.inc.h"
|
||||
|
||||
#define _CONV_FUNCNAME convert_u8_to_f32
|
||||
#define _CONV_IN_MAX UINT8_MAX
|
||||
#define _CONV_IN_TYPE uint8_t
|
||||
#define _CONV_OUT_MAX 1.0f
|
||||
#define _CONV_OUT_TYPE float
|
||||
#include "pixmap_conversion.inc.h"
|
||||
|
||||
#define _CONV_FUNCNAME convert_u16_to_u8
|
||||
#define _CONV_IN_MAX UINT16_MAX
|
||||
#define _CONV_IN_TYPE uint16_t
|
||||
#define _CONV_OUT_MAX UINT8_MAX
|
||||
#define _CONV_OUT_TYPE uint8_t
|
||||
#include "pixmap_conversion.inc.h"
|
||||
|
||||
#define _CONV_FUNCNAME convert_u16_to_u16
|
||||
#define _CONV_IN_MAX UINT16_MAX
|
||||
#define _CONV_IN_TYPE uint16_t
|
||||
#define _CONV_OUT_MAX UINT16_MAX
|
||||
#define _CONV_OUT_TYPE uint16_t
|
||||
#include "pixmap_conversion.inc.h"
|
||||
|
||||
#define _CONV_FUNCNAME convert_u16_to_u32
|
||||
#define _CONV_IN_MAX UINT16_MAX
|
||||
#define _CONV_IN_TYPE uint16_t
|
||||
#define _CONV_OUT_MAX UINT32_MAX
|
||||
#define _CONV_OUT_TYPE uint32_t
|
||||
#include "pixmap_conversion.inc.h"
|
||||
|
||||
#define _CONV_FUNCNAME convert_u16_to_f32
|
||||
#define _CONV_IN_MAX UINT16_MAX
|
||||
#define _CONV_IN_TYPE uint16_t
|
||||
#define _CONV_OUT_MAX 1.0f
|
||||
#define _CONV_OUT_TYPE float
|
||||
#include "pixmap_conversion.inc.h"
|
||||
|
||||
#define _CONV_FUNCNAME convert_u32_to_u8
|
||||
#define _CONV_IN_MAX UINT32_MAX
|
||||
#define _CONV_IN_TYPE uint32_t
|
||||
#define _CONV_OUT_MAX UINT8_MAX
|
||||
#define _CONV_OUT_TYPE uint8_t
|
||||
#include "pixmap_conversion.inc.h"
|
||||
|
||||
#define _CONV_FUNCNAME convert_u32_to_u16
|
||||
#define _CONV_IN_MAX UINT32_MAX
|
||||
#define _CONV_IN_TYPE uint32_t
|
||||
#define _CONV_OUT_MAX UINT16_MAX
|
||||
#define _CONV_OUT_TYPE uint16_t
|
||||
#include "pixmap_conversion.inc.h"
|
||||
|
||||
#define _CONV_FUNCNAME convert_u32_to_u32
|
||||
#define _CONV_IN_MAX UINT32_MAX
|
||||
#define _CONV_IN_TYPE uint32_t
|
||||
#define _CONV_OUT_MAX UINT32_MAX
|
||||
#define _CONV_OUT_TYPE uint32_t
|
||||
#include "pixmap_conversion.inc.h"
|
||||
|
||||
#define _CONV_FUNCNAME convert_u32_to_f32
|
||||
#define _CONV_IN_MAX UINT32_MAX
|
||||
#define _CONV_IN_TYPE uint32_t
|
||||
#define _CONV_OUT_MAX 1.0f
|
||||
#define _CONV_OUT_TYPE float
|
||||
#include "pixmap_conversion.inc.h"
|
||||
|
||||
#define _CONV_FUNCNAME convert_f32_to_u8
|
||||
#define _CONV_IN_MAX 1.0f
|
||||
#define _CONV_IN_TYPE float
|
||||
#define _CONV_OUT_MAX UINT8_MAX
|
||||
#define _CONV_OUT_TYPE uint8_t
|
||||
#include "pixmap_conversion.inc.h"
|
||||
|
||||
#define _CONV_FUNCNAME convert_f32_to_u16
|
||||
#define _CONV_IN_MAX 1.0f
|
||||
#define _CONV_IN_TYPE float
|
||||
#define _CONV_OUT_MAX UINT16_MAX
|
||||
#define _CONV_OUT_TYPE uint16_t
|
||||
#include "pixmap_conversion.inc.h"
|
||||
|
||||
#define _CONV_FUNCNAME convert_f32_to_u32
|
||||
#define _CONV_IN_MAX 1.0f
|
||||
#define _CONV_IN_TYPE float
|
||||
#define _CONV_OUT_MAX UINT32_MAX
|
||||
#define _CONV_OUT_TYPE uint32_t
|
||||
#include "pixmap_conversion.inc.h"
|
||||
|
||||
#define _CONV_FUNCNAME convert_f32_to_f32
|
||||
#define _CONV_IN_MAX 1.0f
|
||||
#define _CONV_IN_TYPE float
|
||||
#define _CONV_OUT_MAX 1.0f
|
||||
#define _CONV_OUT_TYPE float
|
||||
#include "pixmap_conversion.inc.h"
|
||||
|
||||
#define DEPTH_FLOAT_BIT (1 << 10)
|
||||
|
||||
typedef void (*convfunc_t)(
|
||||
size_t in_elements,
|
||||
size_t out_elements,
|
||||
size_t num_pixels,
|
||||
void *vbuf_in,
|
||||
void *vbuf_out,
|
||||
int swizzle[4]
|
||||
);
|
||||
|
||||
#define CONV(in, in_depth, out, out_depth) { convert_##in##_to_##out, in_depth, out_depth }
|
||||
|
||||
struct conversion_def {
|
||||
convfunc_t func;
|
||||
uint depth_in;
|
||||
uint depth_out;
|
||||
static PixmapFileFormatHandler *fileformat_handlers[] = {
|
||||
[PIXMAP_FILEFORMAT_INTERNAL] = &pixmap_fileformat_internal,
|
||||
[PIXMAP_FILEFORMAT_PNG] = &pixmap_fileformat_png,
|
||||
[PIXMAP_FILEFORMAT_WEBP] = &pixmap_fileformat_webp,
|
||||
};
|
||||
|
||||
struct conversion_def conversion_table[] = {
|
||||
CONV(u8, 8, u8, 8),
|
||||
CONV(u8, 8, u16, 16),
|
||||
CONV(u8, 8, u32, 32),
|
||||
CONV(u8, 8, f32, 32 | DEPTH_FLOAT_BIT),
|
||||
|
||||
CONV(u16, 16, u8, 8),
|
||||
CONV(u16, 16, u16, 16),
|
||||
CONV(u16, 16, u32, 32),
|
||||
CONV(u16, 16, f32, 32 | DEPTH_FLOAT_BIT),
|
||||
|
||||
CONV(u32, 32, u8, 8),
|
||||
CONV(u32, 16, u16, 16),
|
||||
CONV(u32, 32, u32, 32),
|
||||
CONV(u32, 32, f32, 32 | DEPTH_FLOAT_BIT),
|
||||
|
||||
CONV(f32, 32 | DEPTH_FLOAT_BIT, u8, 8),
|
||||
CONV(f32, 16 | DEPTH_FLOAT_BIT, u16, 16),
|
||||
CONV(f32, 32 | DEPTH_FLOAT_BIT, u32, 32),
|
||||
CONV(f32, 32 | DEPTH_FLOAT_BIT, f32, 32 | DEPTH_FLOAT_BIT),
|
||||
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static struct conversion_def* find_conversion(uint depth_in, uint depth_out) {
|
||||
for(struct conversion_def *cv = conversion_table; cv->func; ++cv) {
|
||||
if(cv->depth_in == depth_in && cv->depth_out == depth_out) {
|
||||
return cv;
|
||||
}
|
||||
}
|
||||
|
||||
log_fatal("Pixmap conversion for %upbc -> %upbc undefined, please add", depth_in, depth_out);
|
||||
}
|
||||
|
||||
static uint32_t pixmap_data_size(PixmapFormat format, uint32_t width, uint32_t height) {
|
||||
uint32_t pixmap_data_size(PixmapFormat format, uint32_t width, uint32_t height) {
|
||||
assert(width >= 1);
|
||||
assert(height >= 1);
|
||||
uint64_t pixel_size = PIXMAP_FORMAT_PIXEL_SIZE(format);
|
||||
|
@ -207,203 +46,18 @@ void *pixmap_alloc_buffer_for_conversion(const Pixmap *src, PixmapFormat format,
|
|||
return pixmap_alloc_buffer(format, src->width, src->height, out_bufsize);
|
||||
}
|
||||
|
||||
static void pixmap_copy_meta(const Pixmap *src, Pixmap *dst) {
|
||||
dst->format = src->format;
|
||||
dst->width = src->width;
|
||||
dst->height = src->height;
|
||||
dst->origin = src->origin;
|
||||
}
|
||||
|
||||
void pixmap_convert(const Pixmap *src, Pixmap *dst, PixmapFormat format) {
|
||||
size_t num_pixels = src->width * src->height;
|
||||
size_t pixel_size = PIXMAP_FORMAT_PIXEL_SIZE(format);
|
||||
|
||||
assert(dst->data.untyped != NULL);
|
||||
pixmap_copy_meta(src, dst);
|
||||
|
||||
if(src->format == format) {
|
||||
memcpy(dst->data.untyped, src->data.untyped, num_pixels * pixel_size);
|
||||
return;
|
||||
}
|
||||
|
||||
dst->format = format;
|
||||
|
||||
struct conversion_def *cv = find_conversion(
|
||||
PIXMAP_FORMAT_DEPTH(src->format) | (PIXMAP_FORMAT_IS_FLOAT(src->format) * DEPTH_FLOAT_BIT),
|
||||
PIXMAP_FORMAT_DEPTH(dst->format) | (PIXMAP_FORMAT_IS_FLOAT(dst->format) * DEPTH_FLOAT_BIT)
|
||||
);
|
||||
|
||||
cv->func(
|
||||
PIXMAP_FORMAT_LAYOUT(src->format),
|
||||
PIXMAP_FORMAT_LAYOUT(dst->format),
|
||||
num_pixels,
|
||||
src->data.untyped,
|
||||
dst->data.untyped,
|
||||
NULL
|
||||
);
|
||||
}
|
||||
|
||||
static int swizzle_idx(char s) {
|
||||
switch(s) {
|
||||
case 'r': return 0;
|
||||
case 'g': return 1;
|
||||
case 'b': return 2;
|
||||
case 'a': return 3;
|
||||
case '0': return 4;
|
||||
case '1': return 5;
|
||||
default: UNREACHABLE;
|
||||
}
|
||||
}
|
||||
|
||||
void pixmap_swizzle_inplace(Pixmap *px, SwizzleMask swizzle) {
|
||||
uint channels = pixmap_format_layout(px->format);
|
||||
swizzle = swizzle_canonize(swizzle);
|
||||
|
||||
if(!swizzle_is_significant(swizzle, channels)) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint cvt_id = pixmap_format_depth(px->format) | (pixmap_format_is_float(px->format) * DEPTH_FLOAT_BIT);
|
||||
struct conversion_def *cv = find_conversion(cvt_id, cvt_id);
|
||||
|
||||
cv->func(
|
||||
channels,
|
||||
channels,
|
||||
px->width * px->height,
|
||||
px->data.untyped,
|
||||
px->data.untyped,
|
||||
(int[]) {
|
||||
swizzle_idx(swizzle.r),
|
||||
swizzle_idx(swizzle.g),
|
||||
swizzle_idx(swizzle.b),
|
||||
swizzle_idx(swizzle.a),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void pixmap_convert_alloc(const Pixmap *src, Pixmap *dst, PixmapFormat format) {
|
||||
dst->data.untyped = pixmap_alloc_buffer_for_conversion(src, format, &dst->data_size);
|
||||
pixmap_convert(src, dst, format);
|
||||
}
|
||||
|
||||
void pixmap_convert_inplace_realloc(Pixmap *src, PixmapFormat format) {
|
||||
assert(src->data.untyped != NULL);
|
||||
|
||||
if(src->format == format) {
|
||||
return;
|
||||
}
|
||||
|
||||
Pixmap tmp;
|
||||
pixmap_copy_meta(src, &tmp);
|
||||
pixmap_convert_alloc(src, &tmp, format);
|
||||
|
||||
free(src->data.untyped);
|
||||
*src = tmp;
|
||||
}
|
||||
|
||||
void pixmap_copy(const Pixmap *src, Pixmap *dst) {
|
||||
assert(dst->data.untyped != NULL);
|
||||
assert(src->data_size > 0);
|
||||
assert(dst->data_size >= src->data_size);
|
||||
|
||||
if(!pixmap_format_is_compressed(src->format)) {
|
||||
assert(src->data_size == pixmap_data_size(src->format, src->width, src->height));
|
||||
}
|
||||
|
||||
pixmap_copy_meta(src, dst);
|
||||
memcpy(dst->data.untyped, src->data.untyped, src->data_size);
|
||||
}
|
||||
|
||||
void pixmap_copy_alloc(const Pixmap *src, Pixmap *dst) {
|
||||
dst->data.untyped = pixmap_alloc_buffer_for_copy(src, &dst->data_size);
|
||||
pixmap_copy(src, dst);
|
||||
}
|
||||
|
||||
void pixmap_flip_y(const Pixmap *src, Pixmap *dst) {
|
||||
assert(dst->data.untyped != NULL);
|
||||
pixmap_copy_meta(src, dst);
|
||||
|
||||
size_t rows = src->height;
|
||||
size_t row_length = src->width * PIXMAP_FORMAT_PIXEL_SIZE(src->format);
|
||||
|
||||
if(UNLIKELY(row_length == 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
char *cdst = dst->data.untyped;
|
||||
const char *csrc = src->data.untyped;
|
||||
|
||||
for(size_t row = 0, irow = rows - 1; row < rows; ++row, --irow) {
|
||||
memcpy(cdst + irow * row_length, csrc + row * row_length, row_length);
|
||||
}
|
||||
}
|
||||
|
||||
void pixmap_flip_y_alloc(const Pixmap *src, Pixmap *dst) {
|
||||
dst->data.untyped = pixmap_alloc_buffer_for_copy(src, &dst->data_size);
|
||||
pixmap_flip_y(src, dst);
|
||||
}
|
||||
|
||||
void pixmap_flip_y_inplace(Pixmap *src) {
|
||||
size_t rows = src->height;
|
||||
size_t row_length = src->width * PIXMAP_FORMAT_PIXEL_SIZE(src->format);
|
||||
|
||||
if(UNLIKELY(row_length == 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
char *data = src->data.untyped;
|
||||
char swap_buffer[row_length];
|
||||
|
||||
for(size_t row = 0; row < rows / 2; ++row) {
|
||||
memcpy(swap_buffer, data + row * row_length, row_length);
|
||||
memcpy(data + row * row_length, data + (rows - row - 1) * row_length, row_length);
|
||||
memcpy(data + (rows - row - 1) * row_length, swap_buffer, row_length);
|
||||
}
|
||||
}
|
||||
|
||||
void pixmap_flip_to_origin(const Pixmap *src, Pixmap *dst, PixmapOrigin origin) {
|
||||
assert(dst->data.untyped != NULL);
|
||||
|
||||
if(src->origin == origin) {
|
||||
pixmap_copy(src, dst);
|
||||
} else {
|
||||
pixmap_flip_y(src, dst);
|
||||
dst->origin = origin;
|
||||
}
|
||||
}
|
||||
|
||||
void pixmap_flip_to_origin_alloc(const Pixmap *src, Pixmap *dst, PixmapOrigin origin) {
|
||||
dst->data.untyped = pixmap_alloc_buffer_for_copy(src, &dst->data_size);
|
||||
pixmap_flip_to_origin(src, dst, origin);
|
||||
}
|
||||
|
||||
void pixmap_flip_to_origin_inplace(Pixmap *src, PixmapOrigin origin) {
|
||||
if(src->origin == origin) {
|
||||
return;
|
||||
}
|
||||
|
||||
pixmap_flip_y_inplace(src);
|
||||
src->origin = origin;
|
||||
}
|
||||
|
||||
static PixmapLoader *pixmap_loaders[] = {
|
||||
&pixmap_loader_png,
|
||||
&pixmap_loader_webp,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static PixmapLoader *pixmap_loader_for_filename(const char *file) {
|
||||
static PixmapFileFormatHandler *pixmap_handler_for_filename(const char *file) {
|
||||
char *ext = strrchr(file, '.');
|
||||
|
||||
if(!ext || !*(++ext)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for(PixmapLoader **loader = pixmap_loaders; *loader; ++loader) {
|
||||
PixmapLoader *l = *loader;
|
||||
for(const char **l_ext = l->filename_exts; *l_ext; ++l_ext) {
|
||||
for(int i = 0; i < ARRAY_SIZE(fileformat_handlers); ++i) {
|
||||
PixmapFileFormatHandler *h = NOT_NULL(fileformat_handlers[i]);
|
||||
for(const char **l_ext = h->filename_exts; *l_ext; ++l_ext) {
|
||||
if(!SDL_strcasecmp(*l_ext, ext)) {
|
||||
return l;
|
||||
return h;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -411,18 +65,55 @@ static PixmapLoader *pixmap_loader_for_filename(const char *file) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
bool pixmap_load_stream(SDL_RWops *stream, Pixmap *dst, PixmapFormat preferred_format) {
|
||||
for(PixmapLoader **loader = pixmap_loaders; *loader; ++loader) {
|
||||
bool match = (*loader)->probe(stream);
|
||||
SDL_RWseek(stream, 0, RW_SEEK_SET);
|
||||
attr_returns_nonnull
|
||||
static PixmapFileFormatHandler *pixmap_handler_for_fileformat(PixmapFileFormat ff) {
|
||||
uint idx = ff;
|
||||
assert(idx < PIXMAP_NUM_FILEFORMATS);
|
||||
return NOT_NULL(fileformat_handlers[idx]);
|
||||
}
|
||||
|
||||
static PixmapFileFormatHandler *pixmap_probe_stream(SDL_RWops *stream) {
|
||||
for(int i = 0; i < ARRAY_SIZE(fileformat_handlers); ++i) {
|
||||
PixmapFileFormatHandler *h = NOT_NULL(fileformat_handlers[i]);
|
||||
|
||||
if(!h->probe) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool match = h->probe(stream);
|
||||
if(SDL_RWseek(stream, 0, RW_SEEK_SET) < 0) {
|
||||
log_sdl_error(LOG_ERROR, "SDL_RWseek");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(match) {
|
||||
return (*loader)->load(stream, dst, preferred_format);
|
||||
return h;
|
||||
}
|
||||
}
|
||||
|
||||
log_error("Image format not recognized");
|
||||
return false;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool pixmap_load_stream(SDL_RWops *stream, PixmapFileFormat filefmt, Pixmap *dst, PixmapFormat preferred_format) {
|
||||
PixmapFileFormatHandler *handler = NULL;
|
||||
|
||||
if(filefmt == PIXMAP_FILEFORMAT_AUTO) {
|
||||
handler = pixmap_probe_stream(stream);
|
||||
} else {
|
||||
handler = pixmap_handler_for_fileformat(filefmt);
|
||||
}
|
||||
|
||||
if(UNLIKELY(!handler)) {
|
||||
log_error("Image format not recognized");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(UNLIKELY(!handler->load)) {
|
||||
log_error("Can't load images in %s format", NOT_NULL(handler->name));
|
||||
return false;
|
||||
}
|
||||
|
||||
return handler->load(stream, dst, preferred_format);
|
||||
}
|
||||
|
||||
bool pixmap_load_file(const char *path, Pixmap *dst, PixmapFormat preferred_format) {
|
||||
|
@ -434,13 +125,66 @@ bool pixmap_load_file(const char *path, Pixmap *dst, PixmapFormat preferred_form
|
|||
return false;
|
||||
}
|
||||
|
||||
bool result = pixmap_load_stream(stream, dst, preferred_format);
|
||||
bool result = pixmap_load_stream(stream, PIXMAP_FILEFORMAT_AUTO, dst, preferred_format);
|
||||
SDL_RWclose(stream);
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool pixmap_save_stream_internal(
|
||||
SDL_RWops *stream, const Pixmap *src, const PixmapSaveOptions *opts,
|
||||
PixmapFileFormatHandler *handler
|
||||
) {
|
||||
if(UNLIKELY(!handler->save)) {
|
||||
log_error("Can't save images in %s format", NOT_NULL(handler->name));
|
||||
return false;
|
||||
}
|
||||
|
||||
return handler->save(stream, src, opts);
|
||||
}
|
||||
|
||||
bool pixmap_save_stream(SDL_RWops *stream, const Pixmap *src, const PixmapSaveOptions *opts) {
|
||||
PixmapFileFormat fmt = opts->file_format;
|
||||
PixmapFileFormatHandler *h = pixmap_handler_for_fileformat(fmt);
|
||||
return pixmap_save_stream_internal(stream, src, opts, h);
|
||||
}
|
||||
|
||||
bool pixmap_save_file(const char *path, const Pixmap *src, const PixmapSaveOptions *opts) {
|
||||
PixmapFileFormatHandler *h = NULL;
|
||||
PixmapFileFormat fmt = PIXMAP_FILEFORMAT_AUTO;
|
||||
|
||||
if(opts) {
|
||||
assert(opts->struct_size >= sizeof(PixmapSaveOptions));
|
||||
fmt = opts->file_format;
|
||||
}
|
||||
|
||||
if(fmt == PIXMAP_FILEFORMAT_AUTO) {
|
||||
h = pixmap_handler_for_filename(path);
|
||||
} else {
|
||||
h = pixmap_handler_for_fileformat(fmt);
|
||||
}
|
||||
|
||||
if(UNLIKELY(!h)) {
|
||||
h = NOT_NULL(pixmap_handler_for_fileformat(PIXMAP_FILEFORMAT_INTERNAL));
|
||||
log_warn(
|
||||
"Couldn't determine file format from filename `%s`, assuming %s",
|
||||
path, NOT_NULL(h->name)
|
||||
);
|
||||
}
|
||||
|
||||
SDL_RWops *stream = vfs_open(path, VFS_MODE_WRITE);
|
||||
|
||||
if(UNLIKELY(!stream)) {
|
||||
log_error("VFS error: %s", vfs_get_error());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = pixmap_save_stream_internal(stream, src, opts, h);
|
||||
SDL_RWclose(stream);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool pixmap_check_filename(const char *path) {
|
||||
return (bool)pixmap_loader_for_filename(path);
|
||||
return (bool)pixmap_handler_for_filename(path);
|
||||
}
|
||||
|
||||
char *pixmap_source_path(const char *prefix, const char *path) {
|
||||
|
@ -458,9 +202,10 @@ char *pixmap_source_path(const char *prefix, const char *path) {
|
|||
*dot = 0;
|
||||
}
|
||||
|
||||
for(PixmapLoader **loader = pixmap_loaders; *loader; ++loader) {
|
||||
PixmapLoader *l = *loader;
|
||||
for(const char **l_ext = l->filename_exts; *l_ext; ++l_ext) {
|
||||
for(int i = 0; i < ARRAY_SIZE(fileformat_handlers); ++i) {
|
||||
PixmapFileFormatHandler *h = NOT_NULL(fileformat_handlers[i]);
|
||||
|
||||
for(const char **l_ext = h->filename_exts; *l_ext; ++l_ext) {
|
||||
char ext[strlen(*l_ext) + 2];
|
||||
ext[0] = '.';
|
||||
strcpy(ext + 1, *l_ext);
|
||||
|
|
|
@ -15,6 +15,40 @@
|
|||
|
||||
#define PIXMAP_BUFFER_MAX_SIZE INT32_MAX
|
||||
|
||||
typedef enum PixmapFileFormat {
|
||||
PIXMAP_FILEFORMAT_AUTO = -1,
|
||||
|
||||
// NOTE: these are probed from first to last
|
||||
PIXMAP_FILEFORMAT_WEBP,
|
||||
PIXMAP_FILEFORMAT_PNG,
|
||||
PIXMAP_FILEFORMAT_INTERNAL,
|
||||
|
||||
PIXMAP_NUM_FILEFORMATS
|
||||
} PixmapFileFormat;
|
||||
|
||||
typedef struct PixmapSaveOptions {
|
||||
PixmapFileFormat file_format;
|
||||
size_t struct_size;
|
||||
} PixmapSaveOptions;
|
||||
|
||||
#define PIXMAP_DEFAULT_SAVE_OPTIONS \
|
||||
{ \
|
||||
.file_format = PIXMAP_FILEFORMAT_AUTO, \
|
||||
.struct_size = sizeof(PixmapSaveOptions), \
|
||||
}
|
||||
|
||||
typedef struct PixmapPNGSaveOptions {
|
||||
PixmapSaveOptions base;
|
||||
int zlib_compression_level;
|
||||
} PixmapPNGSaveOptions;
|
||||
|
||||
#define PIXMAP_DEFAULT_PNG_SAVE_OPTIONS \
|
||||
{ \
|
||||
.base.file_format = PIXMAP_FILEFORMAT_PNG, \
|
||||
.base.struct_size = sizeof(PixmapPNGSaveOptions), \
|
||||
.zlib_compression_level = -1, \
|
||||
}
|
||||
|
||||
typedef enum PixmapLayout {
|
||||
PIXMAP_LAYOUT_R = 1,
|
||||
PIXMAP_LAYOUT_RG,
|
||||
|
@ -274,12 +308,16 @@ void pixmap_flip_to_origin_alloc(const Pixmap *src, Pixmap *dst, PixmapOrigin or
|
|||
void pixmap_flip_to_origin_inplace(Pixmap *src, PixmapOrigin origin) attr_nonnull(1);
|
||||
|
||||
bool pixmap_load_file(const char *path, Pixmap *dst, PixmapFormat preferred_format) attr_nonnull(1, 2) attr_nodiscard;
|
||||
bool pixmap_load_stream(SDL_RWops *stream, Pixmap *dst, PixmapFormat preferred_format) attr_nonnull(1, 2) attr_nodiscard;
|
||||
bool pixmap_load_stream(SDL_RWops *stream, PixmapFileFormat filefmt, Pixmap *dst, PixmapFormat preferred_format) attr_nonnull(1, 3) attr_nodiscard;
|
||||
|
||||
bool pixmap_save_file(const char *path, const Pixmap *src, const PixmapSaveOptions *opts) attr_nonnull(1, 2);
|
||||
bool pixmap_save_stream(SDL_RWops *stream, const Pixmap *src, const PixmapSaveOptions *opts) attr_nonnull(1, 2, 3);
|
||||
|
||||
bool pixmap_check_filename(const char *path);
|
||||
char *pixmap_source_path(const char *prefix, const char *path) attr_nodiscard;
|
||||
|
||||
const char *pixmap_format_name(PixmapFormat fmt);
|
||||
uint32_t pixmap_data_size(PixmapFormat format, uint32_t width, uint32_t height);
|
||||
|
||||
SwizzleMask swizzle_canonize(SwizzleMask sw_in);
|
||||
bool swizzle_is_valid(SwizzleMask sw);
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
* This software is licensed under the terms of the MIT License.
|
||||
* See COPYING for further information.
|
||||
* ---
|
||||
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
|
||||
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
|
||||
*/
|
||||
|
||||
#ifndef IGUARD_pixmap_serialize_h
|
||||
#define IGUARD_pixmap_serialize_h
|
||||
|
||||
#include "taisei.h"
|
||||
|
||||
#include "pixmap.h"
|
||||
|
||||
enum {
|
||||
PIXMAP_SERIALIZED_VERSION = 1,
|
||||
};
|
||||
|
||||
bool pixmap_serialize(SDL_RWops *stream, const Pixmap *pixmap);
|
||||
bool pixmap_deserialize(SDL_RWops *stream, Pixmap *pixmap);
|
||||
|
||||
#endif // IGUARD_pixmap_serialize_h
|
|
@ -9,7 +9,7 @@
|
|||
#include "taisei.h"
|
||||
|
||||
#include "basisu_cache.h"
|
||||
#include "pixmap/serialize.h"
|
||||
#include "pixmap/pixmap.h"
|
||||
#include "rwops/rwops_zstd.h"
|
||||
|
||||
#include <basisu_transcoder_c_api.h>
|
||||
|
@ -75,7 +75,7 @@ bool texture_loader_basisu_load_cached(
|
|||
|
||||
rw = SDL_RWWrapZstdReader(rw, true);
|
||||
|
||||
bool deserialize_ok = pixmap_deserialize(rw, out_pixmap);
|
||||
bool deserialize_ok = pixmap_load_stream(rw, PIXMAP_FILEFORMAT_INTERNAL, out_pixmap, 0);
|
||||
SDL_RWclose(rw);
|
||||
|
||||
if(!deserialize_ok) {
|
||||
|
@ -178,7 +178,9 @@ bool texture_loader_basisu_cache(
|
|||
|
||||
rw = SDL_RWWrapZstdWriter(rw, RW_ZSTD_LEVEL_DEFAULT, true);
|
||||
|
||||
bool serialize_ok = pixmap_serialize(rw, pixmap);
|
||||
PixmapSaveOptions opts = PIXMAP_DEFAULT_SAVE_OPTIONS;
|
||||
opts.file_format = PIXMAP_FILEFORMAT_INTERNAL;
|
||||
bool serialize_ok = pixmap_save_stream(rw, pixmap, &opts);
|
||||
SDL_RWclose(rw);
|
||||
|
||||
if(!serialize_ok) {
|
||||
|
|
75
src/video.c
75
src/video.c
|
@ -8,8 +8,6 @@
|
|||
|
||||
#include "taisei.h"
|
||||
|
||||
#include <png.h>
|
||||
|
||||
#include "global.h"
|
||||
#include "video.h"
|
||||
#include "renderer/api.h"
|
||||
|
@ -553,77 +551,18 @@ static void *video_screenshot_task(void *arg) {
|
|||
ScreenshotTaskData *tdata = arg;
|
||||
|
||||
pixmap_convert_inplace_realloc(&tdata->image, PIXMAP_FORMAT_RGB8);
|
||||
pixmap_flip_to_origin_inplace(&tdata->image, PIXMAP_ORIGIN_BOTTOMLEFT);
|
||||
|
||||
uint width = tdata->image.width;
|
||||
uint height = tdata->image.height;
|
||||
uint8_t *pixels = tdata->image.data.untyped;
|
||||
PixmapPNGSaveOptions opts = PIXMAP_DEFAULT_PNG_SAVE_OPTIONS;
|
||||
opts.zlib_compression_level = 9;
|
||||
|
||||
SDL_RWops *output = vfs_open(tdata->dest_path, VFS_MODE_WRITE);
|
||||
bool ok = pixmap_save_file(tdata->dest_path, &tdata->image, &opts.base);
|
||||
|
||||
if(!output) {
|
||||
log_error("VFS error: %s", vfs_get_error());
|
||||
return NULL;
|
||||
if(LIKELY(ok)) {
|
||||
char *syspath = vfs_repr(tdata->dest_path, true);
|
||||
log_info("Saved screenshot as %s", syspath);
|
||||
free(syspath);
|
||||
}
|
||||
|
||||
char *syspath = vfs_repr(tdata->dest_path, true);
|
||||
log_info("Saving screenshot as %s", syspath);
|
||||
free(syspath);
|
||||
|
||||
const char *error = NULL;
|
||||
png_structp png_ptr;
|
||||
png_infop info_ptr;
|
||||
|
||||
png_byte *volatile row_pointers[height];
|
||||
memset((void*)row_pointers, 0, sizeof(row_pointers));
|
||||
|
||||
png_ptr = pngutil_create_write_struct();
|
||||
|
||||
if(png_ptr == NULL) {
|
||||
error = "pngutil_create_write_struct() failed";
|
||||
goto done;
|
||||
}
|
||||
|
||||
info_ptr = png_create_info_struct(png_ptr);
|
||||
|
||||
if(info_ptr == NULL) {
|
||||
error = "png_create_info_struct() failed";
|
||||
goto done;
|
||||
}
|
||||
|
||||
if(setjmp(png_jmpbuf(png_ptr))) {
|
||||
error = "PNG error";
|
||||
goto done;
|
||||
}
|
||||
|
||||
png_set_IHDR(
|
||||
png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB,
|
||||
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT
|
||||
);
|
||||
|
||||
for(int y = 0; y < height; y++) {
|
||||
row_pointers[y] = png_malloc(png_ptr, width * 3);
|
||||
memcpy(row_pointers[y], pixels + width * 3 * (height - 1 - y), width * 3);
|
||||
}
|
||||
|
||||
pngutil_init_rwops_write(png_ptr, output);
|
||||
png_set_rows(png_ptr, info_ptr, (void*)row_pointers);
|
||||
png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
|
||||
|
||||
done:
|
||||
if(error) {
|
||||
log_error("Couldn't save screenshot: %s", error);
|
||||
}
|
||||
|
||||
for(int y = 0; y < height; y++) {
|
||||
png_free(png_ptr, row_pointers[y]);
|
||||
}
|
||||
|
||||
if(png_ptr != NULL) {
|
||||
png_destroy_write_struct(&png_ptr, info_ptr ? &info_ptr : NULL);
|
||||
}
|
||||
|
||||
SDL_RWclose(output);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue