1 /++
2  + Authors: Avaxar <avaxar@nekkl.org>
3  + Copyright: Copyright © 2023, Avaxar
4  + License: $(LINK2 https://mit-license.org, MIT License)
5  +/
6 
7 module dsdl2.pixels;
8 @safe:
9 
10 import bindbc.sdl;
11 import dsdl2.sdl;
12 
13 import core.memory : GC;
14 import std.conv : to;
15 import std.format : format;
16 
17 /++
18  + D struct that wraps `SDL_Color` containing 4 bytes for storing color values of 3 color channels and 1 alpha
19  + channel.
20  +
21  + `dsdl2.Color` stores unsigned `byte`-sized (0-255) `r`ed, `g`reen, `b`lue color, and `a`lpha channel values.
22  + In total there are 16,777,216 possible color values. Combined with the `a`lpha (transparency) channel, there
23  + are 4,294,967,296 combinations.
24  +
25  + Examples
26  + ---
27  + auto red = dsdl2.Color(255, 0, 0);
28  + auto translucentRed = dsdl2.Color(255, 0, 0, 128);
29  + ---
30  +/
31 struct Color {
32     SDL_Color sdlColor; /// Internal `SDL_Color` struct
33 
34     this() @disable;
35 
36     /++
37      + Constructs a `dsdl2.Color` from a vanilla `SDL_Color` from bindbc-sdl
38      +
39      + Params:
40      +   sdlColor = the `SDL_Color` struct
41      +/
42     this(SDL_Color sdlColor) {
43         this.sdlColor = sdlColor;
44     }
45 
46     /++
47      + Constructs a `dsdl2.Color` by feeding in `r`ed, `g`reen, `b`lue, and optionally `a`lpha values
48      +
49      + Params:
50      +   r = red color channel value (0-255)
51      +   g = green color channel value (0-255)
52      +   b = blue color channel value (0-255)
53      +   a = alpha transparency channel value (0-255 / transparent-opaque)
54      +/
55     this(ubyte r, ubyte g, ubyte b, ubyte a = 255) {
56         this.sdlColor.r = r;
57         this.sdlColor.g = g;
58         this.sdlColor.b = b;
59         this.sdlColor.a = a;
60     }
61 
62     /++
63      + Constructs a `dsdl2.Color` by feeding in an array of `r`ed, `g`reen, and `b`lue, with `a`lpha being `255`
64      +
65      + Params:
66      +   rgb = array of `r`ed, `g`reen, `b`lue values
67      +/
68     this(ubyte[3] rgb) {
69         this.sdlColor.r = rgb[0];
70         this.sdlColor.g = rgb[1];
71         this.sdlColor.b = rgb[2];
72         this.sdlColor.a = 255;
73     }
74 
75     /++
76      + Constructs a `dsdl2.Color` by feeding in an array of `r`ed, `g`reen, `b`lue, and `a`lpha
77      +
78      + Params:
79      +   rgba = array of `r`ed, `g`reen, `b`lue, `a`lpha values
80      +/
81     this(ubyte[4] rgba) {
82         this.sdlColor.r = rgba[0];
83         this.sdlColor.g = rgba[1];
84         this.sdlColor.b = rgba[2];
85         this.sdlColor.a = rgba[3];
86     }
87 
88     /++
89      + Formats the `dsdl2.Color` into its construction representation: `"dsdl2.Color(<r>, <g>, <b>, <a>)"`
90      +
91      + Returns: the formatted `string`
92      +/
93     string toString() const {
94         return "dsdl2.Color(%d, %d, %d, %d)".format(this.r, this.g, this.b, this.a);
95     }
96 
97     /++
98      + Proxy to the red color value of the `dsdl2.Color`
99      +
100      + Returns: red color value of the `dsdl2.Color`
101      +/
102     ref inout(ubyte) r() return inout @property {
103         return this.sdlColor.r;
104     }
105 
106     /++
107      + Proxy to the green color value of the `dsdl2.Color`
108      +
109      + Returns: green color value of the `dsdl2.Color`
110      +/
111     ref inout(ubyte) g() return inout @property {
112         return this.sdlColor.g;
113     }
114 
115     /++
116      + Proxy to the blue color value of the `dsdl2.Color`
117      +
118      + Returns: blue color value of the `dsdl2.Color`
119      +/
120     ref inout(ubyte) b() return inout @property {
121         return this.sdlColor.b;
122     }
123 
124     /++
125      + Proxy to the alpha transparency value of the `dsdl2.Color`
126      +
127      + Returns: alpha transparency value of the `dsdl2.Color`
128      +/
129     ref inout(ubyte) a() return inout @property {
130         return this.sdlColor.a;
131     }
132 
133     /++
134      + Static array proxy of the `dsdl2.Color`
135      +
136      + Returns: array of `r`, `g`, `b`, `a`
137      +/
138     ref inout(ubyte[4]) array() return inout @property @trusted {
139         return *cast(inout(ubyte[4]*))&this.sdlColor;
140     }
141 }
142 
143 /++
144  + D class that wraps `SDL_Palette` storing multiple `dsdl2.Color` as a palette to use along with indexed
145  + `dsdl2.PixelFormat` instances
146  +/
147 final class Palette {
148     private bool isOwner = true;
149     private void* userRef = null;
150 
151     @system SDL_Palette* sdlPalette = null; /// Internal `SDL_Palette` pointer
152 
153     /++
154      + Constructs a `dsdl2.Palette` from a vanilla `SDL_Palette*` from bindbc-sdl
155      +
156      + Params:
157      +   sdlPalette = the `SDL_Palette` pointer to manage
158      +   isOwner = whether the instance owns the given `SDL_Palette*` and should destroy it on its own
159      +   userRef = optional pointer to maintain reference link, avoiding GC cleanup
160      +/
161     this(SDL_Palette* sdlPalette, bool isOwner = true, void* userRef = null) @system
162     in {
163         assert(sdlPalette !is null);
164     }
165     do {
166         this.sdlPalette = sdlPalette;
167         this.isOwner = isOwner;
168         this.userRef = userRef;
169     }
170 
171     /++
172      + Constructs a `dsdl2.Palette` and allocate memory for a set amount of `dsdl2.Color`s
173      +
174      + Params:
175      +   ncolors = amount of `dsdl2.Color`s to allocate in the `dsdl2.Palette`
176      + Throws: `dsdl2.SDLException` if allocation failed
177      +/
178     this(uint ncolors) @trusted {
179         this.sdlPalette = SDL_AllocPalette(ncolors.to!int);
180         if (this.sdlPalette is null) {
181             throw new SDLException;
182         }
183     }
184 
185     /++
186      + Constructs a `dsdl2.Palette` from an array of `dsdl2.Color`s
187      +
188      + Params:
189      +   colors = an array/slice of `dsdl2.Color`s to put in the `dsdl2.Palette`
190      + Throws: `dsdl2.SDLException` if allocation failed
191      +/
192     this(const Color[] colors) @trusted {
193         this.sdlPalette = SDL_AllocPalette(colors.length.to!int);
194         if (this.sdlPalette is null) {
195             throw new SDLException;
196         }
197 
198         foreach (i, const ref color; colors) {
199             this.sdlPalette.colors[i] = color.sdlColor;
200         }
201     }
202 
203     ~this() @trusted {
204         if (this.isOwner) {
205             SDL_FreePalette(this.sdlPalette);
206         }
207     }
208 
209     @trusted invariant { // @suppress(dscanner.trust_too_much)
210         // Instance might be in an invalid state due to holding a non-owned externally-freed object when
211         // destructed in an unpredictable order.
212         if (!this.isOwner && GC.inFinalizer) {
213             return;
214         }
215 
216         assert(this.sdlPalette !is null);
217     }
218 
219     /++
220      + Equality operator overload
221      +/
222     bool opEquals(const Palette rhs) const @trusted {
223         if (rhs is null) {
224             return false;
225         }
226 
227         if (this.sdlPalette is rhs.sdlPalette) {
228             return true;
229         }
230 
231         if (this.length != rhs.length) {
232             return false;
233         }
234 
235         foreach (i; 0 .. this.length) {
236             if (this[i] != rhs[i]) {
237                 return false;
238             }
239         }
240 
241         return true;
242     }
243 
244     /++
245      + Gets the hash of the `dsdl2.Palette`
246      +
247      + Returns: unique hash for the `dsdl2.Palette`
248      +/
249     override hash_t toHash() const @trusted {
250         try {
251             return this.colors.hashOf;
252         }
253         catch (Exception) {
254             assert(false);
255         }
256     }
257 
258     /++
259      + Indexing operation overload
260      +/
261     ref inout(Color) opIndex(size_t i) return inout @trusted
262     in {
263         assert(0 <= i && i < this.length);
264     }
265     do {
266         return *cast(inout(Color*))&this.sdlPalette.colors[i];
267     }
268 
269     /++
270      + Dollar sign overload
271      +/
272     size_t opDollar(size_t dim)() const if (dim == 0) {
273         return this.length;
274     }
275 
276     /++
277      + Formats the `dsdl2.Palette` into its construction representation: `"dsdl2.Palette([<list of dsdl2.Color>])"`
278      +
279      + Returns: the formatted `string`
280      +/
281     override string toString() const {
282         string str = "dsdl2.Palette([";
283 
284         foreach (i; 0 .. this.length) {
285             str ~= this[i].toString();
286 
287             if (i + 1 < this.length) {
288                 str ~= ", ";
289             }
290         }
291 
292         return str ~= "])";
293     }
294 
295     /++
296      + Gets the length of `dsdl2.Color`s allocated in the `dsdl2.Palette`
297      +
298      + Returns: number of `dsdl2.Color`s
299      +/
300     size_t length() const @property @trusted {
301         return this.sdlPalette.ncolors;
302     }
303 
304     /++
305      + Proxy to the `dsdl2.Color` array of the `dsdl2.Palette`
306      +
307      + This function is marked as `@system` due to the potential of referencing invalid memory.
308      +
309      + Returns: `dsdl2.Color` array of the `dsdl2.Palette`
310      +/
311     inout(Color[]) colors() inout @property @system {
312         return (cast(inout(Color*))&this.sdlPalette.colors)[0 .. this.length];
313     }
314 }
315 ///
316 unittest {
317     auto myPalette = new dsdl2.Palette([dsdl2.Color(1, 2, 3), dsdl2.Color(3, 2, 1)]);
318     assert(myPalette.length == 2);
319     assert(myPalette[0] == dsdl2.Color(1, 2, 3));
320 }
321 
322 /++
323  + D class that wraps `SDL_PixelFormat` defining the color and alpha channel bit layout in the internal
324  + representation of a pixel
325  +/
326 final class PixelFormat {
327     static PixelFormat _multiton(SDL_PixelFormatEnum sdlPixelFormatEnum, ubyte minMinorVer = 0,
328         ubyte minPatchVer = 0)()
329     in {
330         assert(getVersion() >= Version(2, minMinorVer, minPatchVer));
331     }
332     do {
333         static PixelFormat pixelFormat = null;
334         if (pixelFormat is null) {
335             pixelFormat = new PixelFormat(sdlPixelFormatEnum);
336         }
337 
338         return pixelFormat;
339     }
340 
341     static PixelFormat _instantiateIndexed(SDL_PixelFormatEnum sdlPixelFormatEnum, ubyte minMinorVer = 0,
342         ubyte minPatchVer = 0)(Palette palette)
343     in {
344         assert(getVersion() >= Version(2, minMinorVer, minPatchVer));
345         assert(palette !is null);
346     }
347     do {
348         return new PixelFormat(sdlPixelFormatEnum, palette);
349     }
350 
351     /++
352      + Instantiates indexed `dsdl2.PixelFormat` for use with `dsdl2.Palette`s from `SDL_PIXELFORMAT_*` enumeration
353      + constants
354      +/
355     static alias index1lsb = _instantiateIndexed!SDL_PIXELFORMAT_INDEX1LSB;
356     static alias index1msb = _instantiateIndexed!SDL_PIXELFORMAT_INDEX1MSB; /// ditto
357     static alias index4lsb = _instantiateIndexed!SDL_PIXELFORMAT_INDEX4LSB; /// ditto
358     static alias index4msb = _instantiateIndexed!SDL_PIXELFORMAT_INDEX4MSB; /// ditto
359     static alias index8 = _instantiateIndexed!SDL_PIXELFORMAT_INDEX8; /// ditto
360     static alias yv12 = _instantiateIndexed!SDL_PIXELFORMAT_YV12; /// ditto
361     static alias yuy2 = _instantiateIndexed!SDL_PIXELFORMAT_YUY2; /// ditto
362 
363     /++
364      + Retrieves one of the `dsdl2.PixelFormat` multiton presets from `SDL_PIXELFORMAT_*` enumeration constants
365      +/
366     static alias rgb332 = _multiton!SDL_PIXELFORMAT_RGB332;
367     static alias rgb444 = _multiton!SDL_PIXELFORMAT_RGB444; /// ditto
368     static alias rgb555 = _multiton!SDL_PIXELFORMAT_RGB555; /// ditto
369     static alias bgr555 = _multiton!SDL_PIXELFORMAT_BGR555; /// ditto
370     static alias argb4444 = _multiton!SDL_PIXELFORMAT_ARGB4444; /// ditto
371     static alias rgba444 = _multiton!SDL_PIXELFORMAT_RGBA4444; /// ditto
372     static alias abgr4444 = _multiton!SDL_PIXELFORMAT_ABGR4444; /// ditto
373     static alias bgra4444 = _multiton!SDL_PIXELFORMAT_BGRA4444; /// ditto
374     static alias argb1555 = _multiton!SDL_PIXELFORMAT_ARGB1555; /// ditto
375     static alias rgba5551 = _multiton!SDL_PIXELFORMAT_RGBA5551; /// ditto
376     static alias abgr1555 = _multiton!SDL_PIXELFORMAT_ABGR1555; /// ditto
377     static alias bgra5551 = _multiton!SDL_PIXELFORMAT_BGRA5551; /// ditto
378     static alias rgb565 = _multiton!SDL_PIXELFORMAT_RGB565; /// ditto
379     static alias bgr565 = _multiton!SDL_PIXELFORMAT_BGR565; /// ditto
380     static alias rgb24 = _multiton!SDL_PIXELFORMAT_RGB24; /// ditto
381     static alias bgr24 = _multiton!SDL_PIXELFORMAT_BGR24; /// ditto
382     static alias rgb888 = _multiton!SDL_PIXELFORMAT_RGB888; /// ditto
383     static alias rgbx8888 = _multiton!SDL_PIXELFORMAT_RGBX8888; /// ditto
384     static alias bgr888 = _multiton!SDL_PIXELFORMAT_BGR888; /// ditto
385     static alias bgrx8888 = _multiton!SDL_PIXELFORMAT_BGRX8888; /// ditto
386     static alias argb8888 = _multiton!SDL_PIXELFORMAT_ARGB8888; /// ditto
387     static alias rgba8888 = _multiton!SDL_PIXELFORMAT_RGBA8888; /// ditto
388     static alias abgr8888 = _multiton!SDL_PIXELFORMAT_ABGR8888; /// ditto
389     static alias bgra8888 = _multiton!SDL_PIXELFORMAT_BGRA8888; /// ditto
390     static alias argb2101010 = _multiton!SDL_PIXELFORMAT_ARGB2101010; /// ditto
391 
392     static alias iyuv = _multiton!SDL_PIXELFORMAT_IYUV; /// ditto
393     static alias uyvy = _multiton!SDL_PIXELFORMAT_UYVY; /// ditto
394     static alias yvyu = _multiton!SDL_PIXELFORMAT_YVYU; /// ditto
395 
396     static if (sdlSupport >= SDLSupport.v2_0_4) {
397         /++
398          + Instantiates indexed `dsdl2.PixelFormat` for use with `dsdl2.Palette`s from `SDL_PIXELFORMAT_*`
399          + enumeration constants (from SDL 2.0.4)
400          +/
401         static alias nv12 = _instantiateIndexed!(SDL_PIXELFORMAT_NV12, 0, 4);
402         static alias nv21 = _instantiateIndexed!(SDL_PIXELFORMAT_NV21, 0, 4); /// ditto
403     }
404 
405     static if (sdlSupport >= SDLSupport.v2_0_5) {
406         /++
407          + Retrieves one of the `dsdl2.PixelFormat` multiton presets from `SDL_PIXELFORMAT_*` enumeration constants
408          + (from SDL 2.0.5)
409          +/
410         static alias rgba32 = _multiton!(SDL_PIXELFORMAT_RGBA32, 0, 5);
411         static alias argb32 = _multiton!(SDL_PIXELFORMAT_ARGB32, 0, 5); /// ditto
412         static alias bgra32 = _multiton!(SDL_PIXELFORMAT_BGRA32, 0, 5); /// ditto
413         static alias abgr32 = _multiton!(SDL_PIXELFORMAT_ABGR32, 0, 5); /// ditto
414     }
415 
416     private Palette paletteRef = null;
417     private bool isOwner = true;
418     private void* userRef = null;
419 
420     @system SDL_PixelFormat* sdlPixelFormat = null; /// Internal `SDL_PixelFormat` pointer
421 
422     /++
423      + Constructs a `dsdl2.PixelFormat` from a vanilla `SDL_PixelFormat*` from bindbc-sdl
424      +
425      + Params:
426      +   sdlPixelFormat = the `SDL_PixelFormat` pointer to manage
427      +   isOwner = whether the instance owns the given `SDL_PixelFormat*` and should destroy it on its own
428      +   userRef = optional pointer to maintain reference link, avoiding GC cleanup
429      +/
430     this(SDL_PixelFormat* sdlPixelFormat, bool isOwner = true, void* userRef = null) @system
431     in {
432         assert(sdlPixelFormat !is null);
433     }
434     do {
435         this.sdlPixelFormat = sdlPixelFormat;
436         this.isOwner = isOwner;
437         this.userRef = userRef;
438 
439         if (this.sdlPixelFormat.palette !is null) {
440             this.paletteRef = new Palette(this.sdlPixelFormat.palette, false, cast(void*) this);
441         }
442     }
443 
444     /++
445      + Constructs a `dsdl2.PixelFormat` using an `SDL_PixelFormatEnum` from bindbc-sdl
446      +
447      + Params:
448      +   sdlPixelFormatEnum = the `SDL_PixelFormatEnum` enumeration (non-indexed)
449      + Throws: `dsdl2.SDLException` if allocation failed
450      +/
451     this(SDL_PixelFormatEnum sdlPixelFormatEnum) @trusted
452     in {
453         assert(sdlPixelFormatEnum != SDL_PIXELFORMAT_UNKNOWN);
454         assert(!SDL_ISPIXELFORMAT_INDEXED(sdlPixelFormatEnum));
455     }
456     do {
457         this.sdlPixelFormat = SDL_AllocFormat(sdlPixelFormatEnum);
458         if (this.sdlPixelFormat is null) {
459             throw new SDLException;
460         }
461     }
462 
463     /++
464      + Constructs a `dsdl2.PixelFormat` using an indexed `SDL_PixelFormatEnum` from bindbc-sdl, allowing use with
465      + `dsdl2.Palette`s
466      +
467      + Params:
468      +   sdlPixelFormatEnum = the `SDL_PixelFormatEnum` enumeration (indexed)
469      +   palette = the `dsdl2.Palette` class instance to bind as the color palette
470      + Throws: `dsdl2.SDLException` if allocation or palette-setting failed
471      +/
472     this(SDL_PixelFormatEnum sdlPixelFormatEnum, Palette palette) @trusted
473     in {
474         assert(SDL_ISPIXELFORMAT_INDEXED(sdlPixelFormatEnum));
475         assert(palette !is null);
476     }
477     do {
478         this.sdlPixelFormat = SDL_AllocFormat(sdlPixelFormatEnum);
479         if (this.sdlPixelFormat is null) {
480             throw new SDLException;
481         }
482 
483         if (SDL_SetPixelFormatPalette(this.sdlPixelFormat, palette.sdlPalette) != 0) {
484             throw new SDLException;
485         }
486 
487         this.paletteRef = palette;
488     }
489 
490     /++
491      + Constructs a `dsdl2.PixelFormat` from user-provided bit masks for RGB color and alpha channels by internally
492      + using `SDL_MasksToPixelFormatEnum` to retrieve the `SDL_PixelFormatEnum`
493      +
494      + Params:
495      +   bitsPerPixel = size of one pixel in bits
496      +   rgbaMasks = bit masks for the red, green, blue, and alpha channels
497      + Throws: `dsdl2.SDLException` if pixel format conversion not possible
498      +/
499     this(ubyte bitsPerPixel, uint[4] rgbaMasks) @trusted
500     in {
501         assert(bitsPerPixel > 0);
502     }
503     do {
504         uint sdlPixelFormatEnum = SDL_MasksToPixelFormatEnum(bitsPerPixel, rgbaMasks[0], rgbaMasks[1], rgbaMasks[2],
505             rgbaMasks[3]);
506         if (sdlPixelFormatEnum == SDL_PIXELFORMAT_UNKNOWN) {
507             throw new SDLException("Pixel format conversion is not possible", __FILE__, __LINE__);
508         }
509 
510         this(sdlPixelFormatEnum);
511     }
512 
513     ~this() @trusted {
514         if (this.isOwner) {
515             SDL_FreeFormat(this.sdlPixelFormat);
516         }
517     }
518 
519     @trusted invariant { // @suppress(dscanner.trust_too_much)
520         // Instance might be in an invalid state due to holding a non-owned externally-freed object when
521         // destructed in an unpredictable order.
522         if (!this.isOwner && GC.inFinalizer) {
523             return;
524         }
525 
526         assert(this.sdlPixelFormat !is null);
527         if (this.isOwner) {
528             assert(this.sdlPixelFormat.format != SDL_PIXELFORMAT_UNKNOWN);
529         }
530 
531         if (SDL_ISPIXELFORMAT_INDEXED(this.sdlPixelFormat.format)) {
532             assert(this.paletteRef !is null);
533             if (this.isOwner) {
534                 assert(this.sdlPixelFormat.palette !is null);
535             }
536         }
537     }
538 
539     /++
540      + Equality operator overload
541      +/
542     bool opEquals(const PixelFormat rhs) const @trusted {
543         if (rhs is null) {
544             return false;
545         }
546 
547         if (this.sdlPixelFormat.format == rhs.sdlPixelFormat.format) {
548             return true;
549         }
550 
551         if (this.sdlPixelFormatEnum != rhs.sdlPixelFormatEnum) {
552             return false;
553         }
554 
555         if (SDL_ISPIXELFORMAT_INDEXED(this.sdlPixelFormatEnum)) {
556             return this.paletteRef == rhs.paletteRef;
557         }
558         else {
559             return true;
560         }
561     }
562 
563     /++
564      + Gets the hash of the `dsdl2.PixelFormat`
565      +
566      + Returns: unique hash for the `dsdl2.PixelFormat`
567      +/
568     override hash_t toHash() const @trusted {
569         try {
570             return this.sdlPixelFormatEnum.hashOf(this.paletteRef.hashOf);
571         }
572         catch (Exception) {
573             assert(false);
574         }
575     }
576 
577     /++
578      + Formats the `dsdl2.PixelFormat` into its construction representation:
579      + `"dsdl2.PixelFormat(<sdlPixelFormatEnum>)"` or `"dsdl2.PixelFormat(<sdlPixelFormatEnum>, <palette>)"`
580      +
581      + Returns: the formatted `string`
582      +/
583     override string toString() const @trusted {
584         if (SDL_ISPIXELFORMAT_INDEXED(this.sdlPixelFormatEnum)) {
585             return "dsdl2.PixelFormat(0x%x, %s)".format(this.sdlPixelFormatEnum, this.paletteRef);
586         }
587         else {
588             return "dsdl2.PixelFormat(0x%s)".format(this.sdlPixelFormatEnum);
589         }
590     }
591 
592     /++
593      + Gets the `SDL_PixelFormatEnum` of the underlying `SDL_PixelFormat`
594      +
595      + Returns: `SDL_PixelFormatEnum` enumeration from bindbc-sdl
596      +/
597     SDL_PixelFormatEnum sdlPixelFormatEnum() const @property @trusted {
598         return this.sdlPixelFormat.format;
599     }
600 
601     /++
602      + Wraps `SDL_GetRGB` which converts a pixel `uint` value to a comprehensible `dsdl2.Color` struct without
603      + accounting the alpha value (automatically set to opaque [255]), based on the pixel format defined by the
604      + `dsdl2.PixelFormat`
605      +
606      + Params:
607      +   pixel = the pixel `uint` value to convert
608      + Returns: the `dsdl2.Color` struct of the given `pixel` value
609      +/
610     Color getRGB(uint pixel) const @trusted {
611         Color color = Color(0, 0, 0, 255);
612         SDL_GetRGB(pixel, this.sdlPixelFormat, &color.sdlColor.r, &color.sdlColor.g,
613             &color.sdlColor.b);
614         return color;
615     }
616 
617     /++
618      + Wraps `SDL_GetRGBA` which converts a pixel `uint` value to a comprehensible `dsdl2.Color` struct, based on
619      + the pixel format defined by the `dsdl2.PixelFormat`
620      +
621      + Params:
622      +   pixel = the pixel `uint` value to convert
623      + Returns: the `dsdl2.Color` struct of the given `pixel` value
624      +/
625     Color getRGBA(uint pixel) const @trusted {
626         Color color = void;
627         SDL_GetRGBA(pixel, this.sdlPixelFormat, &color.sdlColor.r, &color.sdlColor.g, &color.sdlColor.b,
628             &color.sdlColor.a);
629         return color;
630     }
631 
632     /++
633      + Wraps `SDL_MapRGB` which converts a `dsdl2.Color` to its pixel `uint` value according to the pixel format
634      + defined by the `dsdl2.PixelFormat` without accounting the alpha value, assuming that it's opaque
635      +
636      + Params:
637      +   color = the `dsdl2.Color` struct to convert
638      + Returns: the converted pixel value
639      +/
640     uint mapRGB(Color color) const @trusted {
641         return SDL_MapRGB(this.sdlPixelFormat, color.sdlColor.r, color.sdlColor.g,
642             color.sdlColor.b);
643     }
644 
645     /++
646      + Wraps `SDL_MapRGBA` which converts a `dsdl2.Color` to its pixel `uint` value according to the pixel format
647      + defined by the `dsdl2.PixelFormat`
648      +
649      + Params:
650      +   color = the `dsdl2.Color` struct to convert
651      + Returns: the converted pixel value
652      +/
653     uint mapRGBA(Color color) const @trusted {
654         return SDL_MapRGBA(this.sdlPixelFormat, color.sdlColor.r, color.sdlColor.g, color.sdlColor.b,
655             color.sdlColor.a);
656     }
657 
658     /++
659      + Gets the `dsdl2.Palette` bounds to the indexed `dsdl2.PixelFormat`
660      +
661      + Returns: the bound `dsdl2.Palette`
662      +/
663     inout(Palette) palette() return inout @property @trusted
664     in {
665         assert(this.indexed);
666     }
667     do {
668         return this.paletteRef;
669     }
670 
671     /++
672      + Wraps `SDL_SetPixelFormatPalette` which sets the `dsdl2.Palette` for indexed `dsdl2.PixelFormat`s`
673      +
674      + Params:
675      +   newPalette = the `dsdl2.Palette` class instance to bind as the color palette
676      +/
677     void palette(Palette newPalette) @property @trusted
678     in {
679         assert(this.indexed);
680         assert(newPalette !is null);
681     }
682     do {
683         if (SDL_SetPixelFormatPalette(this.sdlPixelFormat, newPalette.sdlPalette) != 0) {
684             throw new SDLException;
685         }
686 
687         this.paletteRef = newPalette;
688     }
689 
690     /++
691      + Gets the bit depth (size of a pixel in bits) of the `dsdl2.PixelFormat`
692      +
693      + Returns: the bit depth of the `dsdl2.PixelFormat`
694      +/
695     ubyte bitsPerPixel() const @property @trusted {
696         return this.sdlPixelFormat.BitsPerPixel;
697     }
698 
699     /++
700      + Gets the how many bytes needed to represent a pixel in the `dsdl2.PixelFormat`
701      +
702      + Returns: the bytes per pixel value of the `dsdl2.PixelFormat`
703      +/
704     size_t bytesPerPixel() const @property @trusted {
705         return this.sdlPixelFormat.BytesPerPixel;
706     }
707 
708     /++
709      + Wraps `SDL_PixelFormatEnumToMasks` which gets the bit mask for all four channels of the `dsdl2.PixelFormat`
710      +
711      + Returns: an array of 4 bit masks for each channel (red, green, blue, and alpha)
712      +/
713     uint[4] toMasks() const @trusted {
714         uint[4] rgbaMasks = void;
715         int bitsPerPixel = void;
716 
717         if (SDL_PixelFormatEnumToMasks(this.sdlPixelFormatEnum, &bitsPerPixel, &rgbaMasks[0], &rgbaMasks[1],
718                 &rgbaMasks[2], &rgbaMasks[3]) == SDL_FALSE) {
719             throw new SDLException;
720         }
721 
722         return rgbaMasks;
723     }
724 
725     /++
726      + Wraps `SDL_ISPIXELFORMAT_INDEXED` which checks whether the `dsdl2.PixelFormat` is indexed
727      +
728      + Returns: `true` if it is indexed, otherwise `false`
729      +/
730     bool indexed() const @property @trusted {
731         return SDL_ISPIXELFORMAT_INDEXED(this.sdlPixelFormatEnum);
732     }
733 
734     /++
735      + Wraps `SDL_ISPIXELFORMAT_ALPHA` which checks whether the `dsdl2.PixelFormat` is capable of storing alpha value
736      +
737      + Returns: `true` if it can have an alpha channel, otherwise `false`
738      +/
739     bool hasAlpha() const @property @trusted {
740         return SDL_ISPIXELFORMAT_ALPHA(this.sdlPixelFormatEnum);
741     }
742 
743     /++
744      + Wraps `SDL_ISPIXELFORMAT_FOURCC` which checks whether the `dsdl2.PixelFormat` represents a unique format
745      +
746      + Returns: `true` if it is unique, otherwise `false`
747      +/
748     bool fourCC() const @property @trusted {
749         return SDL_ISPIXELFORMAT_FOURCC(this.sdlPixelFormatEnum) != 0;
750     }
751 }
752 ///
753 unittest {
754     static if (sdlSupport >= SDLSupport.v2_0_5) {
755         const auto rgba32 = dsdl2.PixelFormat.rgba32;
756         assert(rgba32.mapRGBA(dsdl2.Color(0x12, 0x34, 0x56, 0x78)) == 0x12345678);
757         assert(rgba32.getRGBA(0x12345678) == dsdl2.Color(0x12, 0x34, 0x56, 0x78));
758     }
759 
760     const auto rgba8888 = dsdl2.PixelFormat.rgba8888;
761     version (LittleEndian) {
762         assert(rgba8888.mapRGBA(dsdl2.Color(0x12, 0x34, 0x56, 0x78)) == 0x12345678);
763         assert(rgba8888.getRGBA(0x12345678) == dsdl2.Color(0x12, 0x34, 0x56, 0x78));
764     }
765     version (BigEndian) {
766         assert(rgba8888.mapRGBA(dsdl2.Color(0x12, 0x34, 0x56, 0x78)) == 0x78563412);
767         assert(rgba8888.get(0x78563412) == dsdl2.Color(0x12, 0x34, 0x56, 0x78));
768     }
769 }