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 = <channels><depth>[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.
This commit is contained in:
Andrei Alexeyev 2018-10-17 23:11:27 +03:00
parent 56e94dbff6
commit b13f98c681
No known key found for this signature in database
GPG key ID: 363707CD4C7FE8A4
8 changed files with 400 additions and 104 deletions

View file

@ -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 {

View file

@ -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));

View file

@ -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;

View file

@ -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));

View file

@ -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;
}

View file

@ -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))
);
}

View file

@ -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;

View file

@ -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