summaryrefslogtreecommitdiffstats
path: root/plug-ins/file-dds/dxt.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--plug-ins/file-dds/dxt.c1526
1 files changed, 1526 insertions, 0 deletions
diff --git a/plug-ins/file-dds/dxt.c b/plug-ins/file-dds/dxt.c
new file mode 100644
index 0000000..28d407a
--- /dev/null
+++ b/plug-ins/file-dds/dxt.c
@@ -0,0 +1,1526 @@
+/*
+ * DDS GIMP plugin
+ *
+ * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>,
+ * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301, USA.
+ */
+
+/*
+ * Parts of this code have been generously released in the public domain
+ * by Fabian 'ryg' Giesen. The original code can be found (at the time
+ * of writing) here: http://mollyrocket.com/forums/viewtopic.php?t=392
+ *
+ * For more information about this code, see the README.dxt file that
+ * came with the source.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <glib.h>
+
+#include "dds.h"
+#include "dxt.h"
+#include "endian_rw.h"
+#include "mipmap.h"
+#include "imath.h"
+#include "vec.h"
+
+#include "dxt_tables.h"
+
+#define SWAP(a, b) do { typeof(a) t; t = a; a = b; b = t; } while(0)
+
+/* SIMD constants */
+static const vec4_t V4ZERO = VEC4_CONST1(0.0f);
+static const vec4_t V4ONE = VEC4_CONST1(1.0f);
+static const vec4_t V4HALF = VEC4_CONST1(0.5f);
+static const vec4_t V4ONETHIRD = VEC4_CONST3(1.0f / 3.0f, 1.0f / 3.0f, 1.0f / 3.0f);
+static const vec4_t V4TWOTHIRDS = VEC4_CONST3(2.0f / 3.0f, 2.0f / 3.0f, 2.0f / 3.0f);
+static const vec4_t V4GRID = VEC4_CONST3(31.0f, 63.0f, 31.0f);
+static const vec4_t V4GRIDRCP = VEC4_CONST3(1.0f / 31.0f, 1.0f / 63.0f, 1.0f / 31.0f);
+static const vec4_t V4EPSILON = VEC4_CONST1(1e-04f);
+
+typedef struct
+{
+ unsigned int single;
+ unsigned int alphamask;
+ vec4_t points[16];
+ vec4_t palette[4];
+ vec4_t max;
+ vec4_t min;
+ vec4_t metric;
+} dxtblock_t;
+
+/* extract 4x4 BGRA block */
+static void
+extract_block (const unsigned char *src,
+ int x,
+ int y,
+ int w,
+ int h,
+ unsigned char *block)
+{
+ int i, j;
+ int bw = MIN(w - x, 4);
+ int bh = MIN(h - y, 4);
+ int bx, by;
+ const int rem[] =
+ {
+ 0, 0, 0, 0,
+ 0, 1, 0, 1,
+ 0, 1, 2, 0,
+ 0, 1, 2, 3
+ };
+
+ for (i = 0; i < 4; ++i)
+ {
+ by = rem[(bh - 1) * 4 + i] + y;
+ for (j = 0; j < 4; ++j)
+ {
+ bx = rem[(bw - 1) * 4 + j] + x;
+ block[(i * 4 * 4) + (j * 4) + 0] =
+ src[(by * (w * 4)) + (bx * 4) + 0];
+ block[(i * 4 * 4) + (j * 4) + 1] =
+ src[(by * (w * 4)) + (bx * 4) + 1];
+ block[(i * 4 * 4) + (j * 4) + 2] =
+ src[(by * (w * 4)) + (bx * 4) + 2];
+ block[(i * 4 * 4) + (j * 4) + 3] =
+ src[(by * (w * 4)) + (bx * 4) + 3];
+ }
+ }
+}
+
+/* pack BGR8 to RGB565 */
+static inline unsigned short
+pack_rgb565 (const unsigned char *c)
+{
+ return (mul8bit(c[2], 31) << 11) |
+ (mul8bit(c[1], 63) << 5) |
+ (mul8bit(c[0], 31) );
+}
+
+/* unpack RGB565 to BGR */
+static void
+unpack_rgb565 (unsigned char *dst,
+ unsigned short v)
+{
+ int r = (v >> 11) & 0x1f;
+ int g = (v >> 5) & 0x3f;
+ int b = (v ) & 0x1f;
+
+ dst[0] = (b << 3) | (b >> 2);
+ dst[1] = (g << 2) | (g >> 4);
+ dst[2] = (r << 3) | (r >> 2);
+}
+
+/* linear interpolation at 1/3 point between a and b */
+static void
+lerp_rgb13 (unsigned char *dst,
+ unsigned char *a,
+ unsigned char *b)
+{
+#if 0
+ dst[0] = blerp(a[0], b[0], 0x55);
+ dst[1] = blerp(a[1], b[1], 0x55);
+ dst[2] = blerp(a[2], b[2], 0x55);
+#else
+ /*
+ * according to the S3TC/DX10 specs, this is the correct way to do the
+ * interpolation (with no rounding bias)
+ *
+ * dst = (2 * a + b) / 3;
+ */
+ dst[0] = (2 * a[0] + b[0]) / 3;
+ dst[1] = (2 * a[1] + b[1]) / 3;
+ dst[2] = (2 * a[2] + b[2]) / 3;
+#endif
+}
+
+static void
+vec4_endpoints_to_565 (int *start,
+ int *end,
+ const vec4_t a,
+ const vec4_t b)
+{
+ int c[8] __attribute__((aligned(16)));
+ vec4_t ta = a * V4GRID + V4HALF;
+ vec4_t tb = b * V4GRID + V4HALF;
+
+#ifdef USE_SSE
+# ifdef __SSE2__
+ const __m128i C565 = _mm_setr_epi16(31, 63, 31, 0, 31, 63, 31, 0);
+ __m128i ia = _mm_cvttps_epi32(ta);
+ __m128i ib = _mm_cvttps_epi32(tb);
+ __m128i zero = _mm_setzero_si128();
+ __m128i words = _mm_packs_epi32(ia, ib);
+ words = _mm_min_epi16(C565, _mm_max_epi16(zero, words));
+ *((__m128i *)&c[0]) = _mm_unpacklo_epi16(words, zero);
+ *((__m128i *)&c[4]) = _mm_unpackhi_epi16(words, zero);
+# else
+ const __m64 C565 = _mm_setr_pi16(31, 63, 31, 0);
+ __m64 lo, hi, c0, c1;
+ __m64 zero = _mm_setzero_si64();
+ lo = _mm_cvttps_pi32(ta);
+ hi = _mm_cvttps_pi32(_mm_movehl_ps(ta, ta));
+ c0 = _mm_packs_pi32(lo, hi);
+ lo = _mm_cvttps_pi32(tb);
+ hi = _mm_cvttps_pi32(_mm_movehl_ps(tb, tb));
+ c1 = _mm_packs_pi32(lo, hi);
+ c0 = _mm_min_pi16(C565, _mm_max_pi16(zero, c0));
+ c1 = _mm_min_pi16(C565, _mm_max_pi16(zero, c1));
+ *((__m64 *)&c[0]) = _mm_unpacklo_pi16(c0, zero);
+ *((__m64 *)&c[2]) = _mm_unpackhi_pi16(c0, zero);
+ *((__m64 *)&c[4]) = _mm_unpacklo_pi16(c1, zero);
+ *((__m64 *)&c[6]) = _mm_unpackhi_pi16(c1, zero);
+ _mm_empty();
+# endif
+#else
+ c[0] = (int)ta[0]; c[4] = (int)tb[0];
+ c[1] = (int)ta[1]; c[5] = (int)tb[1];
+ c[2] = (int)ta[2]; c[6] = (int)tb[2];
+ c[0] = MIN(31, MAX(0, c[0]));
+ c[1] = MIN(63, MAX(0, c[1]));
+ c[2] = MIN(31, MAX(0, c[2]));
+ c[4] = MIN(31, MAX(0, c[4]));
+ c[5] = MIN(63, MAX(0, c[5]));
+ c[6] = MIN(31, MAX(0, c[6]));
+#endif
+
+ *start = ((c[2] << 11) | (c[1] << 5) | c[0]);
+ *end = ((c[6] << 11) | (c[5] << 5) | c[4]);
+}
+
+static void
+dxtblock_init (dxtblock_t *dxtb,
+ const unsigned char *block,
+ int flags)
+{
+ int i, c0, c;
+ int bc1 = (flags & DXT_BC1);
+ float x, y, z;
+ vec4_t min, max, center, t, cov, inset;
+
+ dxtb->single = 1;
+ dxtb->alphamask = 0;
+
+ if(flags & DXT_PERCEPTUAL)
+ /* ITU-R BT.709 luma coefficients */
+ dxtb->metric = vec4_set(0.2126f, 0.7152f, 0.0722f, 0.0f);
+ else
+ dxtb->metric = vec4_set(1.0f, 1.0f, 1.0f, 0.0f);
+
+ c0 = GETL24(block);
+
+ for (i = 0; i < 16; ++i)
+ {
+ if (bc1 && (block[4 * i + 3] < 128))
+ dxtb->alphamask |= (3 << (2 * i));
+
+ x = (float)block[4 * i + 0] / 255.0f;
+ y = (float)block[4 * i + 1] / 255.0f;
+ z = (float)block[4 * i + 2] / 255.0f;
+
+ dxtb->points[i] = vec4_set(x, y, z, 0);
+
+ c = GETL24(&block[4 * i]);
+ dxtb->single = dxtb->single && (c == c0);
+ }
+
+ // no need to continue if this is a single color block
+ if (dxtb->single)
+ return;
+
+ min = vec4_set1(1.0f);
+ max = vec4_zero();
+
+ // get bounding box extents
+ for (i = 0; i < 16; ++i)
+ {
+ min = vec4_min(min, dxtb->points[i]);
+ max = vec4_max(max, dxtb->points[i]);
+ }
+
+ // select diagonal
+ center = (max + min) * V4HALF;
+ cov = vec4_zero();
+ for (i = 0; i < 16; ++i)
+ {
+ t = dxtb->points[i] - center;
+ cov += t * vec4_splatz(t);
+ }
+
+#ifdef USE_SSE
+ {
+ __m128 mask, tmp;
+ // get mask
+ mask = _mm_cmplt_ps(cov, _mm_setzero_ps());
+ // clear high bits (z, w)
+ mask = _mm_movelh_ps(mask, _mm_setzero_ps());
+ // mask and combine
+ tmp = _mm_or_ps(_mm_and_ps(mask, min), _mm_andnot_ps(mask, max));
+ min = _mm_or_ps(_mm_and_ps(mask, max), _mm_andnot_ps(mask, min));
+ max = tmp;
+ }
+#else
+ {
+ float x0, x1, y0, y1;
+ x0 = max[0];
+ y0 = max[1];
+ x1 = min[0];
+ y1 = min[1];
+
+ if (cov[0] < 0) SWAP(x0, x1);
+ if (cov[1] < 0) SWAP(y0, y1);
+
+ max[0] = x0;
+ max[1] = y0;
+ min[0] = x1;
+ min[1] = y1;
+ }
+#endif
+
+ // inset bounding box and clamp to [0,1]
+ inset = (max - min) * vec4_set1(1.0f / 16.0f) - vec4_set1((8.0f / 255.0f) / 16.0f);
+ max = vec4_min(V4ONE, vec4_max(V4ZERO, max - inset));
+ min = vec4_min(V4ONE, vec4_max(V4ZERO, min + inset));
+
+ // clamp to color space and save
+ dxtb->max = vec4_trunc(V4GRID * max + V4HALF) * V4GRIDRCP;
+ dxtb->min = vec4_trunc(V4GRID * min + V4HALF) * V4GRIDRCP;
+}
+
+static void
+construct_palette3 (dxtblock_t *dxtb)
+{
+ dxtb->palette[0] = dxtb->max;
+ dxtb->palette[1] = dxtb->min;
+ dxtb->palette[2] = (dxtb->max * V4HALF) + (dxtb->min * V4HALF);
+ dxtb->palette[3] = vec4_zero();
+}
+
+static void
+construct_palette4 (dxtblock_t *dxtb)
+{
+ dxtb->palette[0] = dxtb->max;
+ dxtb->palette[1] = dxtb->min;
+ dxtb->palette[2] = (dxtb->max * V4TWOTHIRDS) + (dxtb->min * V4ONETHIRD );
+ dxtb->palette[3] = (dxtb->max * V4ONETHIRD ) + (dxtb->min * V4TWOTHIRDS);
+}
+
+/*
+ * from nvidia-texture-tools; see LICENSE.nvtt for copyright information
+ */
+static void
+optimize_endpoints3 (dxtblock_t *dxtb,
+ unsigned int indices,
+ vec4_t *max,
+ vec4_t *min)
+{
+ float alpha, beta;
+ vec4_t alpha2_sum, alphax_sum;
+ vec4_t beta2_sum, betax_sum;
+ vec4_t alphabeta_sum, a, b, factor;
+ int i, bits;
+
+ alpha2_sum = beta2_sum = alphabeta_sum = vec4_zero();
+ alphax_sum = vec4_zero();
+ betax_sum = vec4_zero();
+
+ for (i = 0; i < 16; ++i)
+ {
+ bits = indices >> (2 * i);
+
+ // skip alpha pixels
+ if ((bits & 3) == 3)
+ continue;
+
+ beta = (float)(bits & 1);
+ if (bits & 2)
+ beta = 0.5f;
+ alpha = 1.0f - beta;
+
+ a = vec4_set1(alpha);
+ b = vec4_set1(beta);
+ alpha2_sum += a * a;
+ beta2_sum += b * b;
+ alphabeta_sum += a * b;
+ alphax_sum += dxtb->points[i] * a;
+ betax_sum += dxtb->points[i] * b;
+ }
+
+ factor = alpha2_sum * beta2_sum - alphabeta_sum * alphabeta_sum;
+ if (vec4_cmplt(factor, V4EPSILON))
+ return;
+ factor = vec4_rcp(factor);
+
+ a = (alphax_sum * beta2_sum - betax_sum * alphabeta_sum) * factor;
+ b = (betax_sum * alpha2_sum - alphax_sum * alphabeta_sum) * factor;
+
+ // clamp to the color space
+ a = vec4_min(V4ONE, vec4_max(V4ZERO, a));
+ b = vec4_min(V4ONE, vec4_max(V4ZERO, b));
+ a = vec4_trunc(V4GRID * a + V4HALF) * V4GRIDRCP;
+ b = vec4_trunc(V4GRID * b + V4HALF) * V4GRIDRCP;
+
+ *max = a;
+ *min = b;
+}
+
+/*
+ * from nvidia-texture-tools; see LICENSE.nvtt for copyright information
+ */
+static void
+optimize_endpoints4 (dxtblock_t *dxtb,
+ unsigned int indices,
+ vec4_t *max,
+ vec4_t *min)
+{
+ float alpha, beta;
+ vec4_t alpha2_sum, alphax_sum;
+ vec4_t beta2_sum, betax_sum;
+ vec4_t alphabeta_sum, a, b, factor;
+ int i, bits;
+
+ alpha2_sum = beta2_sum = alphabeta_sum = vec4_zero();
+ alphax_sum = vec4_zero();
+ betax_sum = vec4_zero();
+
+ for (i = 0; i < 16; ++i)
+ {
+ bits = indices >> (2 * i);
+
+ beta = (float)(bits & 1);
+ if (bits & 2)
+ beta = (1.0f + beta) / 3.0f;
+ alpha = 1.0f - beta;
+
+ a = vec4_set1(alpha);
+ b = vec4_set1(beta);
+ alpha2_sum += a * a;
+ beta2_sum += b * b;
+ alphabeta_sum += a * b;
+ alphax_sum += dxtb->points[i] * a;
+ betax_sum += dxtb->points[i] * b;
+ }
+
+ factor = alpha2_sum * beta2_sum - alphabeta_sum * alphabeta_sum;
+ if (vec4_cmplt(factor, V4EPSILON))
+ return;
+ factor = vec4_rcp(factor);
+
+ a = (alphax_sum * beta2_sum - betax_sum * alphabeta_sum) * factor;
+ b = (betax_sum * alpha2_sum - alphax_sum * alphabeta_sum) * factor;
+
+ // clamp to the color space
+ a = vec4_min(V4ONE, vec4_max(V4ZERO, a));
+ b = vec4_min(V4ONE, vec4_max(V4ZERO, b));
+ a = vec4_trunc(V4GRID * a + V4HALF) * V4GRIDRCP;
+ b = vec4_trunc(V4GRID * b + V4HALF) * V4GRIDRCP;
+
+ *max = a;
+ *min = b;
+}
+
+static unsigned int
+match_colors3 (dxtblock_t *dxtb)
+{
+ int i, idx;
+ unsigned int indices = 0;
+ vec4_t t0, t1, t2;
+#ifdef USE_SSE
+ vec4_t d, bits, zero = _mm_setzero_ps();
+ int mask;
+#else
+ float d0, d1, d2;
+#endif
+
+ // match each point to the closest color
+ for (i = 0; i < 16; ++i)
+ {
+ // skip alpha pixels
+ if (((dxtb->alphamask >> (2 * i)) & 3) == 3)
+ {
+ indices |= (3 << (2 * i));
+ continue;
+ }
+
+ t0 = (dxtb->points[i] - dxtb->palette[0]) * dxtb->metric;
+ t1 = (dxtb->points[i] - dxtb->palette[1]) * dxtb->metric;
+ t2 = (dxtb->points[i] - dxtb->palette[2]) * dxtb->metric;
+
+#ifdef USE_SSE
+ _MM_TRANSPOSE4_PS(t0, t1, t2, zero);
+ d = t0 * t0 + t1 * t1 + t2 * t2;
+ bits = _mm_cmplt_ps(_mm_shuffle_ps(d, d, _MM_SHUFFLE(3, 1, 0, 0)),
+ _mm_shuffle_ps(d, d, _MM_SHUFFLE(3, 2, 2, 1)));
+ mask = _mm_movemask_ps(bits);
+ if((mask & 3) == 3) idx = 0;
+ else if(mask & 4) idx = 1;
+ else idx = 2;
+#else
+ d0 = vec4_dot(t0, t0);
+ d1 = vec4_dot(t1, t1);
+ d2 = vec4_dot(t2, t2);
+
+ if ((d0 < d1) && (d0 < d2))
+ idx = 0;
+ else if (d1 < d2)
+ idx = 1;
+ else
+ idx = 2;
+#endif
+
+ indices |= (idx << (2 * i));
+ }
+
+ return indices;
+}
+
+static unsigned int
+match_colors4 (dxtblock_t *dxtb)
+{
+ int i;
+ unsigned int idx, indices = 0;
+ unsigned int b0, b1, b2, b3, b4;
+ unsigned int x0, x1, x2;
+ vec4_t t0, t1, t2, t3;
+#ifdef USE_SSE
+ vec4_t d;
+#else
+ float d[4];
+#endif
+
+ // match each point to the closest color
+ for (i = 0; i < 16; ++i)
+ {
+ t0 = (dxtb->points[i] - dxtb->palette[0]) * dxtb->metric;
+ t1 = (dxtb->points[i] - dxtb->palette[1]) * dxtb->metric;
+ t2 = (dxtb->points[i] - dxtb->palette[2]) * dxtb->metric;
+ t3 = (dxtb->points[i] - dxtb->palette[3]) * dxtb->metric;
+
+#ifdef USE_SSE
+ _MM_TRANSPOSE4_PS(t0, t1, t2, t3);
+ d = t0 * t0 + t1 * t1 + t2 * t2;
+#else
+ d[0] = vec4_dot(t0, t0);
+ d[1] = vec4_dot(t1, t1);
+ d[2] = vec4_dot(t2, t2);
+ d[3] = vec4_dot(t3, t3);
+#endif
+
+ b0 = d[0] > d[3];
+ b1 = d[1] > d[2];
+ b2 = d[0] > d[2];
+ b3 = d[1] > d[3];
+ b4 = d[2] > d[3];
+
+ x0 = b1 & b2;
+ x1 = b0 & b3;
+ x2 = b0 & b4;
+
+ idx = x2 | ((x0 | x1) << 1);
+
+ indices |= (idx << (2 * i));
+ }
+
+ return indices;
+}
+
+static float
+compute_error3 (dxtblock_t *dxtb,
+ unsigned int indices)
+{
+ int i, idx;
+ float error = 0;
+ vec4_t t;
+
+ // compute error
+ for (i = 0; i < 16; ++i)
+ {
+ idx = (indices >> (2 * i)) & 3;
+ // skip alpha pixels
+ if(idx == 3)
+ continue;
+ t = (dxtb->points[i] - dxtb->palette[idx]) * dxtb->metric;
+ error += vec4_dot(t, t);
+ }
+
+ return error;
+}
+
+static float
+compute_error4 (dxtblock_t *dxtb,
+ unsigned int indices)
+{
+ int i, idx;
+ float error = 0;
+
+#ifdef USE_SSE
+ vec4_t a0, a1, a2, a3;
+ vec4_t b0, b1, b2, b3;
+ vec4_t d;
+
+ for (i = 0; i < 4; ++i)
+ {
+ idx = indices >> (8 * i);
+ a0 = dxtb->points[4 * i + 0];
+ a1 = dxtb->points[4 * i + 1];
+ a2 = dxtb->points[4 * i + 2];
+ a3 = dxtb->points[4 * i + 3];
+ b0 = dxtb->palette[(idx ) & 3];
+ b1 = dxtb->palette[(idx >> 2) & 3];
+ b2 = dxtb->palette[(idx >> 4) & 3];
+ b3 = dxtb->palette[(idx >> 6) & 3];
+ a0 = (a0 - b0) * dxtb->metric;
+ a1 = (a1 - b1) * dxtb->metric;
+ a2 = (a2 - b2) * dxtb->metric;
+ a3 = (a3 - b3) * dxtb->metric;
+ _MM_TRANSPOSE4_PS(a0, a1, a2, a3);
+ d = a0 * a0 + a1 * a1 + a2 * a2;
+ error += vec4_accum(d);
+ }
+#else
+ vec4_t t;
+
+ // compute error
+ for (i = 0; i < 16; ++i)
+ {
+ idx = (indices >> (2 * i)) & 3;
+ t = (dxtb->points[i] - dxtb->palette[idx]) * dxtb->metric;
+ error += vec4_dot(t, t);
+ }
+#endif
+
+ return error;
+}
+
+static unsigned int
+compress3 (dxtblock_t *dxtb)
+{
+ const int MAX_ITERATIONS = 8;
+ int i;
+ unsigned int indices, bestindices;
+ float error, besterror = FLT_MAX;
+ vec4_t oldmax, oldmin;
+
+ construct_palette3(dxtb);
+
+ indices = match_colors3(dxtb);
+ bestindices = indices;
+
+ for (i = 0; i < MAX_ITERATIONS; ++i)
+ {
+ oldmax = dxtb->max;
+ oldmin = dxtb->min;
+
+ optimize_endpoints3(dxtb, indices, &dxtb->max, &dxtb->min);
+ construct_palette3(dxtb);
+ indices = match_colors3(dxtb);
+ error = compute_error3(dxtb, indices);
+
+ if (error < besterror)
+ {
+ besterror = error;
+ bestindices = indices;
+ }
+ else
+ {
+ dxtb->max = oldmax;
+ dxtb->min = oldmin;
+ break;
+ }
+ }
+
+ return bestindices;
+}
+
+static unsigned int
+compress4 (dxtblock_t *dxtb)
+{
+ const int MAX_ITERATIONS = 8;
+ int i;
+ unsigned int indices, bestindices;
+ float error, besterror = FLT_MAX;
+ vec4_t oldmax, oldmin;
+
+ construct_palette4(dxtb);
+
+ indices = match_colors4(dxtb);
+ bestindices = indices;
+
+ for (i = 0; i < MAX_ITERATIONS; ++i)
+ {
+ oldmax = dxtb->max;
+ oldmin = dxtb->min;
+
+ optimize_endpoints4(dxtb, indices, &dxtb->max, &dxtb->min);
+ construct_palette4(dxtb);
+ indices = match_colors4(dxtb);
+ error = compute_error4(dxtb, indices);
+
+ if (error < besterror)
+ {
+ besterror = error;
+ bestindices = indices;
+ }
+ else
+ {
+ dxtb->max = oldmax;
+ dxtb->min = oldmin;
+ break;
+ }
+ }
+
+ return bestindices;
+}
+
+static void
+encode_color_block (unsigned char *dst,
+ unsigned char *block,
+ int flags)
+{
+ dxtblock_t dxtb;
+ int max16, min16;
+ unsigned int indices, mask;
+
+ dxtblock_init(&dxtb, block, flags);
+
+ if (dxtb.single) // single color block
+ {
+ max16 = (omatch5[block[2]][0] << 11) |
+ (omatch6[block[1]][0] << 5) |
+ (omatch5[block[0]][0] );
+ min16 = (omatch5[block[2]][1] << 11) |
+ (omatch6[block[1]][1] << 5) |
+ (omatch5[block[0]][1] );
+
+ indices = 0xaaaaaaaa; // 101010...
+
+ if ((flags & DXT_BC1) && dxtb.alphamask)
+ {
+ // DXT1 compression, non-opaque block. Add alpha indices.
+ indices |= dxtb.alphamask;
+ if (max16 > min16)
+ SWAP(max16, min16);
+ }
+ else if (max16 < min16)
+ {
+ SWAP(max16, min16);
+ indices ^= 0x55555555; // 010101...
+ }
+ }
+ else if ((flags & DXT_BC1) && dxtb.alphamask) // DXT1 compression, non-opaque block
+ {
+ indices = compress3(&dxtb);
+
+ vec4_endpoints_to_565(&max16, &min16, dxtb.max, dxtb.min);
+
+ if (max16 > min16)
+ {
+ SWAP(max16, min16);
+ // remap indices 0 -> 1, 1 -> 0
+ mask = indices & 0xaaaaaaaa;
+ mask = mask | (mask >> 1);
+ indices = (indices & mask) | ((indices ^ 0x55555555) & ~mask);
+ }
+ }
+ else
+ {
+ indices = compress4(&dxtb);
+
+ vec4_endpoints_to_565(&max16, &min16, dxtb.max, dxtb.min);
+
+ if (max16 < min16)
+ {
+ SWAP(max16, min16);
+ indices ^= 0x55555555; // 010101...
+ }
+ }
+
+ PUTL16(dst + 0, max16);
+ PUTL16(dst + 2, min16);
+ PUTL32(dst + 4, indices);
+}
+
+static void
+get_min_max_YCoCg (const unsigned char *block,
+ unsigned char *mincolor,
+ unsigned char *maxcolor)
+{
+ int i;
+
+ mincolor[2] = mincolor[1] = 255;
+ maxcolor[2] = maxcolor[1] = 0;
+
+ for (i = 0; i < 16; ++i)
+ {
+ if (block[4 * i + 2] < mincolor[2]) mincolor[2] = block[4 * i + 2];
+ if (block[4 * i + 1] < mincolor[1]) mincolor[1] = block[4 * i + 1];
+ if (block[4 * i + 2] > maxcolor[2]) maxcolor[2] = block[4 * i + 2];
+ if (block[4 * i + 1] > maxcolor[1]) maxcolor[1] = block[4 * i + 1];
+ }
+}
+
+static void
+scale_YCoCg (unsigned char *block,
+ unsigned char *mincolor,
+ unsigned char *maxcolor)
+{
+ const int s0 = 128 / 2 - 1;
+ const int s1 = 128 / 4 - 1;
+ int m0, m1, m2, m3;
+ int mask0, mask1, scale;
+ int i;
+
+ m0 = abs(mincolor[2] - 128);
+ m1 = abs(mincolor[1] - 128);
+ m2 = abs(maxcolor[2] - 128);
+ m3 = abs(maxcolor[1] - 128);
+
+ if (m1 > m0) m0 = m1;
+ if (m3 > m2) m2 = m3;
+ if (m2 > m0) m0 = m2;
+
+ mask0 = -(m0 <= s0);
+ mask1 = -(m0 <= s1);
+ scale = 1 + (1 & mask0) + (2 & mask1);
+
+ mincolor[2] = (mincolor[2] - 128) * scale + 128;
+ mincolor[1] = (mincolor[1] - 128) * scale + 128;
+ mincolor[0] = (scale - 1) << 3;
+
+ maxcolor[2] = (maxcolor[2] - 128) * scale + 128;
+ maxcolor[1] = (maxcolor[1] - 128) * scale + 128;
+ maxcolor[0] = (scale - 1) << 3;
+
+ for (i = 0; i < 16; ++i)
+ {
+ block[i * 4 + 2] = (block[i * 4 + 2] - 128) * scale + 128;
+ block[i * 4 + 1] = (block[i * 4 + 1] - 128) * scale + 128;
+ }
+}
+
+#define INSET_SHIFT 4
+
+static void
+inset_bbox_YCoCg (unsigned char *mincolor,
+ unsigned char *maxcolor)
+{
+ int inset[4], mini[4], maxi[4];
+
+ inset[2] = (maxcolor[2] - mincolor[2]) - ((1 << (INSET_SHIFT - 1)) - 1);
+ inset[1] = (maxcolor[1] - mincolor[1]) - ((1 << (INSET_SHIFT - 1)) - 1);
+
+ mini[2] = ((mincolor[2] << INSET_SHIFT) + inset[2]) >> INSET_SHIFT;
+ mini[1] = ((mincolor[1] << INSET_SHIFT) + inset[1]) >> INSET_SHIFT;
+
+ maxi[2] = ((maxcolor[2] << INSET_SHIFT) - inset[2]) >> INSET_SHIFT;
+ maxi[1] = ((maxcolor[1] << INSET_SHIFT) - inset[1]) >> INSET_SHIFT;
+
+ mini[2] = (mini[2] >= 0) ? mini[2] : 0;
+ mini[1] = (mini[1] >= 0) ? mini[1] : 0;
+
+ maxi[2] = (maxi[2] <= 255) ? maxi[2] : 255;
+ maxi[1] = (maxi[1] <= 255) ? maxi[1] : 255;
+
+ mincolor[2] = (mini[2] & 0xf8) | (mini[2] >> 5);
+ mincolor[1] = (mini[1] & 0xfc) | (mini[1] >> 6);
+
+ maxcolor[2] = (maxi[2] & 0xf8) | (maxi[2] >> 5);
+ maxcolor[1] = (maxi[1] & 0xfc) | (maxi[1] >> 6);
+}
+
+static void
+select_diagonal_YCoCg (const unsigned char *block,
+ unsigned char *mincolor,
+ unsigned char *maxcolor)
+{
+ unsigned char mid0, mid1, side, mask, b0, b1, c0, c1;
+ int i;
+
+ mid0 = ((int)mincolor[2] + maxcolor[2] + 1) >> 1;
+ mid1 = ((int)mincolor[1] + maxcolor[1] + 1) >> 1;
+
+ side = 0;
+ for (i = 0; i < 16; ++i)
+ {
+ b0 = block[i * 4 + 2] >= mid0;
+ b1 = block[i * 4 + 1] >= mid1;
+ side += (b0 ^ b1);
+ }
+
+ mask = -(side > 8);
+ mask &= -(mincolor[2] != maxcolor[2]);
+
+ c0 = mincolor[1];
+ c1 = maxcolor[1];
+
+ c0 ^= c1;
+ c1 ^= c0 & mask;
+ c0 ^= c1;
+
+ mincolor[1] = c0;
+ maxcolor[1] = c1;
+}
+
+static void
+encode_YCoCg_block (unsigned char *dst,
+ unsigned char *block)
+{
+ unsigned char colors[4][3], *maxcolor, *mincolor;
+ unsigned int mask;
+ int c0, c1, d0, d1, d2, d3;
+ int b0, b1, b2, b3, b4;
+ int x0, x1, x2;
+ int i, idx;
+
+ maxcolor = &colors[0][0];
+ mincolor = &colors[1][0];
+
+ get_min_max_YCoCg(block, mincolor, maxcolor);
+ scale_YCoCg(block, mincolor, maxcolor);
+ inset_bbox_YCoCg(mincolor, maxcolor);
+ select_diagonal_YCoCg(block, mincolor, maxcolor);
+
+ lerp_rgb13(&colors[2][0], maxcolor, mincolor);
+ lerp_rgb13(&colors[3][0], mincolor, maxcolor);
+
+ mask = 0;
+
+ for (i = 0; i < 16; ++i)
+ {
+ c0 = block[4 * i + 2];
+ c1 = block[4 * i + 1];
+
+ d0 = abs(colors[0][2] - c0) + abs(colors[0][1] - c1);
+ d1 = abs(colors[1][2] - c0) + abs(colors[1][1] - c1);
+ d2 = abs(colors[2][2] - c0) + abs(colors[2][1] - c1);
+ d3 = abs(colors[3][2] - c0) + abs(colors[3][1] - c1);
+
+ b0 = d0 > d3;
+ b1 = d1 > d2;
+ b2 = d0 > d2;
+ b3 = d1 > d3;
+ b4 = d2 > d3;
+
+ x0 = b1 & b2;
+ x1 = b0 & b3;
+ x2 = b0 & b4;
+
+ idx = (x2 | ((x0 | x1) << 1));
+
+ mask |= idx << (2 * i);
+ }
+
+ PUTL16(dst + 0, pack_rgb565(maxcolor));
+ PUTL16(dst + 2, pack_rgb565(mincolor));
+ PUTL32(dst + 4, mask);
+}
+
+/* write DXT3 alpha block */
+static void
+encode_alpha_block_BC2 (unsigned char *dst,
+ const unsigned char *block)
+{
+ int i, a1, a2;
+
+ block += 3;
+
+ for (i = 0; i < 8; ++i)
+ {
+ a1 = mul8bit(block[8 * i + 0], 0x0f);
+ a2 = mul8bit(block[8 * i + 4], 0x0f);
+ *dst++ = (a2 << 4) | a1;
+ }
+}
+
+/* Write DXT5 alpha block */
+static void
+encode_alpha_block_BC3 (unsigned char *dst,
+ const unsigned char *block,
+ const int offset)
+{
+ int i, v, mn, mx;
+ int dist, bias, dist2, dist4, bits, mask;
+ int a, idx, t;
+
+ block += offset;
+ block += 3;
+
+ /* find min/max alpha pair */
+ mn = mx = block[0];
+ for (i = 0; i < 16; ++i)
+ {
+ v = block[4 * i];
+ if(v > mx) mx = v;
+ if(v < mn) mn = v;
+ }
+
+ /* encode them */
+ *dst++ = mx;
+ *dst++ = mn;
+
+ /*
+ * determine bias and emit indices
+ * given the choice of mx/mn, these indices are optimal:
+ * http://fgiesen.wordpress.com/2009/12/15/dxt5-alpha-block-index-determination/
+ */
+ dist = mx - mn;
+ dist4 = dist * 4;
+ dist2 = dist * 2;
+ bias = (dist < 8) ? (dist - 1) : (dist / 2 + 2);
+ bias -= mn * 7;
+ bits = 0;
+ mask = 0;
+
+ for (i = 0; i < 16; ++i)
+ {
+ a = block[4 * i] * 7 + bias;
+
+ /* select index. this is a "linear scale" lerp factor between 0
+ (val=min) and 7 (val=max). */
+ t = (a >= dist4) ? -1 : 0; idx = t & 4; a -= dist4 & t;
+ t = (a >= dist2) ? -1 : 0; idx += t & 2; a -= dist2 & t;
+ idx += (a >= dist);
+
+ /* turn linear scale into DXT index (0/1 are extremal pts) */
+ idx = -idx & 7;
+ idx ^= (2 > idx);
+
+ /* write index */
+ mask |= idx << bits;
+ if ((bits += 3) >= 8)
+ {
+ *dst++ = mask;
+ mask >>= 8;
+ bits -= 8;
+ }
+ }
+}
+
+#define BLOCK_COUNT(w, h) ((((h) + 3) >> 2) * (((w) + 3) >> 2))
+#define BLOCK_OFFSET(x, y, w, bs) (((y) >> 2) * ((bs) * (((w) + 3) >> 2)) + ((bs) * ((x) >> 2)))
+
+static void
+compress_BC1 (unsigned char *dst,
+ const unsigned char *src,
+ int w,
+ int h,
+ int flags)
+{
+ const unsigned int block_count = BLOCK_COUNT(w, h);
+ unsigned int i;
+ unsigned char block[64], *p;
+ int x, y;
+
+#ifdef _OPENMP
+#pragma omp parallel for schedule(dynamic, 256) private(block, p, x, y)
+#endif
+ for (i = 0; i < block_count; ++i)
+ {
+ x = (i % ((w + 3) >> 2)) << 2;
+ y = (i / ((w + 3) >> 2)) << 2;
+ p = dst + BLOCK_OFFSET(x, y, w, 8);
+ extract_block(src, x, y, w, h, block);
+ encode_color_block(p, block, DXT_BC1 | flags);
+ }
+}
+
+static void
+compress_BC2 (unsigned char *dst,
+ const unsigned char *src,
+ int w,
+ int h,
+ int flags)
+{
+ const unsigned int block_count = BLOCK_COUNT(w, h);
+ unsigned int i;
+ unsigned char block[64], *p;
+ int x, y;
+
+#ifdef _OPENMP
+#pragma omp parallel for schedule(dynamic, 256) private(block, p, x, y)
+#endif
+ for (i = 0; i < block_count; ++i)
+ {
+ x = (i % ((w + 3) >> 2)) << 2;
+ y = (i / ((w + 3) >> 2)) << 2;
+ p = dst + BLOCK_OFFSET(x, y, w, 16);
+ extract_block(src, x, y, w, h, block);
+ encode_alpha_block_BC2(p, block);
+ encode_color_block(p + 8, block, DXT_BC2 | flags);
+ }
+}
+
+static void
+compress_BC3 (unsigned char *dst,
+ const unsigned char *src,
+ int w,
+ int h,
+ int flags)
+{
+ const unsigned int block_count = BLOCK_COUNT(w, h);
+ unsigned int i;
+ unsigned char block[64], *p;
+ int x, y;
+
+#ifdef _OPENMP
+#pragma omp parallel for schedule(dynamic, 256) private(block, p, x, y)
+#endif
+ for (i = 0; i < block_count; ++i)
+ {
+ x = (i % ((w + 3) >> 2)) << 2;
+ y = (i / ((w + 3) >> 2)) << 2;
+ p = dst + BLOCK_OFFSET(x, y, w, 16);
+ extract_block(src, x, y, w, h, block);
+ encode_alpha_block_BC3(p, block, 0);
+ encode_color_block(p + 8, block, DXT_BC3 | flags);
+ }
+}
+
+static void
+compress_BC4 (unsigned char *dst,
+ const unsigned char *src,
+ int w,
+ int h)
+{
+ const unsigned int block_count = BLOCK_COUNT(w, h);
+ unsigned int i;
+ unsigned char block[64], *p;
+ int x, y;
+
+#ifdef _OPENMP
+#pragma omp parallel for schedule(dynamic, 256) private(block, p, x, y)
+#endif
+ for (i = 0; i < block_count; ++i)
+ {
+ x = (i % ((w + 3) >> 2)) << 2;
+ y = (i / ((w + 3) >> 2)) << 2;
+ p = dst + BLOCK_OFFSET(x, y, w, 8);
+ extract_block(src, x, y, w, h, block);
+ encode_alpha_block_BC3(p, block, -1);
+ }
+}
+
+static void
+compress_BC5 (unsigned char *dst,
+ const unsigned char *src,
+ int w,
+ int h)
+{
+ const unsigned int block_count = BLOCK_COUNT(w, h);
+ unsigned int i;
+ unsigned char block[64], *p;
+ int x, y;
+
+#ifdef _OPENMP
+#pragma omp parallel for schedule(dynamic, 256) private(block, p, x, y)
+#endif
+ for (i = 0; i < block_count; ++i)
+ {
+ x = (i % ((w + 3) >> 2)) << 2;
+ y = (i / ((w + 3) >> 2)) << 2;
+ p = dst + BLOCK_OFFSET(x, y, w, 16);
+ extract_block(src, x, y, w, h, block);
+ /* Pixels are ordered as BGRA (see write_layer)
+ * First we encode red -1+3: channel 2;
+ * then we encode green -2+3: channel 1.
+ */
+ encode_alpha_block_BC3(p, block, -1);
+ encode_alpha_block_BC3(p + 8, block, -2);
+ }
+}
+
+static void
+compress_YCoCg (unsigned char *dst,
+ const unsigned char *src,
+ int w,
+ int h)
+{
+ const unsigned int block_count = BLOCK_COUNT(w, h);
+ unsigned int i;
+ unsigned char block[64], *p;
+ int x, y;
+
+#ifdef _OPENMP
+#pragma omp parallel for schedule(dynamic, 256) private(block, p, x, y)
+#endif
+ for (i = 0; i < block_count; ++i)
+ {
+ x = (i % ((w + 3) >> 2)) << 2;
+ y = (i / ((w + 3) >> 2)) << 2;
+ p = dst + BLOCK_OFFSET(x, y, w, 16);
+ extract_block(src, x, y, w, h, block);
+ encode_alpha_block_BC3(p, block, 0);
+ encode_YCoCg_block(p + 8, block);
+ }
+}
+
+int
+dxt_compress (unsigned char *dst,
+ unsigned char *src,
+ int format,
+ unsigned int width,
+ unsigned int height,
+ int bpp,
+ int mipmaps,
+ int flags)
+{
+ int i, size, w, h;
+ unsigned int offset;
+ unsigned char *tmp = NULL;
+ int j;
+ unsigned char *s;
+
+ if (bpp == 1)
+ {
+ /* grayscale promoted to BGRA */
+
+ size = get_mipmapped_size(width, height, 4, 0, mipmaps,
+ DDS_COMPRESS_NONE);
+ tmp = g_malloc(size);
+
+ for (i = j = 0; j < size; ++i, j += 4)
+ {
+ tmp[j + 0] = src[i];
+ tmp[j + 1] = src[i];
+ tmp[j + 2] = src[i];
+ tmp[j + 3] = 255;
+ }
+
+ bpp = 4;
+ }
+ else if (bpp == 2)
+ {
+ /* gray-alpha promoted to BGRA */
+
+ size = get_mipmapped_size(width, height, 4, 0, mipmaps,
+ DDS_COMPRESS_NONE);
+ tmp = g_malloc(size);
+
+ for (i = j = 0; j < size; i += 2, j += 4)
+ {
+ tmp[j + 0] = src[i];
+ tmp[j + 1] = src[i];
+ tmp[j + 2] = src[i];
+ tmp[j + 3] = src[i + 1];
+ }
+
+ bpp = 4;
+ }
+ else if (bpp == 3)
+ {
+ size = get_mipmapped_size(width, height, 4, 0, mipmaps,
+ DDS_COMPRESS_NONE);
+ tmp = g_malloc(size);
+
+ for (i = j = 0; j < size; i += 3, j += 4)
+ {
+ tmp[j + 0] = src[i + 0];
+ tmp[j + 1] = src[i + 1];
+ tmp[j + 2] = src[i + 2];
+ tmp[j + 3] = 255;
+ }
+
+ bpp = 4;
+ }
+
+ offset = 0;
+ w = width;
+ h = height;
+ s = tmp ? tmp : src;
+
+ for (i = 0; i < mipmaps; ++i)
+ {
+ switch (format)
+ {
+ case DDS_COMPRESS_BC1:
+ compress_BC1(dst + offset, s, w, h, flags);
+ break;
+ case DDS_COMPRESS_BC2:
+ compress_BC2(dst + offset, s, w, h, flags);
+ break;
+ case DDS_COMPRESS_BC3:
+ case DDS_COMPRESS_BC3N:
+ case DDS_COMPRESS_RXGB:
+ case DDS_COMPRESS_AEXP:
+ case DDS_COMPRESS_YCOCG:
+ compress_BC3(dst + offset, s, w, h, flags);
+ break;
+ case DDS_COMPRESS_BC4:
+ compress_BC4(dst + offset, s, w, h);
+ break;
+ case DDS_COMPRESS_BC5:
+ compress_BC5(dst + offset, s, w, h);
+ break;
+ case DDS_COMPRESS_YCOCGS:
+ compress_YCoCg(dst + offset, s, w, h);
+ break;
+ default:
+ compress_BC3(dst + offset, s, w, h, flags);
+ break;
+ }
+ s += (w * h * bpp);
+ offset += get_mipmapped_size(w, h, 0, 0, 1, format);
+ w = MAX(1, w >> 1);
+ h = MAX(1, h >> 1);
+ }
+
+ if (tmp)
+ g_free(tmp);
+
+ return 1;
+}
+
+static void
+decode_color_block (unsigned char *block,
+ unsigned char *src,
+ int format)
+{
+ int i, x, y;
+ unsigned char *d = block;
+ unsigned int indices, idx;
+ unsigned char colors[4][3];
+ unsigned short c0, c1;
+
+ c0 = GETL16(&src[0]);
+ c1 = GETL16(&src[2]);
+
+ unpack_rgb565(colors[0], c0);
+ unpack_rgb565(colors[1], c1);
+
+ if ((c0 > c1) || (format == DDS_COMPRESS_BC3))
+ {
+ lerp_rgb13(colors[2], colors[0], colors[1]);
+ lerp_rgb13(colors[3], colors[1], colors[0]);
+ }
+ else
+ {
+ for (i = 0; i < 3; ++i)
+ {
+ colors[2][i] = (colors[0][i] + colors[1][i] + 1) >> 1;
+ colors[3][i] = 255;
+ }
+ }
+
+ src += 4;
+ for (y = 0; y < 4; ++y)
+ {
+ indices = src[y];
+ for (x = 0; x < 4; ++x)
+ {
+ idx = indices & 0x03;
+ d[0] = colors[idx][2];
+ d[1] = colors[idx][1];
+ d[2] = colors[idx][0];
+ if (format == DDS_COMPRESS_BC1)
+ d[3] = ((c0 <= c1) && idx == 3) ? 0 : 255;
+ indices >>= 2;
+ d += 4;
+ }
+ }
+}
+
+static void
+decode_alpha_block_BC2 (unsigned char *block,
+ unsigned char *src)
+{
+ int x, y;
+ unsigned char *d = block;
+ unsigned int bits;
+
+ for (y = 0; y < 4; ++y)
+ {
+ bits = GETL16(&src[2 * y]);
+ for (x = 0; x < 4; ++x)
+ {
+ d[0] = (bits & 0x0f) * 17;
+ bits >>= 4;
+ d += 4;
+ }
+ }
+}
+
+static void
+decode_alpha_block_BC3 (unsigned char *block,
+ unsigned char *src,
+ int w)
+{
+ int x, y, code;
+ unsigned char *d = block;
+ unsigned char a0 = src[0];
+ unsigned char a1 = src[1];
+ unsigned long long bits = GETL64(src) >> 16;
+
+ for (y = 0; y < 4; ++y)
+ {
+ for (x = 0; x < 4; ++x)
+ {
+ code = ((unsigned int)bits) & 0x07;
+ if (code == 0)
+ d[0] = a0;
+ else if (code == 1)
+ d[0] = a1;
+ else if (a0 > a1)
+ d[0] = ((8 - code) * a0 + (code - 1) * a1) / 7;
+ else if (code >= 6)
+ d[0] = (code == 6) ? 0 : 255;
+ else
+ d[0] = ((6 - code) * a0 + (code - 1) * a1) / 5;
+ bits >>= 3;
+ d += 4;
+ }
+
+ if (w < 4)
+ bits >>= (3 * (4 - w));
+ }
+}
+
+static void
+make_normal (unsigned char *dst,
+ unsigned char x,
+ unsigned char y)
+{
+ float nx = 2.0f * ((float)x / 255.0f) - 1.0f;
+ float ny = 2.0f * ((float)y / 255.0f) - 1.0f;
+ float nz = 0.0f;
+ float d = 1.0f - nx * nx + ny * ny;
+ int z;
+
+ if (d > 0)
+ nz = sqrtf(d);
+
+ z = (int)(255.0f * (nz + 1) / 2.0f);
+ z = MAX(0, MIN(255, z));
+
+ dst[0] = x;
+ dst[1] = y;
+ dst[2] = z;
+}
+
+static void
+normalize_block (unsigned char *block,
+ int format)
+{
+ int x, y, tmp;
+
+ for (y = 0; y < 4; ++y)
+ {
+ for (x = 0; x < 4; ++x)
+ {
+ if (format == DDS_COMPRESS_BC3)
+ {
+ tmp = block[y * 16 + (x * 4)];
+ make_normal(&block[y * 16 + (x * 4)],
+ block[y * 16 + (x * 4) + 3],
+ block[y * 16 + (x * 4) + 1]);
+ block[y * 16 + (x * 4) + 3] = tmp;
+ }
+ else if (format == DDS_COMPRESS_BC5)
+ {
+ make_normal(&block[y * 16 + (x * 4)],
+ block[y * 16 + (x * 4)],
+ block[y * 16 + (x * 4) + 1]);
+ }
+ }
+ }
+}
+
+static void
+put_block (unsigned char *dst,
+ unsigned char *block,
+ unsigned int bx,
+ unsigned int by,
+ unsigned int width,
+ unsigned height,
+ int bpp)
+{
+ int x, y, i;
+ unsigned char *d;
+
+ for (y = 0; y < 4 && ((by + y) < height); ++y)
+ {
+ d = dst + ((y + by) * width + bx) * bpp;
+ for (x = 0; x < 4 && ((bx + x) < width); ++x)
+ {
+ for (i = 0; i < bpp; ++ i)
+ *d++ = block[y * 16 + (x * 4) + i];
+ }
+ }
+}
+
+int
+dxt_decompress (unsigned char *dst,
+ unsigned char *src,
+ int format,
+ unsigned int size,
+ unsigned int width,
+ unsigned int height,
+ int bpp,
+ int normals)
+{
+ unsigned char *s;
+ unsigned int x, y;
+ unsigned char block[16 * 4];
+
+ s = src;
+
+ for (y = 0; y < height; y += 4)
+ {
+ for (x = 0; x < width; x += 4)
+ {
+ memset(block, 0, 16 * 4);
+
+ if (format == DDS_COMPRESS_BC1)
+ {
+ decode_color_block(block, s, format);
+ s += 8;
+ }
+ else if (format == DDS_COMPRESS_BC2)
+ {
+ decode_alpha_block_BC2(block + 3, s);
+ decode_color_block(block, s + 8, format);
+ s += 16;
+ }
+ else if (format == DDS_COMPRESS_BC3)
+ {
+ decode_alpha_block_BC3(block + 3, s, width);
+ decode_color_block(block, s + 8, format);
+ s += 16;
+ }
+ else if (format == DDS_COMPRESS_BC4)
+ {
+ decode_alpha_block_BC3(block, s, width);
+ s += 8;
+ }
+ else if (format == DDS_COMPRESS_BC5)
+ {
+ decode_alpha_block_BC3(block, s, width);
+ decode_alpha_block_BC3(block + 1, s + 8, width);
+ s += 16;
+ }
+
+ if (normals)
+ normalize_block(block, format);
+
+ put_block(dst, block, x, y, width, height, bpp);
+ }
+ }
+
+ return 1;
+}