3dcfa0cef43b34271ad276f29cd1d0d0f5869c66
[blender-staging.git] / source / blender / imbuf / intern / dds / ColorBlock.cpp
1 /*
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * Contributors: Amorilia (amorilia@users.sourceforge.net)
21  *
22  * ***** END GPL LICENSE BLOCK *****
23  */
24
25 /** \file blender/imbuf/intern/dds/ColorBlock.cpp
26  *  \ingroup imbdds
27  */
28
29
30 /*
31  * This file is based on a similar file from the NVIDIA texture tools
32  * (http://nvidia-texture-tools.googlecode.com/)
33  *
34  * Original license from NVIDIA follows.
35  */
36
37 // This code is in the public domain -- castanyo@yahoo.es
38
39 #include <ColorBlock.h>
40 #include <Image.h>
41 #include <Common.h>
42
43         // Get approximate luminance.
44         inline static uint colorLuminance(Color32 c)
45         {
46                 return c.r + c.g + c.b;
47         }
48         
49         // Get the euclidean distance between the given colors.
50         inline static uint colorDistance(Color32 c0, Color32 c1)
51         {
52                 return (c0.r - c1.r) * (c0.r - c1.r) + (c0.g - c1.g) * (c0.g - c1.g) + (c0.b - c1.b) * (c0.b - c1.b);
53         }
54         
55
56 /// Default constructor.
57 ColorBlock::ColorBlock()
58 {
59 }
60
61 /// Init the color block from an array of colors.
62 ColorBlock::ColorBlock(const uint * linearImage)
63 {
64         for(uint i = 0; i < 16; i++) {
65                 color(i) = Color32(linearImage[i]);
66         }
67 }
68
69 /// Init the color block with the contents of the given block.
70 ColorBlock::ColorBlock(const ColorBlock & block)
71 {
72         for(uint i = 0; i < 16; i++) {
73                 color(i) = block.color(i);
74         }
75 }
76
77
78 /// Initialize this color block.
79 ColorBlock::ColorBlock(const Image * img, uint x, uint y)
80 {
81         init(img, x, y);
82 }
83
84 void ColorBlock::init(const Image * img, uint x, uint y)
85 {
86         const uint bw = min(img->width() - x, 4U);
87         const uint bh = min(img->height() - y, 4U);
88
89         static int remainder[] = {
90                 0, 0, 0, 0,
91                 0, 1, 0, 1,
92                 0, 1, 2, 0,
93                 0, 1, 2, 3,
94         };
95
96         // Blocks that are smaller than 4x4 are handled by repeating the pixels.
97         // @@ Thats only correct when block size is 1, 2 or 4, but not with 3. :(
98
99         for(uint i = 0; i < 4; i++) {
100                 //const int by = i % bh;
101                 const int by = remainder[(bh - 1) * 4 + i];
102                 for(uint e = 0; e < 4; e++) {
103                         //const int bx = e % bw;
104                         const int bx = remainder[(bw - 1) * 4 + e];
105                         color(e, i) = img->pixel(x + bx, y + by);
106                 }
107         }
108 }
109
110
111 void ColorBlock::swizzleDXT5n()
112 {
113         for(int i = 0; i < 16; i++)
114         {
115                 Color32 c = m_color[i];
116                 m_color[i] = Color32(0xFF, c.g, 0, c.r);
117         }
118 }
119
120 void ColorBlock::splatX()
121 {
122         for(int i = 0; i < 16; i++)
123         {
124                 uint8 x = m_color[i].r;
125                 m_color[i] = Color32(x, x, x, x);
126         }
127 }
128
129 void ColorBlock::splatY()
130 {
131         for(int i = 0; i < 16; i++)
132         {
133                 uint8 y = m_color[i].g;
134                 m_color[i] = Color32(y, y, y, y);
135         }
136 }
137
138 /// Returns true if the block has a single color.
139 bool ColorBlock::isSingleColor() const
140 {
141         Color32 mask(0xFF, 0xFF, 0xFF, 0x00);
142         uint u = m_color[0].u & mask.u;
143         
144         for(int i = 1; i < 16; i++)
145         {
146                 if (u != (m_color[i].u & mask.u))
147                 {
148                         return false;
149                 }
150         }
151         
152         return true;
153 }
154
155 /// Returns true if the block has a single color, ignoring transparent pixels.
156 bool ColorBlock::isSingleColorNoAlpha() const
157 {
158         Color32 c;
159         int i;
160         for(i = 0; i < 16; i++)
161         {
162                 if (m_color[i].a != 0) c = m_color[i];
163         }
164
165         Color32 mask(0xFF, 0xFF, 0xFF, 0x00);
166         uint u = c.u & mask.u;
167
168         for(; i < 16; i++)
169         {
170                 if (u != (m_color[i].u & mask.u))
171                 {
172                         return false;
173                 }
174         }
175         
176         return true;
177 }
178
179 /// Count number of unique colors in this color block.
180 uint ColorBlock::countUniqueColors() const
181 {
182         uint count = 0;
183
184         // @@ This does not have to be o(n^2)
185         for(int i = 0; i < 16; i++)
186         {
187                 bool unique = true;
188                 for(int j = 0; j < i; j++) {
189                         if( m_color[i] != m_color[j] ) {
190                                 unique = false;
191                         }
192                 }
193                 
194                 if( unique ) {
195                         count++;
196                 }
197         }
198         
199         return count;
200 }
201
202 /// Get average color of the block.
203 Color32 ColorBlock::averageColor() const
204 {
205         uint r, g, b, a;
206         r = g = b = a = 0;
207
208         for(uint i = 0; i < 16; i++) {
209                 r += m_color[i].r;
210                 g += m_color[i].g;
211                 b += m_color[i].b;
212                 a += m_color[i].a;
213         }
214         
215         return Color32(uint8(r / 16), uint8(g / 16), uint8(b / 16), uint8(a / 16));
216 }
217
218 /// Return true if the block is not fully opaque.
219 bool ColorBlock::hasAlpha() const
220 {
221         for (uint i = 0; i < 16; i++)
222         {
223                 if (m_color[i].a != 255) return true;
224         }
225         return false;
226 }
227
228
229 /// Get diameter color range.
230 void ColorBlock::diameterRange(Color32 * start, Color32 * end) const
231 {
232         Color32 c0, c1;
233         uint best_dist = 0;
234         
235         for(int i = 0; i < 16; i++) {
236                 for (int j = i+1; j < 16; j++) {
237                         uint dist = colorDistance(m_color[i], m_color[j]);
238                         if( dist > best_dist ) {
239                                 best_dist = dist;
240                                 c0 = m_color[i];
241                                 c1 = m_color[j];
242                         }
243                 }
244         }
245         
246         *start = c0;
247         *end = c1;
248 }
249
250 /// Get luminance color range.
251 void ColorBlock::luminanceRange(Color32 * start, Color32 * end) const
252 {
253         Color32 minColor, maxColor;
254         uint minLuminance, maxLuminance;
255         
256         maxLuminance = minLuminance = colorLuminance(m_color[0]);
257         
258         for(uint i = 1; i < 16; i++)
259         {
260                 uint luminance = colorLuminance(m_color[i]);
261                 
262                 if (luminance > maxLuminance) {
263                         maxLuminance = luminance;
264                         maxColor = m_color[i];
265                 }
266                 else if (luminance < minLuminance) {
267                         minLuminance = luminance;
268                         minColor = m_color[i];
269                 }
270         }
271
272         *start = minColor;
273         *end = maxColor;
274 }
275
276 /// Get color range based on the bounding box. 
277 void ColorBlock::boundsRange(Color32 * start, Color32 * end) const
278 {
279         Color32 minColor(255, 255, 255);
280         Color32 maxColor(0, 0, 0);
281
282         for(uint i = 0; i < 16; i++)
283         {
284                 if (m_color[i].r < minColor.r) { minColor.r = m_color[i].r; }
285                 if (m_color[i].g < minColor.g) { minColor.g = m_color[i].g; }
286                 if (m_color[i].b < minColor.b) { minColor.b = m_color[i].b; }
287                 if (m_color[i].r > maxColor.r) { maxColor.r = m_color[i].r; }
288                 if (m_color[i].g > maxColor.g) { maxColor.g = m_color[i].g; }
289                 if (m_color[i].b > maxColor.b) { maxColor.b = m_color[i].b; }
290         }
291
292         // Offset range by 1/16 of the extents
293         Color32 inset;
294         inset.r = (maxColor.r - minColor.r) >> 4;
295         inset.g = (maxColor.g - minColor.g) >> 4;
296         inset.b = (maxColor.b - minColor.b) >> 4;
297
298         minColor.r = (minColor.r + inset.r <= 255) ? minColor.r + inset.r : 255;
299         minColor.g = (minColor.g + inset.g <= 255) ? minColor.g + inset.g : 255;
300         minColor.b = (minColor.b + inset.b <= 255) ? minColor.b + inset.b : 255;
301
302         maxColor.r = (maxColor.r >= inset.r) ? maxColor.r - inset.r : 0;
303         maxColor.g = (maxColor.g >= inset.g) ? maxColor.g - inset.g : 0;
304         maxColor.b = (maxColor.b >= inset.b) ? maxColor.b - inset.b : 0;
305
306         *start = minColor;
307         *end = maxColor;
308 }
309
310 /// Get color range based on the bounding box. 
311 void ColorBlock::boundsRangeAlpha(Color32 * start, Color32 * end) const
312 {
313         Color32 minColor(255, 255, 255, 255);
314         Color32 maxColor(0, 0, 0, 0);
315
316         for(uint i = 0; i < 16; i++)
317         {
318                 if (m_color[i].r < minColor.r) { minColor.r = m_color[i].r; }
319                 if (m_color[i].g < minColor.g) { minColor.g = m_color[i].g; }
320                 if (m_color[i].b < minColor.b) { minColor.b = m_color[i].b; }
321                 if (m_color[i].a < minColor.a) { minColor.a = m_color[i].a; }
322                 if (m_color[i].r > maxColor.r) { maxColor.r = m_color[i].r; }
323                 if (m_color[i].g > maxColor.g) { maxColor.g = m_color[i].g; }
324                 if (m_color[i].b > maxColor.b) { maxColor.b = m_color[i].b; }
325                 if (m_color[i].a > maxColor.a) { maxColor.a = m_color[i].a; }
326         }
327
328         // Offset range by 1/16 of the extents
329         Color32 inset;
330         inset.r = (maxColor.r - minColor.r) >> 4;
331         inset.g = (maxColor.g - minColor.g) >> 4;
332         inset.b = (maxColor.b - minColor.b) >> 4;
333         inset.a = (maxColor.a - minColor.a) >> 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         minColor.a = (minColor.a + inset.a <= 255) ? minColor.a + inset.a : 255;
339
340         maxColor.r = (maxColor.r >= inset.r) ? maxColor.r - inset.r : 0;
341         maxColor.g = (maxColor.g >= inset.g) ? maxColor.g - inset.g : 0;
342         maxColor.b = (maxColor.b >= inset.b) ? maxColor.b - inset.b : 0;
343         maxColor.a = (maxColor.a >= inset.a) ? maxColor.a - inset.a : 0;
344         
345         *start = minColor;
346         *end = maxColor;
347 }
348
349 /// Sort colors by abosolute value in their 16 bit representation.
350 void ColorBlock::sortColorsByAbsoluteValue()
351 {
352         // Dummy selection sort.
353         for( uint a = 0; a < 16; a++ ) {
354                 uint max = a;
355                 Color16 cmax(m_color[a]);
356                 
357                 for( uint b = a+1; b < 16; b++ ) {
358                         Color16 cb(m_color[b]);
359                         
360                         if( cb.u > cmax.u ) {
361                                 max = b;
362                                 cmax = cb;
363                         }
364                 }
365                 swap( m_color[a], m_color[max] );
366         }
367 }