OSDN Git Service

split compressor into multiple files (preparation for libtxc_dxtn compat)
authorRudolf Polzer <divverent@xonotic.org>
Tue, 12 Jul 2011 17:43:07 +0000 (19:43 +0200)
committerRudolf Polzer <divverent@xonotic.org>
Thu, 14 Jul 2011 17:12:22 +0000 (19:12 +0200)
Makefile [new file with mode: 0644]
s2tc.cpp
s2tc_common.h [new file with mode: 0644]
s2tc_compressor.cpp [new file with mode: 0644]
s2tc_compressor.h [new file with mode: 0644]
test.sh

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..2c4dffb
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,12 @@
+all: s2tc
+
+CXXFLAGS = -O3 -Wall -Wextra
+
+include $(wildcard *.d)
+CXXFLAGS += -MMD
+
+s2tc: s2tc.o s2tc_compressor.o
+       $(CXX) $(LDFLAGS) -o $@ $+
+
+clean:
+       $(RM) *.o s2tc
index aaef040..bdf5c40 100644 (file)
--- a/s2tc.cpp
+++ b/s2tc.cpp
@@ -5,19 +5,8 @@
 #include <stdint.h>
 #include <getopt.h>
 
-template <class T> inline T min(const T &a, const T &b)
-{
-       if(b < a)
-               return b;
-       return a;
-}
-
-template <class T> inline T max(const T &a, const T &b)
-{
-       if(b > a)
-               return b;
-       return a;
-}
+#include "s2tc_compressor.h"
+#include "s2tc_common.h"
 
 /* START stuff that originates from image.c in DarkPlaces */
 int image_width, image_height;
@@ -483,549 +472,6 @@ unsigned char *FS_LoadFile(const char *fn, int *len)
 }
 /* end of darkplaces stuff */
 
-
-
-
-typedef struct
-{
-       signed char r, g, b;
-}
-color_t;
-inline bool operator<(const color_t &a, const color_t &b)
-{
-       signed char d;
-       d = a.r - b.r;
-       if(d)
-               return d < 0;
-       d = a.g - b.g;
-       if(d)
-               return d < 0;
-       d = a.b - b.b;
-       return d < 0;
-}
-
-// 16 differences must fit in int
-// i.e. a difference must be lower than 2^27
-
-// shift right, rounded
-#define SHRR(a,n) (((a) + (1 << ((n)-1))) >> (n))
-
-inline int color_dist_avg(const color_t &a, const color_t &b)
-{
-       int dr = a.r - b.r; // multiplier: 31 (-1..1)
-       int dg = a.g - b.g; // multiplier: 63 (-1..1)
-       int db = a.b - b.b; // multiplier: 31 (-1..1)
-       return ((dr*dr) << 2) + dg*dg + ((db*db) << 2);
-}
-
-inline int color_dist_yuv(const color_t &a, const color_t &b)
-{
-       int dr = a.r - b.r; // multiplier: 31 (-1..1)
-       int dg = a.g - b.g; // multiplier: 63 (-1..1)
-       int db = a.b - b.b; // multiplier: 31 (-1..1)
-       int y = dr * 30*2 + dg * 59 + db * 11*2; // multiplier: 6259
-       int u = dr * 202 - y; // * 0.5 / (1 - 0.30)
-       int v = db * 202 - y; // * 0.5 / (1 - 0.11)
-       return ((y*y) << 1) + SHRR(u*u, 3) + SHRR(v*v, 4);
-       // weight for u: sqrt(2^-4) / (0.5 / (1 - 0.30)) = 0.350
-       // weight for v: sqrt(2^-5) / (0.5 / (1 - 0.11)) = 0.315
-}
-
-inline int color_dist_rgb(const color_t &a, const color_t &b)
-{
-       int dr = a.r - b.r; // multiplier: 31 (-1..1)
-       int dg = a.g - b.g; // multiplier: 63 (-1..1)
-       int db = a.b - b.b; // multiplier: 31 (-1..1)
-       int y = dr * 21*2 + dg * 72 + db * 7*2; // multiplier: 6272
-       int u = dr * 202 - y; // * 0.5 / (1 - 0.21)
-       int v = db * 202 - y; // * 0.5 / (1 - 0.07)
-       return ((y*y) << 1) + SHRR(u*u, 3) + SHRR(v*v, 4);
-       // weight for u: sqrt(2^-4) / (0.5 / (1 - 0.21)) = 0.395
-       // weight for v: sqrt(2^-5) / (0.5 / (1 - 0.07)) = 0.328
-}
-
-inline int color_dist_srgb(const color_t &a, const color_t &b)
-{
-       int dr = a.r * (int) a.r - b.r * (int) b.r; // multiplier: 31*31
-       int dg = a.g * (int) a.g - b.g * (int) b.g; // multiplier: 63*63
-       int db = a.b * (int) a.b - b.b * (int) b.b; // multiplier: 31*31
-       int y = dr * 21*2*2 + dg * 72 + db * 7*2*2; // multiplier: 393400
-       int u = dr * 409 - y; // * 0.5 / (1 - 0.30)
-       int v = db * 409 - y; // * 0.5 / (1 - 0.11)
-       int sy = SHRR(y, 3) * SHRR(y, 4);
-       int su = SHRR(u, 3) * SHRR(u, 4);
-       int sv = SHRR(v, 3) * SHRR(v, 4);
-       return SHRR(sy, 4) + SHRR(su, 8) + SHRR(sv, 9);
-       // weight for u: sqrt(2^-4) / (0.5 / (1 - 0.30)) = 0.350
-       // weight for v: sqrt(2^-5) / (0.5 / (1 - 0.11)) = 0.315
-}
-
-inline int srgb_get_y(const color_t &a)
-{
-       // convert to linear
-       int r = a.r * (int) a.r;
-       int g = a.g * (int) a.g;
-       int b = a.b * (int) a.b;
-       // find luminance
-       int y = 37 * (r * 21*2*2 + g * 72 + b * 7*2*2); // multiplier: 14555800
-       // square root it (!)
-       y = sqrt(y); // now in range 0 to 3815
-       return y;
-}
-
-inline int color_dist_srgb_mixed(const color_t &a, const color_t &b)
-{
-       // get Y
-       int ay = srgb_get_y(a);
-       int by = srgb_get_y(b);
-       // get UV
-       int au = a.r * 191 - ay;
-       int av = a.b * 191 - ay;
-       int bu = b.r * 191 - by;
-       int bv = b.b * 191 - by;
-       // get differences
-       int y = ay - by;
-       int u = au - bu;
-       int v = av - bv;
-       return ((y*y) << 3) + SHRR(u*u, 1) + SHRR(v*v, 2);
-       // weight for u: ???
-       // weight for v: ???
-}
-
-// FIXME this is likely broken
-inline int color_dist_lab_srgb(const color_t &a, const color_t &b)
-{
-       // undo sRGB
-       float ar = powf(a.r / 31.0f, 2.4f);
-       float ag = powf(a.g / 63.0f, 2.4f);
-       float ab = powf(a.b / 31.0f, 2.4f);
-       float br = powf(b.r / 31.0f, 2.4f);
-       float bg = powf(b.g / 63.0f, 2.4f);
-       float bb = powf(b.b / 31.0f, 2.4f);
-       // convert to CIE XYZ
-       float aX = 0.4124f * ar + 0.3576f * ag + 0.1805f * ab;
-       float aY = 0.2126f * ar + 0.7152f * ag + 0.0722f * ab;
-       float aZ = 0.0193f * ar + 0.1192f * ag + 0.9505f * ab;
-       float bX = 0.4124f * br + 0.3576f * bg + 0.1805f * bb;
-       float bY = 0.2126f * br + 0.7152f * bg + 0.0722f * bb;
-       float bZ = 0.0193f * br + 0.1192f * bg + 0.9505f * bb;
-       // convert to CIE Lab
-       float Xn = 0.3127f;
-       float Yn = 0.3290f;
-       float Zn = 0.3583f;
-       float aL = 116 * cbrtf(aY / Yn) - 16;
-       float aA = 500 * (cbrtf(aX / Xn) - cbrtf(aY / Yn));
-       float aB = 200 * (cbrtf(aY / Yn) - cbrtf(aZ / Zn));
-       float bL = 116 * cbrtf(bY / Yn) - 16;
-       float bA = 500 * (cbrtf(bX / Xn) - cbrtf(bY / Yn));
-       float bB = 200 * (cbrtf(bY / Yn) - cbrtf(bZ / Zn));
-       // euclidean distance, but moving weight away from A and B
-       return 1000 * ((aL - bL) * (aL - bL) + (aA - bA) * (aA - bA) + (aB - bB) * (aB - bB));
-}
-
-inline int color_dist_normalmap(const color_t &a, const color_t &b)
-{
-       float ca[3], cb[3];
-       ca[0] = a.r / 31.0 * 2 - 1;
-       ca[1] = a.g / 63.0 * 2 - 1;
-       ca[2] = a.b / 31.0 * 2 - 1;
-       cb[0] = b.r / 31.0 * 2 - 1;
-       cb[1] = b.g / 63.0 * 2 - 1;
-       cb[2] = b.b / 31.0 * 2 - 1;
-
-       return
-               500 *
-               (
-                       (cb[0] - ca[0]) * (cb[0] - ca[0])
-                       +
-                       (cb[1] - ca[1]) * (cb[1] - ca[1])
-                       +
-                       (cb[2] - ca[2]) * (cb[2] - ca[2])
-               )
-               ;
-       // max value: 500 * (4 + 4 + 4) = 6000
-}
-
-typedef int ColorDistFunc(const color_t &a, const color_t &b);
-
-inline int alpha_dist(unsigned char a, unsigned char b)
-{
-       return (a - (int) b) * (a - (int) b);
-}
-
-template <class T, class F>
-// n: input count
-// m: total color count (including non-counted inputs)
-// m >= n
-void reduce_colors_inplace(T *c, int n, int m, F dist)
-{
-       int i, j, k;
-       int bestsum = -1;
-       int besti = 0;
-       int bestj = 1;
-       int dists[m][n];
-       // first the square
-       for(i = 0; i < n; ++i)
-       {
-               dists[i][i] = 0;
-               for(j = i+1; j < n; ++j)
-               {
-                       int d = dist(c[i], c[j]);
-                       dists[i][j] = dists[j][i] = d;
-               }
-       }
-       // then the box
-       for(; i < m; ++i)
-       {
-               for(j = 0; j < n; ++j)
-               {
-                       int d = dist(c[i], c[j]);
-                       dists[i][j] = d;
-               }
-       }
-       for(i = 0; i < m; ++i)
-               for(j = i+1; j < m; ++j)
-               {
-                       int sum = 0;
-                       for(k = 0; k < n; ++k)
-                       {
-                               int di = dists[i][k];
-                               int dj = dists[j][k];
-                               int m  = min(di, dj);
-                               sum += m;
-                       }
-                       if(bestsum < 0 || sum < bestsum)
-                       {
-                               bestsum = sum;
-                               besti = i;
-                               bestj = j;
-                       }
-               }
-       if(besti != 0)
-               c[0] = c[besti];
-       if(bestj != 1)
-               c[1] = c[bestj];
-}
-template <class T, class F>
-void reduce_colors_inplace_2fixpoints(T *c, int n, int m, F dist, const T &fix0, const T &fix1)
-{
-       int i, j, k;
-       int bestsum = -1;
-       int besti = 0;
-       int bestj = 1;
-       int dists[m+2][n];
-       // first the square
-       for(i = 0; i < n; ++i)
-       {
-               dists[i][i] = 0;
-               for(j = i+1; j < n; ++j)
-               {
-                       int d = dist(c[i], c[j]);
-                       dists[i][j] = dists[j][i] = d;
-               }
-       }
-       // then the box
-       for(; i < m; ++i)
-       {
-               for(j = 0; j < n; ++j)
-               {
-                       int d = dist(c[i], c[j]);
-                       dists[i][j] = d;
-               }
-       }
-       // then the two extra rows
-       for(j = 0; j < n; ++j)
-       {
-               int d = dist(fix0, c[j]);
-               dists[m][j] = d;
-       }
-       for(j = 0; j < n; ++j)
-       {
-               int d = dist(fix1, c[j]);
-               dists[m+1][j] = d;
-       }
-       for(i = 0; i < m; ++i)
-               for(j = i+1; j < m; ++j)
-               {
-                       int sum = 0;
-                       for(k = 0; k < n; ++k)
-                       {
-                               int di = dists[i][k];
-                               int dj = dists[j][k];
-                               int d0 = dists[m][k];
-                               int d1 = dists[m+1][k];
-                               int m  = min(min(di, dj), min(d0, d1));
-                               sum += m;
-                       }
-                       if(bestsum < 0 || sum < bestsum)
-                       {
-                               bestsum = sum;
-                               besti = i;
-                               bestj = j;
-                       }
-               }
-       if(besti != 0)
-               c[0] = c[besti];
-       if(bestj != 1)
-               c[1] = c[bestj];
-}
-
-inline int diffuse(float *diff, float src)
-{
-       int ret;
-       src += *diff;
-       ret = src;
-       *diff = (src - ret);
-       return ret;
-}
-
-void rgb565_image(unsigned char *out, const unsigned char *rgba, int w, int h, int alpharange)
-{
-       int x, y;
-       float diffuse_r = 0;
-       float diffuse_g = 0;
-       float diffuse_b = 0;
-       float diffuse_a = 0;
-       for(y = 0; y < h; ++y)
-               for(x = 0; x < w; ++x)
-               {
-                       out[(x + y * w) * 4 + 2] = diffuse(&diffuse_r, rgba[(x + y * w) * 4 + 2] * 31.0 / 255.0);
-                       out[(x + y * w) * 4 + 1] = diffuse(&diffuse_g, rgba[(x + y * w) * 4 + 1] * 63.0 / 255.0);
-                       out[(x + y * w) * 4 + 0] = diffuse(&diffuse_b, rgba[(x + y * w) * 4 + 0] * 31.0 / 255.0);
-                       out[(x + y * w) * 4 + 3] = diffuse(&diffuse_a, rgba[(x + y * w) * 4 + 3] * (alpharange / 255.0));
-               }
-}
-
-enum DxtMode
-{
-       DXT1,
-       DXT3,
-       DXT5
-};
-enum ColorDistMode
-{
-       RGB,
-       YUV,
-       SRGB,
-       SRGB_MIXED,
-       LAB,
-       AVG,
-       NORMALMAP
-};
-
-template<DxtMode dxt, ColorDistFunc ColorDist, bool userandom>
-void s2tc_encode_block(unsigned char *out, const unsigned char *rgba, int iw, int w, int h, int nrandom)
-{
-       color_t c[16 + (userandom ? nrandom : 0)];
-
-       unsigned char ca[16];
-       int n = 0, m = 0;
-       int x, y;
-
-       for(x = 0; x < w; ++x)
-               for(y = 0; y < h; ++y)
-               {
-                       c[n].r = rgba[(x + y * iw) * 4 + 2];
-                       c[n].g = rgba[(x + y * iw) * 4 + 1];
-                       c[n].b = rgba[(x + y * iw) * 4 + 0];
-                       if(dxt == DXT5)
-                               ca[n]  = rgba[(x + y * iw) * 4 + 3];
-                       ++n;
-               }
-
-       m = n;
-
-       if(userandom)
-       {
-               color_t mins = c[0];
-               color_t maxs = c[0];
-               for(x = 1; x < n; ++x)
-               {
-                       mins.r = min(mins.r, c[x].r);
-                       mins.g = min(mins.g, c[x].g);
-                       mins.b = min(mins.b, c[x].b);
-                       maxs.r = max(maxs.r, c[x].r);
-                       maxs.g = max(maxs.g, c[x].g);
-                       maxs.b = max(maxs.b, c[x].b);
-               }
-               color_t len = { maxs.r - mins.r + 1, maxs.g - mins.g + 1, maxs.b - mins.b + 1 };
-               for(x = 0; x < nrandom; ++x)
-               {
-                       c[m].r = mins.r + rand() % len.r;
-                       c[m].g = mins.g + rand() % len.g;
-                       c[m].b = mins.b + rand() % len.b;
-                       ++m;
-               }
-       }
-
-       reduce_colors_inplace(c, n, m, ColorDist);
-       if(dxt == DXT5)
-       {
-               reduce_colors_inplace_2fixpoints(ca, n, n, alpha_dist, (unsigned char) 0, (unsigned char) 255);
-               if(ca[1] < ca[0])
-               {
-                       ca[2] = ca[0];
-                       ca[0] = ca[1];
-                       ca[1] = ca[2];
-               }
-       }
-       if(c[1] < c[0])
-       {
-               c[2] = c[0];
-               c[0] = c[1];
-               c[1] = c[2];
-       }
-
-       memset(out, 0, 16);
-       switch(dxt)
-       {
-               case DXT5:
-                       out[0] = ca[0];
-                       out[1] = ca[1];
-               case DXT3:
-                       out[8] = ((c[0].g & 0x07) << 5) | c[0].b;
-                       out[9] = (c[0].r << 3) | (c[0].g >> 3);
-                       out[10] = ((c[1].g & 0x07) << 5) | c[1].b;
-                       out[11] = (c[1].r << 3) | (c[1].g >> 3);
-                       break;
-               case DXT1:
-                       out[0] = ((c[0].g & 0x07) << 5) | c[0].b;
-                       out[1] = (c[0].r << 3) | (c[0].g >> 3);
-                       out[2] = ((c[1].g & 0x07) << 5) | c[1].b;
-                       out[3] = (c[1].r << 3) | (c[1].g >> 3);
-                       break;
-       }
-       for(x = 0; x < w; ++x)
-               for(y = 0; y < h; ++y)
-               {
-                       int pindex = (x+y*4);
-                       c[2].r = rgba[(x + y * iw) * 4 + 2];
-                       c[2].g = rgba[(x + y * iw) * 4 + 1];
-                       c[2].b = rgba[(x + y * iw) * 4 + 0];
-                       ca[2]  = rgba[(x + y * iw) * 4 + 3];
-                       switch(dxt)
-                       {
-                               case DXT5:
-                                       {
-                                               int da[4];
-                                               int bitindex = pindex * 3;
-                                               da[0] = alpha_dist(ca[0], ca[2]);
-                                               da[1] = alpha_dist(ca[1], ca[2]);
-                                               da[2] = alpha_dist(0, ca[2]);
-                                               da[3] = alpha_dist(255, ca[2]);
-                                               if(da[2] <= da[0] && da[2] <= da[1] && da[2] <= da[3])
-                                               {
-                                                       // 6
-                                                       ++bitindex;
-                                                       out[bitindex / 8 + 2] |= (1 << (bitindex % 8));
-                                                       ++bitindex;
-                                                       out[bitindex / 8 + 2] |= (1 << (bitindex % 8));
-                                               }
-                                               else if(da[3] <= da[0] && da[3] <= da[1])
-                                               {
-                                                       // 7
-                                                       out[bitindex / 8 + 2] |= (1 << (bitindex % 8));
-                                                       ++bitindex;
-                                                       out[bitindex / 8 + 2] |= (1 << (bitindex % 8));
-                                                       ++bitindex;
-                                                       out[bitindex / 8 + 2] |= (1 << (bitindex % 8));
-                                               }
-                                               else if(da[0] <= da[1])
-                                               {
-                                                       // 0
-                                               }
-                                               else
-                                               {
-                                                       // 1
-                                                       out[bitindex / 8 + 2] |= (1 << (bitindex % 8));
-                                               }
-                                       }
-                                       if(ColorDist(c[0], c[2]) > ColorDist(c[1], c[2]))
-                                       {
-                                               int bitindex = pindex * 2;
-                                               out[bitindex / 8 + 12] |= (1 << (bitindex % 8));
-                                       }
-                                       break;
-                               case DXT3:
-                                       {
-                                               int bitindex = pindex * 4;
-                                               out[bitindex / 8 + 0] |= (ca[2] << (bitindex % 8));
-                                       }
-                                       if(ColorDist(c[0], c[2]) > ColorDist(c[1], c[2]))
-                                       {
-                                               int bitindex = pindex * 2;
-                                               out[bitindex / 8 + 12] |= (1 << (bitindex % 8));
-                                       }
-                                       break;
-                               case DXT1:
-                                       {
-                                               int bitindex = pindex * 2;
-                                               if(!ca[2])
-                                                       out[bitindex / 8 + 4] |= (3 << (bitindex % 8));
-                                               else if(ColorDist(c[0], c[2]) > ColorDist(c[1], c[2]))
-                                                       out[bitindex / 8 + 4] |= (1 << (bitindex % 8));
-                                       }
-                                       break;
-                       }
-               }
-}
-
-// compile time dispatch magic
-template<DxtMode dxt, ColorDistFunc ColorDist>
-void s2tc_encode_block(unsigned char *out, const unsigned char *rgba, int iw, int w, int h, int nrandom)
-{
-       if(nrandom)
-               s2tc_encode_block<dxt, ColorDist, true>(out, rgba, iw, w, h, nrandom);
-       else
-               s2tc_encode_block<dxt, ColorDist, false>(out, rgba, iw, w, h, nrandom);
-}
-
-template<ColorDistFunc ColorDist>
-void s2tc_encode_block(unsigned char *out, const unsigned char *rgba, int iw, int w, int h, DxtMode dxt, int nrandom)
-{
-       switch(dxt)
-       {
-               case DXT1:
-                       s2tc_encode_block<DXT1, ColorDist>(out, rgba, iw, w, h, nrandom);
-                       break;
-               case DXT3:
-                       s2tc_encode_block<DXT3, ColorDist>(out, rgba, iw, w, h, nrandom);
-                       break;
-               default:
-               case DXT5:
-                       s2tc_encode_block<DXT5, ColorDist>(out, rgba, iw, w, h, nrandom);
-                       break;
-       }
-}
-
-void s2tc_encode_block(unsigned char *out, const unsigned char *rgba, int iw, int w, int h, DxtMode dxt, ColorDistMode cd, int nrandom)
-{
-       switch(cd)
-       {
-               case RGB:
-                       s2tc_encode_block<color_dist_rgb>(out, rgba, iw, w, h, dxt, nrandom);
-                       break;
-               case YUV:
-                       s2tc_encode_block<color_dist_yuv>(out, rgba, iw, w, h, dxt, nrandom);
-                       break;
-               case SRGB:
-                       s2tc_encode_block<color_dist_srgb>(out, rgba, iw, w, h, dxt, nrandom);
-                       break;
-               case SRGB_MIXED:
-                       s2tc_encode_block<color_dist_srgb_mixed>(out, rgba, iw, w, h, dxt, nrandom);
-                       break;
-               case LAB:
-                       s2tc_encode_block<color_dist_lab_srgb>(out, rgba, iw, w, h, dxt, nrandom);
-                       break;
-               case AVG:
-                       s2tc_encode_block<color_dist_avg>(out, rgba, iw, w, h, dxt, nrandom);
-                       break;
-               case NORMALMAP:
-                       s2tc_encode_block<color_dist_normalmap>(out, rgba, iw, w, h, dxt, nrandom);
-                       break;
-       }
-}
-
 uint32_t LittleLong(uint32_t w)
 {
        union
diff --git a/s2tc_common.h b/s2tc_common.h
new file mode 100644 (file)
index 0000000..8143de2
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef S2TC_COMMON_H
+#define S2TC_COMMON_H
+
+template <class T> inline T min(const T &a, const T &b)
+{
+       if(b < a)
+               return b;
+       return a;
+}
+
+template <class T> inline T max(const T &a, const T &b)
+{
+       if(b > a)
+               return b;
+       return a;
+}
+
+#endif
diff --git a/s2tc_compressor.cpp b/s2tc_compressor.cpp
new file mode 100644 (file)
index 0000000..f80de7b
--- /dev/null
@@ -0,0 +1,529 @@
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "s2tc_compressor.h"
+#include "s2tc_common.h"
+
+typedef struct
+{
+       signed char r, g, b;
+}
+color_t;
+
+inline bool operator<(const color_t &a, const color_t &b)
+{
+       signed char d;
+       d = a.r - b.r;
+       if(d)
+               return d < 0;
+       d = a.g - b.g;
+       if(d)
+               return d < 0;
+       d = a.b - b.b;
+       return d < 0;
+}
+// 16 differences must fit in int
+// i.e. a difference must be lower than 2^27
+
+// shift right, rounded
+#define SHRR(a,n) (((a) + (1 << ((n)-1))) >> (n))
+
+inline int color_dist_avg(const color_t &a, const color_t &b)
+{
+       int dr = a.r - b.r; // multiplier: 31 (-1..1)
+       int dg = a.g - b.g; // multiplier: 63 (-1..1)
+       int db = a.b - b.b; // multiplier: 31 (-1..1)
+       return ((dr*dr) << 2) + dg*dg + ((db*db) << 2);
+}
+
+inline int color_dist_yuv(const color_t &a, const color_t &b)
+{
+       int dr = a.r - b.r; // multiplier: 31 (-1..1)
+       int dg = a.g - b.g; // multiplier: 63 (-1..1)
+       int db = a.b - b.b; // multiplier: 31 (-1..1)
+       int y = dr * 30*2 + dg * 59 + db * 11*2; // multiplier: 6259
+       int u = dr * 202 - y; // * 0.5 / (1 - 0.30)
+       int v = db * 202 - y; // * 0.5 / (1 - 0.11)
+       return ((y*y) << 1) + SHRR(u*u, 3) + SHRR(v*v, 4);
+       // weight for u: sqrt(2^-4) / (0.5 / (1 - 0.30)) = 0.350
+       // weight for v: sqrt(2^-5) / (0.5 / (1 - 0.11)) = 0.315
+}
+
+inline int color_dist_rgb(const color_t &a, const color_t &b)
+{
+       int dr = a.r - b.r; // multiplier: 31 (-1..1)
+       int dg = a.g - b.g; // multiplier: 63 (-1..1)
+       int db = a.b - b.b; // multiplier: 31 (-1..1)
+       int y = dr * 21*2 + dg * 72 + db * 7*2; // multiplier: 6272
+       int u = dr * 202 - y; // * 0.5 / (1 - 0.21)
+       int v = db * 202 - y; // * 0.5 / (1 - 0.07)
+       return ((y*y) << 1) + SHRR(u*u, 3) + SHRR(v*v, 4);
+       // weight for u: sqrt(2^-4) / (0.5 / (1 - 0.21)) = 0.395
+       // weight for v: sqrt(2^-5) / (0.5 / (1 - 0.07)) = 0.328
+}
+
+inline int color_dist_srgb(const color_t &a, const color_t &b)
+{
+       int dr = a.r * (int) a.r - b.r * (int) b.r; // multiplier: 31*31
+       int dg = a.g * (int) a.g - b.g * (int) b.g; // multiplier: 63*63
+       int db = a.b * (int) a.b - b.b * (int) b.b; // multiplier: 31*31
+       int y = dr * 21*2*2 + dg * 72 + db * 7*2*2; // multiplier: 393400
+       int u = dr * 409 - y; // * 0.5 / (1 - 0.30)
+       int v = db * 409 - y; // * 0.5 / (1 - 0.11)
+       int sy = SHRR(y, 3) * SHRR(y, 4);
+       int su = SHRR(u, 3) * SHRR(u, 4);
+       int sv = SHRR(v, 3) * SHRR(v, 4);
+       return SHRR(sy, 4) + SHRR(su, 8) + SHRR(sv, 9);
+       // weight for u: sqrt(2^-4) / (0.5 / (1 - 0.30)) = 0.350
+       // weight for v: sqrt(2^-5) / (0.5 / (1 - 0.11)) = 0.315
+}
+
+inline int srgb_get_y(const color_t &a)
+{
+       // convert to linear
+       int r = a.r * (int) a.r;
+       int g = a.g * (int) a.g;
+       int b = a.b * (int) a.b;
+       // find luminance
+       int y = 37 * (r * 21*2*2 + g * 72 + b * 7*2*2); // multiplier: 14555800
+       // square root it (!)
+       y = sqrt(y); // now in range 0 to 3815
+       return y;
+}
+
+inline int color_dist_srgb_mixed(const color_t &a, const color_t &b)
+{
+       // get Y
+       int ay = srgb_get_y(a);
+       int by = srgb_get_y(b);
+       // get UV
+       int au = a.r * 191 - ay;
+       int av = a.b * 191 - ay;
+       int bu = b.r * 191 - by;
+       int bv = b.b * 191 - by;
+       // get differences
+       int y = ay - by;
+       int u = au - bu;
+       int v = av - bv;
+       return ((y*y) << 3) + SHRR(u*u, 1) + SHRR(v*v, 2);
+       // weight for u: ???
+       // weight for v: ???
+}
+
+// FIXME this is likely broken
+inline int color_dist_lab_srgb(const color_t &a, const color_t &b)
+{
+       // undo sRGB
+       float ar = powf(a.r / 31.0f, 2.4f);
+       float ag = powf(a.g / 63.0f, 2.4f);
+       float ab = powf(a.b / 31.0f, 2.4f);
+       float br = powf(b.r / 31.0f, 2.4f);
+       float bg = powf(b.g / 63.0f, 2.4f);
+       float bb = powf(b.b / 31.0f, 2.4f);
+       // convert to CIE XYZ
+       float aX = 0.4124f * ar + 0.3576f * ag + 0.1805f * ab;
+       float aY = 0.2126f * ar + 0.7152f * ag + 0.0722f * ab;
+       float aZ = 0.0193f * ar + 0.1192f * ag + 0.9505f * ab;
+       float bX = 0.4124f * br + 0.3576f * bg + 0.1805f * bb;
+       float bY = 0.2126f * br + 0.7152f * bg + 0.0722f * bb;
+       float bZ = 0.0193f * br + 0.1192f * bg + 0.9505f * bb;
+       // convert to CIE Lab
+       float Xn = 0.3127f;
+       float Yn = 0.3290f;
+       float Zn = 0.3583f;
+       float aL = 116 * cbrtf(aY / Yn) - 16;
+       float aA = 500 * (cbrtf(aX / Xn) - cbrtf(aY / Yn));
+       float aB = 200 * (cbrtf(aY / Yn) - cbrtf(aZ / Zn));
+       float bL = 116 * cbrtf(bY / Yn) - 16;
+       float bA = 500 * (cbrtf(bX / Xn) - cbrtf(bY / Yn));
+       float bB = 200 * (cbrtf(bY / Yn) - cbrtf(bZ / Zn));
+       // euclidean distance, but moving weight away from A and B
+       return 1000 * ((aL - bL) * (aL - bL) + (aA - bA) * (aA - bA) + (aB - bB) * (aB - bB));
+}
+
+inline int color_dist_normalmap(const color_t &a, const color_t &b)
+{
+       float ca[3], cb[3];
+       ca[0] = a.r / 31.0 * 2 - 1;
+       ca[1] = a.g / 63.0 * 2 - 1;
+       ca[2] = a.b / 31.0 * 2 - 1;
+       cb[0] = b.r / 31.0 * 2 - 1;
+       cb[1] = b.g / 63.0 * 2 - 1;
+       cb[2] = b.b / 31.0 * 2 - 1;
+
+       return
+               500 *
+               (
+                       (cb[0] - ca[0]) * (cb[0] - ca[0])
+                       +
+                       (cb[1] - ca[1]) * (cb[1] - ca[1])
+                       +
+                       (cb[2] - ca[2]) * (cb[2] - ca[2])
+               )
+               ;
+       // max value: 500 * (4 + 4 + 4) = 6000
+}
+
+typedef int ColorDistFunc(const color_t &a, const color_t &b);
+
+inline int alpha_dist(unsigned char a, unsigned char b)
+{
+       return (a - (int) b) * (a - (int) b);
+}
+
+template <class T, class F>
+// n: input count
+// m: total color count (including non-counted inputs)
+// m >= n
+void reduce_colors_inplace(T *c, int n, int m, F dist)
+{
+       int i, j, k;
+       int bestsum = -1;
+       int besti = 0;
+       int bestj = 1;
+       int dists[m][n];
+       // first the square
+       for(i = 0; i < n; ++i)
+       {
+               dists[i][i] = 0;
+               for(j = i+1; j < n; ++j)
+               {
+                       int d = dist(c[i], c[j]);
+                       dists[i][j] = dists[j][i] = d;
+               }
+       }
+       // then the box
+       for(; i < m; ++i)
+       {
+               for(j = 0; j < n; ++j)
+               {
+                       int d = dist(c[i], c[j]);
+                       dists[i][j] = d;
+               }
+       }
+       for(i = 0; i < m; ++i)
+               for(j = i+1; j < m; ++j)
+               {
+                       int sum = 0;
+                       for(k = 0; k < n; ++k)
+                       {
+                               int di = dists[i][k];
+                               int dj = dists[j][k];
+                               int m  = min(di, dj);
+                               sum += m;
+                       }
+                       if(bestsum < 0 || sum < bestsum)
+                       {
+                               bestsum = sum;
+                               besti = i;
+                               bestj = j;
+                       }
+               }
+       if(besti != 0)
+               c[0] = c[besti];
+       if(bestj != 1)
+               c[1] = c[bestj];
+}
+template <class T, class F>
+void reduce_colors_inplace_2fixpoints(T *c, int n, int m, F dist, const T &fix0, const T &fix1)
+{
+       int i, j, k;
+       int bestsum = -1;
+       int besti = 0;
+       int bestj = 1;
+       int dists[m+2][n];
+       // first the square
+       for(i = 0; i < n; ++i)
+       {
+               dists[i][i] = 0;
+               for(j = i+1; j < n; ++j)
+               {
+                       int d = dist(c[i], c[j]);
+                       dists[i][j] = dists[j][i] = d;
+               }
+       }
+       // then the box
+       for(; i < m; ++i)
+       {
+               for(j = 0; j < n; ++j)
+               {
+                       int d = dist(c[i], c[j]);
+                       dists[i][j] = d;
+               }
+       }
+       // then the two extra rows
+       for(j = 0; j < n; ++j)
+       {
+               int d = dist(fix0, c[j]);
+               dists[m][j] = d;
+       }
+       for(j = 0; j < n; ++j)
+       {
+               int d = dist(fix1, c[j]);
+               dists[m+1][j] = d;
+       }
+       for(i = 0; i < m; ++i)
+               for(j = i+1; j < m; ++j)
+               {
+                       int sum = 0;
+                       for(k = 0; k < n; ++k)
+                       {
+                               int di = dists[i][k];
+                               int dj = dists[j][k];
+                               int d0 = dists[m][k];
+                               int d1 = dists[m+1][k];
+                               int m  = min(min(di, dj), min(d0, d1));
+                               sum += m;
+                       }
+                       if(bestsum < 0 || sum < bestsum)
+                       {
+                               bestsum = sum;
+                               besti = i;
+                               bestj = j;
+                       }
+               }
+       if(besti != 0)
+               c[0] = c[besti];
+       if(bestj != 1)
+               c[1] = c[bestj];
+}
+
+inline int diffuse(float *diff, float src)
+{
+       int ret;
+       src += *diff;
+       ret = src;
+       *diff = (src - ret);
+       return ret;
+}
+
+void rgb565_image(unsigned char *out, const unsigned char *rgba, int w, int h, int alpharange)
+{
+       int x, y;
+       float diffuse_r = 0;
+       float diffuse_g = 0;
+       float diffuse_b = 0;
+       float diffuse_a = 0;
+       for(y = 0; y < h; ++y)
+               for(x = 0; x < w; ++x)
+               {
+                       out[(x + y * w) * 4 + 2] = diffuse(&diffuse_r, rgba[(x + y * w) * 4 + 2] * 31.0 / 255.0);
+                       out[(x + y * w) * 4 + 1] = diffuse(&diffuse_g, rgba[(x + y * w) * 4 + 1] * 63.0 / 255.0);
+                       out[(x + y * w) * 4 + 0] = diffuse(&diffuse_b, rgba[(x + y * w) * 4 + 0] * 31.0 / 255.0);
+                       out[(x + y * w) * 4 + 3] = diffuse(&diffuse_a, rgba[(x + y * w) * 4 + 3] * (alpharange / 255.0));
+               }
+}
+
+template<DxtMode dxt, ColorDistFunc ColorDist, bool userandom>
+void s2tc_encode_block(unsigned char *out, const unsigned char *rgba, int iw, int w, int h, int nrandom)
+{
+       color_t c[16 + (userandom ? nrandom : 0)];
+
+       unsigned char ca[16];
+       int n = 0, m = 0;
+       int x, y;
+
+       for(x = 0; x < w; ++x)
+               for(y = 0; y < h; ++y)
+               {
+                       c[n].r = rgba[(x + y * iw) * 4 + 2];
+                       c[n].g = rgba[(x + y * iw) * 4 + 1];
+                       c[n].b = rgba[(x + y * iw) * 4 + 0];
+                       if(dxt == DXT5)
+                               ca[n]  = rgba[(x + y * iw) * 4 + 3];
+                       ++n;
+               }
+
+       m = n;
+
+       if(userandom)
+       {
+               color_t mins = c[0];
+               color_t maxs = c[0];
+               for(x = 1; x < n; ++x)
+               {
+                       mins.r = min(mins.r, c[x].r);
+                       mins.g = min(mins.g, c[x].g);
+                       mins.b = min(mins.b, c[x].b);
+                       maxs.r = max(maxs.r, c[x].r);
+                       maxs.g = max(maxs.g, c[x].g);
+                       maxs.b = max(maxs.b, c[x].b);
+               }
+               color_t len = { maxs.r - mins.r + 1, maxs.g - mins.g + 1, maxs.b - mins.b + 1 };
+               for(x = 0; x < nrandom; ++x)
+               {
+                       c[m].r = mins.r + rand() % len.r;
+                       c[m].g = mins.g + rand() % len.g;
+                       c[m].b = mins.b + rand() % len.b;
+                       ++m;
+               }
+       }
+
+       reduce_colors_inplace(c, n, m, ColorDist);
+       if(dxt == DXT5)
+       {
+               reduce_colors_inplace_2fixpoints(ca, n, n, alpha_dist, (unsigned char) 0, (unsigned char) 255);
+               if(ca[1] < ca[0])
+               {
+                       ca[2] = ca[0];
+                       ca[0] = ca[1];
+                       ca[1] = ca[2];
+               }
+       }
+       if(c[1] < c[0])
+       {
+               c[2] = c[0];
+               c[0] = c[1];
+               c[1] = c[2];
+       }
+
+       memset(out, 0, 16);
+       switch(dxt)
+       {
+               case DXT5:
+                       out[0] = ca[0];
+                       out[1] = ca[1];
+               case DXT3:
+                       out[8] = ((c[0].g & 0x07) << 5) | c[0].b;
+                       out[9] = (c[0].r << 3) | (c[0].g >> 3);
+                       out[10] = ((c[1].g & 0x07) << 5) | c[1].b;
+                       out[11] = (c[1].r << 3) | (c[1].g >> 3);
+                       break;
+               case DXT1:
+                       out[0] = ((c[0].g & 0x07) << 5) | c[0].b;
+                       out[1] = (c[0].r << 3) | (c[0].g >> 3);
+                       out[2] = ((c[1].g & 0x07) << 5) | c[1].b;
+                       out[3] = (c[1].r << 3) | (c[1].g >> 3);
+                       break;
+       }
+       for(x = 0; x < w; ++x)
+               for(y = 0; y < h; ++y)
+               {
+                       int pindex = (x+y*4);
+                       c[2].r = rgba[(x + y * iw) * 4 + 2];
+                       c[2].g = rgba[(x + y * iw) * 4 + 1];
+                       c[2].b = rgba[(x + y * iw) * 4 + 0];
+                       ca[2]  = rgba[(x + y * iw) * 4 + 3];
+                       switch(dxt)
+                       {
+                               case DXT5:
+                                       {
+                                               int da[4];
+                                               int bitindex = pindex * 3;
+                                               da[0] = alpha_dist(ca[0], ca[2]);
+                                               da[1] = alpha_dist(ca[1], ca[2]);
+                                               da[2] = alpha_dist(0, ca[2]);
+                                               da[3] = alpha_dist(255, ca[2]);
+                                               if(da[2] <= da[0] && da[2] <= da[1] && da[2] <= da[3])
+                                               {
+                                                       // 6
+                                                       ++bitindex;
+                                                       out[bitindex / 8 + 2] |= (1 << (bitindex % 8));
+                                                       ++bitindex;
+                                                       out[bitindex / 8 + 2] |= (1 << (bitindex % 8));
+                                               }
+                                               else if(da[3] <= da[0] && da[3] <= da[1])
+                                               {
+                                                       // 7
+                                                       out[bitindex / 8 + 2] |= (1 << (bitindex % 8));
+                                                       ++bitindex;
+                                                       out[bitindex / 8 + 2] |= (1 << (bitindex % 8));
+                                                       ++bitindex;
+                                                       out[bitindex / 8 + 2] |= (1 << (bitindex % 8));
+                                               }
+                                               else if(da[0] <= da[1])
+                                               {
+                                                       // 0
+                                               }
+                                               else
+                                               {
+                                                       // 1
+                                                       out[bitindex / 8 + 2] |= (1 << (bitindex % 8));
+                                               }
+                                       }
+                                       if(ColorDist(c[0], c[2]) > ColorDist(c[1], c[2]))
+                                       {
+                                               int bitindex = pindex * 2;
+                                               out[bitindex / 8 + 12] |= (1 << (bitindex % 8));
+                                       }
+                                       break;
+                               case DXT3:
+                                       {
+                                               int bitindex = pindex * 4;
+                                               out[bitindex / 8 + 0] |= (ca[2] << (bitindex % 8));
+                                       }
+                                       if(ColorDist(c[0], c[2]) > ColorDist(c[1], c[2]))
+                                       {
+                                               int bitindex = pindex * 2;
+                                               out[bitindex / 8 + 12] |= (1 << (bitindex % 8));
+                                       }
+                                       break;
+                               case DXT1:
+                                       {
+                                               int bitindex = pindex * 2;
+                                               if(!ca[2])
+                                                       out[bitindex / 8 + 4] |= (3 << (bitindex % 8));
+                                               else if(ColorDist(c[0], c[2]) > ColorDist(c[1], c[2]))
+                                                       out[bitindex / 8 + 4] |= (1 << (bitindex % 8));
+                                       }
+                                       break;
+                       }
+               }
+}
+
+// compile time dispatch magic
+template<DxtMode dxt, ColorDistFunc ColorDist>
+void s2tc_encode_block(unsigned char *out, const unsigned char *rgba, int iw, int w, int h, int nrandom)
+{
+       if(nrandom)
+               s2tc_encode_block<dxt, ColorDist, true>(out, rgba, iw, w, h, nrandom);
+       else
+               s2tc_encode_block<dxt, ColorDist, false>(out, rgba, iw, w, h, nrandom);
+}
+
+template<ColorDistFunc ColorDist>
+void s2tc_encode_block(unsigned char *out, const unsigned char *rgba, int iw, int w, int h, DxtMode dxt, int nrandom)
+{
+       switch(dxt)
+       {
+               case DXT1:
+                       s2tc_encode_block<DXT1, ColorDist>(out, rgba, iw, w, h, nrandom);
+                       break;
+               case DXT3:
+                       s2tc_encode_block<DXT3, ColorDist>(out, rgba, iw, w, h, nrandom);
+                       break;
+               default:
+               case DXT5:
+                       s2tc_encode_block<DXT5, ColorDist>(out, rgba, iw, w, h, nrandom);
+                       break;
+       }
+}
+
+void s2tc_encode_block(unsigned char *out, const unsigned char *rgba, int iw, int w, int h, DxtMode dxt, ColorDistMode cd, int nrandom)
+{
+       switch(cd)
+       {
+               case RGB:
+                       s2tc_encode_block<color_dist_rgb>(out, rgba, iw, w, h, dxt, nrandom);
+                       break;
+               case YUV:
+                       s2tc_encode_block<color_dist_yuv>(out, rgba, iw, w, h, dxt, nrandom);
+                       break;
+               case SRGB:
+                       s2tc_encode_block<color_dist_srgb>(out, rgba, iw, w, h, dxt, nrandom);
+                       break;
+               case SRGB_MIXED:
+                       s2tc_encode_block<color_dist_srgb_mixed>(out, rgba, iw, w, h, dxt, nrandom);
+                       break;
+               case LAB:
+                       s2tc_encode_block<color_dist_lab_srgb>(out, rgba, iw, w, h, dxt, nrandom);
+                       break;
+               case AVG:
+                       s2tc_encode_block<color_dist_avg>(out, rgba, iw, w, h, dxt, nrandom);
+                       break;
+               case NORMALMAP:
+                       s2tc_encode_block<color_dist_normalmap>(out, rgba, iw, w, h, dxt, nrandom);
+                       break;
+       }
+}
diff --git a/s2tc_compressor.h b/s2tc_compressor.h
new file mode 100644 (file)
index 0000000..d22d565
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef S2TC_COMPRESSOR_H
+#define S2TC_COMPRESSOR_H
+
+void rgb565_image(unsigned char *out, const unsigned char *rgba, int w, int h, int alpharange);
+
+enum DxtMode
+{
+       DXT1,
+       DXT3,
+       DXT5
+};
+enum ColorDistMode
+{
+       RGB,
+       YUV,
+       SRGB,
+       SRGB_MIXED,
+       LAB,
+       AVG,
+       NORMALMAP
+};
+
+void s2tc_encode_block(unsigned char *out, const unsigned char *rgba, int iw, int w, int h, DxtMode dxt, ColorDistMode cd, int nrandom);
+
+#endif
diff --git a/test.sh b/test.sh
index 2cf9d83..fe611e7 100644 (file)
--- a/test.sh
+++ b/test.sh
@@ -3,7 +3,7 @@
 set -ex
 
 CXX="g++ -Wall -Wextra -O3"
-$CXX s2tc.cpp -o s2tc
+make clean all
 
 mkdir -p html
 exec 3>html/index.html