]> git.cworth.org Git - vogl/blob - src/voglcore/vogl_color.h
Initial vogl checkin
[vogl] / src / voglcore / vogl_color.h
1 /**************************************************************************
2  *
3  * Copyright 2013-2014 RAD Game Tools and Valve Software
4  * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
5  * All Rights Reserved.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  * THE SOFTWARE.
24  *
25  **************************************************************************/
26
27 // File: vogl_color.h
28 #pragma once
29
30 #include "vogl_core.h"
31
32 namespace vogl
33 {
34     template <typename component_type>
35     struct color_quad_component_traits
36     {
37         enum
38         {
39             cSigned = false,
40             cFloat = false,
41             cMin = cUINT8_MIN,
42             cMax = cUINT8_MAX
43         };
44     };
45
46     template <>
47     struct color_quad_component_traits<int8>
48     {
49         enum
50         {
51             cSigned = true,
52             cFloat = false,
53             cMin = cINT8_MIN,
54             cMax = cINT8_MAX
55         };
56     };
57
58     template <>
59     struct color_quad_component_traits<int16>
60     {
61         enum
62         {
63             cSigned = true,
64             cFloat = false,
65             cMin = cINT16_MIN,
66             cMax = cINT16_MAX
67         };
68     };
69
70     template <>
71     struct color_quad_component_traits<uint16>
72     {
73         enum
74         {
75             cSigned = false,
76             cFloat = false,
77             cMin = cUINT16_MIN,
78             cMax = cUINT16_MAX
79         };
80     };
81
82     template <>
83     struct color_quad_component_traits<int32>
84     {
85         enum
86         {
87             cSigned = true,
88             cFloat = false,
89             cMin = cINT32_MIN,
90             cMax = cINT32_MAX
91         };
92     };
93
94     template <>
95     struct color_quad_component_traits<uint32>
96     {
97         enum
98         {
99             cSigned = false,
100             cFloat = false,
101             cMin = cUINT32_MIN,
102             cMax = cUINT32_MAX
103         };
104     };
105
106     template <>
107     struct color_quad_component_traits<float>
108     {
109         enum
110         {
111             cSigned = false,
112             cFloat = true,
113             cMin = cINT32_MIN,
114             cMax = cINT32_MAX
115         };
116     };
117
118     template <>
119     struct color_quad_component_traits<double>
120     {
121         enum
122         {
123             cSigned = false,
124             cFloat = true,
125             cMin = cINT32_MIN,
126             cMax = cINT32_MAX
127         };
128     };
129
130     template <typename component_type, typename parameter_type>
131     class color_quad : public helpers::rel_ops<color_quad<component_type, parameter_type> >
132     {
133         template <typename T>
134         static inline parameter_type clamp(T v)
135         {
136             parameter_type result = static_cast<parameter_type>(v);
137             if (!component_traits::cFloat)
138             {
139                 if (v < component_traits::cMin)
140                     result = static_cast<parameter_type>(component_traits::cMin);
141                 else if (v > component_traits::cMax)
142                     result = static_cast<parameter_type>(component_traits::cMax);
143             }
144             return result;
145         }
146
147 #ifdef _MSC_VER
148         template <>
149         static inline parameter_type clamp(int v)
150         {
151             if (!component_traits::cFloat)
152             {
153                 if ((!component_traits::cSigned) && (component_traits::cMin == 0) && (component_traits::cMax == 0xFF))
154                 {
155                     if (v & 0xFFFFFF00U)
156                         v = (~(static_cast<int>(v) >> 31)) & 0xFF;
157                 }
158                 else
159                 {
160                     if (v < component_traits::cMin)
161                         v = component_traits::cMin;
162                     else if (v > component_traits::cMax)
163                         v = component_traits::cMax;
164                 }
165             }
166             return static_cast<parameter_type>(v);
167         }
168 #endif
169
170     public:
171         typedef component_type component_t;
172         typedef parameter_type parameter_t;
173         typedef color_quad_component_traits<component_type> component_traits;
174
175         enum
176         {
177             cNumComps = 4
178         };
179
180         union
181         {
182             struct
183             {
184                 component_type r;
185                 component_type g;
186                 component_type b;
187                 component_type a;
188             };
189
190             component_type c[cNumComps];
191
192             uint32 m_u32;
193         };
194
195         inline color_quad()
196         {
197         }
198
199         inline color_quad(eClear)
200             : r(0), g(0), b(0), a(0)
201         {
202         }
203
204         inline color_quad(const color_quad &other)
205             : r(other.r), g(other.g), b(other.b), a(other.a)
206         {
207         }
208
209         explicit inline color_quad(parameter_type y, parameter_type alpha = component_traits::cMax)
210         {
211             set(y, alpha);
212         }
213
214         inline color_quad(parameter_type red, parameter_type green, parameter_type blue, parameter_type alpha = component_traits::cMax)
215         {
216             set(red, green, blue, alpha);
217         }
218
219         explicit inline color_quad(eNoClamp, parameter_type y, parameter_type alpha = component_traits::cMax)
220         {
221             set_noclamp_y_alpha(y, alpha);
222         }
223
224         inline color_quad(eNoClamp, parameter_type red, parameter_type green, parameter_type blue, parameter_type alpha = component_traits::cMax)
225         {
226             set_noclamp_rgba(red, green, blue, alpha);
227         }
228
229         template <typename other_component_type, typename other_parameter_type>
230         inline color_quad(const color_quad<other_component_type, other_parameter_type> &other)
231             : r(static_cast<component_type>(clamp(other.r))), g(static_cast<component_type>(clamp(other.g))), b(static_cast<component_type>(clamp(other.b))), a(static_cast<component_type>(clamp(other.a)))
232         {
233         }
234
235         inline void clear()
236         {
237             r = 0;
238             g = 0;
239             b = 0;
240             a = 0;
241         }
242
243         inline color_quad &operator=(const color_quad &other)
244         {
245             r = other.r;
246             g = other.g;
247             b = other.b;
248             a = other.a;
249             return *this;
250         }
251
252         inline color_quad &set_rgb(const color_quad &other)
253         {
254             r = other.r;
255             g = other.g;
256             b = other.b;
257             return *this;
258         }
259
260         template <typename other_component_type, typename other_parameter_type>
261         inline color_quad &operator=(const color_quad<other_component_type, other_parameter_type> &other)
262         {
263             r = static_cast<component_type>(clamp(other.r));
264             g = static_cast<component_type>(clamp(other.g));
265             b = static_cast<component_type>(clamp(other.b));
266             a = static_cast<component_type>(clamp(other.a));
267             return *this;
268         }
269
270         inline color_quad &operator=(parameter_type y)
271         {
272             set(y, component_traits::cMax);
273             return *this;
274         }
275
276         inline color_quad &set(parameter_type y, parameter_type alpha = component_traits::cMax)
277         {
278             y = clamp(y);
279             alpha = clamp(alpha);
280             r = static_cast<component_type>(y);
281             g = static_cast<component_type>(y);
282             b = static_cast<component_type>(y);
283             a = static_cast<component_type>(alpha);
284             return *this;
285         }
286
287         inline color_quad &set_alpha(parameter_type alpha)
288         {
289             a = static_cast<component_type>(clamp(alpha));
290             return *this;
291         }
292
293         inline color_quad &set_noclamp_y_alpha(parameter_type y, parameter_type alpha = component_traits::cMax)
294         {
295             VOGL_ASSERT((y >= component_traits::cMin) && (y <= component_traits::cMax));
296             VOGL_ASSERT((alpha >= component_traits::cMin) && (alpha <= component_traits::cMax));
297
298             r = static_cast<component_type>(y);
299             g = static_cast<component_type>(y);
300             b = static_cast<component_type>(y);
301             a = static_cast<component_type>(alpha);
302             return *this;
303         }
304
305         inline color_quad &set(parameter_type red, parameter_type green, parameter_type blue, parameter_type alpha = component_traits::cMax)
306         {
307             r = static_cast<component_type>(clamp(red));
308             g = static_cast<component_type>(clamp(green));
309             b = static_cast<component_type>(clamp(blue));
310             a = static_cast<component_type>(clamp(alpha));
311             return *this;
312         }
313
314         inline color_quad &set_noclamp_rgba(parameter_type red, parameter_type green, parameter_type blue, parameter_type alpha)
315         {
316             VOGL_ASSERT((red >= component_traits::cMin) && (red <= component_traits::cMax));
317             VOGL_ASSERT((green >= component_traits::cMin) && (green <= component_traits::cMax));
318             VOGL_ASSERT((blue >= component_traits::cMin) && (blue <= component_traits::cMax));
319             VOGL_ASSERT((alpha >= component_traits::cMin) && (alpha <= component_traits::cMax));
320
321             r = static_cast<component_type>(red);
322             g = static_cast<component_type>(green);
323             b = static_cast<component_type>(blue);
324             a = static_cast<component_type>(alpha);
325             return *this;
326         }
327
328         inline color_quad &set_noclamp_rgb(parameter_type red, parameter_type green, parameter_type blue)
329         {
330             VOGL_ASSERT((red >= component_traits::cMin) && (red <= component_traits::cMax));
331             VOGL_ASSERT((green >= component_traits::cMin) && (green <= component_traits::cMax));
332             VOGL_ASSERT((blue >= component_traits::cMin) && (blue <= component_traits::cMax));
333
334             r = static_cast<component_type>(red);
335             g = static_cast<component_type>(green);
336             b = static_cast<component_type>(blue);
337             return *this;
338         }
339
340         static inline parameter_type get_min_comp()
341         {
342             return component_traits::cMin;
343         }
344         static inline parameter_type get_max_comp()
345         {
346             return component_traits::cMax;
347         }
348         static inline bool get_comps_are_signed()
349         {
350             return component_traits::cSigned;
351         }
352
353         inline component_type operator[](uint i) const
354         {
355             VOGL_ASSERT(i < cNumComps);
356             return c[i];
357         }
358         inline component_type &operator[](uint i)
359         {
360             VOGL_ASSERT(i < cNumComps);
361             return c[i];
362         }
363
364         inline color_quad &set_component(uint i, parameter_type f)
365         {
366             VOGL_ASSERT(i < cNumComps);
367
368             c[i] = static_cast<component_type>(clamp(f));
369
370             return *this;
371         }
372
373         inline color_quad &set_grayscale(parameter_t l)
374         {
375             component_t x = static_cast<component_t>(clamp(l));
376             c[0] = x;
377             c[1] = x;
378             c[2] = x;
379             return *this;
380         }
381
382         inline color_quad &clamp(const color_quad &l, const color_quad &h)
383         {
384             for (uint i = 0; i < cNumComps; i++)
385                 c[i] = static_cast<component_type>(math::clamp<parameter_type>(c[i], l[i], h[i]));
386             return *this;
387         }
388
389         inline color_quad &clamp(parameter_type l, parameter_type h)
390         {
391             for (uint i = 0; i < cNumComps; i++)
392                 c[i] = static_cast<component_type>(math::clamp<parameter_type>(c[i], l, h));
393             return *this;
394         }
395
396         // Returns CCIR 601 luma (consistent with color_utils::RGB_To_Y).
397         inline parameter_type get_luma() const
398         {
399             return static_cast<parameter_type>((19595U * r + 38470U * g + 7471U * b + 32768U) >> 16U);
400         }
401
402         // Returns REC 709 luma.
403         inline parameter_type get_luma_rec709() const
404         {
405             return static_cast<parameter_type>((13938U * r + 46869U * g + 4729U * b + 32768U) >> 16U);
406         }
407
408         // Beware of endianness!
409         inline uint32 get_uint32() const
410         {
411             VOGL_ASSERT(sizeof(*this) == sizeof(uint32));
412             return *reinterpret_cast<const uint32 *>(this);
413         }
414
415         // Beware of endianness!
416         inline uint64_t get_uint64() const
417         {
418             VOGL_ASSERT(sizeof(*this) == sizeof(uint64_t));
419             return *reinterpret_cast<const uint64_t *>(this);
420         }
421
422         inline uint squared_distance(const color_quad &c, bool alpha = true) const
423         {
424             return math::square(r - c.r) + math::square(g - c.g) + math::square(b - c.b) + (alpha ? math::square(a - c.a) : 0);
425         }
426
427         inline bool rgb_equals(const color_quad &rhs) const
428         {
429             return (r == rhs.r) && (g == rhs.g) && (b == rhs.b);
430         }
431
432         inline bool operator==(const color_quad &rhs) const
433         {
434             if (sizeof(color_quad) == sizeof(uint32))
435                 return m_u32 == rhs.m_u32;
436             else
437                 return (r == rhs.r) && (g == rhs.g) && (b == rhs.b) && (a == rhs.a);
438         }
439
440         inline bool operator<(const color_quad &rhs) const
441         {
442             for (uint i = 0; i < cNumComps; i++)
443             {
444                 if (c[i] < rhs.c[i])
445                     return true;
446                 else if (!(c[i] == rhs.c[i]))
447                     return false;
448             }
449             return false;
450         }
451
452         color_quad &operator+=(const color_quad &other)
453         {
454             for (uint i = 0; i < 4; i++)
455                 c[i] = static_cast<component_type>(clamp(c[i] + other.c[i]));
456             return *this;
457         }
458
459         color_quad &operator-=(const color_quad &other)
460         {
461             for (uint i = 0; i < 4; i++)
462                 c[i] = static_cast<component_type>(clamp(c[i] - other.c[i]));
463             return *this;
464         }
465
466         color_quad &operator*=(parameter_type v)
467         {
468             for (uint i = 0; i < 4; i++)
469                 c[i] = static_cast<component_type>(clamp(c[i] * v));
470             return *this;
471         }
472
473         color_quad &operator/=(parameter_type v)
474         {
475             for (uint i = 0; i < 4; i++)
476                 c[i] = static_cast<component_type>(c[i] / v);
477             return *this;
478         }
479
480         color_quad get_swizzled(uint x, uint y, uint z, uint w) const
481         {
482             VOGL_ASSERT((x | y | z | w) < 4);
483             return color_quad(c[x], c[y], c[z], c[w]);
484         }
485
486         friend color_quad operator+(const color_quad &lhs, const color_quad &rhs)
487         {
488             color_quad result(lhs);
489             result += rhs;
490             return result;
491         }
492
493         friend color_quad operator-(const color_quad &lhs, const color_quad &rhs)
494         {
495             color_quad result(lhs);
496             result -= rhs;
497             return result;
498         }
499
500         friend color_quad operator*(const color_quad &lhs, parameter_type v)
501         {
502             color_quad result(lhs);
503             result *= v;
504             return result;
505         }
506
507         friend color_quad operator/(const color_quad &lhs, parameter_type v)
508         {
509             color_quad result(lhs);
510             result /= v;
511             return result;
512         }
513
514         friend color_quad operator*(parameter_type v, const color_quad &rhs)
515         {
516             color_quad result(rhs);
517             result *= v;
518             return result;
519         }
520
521         inline bool is_grayscale() const
522         {
523             return (c[0] == c[1]) && (c[1] == c[2]);
524         }
525
526         uint get_min_component_index(bool alpha = true) const
527         {
528             uint index = 0;
529             uint limit = alpha ? cNumComps : (cNumComps - 1);
530             for (uint i = 1; i < limit; i++)
531                 if (c[i] < c[index])
532                     index = i;
533             return index;
534         }
535
536         uint get_max_component_index(bool alpha = true) const
537         {
538             uint index = 0;
539             uint limit = alpha ? cNumComps : (cNumComps - 1);
540             for (uint i = 1; i < limit; i++)
541                 if (c[i] > c[index])
542                     index = i;
543             return index;
544         }
545
546         operator size_t() const
547         {
548             return (size_t)fast_hash(this, sizeof(*this));
549         }
550
551         void get_float4(float *pDst)
552         {
553             for (uint i = 0; i < 4; i++)
554                 pDst[i] = ((*this)[i] - component_traits::cMin) / float(component_traits::cMax - component_traits::cMin);
555         }
556
557         void get_float3(float *pDst)
558         {
559             for (uint i = 0; i < 3; i++)
560                 pDst[i] = ((*this)[i] - component_traits::cMin) / float(component_traits::cMax - component_traits::cMin);
561         }
562
563         void scale_rgb_by_float(float flScale)
564         {
565             for (uint i = 0; i < 3; i++)
566                 set_component(i, static_cast<parameter_t>(c[i] * flScale + (component_traits::cFloat ? 0.0f : .5f)));
567         }
568
569         void scale_rgb_by_component(parameter_t v)
570         {
571             float flScale;
572             if (component_traits::cFloat)
573                 flScale = static_cast<float>(v);
574             else
575                 flScale = math::maximum<float>(0.0f, v / static_cast<float>(component_traits::cMax));
576             scale_rgb_by_float(flScale);
577         }
578
579         static color_quad component_min(const color_quad &a, const color_quad &b)
580         {
581             color_quad result;
582             for (uint i = 0; i < 4; i++)
583                 result[i] = static_cast<component_type>(math::minimum(a[i], b[i]));
584             return result;
585         }
586
587         static color_quad component_max(const color_quad &a, const color_quad &b)
588         {
589             color_quad result;
590             for (uint i = 0; i < 4; i++)
591                 result[i] = static_cast<component_type>(math::maximum(a[i], b[i]));
592             return result;
593         }
594
595         static color_quad make_black()
596         {
597             return color_quad(0, 0, 0, static_cast<color_quad::component_t>(component_traits::cMax));
598         }
599
600         static color_quad make_white()
601         {
602             return color_quad(static_cast<color_quad::component_t>(component_traits::cMax), static_cast<color_quad::component_t>(component_traits::cMax), static_cast<color_quad::component_t>(component_traits::cMax), static_cast<color_quad::component_t>(component_traits::cMax));
603         }
604     }; // class color_quad
605
606     template <typename c, typename q>
607     struct scalar_type<color_quad<c, q> >
608     {
609         enum
610         {
611             cFlag = true
612         };
613         static inline void construct(color_quad<c, q> *p)
614         {
615             VOGL_NOTE_UNUSED(p);
616         }
617         static inline void construct(color_quad<c, q> *p, const color_quad<c, q> &init)
618         {
619             memcpy(p, &init, sizeof(color_quad<c, q>));
620         }
621         static inline void construct_array(color_quad<c, q> *p, uint n)
622         {
623             VOGL_NOTE_UNUSED(p), VOGL_NOTE_UNUSED(n);
624         }
625         static inline void destruct(color_quad<c, q> *p)
626         {
627             VOGL_NOTE_UNUSED(p);
628         }
629         static inline void destruct_array(color_quad<c, q> *p, uint n)
630         {
631             VOGL_NOTE_UNUSED(p), VOGL_NOTE_UNUSED(n);
632         }
633     };
634
635     typedef color_quad<uint8, int> color_quad_u8;
636     typedef color_quad<int8, int> color_quad_i8;
637     typedef color_quad<int16, int> color_quad_i16;
638     typedef color_quad<uint16, int> color_quad_u16;
639     typedef color_quad<int32, int> color_quad_i32;
640     typedef color_quad<uint32, uint> color_quad_u32;
641     typedef color_quad<float, float> color_quad_f;
642     typedef color_quad<double, double> color_quad_d;
643
644     namespace color
645     {
646         inline uint elucidian_distance(uint r0, uint g0, uint b0, uint r1, uint g1, uint b1)
647         {
648             int dr = (int)r0 - (int)r1;
649             int dg = (int)g0 - (int)g1;
650             int db = (int)b0 - (int)b1;
651
652             return static_cast<uint>(dr * dr + dg * dg + db * db);
653         }
654
655         inline uint elucidian_distance(uint r0, uint g0, uint b0, uint a0, uint r1, uint g1, uint b1, uint a1)
656         {
657             int dr = (int)r0 - (int)r1;
658             int dg = (int)g0 - (int)g1;
659             int db = (int)b0 - (int)b1;
660             int da = (int)a0 - (int)a1;
661
662             return static_cast<uint>(dr * dr + dg * dg + db * db + da * da);
663         }
664
665         inline uint elucidian_distance(const color_quad_u8 &c0, const color_quad_u8 &c1, bool alpha)
666         {
667             if (alpha)
668                 return elucidian_distance(c0.r, c0.g, c0.b, c0.a, c1.r, c1.g, c1.b, c1.a);
669             else
670                 return elucidian_distance(c0.r, c0.g, c0.b, c1.r, c1.g, c1.b);
671         }
672
673         inline uint weighted_elucidian_distance(uint r0, uint g0, uint b0, uint r1, uint g1, uint b1, uint wr, uint wg, uint wb)
674         {
675             int dr = (int)r0 - (int)r1;
676             int dg = (int)g0 - (int)g1;
677             int db = (int)b0 - (int)b1;
678
679             return static_cast<uint>((wr * dr * dr) + (wg * dg * dg) + (wb * db * db));
680         }
681
682         inline uint weighted_elucidian_distance(
683             uint r0, uint g0, uint b0, uint a0,
684             uint r1, uint g1, uint b1, uint a1,
685             uint wr, uint wg, uint wb, uint wa)
686         {
687             int dr = (int)r0 - (int)r1;
688             int dg = (int)g0 - (int)g1;
689             int db = (int)b0 - (int)b1;
690             int da = (int)a0 - (int)a1;
691
692             return static_cast<uint>((wr * dr * dr) + (wg * dg * dg) + (wb * db * db) + (wa * da * da));
693         }
694
695         inline uint weighted_elucidian_distance(const color_quad_u8 &c0, const color_quad_u8 &c1, uint wr, uint wg, uint wb, uint wa)
696         {
697             return weighted_elucidian_distance(c0.r, c0.g, c0.b, c0.a, c1.r, c1.g, c1.b, c1.a, wr, wg, wb, wa);
698         }
699
700         //const uint cRWeight = 8;//24;
701         //const uint cGWeight = 24;//73;
702         //const uint cBWeight = 1;//3;
703
704         const uint cRWeight = 8;  //24;
705         const uint cGWeight = 25; //73;
706         const uint cBWeight = 1;  //3;
707
708         inline uint color_distance(bool perceptual, const color_quad_u8 &e1, const color_quad_u8 &e2, bool alpha)
709         {
710             if (perceptual)
711             {
712                 if (alpha)
713                     return weighted_elucidian_distance(e1, e2, cRWeight, cGWeight, cBWeight, cRWeight + cGWeight + cBWeight);
714                 else
715                     return weighted_elucidian_distance(e1, e2, cRWeight, cGWeight, cBWeight, 0);
716             }
717             else
718                 return elucidian_distance(e1, e2, alpha);
719         }
720
721         inline uint peak_color_error(const color_quad_u8 &e1, const color_quad_u8 &e2)
722         {
723             return math::maximum<uint>(static_cast<uint>(labs(e1[0] - e2[0])), static_cast<uint>(labs(e1[1] - e2[1])), static_cast<uint>(labs(e1[2] - e2[2])));
724             //return math::square<int>(e1[0] - e2[0]) + math::square<int>(e1[1] - e2[1]) + math::square<int>(e1[2] - e2[2]);
725         }
726
727         // y - [0,255]
728         // co - [-127,127]
729         // cg - [-126,127]
730         inline void RGB_to_YCoCg(int r, int g, int b, int &y, int &co, int &cg)
731         {
732             y = (r >> 2) + (g >> 1) + (b >> 2);
733             co = (r >> 1) - (b >> 1);
734             cg = -(r >> 2) + (g >> 1) - (b >> 2);
735         }
736
737         inline void YCoCg_to_RGB(int y, int co, int cg, int &r, int &g, int &b)
738         {
739             int tmp = y - cg;
740             g = y + cg;
741             r = tmp + co;
742             b = tmp - co;
743         }
744
745         static inline uint8 clamp_component(int i)
746         {
747             if (static_cast<uint>(i) > 255U)
748             {
749                 if (i < 0)
750                     i = 0;
751                 else if (i > 255)
752                     i = 255;
753             }
754             return static_cast<uint8>(i);
755         }
756
757         // RGB->YCbCr constants, scaled by 2^16
758         const int YR = 19595, YG = 38470, YB = 7471, CB_R = -11059, CB_G = -21709, CB_B = 32768, CR_R = 32768, CR_G = -27439, CR_B = -5329;
759         // YCbCr->RGB constants, scaled by 2^16
760         const int R_CR = 91881, B_CB = 116130, G_CR = -46802, G_CB = -22554;
761
762         inline int RGB_to_Y(const color_quad_u8 &rgb)
763         {
764             const int r = rgb[0], g = rgb[1], b = rgb[2];
765             return (r * YR + g * YG + b * YB + 32768) >> 16;
766         }
767
768         // RGB to YCbCr (same as JFIF JPEG).
769         // Odd default biases account for 565 endpoint packing.
770         inline void RGB_to_YCC(color_quad_u8 &ycc, const color_quad_u8 &rgb, int cb_bias = 123, int cr_bias = 125)
771         {
772             const int r = rgb[0], g = rgb[1], b = rgb[2];
773             ycc.a = static_cast<uint8>((r * YR + g * YG + b * YB + 32768) >> 16);
774             ycc.r = clamp_component(cb_bias + ((r * CB_R + g * CB_G + b * CB_B + 32768) >> 16));
775             ycc.g = clamp_component(cr_bias + ((r * CR_R + g * CR_G + b * CR_B + 32768) >> 16));
776             ycc.b = 0;
777         }
778
779         // YCbCr to RGB.
780         // Odd biases account for 565 endpoint packing.
781         inline void YCC_to_RGB(color_quad_u8 &rgb, const color_quad_u8 &ycc, int cb_bias = 123, int cr_bias = 125)
782         {
783             const int y = ycc.a;
784             const int cb = ycc.r - cb_bias;
785             const int cr = ycc.g - cr_bias;
786             rgb.r = clamp_component(y + ((R_CR * cr + 32768) >> 16));
787             rgb.g = clamp_component(y + ((G_CR * cr + G_CB * cb + 32768) >> 16));
788             rgb.b = clamp_component(y + ((B_CB * cb + 32768) >> 16));
789             rgb.a = 255;
790         }
791
792         // Float RGB->YCbCr constants
793         const float S = 1.0f / 65536.0f;
794         const float F_YR = S * YR, F_YG = S * YG, F_YB = S * YB, F_CB_R = S * CB_R, F_CB_G = S * CB_G, F_CB_B = S * CB_B, F_CR_R = S * CR_R, F_CR_G = S * CR_G, F_CR_B = S * CR_B;
795         // Float YCbCr->RGB constants
796         const float F_R_CR = S * R_CR, F_B_CB = S * B_CB, F_G_CR = S * G_CR, F_G_CB = S * G_CB;
797
798         inline void RGB_to_YCC_float(color_quad_f &ycc, const color_quad_u8 &rgb)
799         {
800             const int r = rgb[0], g = rgb[1], b = rgb[2];
801             ycc.a = r * F_YR + g * F_YG + b * F_YB;
802             ycc.r = r * F_CB_R + g * F_CB_G + b * F_CB_B;
803             ycc.g = r * F_CR_R + g * F_CR_G + b * F_CR_B;
804             ycc.b = 0;
805         }
806
807         inline void YCC_float_to_RGB(color_quad_u8 &rgb, const color_quad_f &ycc)
808         {
809             float y = ycc.a, cb = ycc.r, cr = ycc.g;
810             rgb.r = color::clamp_component(static_cast<int>(.5f + y + F_R_CR * cr));
811             rgb.g = color::clamp_component(static_cast<int>(.5f + y + F_G_CR * cr + F_G_CB * cb));
812             rgb.b = color::clamp_component(static_cast<int>(.5f + y + F_B_CB * cb));
813             rgb.a = 255;
814         }
815
816     } // namespace color
817
818     // This class purposely trades off speed for flexibility. It can handle any component swizzle, any pixel type from 1-4 components and 1-32 bits/component,
819     // any pixel size between 1-16 bytes/pixel, any pixel stride, any color_quad data type (signed/unsigned/float 8/16/32 bits/component), and scaled/non-scaled components.
820     // On the downside, it's freaking slow.
821     class pixel_packer
822     {
823     public:
824         pixel_packer()
825         {
826             clear();
827         }
828
829         pixel_packer(uint num_comps, uint bits_per_comp, int pixel_stride = -1, bool reversed = false)
830         {
831             init(num_comps, bits_per_comp, pixel_stride, reversed);
832         }
833
834         pixel_packer(const char *pComp_map, int pixel_stride = -1, int force_comp_size = -1)
835         {
836             init(pComp_map, pixel_stride, force_comp_size);
837         }
838
839         void clear()
840         {
841             utils::zero_this(this);
842         }
843
844         inline bool is_valid() const
845         {
846             return m_pixel_stride > 0;
847         }
848
849         inline uint get_pixel_stride() const
850         {
851             return m_pixel_stride;
852         }
853         void set_pixel_stride(uint n)
854         {
855             m_pixel_stride = n;
856         }
857
858         uint get_num_comps() const
859         {
860             return m_num_comps;
861         }
862         uint get_comp_size(uint index) const
863         {
864             VOGL_ASSERT(index < 4);
865             return m_comp_size[index];
866         }
867         uint get_comp_ofs(uint index) const
868         {
869             VOGL_ASSERT(index < 4);
870             return m_comp_ofs[index];
871         }
872         uint get_comp_max(uint index) const
873         {
874             VOGL_ASSERT(index < 4);
875             return m_comp_max[index];
876         }
877         bool get_rgb_is_luma() const
878         {
879             return m_rgb_is_luma;
880         }
881
882         template <typename color_quad_type>
883         const void *unpack(const void *p, color_quad_type &color, bool rescale = true) const
884         {
885             const uint8 *pSrc = static_cast<const uint8 *>(p);
886
887             for (uint i = 0; i < 4; i++)
888             {
889                 const uint comp_size = m_comp_size[i];
890                 if (!comp_size)
891                 {
892                     if (color_quad_type::component_traits::cFloat)
893                         color[i] = static_cast<typename color_quad_type::parameter_t>((i == 3) ? 1 : 0);
894                     else
895                         color[i] = static_cast<typename color_quad_type::parameter_t>((i == 3) ? color_quad_type::component_traits::cMax : 0);
896                     continue;
897                 }
898
899                 uint n = 0, dst_bit_ofs = 0;
900                 uint src_bit_ofs = m_comp_ofs[i];
901                 while (dst_bit_ofs < comp_size)
902                 {
903                     const uint byte_bit_ofs = src_bit_ofs & 7;
904                     n |= ((pSrc[src_bit_ofs >> 3] >> byte_bit_ofs) << dst_bit_ofs);
905
906                     const uint bits_read = 8 - byte_bit_ofs;
907                     src_bit_ofs += bits_read;
908                     dst_bit_ofs += bits_read;
909                 }
910
911                 const uint32 mx = m_comp_max[i];
912                 n &= mx;
913
914                 const uint32 h = static_cast<uint32>(color_quad_type::component_traits::cMax);
915
916                 if (color_quad_type::component_traits::cFloat)
917                     color.set_component(i, static_cast<typename color_quad_type::parameter_t>(n));
918                 else if (rescale)
919                     color.set_component(i, static_cast<typename color_quad_type::parameter_t>((static_cast<uint64_t>(n) * h + (mx >> 1U)) / mx));
920                 else if (color_quad_type::component_traits::cSigned)
921                     color.set_component(i, static_cast<typename color_quad_type::parameter_t>(math::minimum<uint32>(n, h)));
922                 else
923                     color.set_component(i, static_cast<typename color_quad_type::parameter_t>(n));
924             }
925
926             if (m_rgb_is_luma)
927             {
928                 color[0] = color[1];
929                 color[2] = color[1];
930             }
931
932             return pSrc + m_pixel_stride;
933         }
934
935         template <typename color_quad_type>
936         void *pack(const color_quad_type &color, void *p, bool rescale = true) const
937         {
938             uint8 *pDst = static_cast<uint8 *>(p);
939
940             for (uint i = 0; i < 4; i++)
941             {
942                 const uint comp_size = m_comp_size[i];
943                 if (!comp_size)
944                     continue;
945
946                 uint32 mx = m_comp_max[i];
947
948                 uint32 n;
949                 if (color_quad_type::component_traits::cFloat)
950                 {
951                     typename color_quad_type::parameter_t t = color[i];
952                     if (t < 0.0f)
953                         n = 0;
954                     else if (t > static_cast<typename color_quad_type::parameter_t>(mx))
955                         n = mx;
956                     else
957                         n = math::minimum<uint32>(static_cast<uint32>(floor(t + .5f)), mx);
958                 }
959                 else if (rescale)
960                 {
961                     if (color_quad_type::component_traits::cSigned)
962                         n = math::maximum<int>(static_cast<int>(color[i]), 0);
963                     else
964                         n = static_cast<uint32>(color[i]);
965
966                     const uint32 h = static_cast<uint32>(color_quad_type::component_traits::cMax);
967                     n = static_cast<uint32>((static_cast<uint64_t>(n) * mx + (h >> 1)) / h);
968                 }
969                 else
970                 {
971                     if (color_quad_type::component_traits::cSigned)
972                         n = math::minimum<uint32>(static_cast<uint32>(math::maximum<int>(static_cast<int>(color[i]), 0)), mx);
973                     else
974                         n = math::minimum<uint32>(static_cast<uint32>(color[i]), mx);
975                 }
976
977                 uint src_bit_ofs = 0;
978                 uint dst_bit_ofs = m_comp_ofs[i];
979                 while (src_bit_ofs < comp_size)
980                 {
981                     const uint cur_byte_bit_ofs = (dst_bit_ofs & 7);
982                     const uint cur_byte_bits = 8 - cur_byte_bit_ofs;
983
984                     uint byte_val = pDst[dst_bit_ofs >> 3];
985                     uint bit_mask = (mx << cur_byte_bit_ofs) & 0xFF;
986                     byte_val &= ~bit_mask;
987                     byte_val |= (n << cur_byte_bit_ofs);
988                     pDst[dst_bit_ofs >> 3] = static_cast<uint8>(byte_val);
989
990                     mx >>= cur_byte_bits;
991                     n >>= cur_byte_bits;
992
993                     dst_bit_ofs += cur_byte_bits;
994                     src_bit_ofs += cur_byte_bits;
995                 }
996             }
997
998             return pDst + m_pixel_stride;
999         }
1000
1001         bool init(uint num_comps, uint bits_per_comp, int pixel_stride = -1, bool reversed = false)
1002         {
1003             clear();
1004
1005             if ((num_comps < 1) || (num_comps > 4) || (bits_per_comp < 1) || (bits_per_comp > 32))
1006             {
1007                 VOGL_ASSERT_ALWAYS;
1008                 return false;
1009             }
1010
1011             for (uint i = 0; i < num_comps; i++)
1012             {
1013                 m_comp_size[i] = bits_per_comp;
1014                 m_comp_ofs[i] = i * bits_per_comp;
1015                 if (reversed)
1016                     m_comp_ofs[i] = ((num_comps - 1) * bits_per_comp) - m_comp_ofs[i];
1017             }
1018
1019             for (uint i = 0; i < 4; i++)
1020                 m_comp_max[i] = static_cast<uint32>((1ULL << m_comp_size[i]) - 1ULL);
1021
1022             m_pixel_stride = (pixel_stride >= 0) ? pixel_stride : (num_comps * bits_per_comp + 7) / 8;
1023
1024             return true;
1025         }
1026
1027         // Format examples:
1028         // R16G16B16
1029         // B5G6R5
1030         // B5G5R5x1
1031         // Y8A8
1032         // A8R8G8B8
1033         // First component is at LSB in memory. Assumes unsigned integer components, 1-32bits each.
1034         bool init(const char *pComp_map, int pixel_stride = -1, int force_comp_size = -1)
1035         {
1036             clear();
1037
1038             uint cur_bit_ofs = 0;
1039
1040             while (*pComp_map)
1041             {
1042                 char c = *pComp_map++;
1043
1044                 int comp_index = -1;
1045                 if (c == 'R')
1046                     comp_index = 0;
1047                 else if (c == 'G')
1048                     comp_index = 1;
1049                 else if (c == 'B')
1050                     comp_index = 2;
1051                 else if (c == 'A')
1052                     comp_index = 3;
1053                 else if (c == 'Y')
1054                     comp_index = 4;
1055                 else if (c != 'x')
1056                     return false;
1057
1058                 uint comp_size = 0;
1059
1060                 uint n = *pComp_map;
1061                 if ((n >= '0') && (n <= '9'))
1062                 {
1063                     comp_size = n - '0';
1064                     pComp_map++;
1065
1066                     n = *pComp_map;
1067                     if ((n >= '0') && (n <= '9'))
1068                     {
1069                         comp_size = (comp_size * 10) + (n - '0');
1070                         pComp_map++;
1071                     }
1072                 }
1073
1074                 if (force_comp_size != -1)
1075                     comp_size = force_comp_size;
1076
1077                 if ((!comp_size) || (comp_size > 32))
1078                     return false;
1079
1080                 if (comp_index == 4)
1081                 {
1082                     if (m_comp_size[0] || m_comp_size[1] || m_comp_size[2])
1083                         return false;
1084
1085                     //m_comp_ofs[0] = m_comp_ofs[1] = m_comp_ofs[2] = cur_bit_ofs;
1086                     //m_comp_size[0] = m_comp_size[1] = m_comp_size[2] = comp_size;
1087                     m_comp_ofs[1] = cur_bit_ofs;
1088                     m_comp_size[1] = comp_size;
1089                     m_rgb_is_luma = true;
1090                     m_num_comps++;
1091                 }
1092                 else if (comp_index >= 0)
1093                 {
1094                     if (m_comp_size[comp_index])
1095                         return false;
1096
1097                     m_comp_ofs[comp_index] = cur_bit_ofs;
1098                     m_comp_size[comp_index] = comp_size;
1099                     m_num_comps++;
1100                 }
1101
1102                 cur_bit_ofs += comp_size;
1103             }
1104
1105             for (uint i = 0; i < 4; i++)
1106                 m_comp_max[i] = static_cast<uint32>((1ULL << m_comp_size[i]) - 1ULL);
1107
1108             if (pixel_stride >= 0)
1109                 m_pixel_stride = pixel_stride;
1110             else
1111                 m_pixel_stride = (cur_bit_ofs + 7) / 8;
1112             return true;
1113         }
1114
1115     private:
1116         uint m_pixel_stride;
1117         uint m_num_comps;
1118         uint m_comp_size[4];
1119         uint m_comp_ofs[4];
1120         uint m_comp_max[4];
1121         bool m_rgb_is_luma;
1122     };
1123
1124 } // namespace vogl