1 /++
2 Color manipulation module. Mixing, converting, parsing colors.
3 
4 For mixing use implementations: $(LREF BlendImpl), using 
5 $(HREF #BlendFactor, factors) you will get the desired mixing result as 
6 a function. An example would be the simplest blending by alpha value: 
7 ---
8 BlendImpl!(BlendFactor.SrcAlpha, BlendFactor.OneMinusSrcAlpha, Type).
9 ---
10 
11 If you need to convert a set of bytes to a set of colors, 
12 use the $(LREF fromColors) function.
13 
14 Macros:
15     LREF = <a href="#$1">$1</a>
16     HREF = <a href="$1">$2</a>
17 
18 Authors: $(HREF https://github.com/TodNaz,TodNaz)
19 Copyright: Copyright (c) 2020 - 2021, TodNaz.
20 License: $(HREF https://github.com/TodNaz/Tida/blob/master/LICENSE,MIT)
21 +/
22 module tida.color;
23 
24 import std.traits;
25 
26 enum CannotDetectAuto = 
27 "The format cannot be detected automatically!";
28 
29 /// Pixel representation format.
30 enum PixelFormat : int
31 {
32     None, /// None format
33     Auto, /// Automatic detection
34     RGB, /// Red-Green-Blue
35     RGBA, /// Red-Green-Blue-Alpha
36     ARGB, /// Alpha-Red-Green-Blue
37     BGRA, /// Blue-Green-Red-Alpha
38     BGR /// Blur-Green-Red
39 }
40 
41 /++
42 Whether the pixel format is valid for the job.
43 +/
44 template isValidFormat(int pixelformat)
45 {
46     enum isValidFormat =    pixelformat != PixelFormat.None &&
47                             pixelformat != PixelFormat.Auto &&
48                             pixelformat <= PixelFormat.max;
49 }
50 
51 /++
52 Shows how many bytes are contained in a color unit.
53 +/
54 template bytesPerColor(int pixelformat, T = ubyte)
55 {
56     static assert(isValidFormat!pixelformat, "Invalid or unknown pixel format!");
57 
58     static if ( pixelformat == PixelFormat.RGBA ||
59                 pixelformat == PixelFormat.ARGB ||
60                 pixelformat == PixelFormat.BGRA)
61     {
62         enum bytesPerColor = 4 * T.sizeof;
63     }else
64     {
65         enum bytesPerColor = 3 * T.sizeof;
66     }
67 }
68 
69 T hexTo(T, R)(R hexData) @safe nothrow pure
70 if (isSomeString!R)
71 {
72     import std.math : pow;
73 
74     enum hexNumer = "0123456789";
75     enum hexWord = "ABCDEF";
76     enum hexSmallWord = "abcdef";
77 
78     T result = T.init;
79     int index = -1;
80 
81     hexDataEach: foreach_reverse (e; hexData)
82     {
83         index++;
84         immutable rindex = (cast(int) hexData.length) - index;
85         immutable ai = pow(16, index);
86 
87         foreach (el; hexNumer)
88         {
89             if (e == el)
90             {
91                 result += (e - 48) * (ai == 0 ? 1 : ai);
92                 continue hexDataEach;
93             }
94         }
95 
96         foreach (el; hexWord)
97         {
98             if (e == el)
99             {
100                 result += (e - 55) * (ai == 0 ? 1 : ai);
101                 continue hexDataEach;
102             }
103         }
104 
105         foreach (el; hexSmallWord)
106         {
107             if (e == el)
108             {
109                 result += (e - 87) * (ai == 0 ? 1 : ai);
110                 continue hexDataEach;
111             }
112         }
113     }
114 
115     return result;
116 }
117 
118 /++
119 Creates an RGB color.
120 
121 Params:
122     red = Red.
123     green = Green.
124     blue = Blue. 
125 
126 Returns: RGBA
127 +/
128 Color!ubyte rgb(ubyte red, ubyte green, ubyte blue) @safe nothrow pure
129 {
130     return Color!ubyte(red, green, blue, ubyte.max);
131 }
132 
133 Color!ubyte rgb(ubyte[] data) @safe nothrow pure
134 {
135     return Color!ubyte(data[0], data[1], data[2]);
136 }
137 
138 Color!ubyte rgba(ubyte[] data) @safe nothrow pure
139 {
140     return Color!ubyte(data);
141 }
142 
143 /++
144 Creates an RGBA color.
145 
146 Params:
147     red = Red.
148     green = Green.
149     blue = Blue. 
150     alpha = Alpha.
151 
152 Returns: RGBA
153 +/
154 Color!ubyte rgba(ubyte red, ubyte green, ubyte blue, ubyte alpha) @safe nothrow pure
155 {
156     return Color!ubyte(red, green, blue, alpha);
157 }
158 
159 /++
160 Recognizes a hex format string, converting it to RGBA representation as a 
161 `Color!ubyte` structure.
162 
163 Params:
164     hex = The same performance. The following formats can be used:
165           * `0xRRGGBBAA` / `0xRRGGBB`
166           * `#RRGGBBAA` / `#RRGGBB`
167     format = Pixel format.
168 
169 Returns: `Color!ubyte`
170 +/
171 Color!C parseColor(int format = PixelFormat.Auto, C = ubyte, T)(T hex) 
172 @safe nothrow pure
173 {
174     static if (isSomeString!T)
175     {
176         import std.conv : to;
177         import std.bigint;
178 
179         size_t cv = 0;
180         if (hex[0] == '#') 
181             cv++;
182         else 
183         if (hex[0 .. 2] == "0x") 
184             cv += 2;
185 
186         static if (format == PixelFormat.Auto) 
187         {
188             const alpha = hex[cv .. $].length > 6;
189 
190             return Color!C(
191                 hex[cv .. cv + 2].hexTo!C,
192                 hex[cv + 2 .. cv + 4].hexTo!C,
193                 hex[cv + 4 .. cv + 6].hexTo!C,
194                 alpha ? hex[cv + 6 .. cv + 8].hexTo!C : Color!C.Max
195             );
196         }else
197         static if (format == PixelFormat.RGB) 
198         {
199             return Color!C(
200                 hex[cv .. cv + 2].hexTo!C,
201                 hex[cv + 2 .. cv + 4].hexTo!C,
202                 hex[cv + 4 .. cv + 6].hexTo!C
203             );
204         }else
205         static if (format == PixelFormat.RGBA) 
206         {
207             assert(hex[cv .. $].length > 6,"This is not alpha-channel hex color!");
208 
209             return Color!C(
210                 hex[cv .. cv + 2].hexTo!C,
211                 hex[cv + 2 .. cv + 4].hexTo!C,
212                 hex[cv + 4 .. cv + 6].hexTo!C,
213                 hex[cv + 6 .. cv + 8].hexTo!C
214             );
215         }else
216         static if (format == PixelFormat.ARGB) 
217         {
218             assert(hex[cv .. $].length > 6,"This is not alpha-channel hex color!");
219 
220             return Color!C(
221                 hex[cv + 2 .. cv + 4].hexTo!C,
222                 hex[cv + 4 .. cv + 6].hexTo!C,
223                 hex[cv + 6 .. cv + 8].hexTo!C,
224                 hex[cv .. cv + 2].hexTo!C
225             ); 
226         }else
227         static if (format == PixelFormat.BGRA) 
228         {
229             assert(hex[cv .. $].length > 6,"This is not alpha-channel hex color!");
230 
231             return Color!C(
232                 hex[cv + 6 .. cv + 8].hexTo!C,
233                 hex[cv + 2 .. cv + 4].hexTo!C,
234                 hex[cv .. cv + 2].hexTo!C,
235                 hex[cv + 6 .. cv + 8].hexTO!C
236             );
237         }else
238         static if (format == PixelFormat.BGR) 
239         {
240             return Color!C(
241                 hex[cv + 6 .. cv + 8].hexTo!C,
242                 hex[cv + 2 .. cv + 4].hexTo!C,
243                 hex[cv .. cv + 2].hexTo!C);
244         }else
245             static assert(null,"Unknown pixel format");
246     }else
247     static if (isIntegral!T)
248     {
249         Color!C result;
250     
251         static if (format == PixelFormat.RGBA)
252         {
253             result.r = (hex & 0xFF000000) >> 24;
254             result.g = (hex & 0x00FF0000) >> 16;
255             result.b = (hex & 0x0000FF00) >> 8;
256             result.a = (hex & 0x000000FF);
257             
258             return result;
259         }else
260         static if (format == PixelFormat.RGB)
261         {
262             result.r = (hex & 0xFF0000) >> 16;
263             result.g = (hex & 0x00FF00) >> 8;
264             result.b = (hex & 0x0000FF);
265             result.a = 255;
266 
267             return result;
268         }else
269         static if (format == PixelFormat.ARGB)
270         {
271             result.a = (hex & 0xFF000000) >> 24;
272             result.r = (hex & 0x00FF0000) >> 16;
273             result.g = (hex & 0x0000FF00) >> 8;
274             result.b = (hex & 0x000000FF);
275 
276             return result;
277         }else
278         static if (format == PixelFormat.BGRA)
279         {
280             result.b = (hex & 0xFF000000) >> 24;
281             result.g = (hex & 0x00FF0000) >> 16;
282             result.r = (hex & 0x0000FF00) >> 8;
283             result.a = (hex & 0x000000FF);
284 
285             return result;
286         }else
287         static if (format == PixelFormat.BGR)
288         {
289             result.b = (hex & 0XFF0000) >> 16;
290             result.g = (hex & 0x00FF00) >> 8;
291             result.r = (hex & 0x0000FF);
292         }else
293         static if (format == PixelFormat.Auto) {
294             return parseColor!(PixelFormat.RGB, C, T)(hex);
295         }else
296             static assert(null, "Unknown pixel format!");
297     }else
298         static assert(null, "Unknown type hex!");
299 }
300 
301 unittest
302 {
303     assert(parseColor(0xFFFFFF) == Color!ubyte(255, 255, 255));
304     assert(parseColor("#f9004c") == Color!ubyte(249, 0, 76));
305 }
306 
307 struct Color(T)
308 if (isIntegral!T || isFloatingPoint!T)
309 {
310     alias Type = T;
311 
312     static if (isIntegral!T)
313     {
314         enum Max = T.max;
315         enum Min = 0;
316     }
317     else
318     static if (isFloatingPoint!T)
319     {
320         enum Max = 1.0f;
321         enum Min = 0.0f;
322     }
323 
324 public:
325     T red; /// Red component
326     T green; /// Green component
327     T blue; /// Blue component
328     T alpha = Max; /// Alpha component
329 
330     alias r = red;
331     alias g = green;
332     alias b = blue;
333     alias a = alpha;
334 
335 @safe nothrow pure:
336     /++
337     Color constructor for four components, the latter which is optional.
338     +/
339     this(T red, T green, T blue, T alpha = Max)
340     {
341         this.red = red;
342         this.green = green;
343         this.blue = blue;
344         this.alpha = alpha;
345     }
346 
347     /++
348     Parses a color from the input.
349     +/
350     this(R)(R value)
351     if (isIntegral!R || isSomeString!R || isArray!R)
352     {
353         static if (isArray!R && !isSomeString!R)
354         {
355             this.red = cast(T) value[0];
356             this.green = cast(T) value[1];
357             this.blue = cast(T) value[2];
358             this.alpha = cast(T) (value.length > 3 ? value[3] : Max);
359         }
360         else
361             this = parseColor!(PixelFormat.Auto, T, R)(value);
362     }
363 
364     void opAssign(R)(R value)
365     if (isIntegral!R || isSomeString!R || isArray!R)
366     {
367         static if (isArray!R && !isSomeString!R)
368         {
369             this.red = value[0];
370             this.green = value[1];
371             this.blue = value[2];
372             this.alpha = value.length > 3 ? value[3] : Max;
373         }
374         else
375             this = parseColor!(PixelFormat.Auto, T, R)(value);
376     }
377 
378     R to(R, int format)() inout
379     {
380         static if (isIntegral!R && !isFloatingPoint!T)
381         {
382             static if(format == PixelFormat.RGBA)
383                 return cast(R) (((r & 0xff) << 24) + ((g & 0xff) << 16) + ((b & 0xff) << 8) + (a & 0xff));
384             else
385             static if(format == PixelFormat.RGB)
386                 return cast(R) ((r & 0xff) << 16) + ((g & 0xff) << 8) + ((b & 0xff));
387             else
388             static if(format == PixelFormat.ARGB)
389                 return cast(R) (((a & 0xff) << 24) + ((r & 0xff) << 16) + ((g & 0xff) << 8) + (b & 0xff));
390             else
391             static if(format == PixelFormat.BGRA)
392                 return cast(R) (((b & 0xff) << 24) + ((g & 0xff) << 16) + ((r & 0xff) << 8) + (a & 0xff));
393             else
394             static if(format == PixelFormat.BGR)
395                 return cast(R) (((b & 0xff) << 16) + ((g & 0xff) << 8) + ((r & 0xff)));
396             else
397                 return 0;
398         }else
399         static if (isSomeString!R)
400         {
401             static if (isFloatingPoint!T)
402                 return "";
403             else
404             {
405                 return cast(R) toBytes!format.toHexString;
406             }
407         }
408     }
409 
410     /++
411     Returns an array of components.
412 
413     Params:
414         format = Pixel format.
415     +/
416     T[] toBytes(int format)() inout
417     {
418         static assert(isValidFormat!format, CannotDetectAuto);
419 
420         static if (format == PixelFormat.RGBA)
421             return [r,g,b,a];
422         else
423         static if (format == PixelFormat.RGB)
424             return [r,g,b];
425         else
426         static if (format == PixelFormat.ARGB)
427             return [a,r,g,b];
428         else
429         static if (format == PixelFormat.BGRA)
430             return [b,g,r,a];
431         else
432         static if (format == PixelFormat.BGR)
433             return [b,g,r];
434     }
435 
436     Color!T opBinary(string op)(T koe) inout
437     {
438         import std.conv : to;
439 
440         static if (op == "+")
441             return Color!T( cast(T) (r + koe),
442                             cast(T) (g + koe),
443                             cast(T) (b + koe),
444                             cast(T) (a + koe));
445         else
446         static if (op == "-")
447             return Color!T( cast(T) (r - koe),
448                             cast(T) (g - koe),
449                             cast(T) (b - koe),
450                             cast(T) (a - koe));
451         else
452         static if (op == "*")
453             return Color!T( cast(T) (r * koe),
454                             cast(T) (g * koe),
455                             cast(T) (b * koe),
456                             cast(T) (a * koe));
457         else
458         static if (op == "/")
459             return Color!T( cast(T) (r / koe),
460                             cast(T) (g / koe),
461                             cast(T) (b / koe),
462                             cast(T) (a / koe));
463         else
464             static assert(0, "Operator `" ~ op ~ "` not implemented.");
465     }
466 
467     Color!T opBinary(string op)(float koe) inout
468     {
469         import std.conv : to;
470 
471         static if (op == "+")
472             return Color!T( cast(T) (r + koe),
473                             cast(T) (g + koe),
474                             cast(T) (b + koe),
475                             cast(T) (a + koe));
476         else
477         static if (op == "-")
478             return Color!T( cast(T) (r - koe),
479                             cast(T) (g - koe),
480                             cast(T) (b - koe),
481                             cast(T) (a - koe));
482         else
483         static if (op == "*")
484             return Color!T( cast(T) (r * koe),
485                             cast(T) (g * koe),
486                             cast(T) (b * koe),
487                             cast(T) (a * koe));
488         else
489         static if (op == "/")
490             return Color!T( cast(T) (r / koe),
491                             cast(T) (g / koe),
492                             cast(T) (b / koe),
493                             cast(T) (a / koe));
494         else
495             static assert(0, "Operator `" ~ op ~ "` not implemented.");
496     }
497 
498     Color!T opBinary(string op)(Color!T color) inout
499     {
500         static if (op == "+") {
501             return Color!T( cast(T) (r + color.r),
502                             cast(T) (g + color.g),
503                             cast(T) (b + color.b),
504                             cast(T) (a + color.a));
505         }
506         else
507         static if (op == "-")
508             return Color!T( cast(T) (r - color.r),
509                             cast(T) (g - color.g),
510                             cast(T) (b - color.b),
511                             cast(T) (a - color.a));
512         else
513         static if (op == "*")
514             return Color!T( cast(T) r * color.r,
515                                     g * color.g,
516                                     b * color.b,
517                                     a * color.a);
518         else
519         static if (op == "/")
520             return Color!T( cast(T) r / color.r,
521                                     g / color.g,
522                                     b / color.b,
523                                     a / color.a);
524         else
525             static assert(0, "Operator `" ~ op ~ "` not implemented.");
526     }
527 
528     /// Converts the color to black and white.
529     float toGrayscaleFloat() inout
530     {
531         return (rf * 0.299 + gf * 0.587 + bf * 0.144);
532     }
533 
534     /// Whether the color is dark.
535     bool isDark()
536     {
537         return (toGrayscaleFloat < 0.5f);
538     }
539 
540     /// Whether the color is light.
541     bool isLight()
542     {
543         return (toGrayscaleFloat > 0.5f);
544     }
545 
546     /// Converts the color to black and white.
547     T toGrayscaleNumber() inout
548     {
549         return cast(T) (Max * toGrayscaleFloat());
550     }
551 
552     /// Converts the color to black and white.
553     Color!T toGrayscale() inout
554     {
555         auto graycolor = toGrayscaleNumber();
556 
557         return Color!T(graycolor, graycolor, graycolor, Max);
558     }
559 
560     /// Will return the color opposite to itself.
561     @property Color!T inverted() inout
562     { 
563         return Color!T(Max - r, Max - g, Max - b, a);
564     }
565 
566     /// Invert alpha value
567     @property T invertAlpha() inout
568     {
569         return Max - alpha;
570     }
571 
572     /// Red value in the form of a range from 0 to 1.
573     @property float rf()  inout
574     {
575         return cast(float) r / cast(float) Max;
576     }
577 
578     /// ditto
579     @property void rf(float value)
580     {
581         this.r = cast(T) (Max * value);
582     }
583 
584     /// Green value in the form of a range from 0 to 1.
585     @property float gf() inout
586     {
587         return cast(float) g / cast(float) Max;
588     }
589 
590     /// ditto
591     @property void gf(float value)
592     {
593         this.g = cast(T) (Max * value);
594     }
595 
596     /// Alpha value in the form of a range from 0 to 1.
597     @property float bf() inout
598     {
599         return cast(float) b / cast(float) Max;
600     }
601 
602     /// ditto
603     @property void bf(float value)
604     {
605         this.b = cast(T) (Max * value);
606     }
607 
608     /// Returns a alpha value in the form of a range from 0 to 1.
609     @property float af() inout
610     {
611         return cast(float) a / cast(float) Max;
612     }
613 
614     /// ditto
615     @property void af(float value)
616     {
617         this.a = cast(T) (Max * value);
618     }
619 }
620 
621 unittest
622 {
623     Color!ubyte color = "#f9004c";
624     assert(color == Color!ubyte(249, 0, 76));
625 
626     color = 0xf9004c;
627     assert(color == Color!ubyte(249, 0, 76));
628 
629 }
630 
631 /++
632 Will change the byte sequence to color.
633 
634 Params:
635     format = Pixel format.
636     bytes = byte sequence.
637 +/
638 Color!ubyte fromColor(int format)(ubyte[] bytes) @safe nothrow pure
639 {
640     bytes = bytes.fromFormat!(format,PixelFormat.RGBA);
641 
642     return Color!ubyte(bytes[0],bytes[1],bytes[2],bytes[3]);
643 }
644 
645 /++
646 Will change the sequence of bytes into a collection of colors.
647 
648 Params:
649     format = Pixel format.
650     bytes = byte sequence.
651 +/
652 Color!ubyte[] fromColors(int format)(ubyte[] bytes) @safe nothrow pure
653 {
654     Color!ubyte[] result;
655 
656     for(size_t i = 0; i < bytes.length; i += bytesPerColor!format)
657     {
658         result ~= bytes[i .. i + bytesPerColor!format].fromColor!(format);
659     }
660 
661     return result;
662 }
663 
664 /++
665 Checks if the structure is a color.
666 +/
667 template isColor(T)
668 {
669     enum isColor =  __traits(hasMember, T, "r") &&
670                     __traits(hasMember, T, "g") &&
671                     __traits(hasMember, T, "b") &&
672                     __traits(hasMember, T, "a");
673 }
674 
675 /++
676 Converts one sample color to another.
677 
678 Params:
679     From = From color.
680     To = To color.
681     color = color structre.
682 
683 Returns:
684     Converted color.
685 +/
686 Color!To convert(From, To)(Color!From color) @safe nothrow pure
687 {
688     static if (isFloatingPoint!From) 
689     {
690         static if (isFloatingPoint!To)
691         {
692             return Color!To(
693                 cast(To) color.red,
694                 cast(To) color.green,
695                 cast(To) color.blue,
696                 cast(To) color.alpha
697             );
698         }else
699         static if (isIntegral!To)
700         {
701             return Color!To(
702                 cast(To) (color.red * Color!To.Max),
703                 cast(To) (color.green * Color!To.Max),
704                 cast(To) (color.blue * Color!To.Max),
705                 cast(To) (color.alpha * Color!To.Max)
706             );
707         }
708     }else
709     static if (isIntegral!From)
710     {
711         static if (isFloatingPoint!To)
712         {
713             return Color!To(
714                 cast(To) (color.rf),
715                 cast(To) (color.gf),
716                 cast(To) (color.bf),
717                 cast(To) (color.af)
718             );
719         }else
720         static if (isIntegral!From)
721         {
722             return Color!To(
723                 cast(To) color.red,
724                 cast(To) color.green,
725                 cast(To) color.blue,
726                 cast(To) color.alpha
727             );
728         }
729     }
730 }
731 
732 /++
733 Checks if the byte array is valid for the color description.
734 +/
735 bool validateBytes(int format)(inout(ubyte[]) pixels) @safe nothrow pure
736 {
737     return (pixels.length % bytesPerColor!format) == 0;
738 }
739 
740 /// Mixing factor of two colors.
741 enum BlendFactor
742 {
743     Zero,
744     One,
745     SrcColor,
746     OneMinusSrcColor,
747     DstColor,
748     OneMinusDstColor,
749     SrcAlpha,
750     OneMinusSrcAlpha,
751     DstAlpha,
752     OneMinusDstAlpha,
753     ConstantColor,
754     OneMinusConstantColor,
755     ConstantAlpha,
756     OneMinusConstanceAlpha
757 }
758 
759 /++
760 Color mixing implementations.
761 
762 Params:
763     fac1 = First factor mixing.
764     fac2 = Second factor mixing.
765     T = Two colors type.
766     orig = Original color mixing.
767     color = Second color mixing.
768 +/
769 Color!T BlendImpl(int fac1, int fac2, T)(Color!T orig, Color!T color) @safe nothrow pure
770 {
771     Color!float origf = convert!(T, float)(orig);
772     Color!float colorf = convert!(T, float)(color);
773 
774     Color!float srcf, drtf;
775 
776     // Factory 1
777     static if(fac1 == BlendFactor.Zero)
778         srcf = Color!float(0.0f, 0.0f, 0.0f, 0.0f);
779     else
780     static if(fac1 == BlendFactor.One)
781         srcf = Color!float(1.0f, 1.0f, 1.0f, 1.0f);
782     else
783     static if(fac1 == BlendFactor.SrcColor)
784         srcf = Color!float(origf.r, origf.g, origf.b, 1.0f);
785     else
786     static if(fac1 == BlendFactor.OneMinusSrcAlpha)
787         srcf = Color!float(1.0f - origf.r, 1.0f - origf.g, 1.0f - origf.b, 1.0f);
788     else
789     static if(fac1 == BlendFactor.DstColor)
790         srcf = Color!float(colorf.r, colorf.g, colorf.b, 1.0f);
791     else
792     static if(fac1 == BlendFactor.OneMinusDstColor)
793         srcf = Color!float(1.0f - colorf.r, 1.0f - colorf.g, 1.0f - colorf.b, 1.0f);
794     else
795     static if(fac1 == BlendFactor.SrcAlpha)
796         srcf = Color!float(origf.a, origf.a, origf.a, origf.a);
797     else
798     static if(fac1 == BlendFactor.OneMinusSrcAlpha)
799         srcf = Color!float(1.0f - origf.a, 1.0f - origf.a, 1.0f - origf.a, 1.0f - origf.a);
800     else
801     static if(fac1 == BlendFactor.DstAlpha)
802         srcf = Color!float(colorf.a, colorf.a, colorf.a, colorf.a);
803     else
804     static if(fac1 == BlendFactor.OneMinusDstAlpha)
805         srcf = Color!float(1.0f - colorf.a, 1.0f - colorf.a, 1.0f - colorf.a, 1.0f - colorf.a);
806 
807     // Factory 2
808     static if(fac2 == BlendFactor.Zero)
809         drtf = Color!float(0.0f, 0.0f, 0.0f, 0.0f);
810     else
811     static if(fac2 == BlendFactor.One)
812         drtf = Color!float(1.0f, 1.0f, 1.0f, 1.0f);
813     else
814     static if(fac2 == BlendFactor.SrcColor)
815         drtf = Color!float(origf.r, origf.g, origf.b, 1.0f);
816     else
817     static if(fac2 == BlendFactor.OneMinusSrcAlpha)
818         drtf = Color!float(1.0f - origf.r, 1.0f - origf.g, 1.0f - origf.b, 1.0f);
819     else
820     static if(fac2 == BlendFactor.DstColor)
821         drtf = Color!float(colorf.r, colorf.g, colorf.b, 1.0f);
822     else
823     static if(fac2 == BlendFactor.OneMinusDstColor)
824         drtf = Color!float(1.0f - colorf.r, 1.0f - colorf.g, 1.0f - colorf.b, 1.0f);
825     else
826     static if(fac2 == BlendFactor.SrcAlpha)
827         drtf = Color!float(origf.a, origf.a, origf.a, origf.a);
828     else
829     static if(fac2 == BlendFactor.OneMinusSrcAlpha)
830         drtf = Color!float(1.0f - origf.a, 1.0f - origf.a, 1.0f - origf.a, 1.0f - origf.a);
831     else
832     static if(fac2 == BlendFactor.DstAlpha)
833         drtf = Color!float(colorf.a, colorf.a, colorf.a, colorf.a);
834     else
835     static if(fac2 == BlendFactor.OneMinusDstAlpha)
836         drtf = Color!float(1.0f - colorf.a, 1.0f - colorf.a, 1.0f - colorf.a, 1.0f - colorf.a);
837 
838     Color!float trace = (origf * srcf) + (colorf * drtf);
839     return convert!(float, T)(trace);
840 }
841 
842 alias mix(T) = BlendMultiply!T;
843 alias BlendAlpha(T) = BlendImpl!(BlendFactor.SrcAlpha, BlendFactor.OneMinusSrcAlpha, T);
844 alias BlendAdd(T) = BlendImpl!(BlendFactor.One, BlendFactor.One, T);
845 alias BlendMultiply(T) = BlendImpl!(BlendFactor.DstColor, BlendFactor.Zero, T);
846 alias BlendSrc2DST(T) = BlendImpl!(BlendFactor.SrcColor, BlendFactor.One, T);
847 alias BlendAddMul(T) = BlendImpl!(BlendFactor.OneMinusDstColor, BlendFactor.One, T);
848 alias BlendAddAlpha(T) = BlendImpl!(BlendFactor.SrcAlpha, BlendFactor.One, T);
849 
850 alias FuncBlend(T) = Color!T function(Color!T,Color!T) @safe nothrow pure;
851 
852 /++
853 Returns a reference to a function that implements mixing two colors 
854 by mixing factors.
855 +/
856 FuncBlend!T BlendFunc(T)(int fac1, int fac2) @trusted nothrow pure
857 {
858     if (fac1 == BlendFactor.Zero)
859     {
860         if (fac2 == BlendFactor.Zero)
861             return &BlendImpl!(BlendFactor.Zero, BlendFactor.Zero, T);
862         else
863         if (fac2 == BlendFactor.One)
864             return &BlendImpl!(BlendFactor.Zero, BlendFactor.One, T);
865         else
866         if (fac2 == BlendFactor.SrcColor)
867             return &BlendImpl!(BlendFactor.Zero, BlendFactor.SrcColor, T);
868         else
869         if (fac2 == BlendFactor.OneMinusSrcColor)
870             return &BlendImpl!(BlendFactor.Zero, BlendFactor.OneMinusSrcColor, T);
871         else
872         if (fac2 == BlendFactor.DstColor)
873             return &BlendImpl!(BlendFactor.Zero, BlendFactor.DstColor, T);
874         else
875         if (fac2 == BlendFactor.OneMinusDstColor)
876             return &BlendImpl!(BlendFactor.Zero, BlendFactor.OneMinusDstColor, T);
877         else
878         if (fac2 == BlendFactor.SrcAlpha)
879             return &BlendImpl!(BlendFactor.Zero, BlendFactor.SrcAlpha, T);
880         else
881         if (fac2 == BlendFactor.OneMinusSrcAlpha)
882             return &BlendImpl!(BlendFactor.Zero, BlendFactor.OneMinusSrcAlpha, T);
883         else
884         if (fac2 == BlendFactor.DstAlpha)
885             return &BlendImpl!(BlendFactor.Zero, BlendFactor.DstAlpha, T);
886         else
887         if (fac2 == BlendFactor.OneMinusDstAlpha)
888             return &BlendImpl!(BlendFactor.Zero, BlendFactor.OneMinusDstAlpha, T);
889     }else
890     if (fac1 == BlendFactor.One)
891     {
892         if (fac2 == BlendFactor.Zero)
893             return &BlendImpl!(BlendFactor.One, BlendFactor.Zero, T);
894         else
895         if (fac2 == BlendFactor.One)
896             return &BlendImpl!(BlendFactor.One, BlendFactor.One, T);
897         else
898         if (fac2 == BlendFactor.SrcColor)
899             return &BlendImpl!(BlendFactor.One, BlendFactor.SrcColor, T);
900         else
901         if (fac2 == BlendFactor.OneMinusSrcColor)
902             return &BlendImpl!(BlendFactor.One, BlendFactor.OneMinusSrcColor, T);
903         else
904         if (fac2 == BlendFactor.DstColor)
905             return &BlendImpl!(BlendFactor.One, BlendFactor.DstColor, T);
906         else
907         if (fac2 == BlendFactor.OneMinusDstColor)
908             return &BlendImpl!(BlendFactor.One, BlendFactor.OneMinusDstColor, T);
909         else
910         if (fac2 == BlendFactor.SrcAlpha)
911             return &BlendImpl!(BlendFactor.One, BlendFactor.SrcAlpha, T);
912         else
913         if (fac2 == BlendFactor.OneMinusSrcAlpha)
914             return &BlendImpl!(BlendFactor.One, BlendFactor.OneMinusSrcAlpha, T);
915         else
916         if (fac2 == BlendFactor.DstAlpha)
917             return &BlendImpl!(BlendFactor.One, BlendFactor.DstAlpha, T);
918         else
919         if (fac2 == BlendFactor.OneMinusDstAlpha)
920             return &BlendImpl!(BlendFactor.One, BlendFactor.OneMinusDstAlpha, T);
921     }else
922     if (fac1 == BlendFactor.SrcColor)
923     {
924         if (fac2 == BlendFactor.Zero)
925             return &BlendImpl!(BlendFactor.SrcColor, BlendFactor.Zero, T);
926         else
927         if (fac2 == BlendFactor.One)
928             return &BlendImpl!(BlendFactor.SrcColor, BlendFactor.One, T);
929         else
930         if (fac2 == BlendFactor.SrcColor)
931             return &BlendImpl!(BlendFactor.SrcColor, BlendFactor.SrcColor, T);
932         else
933         if (fac2 == BlendFactor.OneMinusSrcColor)
934             return &BlendImpl!(BlendFactor.SrcColor, BlendFactor.OneMinusSrcColor, T);
935         else
936         if (fac2 == BlendFactor.DstColor)
937             return &BlendImpl!(BlendFactor.SrcColor, BlendFactor.DstColor, T);
938         else
939         if (fac2 == BlendFactor.OneMinusDstColor)
940             return &BlendImpl!(BlendFactor.SrcColor, BlendFactor.OneMinusDstColor, T);
941         else
942         if (fac2 == BlendFactor.SrcAlpha)
943             return &BlendImpl!(BlendFactor.SrcColor, BlendFactor.SrcAlpha, T);
944         else
945         if (fac2 == BlendFactor.OneMinusSrcAlpha)
946             return &BlendImpl!(BlendFactor.SrcColor, BlendFactor.OneMinusSrcAlpha, T);
947         else
948         if (fac2 == BlendFactor.DstAlpha)
949             return &BlendImpl!(BlendFactor.SrcColor, BlendFactor.DstAlpha, T);
950         else
951         if (fac2 == BlendFactor.OneMinusDstAlpha)
952             return &BlendImpl!(BlendFactor.SrcColor, BlendFactor.OneMinusDstAlpha, T);
953     }else
954     if (fac1 == BlendFactor.OneMinusSrcColor)
955     {
956         if (fac2 == BlendFactor.Zero)
957             return &BlendImpl!(BlendFactor.OneMinusSrcColor, BlendFactor.Zero, T);
958         else
959         if (fac2 == BlendFactor.One)
960             return &BlendImpl!(BlendFactor.OneMinusSrcColor, BlendFactor.One, T);
961         else
962         if (fac2 == BlendFactor.SrcColor)
963             return &BlendImpl!(BlendFactor.OneMinusSrcColor, BlendFactor.SrcColor, T);
964         else
965         if (fac2 == BlendFactor.OneMinusSrcColor)
966             return &BlendImpl!(BlendFactor.OneMinusSrcColor, BlendFactor.OneMinusSrcColor, T);
967         else
968         if (fac2 == BlendFactor.DstColor)
969             return &BlendImpl!(BlendFactor.OneMinusSrcColor, BlendFactor.DstColor, T);
970         else
971         if (fac2 == BlendFactor.OneMinusDstColor)
972             return &BlendImpl!(BlendFactor.OneMinusSrcColor, BlendFactor.OneMinusDstColor, T);
973         else
974         if (fac2 == BlendFactor.SrcAlpha)
975             return &BlendImpl!(BlendFactor.OneMinusSrcColor, BlendFactor.SrcAlpha, T);
976         else
977         if (fac2 == BlendFactor.OneMinusSrcAlpha)
978             return &BlendImpl!(BlendFactor.OneMinusSrcColor, BlendFactor.OneMinusSrcAlpha, T);
979         else
980         if (fac2 == BlendFactor.DstAlpha)
981             return &BlendImpl!(BlendFactor.OneMinusSrcColor, BlendFactor.DstAlpha, T);
982         else
983         if (fac2 == BlendFactor.OneMinusDstAlpha)
984             return &BlendImpl!(BlendFactor.OneMinusSrcColor, BlendFactor.OneMinusDstAlpha, T);
985     }else
986     if(fac1 == BlendFactor.DstColor)
987     {
988         if (fac2 == BlendFactor.Zero)
989             return &BlendImpl!(BlendFactor.DstColor, BlendFactor.Zero, T);
990         else
991         if (fac2 == BlendFactor.One)
992             return &BlendImpl!(BlendFactor.DstColor, BlendFactor.One, T);
993         else
994         if (fac2 == BlendFactor.SrcColor)
995             return &BlendImpl!(BlendFactor.DstColor, BlendFactor.SrcColor, T);
996         else
997         if (fac2 == BlendFactor.OneMinusSrcColor)
998             return &BlendImpl!(BlendFactor.DstColor, BlendFactor.OneMinusSrcColor, T);
999         else
1000         if (fac2 == BlendFactor.DstColor)
1001             return &BlendImpl!(BlendFactor.DstColor, BlendFactor.DstColor, T);
1002         else
1003         if (fac2 == BlendFactor.OneMinusDstColor)
1004             return &BlendImpl!(BlendFactor.DstColor, BlendFactor.OneMinusDstColor, T);
1005         else
1006         if (fac2 == BlendFactor.SrcAlpha)
1007             return &BlendImpl!(BlendFactor.DstColor, BlendFactor.SrcAlpha, T);
1008         else
1009         if (fac2 == BlendFactor.OneMinusSrcAlpha)
1010             return &BlendImpl!(BlendFactor.DstColor, BlendFactor.OneMinusSrcAlpha, T);
1011         else
1012         if (fac2 == BlendFactor.DstAlpha)
1013             return &BlendImpl!(BlendFactor.DstColor, BlendFactor.DstAlpha, T);
1014         else
1015         if (fac2 == BlendFactor.OneMinusDstAlpha)
1016             return &BlendImpl!(BlendFactor.DstColor, BlendFactor.OneMinusDstAlpha, T);
1017     }else
1018     if (fac1 == BlendFactor.OneMinusDstColor)
1019     {
1020         if (fac2 == BlendFactor.Zero)
1021             return &BlendImpl!(BlendFactor.OneMinusDstColor, BlendFactor.Zero, T);
1022         else
1023         if (fac2 == BlendFactor.One)
1024             return &BlendImpl!(BlendFactor.OneMinusDstColor, BlendFactor.One, T);
1025         else
1026         if (fac2 == BlendFactor.SrcColor)
1027             return &BlendImpl!(BlendFactor.OneMinusDstColor, BlendFactor.SrcColor, T);
1028         else
1029         if (fac2 == BlendFactor.OneMinusSrcColor)
1030             return &BlendImpl!(BlendFactor.OneMinusDstColor, BlendFactor.OneMinusSrcColor, T);
1031         else
1032         if (fac2 == BlendFactor.DstColor)
1033             return &BlendImpl!(BlendFactor.OneMinusDstColor, BlendFactor.DstColor, T);
1034         else
1035         if (fac2 == BlendFactor.OneMinusDstColor)
1036             return &BlendImpl!(BlendFactor.OneMinusDstColor, BlendFactor.OneMinusDstColor, T);
1037         else
1038         if (fac2 == BlendFactor.SrcAlpha)
1039             return &BlendImpl!(BlendFactor.OneMinusDstColor, BlendFactor.SrcAlpha, T);
1040         else
1041         if (fac2 == BlendFactor.OneMinusSrcAlpha)
1042             return &BlendImpl!(BlendFactor.OneMinusDstColor, BlendFactor.OneMinusSrcAlpha, T);
1043         else
1044         if (fac2 == BlendFactor.DstAlpha)
1045             return &BlendImpl!(BlendFactor.OneMinusDstColor, BlendFactor.DstAlpha, T);
1046         else
1047         if (fac2 == BlendFactor.OneMinusDstAlpha)
1048             return &BlendImpl!(BlendFactor.OneMinusDstColor, BlendFactor.OneMinusDstAlpha, T);
1049     }else
1050     if (fac1 == BlendFactor.SrcAlpha)
1051     {
1052         if (fac2 == BlendFactor.Zero)
1053             return &BlendImpl!(BlendFactor.SrcAlpha, BlendFactor.Zero, T);
1054         else
1055         if (fac2 == BlendFactor.One)
1056             return &BlendImpl!(BlendFactor.SrcAlpha, BlendFactor.One, T);
1057         else
1058         if (fac2 == BlendFactor.SrcColor)
1059             return &BlendImpl!(BlendFactor.SrcAlpha, BlendFactor.SrcColor, T);
1060         else
1061         if (fac2 == BlendFactor.OneMinusSrcColor)
1062             return &BlendImpl!(BlendFactor.SrcAlpha, BlendFactor.OneMinusSrcColor, T);
1063         else
1064         if (fac2 == BlendFactor.DstColor)
1065             return &BlendImpl!(BlendFactor.SrcAlpha, BlendFactor.DstColor, T);
1066         else
1067         if (fac2 == BlendFactor.OneMinusDstColor)
1068             return &BlendImpl!(BlendFactor.SrcAlpha, BlendFactor.OneMinusDstColor, T);
1069         else
1070         if (fac2 == BlendFactor.SrcAlpha)
1071             return &BlendImpl!(BlendFactor.SrcAlpha, BlendFactor.SrcAlpha, T);
1072         else
1073         if (fac2 == BlendFactor.OneMinusSrcAlpha)
1074             return &BlendImpl!(BlendFactor.SrcAlpha, BlendFactor.OneMinusSrcAlpha, T);
1075         else
1076         if (fac2 == BlendFactor.DstAlpha)
1077             return &BlendImpl!(BlendFactor.SrcAlpha, BlendFactor.DstAlpha, T);
1078         else
1079         if (fac2 == BlendFactor.OneMinusDstAlpha)
1080             return &BlendImpl!(BlendFactor.SrcAlpha, BlendFactor.OneMinusDstAlpha, T);
1081     }else
1082     if (fac1 == BlendFactor.OneMinusSrcAlpha)
1083     {
1084         if (fac2 == BlendFactor.Zero)
1085             return &BlendImpl!(BlendFactor.OneMinusSrcAlpha, BlendFactor.Zero, T);
1086         else
1087         if (fac2 == BlendFactor.One)
1088             return &BlendImpl!(BlendFactor.OneMinusSrcAlpha, BlendFactor.One, T);
1089         else
1090         if (fac2 == BlendFactor.SrcColor)
1091             return &BlendImpl!(BlendFactor.OneMinusSrcAlpha, BlendFactor.SrcColor, T);
1092         else
1093         if (fac2 == BlendFactor.OneMinusSrcColor)
1094             return &BlendImpl!(BlendFactor.OneMinusSrcAlpha, BlendFactor.OneMinusSrcColor, T);
1095         else
1096         if (fac2 == BlendFactor.DstColor)
1097             return &BlendImpl!(BlendFactor.OneMinusSrcAlpha, BlendFactor.DstColor, T);
1098         else
1099         if (fac2 == BlendFactor.OneMinusDstColor)
1100             return &BlendImpl!(BlendFactor.OneMinusSrcAlpha, BlendFactor.OneMinusDstColor, T);
1101         else
1102         if (fac2 == BlendFactor.SrcAlpha)
1103             return &BlendImpl!(BlendFactor.OneMinusSrcAlpha, BlendFactor.SrcAlpha, T);
1104         else
1105         if (fac2 == BlendFactor.OneMinusSrcAlpha)
1106             return &BlendImpl!(BlendFactor.OneMinusSrcAlpha, BlendFactor.OneMinusSrcAlpha, T);
1107         else
1108         if (fac2 == BlendFactor.DstAlpha)
1109             return &BlendImpl!(BlendFactor.OneMinusSrcAlpha, BlendFactor.DstAlpha, T);
1110         else
1111         if (fac2 == BlendFactor.OneMinusDstAlpha)
1112             return &BlendImpl!(BlendFactor.OneMinusSrcAlpha, BlendFactor.OneMinusDstAlpha, T);
1113     }else
1114     if (fac1 == BlendFactor.DstAlpha)
1115     {
1116         if (fac2 == BlendFactor.Zero)
1117             return &BlendImpl!(BlendFactor.DstAlpha, BlendFactor.Zero, T);
1118         else
1119         if (fac2 == BlendFactor.One)
1120             return &BlendImpl!(BlendFactor.DstAlpha, BlendFactor.One, T);
1121         else
1122         if (fac2 == BlendFactor.SrcColor)
1123             return &BlendImpl!(BlendFactor.DstAlpha, BlendFactor.SrcColor, T);
1124         else
1125         if (fac2 == BlendFactor.OneMinusSrcColor)
1126             return &BlendImpl!(BlendFactor.DstAlpha, BlendFactor.OneMinusSrcColor, T);
1127         else
1128         if (fac2 == BlendFactor.DstColor)
1129             return &BlendImpl!(BlendFactor.DstAlpha, BlendFactor.DstColor, T);
1130         else
1131         if (fac2 == BlendFactor.OneMinusDstColor)
1132             return &BlendImpl!(BlendFactor.DstAlpha, BlendFactor.OneMinusDstColor, T);
1133         else
1134         if (fac2 == BlendFactor.SrcAlpha)
1135             return &BlendImpl!(BlendFactor.DstAlpha, BlendFactor.SrcAlpha, T);
1136         else
1137         if (fac2 == BlendFactor.OneMinusSrcAlpha)
1138             return &BlendImpl!(BlendFactor.DstAlpha, BlendFactor.OneMinusSrcAlpha, T);
1139         else
1140         if (fac2 == BlendFactor.DstAlpha)
1141             return &BlendImpl!(BlendFactor.DstAlpha, BlendFactor.DstAlpha, T);
1142         else
1143         if (fac2 == BlendFactor.OneMinusDstAlpha)
1144             return &BlendImpl!(BlendFactor.DstAlpha, BlendFactor.OneMinusDstAlpha, T);
1145     }else
1146     if (fac1 == BlendFactor.OneMinusDstAlpha)
1147     {
1148         if (fac2 == BlendFactor.Zero)
1149             return &BlendImpl!(BlendFactor.OneMinusDstAlpha, BlendFactor.Zero, T);
1150         else
1151         if (fac2 == BlendFactor.One)
1152             return &BlendImpl!(BlendFactor.OneMinusDstAlpha, BlendFactor.One, T);
1153         else
1154         if (fac2 == BlendFactor.SrcColor)
1155             return &BlendImpl!(BlendFactor.OneMinusDstAlpha, BlendFactor.SrcColor, T);
1156         else
1157         if (fac2 == BlendFactor.OneMinusSrcColor)
1158             return &BlendImpl!(BlendFactor.OneMinusDstAlpha, BlendFactor.OneMinusSrcColor, T);
1159         else
1160         if (fac2 == BlendFactor.DstColor)
1161             return &BlendImpl!(BlendFactor.OneMinusDstAlpha, BlendFactor.DstColor, T);
1162         else
1163         if (fac2 == BlendFactor.OneMinusDstColor)
1164             return &BlendImpl!(BlendFactor.OneMinusDstAlpha, BlendFactor.OneMinusDstColor, T);
1165         else
1166         if (fac2 == BlendFactor.SrcAlpha)
1167             return &BlendImpl!(BlendFactor.OneMinusDstAlpha, BlendFactor.SrcAlpha, T);
1168         else
1169         if (fac2 == BlendFactor.OneMinusSrcAlpha)
1170             return &BlendImpl!(BlendFactor.OneMinusDstAlpha, BlendFactor.OneMinusSrcAlpha, T);
1171         else
1172         if (fac2 == BlendFactor.DstAlpha)
1173             return &BlendImpl!(BlendFactor.OneMinusDstAlpha, BlendFactor.DstAlpha, T);
1174         else
1175         if (fac2 == BlendFactor.OneMinusDstAlpha)
1176             return &BlendImpl!(BlendFactor.OneMinusDstAlpha, BlendFactor.OneMinusDstAlpha, T);
1177     }
1178 
1179     assert(null, "Unknown blend factor's!");
1180 }
1181 
1182 /++
1183 Converts the format of a sequence of color bytes.
1184 
1185 Params:
1186     format1 = What is the original format.
1187     format2 = What format should be converted.
1188     pixels = Sequence of color bytes.
1189 
1190 Returns:
1191     An array of bytes in the order specified in the pattern according to 
1192     the pixel format.
1193 +/
1194 ubyte[] fromFormat(int format1, int format2)(ubyte[] pixels) @safe nothrow pure
1195 in
1196 {
1197     assert(validateBytes!format1(pixels),"The input pixels data is incorrect!");
1198 }
1199 out(r; validateBytes!format2(r), "The out pixels data is incorrect!")
1200 do
1201 {
1202     static assert(isValidFormat!format1, CannotDetectAuto);
1203     static assert(isValidFormat!format2, CannotDetectAuto);
1204 
1205     static if (format1 == format2) return pixels;
1206 
1207     static if (format1 == PixelFormat.RGB)
1208     {
1209         static if (format2 == PixelFormat.RGBA)
1210         {
1211             ubyte[] result;
1212 
1213             for (size_t i = 3; i <= pixels.length; i += 3)
1214             {
1215                 result ~= pixels[0 .. i] ~ 255;
1216             }
1217 
1218             return result;
1219         }else
1220         static if (format2 == PixelFormat.ARGB)
1221         {
1222             ubyte[] result;
1223 
1224             for (size_t i = 0; i < pixels.length; i += 3)
1225             {
1226                 result ~= pixels[0 .. i] ~ 255;
1227             }
1228 
1229             return result;
1230         }else
1231         static if (format2 == PixelFormat.BGRA)
1232         {
1233             import std.algorithm : reverse;
1234 
1235             size_t count = 0;
1236             ubyte[] result;
1237 
1238             for (size_t i = 3; i <= pixels.length; i += 3)
1239             {
1240                 result ~= pixels[i - 3 .. i].reverse ~ 255;
1241             }
1242 
1243             return result;
1244         }
1245     }else
1246     static if (format1 == PixelFormat.RGBA)
1247     {
1248         static if (format2 == PixelFormat.RGB)
1249         {
1250             ubyte[] result;
1251 
1252             for (size_t i = 0; i < pixels.length; i += 4)
1253             {
1254                 result ~= pixels[i .. i + 3];
1255             }
1256 
1257             return result;
1258         }else
1259         static if (format2 == PixelFormat.ARGB)
1260         {
1261             ubyte[] result;
1262 
1263             for (size_t i = 0; i < pixels.length; i += 4)
1264             {
1265                 result ~= pixels[i + 3] ~ pixels[i .. i + 3];
1266             }
1267 
1268             return result;
1269         }else
1270         static if (format2 == PixelFormat.BGRA)
1271         {
1272             import std.algorithm : reverse;
1273 
1274             ubyte[] result;
1275 
1276             for (size_t i = 0; i < pixels.length; i += 4)
1277             {
1278                 result ~= pixels[i .. i + 3].reverse ~ pixels[i + 3];
1279             }
1280 
1281             return result;
1282         }
1283     }else
1284     static if (format1 == PixelFormat.ARGB)
1285     {
1286         static if (format2 == PixelFormat.RGB)
1287         {
1288             ubyte[] result;
1289 
1290             for (size_t i = 0; i < pixels.length; i += 4)
1291             {
1292                 result ~= pixels[i + 1 .. i + 4];
1293             }
1294 
1295             return result;
1296         }else
1297         static if (format2 == PixelFormat.RGBA)
1298         {
1299             ubyte[] result;
1300 
1301             for(size_t i = 0; i < pixels.length; i += 4)
1302             {
1303                 result ~= pixels[i + 3] ~ pixels[i .. i + 3];
1304             }
1305 
1306             return result;
1307         }else
1308         static if (format2 == PixelFormat.BGRA)
1309         {
1310             import std.algorithm : reverse;
1311 
1312             ubyte[] result;
1313 
1314             for (size_t i = 0; i < pixels.length; i += 4)
1315             {
1316                 result = pixels[i + 3] ~ pixels[i .. i + 3].reverse;
1317             }
1318 
1319             return result;
1320         }
1321     }else
1322     static if (format1 == PixelFormat.BGRA)
1323     {
1324         import std.algorithm : reverse;
1325 
1326         static if (format2 == PixelFormat.RGB)
1327         {
1328             ubyte[] result;
1329 
1330             for (size_t i = 0; i < pixels.length; i += 4)
1331             {
1332                 result ~= pixels[i .. i + 3].reverse;
1333             }
1334 
1335             return result;
1336         }else
1337         static if (format2 == PixelFormat.RGBA)
1338         {
1339             ubyte[] result;
1340 
1341             for (size_t i = 0; i < pixels.length; i += 4)
1342             {
1343                 result ~= pixels[i .. i + 3].reverse ~ pixels[i + 3];
1344             }
1345 
1346             return result;
1347         }else
1348         static if (format2 == PixelFormat.ARGB)
1349         {
1350             ubyte[] result;
1351 
1352             for (size_t i = 0; i < pixels.length; i += 4)
1353             {
1354                 result ~= pixels[i + 1 .. i + 4].reverse ~ pixels[i];
1355             }
1356 
1357             return result;
1358         }
1359     }
1360 }