etcpack.c 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. #include "etcpack.h"
  2. #include "libc.h"
  3. static const int etc1_mod_table[8][4] = {
  4. {2, 8, -2, -8},
  5. {5, 17, -5, -17},
  6. {9, 29, -9, -29},
  7. {13, 42, -13, -42},
  8. {18, 60, -18, -60},
  9. {24, 80, -24, -80},
  10. {33, 106, -33, -106},
  11. {47, 183, -47, -183},
  12. };
  13. typedef struct {
  14. ETC1Color base;
  15. int table;
  16. int error;
  17. uint8_t msb, lsb;
  18. } ETC1SubblockPacked;
  19. static int clamp8(int i) { return (i < 0) ? 0 : (i > 255) ? 255 : i; }
  20. #define ETC1_PIXEL_ERROR_MAX 1000
  21. static int etc1PixelError(ETC1Color a, ETC1Color b) {
  22. return abs(a.r - b.r) + abs(a.g - b.g) + abs(a.b - b.b);
  23. }
  24. static ETC1SubblockPacked etc1PackSubblock2x4(const ETC1Color *in4x2) {
  25. ETC1Color average = {.r = 0, .g = 0, .b = 0};
  26. for (int i = 0; i < 8; ++i) {
  27. average.r += in4x2[i].r;
  28. average.g += in4x2[i].g;
  29. average.b += in4x2[i].b;
  30. }
  31. average.r >>= 3;
  32. average.g >>= 3;
  33. average.b >>= 3;
  34. ETC1SubblockPacked packed = {
  35. .error = ETC1_PIXEL_ERROR_MAX * 8,
  36. };
  37. for (int itbl = 0; itbl < 8; ++itbl) {
  38. const int *const pmod = etc1_mod_table[itbl];
  39. ETC1SubblockPacked variant = {
  40. .base = average,
  41. .table = itbl,
  42. .error = 0,
  43. .msb = 0, .lsb = 0,
  44. };
  45. for (int ip = 0; ip < 8; ++ip) {
  46. const ETC1Color c = in4x2[ip];
  47. int best_pixel_error = ETC1_PIXEL_ERROR_MAX;
  48. int best_pixel_imod = 0;
  49. for (int im = 0; im < 4; ++im) {
  50. const int mod = pmod[im];
  51. const ETC1Color mc = {
  52. .r = clamp8(variant.base.r + mod),
  53. .g = clamp8(variant.base.g + mod),
  54. .b = clamp8(variant.base.b + mod)
  55. };
  56. const int perr = etc1PixelError(c, mc);
  57. if (perr < best_pixel_error) {
  58. best_pixel_error = perr;
  59. best_pixel_imod = im;
  60. }
  61. }
  62. variant.lsb >>= 1;
  63. variant.msb >>= 1;
  64. variant.lsb |= (best_pixel_imod & 1) << 7;
  65. variant.msb |= (best_pixel_imod & 2) << 7;
  66. variant.error += best_pixel_error;
  67. }
  68. if (variant.error < packed.error)
  69. packed = variant;
  70. }
  71. return packed;
  72. }
  73. void etc1PackBlock(const ETC1Color *in4x4, uint8_t *out) {
  74. const ETC1SubblockPacked sub1 = etc1PackSubblock2x4(in4x4);
  75. const ETC1SubblockPacked sub2 = etc1PackSubblock2x4(in4x4 + 8);
  76. out[0] = (sub1.base.r & 0xf0) | (sub2.base.r >> 4);
  77. out[1] = (sub1.base.g & 0xf0) | (sub2.base.g >> 4);
  78. out[2] = (sub1.base.b & 0xf0) | (sub2.base.b >> 4);
  79. out[3] = (sub1.table << 5) | (sub2.table << 2) | 0x00; // diffbit = 0, flipbit = 0
  80. out[4] = sub2.msb;
  81. out[5] = sub1.msb;
  82. out[6] = sub2.lsb;
  83. out[7] = sub1.lsb;
  84. }