1 /** 2 Various public types. 3 4 Copyright: Copyright Guillaume Piolat 2022 5 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 6 */ 7 module gamut.types; 8 9 nothrow @nogc: 10 @safe: 11 12 /// Image format. 13 /// It is the kind of container/codec formats Gamut can read and write to. 14 enum ImageFormat 15 { 16 unknown = -1, /// Unknown format (returned value only, never use it as input value) 17 first = 0, 18 JPEG = 0, /// Independent JPEG Group (*.JPG, *.JIF, *.JPEG, *.JPE) 19 PNG = 1, /// Portable Network Graphics (*.PNG) 20 QOI = 2, /// Quite OK Image format (*.QOI) 21 QOIX = 3, /// Quite OK Image format, eXtended as in Gamut library (*.QOIX) 22 DDS = 4, /// Compressed texture formats. 23 TGA = 5, /// Truevision TGA 24 GIF = 6, /// Graphics Interchange Format 25 BMP = 7, /// Windows or OS/2 Bitmap File (*.BMP) 26 } 27 28 /// Pixel component type. 29 /// Integer components are stored normalized (255 or 65535 being the maximum of intensity). 30 enum PixelType 31 { 32 unknown = -1, /// Unknown format (returned value only, never use it as input value) 33 34 l8, /// Array of ubyte: unsigned 8-bit 35 l16, /// Array of ushort: unsigned 16-bit 36 lf32, /// Array of float: 32-bit IEEE floating point 37 38 la8, /// 16-bit Luminance Alpha image: 2 x unsigned 8-bit 39 la16, /// 32-bit Luminance Alpha image: 2 x unsigned 16-bit 40 laf32, /// 64-bit Luminance Alpha image: 2 x 32-bit IEEE floating point 41 42 rgb8, /// 24-bit RGB image: 3 x unsigned 8-bit 43 rgb16, /// 48-bit RGB image: 3 x unsigned 16-bit 44 rgbf32, /// 96-bit RGB float image: 3 x 32-bit IEEE floating point 45 46 rgba8, /// 32-bit RGBA image: 4 x unsigned 8-bit 47 rgba16, /// 64-bit RGBA image: 4 x unsigned 16-bit 48 rgbaf32, /// 128-bit RGBA float image: 4 x 32-bit IEEE floating point 49 } 50 51 52 // Limits 53 54 55 56 /// When images have an unknown DPI resolution; 57 enum GAMUT_UNKNOWN_RESOLUTION = -1; 58 59 /// When images have an unknown physical pixel ratio. 60 /// Explanation: it is possible to have a known pixel ratio, but an unknown DPI (eg: PNG). 61 enum GAMUT_UNKNOWN_ASPECT_RATIO = -1; 62 63 /// No Gamut `Image` can exceed this width. 64 enum int GAMUT_MAX_IMAGE_WIDTH = 16777216; 65 66 /// No Gamut `Image` can exceed this height. 67 enum int GAMUT_MAX_IMAGE_HEIGHT = 16777216; 68 69 /// No Gamut `Image` can have this many layers. 70 enum int GAMUT_MAX_IMAGE_LAYERS = 4194303; // A bit arbitrary, but can be pretty long even as 120fps video. 71 72 73 /// No Gamut `Image` can have a size that exceeds this value. 74 /// Technically, the true maximum is `MAX(size_t.max, GAMUT_MAX_IMAGE_BYTES)`. 75 /// So this is worth 32gb. Cannot really exceed that size with just malloc/realloc. 76 /// Not strictly needed, but such a large allocation is indicative of forged images / attacks anyway. 77 /// For the decoders limitations themselves, see Issue # resolution. 78 enum long GAMUT_MAX_IMAGE_BYTES = 34359738368; 79 80 81 /// Converts from meters to inches. 82 float convertMetersToInches(float x) pure 83 { 84 return x * 39.37007874f; 85 } 86 87 /// Converts from inches to meters. 88 float convertInchesToMeters(float x) pure 89 { 90 return x / 39.37007874f; 91 } 92 93 /// Converts from PPM (Points Per Meter) to DPI (Dots Per Inch). 94 alias convertPPMToDPI = convertInchesToMeters; 95 96 /// Converts from DPI (Dots Per Inch) to PPM (Points Per Meter). 97 alias convertDPIToPPM = convertMetersToInches; 98 99 100 /// Load flags (range: bits 16 to 23). 101 /// Load flags occupy high-order word so that casting to ushort only keeps `LayoutConstraints` part. 102 alias LoadFlags = int; 103 104 /// No loading options. This will keep the original input pixel format, so as to make the least 105 /// conversions possible. 106 enum LoadFlags LOAD_NORMAL = 0; 107 108 109 /// Load the image in greyscale, can be faster than loading as RGB8 then converting to greyscale. 110 /// This will preserve an alpha channel, if existing. 111 /// The resulting image will have 1 or 2 channels. 112 /// Can't be used with `LOAD_RGB` flag. 113 enum LoadFlags LOAD_GREYSCALE = 0x10000; 114 115 /// Load the image in RGB, can be faster than loading a greyscale image and then converting it RGB. 116 /// The resulting image will have 3 or 4 channels. 117 /// Can't be used with `LOAD_GREYSCALE`. 118 enum LoadFlags LOAD_RGB = 0x80000; 119 120 121 /// Load the image and adds an alpha channel (opaque if not existing). 122 /// This will preserve the color channels. 123 /// The resulting image will have 2 or 4 channels. 124 /// Can't be used with `LOAD_NO_ALPHA` flag. 125 enum LoadFlags LOAD_ALPHA = 0x20000; 126 127 /// Load the image and drops an eventual alpha channel, if it exists. 128 /// The resulting image will have 1 or 3 channels. 129 /// Can't be used with `LOAD_ALPHA` flag. 130 enum LoadFlags LOAD_NO_ALPHA = 0x40000; 131 132 133 /// Load the image directly in 8-bit, can be faster than loading as 16-bit PNG and then converting to 8-bit. 134 /// Can't be used with `LOAD_10BIT` or `LOAD_FP32` flag. 135 enum LoadFlags LOAD_8BIT = 0x100000; 136 137 /// Load the image directly in 16-bit, can be faster than loading as 8-bit PNG and then converting to 16-bit. 138 /// Can't be used with `LOAD_8BIT` or `LOAD_FP32` flag. 139 enum LoadFlags LOAD_16BIT = 0x200000; 140 141 /// Load the image directly in 32-bit floating point. 142 /// Probably the same speed as just calling `convertToFP32` after load though. 143 /// Can't be used with `LOAD_8BIT` or `LOAD_10BIT` flag. 144 enum LoadFlags LOAD_FP32 = 0x400000; 145 146 147 /// Only decode metadata, not the pixels themselves. 148 /// NOT SUPPORTED YET! 149 enum LoadFlags LOAD_NO_PIXELS = 0x800000; 150 151 152 153 154 155 156 // Encode flags 157 158 /// Do nothing particular. 159 /// Supported by: JPEG, PNG, DDS, QOI, QOIX. 160 enum int ENCODE_NORMAL = 0; 161 162 /// Internal use, this is to test a variation of a compiler. 163 /// Supported by: JPEG, PNG, DDS, QOI, QOIX. 164 enum int ENCODE_CHALLENGER = 4; 165 166 167 168 169 /// Layout constraints flags (bits 0 to 15). 170 /// All of those introduce "gap pixels" after the scanline, in order to follow the various constraints. 171 /// 172 /// Example: if you want to process 4x RGBA8 pixels at once, with aligned SSE, use: 173 /// `LAYOUT_MULTIPLICITY_4 | LAYOUT_SCANLINE_ALIGNED_16` 174 alias LayoutConstraints = ushort; 175 176 enum LayoutConstraints 177 LAYOUT_DEFAULT = 0, /// Default / do-not-care layout options. This is what will give 178 /// the fastest loading time when loading images (though most decoders 179 /// tend to return gapless non-flipped images). 180 181 // Multiplicity: 182 // ------------- 183 // 184 // Allows to access pixels by packing them together, without stepping on the next scanline or segfault. 185 // Multiplicity warrants READ access to needed excess pixels at the end of each scanline. 186 // If the image is owned => additionally it warrants WRITE access to those excess pixels. 187 // not-owned => can't WRITE to those pixels. 188 // Subimage: taking a sub-rect of an image REMOVES the constraint guarantee, it forces `LAYOUT_MULTIPLICITY_1`. 189 // 190 LAYOUT_MULTIPLICITY_1 = 0, /// No particular multiplicity requirements. 191 LAYOUT_MULTIPLICITY_2 = 1, /// Beginning at the start of a scanline, pixels can be READ 2 by 2 without segfault. 192 LAYOUT_MULTIPLICITY_4 = 2, /// Beginning at the start of a scanline, pixels can be READ 4 by 4 without segfault. 193 LAYOUT_MULTIPLICITY_8 = 3, /// Beginning at the start of a scanline, pixels can be READ 8 by 8 without segfault. 194 195 // Trailing pixels: 196 // ---------------- 197 // Allows to access the very end of a scanline with SIMD, without stepping on the next scanline or segfault. 198 // Trailing pixels warrants READ access to needed excess pixels at the end of each scanline. 199 // If the image is owned => additionally it warrants WRITE access to those excess pixels. 200 // not-owned => can't WRITE to those pixels. 201 // Subimage: taking a sub-rect of an image KEEPS the trailing pixels guarantee (but removes ability to write to them). 202 // 203 LAYOUT_TRAILING_0 = 0, /// Scanlines have no trailing requirements. 204 LAYOUT_TRAILING_1 = 4, /// Scanlines must be followed by at least 1 READABLE gap pixels. 205 LAYOUT_TRAILING_3 = 8, /// Scanlines must be followed by at least 3 READABLE gap pixels. 206 LAYOUT_TRAILING_7 = 12, /// Scanlines must be followed by at least 7 READABLE gap pixels. 207 208 209 // Scanline alignment: 210 // ------------------- 211 // Allows to access pixels from start of scanline with aligned SIMD. 212 // Both scanling addresses, and also pitchInBytes, must be aligned. 213 // 214 // Gap bytes that would exist are READABLE. 215 // If the image is owned => additionally it warrants WRITE access to those gap bytes, if any. 216 // not-owned => can't WRITE to those bytes. 217 // Subimage: taking a sub-rect of an image REMOVES the scanline alignment guarantee, it forces `LAYOUT_SCANLINE_ALIGNED_1`. 218 // 219 LAYOUT_SCANLINE_ALIGNED_1 = 0, /// No particular alignment for scanline. 220 LAYOUT_SCANLINE_ALIGNED_2 = 16, /// Scanlines required to be at least aligned on 2 bytes boundaries. 221 LAYOUT_SCANLINE_ALIGNED_4 = 32, /// Scanlines required to be at least aligned on 4 bytes boundaries. 222 LAYOUT_SCANLINE_ALIGNED_8 = 48, /// Scanlines required to be at least aligned on 8 bytes boundaries. 223 LAYOUT_SCANLINE_ALIGNED_16 = 64, /// Scanlines required to be at least aligned on 16 bytes boundaries. 224 LAYOUT_SCANLINE_ALIGNED_32 = 80, /// Scanlines required to be at least aligned on 32 bytes boundaries. 225 LAYOUT_SCANLINE_ALIGNED_64 = 96, /// Scanlines required to be at least aligned on 64 bytes boundaries. 226 LAYOUT_SCANLINE_ALIGNED_128 = 112, /// Scanlines required to be at least aligned on 128 bytes boundaries. 227 228 229 // Scanline alignment: 230 // ------------------- 231 // Allow to access additional pixels in every direction, without segfault. 232 // 233 // Border pixels are READABLE. 234 // If the image is owned => additionally iborder pixels are WRITEABLE. 235 // not-owned => can't WRITE to those pixels. 236 // Subimage: taking a sub-rect of an image KEEPS the border pixels constraint (but removes ability to write to them). 237 // 238 LAYOUT_BORDER_0 = 0, /// No particular border constraint. 239 LAYOUT_BORDER_1 = 128, /// The whole image has a border of at least 1 pixel addressable without segfault. 240 LAYOUT_BORDER_2 = 256, /// The whole image has a border of at least 2 pixels addressable without segfault. 241 LAYOUT_BORDER_3 = 384, /// The whole image has a border of at least 3 pixels addressable without segfault. 242 243 // Allow to force the image representation to be stored in a certain vertical direction. 244 LAYOUT_VERT_FLIPPED = 512, /// The whole image MUST be stored upside down. Can't be used with `LAYOUT_VERT_STRAIGHT` flag. 245 LAYOUT_VERT_STRAIGHT = 1024, /// The whole image MUST NOT be stored upside down. Can't be used with `LAYOUT_VERT_FLIPPED` flag. 246 247 // No space between scanlines. 248 // This is logically incompatible with scanline alignment, border, trailing pixels, and multiplicity. 249 // Subimage: LAYOUT_GAPLESS is immediately lost. 250 // Note: In presence of multiple layers, LAYOUT_GAPLESS also forces those layers to be immediately contiguous, not just the scanlines. 251 LAYOUT_GAPLESS = 2048; /// There must be no single trailing bytes between scanlines. 252 253 254 PixelType convertPixelTypeToGreyscale(PixelType type) pure 255 { 256 PixelType t = PixelType.unknown; 257 final switch(type) with (PixelType) 258 { 259 case unknown: t = unknown; break; 260 case l8: t = l8; break; 261 case l16: t = l16; break; 262 case lf32: t = lf32; break; 263 case la8: t = la8; break; 264 case la16: t = la16; break; 265 case laf32: t = laf32; break; 266 case rgb8: t = l8; break; 267 case rgb16: t = l16; break; 268 case rgbf32: t = lf32; break; 269 case rgba8: t = la8; break; 270 case rgba16: t = la16; break; 271 case rgbaf32: t = laf32; break; 272 } 273 return t; 274 } 275 276 PixelType convertPixelTypeToRGB(PixelType type) pure 277 { 278 PixelType t = PixelType.unknown; 279 final switch(type) with (PixelType) 280 { 281 case unknown: t = unknown; break; 282 case l8: t = rgb8; break; 283 case l16: t = rgb16; break; 284 case lf32: t = rgbf32; break; 285 case la8: t = rgba8; break; 286 case la16: t = rgba16; break; 287 case laf32: t = rgbaf32; break; 288 case rgb8: t = rgb8; break; 289 case rgb16: t = rgb16; break; 290 case rgbf32: t = rgbf32; break; 291 case rgba8: t = rgba8; break; 292 case rgba16: t = rgba16; break; 293 case rgbaf32: t = rgbaf32; break; 294 } 295 return t; 296 } 297 298 PixelType convertPixelTypeToAddAlphaChannel(PixelType type) pure 299 { 300 PixelType t = PixelType.unknown; 301 final switch(type) with (PixelType) 302 { 303 case unknown: t = unknown; break; 304 case l8: t = la8; break; 305 case l16: t = la16; break; 306 case lf32: t = laf32; break; 307 case la8: t = la8; break; 308 case la16: t = la16; break; 309 case laf32: t = laf32; break; 310 case rgb8: t = rgba8; break; 311 case rgb16: t = rgba16; break; 312 case rgbf32: t = rgbaf32; break; 313 case rgba8: t = rgba8; break; 314 case rgba16: t = rgba16; break; 315 case rgbaf32: t = rgbaf32; break; 316 } 317 return t; 318 } 319 320 PixelType convertPixelTypeToDropAlphaChannel(PixelType type) pure 321 { 322 PixelType t = PixelType.unknown; 323 final switch(type) with (PixelType) 324 { 325 case unknown: t = unknown; break; 326 case l8: t = l8; break; 327 case l16: t = l16; break; 328 case lf32: t = lf32; break; 329 case la8: t = l8; break; 330 case la16: t = l16; break; 331 case laf32: t = lf32; break; 332 case rgb8: t = rgb8; break; 333 case rgb16: t = rgb16; break; 334 case rgbf32: t = rgbf32; break; 335 case rgba8: t = rgb8; break; 336 case rgba16: t = rgb16; break; 337 case rgbaf32: t = rgbf32; break; 338 } 339 return t; 340 } 341 342 PixelType convertPixelTypeTo8Bit(PixelType type) pure 343 { 344 PixelType t = PixelType.unknown; 345 final switch(type) with (PixelType) 346 { 347 case unknown: t = unknown; break; 348 case l8: t = l8; break; 349 case l16: t = l8; break; 350 case lf32: t = l8; break; 351 case la8: t = la8; break; 352 case la16: t = la8; break; 353 case laf32: t = la8; break; 354 case rgb8: t = rgb8; break; 355 case rgb16: t = rgb8; break; 356 case rgbf32: t = rgb8; break; 357 case rgba8: t = rgba8; break; 358 case rgba16: t = rgba8; break; 359 case rgbaf32: t = rgba8; break; 360 } 361 return t; 362 } 363 364 PixelType convertPixelTypeTo16Bit(PixelType type) pure 365 { 366 PixelType t = PixelType.unknown; 367 final switch(type) with (PixelType) 368 { 369 case unknown: t = unknown; break; 370 case l8: t = l16; break; 371 case l16: t = l16; break; 372 case lf32: t = l16; break; 373 case la8: t = la16; break; 374 case la16: t = la16; break; 375 case laf32: t = la16; break; 376 case rgb8: t = rgb16; break; 377 case rgb16: t = rgb16; break; 378 case rgbf32: t = rgb16; break; 379 case rgba8: t = rgba16; break; 380 case rgba16: t = rgba16; break; 381 case rgbaf32: t = rgba16; break; 382 } 383 return t; 384 } 385 386 387 PixelType convertPixelTypeToFP32(PixelType type) pure 388 { 389 PixelType t = PixelType.unknown; 390 final switch(type) with (PixelType) 391 { 392 case unknown: t = unknown; break; 393 case l8: t = lf32; break; 394 case l16: t = lf32; break; 395 case lf32: t = lf32; break; 396 case la8: t = laf32; break; 397 case la16: t = laf32; break; 398 case laf32: t = laf32; break; 399 case rgb8: t = rgbf32; break; 400 case rgb16: t = rgbf32; break; 401 case rgbf32: t = rgbf32; break; 402 case rgba8: t = rgbaf32; break; 403 case rgba16: t = rgbaf32; break; 404 case rgbaf32: t = rgbaf32; break; 405 } 406 return t; 407 } 408