| 1 | // tPixelFormat.h  |
| 2 | //  |
| 3 | // Pixel formats in Tacent. Not all formats are fully supported. Certainly BC 4, 5, and 7 may not have extensive HW  |
| 4 | // support at this time.  |
| 5 | //  |
| 6 | // Copyright (c) 2004-2006, 2017, 2019, 2022-2024 Tristan Grimmer.  |
| 7 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby  |
| 8 | // granted, provided that the above copyright notice and this permission notice appear in all copies.  |
| 9 | //  |
| 10 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL  |
| 11 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,  |
| 12 | // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN  |
| 13 | // AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR  |
| 14 | // PERFORMANCE OF THIS SOFTWARE.  |
| 15 |   |
| 16 | #pragma once  |
| 17 | namespace tImage  |
| 18 | {  |
| 19 |   |
| 20 |   |
| 21 | // Unlike DirectX, which assumes all machines are little-endian, the enumeration below specifies the components in the  |
| 22 | // order they appear _in memory_. This means formats commonly called things like B5G6R5 are actually G3R5B5G3. The  |
| 23 | // latter is what they are referred to in Tacent, This inconsistent naming gets worse since when things are byte-aligned  |
| 24 | // most vendor pixel formats are actually correct for the mempry representation. In any case, in Tacent, it's always the  |
| 25 | // in-memory representation that gets named. BC stands for Block Compression.  |
| 26 | //  |
| 27 | // A note regarding sRGB. We are _not_ indicating via the pixel format what space/profilr the colour encoded by the  |
| 28 | // format is in. Tacent separates the encoding (the pixel format) from how the encoded data is to be interpreted. This  |
| 29 | // is in contrast to all the MS DXGI formats where they effectively at least double the number of formats unnecessarily.  |
| 30 | //  |
| 31 | // A way to think of it is as follows -- You have some input data (Din) that gets encoded using a pixel format (Epf)  |
| 32 | // resulting in some output data (Dout). Din -> Epf -> Dout. Without changing Din, if changing Epf would result in  |
| 33 | // different Dout, it is correct to have separate formats (eg. BCH6_S vs BCH6_U. DXT1 vs DXT1BA). If changing Epf would  |
| 34 | // not result in different Dout then the formats are not different and satellite info should be used if what's stored in  |
| 35 | // Din (and Dout) has certain properties (eg. sRGB space vs Linear, premultiplied vs not, DXT2 and DXT3 are the same).  |
| 36 | //  |
| 37 | // This is also why we don't distinguish between UNORM and UINT for example, as this is just a runtime distinction, not  |
| 38 | // an encoding difference (For example, UNORM gets converted to a float in [0.0, 1.0] in shaders, UINT doesn't).  |
| 39 | //  |
| 40 | // The only exception to this rule is the Tacent pixel format _does_ make distinctions between formats based on the  |
| 41 | // colour components being represented. It's not ideal, but pixel formats do generally specify R, G, B, A, L etc and  |
| 42 | // what order they appear in. In a perfect world (in my perfect world anyways), R8G8B8 would just be C8C8C8 (C8X3) and  |
| 43 | // satellite info would describe what the data represented (RGB in this case). Anyway, that's too much of a divergence.  |
| 44 | // This exception is why there is a tPixelFormat R8 (Vulkan has one of these), A8, and L8, all 3 with the same internal  |
| 45 | // representation.  |
| 46 | //  |
| 47 | // Summary of Satellite and Pixel-Format information:  |
| 48 | //  |
| 49 | // Colour Profile (Satellite)  |
| 50 | // A colour profile basically specifies the colour space for the various components. Sometimes  |
| 51 | // the same space is not used for all components. It is common for RGB to be sRGB but alpha to be linear -- there is  |
| 52 | // a profile for that. See tColourProfile and tColourSpace enum in tColour.h.  |
| 53 | //  |
| 54 | // Component Format (Pixel-Format)  |
| 55 | // The encoding is different for unsigned int, int, unsigned float, and float. Since the encoding is different,  |
| 56 | // this information IS specified by the pixel format. In particular a lower-case suffix is used for the packed  |
| 57 | // pixel-formats if it is not unsigned int:  |
| 58 | // no suffix -> unsigned int.  |
| 59 | // s -> signed int (2's complimemt).  |
| 60 | // uf -> unsigned float (always >= 0.0). No sign bit.  |
| 61 | // f -> signed float.  |
| 62 | //  |
| 63 | // Some non-packed pixel-formats like BC and EAC distinguich between the encoding of signed vs unsigned data. In  |
| 64 | // these cases we use a single capital letter suffix. If a non-packed encoding does not distinguish, no suffix.  |
| 65 | // No Suffix -> Format does not distinguish.  |
| 66 | // S -> Signed Variant.  |
| 67 | // U -> Unsigned Variant.  |
| 68 | //  |
| 69 | // Channel Type (Satellite)  |
| 70 | // Sometimes it is intended that the data stored with each component is further modified before being used. In  |
| 71 | // particular it may be normalized. ChannelType is additional satellite information that is not entirely specified  |
| 72 | // by the pixel format so it belongs as satellite information here. In particular the part that isn't specified is  |
| 73 | // whether the component data of each colour channel should be normalized or not afterwards. It gets a little tricky  |
| 74 | // here because Vulkan, OpenGL, and DirectX have all decided on variant pixel-format names with channel-type  |
| 75 | // information like UNORM, SNORM, UINT, SINT, and FLOAT. This naming _includes_ both information about how the data  |
| 76 | // is encoded (integer or float, signed or unsigned) as well as whether to normalize after decoding or not. We have a  |
| 77 | // choice here, either ONLY make this satellite info contain whether to normalize of not afterwards, or have a litte  |
| 78 | // redundant information in order to keep the naming as close as possible to UNORM, UINT, etc. I have decided on the  |
| 79 | // latter.  |
| 80 | //  |
| 81 | // The reason it is not part of the pixel format is it is quite common for the data to be encoded as, say, an  |
| 82 | // unsigned integer, but 'converted' to a float when it is passed off the video memory by the graphics API so it is  |
| 83 | // available as a float in the fragment/pixel shader. In short the ChannelType indicates intent for what should  |
| 84 | // happen to the value AFTER decoding. For example, UNORM means the data is stored (or decoded for compressed  |
| 85 | // formats) as an unsigned integer (which is already known by looking at the pixel-format) -- it is then converted to  |
| 86 | // a normalized value in [0.0, 1.0]. SNORM means it's stored as a signed integer and then normalized to the  |
| 87 | // [0.0, 1.0] range. The actual number of bits used is NOT specified here -- that is also specified by the  |
| 88 | // pixel-format itself (either explicitly or implicitly by inspecting the compression method used). I bring this up  |
| 89 | // because, for example, the PVR3 filetype 'channel type' field does contain size information, but it doesn't need to  |
| 90 | // (and probably shouldn't).  |
| 91 | //  |
| 92 | // Example 1. PixelFormat: G3B5R5G3 ChanelType: UNORM  |
| 93 | // We know the R and B are stored as 5-bit unsigned ints and the G with six bits. We know this from the PixelFormat  |
| 94 | // alone because it does not contain a 's', 'f', or 'uf'. We further know the intent is to 'normalize' it after  |
| 95 | // decoding. R would be in [0, 31] and converted to [0.0, 1.0]. The 'U' part of 'UNORM' is redundant because the  |
| 96 | // pixel-format already told us it was an unsigned integer.  |
| 97 | //  |
| 98 | // Example 2. PixelFormat: R11G11B10uf ChanelType: UFLOAT  |
| 99 | // RG stored as 11-bit unsigned floats (5 exponent, 6 mantissa, no sign bit). B stored as a 10-bit (5,5) float. In  |
| 100 | // this case the ChannelType is completely redundant because we already know we're using unsigned floats from the 'uf'.  |
| 101 | //  |
| 102 | // Example 3. PixelFormat: R8G8 ChanelType: UINT  |
| 103 | // RG stored as 8-bit unsigned ints (from pixel-format). In this case the ChannelType indicates _not_ to normalize so  |
| 104 | // each component should be read as an unsigned integer in [0, 255].  |
| 105 | //  |
| 106 |   |
| 107 |   |
| 108 | enum class tPixelFormat  |
| 109 | {  |
| 110 | Invalid = -1,  |
| 111 | Auto = Invalid,  |
| 112 | Unspecified = Invalid,  |
| 113 |   |
| 114 | FirstValid,  |
| 115 | FirstContiguous = FirstValid,  |
| 116 | FirstPacked = FirstContiguous,  |
| 117 | R8 = FirstPacked, // 8 bit. Unsigned representing red. Some file-types not supporting A8 or L8 (eg ktx2) will export to this.  |
| 118 | R8G8, // 16 bit. Unsigned representing red and green. Vulkan has an analagous format.  |
| 119 | R8G8B8, // 24 bit. Full colour. No alpha. Matches GL_RGB source ordering. Not efficient. Most drivers will swizzle to BGR.  |
| 120 | R8G8B8A8, // 32 bit. Full alpha. Matches GL_RGBA source ordering. Not efficient. Most drivers will swizzle to ABGR.  |
| 121 | B8G8R8, // 24 bit. Full colour. No alpha. Matches GL_BGR source ordering. Efficient. Most drivers do not need to swizzle.  |
| 122 | B8G8R8A8, // 32 bit. Full alpha. Matches GL_BGRA source ordering. Most drivers do not need to swizzle.  |
| 123 |   |
| 124 | G3B5R5G3, // 16 bit. No alpha. Incorrectly AKA B5G6R5. The truth is in memory it is GGGBBBBB RRRRRGGG -> this is G3B5R5G3.  |
| 125 | G4B4A4R4, // 16 bit. 12 colour bits. 4 bit alpha. Incorrectly AKA B4G4R4A4.  |
| 126 | B4A4R4G4, // 16 bit. 12 colour bits. 4 bit alpha. Incorrectly AKA R4G4B4A4.  |
| 127 | G3B5A1R5G2, // 16 bit. 15 colour bits. Binary alpha. Incorrectly AKA B5G5R5A1.  |
| 128 | G2B5A1R5G3, // 16 bit. 15 colour bits. Binary alpha. Incorrectly AKA R5G5B5A1.  |
| 129 | A8L8, // 16 bit. Alpha and Luminance.  |
| 130 | A8, // 8 bit. Alpha only.  |
| 131 | L8, // 8 bit. Luminance only.  |
| 132 | R16, // 16 bit. 16 bit Red.  |
| 133 | R16G16, // 32 bit. 16 bit R. 16 bit G.  |
| 134 | R16G16B16, // 48 bit. 16 bit each RGB.  |
| 135 | R16G16B16A16, // 64 bit. 16 bit each RGBA.  |
| 136 | R32, // 32 bit. Red.  |
| 137 | R32G32, // 64 bit. Red Green.  |
| 138 | R32G32B32, // 96 bit. 32 bit each RGB.  |
| 139 | R32G32B32A32, // 128 bit. 32 bit each RGBA.  |
| 140 | R16f, // 16 bit. Half-float red/luminance channel only. HDR linear space.  |
| 141 | R16G16f, // 32 bit. Two half-floats per pixel. RG. HDR linear space.  |
| 142 | R16G16B16f, // 48 bit. Three half-floats per pixel. RGB. HDR linear space.  |
| 143 | R16G16B16A16f, // 64 bit. Four half-floats per pixel. RGBA. HDR linear space.  |
| 144 | R32f, // 32 bit. Float red/luminance channel only. HDR linear space.  |
| 145 | R32G32f, // 64 bit. Two floats per pixel. RG. HDR linear space.  |
| 146 | R32G32B32f, // 96 bit. Three floats per pixel. RGB. HDR linear space.  |
| 147 | R32G32B32A32f, // 128 bit. Four floats per pixel. RGBA. HDR linear space.  |
| 148 | R11G11B10uf, // 32 bit. Unsigned 11-bit floats for RG, and a 10-bit float for B. All use a 5-bit exponent.  |
| 149 | B10G11R11uf, // 32 bit. Unsigned 10-bit floats for B, and 11-bit floats for GR. All use a 5-bit exponent.  |
| 150 | R9G9B9E5uf, // 32 bit. Unsigned 14-bit floats for RGB. Always denorm and each share the same 5-bit exponent.  |
| 151 | E5B9G9R9uf, // 32 bit. Unsigned 14-bit floats for RGB. Always denorm and each share the same 5-bit exponent.  |
| 152 | R8G8B8M8, // 32 bit. Poor-man's HDR. RGB plus shared 8-bit multiplier. Must supply MaxRange when decoding.  |
| 153 | R8G8B8D8, // 32 bit. Poor-man's HDR. RGB plus shared 8-bit divisor. Must supply MaxRange when decoding.  |
| 154 | LastPacked = R8G8B8D8,  |
| 155 |   |
| 156 | FirstBC,  |
| 157 | BC1DXT1 = FirstBC, // BC 1, DXT1. No alpha.  |
| 158 | BC1DXT1A, // BC 1, DXT1. Binary alpha.  |
| 159 | BC2DXT2DXT3, // BC 2, DXT2 (premult-alpha) and DXT3 share the same format. Large alpha gradients (alpha banding).  |
| 160 | BC3DXT4DXT5, // BC 3, DXT4 (premult-alpha) and DXT5 share the same format. Variable alpha (smooth).  |
| 161 | BC4ATI1U, // BC 4. Unsigned. One colour channel only. May not be HW supported.  |
| 162 | BC4ATI1S, // BC 4. Signed. One colour channel only. May not be HW supported.  |
| 163 | BC5ATI2U, // BC 5. Unsigned. Two colour channels only. May not be HW supported.  |
| 164 | BC5ATI2S, // BC 5. Signed. Two colour channels only. May not be HW supported.  |
| 165 | BC6U, // BC 6 HDR. No alpha. 3 x 16bit unsigned half-floats per pixel.  |
| 166 | BC6S, // BC 6 HDR. No alpha. 3 x 16bit signed half-floats per pixel.  |
| 167 | BC7, // BC 7. Full colour. Variable alpha 0 to 8 bits.  |
| 168 |   |
| 169 | FirstETC,  |
| 170 | ETC1 = FirstETC, // ETC1. Ericsson Texture Compression. Similar to BC1. RGB-only. No alpha.  |
| 171 | ETC2RGB, // ETC2. Backwards compatible with ETC1. The sRGB version is the same pixel format.  |
| 172 | ETC2RGBA, // ETC2. RGBA. sRGB uses the same pixel format.  |
| 173 | ETC2RGBA1, // ETC2. RGB with binary alpha. sRGB uses the same pixel format.  |
| 174 | LastETC = ETC2RGBA1,  |
| 175 |   |
| 176 | FirstEAC,  |
| 177 | EACR11U = FirstEAC, // EAC R11. Ericsson. Single channel.  |
| 178 | EACR11S, // EAC R11. Signed.  |
| 179 | EACRG11U, // EAC RG11. Ericsson. Two channels.  |
| 180 | EACRG11S, // EAC RG11. Signed.  |
| 181 | LastEAC = EACRG11S,  |
| 182 | LastBC = LastEAC,  |
| 183 |   |
| 184 | FirstPVR, // PowerVR. Imagination. 8-byte blocks. We do not consider the PVRTC formats to be BC formats because 4 blocks need to be accessed. i.e. The pixels are not 'confined' to the block they are in.  |
| 185 | PVRBPP4 = FirstPVR, // PVRTC Version 1. 4BPP representing RGB or RGBA channels. One block can encode 4x4 pixels (but needs access to adjacent blocks during decompress).  |
| 186 | PVRBPP2, // PVRTC Version 1. 2BPP representing RGB or RGBA channels. One block can encode 8x4 pixels.  |
| 187 |   |
| 188 | #ifdef PIXEL_FORMAT_INCLUDE_NOT_IMPLEMENTED  |
| 189 | PVRHDRBPP8, // PVRTC Version 1. 8BPP representing HDR RGB.  |
| 190 | PVRHDRBPP6, // PVRTC Version 1. 6BPP representing HDR RGB.  |
| 191 | PVR2BPP4, // PVRTC Version 2. 4BPP representing RGB or RGBA channels.  |
| 192 | PVR2BPP2, // PVRTC Version 2. 2BPP representing RGB or RGBA channels.  |
| 193 | PVR2HDRBPP8, // PVRTC Version 2. 8BPP representing HDR RGB.  |
| 194 | PVR2HDRBPP6, // PVRTC Version 2. 6BPP representing HDR RGB.  |
| 195 | LastPVR = PVR2HDRBPP6,  |
| 196 | #else  |
| 197 | LastPVR = PVRBPP2,  |
| 198 | #endif  |
| 199 |   |
| 200 | FirstASTC,  |
| 201 | ASTC4X4 = FirstASTC, // 128 bits per 16 pixels. 8 bpp. LDR UNORM.  |
| 202 | ASTC5X4, // 128 bits per 20 pixels. 6.4 bpp. LDR UNORM.  |
| 203 | ASTC5X5, // 128 bits per 25 pixels. 5.12 bpp. LDR UNORM.  |
| 204 | ASTC6X5, // 128 bits per 30 pixels. 4.27 bpp. LDR UNORM.  |
| 205 | ASTC6X6, // 128 bits per 36 pixels. 3.56 bpp. LDR UNORM.  |
| 206 | ASTC8X5, // 128 bits per 40 pixels. 3.2 bpp. LDR UNORM.  |
| 207 | ASTC8X6, // 128 bits per 48 pixels. 2.67 bpp. LDR UNORM.  |
| 208 | ASTC8X8, // 128 bits per 64 pixels. 2.56 bpp. LDR UNORM.  |
| 209 | ASTC10X5, // 128 bits per 50 pixels. 2.13 bpp. LDR UNORM.  |
| 210 | ASTC10X6, // 128 bits per 60 pixels. 2 bpp. LDR UNORM.  |
| 211 | ASTC10X8, // 128 bits per 80 pixels. 1.6 bpp. LDR UNORM.  |
| 212 | ASTC10X10, // 128 bits per 100 pixels. 1.28 bpp. LDR UNORM.  |
| 213 | ASTC12X10, // 128 bits per 120 pixels. 1.07 bpp. LDR UNORM.  |
| 214 | ASTC12X12, // 128 bits per 144 pixels. 0.89 bpp. LDR UNORM.  |
| 215 | LastASTC = ASTC12X12,  |
| 216 | LastContiguous = LastASTC,  |
| 217 |   |
| 218 | FirstVendor,  |
| 219 | RADIANCE = FirstVendor, // Radiance HDR.  |
| 220 | OPENEXR, // OpenEXR HDR.  |
| 221 | LastVendor = OPENEXR,  |
| 222 |   |
| 223 | FirstPalette,  |
| 224 | PAL1BIT = FirstPalette, // 1-bit indexes to a palette. 2 colour. 1 bpp. Often dithered B/W.  |
| 225 | PAL2BIT, // 2-bit indexes to a palette. 4 colour. 2 bpp.  |
| 226 | PAL3BIT, // 3-bit indexes to a palette. 8 colour. 3 bpp.  |
| 227 | PAL4BIT, // 4-bit indexes to a palette. 16 colour. 4 bpp.  |
| 228 | PAL5BIT, // 5-bit indexes to a palette. 32 colour. 5 bpp.  |
| 229 | PAL6BIT, // 6-bit indexes to a palette. 64 colour. 6 bpp.  |
| 230 | PAL7BIT, // 7-bit indexes to a palette. 128 colour. 7 bpp.  |
| 231 | PAL8BIT, // 8-bit indexes to a palette. 256 colour. 8 bpp.  |
| 232 | LastPalette = PAL8BIT,  |
| 233 |   |
| 234 | LastValid = LastPalette,  |
| 235 | NumPixelFormats,  |
| 236 | NumContiguousFormats = LastContiguous - FirstContiguous + 1,  |
| 237 | NumPackedFormats = LastPacked - FirstPacked + 1,  |
| 238 | NumBCFormats = LastBC - FirstBC + 1,  |
| 239 | NumPVRFormats = LastPVR - FirstPVR + 1,  |
| 240 | NumASTCFormats = LastASTC - FirstASTC + 1,  |
| 241 | NumVendorFormats = LastVendor - FirstVendor + 1,  |
| 242 | NumPaletteFormats = LastPalette - FirstPalette + 1  |
| 243 | };  |
| 244 |   |
| 245 | // Returns true if the supplied pixel format is a valid format and not an out of range marker.  |
| 246 | bool tIsValidFormat (tPixelFormat);  |
| 247 |   |
| 248 | // Is the supplied format one in which the pixels are stored as a contiguous chunk of data.  |
| 249 | bool tIsContiguousFormat(tPixelFormat);  |
| 250 |   |
| 251 | // Simple RGB and RGBA formats with different numbers of bits per component and different orderings.  |
| 252 | bool tIsPackedFormat (tPixelFormat);  |
| 253 |   |
| 254 | // Is the format a 4x4 BC (Block Compression) format. This includes ETC and EAC formats. These 4x4  |
| 255 | // blocks use various numbers of bits per block.  |
| 256 | bool tIsBCFormat (tPixelFormat);  |
| 257 |   |
| 258 | // Returns true if the format is an ETC BC format. EAC is not considered part of ETC for this function.  |
| 259 | // ETC formats are a subset of tIsBCFormat.  |
| 260 | bool tIsETCFormat (tPixelFormat);  |
| 261 |   |
| 262 | // Returns true if the format is an EAC BC format. EAC formats are a subset of tIsBCFormat.  |
| 263 | bool tIsEACFormat (tPixelFormat);  |
| 264 |   |
| 265 | // Is it one of the PVR formats.  |
| 266 | bool tIsPVRFormat (tPixelFormat);  |
| 267 |   |
| 268 | // Is it one of the ASTC (Adaptive Scalable Texture Compression) block formats. Block sizes are avail from 4x4 up  |
| 269 | // to 12x12. The 4x4 ASTC variant is not considered a BC format by tIsBCFormat.  |
| 270 | bool tIsASTCFormat (tPixelFormat);  |
| 271 |   |
| 272 | bool tIsVendorFormat (tPixelFormat);  |
| 273 | bool tIsPaletteFormat (tPixelFormat);  |
| 274 | bool tIsAlphaFormat (tPixelFormat);  |
| 275 | bool tIsOpaqueFormat (tPixelFormat);  |
| 276 | bool tIsHDRFormat (tPixelFormat);  |
| 277 | bool tIsLDRFormat (tPixelFormat);  |
| 278 | bool tIsLuminanceFormat (tPixelFormat); // Single-channel luminance formats. Includes red-only formats. Does not include alpha only.  |
| 279 |   |
| 280 | // Gets the width/height in pixels of a block in the specified pixel-format. BC blocks are all 4x4. PVR blocks are  |
| 281 | // either 4x4 or 8x4. ASTC blocks have varying width/height depending on specific ASTC format -- they vary from 4x4 to  |
| 282 | // 12x12. Packed, Vendor, and Palette formats return 1 for width and height. Invalid pixel-formats return 0.  |
| 283 | int tGetBlockWidth (tPixelFormat);  |
| 284 | int tGetBlockHeight (tPixelFormat);  |
| 285 |   |
| 286 | // Given a block-width or block-height and how may pixels you need to store (image-width or image-height), returns the  |
| 287 | // number of blocks you will need in that dimension.  |
| 288 | int tGetNumBlocks (int blockWH, int imageWH);  |
| 289 |   |
| 290 | // Only applies to formats that can guarantee an integer number of bits per pixel. In particular does not apply to ASTC  |
| 291 | // formats (even if the particular ASTC format has an integer number of bits-per-pixel). We report in bits (not bytes)  |
| 292 | // because some formats (i.e. BC1) are only half a byte per pixel. Palette formats do not consider the palette entry,  |
| 293 | // size, but rather the size of the index as there is one index per pixel. Returns 0 for non-integral bpp formats and  |
| 294 | // all ASTC formats.  |
| 295 | int tGetBitsPerPixel(tPixelFormat);  |
| 296 |   |
| 297 | // Works for any pixel format, even if a non-integral number of bits per pixel. In particular does work for ASTC  |
| 298 | // formats. Returns 0.0f if pixel format is invalid.  |
| 299 | float tGetBitsPerPixelFloat(tPixelFormat);  |
| 300 |   |
| 301 | // This function must be given a BC format, a PVR format, an ASTC format, or a packed format.  |
| 302 | // BC formats : 4x4 with different number of bytes per block.  |
| 303 | // PVR formats : 4x4 or 8x4 for the LDR PVR formats but always 8 bytes. Unknown for the HDR variants.  |
| 304 | // ASTC formats : Varying MxN but always 16 bytes.  |
| 305 | // Packed Formats : Considered 1x1 with varying number of bytes per pixel.  |
| 306 | // Returns 0 otherwise.  |
| 307 | int tGetBytesPerBlock(tPixelFormat);  |
| 308 |   |
| 309 | // Since there are numerous size functions already in the pixel-format header it makes sense to also add functions to  |
| 310 | // query how many mipmaps would be needs for variously sized images. Note that due to various block sizes, you cannot  |
| 311 | // compite the total pixel-area. Each mimpap will need its own set of blocks in either direction. The highest mip level  |
| 312 | // (which will represent the smallest mipmap image of 1x1) will still use a single block. The lowest level (0) is the  |
| 313 | // image at full (highest) dimensions.  |
| 314 |   |
| 315 | // Width and height should be >= 1. Returns 0 otherwise.  |
| 316 | int tGetNumMipmapLevels(int width, int height);  |
| 317 |   |
| 318 | // Given a supplied width or height this function will return the next higher mip level dimension. Once the dimension  |
| 319 | // reaches 1 it stays there. The width or height should be >= 1. If it is <= 0, 0 is returned.  |
| 320 | int tGetNextMipmapLevelDim(int widthOrHeight);  |
| 321 |   |
| 322 | // Given a supplied width and height this function will return the next higher mip level dimensions. Once a dimension  |
| 323 | // reaches 1 it stays there. This function reads and writes width and height. Both width and height should be >= 1. If  |
| 324 | // one of them is <= 0, 0 is returned for that dimension.  |
| 325 | void tGetNextMipmapLevelDims(int& width, int& height);  |
| 326 |   |
| 327 | // Given a base width or height at level 0 this function computes the corresponding dimension at the supplied level.  |
| 328 | // The width or height should be >= 1. If it is <= 0, 0 is returned regardless of level.  |
| 329 | // Level should be E [0, NumMipmapLevels). Below this range widthOrHeight is returned. Above this range 1 is returned.  |
| 330 | int tGetMipmapDim(int widthOrHeight, int level);  |
| 331 |   |
| 332 | // Given level 0 width and height this function returns the width and height at the supplied level. This function  |
| 333 | // reads and writes width and height. The input width and height should be >= 1. If one is <= 0, 0 is returned for that  |
| 334 | // dimension regardless of level. Level should be E [0, NumMipmapLevels). Below this range width and height are  |
| 335 | // left unmodified. Above this range width and height are both set to 1.  |
| 336 | void tGetMipmapDims(int& width, int& height, int level);  |
| 337 |   |
| 338 | // Same as above but writes to mipWidth and mipHeight and reads from width and height.  |
| 339 | void tGetMipmapDims(int& mipWidth, int& mipHeight, int width, int height, int level);  |
| 340 |   |
| 341 | extern const char* PixelFormatNames[];  |
| 342 | extern const char** PixelFormatNames_Packed;  |
| 343 | extern const char** PixelFormatNames_Block;  |
| 344 | extern const char** PixelFormatNames_PVR;  |
| 345 | extern const char** PixelFormatNames_ASTC;  |
| 346 | extern const char** PixelFormatNames_Vendor;  |
| 347 | extern const char** PixelFormatNames_Palette;  |
| 348 | const char* tGetPixelFormatName(tPixelFormat);  |
| 349 |   |
| 350 | extern const char* PixelFormatDescs[];  |
| 351 | extern const char** PixelFormatDescs_Packed;  |
| 352 | extern const char** PixelFormatDescs_Block;  |
| 353 | extern const char** PixelFormatDescs_PVR;  |
| 354 | extern const char** PixelFormatDescs_ASTC;  |
| 355 | extern const char** PixelFormatDescs_Vendor;  |
| 356 | extern const char** PixelFormatDescs_Palette;  |
| 357 | const char* tGetPixelFormatDesc(tPixelFormat);  |
| 358 |   |
| 359 | // Gets the pixel format from its name. Case sensitive. Slow. Use for testing/unit-tests only.  |
| 360 | tPixelFormat tGetPixelFormat(const char* name);  |
| 361 |   |
| 362 |   |
| 363 | // I've decided to put generic aspect ratio stuff in here as well. It doesn't really warrant its own header. This enum  |
| 364 | // is for commonly encountered aspect ratios on-screen and in print. The name array may be indexed by the enum values.  |
| 365 | // They are ordered from largest to smallest.  |
| 366 | enum class tAspectRatio  |
| 367 | {  |
| 368 | Invalid, // Must be 0.  |
| 369 | Free = Invalid,  |
| 370 | First_Valid,  |
| 371 | First_Screen = First_Valid,  |
| 372 | Screen_3_1 = First_Screen, // 3.0  |
| 373 | Screen_2_1, // 2.0  |
| 374 | Screen_16_9, // 1.7777777  |
| 375 | Screen_5_3, // 1.6666666  |
| 376 | Screen_16_10, // 1.6 Reduces to 8_5  |
| 377 | Screen_8_5, // 1.6  |
| 378 | Screen_3_2, // 1.5  |
| 379 | Screen_16_11, // 1.4545454  |
| 380 | Screen_7_5, // 1.4  |
| 381 | Screen_4_3, // 1.3333333  |
| 382 | Screen_22_17, // 1.2941176  |
| 383 | Screen_14_11, // 1.2727272  |
| 384 | Screen_5_4, // 1.25  |
| 385 | Screen_1_1, // 1.0  |
| 386 | Screen_4_5, // 0.8  |
| 387 | Screen_11_14, // 0.7857142  |
| 388 | Screen_17_22, // 0.7727272  |
| 389 | Screen_3_4, // 0.75  |
| 390 | Screen_5_7, // 0.7142857  |
| 391 | Screen_11_16, // 0.6875  |
| 392 | Screen_2_3, // 0.6666666  |
| 393 | Screen_5_8, // 0.625  |
| 394 | Screen_10_16, // 0.625 Reduces to 5_8  |
| 395 | Screen_3_5, // 0.6  |
| 396 | Screen_9_16, // 0.5625  |
| 397 | Screen_1_2, // 0.5  |
| 398 | Screen_1_3, // 0.3333333  |
| 399 | Last_Screen = Screen_1_3,  |
| 400 | NumScreenRatios = Last_Screen,  |
| 401 |   |
| 402 | // Print sizes listed by lower of the two dimensions and ordered by the lower size.  |
| 403 | // L means landscape.  |
| 404 | First_Print,  |
| 405 | Print_2x3 = First_Print, // 0.6666666 Same as 2_3. Wallet size.  |
| 406 | Print_2x3_L, // 1.5 Same as 3_2. Wallet size.  |
| 407 | Print_3x5, // 0.6 Same as 3_5.  |
| 408 | Print_3x5_L, // 1.6666666 Same as 5_3.  |
| 409 | Print_4x4, // 1.0 Same as 1_1.  |
| 410 | Print_4x6, // 0.6666666 Same as 2_3.  |
| 411 | Print_4x6_L, // 1.5 Same as 3_2.  |
| 412 | Print_5x7, // 0.7142857 Same as 5_7.  |
| 413 | Print_5x7_L, // 1.4 Same as 7_5.  |
| 414 | Print_5x15, // 0.3333333 Same as 1_3.  |
| 415 | Print_5x15_L, // 3.0 Same as 3_1.  |
| 416 | Print_8x8, // 1.0 Same as 1_1.  |
| 417 | Print_8x10, // 0.8 Same as 4_5.  |
| 418 | Print_8x10_L, // 1.25 Same as 5_4.  |
| 419 | Print_8x24, // 0.3333333 Same as 1_3.  |
| 420 | Print_8x24_L, // 3.0 Same as 3_1.  |
| 421 | Print_8p5x11, // 0.7727272 Same as 17_22.  |
| 422 | Print_8p5x11_L, // 1.2941176 Same as 22_17.  |
| 423 | Print_9x16, // 0.5625 Same as 9_16.  |
| 424 | Print_9x16_L, // 1.7777777 Same as 16_9.  |
| 425 | Print_11x14, // 0.7857142 Same as 11_14.  |
| 426 | Print_11x14_L, // 1.2727272 Same as 14_11.  |
| 427 | Print_11x16, // 0.6875 Same as 11_16.  |
| 428 | Print_11x16_L, // 1.4545454 Same as 16_11.  |
| 429 | Print_12x12, // 1.0 Same as 1_1.  |
| 430 | Print_12x18, // 0.6666666 Same as 2_3.  |
| 431 | Print_12x18_L, // 1.5 Same as 3_2.  |
| 432 | Print_12x36, // 0.3333333 Same as 1_3.  |
| 433 | Print_12x36_L, // 3.0 Same as 3_1.  |
| 434 | Print_16x20, // 0.8 Same as 4_5.  |
| 435 | Print_16x20_L, // 1.25 Same as 5_4.  |
| 436 | Print_18x24, // 0.75 Same as 3_4.  |
| 437 | Print_18x24_L, // 1.3333333 Same as 4_3.  |
| 438 | Print_20x30, // 0.6666666 Same as 2_3.  |
| 439 | Print_20x30_L, // 1.5 Same as 3_2.  |
| 440 | Print_24x36, // 0.6666666 Same as 2_3.  |
| 441 | Print_24x36_L, // 1.5 Same as 3_2.  |
| 442 | Last_Print = Print_24x36_L,  |
| 443 | Last_Valid = Last_Print,  |
| 444 | NumRatios, // Including Invalid.  |
| 445 | User = NumRatios  |
| 446 | };  |
| 447 |   |
| 448 | // The 'User' aspect ratio name is included in this array as the last item.  |
| 449 | extern const char* tAspectRatioNames[int(tAspectRatio::NumRatios)+1];  |
| 450 |   |
| 451 | bool tIsScreenRatio(tAspectRatio);  |
| 452 | bool tIsPrintRatio(tAspectRatio);  |
| 453 | bool tisValidRatio(tAspectRatio);  |
| 454 |   |
| 455 | // Returns 0.0f for Invalid/Free. Returns -1.0f for User.  |
| 456 | float tGetAspectRatioFloat(tAspectRatio);  |
| 457 |   |
| 458 | // If Invalid/Free/User is passed in, returns false and sets numerator and denominator to 0.  |
| 459 | // Otherwise returns true and fills in numerator and denominator in reduced form (16:10 -> 8:5).  |
| 460 | bool tGetAspectRatioFrac(int& numerator, int& denominator, tAspectRatio);  |
| 461 |   |
| 462 | // Returns the aspect ratio given numerator and denominator. Returns Invalid if either numerator or denominator  |
| 463 | // are <= 0. Returns User if the ratio doesn't exist in the enum. Returns the most reduced Screen_ ratio  |
| 464 | // otherwise. For example tGetAspectRatio(32,20) returns Screen_8_5 rather than Screen_16_10. Does not return  |
| 465 | // any of the Print_ enumerants.  |
| 466 | tAspectRatio tGetAspectRatio(int numerator, int denominator);  |
| 467 |   |
| 468 | // Gets the most reduced screen enumerant given a valid aspect ratio. Returns Invalid if Invalid passed in.  |
| 469 | // Returns User if User passed in. The function tReduceAspectRatio does the same thing, just different syntax/calling.  |
| 470 | tAspectRatio tGetReducedAspectRatio(tAspectRatio);  |
| 471 | void tReduceAspectRatio(tAspectRatio&);  |
| 472 |   |
| 473 | }  |
| 474 |   |
| 475 |   |
| 476 | // Implementation below this line.  |
| 477 |   |
| 478 |   |
| 479 | inline bool tImage::tIsValidFormat(tPixelFormat format)  |
| 480 | {  |
| 481 | return ((format >= tPixelFormat::FirstValid) && (format <= tPixelFormat::LastValid));  |
| 482 | }  |
| 483 |   |
| 484 |   |
| 485 | inline bool tImage::tIsContiguousFormat(tPixelFormat format)  |
| 486 | {  |
| 487 | return ((format >= tPixelFormat::FirstContiguous) && (format <= tPixelFormat::LastContiguous));  |
| 488 | }  |
| 489 |   |
| 490 |   |
| 491 | inline bool tImage::tIsPackedFormat(tPixelFormat format)  |
| 492 | {  |
| 493 | return ((format >= tPixelFormat::FirstPacked) && (format <= tPixelFormat::LastPacked));  |
| 494 | }  |
| 495 |   |
| 496 |   |
| 497 | inline bool tImage::tIsBCFormat(tPixelFormat format)  |
| 498 | {  |
| 499 | return ((format >= tPixelFormat::FirstBC) && (format <= tPixelFormat::LastBC));  |
| 500 | }  |
| 501 |   |
| 502 |   |
| 503 | inline bool tImage::tIsETCFormat(tPixelFormat format)  |
| 504 | {  |
| 505 | return ((format >= tPixelFormat::FirstETC) && (format <= tPixelFormat::LastETC));  |
| 506 | }  |
| 507 |   |
| 508 |   |
| 509 | inline bool tImage::tIsEACFormat(tPixelFormat format)  |
| 510 | {  |
| 511 | return ((format >= tPixelFormat::FirstEAC) && (format <= tPixelFormat::LastEAC));  |
| 512 | }  |
| 513 |   |
| 514 |   |
| 515 | inline bool tImage::tIsPVRFormat(tPixelFormat format)  |
| 516 | {  |
| 517 | return ((format >= tPixelFormat::FirstPVR) && (format <= tPixelFormat::LastPVR));  |
| 518 | }  |
| 519 |   |
| 520 |   |
| 521 | inline bool tImage::tIsASTCFormat(tPixelFormat format)  |
| 522 | {  |
| 523 | return ((format >= tPixelFormat::FirstASTC) && (format <= tPixelFormat::LastASTC));  |
| 524 | }  |
| 525 |   |
| 526 |   |
| 527 | inline bool tImage::tIsVendorFormat(tPixelFormat format)  |
| 528 | {  |
| 529 | return ((format >= tPixelFormat::FirstVendor) && (format <= tPixelFormat::LastVendor));  |
| 530 | }  |
| 531 |   |
| 532 |   |
| 533 | inline bool tImage::tIsPaletteFormat(tPixelFormat format)  |
| 534 | {  |
| 535 | return ((format >= tPixelFormat::FirstPalette) && (format <= tPixelFormat::LastPalette));  |
| 536 | }  |
| 537 |   |
| 538 |   |
| 539 | inline bool tImage::tIsAlphaFormat(tPixelFormat format)  |
| 540 | {  |
| 541 | switch (format)  |
| 542 | {  |
| 543 | case tPixelFormat::R8G8B8A8:  |
| 544 | case tPixelFormat::B8G8R8A8:  |
| 545 | case tPixelFormat::G4B4A4R4:  |
| 546 | case tPixelFormat::B4A4R4G4:  |
| 547 | case tPixelFormat::G3B5A1R5G2:  |
| 548 | case tPixelFormat::G2B5A1R5G3:  |
| 549 | case tPixelFormat::A8L8:  |
| 550 | case tPixelFormat::R16G16B16A16f:  |
| 551 | case tPixelFormat::R32G32B32A32f:  |
| 552 | case tPixelFormat::R16G16B16A16:  |
| 553 | case tPixelFormat::R32G32B32A32:  |
| 554 | case tPixelFormat::BC1DXT1A:  |
| 555 | case tPixelFormat::BC2DXT2DXT3:  |
| 556 | case tPixelFormat::BC3DXT4DXT5:  |
| 557 | case tPixelFormat::BC7:  |
| 558 | case tPixelFormat::OPENEXR:  |
| 559 |   |
| 560 | // For palettized the palette may have an entry that can be considered alpha. However for only 1-bit  |
| 561 | // palettes we consider it dithered (ColourA/ColourB) and not to have an alpha.  |
| 562 | case tPixelFormat::PAL2BIT:  |
| 563 | case tPixelFormat::PAL3BIT:  |
| 564 | case tPixelFormat::PAL4BIT:  |
| 565 | case tPixelFormat::PAL5BIT:  |
| 566 | case tPixelFormat::PAL6BIT:  |
| 567 | case tPixelFormat::PAL7BIT:  |
| 568 | case tPixelFormat::PAL8BIT:  |
| 569 | return true;  |
| 570 | }  |
| 571 |   |
| 572 | // Not quite sure how to handle ASTC formats, but they usually contain an alpha.  |
| 573 | if (tIsASTCFormat(format))  |
| 574 | return true;  |
| 575 |   |
| 576 | // PVR non-HDR formats all support alpha.  |
| 577 | if (tIsPVRFormat(format) && tIsLDRFormat(format))  |
| 578 | return true;  |
| 579 |   |
| 580 | return false;  |
| 581 | }  |
| 582 |   |
| 583 |   |
| 584 | inline bool tImage::tIsOpaqueFormat(tPixelFormat format)  |
| 585 | {  |
| 586 | return !tImage::tIsAlphaFormat(format);  |
| 587 | }  |
| 588 |   |
| 589 |   |
| 590 | inline bool tImage::tIsHDRFormat(tPixelFormat format)  |
| 591 | {  |
| 592 | switch (format)  |
| 593 | {  |
| 594 | case tPixelFormat::R16f:  |
| 595 | case tPixelFormat::R16G16f:  |
| 596 | case tPixelFormat::R16G16B16f:  |
| 597 | case tPixelFormat::R16G16B16A16f:  |
| 598 | case tPixelFormat::R32f:  |
| 599 | case tPixelFormat::R32G32f:  |
| 600 | case tPixelFormat::R32G32B32f:  |
| 601 | case tPixelFormat::R32G32B32A32f:  |
| 602 | case tPixelFormat::R11G11B10uf:  |
| 603 | case tPixelFormat::B10G11R11uf:  |
| 604 | case tPixelFormat::R9G9B9E5uf:  |
| 605 | case tPixelFormat::E5B9G9R9uf:  |
| 606 | case tPixelFormat::BC6U:  |
| 607 | case tPixelFormat::BC6S:  |
| 608 | case tPixelFormat::RADIANCE:  |
| 609 | case tPixelFormat::OPENEXR:  |
| 610 | #ifdef PIXEL_FORMAT_INCLUDE_NOT_IMPLEMENTED  |
| 611 | case tPixelFormat::PVRHDRBPP8:  |
| 612 | case tPixelFormat::PVRHDRBPP6:  |
| 613 | case tPixelFormat::PVR2HDRBPP8:  |
| 614 | case tPixelFormat::PVR2HDRBPP6:  |
| 615 | #endif  |
| 616 | case tPixelFormat::R8G8B8M8:  |
| 617 | case tPixelFormat::R8G8B8D8:  |
| 618 | return true;  |
| 619 | }  |
| 620 |   |
| 621 | // ASTCNxM can be LDR or HDR, but since they are not guaranteed to be HDR we return false for them.  |
| 622 | return false;  |
| 623 | }  |
| 624 |   |
| 625 |   |
| 626 | inline bool tImage::tIsLDRFormat(tPixelFormat format)  |
| 627 | {  |
| 628 | return !tImage::tIsHDRFormat(format);  |
| 629 | }  |
| 630 |   |
| 631 |   |
| 632 | inline bool tImage::tIsLuminanceFormat(tPixelFormat format)  |
| 633 | {  |
| 634 | switch (format)  |
| 635 | {  |
| 636 | case tPixelFormat::L8:  |
| 637 | case tPixelFormat::R8:  |
| 638 | case tPixelFormat::R16:  |
| 639 | case tPixelFormat::R32:  |
| 640 | case tPixelFormat::R16f:  |
| 641 | case tPixelFormat::R32f:  |
| 642 | case tPixelFormat::BC4ATI1U:  |
| 643 | case tPixelFormat::BC4ATI1S:  |
| 644 | case tPixelFormat::EACR11U:  |
| 645 | case tPixelFormat::EACR11S:  |
| 646 | return true;  |
| 647 | }  |
| 648 |   |
| 649 | return false;  |
| 650 | }  |
| 651 |   |
| 652 |   |
| 653 | inline int tImage::tGetNumBlocks(int blockWH, int imageWH)  |
| 654 | {  |
| 655 | tAssert(blockWH > 0);  |
| 656 | return (imageWH + blockWH - 1) / blockWH;  |
| 657 | }  |
| 658 |   |
| 659 |   |
| 660 | inline bool tImage::tIsScreenRatio(tAspectRatio ratio)  |
| 661 | {  |
| 662 | return (int(ratio) >= int(tAspectRatio::First_Screen)) && (int(ratio) <= int(tAspectRatio::Last_Screen));  |
| 663 | }  |
| 664 |   |
| 665 |   |
| 666 | inline bool tImage::tIsPrintRatio(tAspectRatio ratio)  |
| 667 | {  |
| 668 | return (int(ratio) >= int(tAspectRatio::First_Print)) && (int(ratio) <= int(tAspectRatio::Last_Print));  |
| 669 | }  |
| 670 |   |
| 671 |   |
| 672 | inline bool tImage::tisValidRatio(tAspectRatio ratio)  |
| 673 | {  |
| 674 | return (int(ratio) >= int(tAspectRatio::First_Valid)) && (int(ratio) <= int(tAspectRatio::Last_Valid));  |
| 675 | }  |
| 676 |   |
| 677 |   |
| 678 | inline void tImage::tReduceAspectRatio(tAspectRatio& aspect)  |
| 679 | {  |
| 680 | aspect = tGetReducedAspectRatio(aspect);  |
| 681 | }  |
| 682 |   |
| 683 |   |
| 684 | inline float tImage::tGetAspectRatioFloat(tAspectRatio aspect)  |
| 685 | {  |
| 686 | tReduceAspectRatio(aspect);  |
| 687 | switch (aspect)  |
| 688 | {  |
| 689 | case tAspectRatio::Invalid: return 0.0f;  |
| 690 | case tAspectRatio::User: return -1.0f;  |
| 691 | }  |
| 692 |   |
| 693 | int numerator, denominator;  |
| 694 | bool ok = tGetAspectRatioFrac(numerator, denominator, aspect);  |
| 695 | if (!ok)  |
| 696 | return 0.0f;  |
| 697 |   |
| 698 | return float(numerator) / float(denominator);  |
| 699 | }  |
| 700 | |