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