2
0

basisu_pvrtc1_4.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. // basisu_pvrtc1_4.cpp
  2. // Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. #pragma once
  16. #include "basisu_gpu_texture.h"
  17. namespace basisu
  18. {
  19. enum
  20. {
  21. PVRTC2_MIN_WIDTH = 16,
  22. PVRTC2_MIN_HEIGHT = 8,
  23. PVRTC4_MIN_WIDTH = 8,
  24. PVRTC4_MIN_HEIGHT = 8
  25. };
  26. struct pvrtc4_block
  27. {
  28. uint32_t m_modulation;
  29. uint32_t m_endpoints;
  30. pvrtc4_block() : m_modulation(0), m_endpoints(0) { }
  31. inline bool operator== (const pvrtc4_block& rhs) const
  32. {
  33. return (m_modulation == rhs.m_modulation) && (m_endpoints == rhs.m_endpoints);
  34. }
  35. inline void clear()
  36. {
  37. m_modulation = 0;
  38. m_endpoints = 0;
  39. }
  40. inline bool get_block_uses_transparent_modulation() const
  41. {
  42. return (m_endpoints & 1) != 0;
  43. }
  44. inline bool is_endpoint_opaque(uint32_t endpoint_index) const
  45. {
  46. static const uint32_t s_bitmasks[2] = { 0x8000U, 0x80000000U };
  47. return (m_endpoints & s_bitmasks[open_range_check(endpoint_index, 2U)]) != 0;
  48. }
  49. // Returns raw endpoint or 8888
  50. color_rgba get_endpoint(uint32_t endpoint_index, bool unpack) const;
  51. color_rgba get_endpoint_5554(uint32_t endpoint_index) const;
  52. static uint32_t get_component_precision_in_bits(uint32_t c, uint32_t endpoint_index, bool opaque_endpoint)
  53. {
  54. static const uint32_t s_comp_prec[4][4] =
  55. {
  56. // R0 G0 B0 A0 R1 G1 B1 A1
  57. { 4, 4, 3, 3 }, { 4, 4, 4, 3 }, // transparent endpoint
  58. { 5, 5, 4, 0 }, { 5, 5, 5, 0 } // opaque endpoint
  59. };
  60. return s_comp_prec[open_range_check(endpoint_index, 2U) + (opaque_endpoint * 2)][open_range_check(c, 4U)];
  61. }
  62. static color_rgba get_color_precision_in_bits(uint32_t endpoint_index, bool opaque_endpoint)
  63. {
  64. static const color_rgba s_color_prec[4] =
  65. {
  66. color_rgba(4, 4, 3, 3), color_rgba(4, 4, 4, 3), // transparent endpoint
  67. color_rgba(5, 5, 4, 0), color_rgba(5, 5, 5, 0) // opaque endpoint
  68. };
  69. return s_color_prec[open_range_check(endpoint_index, 2U) + (opaque_endpoint * 2)];
  70. }
  71. inline uint32_t get_modulation(uint32_t x, uint32_t y) const
  72. {
  73. assert((x < 4) && (y < 4));
  74. return (m_modulation >> ((y * 4 + x) * 2)) & 3;
  75. }
  76. inline void set_modulation(uint32_t x, uint32_t y, uint32_t s)
  77. {
  78. assert((x < 4) && (y < 4) && (s < 4));
  79. uint32_t n = (y * 4 + x) * 2;
  80. m_modulation = (m_modulation & (~(3 << n))) | (s << n);
  81. assert(get_modulation(x, y) == s);
  82. }
  83. // Scaled by 8
  84. inline const uint32_t* get_scaled_modulation_values(bool block_uses_transparent_modulation) const
  85. {
  86. static const uint32_t s_block_scales[2][4] = { { 0, 3, 5, 8 }, { 0, 4, 4, 8 } };
  87. return s_block_scales[block_uses_transparent_modulation];
  88. }
  89. // Scaled by 8
  90. inline uint32_t get_scaled_modulation(uint32_t x, uint32_t y) const
  91. {
  92. return get_scaled_modulation_values(get_block_uses_transparent_modulation())[get_modulation(x, y)];
  93. }
  94. inline void byte_swap()
  95. {
  96. m_modulation = byteswap32(m_modulation);
  97. m_endpoints = byteswap32(m_endpoints);
  98. }
  99. // opaque endpoints: 554, 555
  100. // transparent endpoints: 3443, 3444
  101. inline void set_endpoint_raw(uint32_t endpoint_index, const color_rgba& c, bool opaque_endpoint)
  102. {
  103. assert(endpoint_index < 2);
  104. const uint32_t m = m_endpoints & 1;
  105. uint32_t r = c[0], g = c[1], b = c[2], a = c[3];
  106. uint32_t packed;
  107. if (opaque_endpoint)
  108. {
  109. if (!endpoint_index)
  110. {
  111. // 554
  112. // 1RRRRRGGGGGBBBBM
  113. assert((r < 32) && (g < 32) && (b < 16));
  114. packed = 0x8000 | (r << 10) | (g << 5) | (b << 1) | m;
  115. }
  116. else
  117. {
  118. // 555
  119. // 1RRRRRGGGGGBBBBB
  120. assert((r < 32) && (g < 32) && (b < 32));
  121. packed = 0x8000 | (r << 10) | (g << 5) | b;
  122. }
  123. }
  124. else
  125. {
  126. if (!endpoint_index)
  127. {
  128. // 3443
  129. // 0AAA RRRR GGGG BBBM
  130. assert((r < 16) && (g < 16) && (b < 8) && (a < 8));
  131. packed = (a << 12) | (r << 8) | (g << 4) | (b << 1) | m;
  132. }
  133. else
  134. {
  135. // 3444
  136. // 0AAA RRRR GGGG BBBB
  137. assert((r < 16) && (g < 16) && (b < 16) && (a < 8));
  138. packed = (a << 12) | (r << 8) | (g << 4) | b;
  139. }
  140. }
  141. assert(packed <= 0xFFFF);
  142. if (endpoint_index)
  143. m_endpoints = (m_endpoints & 0xFFFFU) | (packed << 16);
  144. else
  145. m_endpoints = (m_endpoints & 0xFFFF0000U) | packed;
  146. }
  147. };
  148. typedef vector2D<pvrtc4_block> pvrtc4_block_vector2D;
  149. uint32_t pvrtc4_swizzle_uv(uint32_t XSize, uint32_t YSize, uint32_t XPos, uint32_t YPos);
  150. class pvrtc4_image
  151. {
  152. public:
  153. inline pvrtc4_image() :
  154. m_width(0), m_height(0), m_block_width(0), m_block_height(0), m_uses_alpha(false)
  155. {
  156. }
  157. inline pvrtc4_image(uint32_t width, uint32_t height) :
  158. m_width(0), m_height(0), m_block_width(0), m_block_height(0), m_uses_alpha(false)
  159. {
  160. resize(width, height);
  161. }
  162. inline void clear()
  163. {
  164. m_width = 0;
  165. m_height = 0;
  166. m_block_width = 0;
  167. m_block_height = 0;
  168. m_blocks.clear();
  169. m_uses_alpha = false;
  170. }
  171. inline void resize(uint32_t width, uint32_t height)
  172. {
  173. if ((width == m_width) && (height == m_height))
  174. return;
  175. m_width = width;
  176. m_height = height;
  177. m_block_width = (width + 3) >> 2;
  178. m_block_height = (height + 3) >> 2;
  179. m_blocks.resize(m_block_width, m_block_height);
  180. }
  181. inline uint32_t get_width() const { return m_width; }
  182. inline uint32_t get_height() const { return m_height; }
  183. inline uint32_t get_block_width() const { return m_block_width; }
  184. inline uint32_t get_block_height() const { return m_block_height; }
  185. inline const pvrtc4_block_vector2D &get_blocks() const { return m_blocks; }
  186. inline pvrtc4_block_vector2D &get_blocks() { return m_blocks; }
  187. inline uint32_t get_total_blocks() const { return m_block_width * m_block_height; }
  188. inline bool get_uses_alpha() const { return m_uses_alpha; }
  189. inline void set_uses_alpha(bool uses_alpha) { m_uses_alpha = uses_alpha; }
  190. inline bool are_blocks_equal(const pvrtc4_image& rhs) const
  191. {
  192. return m_blocks == rhs.m_blocks;
  193. }
  194. inline void set_to_black()
  195. {
  196. #ifndef __EMSCRIPTEN__
  197. #ifdef __GNUC__
  198. #pragma GCC diagnostic push
  199. #pragma GCC diagnostic ignored "-Wclass-memaccess"
  200. #endif
  201. #endif
  202. memset(m_blocks.get_ptr(), 0, m_blocks.size_in_bytes());
  203. #ifndef __EMSCRIPTEN__
  204. #ifdef __GNUC__
  205. #pragma GCC diagnostic pop
  206. #endif
  207. #endif
  208. }
  209. inline bool get_block_uses_transparent_modulation(uint32_t bx, uint32_t by) const
  210. {
  211. return m_blocks(bx, by).get_block_uses_transparent_modulation();
  212. }
  213. inline bool is_endpoint_opaque(uint32_t bx, uint32_t by, uint32_t endpoint_index) const
  214. {
  215. return m_blocks(bx, by).is_endpoint_opaque(endpoint_index);
  216. }
  217. color_rgba get_endpoint(uint32_t bx, uint32_t by, uint32_t endpoint_index, bool unpack) const
  218. {
  219. assert((bx < m_block_width) && (by < m_block_height));
  220. return m_blocks(bx, by).get_endpoint(endpoint_index, unpack);
  221. }
  222. inline uint32_t get_modulation(uint32_t x, uint32_t y) const
  223. {
  224. assert((x < m_width) && (y < m_height));
  225. return m_blocks(x >> 2, y >> 2).get_modulation(x & 3, y & 3);
  226. }
  227. // Returns true if the block uses transparent modulation.
  228. bool get_interpolated_colors(uint32_t x, uint32_t y, color_rgba* pColors) const;
  229. color_rgba get_pixel(uint32_t x, uint32_t y, uint32_t m) const;
  230. inline color_rgba get_pixel(uint32_t x, uint32_t y) const
  231. {
  232. assert((x < m_width) && (y < m_height));
  233. return get_pixel(x, y, m_blocks(x >> 2, y >> 2).get_modulation(x & 3, y & 3));
  234. }
  235. void deswizzle()
  236. {
  237. pvrtc4_block_vector2D temp(m_blocks);
  238. for (uint32_t y = 0; y < m_block_height; y++)
  239. for (uint32_t x = 0; x < m_block_width; x++)
  240. m_blocks(x, y) = temp[pvrtc4_swizzle_uv(m_block_width, m_block_height, x, y)];
  241. }
  242. void swizzle()
  243. {
  244. pvrtc4_block_vector2D temp(m_blocks);
  245. for (uint32_t y = 0; y < m_block_height; y++)
  246. for (uint32_t x = 0; x < m_block_width; x++)
  247. m_blocks[pvrtc4_swizzle_uv(m_block_width, m_block_height, x, y)] = temp(x, y);
  248. }
  249. void unpack_all_pixels(image& img) const
  250. {
  251. img.crop(m_width, m_height);
  252. for (uint32_t y = 0; y < m_height; y++)
  253. for (uint32_t x = 0; x < m_width; x++)
  254. img(x, y) = get_pixel(x, y);
  255. }
  256. void unpack_block(image &dst, uint32_t block_x, uint32_t block_y)
  257. {
  258. for (uint32_t y = 0; y < 4; y++)
  259. for (uint32_t x = 0; x < 4; x++)
  260. dst(x, y) = get_pixel(block_x * 4 + x, block_y * 4 + y);
  261. }
  262. inline int wrap_x(int x) const
  263. {
  264. return posmod(x, m_width);
  265. }
  266. inline int wrap_y(int y) const
  267. {
  268. return posmod(y, m_height);
  269. }
  270. inline int wrap_block_x(int bx) const
  271. {
  272. return posmod(bx, m_block_width);
  273. }
  274. inline int wrap_block_y(int by) const
  275. {
  276. return posmod(by, m_block_height);
  277. }
  278. inline vec2F get_interpolation_factors(uint32_t x, uint32_t y) const
  279. {
  280. // 0 1 2 3
  281. // 2 3 0 1
  282. // .5 .75 0 .25
  283. static const float s_interp[4] = { 2, 3, 0, 1 };
  284. return vec2F(s_interp[x & 3], s_interp[y & 3]);
  285. }
  286. inline color_rgba interpolate(int x, int y,
  287. const color_rgba& p, const color_rgba& q,
  288. const color_rgba& r, const color_rgba& s) const
  289. {
  290. static const int s_interp[4] = { 2, 3, 0, 1 };
  291. const int u_interp = s_interp[x & 3];
  292. const int v_interp = s_interp[y & 3];
  293. color_rgba result;
  294. for (uint32_t c = 0; c < 4; c++)
  295. {
  296. int t = p[c] * 4 + u_interp * ((int)q[c] - (int)p[c]);
  297. int b = r[c] * 4 + u_interp * ((int)s[c] - (int)r[c]);
  298. int v = t * 4 + v_interp * (b - t);
  299. if (c < 3)
  300. {
  301. v >>= 1;
  302. v += (v >> 5);
  303. }
  304. else
  305. {
  306. v += (v >> 4);
  307. }
  308. assert((v >= 0) && (v < 256));
  309. result[c] = static_cast<uint8_t>(v);
  310. }
  311. return result;
  312. }
  313. inline void set_modulation(uint32_t x, uint32_t y, uint32_t s)
  314. {
  315. assert((x < m_width) && (y < m_height));
  316. return m_blocks(x >> 2, y >> 2).set_modulation(x & 3, y & 3, s);
  317. }
  318. inline uint64_t map_pixel(uint32_t x, uint32_t y, const color_rgba& c, bool perceptual, bool alpha_is_significant, bool record = true)
  319. {
  320. color_rgba v[4];
  321. get_interpolated_colors(x, y, v);
  322. uint64_t best_dist = color_distance(perceptual, c, v[0], alpha_is_significant);
  323. uint32_t best_v = 0;
  324. for (uint32_t i = 1; i < 4; i++)
  325. {
  326. uint64_t dist = color_distance(perceptual, c, v[i], alpha_is_significant);
  327. if (dist < best_dist)
  328. {
  329. best_dist = dist;
  330. best_v = i;
  331. }
  332. }
  333. if (record)
  334. set_modulation(x, y, best_v);
  335. return best_dist;
  336. }
  337. inline uint64_t remap_pixels_influenced_by_endpoint(uint32_t bx, uint32_t by, const image& orig_img, bool perceptual, bool alpha_is_significant)
  338. {
  339. uint64_t total_error = 0;
  340. for (int yd = -3; yd <= 3; yd++)
  341. {
  342. const int y = wrap_y((int)by * 4 + 2 + yd);
  343. for (int xd = -3; xd <= 3; xd++)
  344. {
  345. const int x = wrap_x((int)bx * 4 + 2 + xd);
  346. total_error += map_pixel(x, y, orig_img(x, y), perceptual, alpha_is_significant);
  347. }
  348. }
  349. return total_error;
  350. }
  351. inline uint64_t evaluate_1x1_endpoint_error(uint32_t bx, uint32_t by, const image& orig_img, bool perceptual, bool alpha_is_significant, uint64_t threshold_error = 0) const
  352. {
  353. uint64_t total_error = 0;
  354. for (int yd = -3; yd <= 3; yd++)
  355. {
  356. const int y = wrap_y((int)by * 4 + 2 + yd);
  357. for (int xd = -3; xd <= 3; xd++)
  358. {
  359. const int x = wrap_x((int)bx * 4 + 2 + xd);
  360. total_error += color_distance(perceptual, get_pixel(x, y), orig_img(x, y), alpha_is_significant);
  361. if ((threshold_error) && (total_error >= threshold_error))
  362. return total_error;
  363. }
  364. }
  365. return total_error;
  366. }
  367. uint64_t local_endpoint_optimization_opaque(uint32_t bx, uint32_t by, const image& orig_img, bool perceptual);
  368. inline uint64_t map_all_pixels(const image& img, bool perceptual, bool alpha_is_significant)
  369. {
  370. assert(m_width == img.get_width());
  371. assert(m_height == img.get_height());
  372. uint64_t total_error = 0;
  373. for (uint32_t y = 0; y < img.get_height(); y++)
  374. for (uint32_t x = 0; x < img.get_width(); x++)
  375. total_error += map_pixel(x, y, img(x, y), perceptual, alpha_is_significant);
  376. return total_error;
  377. }
  378. public:
  379. uint32_t m_width, m_height;
  380. pvrtc4_block_vector2D m_blocks;
  381. uint32_t m_block_width, m_block_height;
  382. bool m_uses_alpha;
  383. };
  384. } // namespace basisu