1 /**************************************************************************
3 * Copyright 2013-2014 RAD Game Tools and Valve Software
4 * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
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:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
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
25 **************************************************************************/
27 // File: vogl_dxt_fast.cpp
28 // Parts of this module are derived from RYG's excellent public domain DXTx compressor.
29 #include "vogl_core.h"
30 #include "vogl_dxt_fast.h"
31 #include "vogl_ryg_dxt.hpp"
37 static inline int mul_8bit(int a, int b)
40 return (t + (t >> 8)) >> 8;
43 static inline color_quad_u8 &unpack_color(color_quad_u8 &c, uint v)
45 uint rv = (v & 0xf800) >> 11;
46 uint gv = (v & 0x07e0) >> 5;
47 uint bv = (v & 0x001f) >> 0;
49 c.r = ryg_dxt::Expand5[rv];
50 c.g = ryg_dxt::Expand6[gv];
51 c.b = ryg_dxt::Expand5[bv];
57 static inline uint pack_color(const color_quad_u8 &c)
59 return (mul_8bit(c.r, 31) << 11) + (mul_8bit(c.g, 63) << 5) + mul_8bit(c.b, 31);
63 static inline void lerp_color(color_quad_u8 &result, const color_quad_u8 &p1, const color_quad_u8 &p2, uint f)
65 VOGL_ASSERT(f <= 255);
67 result.r = static_cast<uint8>(p1.r + mul_8bit(p2.r - p1.r, f));
68 result.g = static_cast<uint8>(p1.g + mul_8bit(p2.g - p1.g, f));
69 result.b = static_cast<uint8>(p1.b + mul_8bit(p2.b - p1.b, f));
73 static inline void eval_colors(color_quad_u8 *pColors, uint c0, uint c1)
75 unpack_color(pColors[0], c0);
76 unpack_color(pColors[1], c1);
79 lerp_color(pColors[2], pColors[0], pColors[1], 0x55);
80 lerp_color(pColors[3], pColors[0], pColors[1], 0xAA);
82 pColors[2].r = (pColors[0].r * 2 + pColors[1].r) / 3;
83 pColors[2].g = (pColors[0].g * 2 + pColors[1].g) / 3;
84 pColors[2].b = (pColors[0].b * 2 + pColors[1].b) / 3;
86 pColors[3].r = (pColors[1].r * 2 + pColors[0].r) / 3;
87 pColors[3].g = (pColors[1].g * 2 + pColors[0].g) / 3;
88 pColors[3].b = (pColors[1].b * 2 + pColors[0].b) / 3;
92 // false if all selectors equal
93 static bool match_block_colors(uint n, const color_quad_u8 *pBlock, const color_quad_u8 *pColors, uint8 *pSelectors)
95 int dirr = pColors[0].r - pColors[1].r;
96 int dirg = pColors[0].g - pColors[1].g;
97 int dirb = pColors[0].b - pColors[1].b;
100 for (int i = 0; i < 4; i++)
101 stops[i] = pColors[i].r * dirr + pColors[i].g * dirg + pColors[i].b * dirb;
104 int c0Point = stops[1] + stops[3];
105 int halfPoint = stops[3] + stops[2];
106 int c3Point = stops[2] + stops[0];
116 for (uint i = 0; i < n; i++)
118 int dot = pBlock[i].r * dirr + pBlock[i].g * dirg + pBlock[i].b * dirb;
122 s = (dot < c0Point) ? 1 : 3;
124 s = (dot < c3Point) ? 2 : 0;
128 if (s != pSelectors[0])
135 static bool optimize_block_colors(uint n, const color_quad_u8 *block, uint &max16, uint &min16, uint ave_color[3], float axis[3])
139 for (uint ch = 0; ch < 3; ch++)
141 const uint8 *bp = ((const uint8 *)block) + ch;
147 const uint l = n << 2;
148 for (uint i = 4; i < l; i += 4)
151 minv = math::minimum<int>(minv, bp[i]);
152 maxv = math::maximum<int>(maxv, bp[i]);
155 ave_color[ch] = static_cast<int>((muv + (n / 2)) / n);
160 if ((min[0] == max[0]) && (min[1] == max[1]) && (min[2] == max[2]))
163 // determine covariance matrix
165 for (int i = 0; i < 6; i++)
168 for (uint i = 0; i < n; i++)
170 double r = (int)block[i].r - (int)ave_color[0];
171 double g = (int)block[i].g - (int)ave_color[1];
172 double b = (int)block[i].b - (int)ave_color[2];
182 double covf[6], vfr, vfg, vfb;
183 for (int i = 0; i < 6; i++)
184 covf[i] = cov[i] * (1.0f / 255.0f);
186 vfr = max[0] - min[0];
187 vfg = max[1] - min[1];
188 vfb = max[2] - min[2];
190 static const uint nIterPower = 4;
191 for (uint iter = 0; iter < nIterPower; iter++)
193 double r = vfr * covf[0] + vfg * covf[1] + vfb * covf[2];
194 double g = vfr * covf[1] + vfg * covf[3] + vfb * covf[4];
195 double b = vfr * covf[2] + vfg * covf[4] + vfb * covf[5];
202 double magn = math::maximum(math::maximum(fabs(vfr), fabs(vfg)), fabs(vfb));
205 if (magn < 4.0f) // too small, default to luminance
211 axis[0] = (float)v_r;
212 axis[1] = (float)v_g;
213 axis[2] = (float)v_b;
217 magn = 512.0f / magn;
221 v_r = static_cast<int>(vfr);
222 v_g = static_cast<int>(vfg);
223 v_b = static_cast<int>(vfb);
225 axis[0] = (float)vfr;
226 axis[1] = (float)vfg;
227 axis[2] = (float)vfb;
230 int mind = block[0].r * v_r + block[0].g * v_g + block[0].b * v_b;
232 color_quad_u8 minp(block[0]);
233 color_quad_u8 maxp(block[0]);
235 for (uint i = 1; i < n; i++)
237 int dot = block[i].r * v_r + block[i].g * v_g + block[i].b * v_b;
252 max16 = pack_color(maxp);
253 min16 = pack_color(minp);
258 // The refinement function. (Clever code, part 2)
259 // Tries to optimize colors to suit block contents better.
260 // (By solving a least squares system via normal equations+Cramer's rule)
261 static bool refine_block(uint n, const color_quad_u8 *block, uint &max16, uint &min16, const uint8 *pSelectors)
263 static const int w1Tab[4] = { 3, 0, 2, 1 };
265 static const int prods_0[4] = { 0x00, 0x00, 0x02, 0x02 };
266 static const int prods_1[4] = { 0x00, 0x09, 0x01, 0x04 };
267 static const int prods_2[4] = { 0x09, 0x00, 0x04, 0x01 };
272 double At1_r, At1_g, At1_b;
273 double At2_r, At2_g, At2_b;
275 At1_r = At1_g = At1_b = 0;
276 At2_r = At2_g = At2_b = 0;
277 for (uint i = 0; i < n; i++)
279 double r = block[i].r;
280 double g = block[i].g;
281 double b = block[i].b;
282 int step = pSelectors[i];
284 int w1 = w1Tab[step];
286 akku_0 += prods_0[step];
287 akku_1 += prods_1[step];
288 akku_2 += prods_2[step];
297 At2_r = 3 * At2_r - At1_r;
298 At2_g = 3 * At2_g - At1_g;
299 At2_b = 3 * At2_b - At1_b;
305 double t = xx * yy - xy * xy;
306 if (!yy || !xx || (fabs(t) < .0000125f))
309 double frb = (3.0f * 31.0f / 255.0f) / t;
310 double fg = frb * (63.0f / 31.0f);
316 max16 = math::clamp<int>(static_cast<int>((At1_r * yy - At2_r * xy) * frb + 0.5f), 0, 31) << 11;
317 max16 |= math::clamp<int>(static_cast<int>((At1_g * yy - At2_g * xy) * fg + 0.5f), 0, 63) << 5;
318 max16 |= math::clamp<int>(static_cast<int>((At1_b * yy - At2_b * xy) * frb + 0.5f), 0, 31) << 0;
320 min16 = math::clamp<int>(static_cast<int>((At2_r * xx - At1_r * xy) * frb + 0.5f), 0, 31) << 11;
321 min16 |= math::clamp<int>(static_cast<int>((At2_g * xx - At1_g * xy) * fg + 0.5f), 0, 63) << 5;
322 min16 |= math::clamp<int>(static_cast<int>((At2_b * xx - At1_b * xy) * frb + 0.5f), 0, 31) << 0;
324 return (oldMin != min16) || (oldMax != max16);
327 // false if all selectors equal
328 static bool determine_selectors(uint n, const color_quad_u8 *block, uint min16, uint max16, uint8 *pSelectors)
330 color_quad_u8 color[4];
334 eval_colors(color, min16, max16);
336 return match_block_colors(n, block, color, pSelectors);
339 memset(pSelectors, 0, n);
343 static uint64_t determine_error(uint n, const color_quad_u8 *block, uint min16, uint max16, uint64_t early_out_error)
345 color_quad_u8 color[4];
347 eval_colors(color, min16, max16);
349 int dirr = color[0].r - color[1].r;
350 int dirg = color[0].g - color[1].g;
351 int dirb = color[0].b - color[1].b;
354 for (int i = 0; i < 4; i++)
355 stops[i] = color[i].r * dirr + color[i].g * dirg + color[i].b * dirb;
358 int c0Point = stops[1] + stops[3];
359 int halfPoint = stops[3] + stops[2];
360 int c3Point = stops[2] + stops[0];
366 uint64_t total_error = 0;
368 for (uint i = 0; i < n; i++)
370 const color_quad_u8 &a = block[i];
375 int dot = a.r * dirr + a.g * dirg + a.b * dirb;
378 s = (dot < c0Point) ? 1 : 3;
380 s = (dot < c3Point) ? 2 : 0;
383 const color_quad_u8 &b = color[s];
386 total_error += e * e;
389 total_error += e * e;
392 total_error += e * e;
394 if (total_error >= early_out_error)
401 static bool refine_endpoints(uint n, const color_quad_u8 *pBlock, uint &low16, uint &high16, uint8 *pSelectors)
403 bool optimized = false;
405 const int limits[3] = { 31, 63, 31 };
407 for (uint trial = 0; trial < 2; trial++)
409 color_quad_u8 color[4];
410 eval_colors(color, low16, high16);
412 uint64_t total_error[3] = { 0, 0, 0 };
414 for (uint i = 0; i < n; i++)
416 const color_quad_u8 &a = pBlock[i];
418 const uint s = pSelectors[i];
419 const color_quad_u8 &b = color[s];
422 total_error[0] += e * e;
425 total_error[1] += e * e;
428 total_error[2] += e * e;
431 color_quad_u8 endpoints[2];
432 endpoints[0] = dxt1_block::unpack_color((uint16)low16, false);
433 endpoints[1] = dxt1_block::unpack_color((uint16)high16, false);
435 color_quad_u8 expanded_endpoints[2];
436 expanded_endpoints[0] = dxt1_block::unpack_color((uint16)low16, true);
437 expanded_endpoints[1] = dxt1_block::unpack_color((uint16)high16, true);
439 bool trial_optimized = false;
441 for (uint axis = 0; axis < 3; axis++)
443 if (!total_error[axis])
446 const sU8 *const pExpand = (axis == 1) ? ryg_dxt::Expand6 : ryg_dxt::Expand5;
448 for (uint e = 0; e < 2; e++)
451 v[e ^ 1] = expanded_endpoints[e ^ 1][axis];
453 for (int t = -1; t <= 1; t += 2)
455 int a = endpoints[e][axis] + t;
456 if ((a < 0) || (a > limits[axis]))
461 //int delta = v[1] - v[0];
462 //v[2] = v[0] + mul_8bit(delta, 0x55);
463 //v[3] = v[0] + mul_8bit(delta, 0xAA);
465 v[2] = (v[0] * 2 + v[1]) / 3;
466 v[3] = (v[0] + v[1] * 2) / 3;
468 uint64_t axis_error = 0;
470 for (uint i = 0; i < n; i++)
472 const color_quad_u8 &p = pBlock[i];
474 int e = v[pSelectors[i]] - p[axis];
478 if (axis_error >= total_error[axis])
482 if (axis_error < total_error[axis])
484 //total_error[axis] = axis_error;
486 endpoints[e][axis] = (uint8)a;
487 expanded_endpoints[e][axis] = (uint8)v[e];
490 high16 = dxt1_block::pack_color(endpoints[1], false);
492 low16 = dxt1_block::pack_color(endpoints[0], false);
494 determine_selectors(n, pBlock, low16, high16, pSelectors);
496 eval_colors(color, low16, high16);
498 utils::zero_object(total_error);
500 for (uint i = 0; i < n; i++)
502 const color_quad_u8 &a = pBlock[i];
504 const uint s = pSelectors[i];
505 const color_quad_u8 &b = color[s];
508 total_error[0] += e * e;
511 total_error[1] += e * e;
514 total_error[2] += e * e;
517 trial_optimized = true;
525 if (!trial_optimized)
535 static void refine_endpoints2(uint n, const color_quad_u8 *pBlock, uint &low16, uint &high16, uint8 *pSelectors, float axis[3])
537 uint64_t orig_error = determine_error(n, pBlock, low16, high16, cUINT64_MAX);
541 float l = 1.0f / sqrt(axis[0] * axis[0] + axis[1] * axis[1] + axis[2] * axis[2]);
542 vec3F principle_axis(axis[0] * l, axis[1] * l, axis[2] * l);
544 const float dist_per_trial = 0.027063293f;
546 const uint cMaxProbeRange = 8;
547 uint probe_low[cMaxProbeRange * 2 + 1];
548 uint probe_high[cMaxProbeRange * 2 + 1];
553 const uint num_trials = probe_range * 2 + 1;
555 vec3F scaled_principle_axis(principle_axis * dist_per_trial);
556 scaled_principle_axis[0] *= 31.0f;
557 scaled_principle_axis[1] *= 63.0f;
558 scaled_principle_axis[2] *= 31.0f;
559 vec3F initial_ofs(scaled_principle_axis * (float)-probe_range);
560 initial_ofs[0] += .5f;
561 initial_ofs[1] += .5f;
562 initial_ofs[2] += .5f;
564 uint64_t cur_error = orig_error;
566 for (uint iter = 0; iter < num_iters; iter++)
568 color_quad_u8 endpoints[2];
570 endpoints[0] = dxt1_block::unpack_color((uint16)low16, false);
571 endpoints[1] = dxt1_block::unpack_color((uint16)high16, false);
573 vec3F low_color(endpoints[0][0], endpoints[0][1], endpoints[0][2]);
574 vec3F high_color(endpoints[1][0], endpoints[1][1], endpoints[1][2]);
576 vec3F probe_low_color(low_color + initial_ofs);
577 for (uint i = 0; i < num_trials; i++)
579 int r = math::clamp((int)floor(probe_low_color[0]), 0, 31);
580 int g = math::clamp((int)floor(probe_low_color[1]), 0, 63);
581 int b = math::clamp((int)floor(probe_low_color[2]), 0, 31);
582 probe_low[i] = b | (g << 5U) | (r << 11U);
584 probe_low_color += scaled_principle_axis;
587 vec3F probe_high_color(high_color + initial_ofs);
588 for (uint i = 0; i < num_trials; i++)
590 int r = math::clamp((int)floor(probe_high_color[0]), 0, 31);
591 int g = math::clamp((int)floor(probe_high_color[1]), 0, 63);
592 int b = math::clamp((int)floor(probe_high_color[2]), 0, 31);
593 probe_high[i] = b | (g << 5U) | (r << 11U);
595 probe_high_color += scaled_principle_axis;
599 uint best_h = high16;
605 uint64_t hash[cMaxHash];
606 for (uint i = 0; i < cMaxHash; i++)
609 uint c = best_l | (best_h << 16);
610 c = fast_hash(&c, sizeof(c));
611 hash[(c >> 6) & 3] = 1ULL << (c & 63);
613 for (uint i = 0; i < num_trials; i++)
615 for (uint j = 0; j < num_trials; j++)
617 uint l = probe_low[i];
618 uint h = probe_high[j];
622 uint c = l | (h << 16);
623 c = fast_hash(&c, sizeof(c));
624 uint64_t mask = 1ULL << (c & 63);
625 uint ofs = (c >> 6) & 3;
626 if (hash[ofs] & mask)
631 uint64_t new_error = determine_error(n, pBlock, l, h, cur_error);
632 if (new_error < cur_error)
636 cur_error = new_error;
641 bool improved = false;
643 if ((best_l != low16) || (best_h != high16))
648 determine_selectors(n, pBlock, low16, high16, pSelectors);
652 if (refine_endpoints(n, pBlock, low16, high16, pSelectors))
656 uint64_t cur_error = determine_error(n, pBlock, low16, high16, cUINT64_MAX);
666 //uint64_t end_error = determine_error(n, pBlock, low16, high16, UINT64_MAX);
667 //if (end_error > orig_error) DebugBreak();
670 static void compress_solid_block(uint n, uint ave_color[3], uint &low16, uint &high16, uint8 *pSelectors)
672 uint r = ave_color[0];
673 uint g = ave_color[1];
674 uint b = ave_color[2];
676 memset(pSelectors, 2, n);
678 low16 = (ryg_dxt::OMatch5[r][0] << 11) | (ryg_dxt::OMatch6[g][0] << 5) | ryg_dxt::OMatch5[b][0];
679 high16 = (ryg_dxt::OMatch5[r][1] << 11) | (ryg_dxt::OMatch6[g][1] << 5) | ryg_dxt::OMatch5[b][1];
682 void compress_color_block(uint n, const color_quad_u8 *block, uint &low16, uint &high16, uint8 *pSelectors, bool refine)
684 VOGL_ASSERT((n & 15) == 0);
689 if (!optimize_block_colors(n, block, low16, high16, ave_color, axis))
691 compress_solid_block(n, ave_color, low16, high16, pSelectors);
695 if (!determine_selectors(n, block, low16, high16, pSelectors))
696 compress_solid_block(n, ave_color, low16, high16, pSelectors);
699 if (refine_block(n, block, low16, high16, pSelectors))
700 determine_selectors(n, block, low16, high16, pSelectors);
703 refine_endpoints2(n, block, low16, high16, pSelectors, axis);
709 utils::swap(low16, high16);
710 for (uint i = 0; i < n; i++)
715 void compress_color_block(dxt1_block *pDXT1_block, const color_quad_u8 *pBlock, bool refine)
717 uint8 color_selectors[16];
719 dxt_fast::compress_color_block(16, pBlock, low16, high16, color_selectors, refine);
721 pDXT1_block->set_low_color(static_cast<uint16>(low16));
722 pDXT1_block->set_high_color(static_cast<uint16>(high16));
725 for (int i = 15; i >= 0; i--)
728 mask |= color_selectors[i];
731 pDXT1_block->m_selectors[0] = (uint8)(mask & 0xFF);
732 pDXT1_block->m_selectors[1] = (uint8)((mask >> 8) & 0xFF);
733 pDXT1_block->m_selectors[2] = (uint8)((mask >> 16) & 0xFF);
734 pDXT1_block->m_selectors[3] = (uint8)((mask >> 24) & 0xFF);
737 void compress_alpha_block(uint n, const color_quad_u8 *block, uint &low8, uint &high8, uint8 *pSelectors, uint comp_index)
740 min = max = block[0][comp_index];
742 for (uint i = 1; i < n; i++)
744 min = math::minimum<int>(min, block[i][comp_index]);
745 max = math::maximum<int>(max, block[i][comp_index]);
751 int dist = max - min;
752 int bias = min * 7 - (dist >> 1);
753 int dist4 = dist * 4;
754 int dist2 = dist * 2;
756 for (uint i = 0; i < n; i++)
758 int a = block[i][comp_index] * 7 - bias;
761 t = (dist4 - a) >> 31;
764 t = (dist2 - a) >> 31;
767 t = (dist - a) >> 31;
773 pSelectors[i] = static_cast<uint8>(ind);
777 void compress_alpha_block(dxt5_block *pDXT5_block, const color_quad_u8 *pBlock, uint comp_index)
782 compress_alpha_block(16, pBlock, low8, high8, selectors, comp_index);
784 pDXT5_block->set_low_alpha(low8);
785 pDXT5_block->set_high_alpha(high8);
789 uint8 *pDst = pDXT5_block->m_selectors;
791 for (uint i = 0; i < 16; i++)
793 mask |= (selectors[i] << bits);
795 if ((bits += 3) >= 8)
797 *pDst++ = static_cast<uint8>(mask);
804 void find_representative_colors(uint n, const color_quad_u8 *pBlock, color_quad_u8 &lo, color_quad_u8 &hi)
811 for (uint i = 0; i < n; i++)
813 ave64[0] += pBlock[i].r;
814 ave64[1] += pBlock[i].g;
815 ave64[2] += pBlock[i].b;
819 ave[0] = static_cast<uint>((ave64[0] + (n / 2)) / n);
820 ave[1] = static_cast<uint>((ave64[1] + (n / 2)) / n);
821 ave[2] = static_cast<uint>((ave64[2] + (n / 2)) / n);
823 int furthest_dist = -1;
824 uint furthest_index = 0;
825 for (uint i = 0; i < n; i++)
827 int r = pBlock[i].r - ave[0];
828 int g = pBlock[i].g - ave[1];
829 int b = pBlock[i].b - ave[2];
830 int dist = r * r + g * g + b * b;
831 if (dist > furthest_dist)
833 furthest_dist = dist;
838 color_quad_u8 lo_color(pBlock[furthest_index]);
842 for (uint i = 0; i < n; i++)
844 int r = pBlock[i].r - lo_color.r;
845 int g = pBlock[i].g - lo_color.g;
846 int b = pBlock[i].b - lo_color.b;
847 int dist = r * r + g * g + b * b;
855 color_quad_u8 hi_color(pBlock[opp_index]);
857 for (uint i = 0; i < 3; i++)
859 lo_color[i] = static_cast<uint8>((lo_color[i] + ave[i]) >> 1);
860 hi_color[i] = static_cast<uint8>((hi_color[i] + ave[i]) >> 1);
863 const uint cMaxIters = 4;
864 for (uint iter_index = 0; iter_index < cMaxIters; iter_index++)
866 if ((lo_color[0] == hi_color[0]) && (lo_color[1] == hi_color[1]) && (lo_color[2] == hi_color[2]))
869 uint64_t new_color[2][3];
872 utils::zero_object(new_color);
873 utils::zero_object(weight);
875 int vec_r = hi_color[0] - lo_color[0];
876 int vec_g = hi_color[1] - lo_color[1];
877 int vec_b = hi_color[2] - lo_color[2];
879 int lo_dot = vec_r * lo_color[0] + vec_g * lo_color[1] + vec_b * lo_color[2];
880 int hi_dot = vec_r * hi_color[0] + vec_g * hi_color[1] + vec_b * hi_color[2];
881 int mid_dot = lo_dot + hi_dot;
887 for (uint i = 0; i < n; i++)
889 const color_quad_u8 &c = pBlock[i];
891 const int dot = c[0] * vec_r + c[1] * vec_g + c[2] * vec_b;
892 const uint match_index = (dot > mid_dot);
894 new_color[match_index][0] += c.r;
895 new_color[match_index][1] += c.g;
896 new_color[match_index][2] += c.b;
897 weight[match_index]++;
900 if ((!weight[0]) || (!weight[1]))
903 uint8 new_color8[2][3];
905 for (uint j = 0; j < 2; j++)
906 for (uint i = 0; i < 3; i++)
907 new_color8[j][i] = static_cast<uint8>((new_color[j][i] + (weight[j] / 2)) / weight[j]);
909 if ((new_color8[0][0] == lo_color[0]) && (new_color8[0][1] == lo_color[1]) && (new_color8[0][2] == lo_color[2]) &&
910 (new_color8[1][0] == hi_color[0]) && (new_color8[1][1] == hi_color[1]) && (new_color8[1][2] == hi_color[2]))
913 for (uint i = 0; i < 3; i++)
915 lo_color[i] = new_color8[0][i];
916 hi_color[i] = new_color8[1][i];
920 uint energy[2] = { 0, 0 };
921 for (uint i = 0; i < 3; i++)
923 energy[0] += lo_color[i] * lo_color[i];
924 energy[1] += hi_color[i] * hi_color[i];
927 if (energy[0] > energy[1])
928 utils::swap(lo_color, hi_color);
934 } // namespace dxt_fast