1ca584e9b0e9b5f7c4fac470e54af3b754a6d106
[blender.git] / source / blender / imbuf / intern / dds / ColorBlock.cpp
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16
17 /** \file
18  * \ingroup imbdds
19  */
20
21 /*
22  * This file is based on a similar file from the NVIDIA texture tools
23  * (http://nvidia-texture-tools.googlecode.com/)
24  *
25  * Original license from NVIDIA follows.
26  */
27
28 // This code is in the public domain -- castanyo@yahoo.es
29
30 #include <ColorBlock.h>
31 #include <Image.h>
32 #include <Common.h>
33
34 #if 0
35 // Get approximate luminance.
36 inline static uint colorLuminance(Color32 c)
37 {
38   return c.r + c.g + c.b;
39 }
40
41 // Get the euclidean distance between the given colors.
42 inline static uint colorDistance(Color32 c0, Color32 c1)
43 {
44   return (c0.r - c1.r) * (c0.r - c1.r) + (c0.g - c1.g) * (c0.g - c1.g) +
45          (c0.b - c1.b) * (c0.b - c1.b);
46 }
47 #endif
48
49 /// Default constructor.
50 ColorBlock::ColorBlock()
51 {
52 }
53
54 /// Init the color block from an array of colors.
55 ColorBlock::ColorBlock(const uint *linearImage)
56 {
57   for (uint i = 0; i < 16; i++) {
58     color(i) = Color32(linearImage[i]);
59   }
60 }
61
62 /// Init the color block with the contents of the given block.
63 ColorBlock::ColorBlock(const ColorBlock &block)
64 {
65   for (uint i = 0; i < 16; i++) {
66     color(i) = block.color(i);
67   }
68 }
69
70 /// Initialize this color block.
71 ColorBlock::ColorBlock(const Image *img, uint x, uint y)
72 {
73   init(img, x, y);
74 }
75
76 void ColorBlock::init(const Image *img, uint x, uint y)
77 {
78   init(img->width(), img->height(), (const uint *)img->pixels(), x, y);
79 }
80
81 void ColorBlock::init(uint w, uint h, const uint *data, uint x, uint y)
82 {
83   const uint bw = MIN(w - x, 4U);
84   const uint bh = MIN(h - y, 4U);
85
86   // Blocks that are smaller than 4x4 are handled by repeating the pixels.
87   // @@ Thats only correct when block size is 1, 2 or 4, but not with 3. :(
88   // @@ Ideally we should zero the weights of the pixels out of range.
89
90   for (uint i = 0; i < 4; i++) {
91     const int by = i % bh;
92
93     for (uint e = 0; e < 4; e++) {
94       const int bx = e % bw;
95       const uint idx = (y + by) * w + x + bx;
96
97       color(e, i).u = data[idx];
98     }
99   }
100 }
101
102 void ColorBlock::init(uint w, uint h, const float *data, uint x, uint y)
103 {
104   const uint bw = MIN(w - x, 4U);
105   const uint bh = MIN(h - y, 4U);
106
107   // Blocks that are smaller than 4x4 are handled by repeating the pixels.
108   // @@ Thats only correct when block size is 1, 2 or 4, but not with 3. :(
109   // @@ Ideally we should zero the weights of the pixels out of range.
110
111   uint srcPlane = w * h;
112
113   for (uint i = 0; i < 4; i++) {
114     const uint by = i % bh;
115
116     for (uint e = 0; e < 4; e++) {
117       const uint bx = e % bw;
118       const uint idx = ((y + by) * w + x + bx);
119
120       Color32 &c = color(e, i);
121       c.r = uint8(255 * CLAMP(data[idx + 0 * srcPlane],
122                               0.0f,
123                               1.0f));  // @@ Is this the right way to quantize floats to bytes?
124       c.g = uint8(255 * CLAMP(data[idx + 1 * srcPlane], 0.0f, 1.0f));
125       c.b = uint8(255 * CLAMP(data[idx + 2 * srcPlane], 0.0f, 1.0f));
126       c.a = uint8(255 * CLAMP(data[idx + 3 * srcPlane], 0.0f, 1.0f));
127     }
128   }
129 }
130
131 static inline uint8 component(Color32 c, uint i)
132 {
133   if (i == 0) {
134     return c.r;
135   }
136   if (i == 1) {
137     return c.g;
138   }
139   if (i == 2) {
140     return c.b;
141   }
142   if (i == 3) {
143     return c.a;
144   }
145   if (i == 4) {
146     return 0xFF;
147   }
148   return 0;
149 }
150
151 void ColorBlock::swizzle(uint x, uint y, uint z, uint w)
152 {
153   for (int i = 0; i < 16; i++) {
154     Color32 c = m_color[i];
155     m_color[i].r = component(c, x);
156     m_color[i].g = component(c, y);
157     m_color[i].b = component(c, z);
158     m_color[i].a = component(c, w);
159   }
160 }
161
162 /// Returns true if the block has a single color.
163 bool ColorBlock::isSingleColor(Color32 mask /*= Color32(0xFF, 0xFF, 0xFF, 0x00)*/) const
164 {
165   uint u = m_color[0].u & mask.u;
166
167   for (int i = 1; i < 16; i++) {
168     if (u != (m_color[i].u & mask.u)) {
169       return false;
170     }
171   }
172
173   return true;
174 }
175
176 #if 0
177 /// Returns true if the block has a single color, ignoring transparent pixels.
178 bool ColorBlock::isSingleColorNoAlpha() const
179 {
180   Color32 c;
181   int i;
182   for (i = 0; i < 16; i++) {
183     if (m_color[i].a != 0) {
184       c = m_color[i];
185     }
186   }
187
188   Color32 mask(0xFF, 0xFF, 0xFF, 0x00);
189   uint u = c.u & mask.u;
190
191   for (; i < 16; i++) {
192     if (u != (m_color[i].u & mask.u)) {
193       return false;
194     }
195   }
196
197   return true;
198 }
199 #endif
200
201 #if 0
202 /// Count number of unique colors in this color block.
203 uint ColorBlock::countUniqueColors() const
204 {
205   uint count = 0;
206
207   // @@ This does not have to be o(n^2)
208   for (int i = 0; i < 16; i++) {
209     bool unique = true;
210     for (int j = 0; j < i; j++) {
211       if (m_color[i] != m_color[j]) {
212         unique = false;
213       }
214     }
215
216     if (unique) {
217       count++;
218     }
219   }
220
221   return count;
222 }
223 #endif
224
225 #if 0
226 /// Get average color of the block.
227 Color32 ColorBlock::averageColor() const
228 {
229   uint r, g, b, a;
230   r = g = b = a = 0;
231
232   for (uint i = 0; i < 16; i++) {
233     r += m_color[i].r;
234     g += m_color[i].g;
235     b += m_color[i].b;
236     a += m_color[i].a;
237   }
238
239   return Color32(uint8(r / 16), uint8(g / 16), uint8(b / 16), uint8(a / 16));
240 }
241 #endif
242
243 /// Return true if the block is not fully opaque.
244 bool ColorBlock::hasAlpha() const
245 {
246   for (uint i = 0; i < 16; i++) {
247     if (m_color[i].a != 255) {
248       return true;
249     }
250   }
251   return false;
252 }
253
254 #if 0
255
256 /// Get diameter color range.
257 void ColorBlock::diameterRange(Color32 *start, Color32 *end) const
258 {
259   Color32 c0, c1;
260   uint best_dist = 0;
261
262   for (int i = 0; i < 16; i++) {
263     for (int j = i + 1; j < 16; j++) {
264       uint dist = colorDistance(m_color[i], m_color[j]);
265       if (dist > best_dist) {
266         best_dist = dist;
267         c0 = m_color[i];
268         c1 = m_color[j];
269       }
270     }
271   }
272
273   *start = c0;
274   *end = c1;
275 }
276
277 /// Get luminance color range.
278 void ColorBlock::luminanceRange(Color32 *start, Color32 *end) const
279 {
280   Color32 minColor, maxColor;
281   uint minLuminance, maxLuminance;
282
283   maxLuminance = minLuminance = colorLuminance(m_color[0]);
284
285   for (uint i = 1; i < 16; i++) {
286     uint luminance = colorLuminance(m_color[i]);
287
288     if (luminance > maxLuminance) {
289       maxLuminance = luminance;
290       maxColor = m_color[i];
291     }
292     else if (luminance < minLuminance) {
293       minLuminance = luminance;
294       minColor = m_color[i];
295     }
296   }
297
298   *start = minColor;
299   *end = maxColor;
300 }
301
302 /// Get color range based on the bounding box.
303 void ColorBlock::boundsRange(Color32 *start, Color32 *end) const
304 {
305   Color32 minColor(255, 255, 255);
306   Color32 maxColor(0, 0, 0);
307
308   for (uint i = 0; i < 16; i++) {
309     if (m_color[i].r < minColor.r) {
310       minColor.r = m_color[i].r;
311     }
312     if (m_color[i].g < minColor.g) {
313       minColor.g = m_color[i].g;
314     }
315     if (m_color[i].b < minColor.b) {
316       minColor.b = m_color[i].b;
317     }
318     if (m_color[i].r > maxColor.r) {
319       maxColor.r = m_color[i].r;
320     }
321     if (m_color[i].g > maxColor.g) {
322       maxColor.g = m_color[i].g;
323     }
324     if (m_color[i].b > maxColor.b) {
325       maxColor.b = m_color[i].b;
326     }
327   }
328
329   // Offset range by 1/16 of the extents
330   Color32 inset;
331   inset.r = (maxColor.r - minColor.r) >> 4;
332   inset.g = (maxColor.g - minColor.g) >> 4;
333   inset.b = (maxColor.b - minColor.b) >> 4;
334
335   minColor.r = (minColor.r + inset.r <= 255) ? minColor.r + inset.r : 255;
336   minColor.g = (minColor.g + inset.g <= 255) ? minColor.g + inset.g : 255;
337   minColor.b = (minColor.b + inset.b <= 255) ? minColor.b + inset.b : 255;
338
339   maxColor.r = (maxColor.r >= inset.r) ? maxColor.r - inset.r : 0;
340   maxColor.g = (maxColor.g >= inset.g) ? maxColor.g - inset.g : 0;
341   maxColor.b = (maxColor.b >= inset.b) ? maxColor.b - inset.b : 0;
342
343   *start = minColor;
344   *end = maxColor;
345 }
346
347 /// Get color range based on the bounding box.
348 void ColorBlock::boundsRangeAlpha(Color32 *start, Color32 *end) const
349 {
350   Color32 minColor(255, 255, 255, 255);
351   Color32 maxColor(0, 0, 0, 0);
352
353   for (uint i = 0; i < 16; i++) {
354     if (m_color[i].r < minColor.r) {
355       minColor.r = m_color[i].r;
356     }
357     if (m_color[i].g < minColor.g) {
358       minColor.g = m_color[i].g;
359     }
360     if (m_color[i].b < minColor.b) {
361       minColor.b = m_color[i].b;
362     }
363     if (m_color[i].a < minColor.a) {
364       minColor.a = m_color[i].a;
365     }
366     if (m_color[i].r > maxColor.r) {
367       maxColor.r = m_color[i].r;
368     }
369     if (m_color[i].g > maxColor.g) {
370       maxColor.g = m_color[i].g;
371     }
372     if (m_color[i].b > maxColor.b) {
373       maxColor.b = m_color[i].b;
374     }
375     if (m_color[i].a > maxColor.a) {
376       maxColor.a = m_color[i].a;
377     }
378   }
379
380   // Offset range by 1/16 of the extents
381   Color32 inset;
382   inset.r = (maxColor.r - minColor.r) >> 4;
383   inset.g = (maxColor.g - minColor.g) >> 4;
384   inset.b = (maxColor.b - minColor.b) >> 4;
385   inset.a = (maxColor.a - minColor.a) >> 4;
386
387   minColor.r = (minColor.r + inset.r <= 255) ? minColor.r + inset.r : 255;
388   minColor.g = (minColor.g + inset.g <= 255) ? minColor.g + inset.g : 255;
389   minColor.b = (minColor.b + inset.b <= 255) ? minColor.b + inset.b : 255;
390   minColor.a = (minColor.a + inset.a <= 255) ? minColor.a + inset.a : 255;
391
392   maxColor.r = (maxColor.r >= inset.r) ? maxColor.r - inset.r : 0;
393   maxColor.g = (maxColor.g >= inset.g) ? maxColor.g - inset.g : 0;
394   maxColor.b = (maxColor.b >= inset.b) ? maxColor.b - inset.b : 0;
395   maxColor.a = (maxColor.a >= inset.a) ? maxColor.a - inset.a : 0;
396
397   *start = minColor;
398   *end = maxColor;
399 }
400 #endif
401
402 #if 0
403 /// Sort colors by abosolute value in their 16 bit representation.
404 void ColorBlock::sortColorsByAbsoluteValue()
405 {
406   // Dummy selection sort.
407   for (uint a = 0; a < 16; a++) {
408     uint max = a;
409     Color16 cmax(m_color[a]);
410
411     for (uint b = a + 1; b < 16; b++) {
412       Color16 cb(m_color[b]);
413
414       if (cb.u > cmax.u) {
415         max = b;
416         cmax = cb;
417       }
418     }
419     swap(m_color[a], m_color[max]);
420   }
421 }
422 #endif
423
424 #if 0
425 /// Find extreme colors in the given axis.
426 void ColorBlock::computeRange(Vector3::Arg axis, Color32 *start, Color32 *end) const
427 {
428
429   int mini, maxi;
430   mini = maxi = 0;
431
432   float min, max;
433   min = max = dot(Vector3(m_color[0].r, m_color[0].g, m_color[0].b), axis);
434
435   for (uint i = 1; i < 16; i++) {
436     const Vector3 vec(m_color[i].r, m_color[i].g, m_color[i].b);
437
438     float val = dot(vec, axis);
439     if (val < min) {
440       mini = i;
441       min = val;
442     }
443     else if (val > max) {
444       maxi = i;
445       max = val;
446     }
447   }
448
449   *start = m_color[mini];
450   *end = m_color[maxi];
451 }
452 #endif
453
454 #if 0
455 /// Sort colors in the given axis.
456 void ColorBlock::sortColors(const Vector3 &axis)
457 {
458   float luma_array[16];
459
460   for (uint i = 0; i < 16; i++) {
461     const Vector3 vec(m_color[i].r, m_color[i].g, m_color[i].b);
462     luma_array[i] = dot(vec, axis);
463   }
464
465   // Dummy selection sort.
466   for (uint a = 0; a < 16; a++) {
467     uint min = a;
468     for (uint b = a + 1; b < 16; b++) {
469       if (luma_array[b] < luma_array[min]) {
470         min = b;
471       }
472     }
473     swap(luma_array[a], luma_array[min]);
474     swap(m_color[a], m_color[min]);
475   }
476 }
477 #endif
478
479 #if 0
480 /// Get the volume of the color block.
481 float ColorBlock::volume() const
482 {
483   Box bounds;
484   bounds.clearBounds();
485
486   for (int i = 0; i < 16; i++) {
487     const Vector3 point(m_color[i].r, m_color[i].g, m_color[i].b);
488     bounds.addPointToBounds(point);
489   }
490
491   return bounds.volume();
492 }
493 #endif