Browse Source

Add a pixelFormat(PixelFormat, UnsignedInt, bool) helper.

Useful for creating pixel formats with different channel count,
adding/removing the sRGB bit and such. Counterpart to
vertexFormat(VertexFormat, UnsignedInt, bool) that got added back in
2020.06 already.
pull/620/head
Vladimír Vondruš 3 years ago
parent
commit
17ab578443
  1. 5
      doc/changelog.dox
  2. 12
      doc/snippets/Magnum.cpp
  3. 43
      src/Magnum/PixelFormat.cpp
  4. 42
      src/Magnum/PixelFormat.h
  5. 104
      src/Magnum/Test/PixelFormatTest.cpp
  6. 6
      src/Magnum/VertexFormat.h

5
doc/changelog.dox

@ -55,8 +55,9 @@ See also:
@ref DebugTools::CompareImage
- New @ref pixelFormatChannelFormat(), @ref pixelFormatChannelCount(),
@ref isPixelFormatNormalized(), @ref isPixelFormatIntegral(),
@ref isPixelFormatFloatingPoint(), @ref isPixelFormatSrgb() and
@ref isPixelFormatDepthOrStencil() helpers
@ref isPixelFormatFloatingPoint(), @ref isPixelFormatSrgb(),
@ref isPixelFormatDepthOrStencil() and
@ref pixelFormat(PixelFormat, UnsignedInt, bool) helpers
- New @ref Degh, @ref Radh, @ref Range1Dh, @ref Range2Dh and @ref Range3Dh
typedefs for half-float angles and ranges
- New @ref Range1Dui, @ref Range2Dui and @ref Range3Dui typedefs for unsigned

12
doc/snippets/Magnum.cpp

@ -333,4 +333,16 @@ VertexFormat tangentFormat = vertexFormat(
static_cast<void>(tangentFormat);
}
{
/* [pixelFormat] */
PixelFormat grayscaleFormat = DOXYGEN_ELLIPSIS({});
PixelFormat rgbFormat = pixelFormat(
grayscaleFormat,
pixelFormatChannelCount(grayscaleFormat) == 2 ? 4 : 3,
isPixelFormatSrgb(grayscaleFormat));
/* [pixelFormat] */
static_cast<void>(rgbFormat);
}
}

43
src/Magnum/PixelFormat.cpp

@ -684,6 +684,49 @@ bool isPixelFormatDepthOrStencil(const PixelFormat format) {
CORRADE_ASSERT_UNREACHABLE("isPixelFormatDepthOrStencil(): invalid format" << format, {});
}
PixelFormat pixelFormat(const PixelFormat format, const UnsignedInt channelCount, const bool srgb) {
CORRADE_ASSERT(!isPixelFormatImplementationSpecific(format),
"pixelFormat(): can't assemble a format out of an implementation-specific format" << reinterpret_cast<void*>(pixelFormatUnwrap(format)), {});
CORRADE_ASSERT(!isPixelFormatDepthOrStencil(format),
"pixelFormat(): can't assemble a format out of" << format, {});
PixelFormat channelFormat = pixelFormatChannelFormat(format);
/* First turn the format into a sRGB one or remove the sRGB property, if
requested. The [RGBA]8Srgb formats follow [RGBA]8Unorm in the same order
so it's just constant addition / subtraction for all four variants. */
if(srgb && channelFormat != PixelFormat::R8Srgb) {
CORRADE_ASSERT(channelFormat == PixelFormat::R8Unorm,
"pixelFormat():" << format << "can't be made sRGB", {});
channelFormat = PixelFormat(UnsignedInt(channelFormat) - UnsignedInt(PixelFormat::R8Unorm) + UnsignedInt(PixelFormat::R8Srgb));
} else if(!srgb && channelFormat == PixelFormat::R8Srgb) {
channelFormat = PixelFormat(UnsignedInt(channelFormat) - UnsignedInt(PixelFormat::R8Srgb) + UnsignedInt(PixelFormat::R8Unorm));
}
CORRADE_ASSERT(channelCount >= 1 && channelCount <= 4,
"pixelFormat(): invalid component count" << channelCount, {});
/* The two-, three- and four-channel variants follow each other, so it's
just addition again. There may be packed formats in the future, so
whitelist for the known set of single-channel formats. */
if(channelFormat == PixelFormat::R8Unorm ||
channelFormat == PixelFormat::R8Snorm ||
channelFormat == PixelFormat::R8Srgb ||
channelFormat == PixelFormat::R8UI ||
channelFormat == PixelFormat::R8I ||
channelFormat == PixelFormat::R16Unorm ||
channelFormat == PixelFormat::R16Snorm ||
channelFormat == PixelFormat::R16UI ||
channelFormat == PixelFormat::R16I ||
channelFormat == PixelFormat::R32UI ||
channelFormat == PixelFormat::R32I ||
channelFormat == PixelFormat::R16F ||
channelFormat == PixelFormat::R32F)
return PixelFormat(UnsignedInt(channelFormat) + channelCount - 1);
CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
namespace {
#ifndef DOXYGEN_GENERATING_OUTPUT /* It gets *really* confused */

42
src/Magnum/PixelFormat.h

@ -26,7 +26,7 @@
*/
/** @file
* @brief Enum @ref Magnum::PixelFormat, @ref Magnum::CompressedPixelFormat, function @ref Magnum::pixelFormatSize(), @ref Magnum::pixelFormatChannelFormat(), @ref Magnum::pixelFormatChannelCount(), @ref Magnum::isPixelFormatNormalized(), @ref Magnum::isPixelFormatNormalized(), @ref Magnum::isPixelFormatIntegral(), @ref Magnum::isPixelFormatFloatingPoint(), @ref Magnum::isPixelFormatSrgb(), @ref Magnum::isPixelFormatDepthOrStencil(), @ref Magnum::isPixelFormatImplementationSpecific(), @ref Magnum::pixelFormatWrap(), @ref Magnum::pixelFormatUnwrap(), @ref Magnum::compressedPixelFormatBlockSize(), @ref Magnum::compressedPixelFormatBlockDataSize(), @ref Magnum::isCompressedPixelFormatImplementationSpecific(), @ref Magnum::compressedPixelFormatWrap(), @ref Magnum::compressedPixelFormatUnwrap()
* @brief Enum @ref Magnum::PixelFormat, @ref Magnum::CompressedPixelFormat, function @ref Magnum::pixelFormatSize(), @ref Magnum::pixelFormatChannelFormat(), @ref Magnum::pixelFormatChannelCount(), @ref Magnum::isPixelFormatNormalized(), @ref Magnum::isPixelFormatNormalized(), @ref Magnum::isPixelFormatIntegral(), @ref Magnum::isPixelFormatFloatingPoint(), @ref Magnum::isPixelFormatSrgb(), @ref Magnum::isPixelFormatDepthOrStencil(), @ref Magnum::pixelFormat(), @ref Magnum::isPixelFormatImplementationSpecific(), @ref Magnum::pixelFormatWrap(), @ref Magnum::pixelFormatUnwrap(), @ref Magnum::compressedPixelFormatBlockSize(), @ref Magnum::compressedPixelFormatBlockDataSize(), @ref Magnum::isCompressedPixelFormatImplementationSpecific(), @ref Magnum::compressedPixelFormatWrap(), @ref Magnum::compressedPixelFormatUnwrap()
*/
#include <Corrade/Utility/Assert.h>
@ -68,8 +68,9 @@ See documentation of each value for more information about the mapping.
See also @ref pixelFormatSize(), @ref pixelFormatChannelFormat(),
@ref pixelFormatChannelCount(), @ref isPixelFormatNormalized(),
@ref isPixelFormatIntegral(), @ref isPixelFormatFloatingPoint(),
@ref isPixelFormatSrgb() and @ref isPixelFormatDepthOrStencil() for querying
various aspects of a format.
@ref isPixelFormatSrgb(), @ref isPixelFormatDepthOrStencil() and
@ref pixelFormat() for querying various aspects of a format and assembling it
from a set of singular properties.
@see @ref CompressedPixelFormat, @ref Image, @ref ImageView, @ref VertexFormat
*/
enum class PixelFormat: UnsignedInt {
@ -828,6 +829,7 @@ For any pixel format, exactly one of @ref isPixelFormatNormalized(),
@cpp true @ce.
@see @ref isPixelFormatImplementationSpecific(),
@ref isPixelFormatDepthOrStencil(), @ref isPixelFormatSrgb(),
@ref pixelFormat(PixelFormat, UnsignedInt, bool),
@ref isVertexFormatNormalized()
*/
MAGNUM_EXPORT bool isPixelFormatNormalized(PixelFormat format);
@ -845,7 +847,8 @@ For any pixel format, exactly one of @ref isPixelFormatNormalized(),
@ref isPixelFormatIntegral() and @ref isPixelFormatFloatingPoint() returns
@cpp true @ce.
@see @ref isPixelFormatImplementationSpecific(),
@ref isPixelFormatDepthOrStencil(), @ref isPixelFormatSrgb()
@ref isPixelFormatDepthOrStencil(), @ref isPixelFormatSrgb(),
@ref pixelFormat(PixelFormat, UnsignedInt, bool)
*/
MAGNUM_EXPORT bool isPixelFormatIntegral(PixelFormat format);
@ -863,7 +866,8 @@ For any pixel format, exactly one of @ref isPixelFormatNormalized(),
@ref isPixelFormatIntegral() and @ref isPixelFormatFloatingPoint() returns
@cpp true @ce.
@see @ref isPixelFormatImplementationSpecific(),
@ref isPixelFormatDepthOrStencil(), @ref isPixelFormatSrgb()
@ref isPixelFormatDepthOrStencil(), @ref isPixelFormatSrgb(),
@ref pixelFormat(PixelFormat, UnsignedInt, bool)
*/
MAGNUM_EXPORT bool isPixelFormatFloatingPoint(PixelFormat format);
@ -875,7 +879,8 @@ Returns @cpp true @ce for `*Srgb` formats, @cpp false @ce otherwise. If this
function returns true, @ref isPixelFormatNormalized() also returns true.
Expects that the pixel format is *not* implementation-specific and not a
depth/stencil format.
@see @ref isPixelFormatImplementationSpecific()
@see @ref isPixelFormatImplementationSpecific(),
@ref pixelFormat(PixelFormat, UnsignedInt, bool)
*/
MAGNUM_EXPORT bool isPixelFormatSrgb(PixelFormat format);
@ -889,6 +894,31 @@ formats, @cpp false @ce otherwise. Expects that the pixel format is *not* implem
*/
MAGNUM_EXPORT bool isPixelFormatDepthOrStencil(PixelFormat format);
/**
@brief Assemble a pixel format from parts
@m_since_latest
Converts @p format to a new format with desired channel count and
normalization. Expects that the pixel format is *not* implementation-specific
and not a depth/stencil format, @p channelCount is @cpp 1 @ce, @cpp 2 @ce,
@cpp 3 @ce or @cpp 4 @ce and @p srgb is @cpp true @ce only for 8-bit integer
formats.
Example usage --- picking a three or four-channel format corresponding to a
grayscale or grayscale + alpha format:
@snippet Magnum.cpp pixelFormat
@see @ref pixelFormatChannelFormat(), @ref pixelFormatChannelCount(),
@ref vertexFormat(VertexFormat, UnsignedInt, bool)
@todo Unlike @ref vertexFormat(), this doesn't allow changing the normalized
property because I don't see a point as integral pixels are usually treated
as a vastly different kind of data. Once such use case exists, it might
make sense to add a pixelFormat(PixelFormat, UnsignedInt, bool, bool)
overload, the two bools are rather nasty though.
*/
MAGNUM_EXPORT PixelFormat pixelFormat(PixelFormat format, UnsignedInt channelCount, bool srgb);
#ifdef MAGNUM_BUILD_DEPRECATED
/**
* @brief @copybrief pixelFormatSize()

104
src/Magnum/Test/PixelFormatTest.cpp

@ -61,6 +61,12 @@ struct PixelFormatTest: TestSuite::Tester {
void isDepthOrStencilInvalid();
void isDepthOrStencilImplementationSpecific();
void assemble();
void assembleRoundtrip();
void assembleInvalidSrgb();
void assembleInvalidComponentCount();
void assembleDepthStencilImplementationSpecific();
void compressedBlockSize();
void compressedBlockSizeInvalid();
void compressedBlockSizeImplementationSpecific();
@ -91,6 +97,25 @@ struct PixelFormatTest: TestSuite::Tester {
void compresedConfiguration();
};
const struct {
PixelFormat channelType;
bool srgb;
} AssembleRoundtripData[]{
{PixelFormat::R8Unorm, false},
{PixelFormat::R8Snorm, false},
{PixelFormat::R8Srgb, true},
{PixelFormat::R8UI, false},
{PixelFormat::R8I, false},
{PixelFormat::R16Unorm, false},
{PixelFormat::R16Snorm, false},
{PixelFormat::R16UI, false},
{PixelFormat::R16I, false},
{PixelFormat::R32UI, false},
{PixelFormat::R32I, false},
{PixelFormat::R16F, false},
{PixelFormat::R32F, false},
};
PixelFormatTest::PixelFormatTest() {
addTests({&PixelFormatTest::mapping,
&PixelFormatTest::compressedMapping,
@ -115,6 +140,15 @@ PixelFormatTest::PixelFormatTest() {
&PixelFormatTest::isDepthOrStencilInvalid,
&PixelFormatTest::isDepthOrStencilImplementationSpecific,
&PixelFormatTest::assemble});
addRepeatedInstancedTests({&PixelFormatTest::assembleRoundtrip}, 4,
Containers::arraySize(AssembleRoundtripData));
addTests({&PixelFormatTest::assembleInvalidSrgb,
&PixelFormatTest::assembleInvalidComponentCount,
&PixelFormatTest::assembleDepthStencilImplementationSpecific,
&PixelFormatTest::compressedBlockSize,
&PixelFormatTest::compressedBlockSizeInvalid,
&PixelFormatTest::compressedBlockSizeImplementationSpecific,
@ -449,6 +483,76 @@ void PixelFormatTest::isDepthOrStencilImplementationSpecific() {
"isPixelFormatDepthOrStencil(): can't determine type of an implementation-specific format 0xdead\n");
}
void PixelFormatTest::assemble() {
/* Changing component count */
CORRADE_COMPARE(pixelFormat(PixelFormat::RGB16F, 4, false), PixelFormat::RGBA16F);
CORRADE_COMPARE(pixelFormat(PixelFormat::RGBA32UI, 2, false), PixelFormat::RG32UI);
CORRADE_COMPARE(pixelFormat(PixelFormat::R8Snorm, 3, false), PixelFormat::RGB8Snorm);
/* Same as pixelFormatChannelFormat() */
CORRADE_COMPARE(pixelFormat(PixelFormat::RGB32F, 1, false), pixelFormatChannelFormat(PixelFormat::RGB32F));
/* Adding / removing a sRGB property */
CORRADE_COMPARE(pixelFormat(PixelFormat::RGB8Unorm, 3, true), PixelFormat::RGB8Srgb);
CORRADE_COMPARE(pixelFormat(PixelFormat::RGBA8Srgb, 4, false), PixelFormat::RGBA8Unorm);
}
void PixelFormatTest::assembleRoundtrip() {
auto&& data = AssembleRoundtripData[testCaseInstanceId()];
std::ostringstream out;
{
Debug d{&out, Debug::Flag::NoNewlineAtTheEnd};
d << data.channelType;
if(data.srgb) d << Debug::nospace << ", srgb";
}
setTestCaseDescription(out.str());
PixelFormat result = pixelFormat(data.channelType, testCaseRepeatId() + 1, data.srgb);
CORRADE_COMPARE(pixelFormat(result, testCaseRepeatId() + 1, data.srgb), result);
CORRADE_COMPARE(pixelFormatChannelFormat(result), data.channelType);
CORRADE_COMPARE(pixelFormatChannelCount(result), testCaseRepeatId() + 1);
CORRADE_COMPARE(isPixelFormatSrgb(result), data.srgb);
}
void PixelFormatTest::assembleInvalidSrgb() {
CORRADE_SKIP_IF_NO_ASSERT();
std::ostringstream out;
Error redirectError{&out};
pixelFormat(PixelFormat::R8Snorm, 1, true);
pixelFormat(PixelFormat::RGB16Unorm, 4, true);
pixelFormat(PixelFormat::RGBA16F, 3, true);
CORRADE_COMPARE(out.str(),
"pixelFormat(): PixelFormat::R8Snorm can't be made sRGB\n"
"pixelFormat(): PixelFormat::RGB16Unorm can't be made sRGB\n"
"pixelFormat(): PixelFormat::RGBA16F can't be made sRGB\n");
}
void PixelFormatTest::assembleInvalidComponentCount() {
CORRADE_SKIP_IF_NO_ASSERT();
std::ostringstream out;
Error redirectError{&out};
pixelFormat(PixelFormat::RGB8Unorm, 0, false);
pixelFormat(PixelFormat::RGB8Unorm, 5, false);
CORRADE_COMPARE(out.str(),
"pixelFormat(): invalid component count 0\n"
"pixelFormat(): invalid component count 5\n");
}
void PixelFormatTest::assembleDepthStencilImplementationSpecific() {
CORRADE_SKIP_IF_NO_ASSERT();
std::ostringstream out;
Error redirectError{&out};
pixelFormat(pixelFormatWrap(0xdead), 1, true);
pixelFormat(PixelFormat::Depth32F, 1, true);
CORRADE_COMPARE(out.str(),
"pixelFormat(): can't assemble a format out of an implementation-specific format 0xdead\n"
"pixelFormat(): can't assemble a format out of PixelFormat::Depth32F\n");
}
void PixelFormatTest::compressedBlockSize() {
CORRADE_COMPARE(compressedPixelFormatBlockSize(CompressedPixelFormat::Etc2RGB8A1Srgb), (Vector3i{4, 4, 1}));
CORRADE_COMPARE(compressedPixelFormatBlockDataSize(CompressedPixelFormat::Etc2RGB8A1Srgb), 8);

6
src/Magnum/VertexFormat.h

@ -1463,7 +1463,8 @@ existing (three-component) vertex normal format:
@see @ref isVertexFormatImplementationSpecific(),
@ref vertexFormat(VertexFormat, UnsignedInt, UnsignedInt, bool),
@ref vertexFormatComponentFormat(), @ref vertexFormatComponentCount(),
@ref isVertexFormatNormalized()
@ref isVertexFormatNormalized(),
@ref pixelFormat(PixelFormat, UnsignedInt, bool)
*/
MAGNUM_EXPORT VertexFormat vertexFormat(VertexFormat format, UnsignedInt componentCount, bool normalized);
@ -1480,7 +1481,8 @@ implementation-specific.
@ref vertexFormat(VertexFormat, UnsignedInt, bool),
@ref vertexFormatComponentFormat(), @ref vertexFormatComponentCount(),
@ref vertexFormatVectorCount(), @ref vertexFormatVectorStride(),
@ref isVertexFormatNormalized()
@ref isVertexFormatNormalized(),
@ref pixelFormat(PixelFormat, UnsignedInt, bool)
*/
MAGNUM_EXPORT VertexFormat vertexFormat(VertexFormat format, UnsignedInt vectorCount, UnsignedInt componentCount, bool aligned);

Loading…
Cancel
Save