| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- // zlib open source license
- //
- // Copyright (c) 2017 to 2025 David Forsgren Piuva
- //
- // This software is provided 'as-is', without any express or implied
- // warranty. In no event will the authors be held liable for any damages
- // arising from the use of this software.
- //
- // Permission is granted to anyone to use this software for any purpose,
- // including commercial applications, and to alter it and redistribute it
- // freely, subject to the following restrictions:
- //
- // 1. The origin of this software must not be misrepresented; you must not
- // claim that you wrote the original software. If you use this software
- // in a product, an acknowledgment in the product documentation would be
- // appreciated but is not required.
- //
- // 2. Altered source versions must be plainly marked as such, and must not be
- // misrepresented as being the original software.
- //
- // 3. This notice may not be removed or altered from any source
- // distribution.
- #ifndef DFPSR_IMAGE_PACK_ORDER
- #define DFPSR_IMAGE_PACK_ORDER
- #include <cstdint>
- #include "Color.h"
- #include "../../base/endian.h"
- #include "../../base/noSimd.h"
- #include "../../api/stringAPI.h"
- namespace dsr {
- // The pack order defines where each color channel should be when uint32_t is interpreted as an array of four bytes using local endianness.
- // Packed into 2 bits in ImageDimensions, because one can assume that future pack orders at least have visible colors sorted by wavelength.
- enum class PackOrderIndex : uint32_t {
- RGBA, // Red Green Blue Alpha
- BGRA, // Blue Green Red Alpha
- ARGB, // Alpha Red Green Blue
- ABGR // Alpha Blue Green Red
- };
- inline String& string_toStreamIndented(String& target, const PackOrderIndex& index, const ReadableString& indentation) {
- ReadableString name;
- if (index == PackOrderIndex::RGBA) {
- name = U"RGBA";
- } else if (index == PackOrderIndex::BGRA) {
- name = U"BGRA";
- } else if (index == PackOrderIndex::ARGB) {
- name = U"ARGB";
- } else if (index == PackOrderIndex::ABGR) {
- name = U"ABGR";
- } else {
- name = U"?";
- }
- string_append(target, indentation, name);
- return target;
- }
- struct PackOrder {
- public:
- // Byte array indices for each channel
- // Indices are the locations of each color, not which color that holds each location
- // Example:
- // The indices for ARGB are (1, 2, 3, 0), because RGB are placed at byte indices 1..3 and A is placed first at byte index 0.
- int32_t redIndex, greenIndex, blueIndex, alphaIndex;
- // Pre-multipled bit offsets
- int32_t redOffset, greenOffset, blueOffset, alphaOffset;
- uint32_t redMask, greenMask, blueMask, alphaMask;
- private:
- constexpr PackOrder(int32_t redIndex, int32_t greenIndex, int32_t blueIndex, int32_t alphaIndex) :
- redIndex(redIndex), greenIndex(greenIndex), blueIndex(blueIndex), alphaIndex(alphaIndex),
- redOffset(redIndex << 3), greenOffset(greenIndex << 3), blueOffset(blueIndex << 3), alphaOffset(alphaIndex << 3),
- redMask(ENDIAN_POS_ADDR(ENDIAN32_BYTE_0, this->redOffset)),
- greenMask(ENDIAN_POS_ADDR(ENDIAN32_BYTE_0, this->greenOffset)),
- blueMask(ENDIAN_POS_ADDR(ENDIAN32_BYTE_0, this->blueOffset)),
- alphaMask(ENDIAN_POS_ADDR(ENDIAN32_BYTE_0, this->alphaOffset)) {}
- public:
- // Constructors
- PackOrder() :
- redIndex(0), greenIndex(1), blueIndex(2), alphaIndex(3),
- redOffset(0), greenOffset(8), blueOffset(16), alphaOffset(24),
- redMask(ENDIAN32_BYTE_0), greenMask(ENDIAN32_BYTE_1), blueMask(ENDIAN32_BYTE_2), alphaMask(ENDIAN32_BYTE_3) {}
- static PackOrder getPackOrder(PackOrderIndex index) {
- // Because the PackOrder constuctor is constexpr and all arguments are constant, these pack orders should be generated in compile time.
- if (index == PackOrderIndex::BGRA) {
- return PackOrder(2, 1, 0, 3); // PackOrderIndex::BGRA
- } else if (index == PackOrderIndex::ARGB) {
- return PackOrder(1, 2, 3, 0); // PackOrderIndex::ARGB
- } else if (index == PackOrderIndex::ABGR) {
- return PackOrder(3, 2, 1, 0); // PackOrderIndex::ABGR
- } else {
- return PackOrder(0, 1, 2, 3); // PackOrderIndex::RGBA
- }
- }
- // Pack the channels into a pixel color.
- uint32_t packRgba(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) const {
- uint32_t result;
- uint8_t *channels = (uint8_t*)(&result);
- channels[this->redIndex] = red;
- channels[this->greenIndex] = green;
- channels[this->blueIndex] = blue;
- channels[this->alphaIndex] = alpha;
- return result;
- }
- // Limit color to a 0..255 range and pack the channels into a pixel color.
- uint32_t saturateAndPackRgba(const ColorRgbaI32& color) {
- return this->packRgba(clamp(0, color.red, 255), clamp(0, color.green, 255), clamp(0, color.blue, 255), clamp(0, color.alpha, 255));
- }
- // A faster way of limiting input when you are sure that it won't overflow.
- uint32_t truncateAndPackRgba(const ColorRgbaI32& color) {
- return this->packRgba((uint8_t)color.red, (uint8_t)color.green, (uint8_t)color.blue, (uint8_t)color.alpha);
- }
- // The inverse of packRgba putting the channels back in order.
- ColorRgbaI32 unpackRgba(uint32_t packedColor) {
- uint8_t *channels = (uint8_t*)(&packedColor);
- return ColorRgbaI32(channels[this->redIndex], channels[this->greenIndex], channels[this->blueIndex], channels[this->alphaIndex]);
- }
- };
- template<typename U, DSR_ENABLE_IF(DSR_CHECK_PROPERTY(DsrTrait_Any_U32, U))> // Accepting uint32_t, U32x4, U32x8 ... U32xX
- inline U packOrder_getRed(U color) {
- return color & ENDIAN32_BYTE_0;
- }
- template<typename U, DSR_ENABLE_IF(DSR_CHECK_PROPERTY(DsrTrait_Any_U32, U))> // Accepting uint32_t, U32x4, U32x8 ... U32xX
- inline U packOrder_getRed(U color, const PackOrder &order) {
- return ENDIAN_NEG_ADDR(color & order.redMask, U(order.redOffset));
- }
- template<typename U, DSR_ENABLE_IF(DSR_CHECK_PROPERTY(DsrTrait_Any_U32, U))> // Accepting uint32_t, U32x4, U32x8 ... U32xX
- inline U packOrder_getGreen(U color) {
- return ENDIAN_NEG_ADDR_IMM(color & ENDIAN32_BYTE_1, 8);
- }
- template<typename U, DSR_ENABLE_IF(DSR_CHECK_PROPERTY(DsrTrait_Any_U32, U))> // Accepting uint32_t, U32x4, U32x8 ... U32xX
- inline U packOrder_getGreen(U color, const PackOrder &order) {
- return ENDIAN_NEG_ADDR(color & order.greenMask, U(order.greenOffset));
- }
- template<typename U, DSR_ENABLE_IF(DSR_CHECK_PROPERTY(DsrTrait_Any_U32, U))> // Accepting uint32_t, U32x4, U32x8 ... U32xX
- inline U packOrder_getBlue(U color) {
- return ENDIAN_NEG_ADDR_IMM(color & ENDIAN32_BYTE_2, 16);
- }
- template<typename U, DSR_ENABLE_IF(DSR_CHECK_PROPERTY(DsrTrait_Any_U32, U))> // Accepting uint32_t, U32x4, U32x8 ... U32xX
- inline U packOrder_getBlue(U color, const PackOrder &order) {
- return ENDIAN_NEG_ADDR(color & order.blueMask, U(order.blueOffset));
- }
- template<typename U, DSR_ENABLE_IF(DSR_CHECK_PROPERTY(DsrTrait_Any_U32, U))> // Accepting uint32_t, U32x4, U32x8 ... U32xX
- inline U packOrder_getAlpha(U color) {
- return ENDIAN_NEG_ADDR_IMM(color & ENDIAN32_BYTE_3, 24);
- }
- template<typename U, DSR_ENABLE_IF(DSR_CHECK_PROPERTY(DsrTrait_Any_U32, U))> // Accepting uint32_t, U32x4, U32x8 ... U32xX
- inline U packOrder_getAlpha(U color, const PackOrder &order) {
- return ENDIAN_NEG_ADDR(color & order.alphaMask, U(order.alphaOffset));
- }
- // Each input 32-bit element is from 0 to 255. Otherwise, the remainder will leak to other elements.
- template<typename U, DSR_ENABLE_IF(DSR_CHECK_PROPERTY(DsrTrait_Any_U32, U))> // Accepting uint32_t, U32x4, U32x8 ... U32xX
- U packOrder_packBytes(const U &s0, const U &s1, const U &s2) {
- return s0 | ENDIAN_POS_ADDR_IMM(s1, 8) | ENDIAN_POS_ADDR_IMM(s2, 16);
- }
- // Using a specified packing order
- template<typename U, DSR_ENABLE_IF(DSR_CHECK_PROPERTY(DsrTrait_Any_U32, U))> // Accepting uint32_t, U32x4, U32x8 ... U32xX
- U packOrder_packBytes(const U &s0, const U &s1, const U &s2, const PackOrder &order) {
- return ENDIAN_POS_ADDR(s0, U(order.redOffset))
- | ENDIAN_POS_ADDR(s1, U(order.greenOffset))
- | ENDIAN_POS_ADDR(s2, U(order.blueOffset));
- }
- // Each input 32-bit element is from 0 to 255. Otherwise, the remainder will leak to other elements.
- template<typename U, DSR_ENABLE_IF(DSR_CHECK_PROPERTY(DsrTrait_Any_U32, U))> // Accepting uint32_t, U32x4, U32x8 ... U32xX
- U packOrder_packBytes(const U &s0, const U &s1, const U &s2, const U &s3) {
- return s0 | ENDIAN_POS_ADDR_IMM(s1, 8) | ENDIAN_POS_ADDR_IMM(s2, 16) | ENDIAN_POS_ADDR_IMM(s3, 24);
- }
- // Using a specified packing order
- template<typename U, DSR_ENABLE_IF(DSR_CHECK_PROPERTY(DsrTrait_Any_U32, U))> // Accepting uint32_t, U32x4, U32x8 ... U32xX
- U packOrder_packBytes(const U &s0, const U &s1, const U &s2, const U &s3, const PackOrder &order) {
- return ENDIAN_POS_ADDR(s0, U(order.redOffset))
- | ENDIAN_POS_ADDR(s1, U(order.greenOffset))
- | ENDIAN_POS_ADDR(s2, U(order.blueOffset))
- | ENDIAN_POS_ADDR(s3, U(order.alphaOffset));
- }
- // Pack separate floats into saturated bytes
- // From float to uint32_t
- // From F32x4 to U32x4
- // From F32x8 to U32x8
- // From F32xX to U32xX
- template<typename U, typename F, DSR_ENABLE_IF(
- DSR_CHECK_PROPERTY(DsrTrait_Any_U32, U)
- && DSR_CHECK_PROPERTY(DsrTrait_Any_F32, F)
- )>
- inline U packOrder_floatToSaturatedByte(const F &s0, const F &s1, const F &s2, const F &s3) {
- return packOrder_packBytes(
- truncateToU32(clampUpper(s0, F(255.1f))),
- truncateToU32(clampUpper(s1, F(255.1f))),
- truncateToU32(clampUpper(s2, F(255.1f))),
- truncateToU32(clampUpper(s3, F(255.1f)))
- );
- }
- // Using a specified pack order
- template<typename U, typename F, DSR_ENABLE_IF(
- DSR_CHECK_PROPERTY(DsrTrait_Any_U32, U)
- && DSR_CHECK_PROPERTY(DsrTrait_Any_F32, F)
- )>
- inline U packOrder_floatToSaturatedByte(const F &s0, const F &s1, const F &s2, const F &s3, const PackOrder &order) {
- return packOrder_packBytes(
- truncateToU32(clampUpper(s0, F(255.1f))),
- truncateToU32(clampUpper(s1, F(255.1f))),
- truncateToU32(clampUpper(s2, F(255.1f))),
- truncateToU32(clampUpper(s3, F(255.1f))),
- order
- );
- }
- }
- #endif
|