From b13f98c6817a3c4dcdc08e82398a5e11320045fe Mon Sep 17 00:00:00 2001 From: Andrei Alexeyev <0x416b617269@gmail.com> Date: Wed, 17 Oct 2018 23:11:27 +0300 Subject: [PATCH] Support 16-bit uint and 32-bit float textures When textures are loaded as resources, the best available format is now picked by default. That means that if your source image is 16-bit RGB, you will get a 16-bit RGB texture (the alpha channel is added only if necessary). However, grayscale images currently get expanded into RGB(A) by the loaders automatically, so you will never get less than 3 channels by default. This is good, because you most often expect grayscale images to stay gray, and not red. This behavior can be overriden with the new 'format' key in .tex files. For example, if you only care about one channel in a gray image, you can do this to save some VRAM: format = R16 The following is equivalent: format = R16U And so is this: format = r 16 uint The general syntax is: format = [datatype] Where: * Channels is one of: R, RG, RGB, RGBA; * Depth is one of: 8, 16, 32; * Datatype is one of: uint (or just u), float (or just f) All fields are case-insensitive and may be separated by whitespace. Note that the rendering backend may not support all of these formats, and fallback to an inferior or a redundant one. The gl33 backend should support all of them. The gles30 backend only supports 8-bit uint textures for now. --- src/renderer/api.h | 28 +++++-- src/renderer/gl33/texture.c | 22 +++-- src/renderer/glcommon/texture.c | 26 +++++- src/renderer/gles20/core.c | 44 +++++++--- src/resource/texture.c | 135 ++++++++++++++++++++++++++++++- src/util/pixmap.c | 105 +++++++++++++++++++----- src/util/pixmap.h | 129 ++++++++++++++++------------- src/util/pixmap_conversion.inc.h | 15 +++- 8 files changed, 400 insertions(+), 104 deletions(-) diff --git a/src/renderer/api.h b/src/renderer/api.h index ed2ddf6b..074aea71 100644 --- a/src/renderer/api.h +++ b/src/renderer/api.h @@ -63,11 +63,29 @@ typedef enum MatrixMode { typedef enum TextureType { // NOTE: whichever is placed first here is considered the "default" where applicable. - TEX_TYPE_RGBA, - TEX_TYPE_RGB, - TEX_TYPE_RG, - TEX_TYPE_R, - TEX_TYPE_DEPTH, + TEX_TYPE_RGBA_8, + TEX_TYPE_RGB_8, + TEX_TYPE_RG_8, + TEX_TYPE_R_8, + TEX_TYPE_DEPTH_8, + + TEX_TYPE_RGBA_16, + TEX_TYPE_RGB_16, + TEX_TYPE_RG_16, + TEX_TYPE_R_16, + TEX_TYPE_DEPTH_16, + + TEX_TYPE_RGBA_32_FLOAT, + TEX_TYPE_RGB_32_FLOAT, + TEX_TYPE_RG_32_FLOAT, + TEX_TYPE_R_32_FLOAT, + TEX_TYPE_DEPTH_32_FLOAT, + + TEX_TYPE_RGBA = TEX_TYPE_RGBA_8, + TEX_TYPE_RGB = TEX_TYPE_RGB_8, + TEX_TYPE_RG = TEX_TYPE_RG_8, + TEX_TYPE_R = TEX_TYPE_R_8, + TEX_TYPE_DEPTH = TEX_TYPE_DEPTH_8, } TextureType; typedef enum TextureFilterMode { diff --git a/src/renderer/gl33/texture.c b/src/renderer/gl33/texture.c index 7c5f145c..910691af 100644 --- a/src/renderer/gl33/texture.c +++ b/src/renderer/gl33/texture.c @@ -70,11 +70,23 @@ GLTextureTypeInfo* gl33_texture_type_info(TextureType type) { }; static GLTextureTypeInfo map[] = { - [TEX_TYPE_R] = { GL_R8, 1, color_formats, { GL_RED, GL_UNSIGNED_BYTE, PIXMAP_FORMAT_R8 } }, - [TEX_TYPE_RG] = { GL_RG8, 2, color_formats, { GL_RG, GL_UNSIGNED_BYTE, PIXMAP_FORMAT_RG8 } }, - [TEX_TYPE_RGB] = { GL_RGB8, 3, color_formats, { GL_RGB, GL_UNSIGNED_BYTE, PIXMAP_FORMAT_RGB8 } }, - [TEX_TYPE_RGBA] = { GL_RGBA8, 4, color_formats, { GL_RGBA, GL_UNSIGNED_BYTE, PIXMAP_FORMAT_RGBA8 } }, - [TEX_TYPE_DEPTH] = { GL_DEPTH_COMPONENT16, 2, depth_formats, { GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, PIXMAP_FORMAT_R16 } }, + [TEX_TYPE_R_8] = { GL_R8, 1, color_formats, { GL_RED, GL_UNSIGNED_BYTE, PIXMAP_FORMAT_R8 } }, + [TEX_TYPE_RG_8] = { GL_RG8, 2, color_formats, { GL_RG, GL_UNSIGNED_BYTE, PIXMAP_FORMAT_RG8 } }, + [TEX_TYPE_RGB_8] = { GL_RGB8, 3, color_formats, { GL_RGB, GL_UNSIGNED_BYTE, PIXMAP_FORMAT_RGB8 } }, + [TEX_TYPE_RGBA_8] = { GL_RGBA8, 4, color_formats, { GL_RGBA, GL_UNSIGNED_BYTE, PIXMAP_FORMAT_RGBA8 } }, + [TEX_TYPE_DEPTH_8] = { GL_DEPTH_COMPONENT16, 2, depth_formats, { GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, PIXMAP_FORMAT_R16 } }, + + [TEX_TYPE_R_16] = { GL_R16, 1, color_formats, { GL_RED, GL_UNSIGNED_SHORT, PIXMAP_FORMAT_R16 } }, + [TEX_TYPE_RG_16] = { GL_RG16, 2, color_formats, { GL_RG, GL_UNSIGNED_SHORT, PIXMAP_FORMAT_RG16 } }, + [TEX_TYPE_RGB_16] = { GL_RGB16, 3, color_formats, { GL_RGB, GL_UNSIGNED_SHORT, PIXMAP_FORMAT_RGB16 } }, + [TEX_TYPE_RGBA_16] = { GL_RGBA16, 4, color_formats, { GL_RGBA, GL_UNSIGNED_SHORT, PIXMAP_FORMAT_RGBA16 } }, + [TEX_TYPE_DEPTH_16] = { GL_DEPTH_COMPONENT16, 2, depth_formats, { GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, PIXMAP_FORMAT_R16 } }, + + [TEX_TYPE_R_32_FLOAT] = { GL_R32F, 1, color_formats, { GL_RED, GL_UNSIGNED_SHORT, PIXMAP_FORMAT_R16 } }, + [TEX_TYPE_RG_32_FLOAT] = { GL_RG32F, 2, color_formats, { GL_RG, GL_UNSIGNED_SHORT, PIXMAP_FORMAT_RG16 } }, + [TEX_TYPE_RGB_32_FLOAT] = { GL_RGB32F, 3, color_formats, { GL_RGB, GL_UNSIGNED_SHORT, PIXMAP_FORMAT_RGB16 } }, + [TEX_TYPE_RGBA_32_FLOAT] = { GL_RGBA32F, 4, color_formats, { GL_RGBA, GL_UNSIGNED_SHORT, PIXMAP_FORMAT_RGBA16 } }, + [TEX_TYPE_DEPTH_32_FLOAT] = { GL_DEPTH_COMPONENT32, 2, depth_formats, { GL_DEPTH_COMPONENT32F, GL_FLOAT, PIXMAP_FORMAT_R32 } }, }; assert((uint)type < sizeof(map)/sizeof(*map)); diff --git a/src/renderer/glcommon/texture.c b/src/renderer/glcommon/texture.c index 2049b35a..00f0d0b2 100644 --- a/src/renderer/glcommon/texture.c +++ b/src/renderer/glcommon/texture.c @@ -22,6 +22,8 @@ static GLTextureFormatTuple* glcommon_find_best_pixformat_internal(GLTextureForm size_t ideal_channels = PIXMAP_FORMAT_LAYOUT(pxfmt); size_t ideal_depth = PIXMAP_FORMAT_DEPTH(pxfmt); + bool ideal_is_float = PIXMAP_FORMAT_IS_FLOAT(pxfmt); + bool best_is_float; for(GLTextureFormatTuple *fmt = formats + 1; fmt->px_fmt; ++fmt) { if(pxfmt == fmt->px_fmt) { @@ -30,12 +32,24 @@ static GLTextureFormatTuple* glcommon_find_best_pixformat_internal(GLTextureForm size_t best_channels = PIXMAP_FORMAT_LAYOUT(best->px_fmt); size_t best_depth = PIXMAP_FORMAT_DEPTH(best->px_fmt); + best_is_float = PIXMAP_FORMAT_IS_FLOAT(best->px_fmt); size_t this_channels = PIXMAP_FORMAT_LAYOUT(fmt->px_fmt); size_t this_depth = PIXMAP_FORMAT_DEPTH(fmt->px_fmt); + bool this_is_float = PIXMAP_FORMAT_IS_FLOAT(fmt->px_fmt); if(best_channels < ideal_channels) { - if(this_channels > best_channels || (this_channels == best_channels && this_depth >= ideal_depth)) { + if( + this_channels > best_channels || ( + this_channels == best_channels && ( + (this_is_float && ideal_is_float && !best_is_float) || this_depth >= ideal_depth + ) + ) + ) { + best = fmt; + } + } else if(ideal_is_float && !best_is_float) { + if(this_channels >= ideal_channels && (this_is_float || this_depth >= best_depth)) { best = fmt; } } else if(best_depth < ideal_depth) { @@ -43,18 +57,22 @@ static GLTextureFormatTuple* glcommon_find_best_pixformat_internal(GLTextureForm best = fmt; } } else if( - this_channels >= ideal_channels && this_depth >= ideal_depth && + this_channels >= ideal_channels && + this_depth >= ideal_depth && + this_is_float == ideal_is_float && (this_channels < best_channels || this_depth < best_depth) ) { best = fmt; } } - log_debug("(%u, %u) --> (%u, %u)", + log_debug("(%u, %u, %s) --> (%u, %u, %s)", PIXMAP_FORMAT_LAYOUT(pxfmt), PIXMAP_FORMAT_DEPTH(pxfmt), + ideal_is_float ? "float" : "uint", PIXMAP_FORMAT_LAYOUT(best->px_fmt), - PIXMAP_FORMAT_DEPTH(best->px_fmt) + PIXMAP_FORMAT_DEPTH(best->px_fmt), + best_is_float ? "float" : "uint" ); return best; diff --git a/src/renderer/gles20/core.c b/src/renderer/gles20/core.c index b43c72b3..0d274022 100644 --- a/src/renderer/gles20/core.c +++ b/src/renderer/gles20/core.c @@ -52,11 +52,23 @@ GLTextureTypeInfo* gles20_texture_type_info(TextureType type) { static GLTextureFormatTuple formats_depth[] = { FMT_DEPTH, { 0 } }; static GLTextureTypeInfo map[] = { - [TEX_TYPE_R] = { GL_RGBA, 4, formats_r, FMT_R }, - [TEX_TYPE_RG] = { GL_RGBA, 4, formats_rg, FMT_RG }, - [TEX_TYPE_RGB] = { GL_RGBA, 4, formats_rgb, FMT_RGB }, - [TEX_TYPE_RGBA] = { GL_RGBA, 4, formats_rgba, FMT_RGBA }, - [TEX_TYPE_DEPTH] = { GL_DEPTH_COMPONENT, 2, formats_depth, FMT_DEPTH }, + [TEX_TYPE_R_8] = { GL_RGBA, 4, formats_r, FMT_R }, + [TEX_TYPE_RG_8] = { GL_RGBA, 4, formats_rg, FMT_RG }, + [TEX_TYPE_RGB_8] = { GL_RGBA, 4, formats_rgb, FMT_RGB }, + [TEX_TYPE_RGBA_8] = { GL_RGBA, 4, formats_rgba, FMT_RGBA }, + [TEX_TYPE_DEPTH_8] = { GL_DEPTH_COMPONENT, 2, formats_depth, FMT_DEPTH }, + + [TEX_TYPE_R_16] = { GL_RGBA, 4, formats_r, FMT_R }, + [TEX_TYPE_RG_16] = { GL_RGBA, 4, formats_rg, FMT_RG }, + [TEX_TYPE_RGB_16] = { GL_RGBA, 4, formats_rgb, FMT_RGB }, + [TEX_TYPE_RGBA_16] = { GL_RGBA, 4, formats_rgba, FMT_RGBA }, + [TEX_TYPE_DEPTH_16] = { GL_DEPTH_COMPONENT, 2, formats_depth, FMT_DEPTH }, + + [TEX_TYPE_R_32_FLOAT] = { GL_RGBA, 4, formats_r, FMT_R }, + [TEX_TYPE_RG_32_FLOAT] = { GL_RGBA, 4, formats_rg, FMT_RG }, + [TEX_TYPE_RGB_32_FLOAT] = { GL_RGBA, 4, formats_rgb, FMT_RGB }, + [TEX_TYPE_RGBA_32_FLOAT] = { GL_RGBA, 4, formats_rgba, FMT_RGBA }, + [TEX_TYPE_DEPTH_32_FLOAT] = { GL_DEPTH_COMPONENT, 2, formats_depth, FMT_DEPTH }, }; assert((uint)type < sizeof(map)/sizeof(*map)); @@ -79,13 +91,25 @@ GLTextureTypeInfo* gles30_texture_type_info(TextureType type) { }; static GLTextureTypeInfo map[] = { - [TEX_TYPE_R] = { GL_R8, 1, color_formats, { GL_RED, GL_UNSIGNED_BYTE, PIXMAP_FORMAT_R8 } }, - [TEX_TYPE_RG] = { GL_RG8, 2, color_formats, { GL_RG, GL_UNSIGNED_BYTE, PIXMAP_FORMAT_RG8 } }, - [TEX_TYPE_RGB] = { GL_RGB, 3, color_formats, { GL_RGB, GL_UNSIGNED_BYTE, PIXMAP_FORMAT_RGB8 } }, - [TEX_TYPE_RGBA] = { GL_RGBA, 4, color_formats, { GL_RGBA, GL_UNSIGNED_BYTE, PIXMAP_FORMAT_RGBA8 } }, + [TEX_TYPE_R_8] = { GL_R8, 1, color_formats, { GL_RED, GL_UNSIGNED_BYTE, PIXMAP_FORMAT_R8 } }, + [TEX_TYPE_RG_8] = { GL_RG8, 2, color_formats, { GL_RG, GL_UNSIGNED_BYTE, PIXMAP_FORMAT_RG8 } }, + [TEX_TYPE_RGB_8] = { GL_RGB, 3, color_formats, { GL_RGB, GL_UNSIGNED_BYTE, PIXMAP_FORMAT_RGB8 } }, + [TEX_TYPE_RGBA_8] = { GL_RGBA, 4, color_formats, { GL_RGBA, GL_UNSIGNED_BYTE, PIXMAP_FORMAT_RGBA8 } }, + + [TEX_TYPE_R_16] = { GL_R8, 1, color_formats, { GL_RED, GL_UNSIGNED_BYTE, PIXMAP_FORMAT_R8 } }, + [TEX_TYPE_RG_16] = { GL_RG8, 2, color_formats, { GL_RG, GL_UNSIGNED_BYTE, PIXMAP_FORMAT_RG8 } }, + [TEX_TYPE_RGB_16] = { GL_RGB, 3, color_formats, { GL_RGB, GL_UNSIGNED_BYTE, PIXMAP_FORMAT_RGB8 } }, + [TEX_TYPE_RGBA_16] = { GL_RGBA, 4, color_formats, { GL_RGBA, GL_UNSIGNED_BYTE, PIXMAP_FORMAT_RGBA8 } }, + + [TEX_TYPE_R_32_FLOAT] = { GL_R8, 1, color_formats, { GL_RED, GL_UNSIGNED_BYTE, PIXMAP_FORMAT_R8 } }, + [TEX_TYPE_RG_32_FLOAT] = { GL_RG8, 2, color_formats, { GL_RG, GL_UNSIGNED_BYTE, PIXMAP_FORMAT_RG8 } }, + [TEX_TYPE_RGB_32_FLOAT] = { GL_RGB, 3, color_formats, { GL_RGB, GL_UNSIGNED_BYTE, PIXMAP_FORMAT_RGB8 } }, + [TEX_TYPE_RGBA_32_FLOAT] = { GL_RGBA, 4, color_formats, { GL_RGBA, GL_UNSIGNED_BYTE, PIXMAP_FORMAT_RGBA8 } }, // WARNING: ANGLE bug(?): texture binding fails if a sized format is used here. - [TEX_TYPE_DEPTH] = { GL_DEPTH_COMPONENT, 2, depth_formats, { GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, PIXMAP_FORMAT_R16 } }, + [TEX_TYPE_DEPTH_8] = { GL_DEPTH_COMPONENT, 2, depth_formats, { GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, PIXMAP_FORMAT_R16 } }, + [TEX_TYPE_DEPTH_16] = { GL_DEPTH_COMPONENT, 2, depth_formats, { GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, PIXMAP_FORMAT_R16 } }, + [TEX_TYPE_DEPTH_32_FLOAT] = { GL_DEPTH_COMPONENT, 2, depth_formats, { GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, PIXMAP_FORMAT_R16 } }, }; assert((uint)type < sizeof(map)/sizeof(*map)); diff --git a/src/resource/texture.c b/src/resource/texture.c index 3da10aef..9becd548 100644 --- a/src/resource/texture.c +++ b/src/resource/texture.c @@ -135,6 +135,118 @@ static void parse_wrap(const char *val, TextureWrapMode *out) { log_warn("Bad value `%s`, assuming default", pbuf); } +static TextureType pixmap_format_to_texture_type(PixmapFormat fmt) { + PixmapLayout layout = PIXMAP_FORMAT_LAYOUT(fmt); + uint depth = PIXMAP_FORMAT_DEPTH(fmt); + bool is_float = PIXMAP_FORMAT_IS_FLOAT(fmt); + + if(is_float) { + switch(layout) { + case PIXMAP_LAYOUT_R: return TEX_TYPE_R_32_FLOAT; + case PIXMAP_LAYOUT_RG: return TEX_TYPE_RG_32_FLOAT; + case PIXMAP_LAYOUT_RGB: return TEX_TYPE_RGB_32_FLOAT; + case PIXMAP_LAYOUT_RGBA: return TEX_TYPE_RGBA_32_FLOAT; + } + + UNREACHABLE; + } + + if(depth > 8) { + switch(layout) { + case PIXMAP_LAYOUT_R: return TEX_TYPE_R_16; + case PIXMAP_LAYOUT_RG: return TEX_TYPE_RG_16; + case PIXMAP_LAYOUT_RGB: return TEX_TYPE_RGB_16; + case PIXMAP_LAYOUT_RGBA: return TEX_TYPE_RGBA_16; + } + + UNREACHABLE; + } + + switch(layout) { + case PIXMAP_LAYOUT_R: return TEX_TYPE_R_8; + case PIXMAP_LAYOUT_RG: return TEX_TYPE_RG_8; + case PIXMAP_LAYOUT_RGB: return TEX_TYPE_RGB_8; + case PIXMAP_LAYOUT_RGBA: return TEX_TYPE_RGBA_8; + } + + UNREACHABLE; +} + +static bool parse_format(const char *val, PixmapFormat *out) { + if(!val) { + return true; + } + + uint channels = 0; + + char buf[strlen(val) + 1]; + strcpy(buf, val); + + if(*val) { + char *c = strrchr(buf, 0) - 1; + while(isspace(*c) && c >= buf) { + *c-- = 0; + } + } + + val = buf; + + for(uint i = 4; i >= 1; --i) { + if(!SDL_strncasecmp(val, "rgba", i)) { + channels = i; + break; + } + } + + if(channels < 1) { + log_warn("Invalid format '%s': bad channels specification, expected one of: RGBA, RGB, RG, R", val); + return false; + } + + assert(channels <= 4); + + char *end; + uint depth = strtol(val + channels, &end, 10); + + if(val + channels == end) { + log_warn("Invalid format '%s': bad depth specification, expected an integer", val); + return false; + } + + if(depth != 8 && depth != 16 && depth != 32) { + log_warn("Invalid format '%s': invalid bit depth %d, only 8, 16, and 32 are currently supported", val, depth); + return false; + } + + while(isspace(*end)) { + ++end; + } + + bool is_float = false; + + if(*end) { + if(!SDL_strcasecmp(end, "u") || !SDL_strcasecmp(end, "uint")) { + // is_float = false; + } else if(!SDL_strcasecmp(end, "f") || !SDL_strcasecmp(end, "float")) { + is_float = true; + } else { + log_warn("Invalid format '%s': bad type specification, expected one of: U, UINT, F, FLOAT, or nothing", val); + return false; + } + } + + if(depth == 32 && !is_float) { + log_warn("Invalid format '%s': bit depth %d is currently only supported for floating point pixels", val, depth); + return false; + } else if(depth < 32 && is_float) { + log_warn("Bit depth %d is currently not supported for floating point pixels, promoting to 32", depth); + depth = 32; + } + + *out = PIXMAP_MAKE_FORMAT(channels, depth) | (is_float * PIXMAP_FLOAT_BIT); + return true; +} + typedef struct TextureLoadData { Pixmap pixmap; TextureParams params; @@ -146,7 +258,6 @@ static void* load_texture_begin(const char *path, uint flags) { TextureLoadData ld = { .params = { - .type = TEX_TYPE_RGBA, .filter = { .mag = TEX_FILTER_LINEAR, .min = TEX_FILTER_LINEAR_MIPMAP_LINEAR, @@ -165,11 +276,14 @@ static void* load_texture_begin(const char *path, uint flags) { } }; + PixmapFormat override_format = 0; + if(strendswith(path, TEX_EXTENSION)) { char *str_filter_min = NULL; char *str_filter_mag = NULL; char *str_wrap_s = NULL; char *str_wrap_t = NULL; + char *str_format = NULL; if(!parse_keyvalue_file_with_spec(path, (KVSpec[]) { { "source", .out_str = &source_allocated }, @@ -177,6 +291,7 @@ static void* load_texture_begin(const char *path, uint flags) { { "filter_mag", .out_str = &str_filter_mag }, { "wrap_s", .out_str = &str_wrap_s }, { "wrap_t", .out_str = &str_wrap_t }, + { "format", .out_str = &str_format }, { "mipmaps", .out_int = (int*)&ld.params.mipmaps }, { "anisotropy", .out_int = (int*)&ld.params.anisotropy }, { NULL } @@ -215,6 +330,15 @@ static void* load_texture_begin(const char *path, uint flags) { parse_wrap(str_wrap_t, &ld.params.wrap.t); free(str_wrap_t); + + bool format_ok = parse_format(str_format, &override_format); + free(str_format); + + if(!format_ok) { + free(source_allocated); + log_warn("%s: bad or unsupported pixel format specification", path); + return NULL; + } } if(!pixmap_load_file(source, &ld.pixmap)) { @@ -231,6 +355,15 @@ static void* load_texture_begin(const char *path, uint flags) { pixmap_flip_to_origin_inplace(&ld.pixmap, PIXMAP_ORIGIN_TOPLEFT); } + override_format = override_format ? override_format : ld.pixmap.format; + ld.params.type = pixmap_format_to_texture_type(override_format); + log_debug("%s: %d channels, %d bits per channel, %s", + path, + PIXMAP_FORMAT_LAYOUT(override_format), + PIXMAP_FORMAT_DEPTH(override_format), + PIXMAP_FORMAT_IS_FLOAT(override_format) ? "float" : "uint" + ); + if(ld.params.mipmaps == 0) { ld.params.mipmaps = TEX_MIPMAPS_MAX; } diff --git a/src/util/pixmap.c b/src/util/pixmap.c index 5d62906c..f917961c 100644 --- a/src/util/pixmap.c +++ b/src/util/pixmap.c @@ -14,69 +14,120 @@ // NOTE: this is pretty stupid and not at all optimized, patches welcome -#define _CONV_FUNCNAME convert_x8_to_x8 +#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_x8_to_x16 +#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_x8_to_x32 +#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_x16_to_x8 +#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_x16_to_x16 +#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_x16_to_x32 +#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_x32_to_x8 +#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_x32_to_x16 +#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_x32_to_x32 +#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, @@ -86,7 +137,7 @@ typedef void (*convfunc_t)( void *default_pixel ); -#define CONV(in, out) { convert_x##in##_to_x##out, in, out } +#define CONV(in, in_depth, out, out_depth) { convert_##in##_to_##out, in_depth, out_depth } struct conversion_def { convfunc_t func; @@ -95,17 +146,25 @@ struct conversion_def { }; struct conversion_def conversion_table[] = { - CONV(8, 8), - CONV(8, 16), - CONV(8, 32), + CONV(u8, 8, u8, 8), + CONV(u8, 8, u16, 16), + CONV(u8, 8, u32, 32), + CONV(u8, 8, f32, 32 | DEPTH_FLOAT_BIT), - CONV(16, 8), - CONV(16, 16), - CONV(16, 32), + CONV(u16, 16, u8, 8), + CONV(u16, 16, u16, 16), + CONV(u16, 16, u32, 32), + CONV(u16, 16, f32, 32 | DEPTH_FLOAT_BIT), - CONV(32, 8), - CONV(32, 16), - CONV(32, 32), + 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 } }; @@ -124,11 +183,13 @@ static void* default_pixel(uint depth) { static uint8_t default_u8[] = { 0, 0, 0, UINT8_MAX }; static uint16_t default_u16[] = { 0, 0, 0, UINT16_MAX }; static uint32_t default_u32[] = { 0, 0, 0, UINT32_MAX }; + static float default_f32[] = { 0, 0, 0, 1.0f }; switch(depth) { case 8: return default_u8; case 16: return default_u16; case 32: return default_u32; + case 32 | DEPTH_FLOAT_BIT: return default_f32; default: UNREACHABLE; } } @@ -171,8 +232,8 @@ void pixmap_convert(const Pixmap *src, Pixmap *dst, PixmapFormat format) { dst->format = format; struct conversion_def *cv = find_conversion( - PIXMAP_FORMAT_DEPTH(src->format), - PIXMAP_FORMAT_DEPTH(dst->format) + 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( @@ -181,7 +242,7 @@ void pixmap_convert(const Pixmap *src, Pixmap *dst, PixmapFormat format) { num_pixels, src->data.untyped, dst->data.untyped, - default_pixel(PIXMAP_FORMAT_DEPTH(dst->format)) + default_pixel(PIXMAP_FORMAT_DEPTH(dst->format) | (PIXMAP_FORMAT_IS_FLOAT(dst->format) * DEPTH_FLOAT_BIT)) ); } diff --git a/src/util/pixmap.h b/src/util/pixmap.h index 9eff2e43..5930b042 100644 --- a/src/util/pixmap.h +++ b/src/util/pixmap.h @@ -23,98 +23,115 @@ typedef enum PixmapOrigin { PIXMAP_ORIGIN_BOTTOMLEFT, } PixmapOrigin; +enum { + PIXMAP_FLOAT_BIT = 0x80, +}; + #define PIXMAP_MAKE_FORMAT(layout, depth) (((depth) >> 3) | ((layout) << 8)) #define PIXMAP_FORMAT_LAYOUT(format) ((format) >> 8) -#define PIXMAP_FORMAT_DEPTH(format) (((format) & 0xff) << 3) +#define PIXMAP_FORMAT_DEPTH(format) (((format) & ~PIXMAP_FLOAT_BIT & 0xff) << 3) #define PIXMAP_FORMAT_PIXEL_SIZE(format) ((PIXMAP_FORMAT_DEPTH(format) >> 3) * PIXMAP_FORMAT_LAYOUT(format)) +#define PIXMAP_FORMAT_IS_FLOAT(format) ((bool)((format) & PIXMAP_FLOAT_BIT)) typedef enum PixmapFormat { - PIXMAP_FORMAT_R8 = PIXMAP_MAKE_FORMAT(PIXMAP_LAYOUT_R, 8), - PIXMAP_FORMAT_R16 = PIXMAP_MAKE_FORMAT(PIXMAP_LAYOUT_R, 16), - PIXMAP_FORMAT_R32 = PIXMAP_MAKE_FORMAT(PIXMAP_LAYOUT_R, 32), + PIXMAP_FORMAT_R8 = PIXMAP_MAKE_FORMAT(PIXMAP_LAYOUT_R, 8), + PIXMAP_FORMAT_R16 = PIXMAP_MAKE_FORMAT(PIXMAP_LAYOUT_R, 16), + PIXMAP_FORMAT_R32 = PIXMAP_MAKE_FORMAT(PIXMAP_LAYOUT_R, 32), + PIXMAP_FORMAT_R32F = PIXMAP_MAKE_FORMAT(PIXMAP_LAYOUT_R, 32) | PIXMAP_FLOAT_BIT, - PIXMAP_FORMAT_RG8 = PIXMAP_MAKE_FORMAT(PIXMAP_LAYOUT_RG, 8), - PIXMAP_FORMAT_RG16 = PIXMAP_MAKE_FORMAT(PIXMAP_LAYOUT_RG, 16), - PIXMAP_FORMAT_RG32 = PIXMAP_MAKE_FORMAT(PIXMAP_LAYOUT_RG, 32), + PIXMAP_FORMAT_RG8 = PIXMAP_MAKE_FORMAT(PIXMAP_LAYOUT_RG, 8), + PIXMAP_FORMAT_RG16 = PIXMAP_MAKE_FORMAT(PIXMAP_LAYOUT_RG, 16), + PIXMAP_FORMAT_RG32 = PIXMAP_MAKE_FORMAT(PIXMAP_LAYOUT_RG, 32), + PIXMAP_FORMAT_RG32F = PIXMAP_MAKE_FORMAT(PIXMAP_LAYOUT_RG, 32) | PIXMAP_FLOAT_BIT, - PIXMAP_FORMAT_RGB8 = PIXMAP_MAKE_FORMAT(PIXMAP_LAYOUT_RGB, 8), - PIXMAP_FORMAT_RGB16 = PIXMAP_MAKE_FORMAT(PIXMAP_LAYOUT_RGB, 16), - PIXMAP_FORMAT_RGB32 = PIXMAP_MAKE_FORMAT(PIXMAP_LAYOUT_RGB, 32), + PIXMAP_FORMAT_RGB8 = PIXMAP_MAKE_FORMAT(PIXMAP_LAYOUT_RGB, 8), + PIXMAP_FORMAT_RGB16 = PIXMAP_MAKE_FORMAT(PIXMAP_LAYOUT_RGB, 16), + PIXMAP_FORMAT_RGB32 = PIXMAP_MAKE_FORMAT(PIXMAP_LAYOUT_RGB, 32), + PIXMAP_FORMAT_RGB32F = PIXMAP_MAKE_FORMAT(PIXMAP_LAYOUT_RGB, 32) | PIXMAP_FLOAT_BIT, - PIXMAP_FORMAT_RGBA8 = PIXMAP_MAKE_FORMAT(PIXMAP_LAYOUT_RGBA, 8), - PIXMAP_FORMAT_RGBA16 = PIXMAP_MAKE_FORMAT(PIXMAP_LAYOUT_RGBA, 16), - PIXMAP_FORMAT_RGBA32 = PIXMAP_MAKE_FORMAT(PIXMAP_LAYOUT_RGBA, 32), + PIXMAP_FORMAT_RGBA8 = PIXMAP_MAKE_FORMAT(PIXMAP_LAYOUT_RGBA, 8), + PIXMAP_FORMAT_RGBA16 = PIXMAP_MAKE_FORMAT(PIXMAP_LAYOUT_RGBA, 16), + PIXMAP_FORMAT_RGBA32 = PIXMAP_MAKE_FORMAT(PIXMAP_LAYOUT_RGBA, 32), + PIXMAP_FORMAT_RGBA32F = PIXMAP_MAKE_FORMAT(PIXMAP_LAYOUT_RGBA, 32) | PIXMAP_FLOAT_BIT, } PixmapFormat; -#define PIXMAP_PIXEL_STRUCT_R(depth) union { \ - uint##depth##_t values[1]; \ +#define PIXMAP_PIXEL_STRUCT_R(type) union { \ + type values[1]; \ struct { \ - uint##depth##_t r; \ + type r; \ }; \ } -#define PIXMAP_PIXEL_STRUCT_RG(depth) union { \ - uint##depth##_t values[2]; \ +#define PIXMAP_PIXEL_STRUCT_RG(type) union { \ + type values[2]; \ struct { \ - uint##depth##_t r; \ - uint##depth##_t g; \ + type r; \ + type g; \ }; \ } -#define PIXMAP_PIXEL_STRUCT_RGB(depth) union { \ - uint##depth##_t values[3]; \ +#define PIXMAP_PIXEL_STRUCT_RGB(type) union { \ + type values[3]; \ struct { \ - uint##depth##_t r; \ - uint##depth##_t g; \ - uint##depth##_t b; \ + type r; \ + type g; \ + type b; \ }; \ } -#define PIXMAP_PIXEL_STRUCT_RGBA(depth) union { \ - uint##depth##_t values[4]; \ +#define PIXMAP_PIXEL_STRUCT_RGBA(type) union { \ + type values[4]; \ struct { \ - uint##depth##_t r; \ - uint##depth##_t g; \ - uint##depth##_t b; \ - uint##depth##_t a; \ + type r; \ + type g; \ + type b; \ + type a; \ }; \ } -typedef PIXMAP_PIXEL_STRUCT_R(8) PixelR8; -typedef PIXMAP_PIXEL_STRUCT_R(16) PixelR16; -typedef PIXMAP_PIXEL_STRUCT_R(32) PixelR32; +typedef PIXMAP_PIXEL_STRUCT_R(uint8_t) PixelR8; +typedef PIXMAP_PIXEL_STRUCT_R(uint16_t) PixelR16; +typedef PIXMAP_PIXEL_STRUCT_R(uint32_t) PixelR32; +typedef PIXMAP_PIXEL_STRUCT_R(float) PixelR32F; -typedef PIXMAP_PIXEL_STRUCT_RG(8) PixelRG8; -typedef PIXMAP_PIXEL_STRUCT_RG(16) PixelRG16; -typedef PIXMAP_PIXEL_STRUCT_RG(32) PixelRG32; +typedef PIXMAP_PIXEL_STRUCT_RG(uint8_t) PixelRG8; +typedef PIXMAP_PIXEL_STRUCT_RG(uint16_t) PixelRG16; +typedef PIXMAP_PIXEL_STRUCT_RG(uint32_t) PixelRG32; +typedef PIXMAP_PIXEL_STRUCT_RG(float) PixelRG32F; -typedef PIXMAP_PIXEL_STRUCT_RGB(8) PixelRGB8; -typedef PIXMAP_PIXEL_STRUCT_RGB(16) PixelRGB16; -typedef PIXMAP_PIXEL_STRUCT_RGB(32) PixelRGB32; +typedef PIXMAP_PIXEL_STRUCT_RGB(uint8_t) PixelRGB8; +typedef PIXMAP_PIXEL_STRUCT_RGB(uint16_t) PixelRGB16; +typedef PIXMAP_PIXEL_STRUCT_RGB(uint32_t) PixelRGB32; +typedef PIXMAP_PIXEL_STRUCT_RGB(float) PixelRGB32F; -typedef PIXMAP_PIXEL_STRUCT_RGBA(8) PixelRGBA8; -typedef PIXMAP_PIXEL_STRUCT_RGBA(16) PixelRGBA16; -typedef PIXMAP_PIXEL_STRUCT_RGBA(32) PixelRGBA32; +typedef PIXMAP_PIXEL_STRUCT_RGBA(uint8_t) PixelRGBA8; +typedef PIXMAP_PIXEL_STRUCT_RGBA(uint16_t) PixelRGBA16; +typedef PIXMAP_PIXEL_STRUCT_RGBA(uint32_t) PixelRGBA32; +typedef PIXMAP_PIXEL_STRUCT_RGBA(float) PixelRGBA32F; typedef struct Pixmap { union { - void *untyped; + void *untyped; - PixelR8 *r8; - PixelR16 *r16; - PixelR32 *r32; + PixelR8 *r8; + PixelR16 *r16; + PixelR32 *r32; + PixelR32F *r32f; - PixelRG8 *rg8; - PixelRG16 *rg16; - PixelRG32 *rg32; + PixelRG8 *rg8; + PixelRG16 *rg16; + PixelRG32 *rg32; + PixelRG32F *rg32f; - PixelRGB8 *rgb8; - PixelRGB16 *rgb16; - PixelRGB32 *rgb32; + PixelRGB8 *rgb8; + PixelRGB16 *rgb16; + PixelRGB32 *rgb32; + PixelRGB32F *rgb32f; - PixelRGBA8 *rgba8; - PixelRGBA16 *rgba16; - PixelRGBA32 *rgba32; + PixelRGBA8 *rgba8; + PixelRGBA16 *rgba16; + PixelRGBA32 *rgba32; + PixelRGBA32F *rgba32f; } data; size_t width; diff --git a/src/util/pixmap_conversion.inc.h b/src/util/pixmap_conversion.inc.h index 7b44d83b..425b1e94 100644 --- a/src/util/pixmap_conversion.inc.h +++ b/src/util/pixmap_conversion.inc.h @@ -1,4 +1,7 @@ +#define _CONV_IN_IS_FLOAT (_CONV_IN_MAX == 1.0f) +#define _CONV_OUT_IS_FLOAT (_CONV_OUT_MAX == 1.0f) + static void _CONV_FUNCNAME( size_t in_elements, size_t out_elements, @@ -16,7 +19,15 @@ static void _CONV_FUNCNAME( _CONV_OUT_TYPE fill; if(element < in_elements) { - if(_CONV_OUT_MAX >= _CONV_IN_MAX) { + if(_CONV_IN_IS_FLOAT) { + if(_CONV_OUT_IS_FLOAT) { + fill = *buf_in++; + } else { + fill = (_CONV_OUT_TYPE)round(clamp(*buf_in++, 0, 1) * _CONV_OUT_MAX); + } + } else if(_CONV_OUT_IS_FLOAT) { + fill = *buf_in++ * (1.0f / _CONV_IN_MAX); + } else if(_CONV_OUT_MAX >= _CONV_IN_MAX) { fill = *buf_in++ * (_CONV_OUT_MAX / _CONV_IN_MAX); } else { fill = (_CONV_OUT_TYPE)round(*buf_in++ * ((float)_CONV_OUT_MAX / _CONV_IN_MAX)); @@ -37,3 +48,5 @@ static void _CONV_FUNCNAME( #undef _CONV_IN_TYPE #undef _CONV_OUT_MAX #undef _CONV_OUT_TYPE +#undef _CONV_IN_IS_FLOAT +#undef _CONV_OUT_IS_FLOAT