[Mesa-dev] [PATCH v2 01/29] mesa: Add an implementation of a master convert function.
Iago Toral Quiroga
itoral at igalia.com
Mon Dec 1 03:04:54 PST 2014
From: Jason Ekstrand <jason.ekstrand at intel.com>
v2 by Iago Toral <itoral at igalia.com>:
- When testing if we can directly pack we should use the src format to check
if we are packing from an RGBA format. The original code used the dst format
for the ubyte case by mistake.
- Fixed incorrect number of bits for dst, it was computed using the src format
instead of the dst format.
- If the dst format is an array format, check if it is signed. We were only
checking this for the case where it was not an array format, but we need
to know this in both scenarios.
- Fixed incorrect swizzle transform for the cases where we convert between
array formats.
- Compute is_signed and bits only once and for the dst format. We were
computing these for the src format too but they were overwritten by the
dst values immediately after.
- Be more careful when selecting the integer path. Specifically, check that
both src and dst are integer types. Checking only one of them should suffice
since OpenGL does not allow conversions between normalized and integer types,
but putting extra care here makes sense and also makes the actual requirements
for this path more clear.
- The format argument for pack functions is the destination format we are
packing to, not the source format (which has to be RGBA).
- Expose RGBA8888_* to other files. These will come in handy when in need to
test if a given array format is RGBA or in need to pass RGBA formats to
mesa_format_convert.
v3 by Samuel Iglesias <siglesias at igalia.com>:
- Add an RGBA8888_INT definition.
v4 by Iago Toral <itoral at igalia.com> after review by Jason Ekstrand:
- Added documentation for _mesa_format_convert.
- Added additional explanatory comments for integer conversions.
- Ensure that we use _messa_swizzle_and_convert for all signed source formats.
- Squashed: do not directly (un)pack to RGBA UINT if the source is not unsigned.
---
src/mesa/main/format_utils.c | 425 +++++++++++++++++++++++++++++++++++++++++++
src/mesa/main/format_utils.h | 10 +
2 files changed, 435 insertions(+)
diff --git a/src/mesa/main/format_utils.c b/src/mesa/main/format_utils.c
index dc63e1f..4db5269 100644
--- a/src/mesa/main/format_utils.c
+++ b/src/mesa/main/format_utils.c
@@ -24,6 +24,431 @@
#include "format_utils.h"
#include "glformats.h"
+#include "format_pack.h"
+#include "format_unpack.h"
+
+mesa_array_format RGBA8888_FLOAT = {{
+ MESA_ARRAY_FORMAT_TYPE_FLOAT,
+ 0,
+ 4,
+ 0, 1, 2, 3,
+ 0, 1
+}};
+
+mesa_array_format RGBA8888_UBYTE = {{
+ MESA_ARRAY_FORMAT_TYPE_UBYTE,
+ 1,
+ 4,
+ 0, 1, 2, 3,
+ 0, 1
+}};
+
+mesa_array_format RGBA8888_UINT = {{
+ MESA_ARRAY_FORMAT_TYPE_UINT,
+ 0,
+ 4,
+ 0, 1, 2, 3,
+ 0, 1
+}};
+
+mesa_array_format RGBA8888_INT = {{
+ MESA_ARRAY_FORMAT_TYPE_INT,
+ 0,
+ 4,
+ 0, 1, 2, 3,
+ 0, 1
+}};
+
+static void
+invert_swizzle(uint8_t dst[4], const uint8_t src[4])
+{
+ int i, j;
+
+ dst[0] = MESA_FORMAT_SWIZZLE_NONE;
+ dst[1] = MESA_FORMAT_SWIZZLE_NONE;
+ dst[2] = MESA_FORMAT_SWIZZLE_NONE;
+ dst[3] = MESA_FORMAT_SWIZZLE_NONE;
+
+ for (i = 0; i < 4; ++i)
+ for (j = 0; j < 4; ++j)
+ if (src[j] == i && dst[i] == MESA_FORMAT_SWIZZLE_NONE)
+ dst[i] = j;
+}
+
+static GLenum
+gl_type_for_array_format_datatype(enum mesa_array_format_datatype type)
+{
+ switch (type) {
+ case MESA_ARRAY_FORMAT_TYPE_UBYTE:
+ return GL_UNSIGNED_BYTE;
+ case MESA_ARRAY_FORMAT_TYPE_USHORT:
+ return GL_UNSIGNED_SHORT;
+ case MESA_ARRAY_FORMAT_TYPE_UINT:
+ return GL_UNSIGNED_INT;
+ case MESA_ARRAY_FORMAT_TYPE_BYTE:
+ return GL_BYTE;
+ case MESA_ARRAY_FORMAT_TYPE_SHORT:
+ return GL_SHORT;
+ case MESA_ARRAY_FORMAT_TYPE_INT:
+ return GL_INT;
+ case MESA_ARRAY_FORMAT_TYPE_HALF:
+ return GL_HALF_FLOAT;
+ case MESA_ARRAY_FORMAT_TYPE_FLOAT:
+ return GL_FLOAT;
+ default:
+ assert(!"Invalid datatype");
+ return GL_NONE;
+ }
+}
+
+/**
+ * This can be used to convert between most color formats.
+ *
+ * Limitations:
+ * - This function doesn't handle GL_COLOR_INDEX or YCBCR formats.
+ * - This function doesn't handle byte-swapping or transferOps, these should
+ * be handled by the caller.
+ *
+ * \param void_dst The address where converted color data will be stored.
+ * The caller must ensure that the buffer is large enough
+ * to hold the converted pixel data.
+ * \param dst_format The destination color format. It can be a mesa_format
+ * or a mesa_array_format represented as an uint32_t.
+ * \param dst_stride The stride of the destination format in bytes.
+ * \param void_src The address of the source color data to convert.
+ * \param src_format The source color format. It can be a mesa_format
+ * or a mesa_array_format represented as an uint32_t.
+ * \param src_stride The stride of the source format in bytes.
+ * \param width The width, in pixels, of the source image to convert.
+ * \param height The height, in pixels, of the source image to convert.
+ */
+void
+_mesa_format_convert(void *void_dst, uint32_t dst_format, size_t dst_stride,
+ void *void_src, uint32_t src_format, size_t src_stride,
+ size_t width, size_t height)
+{
+ uint8_t *dst = (uint8_t *)void_dst;
+ uint8_t *src = (uint8_t *)void_src;
+ mesa_array_format src_array_format, dst_array_format;
+ uint8_t src2dst[4], src2rgba[4], rgba2dst[4], dst2rgba[4];
+ GLenum src_gl_type, dst_gl_type, common_gl_type;
+ bool normalized, dst_integer, src_integer, is_signed;
+ uint8_t (*tmp_ubyte)[4];
+ float (*tmp_float)[4];
+ uint32_t (*tmp_uint)[4];
+ int i, bits;
+ size_t row;
+
+ if (src_format & MESA_ARRAY_FORMAT_BIT) {
+ src_array_format.as_uint = src_format;
+ } else {
+ assert(_mesa_is_format_color_format(src_format));
+ src_array_format.as_uint = _mesa_format_to_array_format(src_format);
+ }
+
+ if (dst_format & MESA_ARRAY_FORMAT_BIT) {
+ dst_array_format.as_uint = dst_format;
+ } else {
+ assert(_mesa_is_format_color_format(dst_format));
+ dst_array_format.as_uint = _mesa_format_to_array_format(dst_format);
+ }
+
+ /* Handle the cases where we can directly unpack */
+ if (!(src_format & MESA_ARRAY_FORMAT_BIT)) {
+ if (dst_array_format.as_uint == RGBA8888_FLOAT.as_uint) {
+ for (row = 0; row < height; ++row) {
+ _mesa_unpack_rgba_row(src_format, width,
+ src, (float (*)[4])dst);
+ src += src_stride;
+ dst += dst_stride;
+ }
+ return;
+ } else if (dst_array_format.as_uint == RGBA8888_UBYTE.as_uint) {
+ assert(!_mesa_is_format_integer_color(src_format));
+ for (row = 0; row < height; ++row) {
+ _mesa_unpack_ubyte_rgba_row(src_format, width,
+ src, (uint8_t (*)[4])dst);
+ src += src_stride;
+ dst += dst_stride;
+ }
+ return;
+ } else if (dst_array_format.as_uint == RGBA8888_UINT.as_uint &&
+ _mesa_is_format_unsigned(src_format)) {
+ assert(_mesa_is_format_integer_color(src_format));
+ for (row = 0; row < height; ++row) {
+ _mesa_unpack_uint_rgba_row(src_format, width,
+ src, (uint32_t (*)[4])dst);
+ src += src_stride;
+ dst += dst_stride;
+ }
+ return;
+ }
+ }
+
+ /* Handle the cases where we can directly pack */
+ if (!(dst_format & MESA_ARRAY_FORMAT_BIT)) {
+ if (src_array_format.as_uint == RGBA8888_FLOAT.as_uint) {
+ for (row = 0; row < height; ++row) {
+ _mesa_pack_float_rgba_row(dst_format, width,
+ (const float (*)[4])src, dst);
+ src += src_stride;
+ dst += dst_stride;
+ }
+ return;
+ } else if (src_array_format.as_uint == RGBA8888_UBYTE.as_uint) {
+ assert(!_mesa_is_format_integer_color(dst_format));
+ for (row = 0; row < height; ++row) {
+ _mesa_pack_ubyte_rgba_row(dst_format, width,
+ (const uint8_t (*)[4])src, dst);
+ src += src_stride;
+ dst += dst_stride;
+ }
+ return;
+ } else if (src_array_format.as_uint == RGBA8888_UINT.as_uint &&
+ _mesa_is_format_unsigned(dst_format)) {
+ assert(_mesa_is_format_integer_color(dst_format));
+ for (row = 0; row < height; ++row) {
+ _mesa_pack_uint_rgba_row(dst_format, width,
+ (const uint32_t (*)[4])src, dst);
+ src += src_stride;
+ dst += dst_stride;
+ }
+ return;
+ }
+ }
+
+ /* Handle conversions between array formats */
+ normalized = false;
+ if (src_array_format.as_uint) {
+ src_gl_type = gl_type_for_array_format_datatype(src_array_format.type);
+
+ src2rgba[0] = src_array_format.swizzle_x;
+ src2rgba[1] = src_array_format.swizzle_y;
+ src2rgba[2] = src_array_format.swizzle_z;
+ src2rgba[3] = src_array_format.swizzle_w;
+
+ normalized = src_array_format.normalized;
+ }
+
+ if (dst_array_format.as_uint) {
+ dst_gl_type = gl_type_for_array_format_datatype(dst_array_format.type);
+
+ dst2rgba[0] = dst_array_format.swizzle_x;
+ dst2rgba[1] = dst_array_format.swizzle_y;
+ dst2rgba[2] = dst_array_format.swizzle_z;
+ dst2rgba[3] = dst_array_format.swizzle_w;
+
+ invert_swizzle(rgba2dst, dst2rgba);
+
+ normalized |= dst_array_format.normalized;
+ }
+
+ if (src_array_format.as_uint && dst_array_format.as_uint) {
+ assert(src_array_format.normalized == dst_array_format.normalized);
+
+ for (i = 0; i < 4; i++) {
+ if (rgba2dst[i] > MESA_FORMAT_SWIZZLE_W) {
+ src2dst[i] = rgba2dst[i];
+ } else {
+ src2dst[i] = src2rgba[rgba2dst[i]];
+ }
+ }
+
+ for (row = 0; row < height; ++row) {
+ _mesa_swizzle_and_convert(dst, dst_gl_type, dst_array_format.num_channels,
+ src, src_gl_type, src_array_format.num_channels,
+ src2dst, normalized, width);
+ src += src_stride;
+ dst += dst_stride;
+ }
+ return;
+ }
+
+ /* At this point, we're fresh out of fast-paths and we need to convert
+ * to float, uint32, or, if we're lucky, uint8.
+ */
+ dst_integer = false;
+ src_integer = false;
+
+ if (src_array_format.array_format_bit) {
+ if (!(src_array_format.type & MESA_ARRAY_FORMAT_TYPE_IS_FLOAT) &&
+ !src_array_format.normalized)
+ src_integer = true;
+ } else {
+ switch (_mesa_get_format_datatype(src_format)) {
+ case GL_UNSIGNED_INT:
+ case GL_INT:
+ src_integer = true;
+ break;
+ }
+ }
+
+ /* If the destination format is signed but the source is unsigned, then we
+ * don't loose any data by converting to a signed intermediate format above
+ * and beyond the precision that we loose in the conversion itself. If the
+ * destination is unsigned then, by using an unsigned intermediate format,
+ * we make the conversion function that converts from the source to the
+ * intermediate format take care of truncating at zero. The exception here
+ * is if the intermediate format is float, in which case the first
+ * conversion will leave it signed and the second conversion will truncate
+ * at zero.
+ */
+ if (dst_array_format.array_format_bit) {
+ if (!(dst_array_format.type & MESA_ARRAY_FORMAT_TYPE_IS_FLOAT) &&
+ !dst_array_format.normalized)
+ dst_integer = true;
+ is_signed = dst_array_format.as_uint & MESA_ARRAY_FORMAT_TYPE_IS_SIGNED;
+ bits = 8 * _mesa_array_format_datatype_size(dst_array_format.type);
+ } else {
+ switch (_mesa_get_format_datatype(dst_format)) {
+ case GL_UNSIGNED_NORMALIZED:
+ is_signed = false;
+ break;
+ case GL_SIGNED_NORMALIZED:
+ is_signed = true;
+ break;
+ case GL_FLOAT:
+ is_signed = true;
+ break;
+ case GL_UNSIGNED_INT:
+ is_signed = false;
+ dst_integer = true;
+ break;
+ case GL_INT:
+ is_signed = true;
+ dst_integer = true;
+ break;
+ }
+ bits = _mesa_get_format_max_bits(dst_format);
+ }
+
+ assert(src_integer == dst_integer);
+
+ if (src_integer && dst_integer) {
+ tmp_uint = malloc(width * height * sizeof(*tmp_uint));
+
+ /* The [un]packing functions for unsigned datatypes treat the 32-bit
+ * integer array as signed for signed formats and as unsigned for
+ * unsigned formats. This is a bit of a problem if we ever convert from
+ * a signed to an unsigned format because the unsigned packing function
+ * doesn't know that the input is signed and will treat it as unsigned
+ * and not do the trunctation. The thing that saves us here is that all
+ * of the packed formats are unsigned, so we can just always use
+ * _mesa_swizzle_and_convert for signed formats, which is aware of the
+ * truncation problem.
+ */
+ common_gl_type = is_signed ? GL_INT : GL_UNSIGNED_INT;
+ if (src_array_format.as_uint) {
+ for (row = 0; row < height; ++row) {
+ _mesa_swizzle_and_convert(tmp_uint + row * width, common_gl_type, 4,
+ src, src_gl_type,
+ src_array_format.num_channels,
+ src2rgba, normalized, width);
+ src += src_stride;
+ }
+ } else {
+ for (row = 0; row < height; ++row) {
+ _mesa_unpack_uint_rgba_row(src_format, width,
+ src, tmp_uint + row * width);
+ src += src_stride;
+ }
+ }
+
+ /* At this point, we have already done the truncation if the source is
+ * signed but the destination is unsigned, so no need to force the
+ * _mesa_swizzle_and_convert path.
+ */
+ if (dst_format & MESA_ARRAY_FORMAT_BIT) {
+ for (row = 0; row < height; ++row) {
+ _mesa_swizzle_and_convert(dst, dst_gl_type,
+ dst_array_format.num_channels,
+ tmp_uint + row * width, common_gl_type, 4,
+ rgba2dst, normalized, width);
+ dst += dst_stride;
+ }
+ } else {
+ for (row = 0; row < height; ++row) {
+ _mesa_pack_uint_rgba_row(dst_format, width,
+ (const uint32_t (*)[4])tmp_uint + row * width, dst);
+ dst += dst_stride;
+ }
+ }
+
+ free(tmp_uint);
+ } else if (is_signed || bits > 8) {
+ tmp_float = malloc(width * height * sizeof(*tmp_float));
+
+ if (src_format & MESA_ARRAY_FORMAT_BIT) {
+ for (row = 0; row < height; ++row) {
+ _mesa_swizzle_and_convert(tmp_float + row * width, GL_FLOAT, 4,
+ src, src_gl_type,
+ src_array_format.num_channels,
+ src2rgba, normalized, width);
+ src += src_stride;
+ }
+ } else {
+ for (row = 0; row < height; ++row) {
+ _mesa_unpack_rgba_row(src_format, width,
+ src, tmp_float + row * width);
+ src += src_stride;
+ }
+ }
+
+ if (dst_format & MESA_ARRAY_FORMAT_BIT) {
+ for (row = 0; row < height; ++row) {
+ _mesa_swizzle_and_convert(dst, dst_gl_type,
+ dst_array_format.num_channels,
+ tmp_float + row * width, GL_FLOAT, 4,
+ rgba2dst, normalized, width);
+ dst += dst_stride;
+ }
+ } else {
+ for (row = 0; row < height; ++row) {
+ _mesa_pack_float_rgba_row(dst_format, width,
+ (const float (*)[4])tmp_float + row * width, dst);
+ dst += dst_stride;
+ }
+ }
+
+ free(tmp_float);
+ } else {
+ tmp_ubyte = malloc(width * height * sizeof(*tmp_ubyte));
+
+ if (src_format & MESA_ARRAY_FORMAT_BIT) {
+ for (row = 0; row < height; ++row) {
+ _mesa_swizzle_and_convert(tmp_ubyte + row * width, GL_UNSIGNED_BYTE, 4,
+ src, src_gl_type,
+ src_array_format.num_channels,
+ src2rgba, normalized, width);
+ src += src_stride;
+ }
+ } else {
+ for (row = 0; row < height; ++row) {
+ _mesa_unpack_ubyte_rgba_row(src_format, width,
+ src, tmp_ubyte + row * width);
+ src += src_stride;
+ }
+ }
+
+ if (dst_format & MESA_ARRAY_FORMAT_BIT) {
+ for (row = 0; row < height; ++row) {
+ _mesa_swizzle_and_convert(dst, dst_gl_type,
+ dst_array_format.num_channels,
+ tmp_ubyte + row * width, GL_UNSIGNED_BYTE, 4,
+ rgba2dst, normalized, width);
+ dst += dst_stride;
+ }
+ } else {
+ for (row = 0; row < height; ++row) {
+ _mesa_pack_ubyte_rgba_row(dst_format, width,
+ (const uint8_t (*)[4])tmp_ubyte + row * width, dst);
+ dst += dst_stride;
+ }
+ }
+
+ free(tmp_ubyte);
+ }
+}
static const uint8_t map_identity[7] = { 0, 1, 2, 3, 4, 5, 6 };
static const uint8_t map_3210[7] = { 3, 2, 1, 0, 4, 5, 6 };
diff --git a/src/mesa/main/format_utils.h b/src/mesa/main/format_utils.h
index 6d27752..e41f6c7 100644
--- a/src/mesa/main/format_utils.h
+++ b/src/mesa/main/format_utils.h
@@ -34,6 +34,11 @@
#include "imports.h"
#include "macros.h"
+extern mesa_array_format RGBA8888_FLOAT;
+extern mesa_array_format RGBA8888_UBYTE;
+extern mesa_array_format RGBA8888_UINT;
+extern mesa_array_format RGBA8888_INT;
+
/* Only guaranteed to work for BITS <= 32 */
#define MAX_UINT(BITS) ((BITS) == 32 ? UINT32_MAX : ((1u << (BITS)) - 1))
#define MAX_INT(BITS) ((int)MAX_UINT((BITS) - 1))
@@ -172,4 +177,9 @@ _mesa_swizzle_and_convert(void *dst, GLenum dst_type, int num_dst_channels,
const void *src, GLenum src_type, int num_src_channels,
const uint8_t swizzle[4], bool normalized, int count);
+void
+_mesa_format_convert(void *void_dst, uint32_t dst_format, size_t dst_stride,
+ void *void_src, uint32_t src_format, size_t src_stride,
+ size_t width, size_t height);
+
#endif
--
1.9.1
More information about the mesa-dev
mailing list