/
redgpu
/
ezEngine
Обзор
Документация
Войти
/
redgpu
/
ezEngine
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
dev
Code/ThirdParty/bc7enc_rdo/rgbcx.cpp
3 080 строк
121 KB
Sanakan8472
Added BC7Enc Library for Linux (#1326)
08 июн 2024, 08:46
Не верифицирован
08 июн 2024, 08:46
b726d9c
Код
Авторство
О чём код?
// rgbcx.cpp - see license at end of rgbcx.h #include "rgbcx.h" #include <string.h> #include <math.h> #include <vector> namespace rgbcx { const uint8_t g_bc1_to_linear[4] = { 0, 3, 1, 2 }; const uint32_t NUM_UNIQUE_TOTAL_ORDERINGS4 = 969; #ifdef _MSC_VER #pragma region #endif // All total orderings for 16 pixels 2-bit selectors. // BC1 selector order 0, 2, 3, 1 (i.e. the selectors are reordered into linear order). static uint8_t g_unique_total_orders4[NUM_UNIQUE_TOTAL_ORDERINGS4][4] = { {0,8,2,6},{4,3,9,0},{4,8,1,3},{12,0,3,1},{11,3,2,0},{6,4,6,0},{7,5,0,4},{6,0,8,2},{1,0,0,15},{3,0,8,5},{1,1,13,1},{13,1,2,0},{0,14,1,1},{0,15,1,0},{0,13,0,3},{16,0,0,0},{4,3,4,5},{8,6,0,2},{0,10,0,6},{10,0,4,2},{7,2,1,6},{4,7,5,0},{1,4,7,4},{0,14,2,0},{2,7,2,5},{9,0,5,2},{9,2,2,3},{10,0,5,1},{2,3,7,4},{4,9,0,3},{1,5,0,10},{1,1,6,8}, {6,6,4,0},{11,5,0,0},{11,2,0,3},{4,0,10,2},{2,3,10,1},{1,13,1,1},{0,14,0,2},{2,3,3,8},{12,3,1,0},{14,0,0,2},{9,1,3,3},{6,4,0,6},{1,1,5,9},{5,9,0,2},{2,10,1,3},{12,0,0,4},{4,6,6,0},{0,6,4,6},{3,7,4,2},{0,13,3,0},{3,10,0,3},{10,2,1,3},{1,12,1,2},{2,0,13,1},{11,0,5,0},{12,1,3,0},{6,4,5,1},{10,4,2,0},{3,6,1,6},{7,3,6,0},{10,4,0,2},{10,0,2,4}, {0,5,9,2},{0,9,3,4},{6,4,2,4},{3,4,7,2},{3,3,5,5},{4,2,9,1},{6,2,8,0},{3,5,3,5},{4,10,1,1},{10,1,3,2},{5,7,0,4},{5,3,7,1},{6,8,1,1},{8,8,0,0},{11,1,0,4},{14,1,0,1},{9,3,2,2},{8,2,1,5},{0,0,2,14},{3,3,9,1},{10,1,5,0},{8,3,1,4},{1,5,8,2},{6,1,9,0},{3,2,1,10},{3,11,1,1},{7,6,3,0},{9,0,3,4},{5,2,5,4},{0,2,3,11},{15,0,0,1},{0,6,6,4}, {3,4,9,0},{4,7,0,5},{0,4,4,8},{0,13,2,1},{2,4,1,9},{3,2,5,6},{10,6,0,0},{3,5,6,2},{8,0,4,4},{1,3,6,6},{7,7,0,2},{6,1,4,5},{0,11,1,4},{2,2,8,4},{0,1,2,13},{15,0,1,0},{7,2,6,1},{8,1,7,0},{1,8,4,3},{2,13,1,0},{1,0,7,8},{14,2,0,0},{1,8,1,6},{9,3,3,1},{0,0,7,9},{4,4,1,7},{9,0,6,1},{10,2,4,0},{1,7,3,5},{0,3,8,5},{5,2,4,5},{1,2,5,8}, {0,8,7,1},{10,3,2,1},{12,0,4,0},{2,1,4,9},{5,2,2,7},{1,9,3,3},{15,1,0,0},{6,3,4,3},{9,5,0,2},{1,6,9,0},{6,6,0,4},{13,2,1,0},{5,1,8,2},{0,5,11,0},{7,1,0,8},{1,2,12,1},{0,3,3,10},{7,4,2,3},{5,1,4,6},{7,0,3,6},{3,12,0,1},{3,4,5,4},{1,10,0,5},{7,4,3,2},{10,5,0,1},{13,3,0,0},{2,5,4,5},{3,10,1,2},{5,1,2,8},{14,0,1,1},{1,5,4,6},{1,4,5,6}, {2,3,11,0},{11,0,4,1},{11,2,2,1},{5,3,8,0},{1,3,10,2},{0,1,13,2},{3,1,4,8},{4,2,4,6},{1,5,6,4},{2,1,11,2},{1,2,9,4},{4,7,3,2},{6,2,5,3},{7,2,2,5},{8,1,4,3},{3,2,8,3},{12,1,0,3},{7,8,1,0},{7,0,2,7},{5,10,0,1},{0,2,14,0},{2,9,3,2},{7,0,0,9},{11,1,4,0},{10,4,1,1},{2,2,9,3},{5,7,2,2},{1,3,1,11},{13,2,0,1},{4,2,8,2},{2,3,1,10},{4,2,5,5}, {7,0,7,2},{10,0,0,6},{0,8,5,3},{4,4,0,8},{12,4,0,0},{0,1,14,1},{8,0,1,7},{5,1,5,5},{11,0,3,2},{0,4,1,11},{0,8,8,0},{0,2,5,9},{7,3,2,4},{7,8,0,1},{1,0,3,12},{7,4,5,0},{1,6,7,2},{7,6,1,2},{9,6,1,0},{12,2,0,2},{4,1,6,5},{4,0,1,11},{8,4,4,0},{13,0,1,2},{8,6,2,0},{4,12,0,0},{2,7,5,2},{2,0,5,9},{5,4,5,2},{3,8,5,0},{7,3,3,3},{4,4,8,0}, {2,1,3,10},{5,0,1,10},{6,4,3,3},{4,9,1,2},{1,4,0,11},{11,3,1,1},{4,0,12,0},{13,0,0,3},{6,1,6,3},{9,0,4,3},{8,0,0,8},{8,4,0,4},{0,12,1,3},{0,4,10,2},{3,4,8,1},{1,3,8,4},{9,2,5,0},{5,7,4,0},{1,0,11,4},{4,10,0,2},{1,3,12,0},{6,9,0,1},{5,0,9,2},{5,9,2,0},{13,1,0,2},{9,3,4,0},{9,4,0,3},{3,1,12,0},{2,4,3,7},{1,2,13,0},{2,2,4,8},{6,8,0,2}, {9,2,1,4},{9,5,1,1},{2,0,4,10},{5,4,0,7},{0,0,6,10},{1,2,0,13},{4,7,2,3},{6,5,5,0},{3,3,1,9},{1,6,1,8},{12,2,1,1},{4,4,5,3},{1,0,6,9},{0,6,10,0},{4,8,3,1},{4,3,2,7},{2,1,7,6},{1,9,1,5},{3,1,3,9},{8,7,1,0},{1,2,3,10},{14,1,1,0},{5,4,4,3},{3,7,0,6},{7,4,1,4},{3,7,5,1},{1,1,0,14},{0,10,3,3},{0,4,3,9},{1,7,7,1},{2,0,10,4},{5,8,0,3}, {6,7,3,0},{0,8,4,4},{5,7,3,1},{7,9,0,0},{7,6,2,1},{0,4,5,7},{6,3,5,2},{1,2,1,12},{5,2,0,9},{8,5,0,3},{4,6,1,5},{1,1,7,7},{10,5,1,0},{1,2,8,5},{1,8,2,5},{5,1,0,10},{6,9,1,0},{13,0,2,1},{8,3,5,0},{6,3,6,1},{2,11,3,0},{3,7,3,3},{1,5,2,8},{7,5,2,2},{0,6,7,3},{13,1,1,1},{5,3,4,4},{7,2,7,0},{5,8,3,0},{3,13,0,0},{0,7,9,0},{8,0,3,5}, {1,3,7,5},{4,0,2,10},{12,0,1,3},{1,7,6,2},{3,9,0,4},{7,2,0,7},{0,1,7,8},{2,1,8,5},{0,13,1,2},{0,8,1,7},{5,0,11,0},{5,6,2,3},{0,3,0,13},{2,3,4,7},{5,6,3,2},{4,2,10,0},{3,3,7,3},{7,2,5,2},{1,1,11,3},{12,3,0,1},{5,1,1,9},{1,15,0,0},{9,7,0,0},{9,1,2,4},{0,7,3,6},{3,0,13,0},{3,0,11,2},{0,6,5,5},{8,2,2,4},{6,10,0,0},{4,8,4,0},{0,0,3,13}, {0,4,12,0},{7,1,6,2},{3,5,0,8},{8,0,6,2},{6,2,3,5},{2,10,0,4},{4,11,0,1},{6,1,5,4},{5,1,3,7},{0,11,3,2},{4,6,0,6},{2,6,0,8},{3,1,7,5},{2,14,0,0},{2,9,2,3},{0,3,4,9},{11,0,1,4},{13,0,3,0},{8,3,0,5},{0,5,3,8},{5,11,0,0},{0,1,4,11},{2,1,9,4},{3,4,4,5},{7,1,2,6},{12,2,2,0},{9,4,1,2},{6,0,2,8},{4,6,2,4},{11,2,3,0},{3,2,2,9},{10,3,1,2}, {1,1,2,12},{0,5,2,9},{0,1,11,4},{6,2,4,4},{2,8,2,4},{0,9,4,3},{11,0,2,3},{0,2,11,3},{6,0,7,3},{0,3,6,7},{4,5,5,2},{1,2,6,7},{7,5,1,3},{9,0,2,5},{2,6,4,4},{4,1,9,2},{4,8,2,2},{1,12,3,0},{0,9,6,1},{0,10,6,0},{3,1,5,7},{2,13,0,1},{2,2,1,11},{3,6,0,7},{5,6,5,0},{5,5,4,2},{4,0,3,9},{3,4,1,8},{0,11,2,3},{2,12,1,1},{7,1,3,5},{7,0,9,0}, {8,0,8,0},{1,0,2,13},{3,3,10,0},{2,4,4,6},{2,3,8,3},{1,10,5,0},{7,3,0,6},{2,9,0,5},{1,4,6,5},{6,6,3,1},{5,6,0,5},{6,3,0,7},{3,10,2,1},{2,5,5,4},{3,8,4,1},{1,14,0,1},{10,3,3,0},{3,5,7,1},{1,1,3,11},{2,4,0,10},{9,3,1,3},{5,10,1,0},{3,0,6,7},{3,1,9,3},{11,2,1,2},{5,3,3,5},{0,5,1,10},{4,1,11,0},{10,2,0,4},{7,6,0,3},{2,7,0,7},{4,2,2,8}, {6,1,7,2},{4,9,2,1},{0,0,8,8},{3,7,2,4},{9,6,0,1},{0,12,4,0},{6,7,1,2},{0,7,2,7},{1,0,10,5},{0,0,14,2},{2,7,3,4},{5,0,0,11},{7,7,1,1},{6,2,7,1},{4,5,3,4},{3,5,1,7},{5,9,1,1},{6,2,1,7},{3,2,0,11},{0,11,0,5},{3,11,2,0},{10,1,4,1},{7,0,4,5},{11,4,0,1},{10,3,0,3},{0,2,4,10},{0,15,0,1},{0,11,5,0},{6,7,2,1},{1,12,2,1},{4,1,3,8},{1,0,13,2}, {1,8,5,2},{7,0,1,8},{3,12,1,0},{9,2,4,1},{1,7,4,4},{11,4,1,0},{4,3,8,1},{2,8,4,2},{1,11,3,1},{1,1,4,10},{4,10,2,0},{8,2,5,1},{1,0,9,6},{5,3,2,6},{0,9,7,0},{10,2,2,2},{5,8,1,2},{8,7,0,1},{0,3,12,1},{1,0,1,14},{4,8,0,4},{3,8,0,5},{4,6,5,1},{0,9,5,2},{10,2,3,1},{2,3,9,2},{1,0,12,3},{11,3,0,2},{4,5,2,5},{0,2,12,2},{9,1,0,6},{9,2,0,5}, {1,2,7,6},{4,7,4,1},{0,12,2,2},{0,0,0,16},{2,8,3,3},{3,6,2,5},{0,6,3,7},{7,5,4,0},{3,3,3,7},{3,3,0,10},{5,0,6,5},{0,0,10,6},{8,5,3,0},{8,1,5,2},{6,0,9,1},{11,1,2,2},{2,11,2,1},{9,5,2,0},{3,0,4,9},{2,2,12,0},{2,6,6,2},{2,1,13,0},{6,0,5,5},{2,0,14,0},{2,11,1,2},{4,4,7,1},{2,0,11,3},{3,1,1,11},{2,9,4,1},{3,7,6,0},{14,0,2,0},{1,10,4,1}, {8,0,7,1},{3,6,5,2},{0,3,11,2},{2,5,6,3},{11,1,3,1},{6,5,3,2},{3,8,1,4},{0,2,7,7},{2,10,2,2},{1,6,2,7},{11,0,0,5},{12,1,1,2},{12,1,2,1},{0,7,1,8},{0,3,9,4},{0,2,1,13},{7,1,4,4},{10,1,0,5},{4,0,8,4},{5,2,7,2},{0,2,0,14},{4,3,7,2},{2,7,1,6},{1,2,2,11},{6,3,3,4},{1,14,1,0},{2,4,6,4},{5,3,6,2},{5,3,5,3},{8,4,1,3},{1,3,0,12},{3,5,2,6}, {1,8,7,0},{0,7,4,5},{2,1,6,7},{4,11,1,0},{7,2,4,3},{6,1,3,6},{4,5,4,3},{2,11,0,3},{1,5,7,3},{12,0,2,2},{5,0,4,7},{1,13,0,2},{7,7,2,0},{4,1,7,4},{4,5,0,7},{5,0,5,6},{6,5,4,1},{2,4,2,8},{1,10,1,4},{6,3,1,6},{3,3,8,2},{0,7,7,2},{4,4,2,6},{1,1,8,6},{1,12,0,3},{2,1,12,1},{1,9,2,4},{1,11,0,4},{2,5,2,7},{10,0,3,3},{4,6,3,3},{3,7,1,5}, {1,9,0,6},{7,1,7,1},{1,6,5,4},{9,2,3,2},{6,2,2,6},{2,2,2,10},{8,3,3,2},{0,1,8,7},{2,0,8,6},{0,3,1,12},{9,4,2,1},{9,4,3,0},{6,2,6,2},{1,8,0,7},{5,1,10,0},{0,5,5,6},{8,2,4,2},{2,3,2,9},{6,0,3,7},{2,2,6,6},{2,6,2,6},{1,13,2,0},{9,3,0,4},{7,3,5,1},{6,5,2,3},{5,2,6,3},{2,0,12,2},{5,7,1,3},{8,1,3,4},{3,1,10,2},{1,0,15,0},{0,8,0,8}, {5,0,7,4},{4,4,6,2},{0,1,0,15},{10,0,1,5},{7,3,4,2},{4,9,3,0},{2,5,7,2},{3,4,2,7},{8,3,2,3},{5,1,6,4},{0,10,2,4},{6,6,1,3},{6,0,0,10},{4,4,3,5},{1,3,9,3},{7,5,3,1},{3,0,7,6},{1,8,6,1},{4,3,0,9},{3,11,0,2},{6,0,6,4},{0,1,3,12},{0,4,2,10},{5,5,6,0},{4,1,4,7},{8,1,6,1},{5,6,4,1},{8,4,2,2},{4,3,1,8},{3,0,2,11},{1,11,4,0},{0,8,3,5}, {5,1,7,3},{7,0,8,1},{4,3,5,4},{4,6,4,2},{3,2,4,7},{1,6,3,6},{0,7,8,1},{3,0,1,12},{9,1,4,2},{7,4,0,5},{1,7,0,8},{5,4,1,6},{9,1,5,1},{1,1,9,5},{4,1,1,10},{5,3,0,8},{2,2,5,7},{4,0,0,12},{9,0,7,0},{3,4,0,9},{0,2,6,8},{8,2,0,6},{3,2,6,5},{4,2,6,4},{3,6,4,3},{2,8,6,0},{5,0,3,8},{0,4,0,12},{0,16,0,0},{0,9,2,5},{4,0,11,1},{1,6,4,5}, {0,1,6,9},{3,4,6,3},{3,0,10,3},{7,0,6,3},{1,4,9,2},{1,5,3,7},{8,5,2,1},{0,12,0,4},{7,2,3,4},{0,5,6,5},{11,1,1,3},{6,5,0,5},{2,1,5,8},{1,4,11,0},{9,1,1,5},{0,0,13,3},{5,8,2,1},{2,12,0,2},{3,3,6,4},{4,1,10,1},{4,0,5,7},{8,1,0,7},{5,1,9,1},{4,3,3,6},{0,2,2,12},{6,3,2,5},{0,0,12,4},{1,5,1,9},{2,6,5,3},{3,6,3,4},{2,12,2,0},{1,6,8,1}, {10,1,1,4},{1,3,4,8},{7,4,4,1},{1,11,1,3},{1,2,10,3},{3,9,3,1},{8,5,1,2},{2,10,4,0},{4,2,0,10},{2,7,6,1},{8,2,3,3},{1,5,5,5},{3,1,0,12},{3,10,3,0},{8,0,5,3},{0,6,8,2},{0,3,13,0},{0,0,16,0},{1,9,4,2},{4,1,8,3},{1,6,6,3},{0,10,5,1},{0,1,12,3},{4,0,6,6},{3,8,3,2},{0,5,4,7},{1,0,14,1},{0,4,6,6},{3,9,1,3},{3,5,8,0},{3,6,6,1},{5,4,7,0}, {3,0,12,1},{8,6,1,1},{2,9,5,0},{6,1,1,8},{4,1,2,9},{3,9,4,0},{5,2,9,0},{0,12,3,1},{1,4,10,1},{4,0,7,5},{3,1,2,10},{5,4,2,5},{5,5,5,1},{4,2,3,7},{1,7,5,3},{2,8,0,6},{8,1,2,5},{3,8,2,3},{6,1,2,7},{3,9,2,2},{9,0,0,7},{0,8,6,2},{8,4,3,1},{0,2,8,6},{6,5,1,4},{2,3,5,6},{2,10,3,1},{0,7,0,9},{4,2,7,3},{2,4,8,2},{7,1,1,7},{2,4,7,3}, {2,4,10,0},{0,1,10,5},{4,7,1,4},{0,10,4,2},{9,0,1,6},{1,9,6,0},{3,3,4,6},{4,5,7,0},{5,5,2,4},{2,8,1,5},{2,3,6,5},{0,1,1,14},{3,2,3,8},{10,1,2,3},{9,1,6,0},{3,4,3,6},{2,2,0,12},{0,0,9,7},{4,0,9,3},{7,0,5,4},{4,5,6,1},{2,5,1,8},{2,5,9,0},{3,5,4,4},{1,3,11,1},{7,1,5,3},{3,2,7,4},{1,4,2,9},{1,11,2,2},{2,2,3,9},{5,0,10,1},{3,2,11,0}, {1,10,3,2},{8,3,4,1},{3,6,7,0},{0,7,5,4},{1,3,3,9},{2,2,10,2},{1,9,5,1},{0,5,0,11},{3,0,3,10},{0,4,8,4},{2,7,7,0},{2,0,2,12},{1,2,11,2},{6,3,7,0},{0,6,2,8},{0,10,1,5},{0,9,0,7},{6,4,4,2},{6,0,1,9},{1,5,10,0},{5,4,6,1},{5,5,3,3},{0,0,4,12},{0,3,2,11},{1,4,1,10},{3,0,9,4},{5,5,0,6},{1,7,8,0},{2,0,3,11},{6,4,1,5},{10,0,6,0},{0,6,0,10}, {0,4,11,1},{3,1,6,6},{2,5,8,1},{0,2,10,4},{3,1,11,1},{6,6,2,2},{1,1,10,4},{2,1,2,11},{6,1,8,1},{0,2,13,1},{0,7,6,3},{6,8,2,0},{3,0,0,13},{4,4,4,4},{6,2,0,8},{7,3,1,5},{0,11,4,1},{6,7,0,3},{2,6,3,5},{5,2,1,8},{7,1,8,0},{5,5,1,5},{1,8,3,4},{8,2,6,0},{6,0,10,0},{5,6,1,4},{1,4,4,7},{2,7,4,3},{1,4,8,3},{5,4,3,4},{1,10,2,3},{2,9,1,4}, {2,2,11,1},{2,5,0,9},{0,0,1,15},{0,0,11,5},{0,4,7,5},{0,1,15,0},{2,1,0,13},{0,3,10,3},{8,0,2,6},{3,3,2,8},{3,5,5,3},{1,7,1,7},{1,3,2,10},{4,0,4,8},{2,0,9,5},{1,1,1,13},{2,2,7,5},{2,1,10,3},{4,2,1,9},{4,3,6,3},{1,3,5,7},{2,5,3,6},{1,0,8,7},{5,0,2,9},{2,8,5,1},{1,6,0,9},{0,0,5,11},{0,4,9,3},{2,0,7,7},{1,7,2,6},{2,1,1,12},{2,4,9,1}, {0,5,7,4},{6,0,4,6},{3,2,10,1},{0,6,1,9},{2,6,1,7},{0,5,8,3},{4,1,0,11},{1,2,4,9},{4,1,5,6},{6,1,0,9},{1,4,3,8},{4,5,1,6},{1,0,5,10},{5,3,1,7},{0,9,1,6},{2,0,1,13},{2,0,6,8},{8,1,1,6},{1,5,9,1},{0,6,9,1},{0,3,5,8},{0,2,9,5},{5,2,8,1},{1,1,14,0},{3,2,9,2},{5,0,8,3},{0,5,10,1},{5,2,3,6},{2,6,7,1},{2,3,0,11},{0,1,9,6},{1,0,4,11}, {3,0,5,8},{0,0,15,1},{2,4,5,5},{0,3,7,6},{2,0,0,14},{1,1,12,2},{2,6,8,0},{3,1,8,4},{0,1,5,10} }; // All total orderings for 16 pixels [0,2] 2-bit selectors. // BC1 selector order: 0, 1, 2 // Note this is different from g_unique_total_orders4[], which reorders the selectors into linear order. const uint32_t NUM_UNIQUE_TOTAL_ORDERINGS3 = 153; static uint8_t g_unique_total_orders3[NUM_UNIQUE_TOTAL_ORDERINGS3][3] = { {6,0,10},{3,6,7},{3,0,13},{13,3,0},{12,4,0},{9,1,6},{2,13,1},{4,7,5},{7,5,4},{9,6,1},{7,4,5},{8,6,2},{16,0,0},{10,6,0},{2,7,7}, {0,0,16},{0,3,13},{1,15,0},{0,2,14},{1,4,11},{15,1,0},{1,12,3},{9,2,5},{14,1,1},{8,2,6},{3,3,10},{4,2,10},{14,0,2},{0,14,2},{1,7,8},{6,6,4}, {11,5,0},{6,4,6},{11,3,2},{4,3,9},{7,1,8},{10,4,2},{12,1,3},{11,0,5},{9,3,4},{1,0,15},{9,0,7},{2,6,8},{12,2,2},{6,2,8},{6,8,2},{15,0,1}, {4,8,4},{0,4,12},{8,5,3},{5,9,2},{11,2,3},{12,3,1},{6,3,7},{1,1,14},{2,9,5},{1,8,7},{4,10,2},{7,7,2},{13,1,2},{0,15,1},{3,2,11},{7,0,9}, {4,4,8},{3,8,5},{0,5,11},{13,2,1},{1,10,5},{4,11,1},{3,10,3},{5,10,1},{10,2,4},{0,6,10},{14,2,0},{11,4,1},{3,12,1},{1,13,2},{1,5,10},{5,11,0}, {12,0,4},{8,1,7},{6,10,0},{3,13,0},{7,2,7},{0,7,9},{5,8,3},{0,12,4},{11,1,4},{13,0,3},{0,16,0},{5,7,4},{10,3,3},{10,0,6},{0,13,3},{4,6,6}, {2,8,6},{2,5,9},{7,8,1},{2,1,13},{2,0,14},{7,3,6},{5,1,10},{3,11,2},{5,4,7},{8,3,5},{10,5,1},{6,9,1},{1,3,12},{4,5,7},{2,2,12},{4,1,11}, {0,8,8},{4,12,0},{6,5,5},{8,7,1},{5,5,6},{3,7,6},{7,9,0},{4,9,3},{0,10,6},{8,0,8},{5,3,8},{10,1,5},{6,1,9},{7,6,3},{9,5,2},{0,1,15}, {9,7,0},{2,14,0},{3,4,9},{8,4,4},{9,4,3},{0,9,7},{1,9,6},{3,9,4},{5,2,9},{2,3,11},{5,6,5},{1,14,1},{6,7,3},{2,4,10},{2,12,2},{8,8,0}, {2,10,4},{4,0,12},{0,11,5},{2,11,3},{1,11,4},{3,5,8},{5,0,11},{3,1,12},{1,2,13},{1,6,9} }; // For each total ordering, this table indicates which other total orderings are likely to improve quality using a least squares pass. Each array is sorted by usefulness. static uint16_t g_best_total_orderings4[NUM_UNIQUE_TOTAL_ORDERINGS4][MAX_TOTAL_ORDERINGS4] = { #if RGBCX_USE_SMALLER_TABLES #include "rgbcx_table4_small.h" #else #include "rgbcx_table4.h" #endif }; static uint8_t g_best_total_orderings3[NUM_UNIQUE_TOTAL_ORDERINGS3][32] = { { 12,1,3,5,27,2,4,38,8,7,16,18,6,10,41,79,40,23,46,9,20,88,22,37,14,19,24,126,99,119,35,11 }, { 7,64,116,14,94,30,8,42,1,108,47,55,137,10,134,95,96,115,69,32,63,29,90,113,11,148,16,103,19,9,34,25 }, { 12,1,0,5,3,7,4,27,8,6,38,40,41,16,18,46,9,10,20,23,79,62,14,22,88,99,37,126,92,19,120,11 }, { 16,88,27,18,46,48,126,107,79,19,59,38,37,65,23,66,0,2,3,43,12,151,28,25,5,87,72,40,1,20,52,92 }, { 79,48,88,16,27,65,18,38,46,19,37,4,72,33,126,41,52,0,12,92,5,1,2,107,3,77,23,91,43,51,22,74 }, { 1,8,41,122,10,22,2,0,87,24,37,120,38,7,39,4,5,3,9,92,62,59,23,16,104,11,27,79,19,26,25,32 }, { 2,76,99,28,40,86,93,21,138,60,6,0,17,128,145,119,98,144,141,82,147,54,67,75,5,12,27,132,146,1,38,14 }, { 47,7,64,90,1,118,116,85,57,14,30,94,50,45,137,134,8,42,69,139,55,68,58,108,95,29,10,115,0,32,2,11 }, { 49,8,10,30,124,11,32,113,130,58,125,9,100,53,104,115,131,103,24,7,1,39,45,36,139,0,137,22,90,44,114,105 }, { 9,38,72,125,49,41,84,11,13,5,27,0,16,92,8,2,65,105,10,18,48,29,127,131,36,14,1,46,111,79,130,12 }, { 130,8,10,100,104,131,49,32,53,39,30,36,113,24,11,22,124,44,83,58,7,103,1,4,9,125,5,0,91,33,115,74 }, { 114,11,58,8,120,49,9,124,142,111,41,30,10,0,97,130,62,84,38,5,72,125,92,127,100,27,139,113,13,132,32,1 }, { 60,46,28,27,40,20,0,17,18,2,126,16,6,38,86,23,79,54,1,93,5,88,41,14,21,111,7,48,3,84,72,62 }, { 72,92,38,65,84,48,41,79,27,16,29,111,88,5,18,46,1,0,152,14,37,19,77,42,132,7,22,13,119,56,12,2 }, { 7,55,1,95,29,56,64,116,143,8,14,30,47,94,152,90,65,67,10,133,42,72,146,84,16,48,6,0,25,108,77,21 }, { 27,23,20,5,0,79,38,2,3,1,59,46,4,41,33,86,37,87,88,92,7,126,43,8,22,152,151,150,149,148,147,146 }, { 12,0,1,2,7,6,3,5,28,4,8,14,60,40,17,19,21,86,126,93,10,18,9,29,48,99,65,25,84,119,72,41 }, { 60,40,99,2,54,12,0,1,19,28,98,93,6,138,21,5,27,17,151,14,76,46,16,18,38,29,86,144,107,7,25,41 }, { 12,0,1,2,3,5,6,7,4,28,8,60,14,40,16,17,21,10,19,9,86,38,126,41,93,27,29,48,62,84,79,99 }, { 0,1,2,10,5,8,3,25,4,29,32,34,63,7,77,26,16,48,65,56,14,22,129,103,72,24,18,152,140,53,96,42 }, { 46,126,18,54,12,16,1,0,5,2,27,98,20,23,6,3,88,48,28,7,19,8,4,60,151,38,37,21,79,14,65,40 }, { 76,6,141,86,119,2,138,67,28,145,0,93,17,1,40,60,146,99,147,14,21,144,132,7,5,29,55,27,16,75,19,12 }, { 71,5,51,39,22,80,0,43,10,122,8,62,41,24,104,87,35,37,2,91,33,120,36,38,1,131,9,100,130,66,3,4 }, { 126,18,46,27,20,16,88,23,12,79,54,59,48,0,73,1,37,151,5,19,28,38,2,66,60,3,65,98,14,26,6,43 }, { 22,10,8,5,0,71,35,80,104,39,24,51,100,1,62,32,2,130,11,41,7,9,53,43,49,83,122,120,30,44,37,38 }, { 1,34,14,129,53,63,42,26,121,148,7,44,96,10,0,24,100,32,64,116,140,22,5,19,29,103,135,108,8,61,39,83 }, { 1,7,34,63,44,25,135,14,24,108,22,0,83,94,5,129,35,101,47,121,2,19,42,53,6,110,103,8,148,10,16,123 }, { 12,28,16,60,18,1,6,21,14,0,86,19,2,48,93,17,38,29,7,5,65,126,46,72,41,79,84,119,40,56,54,88 }, { 0,2,12,27,5,46,38,40,41,79,88,99,3,23,1,62,20,4,22,37,92,35,18,8,16,24,10,60,7,120,98,54 }, { 1,7,14,56,8,0,84,67,10,2,133,72,42,111,5,30,21,4,9,3,25,94,16,116,47,11,65,18,132,90,55,64 }, { 30,8,124,139,45,11,58,90,113,137,7,115,10,32,1,49,94,85,9,47,108,103,0,97,63,14,50,114,53,106,100,25 }, { 65,38,48,27,16,79,72,18,88,19,46,77,84,92,37,41,0,29,1,14,12,111,2,5,31,36,87,74,105,40,28,51 }, { 10,8,30,113,130,100,53,32,115,103,104,7,1,121,39,49,131,44,24,36,63,137,34,45,22,90,108,83,26,11,94,139 }, { 51,52,43,33,5,74,16,37,71,91,38,3,36,87,48,22,4,0,122,41,39,18,66,27,79,24,65,88,59,23,62,92 }, { 1,7,63,53,108,121,94,44,103,100,14,10,129,47,32,26,24,25,148,42,135,22,0,61,83,8,39,104,5,64,115,34 }, { 1,8,10,7,5,0,80,32,62,2,24,44,53,83,9,41,30,22,100,11,14,25,120,4,26,6,3,16,122,34,19,35 }, { 74,4,36,48,33,91,39,79,22,16,65,5,131,38,24,71,27,52,0,105,51,18,88,104,3,31,10,37,72,19,41,130 }, { 59,43,38,79,23,27,92,51,0,16,46,5,18,88,41,37,66,3,87,20,48,2,122,4,22,12,1,126,19,65,33,24 }, { 12,28,1,27,0,16,2,46,65,60,21,3,5,18,6,19,48,14,4,7,79,88,86,29,22,72,93,40,23,8,17,41 }, { 22,91,39,33,24,71,5,131,36,10,51,0,130,8,104,2,35,125,9,43,52,49,83,80,100,41,122,3,37,38,4,16 }, { 12,0,1,2,5,3,4,8,7,27,18,38,10,6,16,46,9,20,41,23,126,79,22,14,19,99,88,54,37,48,62,35 }, { 12,27,1,2,3,0,46,4,38,16,8,28,7,79,18,5,84,6,88,10,14,21,23,20,40,22,60,19,9,29,72,65 }, { 1,14,7,55,95,29,8,94,30,56,10,108,77,116,152,64,32,48,63,42,143,148,16,25,137,65,11,0,115,9,19,72 }, { 37,79,66,38,16,52,48,59,43,27,87,33,41,4,23,51,3,5,88,18,92,46,73,122,22,71,20,0,65,19,2,120 }, { 24,32,83,22,53,1,8,10,7,30,35,5,103,0,100,101,121,113,34,123,63,2,44,25,71,115,80,14,26,108,51,39 }, { 97,45,111,58,85,139,0,90,47,7,120,106,142,30,50,132,41,62,84,1,119,114,14,56,117,8,38,29,2,64,116,5 }, { 12,28,16,18,1,60,6,14,2,21,0,86,126,19,48,93,7,27,17,29,5,65,54,38,72,79,84,88,119,145,8,111 }, { 118,47,64,116,57,85,7,14,50,1,42,0,45,68,86,69,2,111,134,28,90,55,16,29,56,48,84,144,60,30,112,41 }, { 12,1,2,0,7,6,28,5,3,4,8,14,60,21,18,40,17,86,10,9,16,29,19,93,126,79,38,84,72,27,111,119 }, { 11,8,49,130,10,125,9,124,100,114,131,30,58,104,32,39,24,113,36,105,0,41,22,120,5,53,111,38,142,44,83,35 }, { 50,70,47,118,85,57,106,0,45,7,64,90,81,14,2,134,28,62,86,55,69,1,78,119,68,56,18,67,16,60,29,21 }, { 43,37,33,87,51,41,66,5,122,38,22,59,92,0,23,91,27,16,71,79,18,52,120,4,3,24,46,20,73,39,62,36 }, { 79,48,4,16,27,88,43,33,18,38,65,37,46,3,19,51,52,22,66,87,74,5,41,91,23,59,0,71,122,72,20,92 }, { 32,100,10,8,30,104,24,44,39,113,83,103,1,7,22,53,115,63,135,121,26,35,34,5,0,108,137,90,91,45,2,130 }, { 0,1,2,5,16,12,6,7,14,3,19,18,29,20,4,21,40,8,17,35,23,48,126,22,25,56,26,10,98,27,38,65 }, { 143,67,56,146,1,7,133,55,64,141,134,69,6,47,14,29,84,21,111,147,57,16,95,72,118,132,50,0,2,18,119,42 }, { 1,7,67,14,133,111,8,84,0,21,2,47,64,132,55,10,95,147,119,42,16,5,72,56,4,3,6,29,9,25,18,30 }, { 68,57,69,112,144,86,102,2,134,55,0,70,118,64,75,47,14,28,93,143,67,7,50,149,1,21,29,56,119,95,60,78 }, { 58,97,114,30,124,45,11,139,8,90,0,142,7,10,41,113,84,62,49,111,85,1,9,5,137,120,32,14,2,117,47,38 }, { 23,66,18,79,38,20,43,27,16,88,46,59,126,37,87,12,73,92,3,5,48,0,19,54,2,51,28,1,41,65,122,22 }, { 0,12,2,27,5,40,46,38,1,41,3,79,88,23,99,4,20,62,22,54,92,18,8,37,16,35,10,7,19,120,144,24 }, { 1,14,25,26,0,7,44,34,129,42,24,5,135,22,19,148,6,96,83,2,29,16,63,35,101,64,140,136,116,110,3,10 }, { 12,1,2,27,3,4,38,5,7,8,18,16,46,6,0,40,41,10,79,23,88,9,20,22,14,19,37,92,48,126,28,21 }, { 7,1,10,32,108,103,94,47,8,53,25,14,34,115,100,129,121,130,148,42,64,116,63,26,44,0,24,30,113,4,104,22 }, { 47,134,7,14,55,69,64,95,1,29,85,118,56,116,45,57,102,143,50,90,42,30,16,94,0,8,67,75,133,2,18,48 }, { 12,1,2,0,7,6,28,8,14,5,3,4,40,21,17,18,60,86,16,93,126,10,9,29,99,38,119,25,19,54,27,84 }, { 59,16,27,18,23,88,79,37,46,66,38,20,73,126,3,43,48,87,92,51,41,12,19,5,52,107,65,0,151,122,54,2 }, { 1,21,147,7,119,14,76,132,55,0,86,145,2,6,69,67,16,143,111,138,17,28,29,60,18,93,8,19,40,56,84,5 }, { 144,86,112,2,68,102,69,0,149,93,75,28,57,55,145,60,21,67,99,134,143,40,146,119,82,110,62,6,29,26,78,14 }, { 102,57,55,69,143,75,146,67,56,68,134,2,29,141,0,21,6,14,133,118,64,1,7,95,47,84,111,28,147,82,72,119 }, { 0,70,57,119,50,145,2,86,28,118,69,78,149,47,60,68,67,55,93,81,134,21,14,62,64,7,5,1,132,85,41,16 }, { 51,5,43,71,122,87,41,37,91,39,0,22,33,36,38,24,66,120,62,2,80,16,92,10,59,4,27,23,35,79,8,3 }, { 12,1,2,0,7,6,28,5,8,14,3,21,40,4,60,17,86,18,16,93,10,9,126,119,99,29,19,41,38,27,25,92 }, { 27,18,46,126,23,16,88,79,20,151,59,73,48,38,0,54,12,2,37,1,19,5,28,60,66,41,3,109,86,65,40,6 }, { 48,79,4,33,16,74,65,38,88,27,91,52,18,36,22,19,46,0,37,3,51,5,71,39,72,43,24,41,92,87,2,10 }, { 86,2,144,93,28,112,141,6,102,21,99,60,75,0,68,82,69,146,67,149,55,40,145,76,111,147,56,119,110,143,26,132 }, { 6,138,2,99,86,17,40,93,28,21,145,141,0,60,119,147,128,76,67,54,1,12,5,27,144,14,38,98,146,41,29,19 }, { 1,8,0,10,2,29,7,5,3,56,4,25,14,152,63,32,65,72,96,42,34,108,48,9,26,16,84,103,67,148,22,129 }, { 149,145,0,86,2,28,93,144,62,60,119,101,21,41,5,35,78,99,26,40,12,68,57,67,110,120,69,18,55,76,132,70 }, { 12,28,16,1,48,19,6,60,2,14,18,21,0,27,46,65,86,29,5,7,72,93,40,3,17,84,56,88,126,4,38,8 }, { 1,8,5,10,7,24,2,62,0,41,22,122,120,9,4,3,32,87,11,37,38,83,100,44,25,104,16,26,39,80,14,6 }, { 0,119,62,86,145,149,28,132,93,2,120,67,60,41,35,5,144,21,123,38,111,81,84,56,12,44,24,50,92,55,40,22 }, { 2,93,99,28,40,144,60,0,86,150,76,21,149,98,6,25,1,61,82,26,12,5,54,141,7,18,145,16,27,138,110,38 }, { 24,8,10,22,32,35,100,5,1,53,0,7,71,80,30,123,83,104,51,11,2,39,44,113,9,62,25,103,34,101,43,41 }, { 12,1,2,0,7,6,28,5,40,60,8,16,3,18,14,4,86,21,17,93,41,10,9,99,27,119,38,19,126,22,48,145 }, { 45,47,50,7,85,90,97,1,64,139,116,118,30,58,14,106,70,111,0,57,94,42,137,142,29,120,8,56,18,134,84,41 }, { 12,0,2,5,27,38,1,46,41,40,79,144,3,22,88,23,28,60,99,62,6,24,26,7,4,16,10,35,37,18,14,20 }, { 37,38,59,92,0,5,23,51,79,41,27,22,2,3,87,16,46,4,1,43,20,33,18,88,24,71,8,10,48,19,126,122 }, { 12,28,16,60,1,18,6,21,19,14,48,0,2,86,93,5,46,29,17,27,65,7,3,72,38,126,119,40,84,37,56,4 }, { 0,2,5,1,16,6,27,28,18,38,60,7,14,21,46,40,86,41,19,48,93,8,3,79,22,4,10,37,62,23,24,111 }, { 85,7,90,30,47,139,45,50,94,58,137,1,8,64,14,116,118,115,113,11,124,108,0,10,97,57,32,70,42,106,29,114 }, { 33,36,22,71,51,5,91,39,0,52,43,24,131,74,16,37,38,122,41,3,87,48,4,104,35,80,10,2,105,62,27,18 }, { 12,1,27,2,0,16,3,28,46,18,4,6,5,72,21,79,38,7,14,60,88,8,65,19,48,29,23,40,22,20,86,126 }, { 0,12,2,27,5,38,46,41,1,40,79,3,88,23,22,99,20,37,62,4,18,6,16,35,60,28,24,7,92,8,14,10 }, { 7,47,1,30,137,8,116,94,90,64,14,115,108,118,57,10,148,113,42,85,32,11,63,50,103,45,124,134,55,9,69,34 }, { 55,7,1,29,56,143,64,47,67,133,14,146,95,72,84,8,116,111,6,134,141,21,65,0,69,30,16,45,85,42,50,10 }, { 14,1,42,8,10,29,108,63,55,148,95,32,7,19,25,115,103,34,56,129,77,0,16,152,94,30,113,26,2,5,48,4 }, { 111,120,142,97,58,0,41,45,62,132,114,84,139,30,5,8,38,2,7,85,119,90,117,1,124,11,56,47,28,27,35,72 }, { 1,0,14,2,6,5,16,19,7,29,42,18,3,25,12,35,21,8,26,17,40,4,20,48,109,99,22,96,55,101,10,61 }, { 12,0,1,5,3,2,4,7,27,8,38,6,40,18,16,10,20,46,9,41,23,22,79,14,62,19,37,126,88,11,92,48 }, { 10,8,104,39,24,32,22,83,44,100,30,130,53,91,113,5,11,1,35,33,7,49,0,2,103,71,36,124,9,80,131,34 }, { 1,7,0,14,8,34,5,25,35,26,6,63,10,123,2,16,103,19,44,32,135,121,108,80,62,30,115,94,149,144,53,18 }, { 75,68,146,141,102,67,2,21,6,57,69,143,0,55,82,86,28,144,147,29,93,112,56,119,133,14,76,60,84,134,111,145 }, { 10,32,115,7,8,53,1,108,30,113,94,137,100,63,90,34,130,103,121,47,44,25,104,39,24,26,85,14,49,36,22,131 }, { 39,24,10,22,8,130,91,104,83,49,5,33,100,11,0,35,32,131,71,36,9,44,53,2,80,51,30,1,41,7,43,62 }, { 38,36,65,105,27,72,31,79,41,131,5,48,125,39,0,16,92,46,22,13,18,84,24,37,88,2,33,74,91,71,130,49 }, { 0,106,62,50,45,119,85,81,132,28,2,86,41,47,38,60,35,117,5,29,7,30,145,90,55,70,14,111,18,67,93,56 }, { 0,2,5,1,3,25,19,26,4,34,29,10,22,16,8,7,24,14,48,65,53,18,6,77,44,56,72,61,121,21,136,40 }, { 7,1,94,8,47,115,10,32,113,103,30,108,137,63,14,64,116,148,129,42,90,25,34,118,53,57,11,49,85,9,96,50 }, { 14,0,1,26,19,5,42,2,25,24,29,22,6,44,61,16,7,96,136,3,140,34,35,55,135,18,48,77,83,4,8,10 }, { 1,7,14,0,25,6,34,5,26,16,63,2,19,8,35,101,108,29,94,10,18,42,123,144,129,47,61,21,3,62,149,4 }, { 12,0,2,1,28,5,6,120,7,60,40,16,18,86,27,14,21,93,8,62,41,38,3,17,4,119,99,48,19,126,10,9 }, { 86,144,93,2,28,149,0,60,99,112,110,145,40,21,102,26,75,62,69,1,12,101,119,25,76,67,7,68,55,5,6,14 }, { 8,30,10,32,113,49,115,137,124,103,45,90,7,139,11,1,58,53,130,94,108,100,9,63,85,125,34,47,0,24,44,104 }, { 120,142,111,41,58,114,97,0,11,62,84,124,5,30,8,38,132,127,27,139,92,10,72,45,49,9,28,2,29,56,16,1 }, { 8,113,30,137,7,32,10,90,94,115,1,103,108,63,47,85,49,53,11,45,34,50,14,25,9,124,100,130,139,121,42,26 }, { 64,7,14,47,134,55,1,42,95,69,116,90,94,30,8,29,56,137,45,108,85,10,57,16,102,143,118,19,63,32,11,50 }, { 62,132,0,119,120,41,111,86,35,28,5,84,56,38,2,93,145,60,67,12,92,27,29,72,55,117,21,24,133,149,22,45 }, { 57,68,69,118,134,64,50,47,55,14,7,2,102,144,0,112,70,86,85,1,95,29,116,143,42,75,16,56,28,45,21,48 }, { 0,12,2,1,5,28,6,40,60,27,7,38,16,14,86,18,93,41,62,46,99,35,8,23,3,17,22,21,10,19,79,20 }, { 12,1,2,27,16,3,38,111,4,0,18,5,7,46,40,8,79,6,14,28,88,10,48,41,19,84,21,9,22,23,20,72 }, { 53,103,32,7,1,100,22,63,71,44,10,115,108,24,92,104,26,30,122,94,8,39,83,34,137,135,90,91,121,5,87,47 }, { 87,37,41,0,22,38,2,92,1,24,4,8,3,59,10,5,39,23,71,79,122,27,16,46,33,7,91,20,18,51,9,120 }, { 1,7,8,10,0,5,35,32,53,44,14,30,2,80,25,34,6,62,26,103,16,19,63,9,149,24,121,41,22,11,113,83 }, { 11,58,8,30,124,49,10,113,9,114,139,45,97,32,7,137,90,1,0,130,115,125,100,24,5,94,53,41,14,13,35,38 }, { 125,105,9,36,131,49,8,130,39,11,10,5,22,38,41,104,0,31,13,24,27,16,2,72,65,91,48,32,84,18,100,74 }, { 12,1,0,2,6,3,7,5,4,8,14,28,16,60,18,10,21,17,19,9,40,27,86,93,29,38,54,11,25,48,46,41 }, { 84,41,38,72,92,29,111,5,65,120,79,0,27,56,48,14,132,16,119,22,86,88,46,28,62,12,1,2,93,18,24,127 }, { 99,28,40,60,2,93,138,0,98,17,86,54,76,12,27,1,21,144,128,38,5,14,46,18,25,16,109,6,41,145,7,29 }, { 1,63,10,32,148,14,103,34,42,7,8,108,116,53,64,96,25,121,26,94,140,0,29,19,55,24,100,136,5,4,44,115 }, { 131,100,130,49,10,8,36,104,39,0,48,41,11,38,4,24,27,22,16,44,79,5,33,2,53,9,125,74,91,120,32,83 }, { 36,39,131,74,4,91,22,33,125,104,130,48,10,24,16,5,49,8,100,105,79,0,9,65,71,2,18,83,31,11,19,44 }, { 0,12,2,1,6,5,7,28,40,60,16,14,18,62,86,27,93,8,17,38,21,41,35,99,3,19,10,23,22,4,9,48 }, { 1,7,67,14,21,147,111,55,132,119,0,8,2,76,64,16,47,84,6,18,86,95,145,10,42,29,133,5,56,134,17,72 }, { 69,55,47,134,102,143,7,57,118,95,14,64,29,56,1,50,75,67,146,2,0,133,68,16,21,6,141,85,116,18,72,65 }, { 1,44,7,24,83,63,34,103,22,121,53,32,25,35,0,115,108,5,14,8,10,101,94,30,2,123,110,26,137,47,90,19 }, { 14,1,25,42,34,0,26,96,19,29,140,5,53,10,2,121,3,24,44,22,55,77,129,7,63,16,8,4,6,61,100,48 }, { 30,90,7,8,137,94,85,1,47,113,115,108,45,139,124,11,10,32,50,58,103,14,63,64,9,116,49,42,25,148,0,53 }, { 40,99,2,60,28,17,0,54,93,98,86,138,6,12,21,76,1,5,27,144,128,38,19,46,14,41,145,7,16,67,3,109 }, { 45,58,30,139,90,7,85,137,97,8,124,47,1,11,106,114,50,94,0,113,10,115,14,32,9,64,108,41,49,29,62,116 }, { 14,42,10,1,63,96,32,25,34,8,129,29,0,103,55,19,26,53,77,5,95,2,4,7,3,16,148,56,18,24,121,108 }, { 21,2,75,86,6,76,144,28,119,99,93,147,141,67,102,145,60,132,146,128,0,82,40,138,55,111,143,17,133,112,69,14 }, { 111,120,41,62,84,132,0,5,38,119,56,92,72,142,27,28,29,35,58,80,2,86,65,79,12,14,1,24,145,16,21,48 }, { 146,67,141,69,133,21,6,143,57,55,111,147,56,1,14,132,7,2,134,102,0,119,29,84,76,64,86,72,28,68,47,75 }, { 12,1,0,5,27,3,7,4,38,8,6,41,16,40,46,10,18,79,2,9,23,86,20,22,62,14,37,88,92,19,24,11 }, { 0,12,2,1,27,5,38,28,60,6,40,7,16,46,18,14,41,99,93,62,3,79,86,23,149,8,22,35,88,17,19,10 }, { 141,6,21,67,147,102,146,2,76,119,132,69,55,111,86,75,28,133,143,0,1,145,14,128,56,99,17,60,29,93,84,68 }, { 21,76,1,119,86,145,2,0,14,7,6,138,146,55,17,28,132,93,67,40,60,143,29,147,111,16,69,141,5,56,19,133 }, { 1,8,108,14,7,116,64,42,10,63,94,32,115,103,113,96,30,34,55,47,95,148,29,140,129,25,134,53,69,26,19,11 }, { 12,1,3,5,4,2,0,7,8,38,27,16,18,6,10,20,41,40,79,46,9,23,22,88,92,37,14,24,62,19,48,99 }, { 1,14,7,0,6,25,5,16,19,2,42,26,29,35,61,8,18,129,101,21,3,110,34,148,96,10,17,4,22,40,12,20 }, { 0,2,5,1,3,19,22,26,16,24,29,7,14,6,4,25,18,44,8,48,12,61,20,21,10,35,65,56,23,40,17,107 }, { 1,7,8,29,56,0,10,14,2,42,72,5,4,65,3,30,84,94,67,9,25,133,111,11,32,108,16,63,21,96,26,48 } }; #ifdef _MSC_VER #pragma endregion #endif static inline uint32_t iabs(int32_t i) { return (i < 0) ? static_cast<uint32_t>(-i) : static_cast<uint32_t>(i); } //static inline uint64_t iabs(int64_t i) { return (i < 0) ? static_cast<uint64_t>(-i) : static_cast<uint64_t>(i); } static inline uint8_t to_5(uint32_t v) { v = v * 31 + 128; return (uint8_t)((v + (v >> 8)) >> 8); } static inline uint8_t to_6(uint32_t v) { v = v * 63 + 128; return (uint8_t)((v + (v >> 8)) >> 8); } template<typename T> inline T square(T a) { return a * a; } static inline float clampf(float value, float low, float high) { if (value < low) value = low; else if (value > high) value = high; return value; } template <typename S> inline S clamp(S value, S low, S high) { return (value < low) ? low : ((value > high) ? high : value); } static inline int32_t clampi(int32_t value, int32_t low, int32_t high) { if (value < low) value = low; else if (value > high) value = high; return value; } static inline int squarei(int a) { return a * a; } //static inline int absi(int a) { return (a < 0) ? -a : a; } template<typename F> inline F lerp(F a, F b, F s) { return a + (b - a) * s; } static const uint32_t TOTAL_ORDER_4_0_16 = 15; static const uint32_t TOTAL_ORDER_4_1_16 = 700; static const uint32_t TOTAL_ORDER_4_2_16 = 753; static const uint32_t TOTAL_ORDER_4_3_16 = 515; static uint16_t g_total_ordering4_hash[4096]; static float g_selector_factors4[NUM_UNIQUE_TOTAL_ORDERINGS4][3]; static const uint32_t TOTAL_ORDER_3_0_16 = 12; static const uint32_t TOTAL_ORDER_3_1_16 = 15; static const uint32_t TOTAL_ORDER_3_2_16 = 89; static uint16_t g_total_ordering3_hash[256]; static float g_selector_factors3[NUM_UNIQUE_TOTAL_ORDERINGS3][3]; struct hist4 { uint8_t m_hist[4]; hist4() { memset(m_hist, 0, sizeof(m_hist)); } hist4(uint32_t i, uint32_t j, uint32_t k, uint32_t l) { m_hist[0] = (uint8_t)i; m_hist[1] = (uint8_t)j; m_hist[2] = (uint8_t)k; m_hist[3] = (uint8_t)l; } inline bool operator== (const hist4& h) const { if (m_hist[0] != h.m_hist[0]) return false; if (m_hist[1] != h.m_hist[1]) return false; if (m_hist[2] != h.m_hist[2]) return false; if (m_hist[3] != h.m_hist[3]) return false; return true; } inline bool any_16() const { return (m_hist[0] == 16) || (m_hist[1] == 16) || (m_hist[2] == 16) || (m_hist[3] == 16); } inline uint32_t lookup_total_ordering_index() const { if (m_hist[0] == 16) return TOTAL_ORDER_4_0_16; else if (m_hist[1] == 16) return TOTAL_ORDER_4_1_16; else if (m_hist[2] == 16) return TOTAL_ORDER_4_2_16; else if (m_hist[3] == 16) return TOTAL_ORDER_4_3_16; // Must sum to 16, so m_hist[3] isn't needed. return g_total_ordering4_hash[m_hist[0] | (m_hist[1] << 4) | (m_hist[2] << 8)]; } }; struct hist3 { uint8_t m_hist[3]; hist3() { memset(m_hist, 0, sizeof(m_hist)); } hist3(uint32_t i, uint32_t j, uint32_t k) { m_hist[0] = (uint8_t)i; m_hist[1] = (uint8_t)j; m_hist[2] = (uint8_t)k; } inline bool operator== (const hist3& h) const { if (m_hist[0] != h.m_hist[0]) return false; if (m_hist[1] != h.m_hist[1]) return false; if (m_hist[2] != h.m_hist[2]) return false; return true; } inline bool any_16() const { return (m_hist[0] == 16) || (m_hist[1] == 16) || (m_hist[2] == 16); } inline uint32_t lookup_total_ordering_index() const { if (m_hist[0] == 16) return TOTAL_ORDER_3_0_16; else if (m_hist[1] == 16) return TOTAL_ORDER_3_1_16; else if (m_hist[2] == 16) return TOTAL_ORDER_3_2_16; // Must sum to 16, so m_hist[2] isn't needed. return g_total_ordering3_hash[m_hist[0] | (m_hist[1] << 4)]; } }; struct bc1_match_entry { uint8_t m_hi; uint8_t m_lo; uint8_t m_e; }; static bc1_approx_mode g_bc1_approx_mode; static bc1_match_entry g_bc1_match5_equals_1[256], g_bc1_match6_equals_1[256]; static bc1_match_entry g_bc1_match5_half[256], g_bc1_match6_half[256]; static inline int scale_5_to_8(int v) { return (v << 3) | (v >> 2); } static inline int scale_6_to_8(int v) { return (v << 2) | (v >> 4); } // v0, v1 = unexpanded DXT1 endpoint values (5/6-bits) // c0, c1 = expanded DXT1 endpoint values (8-bits) static inline int interp_5_6_ideal(int c0, int c1) { assert(c0 < 256 && c1 < 256); return (c0 * 2 + c1) / 3; } static inline int interp_5_6_ideal_round(int c0, int c1) { assert(c0 < 256 && c1 < 256); return (c0 * 2 + c1 + 1) / 3; } static inline int interp_half_5_6_ideal(int c0, int c1) { assert(c0 < 256 && c1 < 256); return (c0 + c1) / 2; } static inline int interp_5_nv(int v0, int v1) { assert(v0 < 32 && v1 < 32); return ((2 * v0 + v1) * 22) / 8; } static inline int interp_6_nv(int c0, int c1) { assert(c0 < 256 && c1 < 256); const int gdiff = c1 - c0; return (256 * c0 + (gdiff / 4) + 128 + gdiff * 80) / 256; } static inline int interp_half_5_nv(int v0, int v1) { assert(v0 < 32 && v1 < 32); return ((v0 + v1) * 33) / 8; } static inline int interp_half_6_nv(int c0, int c1) { assert(c0 < 256 && c1 < 256); const int gdiff = c1 - c0; return (256 * c0 + gdiff / 4 + 128 + gdiff * 128) / 256; } static inline int interp_5_6_amd(int c0, int c1) { assert(c0 < 256 && c1 < 256); return (c0 * 43 + c1 * 21 + 32) >> 6; } static inline int interp_half_5_6_amd(int c0, int c1) { assert(c0 < 256 && c1 < 256); return (c0 + c1 + 1) >> 1; } static inline int interp_5(int v0, int v1, int c0, int c1, bc1_approx_mode mode) { assert(scale_5_to_8(v0) == c0 && scale_5_to_8(v1) == c1); switch (mode) { case bc1_approx_mode::cBC1NVidia: return interp_5_nv(v0, v1); case bc1_approx_mode::cBC1AMD: return interp_5_6_amd(c0, c1); default: case bc1_approx_mode::cBC1Ideal: return interp_5_6_ideal(c0, c1); case bc1_approx_mode::cBC1IdealRound4: return interp_5_6_ideal_round(c0, c1); } } static inline int interp_6(int v0, int v1, int c0, int c1, bc1_approx_mode mode) { (void)v0; (void)v1; assert(scale_6_to_8(v0) == c0 && scale_6_to_8(v1) == c1); switch (mode) { case bc1_approx_mode::cBC1NVidia: return interp_6_nv(c0, c1); case bc1_approx_mode::cBC1AMD: return interp_5_6_amd(c0, c1); default: case bc1_approx_mode::cBC1Ideal: return interp_5_6_ideal(c0, c1); case bc1_approx_mode::cBC1IdealRound4: return interp_5_6_ideal_round(c0, c1); } } static inline int interp_half_5(int v0, int v1, int c0, int c1, bc1_approx_mode mode) { assert(scale_5_to_8(v0) == c0 && scale_5_to_8(v1) == c1); switch (mode) { case bc1_approx_mode::cBC1NVidia: return interp_half_5_nv(v0, v1); case bc1_approx_mode::cBC1AMD: return interp_half_5_6_amd(c0, c1); case bc1_approx_mode::cBC1Ideal: case bc1_approx_mode::cBC1IdealRound4: default: return interp_half_5_6_ideal(c0, c1); } } static inline int interp_half_6(int v0, int v1, int c0, int c1, bc1_approx_mode mode) { (void)v0; (void)v1; assert(scale_6_to_8(v0) == c0 && scale_6_to_8(v1) == c1); switch (mode) { case bc1_approx_mode::cBC1NVidia: return interp_half_6_nv(c0, c1); case bc1_approx_mode::cBC1AMD: return interp_half_5_6_amd(c0, c1); case bc1_approx_mode::cBC1Ideal: case bc1_approx_mode::cBC1IdealRound4: default: return interp_half_5_6_ideal(c0, c1); } } static void prepare_bc1_single_color_table_half(bc1_match_entry* pTable, const uint8_t* pExpand, int size, bc1_approx_mode mode) { for (int i = 0; i < 256; i++) { int lowest_e = 256; for (int lo = 0; lo < size; lo++) { const int lo_e = pExpand[lo]; for (int hi = 0; hi < size; hi++) { const int hi_e = pExpand[hi]; const int v = (size == 32) ? interp_half_5(hi, lo, hi_e, lo_e, mode) : interp_half_6(hi, lo, hi_e, lo_e, mode); int e = iabs(v - i); // We only need to factor in 3% error in BC1 ideal mode. if ((mode == bc1_approx_mode::cBC1Ideal) || (mode == bc1_approx_mode::cBC1IdealRound4)) e += (iabs(hi_e - lo_e) * 3) / 100; // Favor equal endpoints, for lower error on actual GPU's which approximate the interpolation. if ((e < lowest_e) || ((e == lowest_e) && (lo == hi))) { pTable[i].m_hi = static_cast<uint8_t>(hi); pTable[i].m_lo = static_cast<uint8_t>(lo); assert(e <= UINT8_MAX); pTable[i].m_e = static_cast<uint8_t>(e); lowest_e = e; } } // hi } // lo } } static void prepare_bc1_single_color_table(bc1_match_entry* pTable, const uint8_t* pExpand, int size, bc1_approx_mode mode) { for (int i = 0; i < 256; i++) { int lowest_e = 256; for (int lo = 0; lo < size; lo++) { const int lo_e = pExpand[lo]; for (int hi = 0; hi < size; hi++) { const int hi_e = pExpand[hi]; const int v = (size == 32) ? interp_5(hi, lo, hi_e, lo_e, mode) : interp_6(hi, lo, hi_e, lo_e, mode); int e = iabs(v - i); if ((mode == bc1_approx_mode::cBC1Ideal) || (mode == bc1_approx_mode::cBC1IdealRound4)) e += (iabs(hi_e - lo_e) * 3) / 100; // Favor equal endpoints, for lower error on actual GPU's which approximate the interpolation. if ((e < lowest_e) || ((e == lowest_e) && (lo == hi))) { pTable[i].m_hi = static_cast<uint8_t>(hi); pTable[i].m_lo = static_cast<uint8_t>(lo); assert(e <= UINT8_MAX); pTable[i].m_e = static_cast<uint8_t>(e); lowest_e = e; } } // hi } // lo } } // This table is: 9 * (w * w), 9 * ((1.0f - w) * w), 9 * ((1.0f - w) * (1.0f - w)) // where w is [0,1/3,2/3,1]. 9 is the perfect multiplier. static const uint32_t g_weight_vals4[4] = { 0x000009, 0x010204, 0x040201, 0x090000 }; // multiplier is 4 for 3-color static const uint32_t g_weight_vals3[3] = { 0x000004, 0x040000, 0x010101 }; static inline void compute_selector_factors4(const hist4& h, float& iz00, float& iz10, float& iz11) { uint32_t weight_accum = 0; for (uint32_t sel = 0; sel < 4; sel++) weight_accum += g_weight_vals4[sel] * h.m_hist[sel]; float z00 = (float)((weight_accum >> 16) & 0xFF); float z10 = (float)((weight_accum >> 8) & 0xFF); float z11 = (float)(weight_accum & 0xFF); float z01 = z10; float det = z00 * z11 - z01 * z10; if (fabs(det) < 1e-8f) det = 0.0f; else det = (3.0f / 255.0f) / det; iz00 = z11 * det; iz10 = -z10 * det; iz11 = z00 * det; } static inline void compute_selector_factors3(const hist3& h, float& iz00, float& iz10, float& iz11) { uint32_t weight_accum = 0; for (uint32_t sel = 0; sel < 3; sel++) weight_accum += g_weight_vals3[sel] * h.m_hist[sel]; float z00 = (float)((weight_accum >> 16) & 0xFF); float z10 = (float)((weight_accum >> 8) & 0xFF); float z11 = (float)(weight_accum & 0xFF); float z01 = z10; float det = z00 * z11 - z01 * z10; if (fabs(det) < 1e-8f) det = 0.0f; else det = (2.0f / 255.0f) / det; iz00 = z11 * det; iz10 = -z10 * det; iz11 = z00 * det; } static bool g_initialized; void init(bc1_approx_mode mode) { g_bc1_approx_mode = mode; uint8_t bc1_expand5[32]; for (int i = 0; i < 32; i++) bc1_expand5[i] = static_cast<uint8_t>((i << 3) | (i >> 2)); prepare_bc1_single_color_table(g_bc1_match5_equals_1, bc1_expand5, 32, mode); prepare_bc1_single_color_table_half(g_bc1_match5_half, bc1_expand5, 32, mode); uint8_t bc1_expand6[64]; for (int i = 0; i < 64; i++) bc1_expand6[i] = static_cast<uint8_t>((i << 2) | (i >> 4)); prepare_bc1_single_color_table(g_bc1_match6_equals_1, bc1_expand6, 64, mode); prepare_bc1_single_color_table_half(g_bc1_match6_half, bc1_expand6, 64, mode); for (uint32_t i = 0; i < NUM_UNIQUE_TOTAL_ORDERINGS4; i++) { hist4 h; h.m_hist[0] = (uint8_t)g_unique_total_orders4[i][0]; h.m_hist[1] = (uint8_t)g_unique_total_orders4[i][1]; h.m_hist[2] = (uint8_t)g_unique_total_orders4[i][2]; h.m_hist[3] = (uint8_t)g_unique_total_orders4[i][3]; if (!h.any_16()) { const uint32_t index = h.m_hist[0] | (h.m_hist[1] << 4) | (h.m_hist[2] << 8); assert(index < 4096); g_total_ordering4_hash[index] = (uint16_t)i; } compute_selector_factors4(h, g_selector_factors4[i][0], g_selector_factors4[i][1], g_selector_factors4[i][2]); } for (uint32_t i = 0; i < NUM_UNIQUE_TOTAL_ORDERINGS3; i++) { hist3 h; h.m_hist[0] = (uint8_t)g_unique_total_orders3[i][0]; h.m_hist[1] = (uint8_t)g_unique_total_orders3[i][1]; h.m_hist[2] = (uint8_t)g_unique_total_orders3[i][2]; if (!h.any_16()) { const uint32_t index = h.m_hist[0] | (h.m_hist[1] << 4); assert(index < 256); g_total_ordering3_hash[index] = (uint16_t)i; } compute_selector_factors3(h, g_selector_factors3[i][0], g_selector_factors3[i][1], g_selector_factors3[i][2]); } g_initialized = true; } void encode_bc1_solid_block(void* pDst, uint32_t fr, uint32_t fg, uint32_t fb, bool allow_3color) { bc1_block* pDst_block = static_cast<bc1_block*>(pDst); uint32_t mask = 0xAA; int max16 = -1, min16 = 0; if (allow_3color) { const uint32_t err4 = g_bc1_match5_equals_1[fr].m_e + g_bc1_match6_equals_1[fg].m_e + g_bc1_match5_equals_1[fb].m_e; const uint32_t err3 = g_bc1_match5_half[fr].m_e + g_bc1_match6_half[fg].m_e + g_bc1_match5_half[fb].m_e; if (err3 < err4) { max16 = (g_bc1_match5_half[fr].m_hi << 11) | (g_bc1_match6_half[fg].m_hi << 5) | g_bc1_match5_half[fb].m_hi; min16 = (g_bc1_match5_half[fr].m_lo << 11) | (g_bc1_match6_half[fg].m_lo << 5) | g_bc1_match5_half[fb].m_lo; if (max16 > min16) std::swap(max16, min16); } } if (max16 == -1) { max16 = (g_bc1_match5_equals_1[fr].m_hi << 11) | (g_bc1_match6_equals_1[fg].m_hi << 5) | g_bc1_match5_equals_1[fb].m_hi; min16 = (g_bc1_match5_equals_1[fr].m_lo << 11) | (g_bc1_match6_equals_1[fg].m_lo << 5) | g_bc1_match5_equals_1[fb].m_lo; if (min16 == max16) { // Always forbid 3 color blocks // This is to guarantee that BC3 blocks never use punchthrough alpha (3 color) mode, which isn't supported on some (all?) GPU's. mask = 0; // Make l > h if (min16 > 0) min16--; else { // l = h = 0 assert(min16 == max16 && max16 == 0); max16 = 1; min16 = 0; mask = 0x55; } assert(max16 > min16); } if (max16 < min16) { std::swap(max16, min16); mask ^= 0x55; } } pDst_block->set_low_color(static_cast<uint16_t>(max16)); pDst_block->set_high_color(static_cast<uint16_t>(min16)); pDst_block->m_selectors[0] = static_cast<uint8_t>(mask); pDst_block->m_selectors[1] = static_cast<uint8_t>(mask); pDst_block->m_selectors[2] = static_cast<uint8_t>(mask); pDst_block->m_selectors[3] = static_cast<uint8_t>(mask); } static const float g_midpoint5[32] = { .015686f, .047059f, .078431f, .111765f, .145098f, .176471f, .207843f, .241176f, .274510f, .305882f, .337255f, .370588f, .403922f, .435294f, .466667f, .5f, .533333f, .564706f, .596078f, .629412f, .662745f, .694118f, .725490f, .758824f, .792157f, .823529f, .854902f, .888235f, .921569f, .952941f, .984314f, 1e+37f }; static const float g_midpoint6[64] = { .007843f, .023529f, .039216f, .054902f, .070588f, .086275f, .101961f, .117647f, .133333f, .149020f, .164706f, .180392f, .196078f, .211765f, .227451f, .245098f, .262745f, .278431f, .294118f, .309804f, .325490f, .341176f, .356863f, .372549f, .388235f, .403922f, .419608f, .435294f, .450980f, .466667f, .482353f, .500000f, .517647f, .533333f, .549020f, .564706f, .580392f, .596078f, .611765f, .627451f, .643137f, .658824f, .674510f, .690196f, .705882f, .721569f, .737255f, .754902f, .772549f, .788235f, .803922f, .819608f, .835294f, .850980f, .866667f, .882353f, .898039f, .913725f, .929412f, .945098f, .960784f, .976471f, .992157f, 1e+37f }; struct vec3F { float c[3]; }; static inline void compute_least_squares_endpoints4_rgb( vec3F* pXl, vec3F* pXh, int total_r, int total_g, int total_b, float iz00, float iz10, float iz11, uint32_t s, const uint32_t r_sum[17], const uint32_t g_sum[17], const uint32_t b_sum[17]) { const float iz01 = iz10; const uint32_t f1 = g_unique_total_orders4[s][0]; const uint32_t f2 = g_unique_total_orders4[s][0] + g_unique_total_orders4[s][1]; const uint32_t f3 = g_unique_total_orders4[s][0] + g_unique_total_orders4[s][1] + g_unique_total_orders4[s][2]; uint32_t uq00_r = (r_sum[f2] - r_sum[f1]) + (r_sum[f3] - r_sum[f2]) * 2 + (r_sum[16] - r_sum[f3]) * 3; uint32_t uq00_g = (g_sum[f2] - g_sum[f1]) + (g_sum[f3] - g_sum[f2]) * 2 + (g_sum[16] - g_sum[f3]) * 3; uint32_t uq00_b = (b_sum[f2] - b_sum[f1]) + (b_sum[f3] - b_sum[f2]) * 2 + (b_sum[16] - b_sum[f3]) * 3; float q10_r = (float)(total_r * 3 - uq00_r); float q10_g = (float)(total_g * 3 - uq00_g); float q10_b = (float)(total_b * 3 - uq00_b); pXl->c[0] = iz00 * (float)uq00_r + iz01 * q10_r; pXh->c[0] = iz10 * (float)uq00_r + iz11 * q10_r; pXl->c[1] = iz00 * (float)uq00_g + iz01 * q10_g; pXh->c[1] = iz10 * (float)uq00_g + iz11 * q10_g; pXl->c[2] = iz00 * (float)uq00_b + iz01 * q10_b; pXh->c[2] = iz10 * (float)uq00_b + iz11 * q10_b; } static inline bool compute_least_squares_endpoints4_rgb(const color32* pColors, const uint8_t* pSelectors, vec3F* pXl, vec3F* pXh, int total_r, int total_g, int total_b) { uint32_t uq00_r = 0, uq00_g = 0, uq00_b = 0; uint32_t weight_accum = 0; for (uint32_t i = 0; i < 16; i++) { const uint8_t r = pColors[i].c[0], g = pColors[i].c[1], b = pColors[i].c[2]; const uint8_t sel = pSelectors[i]; weight_accum += g_weight_vals4[sel]; uq00_r += sel * r; uq00_g += sel * g; uq00_b += sel * b; } int q10_r = total_r * 3 - uq00_r; int q10_g = total_g * 3 - uq00_g; int q10_b = total_b * 3 - uq00_b; float z00 = (float)((weight_accum >> 16) & 0xFF); float z10 = (float)((weight_accum >> 8) & 0xFF); float z11 = (float)(weight_accum & 0xFF); float z01 = z10; float det = z00 * z11 - z01 * z10; if (fabs(det) < 1e-8f) return false; det = (3.0f / 255.0f) / det; float iz00, iz01, iz10, iz11; iz00 = z11 * det; iz01 = -z01 * det; iz10 = -z10 * det; iz11 = z00 * det; pXl->c[0] = iz00 * (float)uq00_r + iz01 * q10_r; pXh->c[0] = iz10 * (float)uq00_r + iz11 * q10_r; pXl->c[1] = iz00 * (float)uq00_g + iz01 * q10_g; pXh->c[1] = iz10 * (float)uq00_g + iz11 * q10_g; pXl->c[2] = iz00 * (float)uq00_b + iz01 * q10_b; pXh->c[2] = iz10 * (float)uq00_b + iz11 * q10_b; return true; } static inline void compute_least_squares_endpoints3_rgb( vec3F* pXl, vec3F* pXh, int total_r, int total_g, int total_b, float iz00, float iz10, float iz11, uint32_t s, const uint32_t r_sum[17], const uint32_t g_sum[17], const uint32_t b_sum[17]) { const float iz01 = iz10; // Compensates for BC1 3-color ordering, which is selector 0, 2, 1 const uint32_t f1 = g_unique_total_orders3[s][0]; const uint32_t f2 = g_unique_total_orders3[s][0] + g_unique_total_orders3[s][2]; uint32_t uq00_r = (r_sum[16] - r_sum[f2]) * 2 + (r_sum[f2] - r_sum[f1]); uint32_t uq00_g = (g_sum[16] - g_sum[f2]) * 2 + (g_sum[f2] - g_sum[f1]); uint32_t uq00_b = (b_sum[16] - b_sum[f2]) * 2 + (b_sum[f2] - b_sum[f1]); float q10_r = (float)(total_r * 2 - uq00_r); float q10_g = (float)(total_g * 2 - uq00_g); float q10_b = (float)(total_b * 2 - uq00_b); pXl->c[0] = iz00 * (float)uq00_r + iz01 * q10_r; pXh->c[0] = iz10 * (float)uq00_r + iz11 * q10_r; pXl->c[1] = iz00 * (float)uq00_g + iz01 * q10_g; pXh->c[1] = iz10 * (float)uq00_g + iz11 * q10_g; pXl->c[2] = iz00 * (float)uq00_b + iz01 * q10_b; pXh->c[2] = iz10 * (float)uq00_b + iz11 * q10_b; } static inline bool compute_least_squares_endpoints3_rgb(bool use_black, const color32* pColors, const uint8_t* pSelectors, vec3F* pXl, vec3F* pXh) { int uq00_r = 0, uq00_g = 0, uq00_b = 0; uint32_t weight_accum = 0; int total_r = 0, total_g = 0, total_b = 0; for (uint32_t i = 0; i < 16; i++) { const uint8_t r = pColors[i].c[0], g = pColors[i].c[1], b = pColors[i].c[2]; if (use_black) { if ((r | g | b) < 4) continue; } const uint8_t sel = pSelectors[i]; assert(sel <= 3); if (sel == 3) continue; weight_accum += g_weight_vals3[sel]; static const uint8_t s_tran[3] = { 0, 2, 1 }; const uint8_t tsel = s_tran[sel]; uq00_r += tsel * r; uq00_g += tsel * g; uq00_b += tsel * b; total_r += r; total_g += g; total_b += b; } int q10_r = total_r * 2 - uq00_r; int q10_g = total_g * 2 - uq00_g; int q10_b = total_b * 2 - uq00_b; float z00 = (float)((weight_accum >> 16) & 0xFF); float z10 = (float)((weight_accum >> 8) & 0xFF); float z11 = (float)(weight_accum & 0xFF); float z01 = z10; float det = z00 * z11 - z01 * z10; if (fabs(det) < 1e-8f) return false; det = (2.0f / 255.0f) / det; float iz00, iz01, iz10, iz11; iz00 = z11 * det; iz01 = -z01 * det; iz10 = -z10 * det; iz11 = z00 * det; pXl->c[0] = iz00 * (float)uq00_r + iz01 * q10_r; pXh->c[0] = iz10 * (float)uq00_r + iz11 * q10_r; pXl->c[1] = iz00 * (float)uq00_g + iz01 * q10_g; pXh->c[1] = iz10 * (float)uq00_g + iz11 * q10_g; pXl->c[2] = iz00 * (float)uq00_b + iz01 * q10_b; pXh->c[2] = iz10 * (float)uq00_b + iz11 * q10_b; return true; } static inline void bc1_get_block_colors4(uint32_t block_r[4], uint32_t block_g[4], uint32_t block_b[4], uint32_t lr, uint32_t lg, uint32_t lb, uint32_t hr, uint32_t hg, uint32_t hb) { block_r[0] = (lr << 3) | (lr >> 2); block_g[0] = (lg << 2) | (lg >> 4); block_b[0] = (lb << 3) | (lb >> 2); block_r[3] = (hr << 3) | (hr >> 2); block_g[3] = (hg << 2) | (hg >> 4); block_b[3] = (hb << 3) | (hb >> 2); if (g_bc1_approx_mode == bc1_approx_mode::cBC1Ideal) { block_r[1] = (block_r[0] * 2 + block_r[3]) / 3; block_g[1] = (block_g[0] * 2 + block_g[3]) / 3; block_b[1] = (block_b[0] * 2 + block_b[3]) / 3; block_r[2] = (block_r[3] * 2 + block_r[0]) / 3; block_g[2] = (block_g[3] * 2 + block_g[0]) / 3; block_b[2] = (block_b[3] * 2 + block_b[0]) / 3; } else if (g_bc1_approx_mode == bc1_approx_mode::cBC1IdealRound4) { block_r[1] = (block_r[0] * 2 + block_r[3] + 1) / 3; block_g[1] = (block_g[0] * 2 + block_g[3] + 1) / 3; block_b[1] = (block_b[0] * 2 + block_b[3] + 1) / 3; block_r[2] = (block_r[3] * 2 + block_r[0] + 1) / 3; block_g[2] = (block_g[3] * 2 + block_g[0] + 1) / 3; block_b[2] = (block_b[3] * 2 + block_b[0] + 1) / 3; } else if (g_bc1_approx_mode == bc1_approx_mode::cBC1AMD) { block_r[1] = interp_5_6_amd(block_r[0], block_r[3]); block_g[1] = interp_5_6_amd(block_g[0], block_g[3]); block_b[1] = interp_5_6_amd(block_b[0], block_b[3]); block_r[2] = interp_5_6_amd(block_r[3], block_r[0]); block_g[2] = interp_5_6_amd(block_g[3], block_g[0]); block_b[2] = interp_5_6_amd(block_b[3], block_b[0]); } else { block_r[1] = interp_5_nv(lr, hr); block_g[1] = interp_6_nv(block_g[0], block_g[3]); block_b[1] = interp_5_nv(lb, hb); block_r[2] = interp_5_nv(hr, lr); block_g[2] = interp_6_nv(block_g[3], block_g[0]); block_b[2] = interp_5_nv(hb, lb); } } static inline void bc1_get_block_colors3(uint32_t block_r[3], uint32_t block_g[3], uint32_t block_b[3], uint32_t lr, uint32_t lg, uint32_t lb, uint32_t hr, uint32_t hg, uint32_t hb) { block_r[0] = (lr << 3) | (lr >> 2); block_g[0] = (lg << 2) | (lg >> 4); block_b[0] = (lb << 3) | (lb >> 2); block_r[1] = (hr << 3) | (hr >> 2); block_g[1] = (hg << 2) | (hg >> 4); block_b[1] = (hb << 3) | (hb >> 2); if ((g_bc1_approx_mode == bc1_approx_mode::cBC1Ideal) || (g_bc1_approx_mode == bc1_approx_mode::cBC1IdealRound4)) { block_r[2] = (block_r[0] + block_r[1]) / 2; block_g[2] = (block_g[0] + block_g[1]) / 2; block_b[2] = (block_b[0] + block_b[1]) / 2; } else if (g_bc1_approx_mode == bc1_approx_mode::cBC1AMD) { block_r[2] = interp_half_5_6_amd(block_r[0], block_r[1]); block_g[2] = interp_half_5_6_amd(block_g[0], block_g[1]); block_b[2] = interp_half_5_6_amd(block_b[0], block_b[1]); } else { block_r[2] = interp_half_5_nv(lr, hr); block_g[2] = interp_half_6_nv(block_g[0], block_g[1]); block_b[2] = interp_half_5_nv(lb, hb); } } static inline void bc1_find_sels4_noerr(const color32* pSrc_pixels, uint32_t lr, uint32_t lg, uint32_t lb, uint32_t hr, uint32_t hg, uint32_t hb, uint8_t sels[16], const uint8_t* pForce_selectors) { if (pForce_selectors) { memcpy(sels, pForce_selectors, 16); return; } uint32_t block_r[4], block_g[4], block_b[4]; bc1_get_block_colors4(block_r, block_g, block_b, lr, lg, lb, hr, hg, hb); int ar = block_r[3] - block_r[0], ag = block_g[3] - block_g[0], ab = block_b[3] - block_b[0]; int dots[4]; for (uint32_t i = 0; i < 4; i++) dots[i] = (int)block_r[i] * ar + (int)block_g[i] * ag + (int)block_b[i] * ab; int t0 = dots[0] + dots[1], t1 = dots[1] + dots[2], t2 = dots[2] + dots[3]; ar *= 2; ag *= 2; ab *= 2; static const uint8_t s_sels[4] = { 3, 2, 1, 0 }; for (uint32_t i = 0; i < 16; i += 4) { const int d0 = pSrc_pixels[i + 0].r * ar + pSrc_pixels[i + 0].g * ag + pSrc_pixels[i + 0].b * ab; const int d1 = pSrc_pixels[i + 1].r * ar + pSrc_pixels[i + 1].g * ag + pSrc_pixels[i + 1].b * ab; const int d2 = pSrc_pixels[i + 2].r * ar + pSrc_pixels[i + 2].g * ag + pSrc_pixels[i + 2].b * ab; const int d3 = pSrc_pixels[i + 3].r * ar + pSrc_pixels[i + 3].g * ag + pSrc_pixels[i + 3].b * ab; sels[i + 0] = s_sels[(d0 <= t0) + (d0 < t1) + (d0 < t2)]; sels[i + 1] = s_sels[(d1 <= t0) + (d1 < t1) + (d1 < t2)]; sels[i + 2] = s_sels[(d2 <= t0) + (d2 < t1) + (d2 < t2)]; sels[i + 3] = s_sels[(d3 <= t0) + (d3 < t1) + (d3 < t2)]; } } static inline uint32_t bc1_find_sels4_fasterr(const color32* pSrc_pixels, uint32_t lr, uint32_t lg, uint32_t lb, uint32_t hr, uint32_t hg, uint32_t hb, uint8_t sels[16], uint32_t cur_err) { uint32_t block_r[4], block_g[4], block_b[4]; bc1_get_block_colors4(block_r, block_g, block_b, lr, lg, lb, hr, hg, hb); int ar = block_r[3] - block_r[0], ag = block_g[3] - block_g[0], ab = block_b[3] - block_b[0]; int dots[4]; for (uint32_t i = 0; i < 4; i++) dots[i] = (int)block_r[i] * ar + (int)block_g[i] * ag + (int)block_b[i] * ab; int t0 = dots[0] + dots[1], t1 = dots[1] + dots[2], t2 = dots[2] + dots[3]; ar *= 2; ag *= 2; ab *= 2; static const uint8_t s_sels[4] = { 3, 2, 1, 0 }; uint32_t total_err = 0; for (uint32_t i = 0; i < 16; i += 4) { const int d0 = pSrc_pixels[i + 0].r * ar + pSrc_pixels[i + 0].g * ag + pSrc_pixels[i + 0].b * ab; const int d1 = pSrc_pixels[i + 1].r * ar + pSrc_pixels[i + 1].g * ag + pSrc_pixels[i + 1].b * ab; const int d2 = pSrc_pixels[i + 2].r * ar + pSrc_pixels[i + 2].g * ag + pSrc_pixels[i + 2].b * ab; const int d3 = pSrc_pixels[i + 3].r * ar + pSrc_pixels[i + 3].g * ag + pSrc_pixels[i + 3].b * ab; uint8_t sel0 = s_sels[(d0 <= t0) + (d0 < t1) + (d0 < t2)]; uint8_t sel1 = s_sels[(d1 <= t0) + (d1 < t1) + (d1 < t2)]; uint8_t sel2 = s_sels[(d2 <= t0) + (d2 < t1) + (d2 < t2)]; uint8_t sel3 = s_sels[(d3 <= t0) + (d3 < t1) + (d3 < t2)]; sels[i + 0] = sel0; sels[i + 1] = sel1; sels[i + 2] = sel2; sels[i + 3] = sel3; total_err += squarei(pSrc_pixels[i + 0].r - block_r[sel0]) + squarei(pSrc_pixels[i + 0].g - block_g[sel0]) + squarei(pSrc_pixels[i + 0].b - block_b[sel0]); total_err += squarei(pSrc_pixels[i + 1].r - block_r[sel1]) + squarei(pSrc_pixels[i + 1].g - block_g[sel1]) + squarei(pSrc_pixels[i + 1].b - block_b[sel1]); total_err += squarei(pSrc_pixels[i + 2].r - block_r[sel2]) + squarei(pSrc_pixels[i + 2].g - block_g[sel2]) + squarei(pSrc_pixels[i + 2].b - block_b[sel2]); total_err += squarei(pSrc_pixels[i + 3].r - block_r[sel3]) + squarei(pSrc_pixels[i + 3].g - block_g[sel3]) + squarei(pSrc_pixels[i + 3].b - block_b[sel3]); if (total_err >= cur_err) break; } return total_err; } static inline uint32_t bc1_find_sels4_check2_err(const color32* pSrc_pixels, uint32_t lr, uint32_t lg, uint32_t lb, uint32_t hr, uint32_t hg, uint32_t hb, uint8_t sels[16], uint32_t cur_err) { uint32_t block_r[4], block_g[4], block_b[4]; bc1_get_block_colors4(block_r, block_g, block_b, lr, lg, lb, hr, hg, hb); int dr = block_r[3] - block_r[0], dg = block_g[3] - block_g[0], db = block_b[3] - block_b[0]; const float f = 4.0f / (float)(squarei(dr) + squarei(dg) + squarei(db) + .00000125f); uint32_t total_err = 0; for (uint32_t i = 0; i < 16; i++) { const int r = pSrc_pixels[i].r; const int g = pSrc_pixels[i].g; const int b = pSrc_pixels[i].b; int sel = (int)((float)((r - (int)block_r[0]) * dr + (g - (int)block_g[0]) * dg + (b - (int)block_b[0]) * db) * f + .5f); sel = clampi(sel, 1, 3); uint32_t err0 = squarei((int)block_r[sel - 1] - (int)r) + squarei((int)block_g[sel - 1] - (int)g) + squarei((int)block_b[sel - 1] - (int)b); uint32_t err1 = squarei((int)block_r[sel] - (int)r) + squarei((int)block_g[sel] - (int)g) + squarei((int)block_b[sel] - (int)b); int best_sel = sel; uint32_t best_err = err1; if (err0 == err1) { // Prefer non-interpolation if ((best_sel - 1) == 0) best_sel = 0; } else if (err0 < best_err) { best_sel = sel - 1; best_err = err0; } total_err += best_err; if (total_err >= cur_err) break; sels[i] = (uint8_t)best_sel; } return total_err; } static inline uint32_t bc1_find_sels4_fullerr(const color32* pSrc_pixels, uint32_t lr, uint32_t lg, uint32_t lb, uint32_t hr, uint32_t hg, uint32_t hb, uint8_t sels[16], uint32_t cur_err) { uint32_t block_r[4], block_g[4], block_b[4]; bc1_get_block_colors4(block_r, block_g, block_b, lr, lg, lb, hr, hg, hb); uint32_t total_err = 0; for (uint32_t i = 0; i < 16; i++) { const int r = pSrc_pixels[i].r; const int g = pSrc_pixels[i].g; const int b = pSrc_pixels[i].b; uint32_t best_err = squarei((int)block_r[0] - (int)r) + squarei((int)block_g[0] - (int)g) + squarei((int)block_b[0] - (int)b); uint8_t best_sel = 0; for (uint32_t j = 1; (j < 4) && best_err; j++) { uint32_t err = squarei((int)block_r[j] - (int)r) + squarei((int)block_g[j] - (int)g) + squarei((int)block_b[j] - (int)b); if ((err < best_err) || ((err == best_err) && (j == 3))) { best_err = err; best_sel = (uint8_t)j; } } total_err += best_err; if (total_err >= cur_err) break; sels[i] = (uint8_t)best_sel; } return total_err; } static inline uint32_t bc1_find_sels4(uint32_t flags, const color32* pSrc_pixels, uint32_t lr, uint32_t lg, uint32_t lb, uint32_t hr, uint32_t hg, uint32_t hb, uint8_t sels[16], uint32_t cur_err, const uint8_t* pForce_selectors) { uint32_t err; if (pForce_selectors) { memcpy(sels, pForce_selectors, 16); uint32_t block_r[4], block_g[4], block_b[4]; bc1_get_block_colors4(block_r, block_g, block_b, lr, lg, lb, hr, hg, hb); err = 0; for (uint32_t i = 0; i < 16; i++) { const int r = pSrc_pixels[i].r; const int g = pSrc_pixels[i].g; const int b = pSrc_pixels[i].b; const uint32_t sel = pForce_selectors[i]; assert(sel <= 3); err += squarei((int)block_r[sel] - (int)r) + squarei((int)block_g[sel] - (int)g) + squarei((int)block_b[sel] - (int)b); } } else { if (flags & cEncodeBC1UseFasterMSEEval) err = bc1_find_sels4_fasterr(pSrc_pixels, lr, lg, lb, hr, hg, hb, sels, cur_err); else if (flags & cEncodeBC1UseFullMSEEval) err = bc1_find_sels4_fullerr(pSrc_pixels, lr, lg, lb, hr, hg, hb, sels, cur_err); else err = bc1_find_sels4_check2_err(pSrc_pixels, lr, lg, lb, hr, hg, hb, sels, cur_err); } return err; } static inline uint32_t bc1_find_sels3_fullerr(bool use_black, const color32* pSrc_pixels, uint32_t lr, uint32_t lg, uint32_t lb, uint32_t hr, uint32_t hg, uint32_t hb, uint8_t sels[16], uint32_t cur_err, const uint8_t* pForce_selectors) { uint32_t block_r[4], block_g[4], block_b[4]; bc1_get_block_colors3(block_r, block_g, block_b, lr, lg, lb, hr, hg, hb); uint32_t total_err = 0; if (pForce_selectors) { memcpy(sels, pForce_selectors, 16); //uint32_t block_r[4], block_g[4], block_b[4]; //bc1_get_block_colors3(block_r, block_g, block_b, lr, lg, lb, hr, hg, hb); block_r[3] = 0; block_g[3] = 0; block_b[3] = 0; for (uint32_t i = 0; i < 16; i++) { const int r = pSrc_pixels[i].r; const int g = pSrc_pixels[i].g; const int b = pSrc_pixels[i].b; const uint32_t sel = pForce_selectors[i]; assert(sel <= 3); total_err += squarei((int)block_r[sel] - (int)r) + squarei((int)block_g[sel] - (int)g) + squarei((int)block_b[sel] - (int)b); } } else { for (uint32_t i = 0; i < 16; i++) { const int r = pSrc_pixels[i].r; const int g = pSrc_pixels[i].g; const int b = pSrc_pixels[i].b; uint32_t best_err = squarei((int)block_r[0] - (int)r) + squarei((int)block_g[0] - (int)g) + squarei((int)block_b[0] - (int)b); uint32_t best_sel = 0; uint32_t err1 = squarei((int)block_r[1] - (int)r) + squarei((int)block_g[1] - (int)g) + squarei((int)block_b[1] - (int)b); if (err1 < best_err) { best_err = err1; best_sel = 1; } uint32_t err2 = squarei((int)block_r[2] - (int)r) + squarei((int)block_g[2] - (int)g) + squarei((int)block_b[2] - (int)b); if (err2 < best_err) { best_err = err2; best_sel = 2; } if (use_black) { uint32_t err3 = squarei(r) + squarei(g) + squarei(b); if (err3 < best_err) { best_err = err3; best_sel = 3; } } total_err += best_err; if (total_err >= cur_err) return total_err; sels[i] = (uint8_t)best_sel; } } return total_err; } static inline void precise_round_565(const vec3F& xl, const vec3F& xh, int& trial_lr, int& trial_lg, int& trial_lb, int& trial_hr, int& trial_hg, int& trial_hb) { trial_lr = (int)(xl.c[0] * 31.0f); trial_lg = (int)(xl.c[1] * 63.0f); trial_lb = (int)(xl.c[2] * 31.0f); trial_hr = (int)(xh.c[0] * 31.0f); trial_hg = (int)(xh.c[1] * 63.0f); trial_hb = (int)(xh.c[2] * 31.0f); if ((uint32_t)(trial_lr | trial_lb | trial_hr | trial_hb) > 31U) { trial_lr = ((uint32_t)trial_lr > 31U) ? (~trial_lr >> 31) & 31 : trial_lr; trial_hr = ((uint32_t)trial_hr > 31U) ? (~trial_hr >> 31) & 31 : trial_hr; trial_lb = ((uint32_t)trial_lb > 31U) ? (~trial_lb >> 31) & 31 : trial_lb; trial_hb = ((uint32_t)trial_hb > 31U) ? (~trial_hb >> 31) & 31 : trial_hb; } if ((uint32_t)(trial_lg | trial_hg) > 63U) { trial_lg = ((uint32_t)trial_lg > 63U) ? (~trial_lg >> 31) & 63 : trial_lg; trial_hg = ((uint32_t)trial_hg > 63U) ? (~trial_hg >> 31) & 63 : trial_hg; } trial_lr = (trial_lr + (xl.c[0] > g_midpoint5[trial_lr])) & 31; trial_lg = (trial_lg + (xl.c[1] > g_midpoint6[trial_lg])) & 63; trial_lb = (trial_lb + (xl.c[2] > g_midpoint5[trial_lb])) & 31; trial_hr = (trial_hr + (xh.c[0] > g_midpoint5[trial_hr])) & 31; trial_hg = (trial_hg + (xh.c[1] > g_midpoint6[trial_hg])) & 63; trial_hb = (trial_hb + (xh.c[2] > g_midpoint5[trial_hb])) & 31; } static inline void precise_round_565_noscale(vec3F xl, vec3F xh, int& trial_lr, int& trial_lg, int& trial_lb, int& trial_hr, int& trial_hg, int& trial_hb) { xl.c[0] *= 1.0f / 255.0f; xl.c[1] *= 1.0f / 255.0f; xl.c[2] *= 1.0f / 255.0f; xh.c[0] *= 1.0f / 255.0f; xh.c[1] *= 1.0f / 255.0f; xh.c[2] *= 1.0f / 255.0f; precise_round_565(xl, xh, trial_lr, trial_lg, trial_lb, trial_hr, trial_hg, trial_hb); } static inline void bc1_encode4(bc1_block* pDst_block, int lr, int lg, int lb, int hr, int hg, int hb, const uint8_t sels[16]) { uint32_t lc16 = bc1_block::pack_unscaled_color(lr, lg, lb); uint32_t hc16 = bc1_block::pack_unscaled_color(hr, hg, hb); // Always forbid 3 color blocks if (lc16 == hc16) { uint8_t mask = 0; // Make l > h if (hc16 > 0) hc16--; else { // lc16 = hc16 = 0 assert(lc16 == hc16 && hc16 == 0); hc16 = 0; lc16 = 1; mask = 0x55; // select hc16 } assert(lc16 > hc16); pDst_block->set_low_color(static_cast<uint16_t>(lc16)); pDst_block->set_high_color(static_cast<uint16_t>(hc16)); pDst_block->m_selectors[0] = mask; pDst_block->m_selectors[1] = mask; pDst_block->m_selectors[2] = mask; pDst_block->m_selectors[3] = mask; } else { uint8_t invert_mask = 0; if (lc16 < hc16) { std::swap(lc16, hc16); invert_mask = 0x55; } assert(lc16 > hc16); pDst_block->set_low_color((uint16_t)lc16); pDst_block->set_high_color((uint16_t)hc16); uint32_t packed_sels = 0; static const uint8_t s_sel_trans[4] = { 0, 2, 3, 1 }; for (uint32_t i = 0; i < 16; i++) packed_sels |= ((uint32_t)s_sel_trans[sels[i]] << (i * 2)); pDst_block->m_selectors[0] = (uint8_t)packed_sels ^ invert_mask; pDst_block->m_selectors[1] = (uint8_t)(packed_sels >> 8) ^ invert_mask; pDst_block->m_selectors[2] = (uint8_t)(packed_sels >> 16) ^ invert_mask; pDst_block->m_selectors[3] = (uint8_t)(packed_sels >> 24) ^ invert_mask; } } static inline void bc1_encode3(bc1_block* pDst_block, int lr, int lg, int lb, int hr, int hg, int hb, const uint8_t sels[16]) { uint32_t lc16 = bc1_block::pack_unscaled_color(lr, lg, lb); uint32_t hc16 = bc1_block::pack_unscaled_color(hr, hg, hb); bool invert_flag = false; if (lc16 > hc16) { std::swap(lc16, hc16); invert_flag = true; } assert(lc16 <= hc16); pDst_block->set_low_color((uint16_t)lc16); pDst_block->set_high_color((uint16_t)hc16); uint32_t packed_sels = 0; if (invert_flag) { static const uint8_t s_sel_trans_inv[4] = { 1, 0, 2, 3 }; for (uint32_t i = 0; i < 16; i++) packed_sels |= ((uint32_t)s_sel_trans_inv[sels[i]] << (i * 2)); } else { for (uint32_t i = 0; i < 16; i++) packed_sels |= ((uint32_t)sels[i] << (i * 2)); } pDst_block->m_selectors[0] = (uint8_t)packed_sels; pDst_block->m_selectors[1] = (uint8_t)(packed_sels >> 8); pDst_block->m_selectors[2] = (uint8_t)(packed_sels >> 16); pDst_block->m_selectors[3] = (uint8_t)(packed_sels >> 24); } struct bc1_encode_results { int lr, lg, lb; int hr, hg, hb; uint8_t sels[16]; bool m_3color; }; static bool try_3color_block_useblack(const color32* pSrc_pixels, uint32_t flags, uint32_t& cur_err, bc1_encode_results& results, const uint8_t* pForce_selectors) { int total_r = 0, total_g = 0, total_b = 0; int max_r = 0, max_g = 0, max_b = 0; int min_r = 255, min_g = 255, min_b = 255; int total_pixels = 0; for (uint32_t i = 0; i < 16; i++) { const int r = pSrc_pixels[i].r, g = pSrc_pixels[i].g, b = pSrc_pixels[i].b; if ((r | g | b) < 4) continue; max_r = std::max(max_r, r); max_g = std::max(max_g, g); max_b = std::max(max_b, b); min_r = std::min(min_r, r); min_g = std::min(min_g, g); min_b = std::min(min_b, b); total_r += r; total_g += g; total_b += b; total_pixels++; } if (!total_pixels) return false; int half_total_pixels = total_pixels >> 1; int avg_r = (total_r + half_total_pixels) / total_pixels; int avg_g = (total_g + half_total_pixels) / total_pixels; int avg_b = (total_b + half_total_pixels) / total_pixels; uint32_t low_c = 0, high_c = 0; int icov[6] = { 0, 0, 0, 0, 0, 0 }; for (uint32_t i = 0; i < 16; i++) { int r = (int)pSrc_pixels[i].r; int g = (int)pSrc_pixels[i].g; int b = (int)pSrc_pixels[i].b; if ((r | g | b) < 4) continue; r -= avg_r; g -= avg_g; b -= avg_b; icov[0] += r * r; icov[1] += r * g; icov[2] += r * b; icov[3] += g * g; icov[4] += g * b; icov[5] += b * b; } float cov[6]; for (uint32_t i = 0; i < 6; i++) cov[i] = (float)(icov[i]) * (1.0f / 255.0f); float xr = (float)(max_r - min_r); float xg = (float)(max_g - min_g); float xb = (float)(max_b - min_b); if (icov[2] < 0) xr = -xr; if (icov[4] < 0) xg = -xg; for (uint32_t power_iter = 0; power_iter < 4; power_iter++) { float r = xr * cov[0] + xg * cov[1] + xb * cov[2]; float g = xr * cov[1] + xg * cov[3] + xb * cov[4]; float b = xr * cov[2] + xg * cov[4] + xb * cov[5]; xr = r; xg = g; xb = b; } float k = maximum(fabsf(xr), fabsf(xg), fabsf(xb)); int saxis_r = 306, saxis_g = 601, saxis_b = 117; if (k >= 2) { float m = 1024.0f / k; saxis_r = (int)(xr * m); saxis_g = (int)(xg * m); saxis_b = (int)(xb * m); } int low_dot = INT_MAX, high_dot = INT_MIN; for (uint32_t i = 0; i < 16; i++) { int r = (int)pSrc_pixels[i].r, g = (int)pSrc_pixels[i].g, b = (int)pSrc_pixels[i].b; if ((r | g | b) < 4) continue; int dot = r * saxis_r + g * saxis_g + b * saxis_b; if (dot < low_dot) { low_dot = dot; low_c = i; } if (dot > high_dot) { high_dot = dot; high_c = i; } } int lr = to_5(pSrc_pixels[low_c].r); int lg = to_6(pSrc_pixels[low_c].g); int lb = to_5(pSrc_pixels[low_c].b); int hr = to_5(pSrc_pixels[high_c].r); int hg = to_6(pSrc_pixels[high_c].g); int hb = to_5(pSrc_pixels[high_c].b); uint8_t trial_sels[16]; uint32_t trial_err = bc1_find_sels3_fullerr(true, pSrc_pixels, lr, lg, lb, hr, hg, hb, trial_sels, UINT32_MAX, pForce_selectors); if (trial_err) { const uint32_t total_ls_passes = flags & cEncodeBC1TwoLeastSquaresPasses ? 2 : 1; for (uint32_t trials = 0; trials < total_ls_passes; trials++) { vec3F xl, xh; int lr2, lg2, lb2, hr2, hg2, hb2; if (!compute_least_squares_endpoints3_rgb(true, pSrc_pixels, trial_sels, &xl, &xh)) { lr2 = g_bc1_match5_half[avg_r].m_hi; lg2 = g_bc1_match6_half[avg_g].m_hi; lb2 = g_bc1_match5_half[avg_b].m_hi; hr2 = g_bc1_match5_half[avg_r].m_lo; hg2 = g_bc1_match6_half[avg_g].m_lo; hb2 = g_bc1_match5_half[avg_b].m_lo; } else { precise_round_565(xl, xh, hr2, hg2, hb2, lr2, lg2, lb2); } if ((lr == lr2) && (lg == lg2) && (lb == lb2) && (hr == hr2) && (hg == hg2) && (hb == hb2)) break; uint8_t trial_sels2[16]; uint32_t trial_err2 = bc1_find_sels3_fullerr(true, pSrc_pixels, lr2, lg2, lb2, hr2, hg2, hb2, trial_sels2, trial_err, pForce_selectors); if (trial_err2 < trial_err) { trial_err = trial_err2; lr = lr2; lg = lg2; lb = lb2; hr = hr2; hg = hg2; hb = hb2; memcpy(trial_sels, trial_sels2, sizeof(trial_sels)); } else break; } } if (trial_err < cur_err) { results.m_3color = true; results.lr = lr; results.lg = lg; results.lb = lb; results.hr = hr; results.hg = hg; results.hb = hb; memcpy(results.sels, trial_sels, 16); cur_err = trial_err; return true; } return false; } static bool try_3color_block(const color32* pSrc_pixels, uint32_t flags, uint32_t& cur_err, int avg_r, int avg_g, int avg_b, int lr, int lg, int lb, int hr, int hg, int hb, int total_r, int total_g, int total_b, uint32_t total_orderings_to_try, bc1_encode_results& results, const uint8_t* pForce_selectors) { if (pForce_selectors) { for (uint32_t i = 0; i < 16; i++) if (pForce_selectors[i] == 3) return false; } uint8_t trial_sels[16]; uint32_t trial_err = bc1_find_sels3_fullerr(false, pSrc_pixels, lr, lg, lb, hr, hg, hb, trial_sels, UINT32_MAX, pForce_selectors); if (trial_err) { const uint32_t total_ls_passes = flags & cEncodeBC1TwoLeastSquaresPasses ? 2 : 1; for (uint32_t trials = 0; trials < total_ls_passes; trials++) { vec3F xl, xh; int lr2, lg2, lb2, hr2, hg2, hb2; if (!compute_least_squares_endpoints3_rgb(false, pSrc_pixels, trial_sels, &xl, &xh)) { lr2 = g_bc1_match5_half[avg_r].m_hi; lg2 = g_bc1_match6_half[avg_g].m_hi; lb2 = g_bc1_match5_half[avg_b].m_hi; hr2 = g_bc1_match5_half[avg_r].m_lo; hg2 = g_bc1_match6_half[avg_g].m_lo; hb2 = g_bc1_match5_half[avg_b].m_lo; } else { precise_round_565(xl, xh, hr2, hg2, hb2, lr2, lg2, lb2); } if ((lr == lr2) && (lg == lg2) && (lb == lb2) && (hr == hr2) && (hg == hg2) && (hb == hb2)) break; uint8_t trial_sels2[16]; uint32_t trial_err2 = bc1_find_sels3_fullerr(false, pSrc_pixels, lr2, lg2, lb2, hr2, hg2, hb2, trial_sels2, trial_err, pForce_selectors); if (trial_err2 < trial_err) { trial_err = trial_err2; lr = lr2; lg = lg2; lb = lb2; hr = hr2; hg = hg2; hb = hb2; memcpy(trial_sels, trial_sels2, sizeof(trial_sels)); } else break; } } if ((trial_err) && (flags & cEncodeBC1UseLikelyTotalOrderings) && (total_orderings_to_try)) { hist3 h; for (uint32_t i = 0; i < 16; i++) { assert(trial_sels[i] < 3); h.m_hist[trial_sels[i]]++; } const uint32_t orig_total_order_index = h.lookup_total_ordering_index(); int r0, g0, b0, r3, g3, b3; r0 = (lr << 3) | (lr >> 2); g0 = (lg << 2) | (lg >> 4); b0 = (lb << 3) | (lb >> 2); r3 = (hr << 3) | (hr >> 2); g3 = (hg << 2) | (hg >> 4); b3 = (hb << 3) | (hb >> 2); int ar = r3 - r0, ag = g3 - g0, ab = b3 - b0; int dots[16]; for (uint32_t i = 0; i < 16; i++) { int r = pSrc_pixels[i].r; int g = pSrc_pixels[i].g; int b = pSrc_pixels[i].b; int d = 0x1000000 + (r * ar + g * ag + b * ab); assert(d >= 0); dots[i] = (d << 4) + i; } std::sort(dots, dots + 16); uint32_t r_sum[17], g_sum[17], b_sum[17]; uint32_t r = 0, g = 0, b = 0; for (uint32_t i = 0; i < 16; i++) { const uint32_t p = dots[i] & 15; r_sum[i] = r; g_sum[i] = g; b_sum[i] = b; r += pSrc_pixels[p].r; g += pSrc_pixels[p].g; b += pSrc_pixels[p].b; } r_sum[16] = total_r; g_sum[16] = total_g; b_sum[16] = total_b; const uint32_t q_total = (flags & cEncodeBC1Exhaustive) ? NUM_UNIQUE_TOTAL_ORDERINGS3 : std::min(total_orderings_to_try, MAX_TOTAL_ORDERINGS3); for (uint32_t q = 0; q < q_total; q++) { const uint32_t s = (flags & cEncodeBC1Exhaustive) ? q : g_best_total_orderings3[orig_total_order_index][q]; int trial_lr, trial_lg, trial_lb, trial_hr, trial_hg, trial_hb; vec3F xl, xh; if ((s == TOTAL_ORDER_3_0_16) || (s == TOTAL_ORDER_3_1_16) || (s == TOTAL_ORDER_3_2_16)) { trial_lr = g_bc1_match5_half[avg_r].m_hi; trial_lg = g_bc1_match6_half[avg_g].m_hi; trial_lb = g_bc1_match5_half[avg_b].m_hi; trial_hr = g_bc1_match5_half[avg_r].m_lo; trial_hg = g_bc1_match6_half[avg_g].m_lo; trial_hb = g_bc1_match5_half[avg_b].m_lo; } else { compute_least_squares_endpoints3_rgb(&xl, &xh, total_r, total_g, total_b, g_selector_factors3[s][0], g_selector_factors3[s][1], g_selector_factors3[s][2], s, r_sum, g_sum, b_sum); precise_round_565(xl, xh, trial_hr, trial_hg, trial_hb, trial_lr, trial_lg, trial_lb); } uint8_t trial_sels2[16]; uint32_t trial_err2 = bc1_find_sels3_fullerr(false, pSrc_pixels, trial_lr, trial_lg, trial_lb, trial_hr, trial_hg, trial_hb, trial_sels2, UINT32_MAX, pForce_selectors); if (trial_err2 < trial_err) { trial_err = trial_err2; lr = trial_lr; lg = trial_lg; lb = trial_lb; hr = trial_hr; hg = trial_hg; hb = trial_hb; memcpy(trial_sels, trial_sels2, sizeof(trial_sels)); } } // s } if (trial_err < cur_err) { results.m_3color = true; results.lr = lr; results.lg = lg; results.lb = lb; results.hr = hr; results.hg = hg; results.hb = hb; memcpy(results.sels, trial_sels, 16); cur_err = trial_err; return true; } return false; } void encode_bc1(uint32_t level, void* pDst, const uint8_t* pPixels, bool allow_3color, bool allow_transparent_texels_for_black, const uint8_t* pForce_selectors) { uint32_t flags = 0, total_orderings4 = 1, total_orderings3 = 1; static_assert(MAX_TOTAL_ORDERINGS3 >= 32, "MAX_TOTAL_ORDERINGS3 >= 32"); static_assert(MAX_TOTAL_ORDERINGS4 >= 32, "MAX_TOTAL_ORDERINGS4 >= 32"); switch (level) { case 0: // Faster/higher quality than stb_dxt default. flags = cEncodeBC1BoundingBoxInt; break; case 1: // Faster/higher quality than stb_dxt default. A bit higher average quality vs. mode 0. flags = cEncodeBC1Use2DLS; break; case 2: // On average mode 2 is a little weaker than modes 0/1, but it's stronger on outliers (very tough textures). // Slightly stronger than stb_dxt. flags = 0; break; case 3: // Slightly stronger than stb_dxt HIGHQUAL. flags = cEncodeBC1TwoLeastSquaresPasses; break; case 4: flags = cEncodeBC1TwoLeastSquaresPasses | cEncodeBC1UseFullMSEEval | cEncodeBC1Use6PowerIters; break; default: case 5: // stb_dxt HIGHQUAL + permit 3 color (if it's enabled). flags = cEncodeBC1TwoLeastSquaresPasses | cEncodeBC1UseFasterMSEEval; flags |= (allow_3color ? cEncodeBC1Use3ColorBlocks : 0) | (allow_transparent_texels_for_black ? cEncodeBC1Use3ColorBlocksForBlackPixels : 0); break; case 6: flags = cEncodeBC1TwoLeastSquaresPasses | cEncodeBC1UseFasterMSEEval | cEncodeBC1UseLikelyTotalOrderings; flags |= (allow_3color ? cEncodeBC1Use3ColorBlocks : 0) | (allow_transparent_texels_for_black ? cEncodeBC1Use3ColorBlocksForBlackPixels : 0); break; case 7: flags = cEncodeBC1TwoLeastSquaresPasses | cEncodeBC1UseFasterMSEEval | cEncodeBC1UseLikelyTotalOrderings; flags |= (allow_3color ? cEncodeBC1Use3ColorBlocks : 0) | (allow_transparent_texels_for_black ? cEncodeBC1Use3ColorBlocksForBlackPixels : 0); total_orderings4 = 4; break; case 8: flags = cEncodeBC1TwoLeastSquaresPasses | cEncodeBC1UseFasterMSEEval | cEncodeBC1UseLikelyTotalOrderings; flags |= (allow_3color ? cEncodeBC1Use3ColorBlocks : 0) | (allow_transparent_texels_for_black ? cEncodeBC1Use3ColorBlocksForBlackPixels : 0); total_orderings4 = 8; break; case 9: flags = cEncodeBC1TwoLeastSquaresPasses | cEncodeBC1UseLikelyTotalOrderings; flags |= (allow_3color ? cEncodeBC1Use3ColorBlocks : 0) | (allow_transparent_texels_for_black ? cEncodeBC1Use3ColorBlocksForBlackPixels : 0); total_orderings4 = 11; total_orderings3 = 3; break; case 10: flags = cEncodeBC1TwoLeastSquaresPasses | cEncodeBC1UseLikelyTotalOrderings; flags |= (allow_3color ? cEncodeBC1Use3ColorBlocks : 0) | (allow_transparent_texels_for_black ? cEncodeBC1Use3ColorBlocksForBlackPixels : 0); total_orderings4 = 20; total_orderings3 = 8; break; case 11: flags = cEncodeBC1TwoLeastSquaresPasses | cEncodeBC1UseLikelyTotalOrderings; flags |= (allow_3color ? cEncodeBC1Use3ColorBlocks : 0) | (allow_transparent_texels_for_black ? cEncodeBC1Use3ColorBlocksForBlackPixels : 0); total_orderings4 = 28; total_orderings3 = 16; break; case 12: flags = cEncodeBC1TwoLeastSquaresPasses | cEncodeBC1UseLikelyTotalOrderings; flags |= (allow_3color ? cEncodeBC1Use3ColorBlocks : 0) | (allow_transparent_texels_for_black ? cEncodeBC1Use3ColorBlocksForBlackPixels : 0); total_orderings4 = 32; total_orderings3 = 32; break; case 13: flags = cEncodeBC1TwoLeastSquaresPasses | cEncodeBC1UseFullMSEEval | cEncodeBC1UseLikelyTotalOrderings | cEncodeBC1Use6PowerIters | (20 << cEncodeBC1EndpointSearchRoundsShift) | cEncodeBC1TryAllInitialEndponts; flags |= (allow_3color ? cEncodeBC1Use3ColorBlocks : 0) | (allow_transparent_texels_for_black ? cEncodeBC1Use3ColorBlocksForBlackPixels : 0); total_orderings4 = 32; total_orderings3 = 32; break; case 14: flags = cEncodeBC1TwoLeastSquaresPasses | cEncodeBC1UseFullMSEEval | cEncodeBC1UseLikelyTotalOrderings | cEncodeBC1Use6PowerIters | (32 << cEncodeBC1EndpointSearchRoundsShift) | cEncodeBC1TryAllInitialEndponts; flags |= (allow_3color ? cEncodeBC1Use3ColorBlocks : 0) | (allow_transparent_texels_for_black ? cEncodeBC1Use3ColorBlocksForBlackPixels : 0); total_orderings4 = 32; total_orderings3 = 32; break; case 15: flags = cEncodeBC1TwoLeastSquaresPasses | cEncodeBC1UseFullMSEEval | cEncodeBC1UseLikelyTotalOrderings | cEncodeBC1Use6PowerIters | (32 << cEncodeBC1EndpointSearchRoundsShift) | cEncodeBC1TryAllInitialEndponts; flags |= (allow_3color ? cEncodeBC1Use3ColorBlocks : 0) | (allow_transparent_texels_for_black ? cEncodeBC1Use3ColorBlocksForBlackPixels : 0); total_orderings4 = ((((32 + MAX_TOTAL_ORDERINGS4) / 2) + 32) / 2); total_orderings3 = 32; break; case 16: flags = cEncodeBC1TwoLeastSquaresPasses | cEncodeBC1UseFullMSEEval | cEncodeBC1UseLikelyTotalOrderings | cEncodeBC1Use6PowerIters | (256 << cEncodeBC1EndpointSearchRoundsShift) | cEncodeBC1TryAllInitialEndponts; flags |= (allow_3color ? cEncodeBC1Use3ColorBlocks : 0) | (allow_transparent_texels_for_black ? cEncodeBC1Use3ColorBlocksForBlackPixels : 0); total_orderings4 = (32 + MAX_TOTAL_ORDERINGS4) / 2; total_orderings3 = 32; break; case 17: flags = cEncodeBC1TwoLeastSquaresPasses | cEncodeBC1UseFullMSEEval | cEncodeBC1UseLikelyTotalOrderings | cEncodeBC1Use6PowerIters | (256 << cEncodeBC1EndpointSearchRoundsShift) | cEncodeBC1TryAllInitialEndponts; flags |= (allow_3color ? cEncodeBC1Use3ColorBlocks : 0) | (allow_transparent_texels_for_black ? cEncodeBC1Use3ColorBlocksForBlackPixels : 0); total_orderings4 = MAX_TOTAL_ORDERINGS4; total_orderings3 = 32; break; case 18: flags = cEncodeBC1TwoLeastSquaresPasses | cEncodeBC1UseFullMSEEval | cEncodeBC1UseLikelyTotalOrderings | cEncodeBC1Use6PowerIters | cEncodeBC1Iterative | (256 << cEncodeBC1EndpointSearchRoundsShift) | cEncodeBC1TryAllInitialEndponts; flags |= (allow_3color ? cEncodeBC1Use3ColorBlocks : 0) | (allow_transparent_texels_for_black ? cEncodeBC1Use3ColorBlocksForBlackPixels : 0); total_orderings4 = MAX_TOTAL_ORDERINGS4; total_orderings3 = 32; break; case 19: // This hidden mode is *extremely* slow and abuses the encoder. It's just for testing/training. flags = cEncodeBC1TwoLeastSquaresPasses | cEncodeBC1UseFullMSEEval | cEncodeBC1UseLikelyTotalOrderings | cEncodeBC1Use6PowerIters | cEncodeBC1Exhaustive | cEncodeBC1Iterative | (256 << cEncodeBC1EndpointSearchRoundsShift) | cEncodeBC1TryAllInitialEndponts; flags |= (allow_3color ? cEncodeBC1Use3ColorBlocks : 0) | (allow_transparent_texels_for_black ? cEncodeBC1Use3ColorBlocksForBlackPixels : 0); total_orderings4 = 32; total_orderings3 = 32; break; } encode_bc1(pDst, pPixels, flags, total_orderings4, total_orderings3, pForce_selectors); } static inline void encode_bc1_pick_initial(const color32* pSrc_pixels, uint32_t flags, bool grayscale_flag, int min_r, int min_g, int min_b, int max_r, int max_g, int max_b, int avg_r, int avg_g, int avg_b, int total_r, int total_g, int total_b, int& lr, int& lg, int& lb, int& hr, int& hg, int& hb) { if (grayscale_flag) { const int fr = pSrc_pixels[0].r; // Grayscale blocks are a common enough case to specialize. if ((max_r - min_r) < 2) { lr = lb = hr = hb = to_5(fr); lg = hg = to_6(fr); } else { lr = lb = to_5(min_r); lg = to_6(min_r); hr = hb = to_5(max_r); hg = to_6(max_r); } } else if (flags & cEncodeBC1Use2DLS) { // 2D Least Squares approach from Humus's example, with added inset and optimal rounding. int big_chan = 0, min_chan_val = min_r, max_chan_val = max_r; if ((max_g - min_g) > (max_chan_val - min_chan_val)) big_chan = 1, min_chan_val = min_g, max_chan_val = max_g; if ((max_b - min_b) > (max_chan_val - min_chan_val)) big_chan = 2, min_chan_val = min_b, max_chan_val = max_b; int sum_xy_r = 0, sum_xy_g = 0, sum_xy_b = 0; vec3F l, h; if (big_chan == 0) { for (uint32_t i = 0; i < 16; i++) { const int r = pSrc_pixels[i].r, g = pSrc_pixels[i].g, b = pSrc_pixels[i].b; sum_xy_r += r * r, sum_xy_g += r * g, sum_xy_b += r * b; } int sum_x = total_r; int sum_x2 = sum_xy_r; float div = (float)(16 * sum_x2 - sum_x * sum_x); float b_y = 0.0f, b_z = 0.0f; if (fabs(div) > 1e-8f) { div = 1.0f / div; b_y = (16 * sum_xy_g - sum_x * total_g) * div; b_z = (16 * sum_xy_b - sum_x * total_b) * div; } float a_y = (total_g - b_y * sum_x) / 16.0f; float a_z = (total_b - b_z * sum_x) / 16.0f; l.c[1] = a_y + b_y * min_chan_val; l.c[2] = a_z + b_z * min_chan_val; h.c[1] = a_y + b_y * max_chan_val; h.c[2] = a_z + b_z * max_chan_val; float dg = (h.c[1] - l.c[1]); float db = (h.c[2] - l.c[2]); h.c[1] = l.c[1] + dg * (15.0f / 16.0f); h.c[2] = l.c[2] + db * (15.0f / 16.0f); l.c[1] = l.c[1] + dg * (1.0f / 16.0f); l.c[2] = l.c[2] + db * (1.0f / 16.0f); float d = (float)(max_chan_val - min_chan_val); float fmin_chan_val = min_chan_val + d * (1.0f / 16.0f); float fmax_chan_val = min_chan_val + d * (15.0f / 16.0f); l.c[0] = fmin_chan_val; h.c[0] = fmax_chan_val; } else if (big_chan == 1) { for (uint32_t i = 0; i < 16; i++) { const int r = pSrc_pixels[i].r, g = pSrc_pixels[i].g, b = pSrc_pixels[i].b; sum_xy_r += g * r, sum_xy_g += g * g, sum_xy_b += g * b; } int sum_x = total_g; int sum_x2 = sum_xy_g; float div = (float)(16 * sum_x2 - sum_x * sum_x); float b_x = 0.0f, b_z = 0.0f; if (fabs(div) > 1e-8f) { div = 1.0f / div; b_x = (16 * sum_xy_r - sum_x * total_r) * div; b_z = (16 * sum_xy_b - sum_x * total_b) * div; } float a_x = (total_r - b_x * sum_x) / 16.0f; float a_z = (total_b - b_z * sum_x) / 16.0f; l.c[0] = a_x + b_x * min_chan_val; l.c[2] = a_z + b_z * min_chan_val; h.c[0] = a_x + b_x * max_chan_val; h.c[2] = a_z + b_z * max_chan_val; float dr = (h.c[0] - l.c[0]); float db = (h.c[2] - l.c[2]); h.c[0] = l.c[0] + dr * (15.0f / 16.0f); h.c[2] = l.c[2] + db * (15.0f / 16.0f); l.c[0] = l.c[0] + dr * (1.0f / 16.0f); l.c[2] = l.c[2] + db * (1.0f / 16.0f); float d = (float)(max_chan_val - min_chan_val); float fmin_chan_val = min_chan_val + d * (1.0f / 16.0f); float fmax_chan_val = min_chan_val + d * (15.0f / 16.0f); l.c[1] = fmin_chan_val; h.c[1] = fmax_chan_val; } else { for (uint32_t i = 0; i < 16; i++) { const int r = pSrc_pixels[i].r, g = pSrc_pixels[i].g, b = pSrc_pixels[i].b; sum_xy_r += b * r, sum_xy_g += b * g, sum_xy_b += b * b; } int sum_x = total_b; int sum_x2 = sum_xy_b; float div = (float)(16 * sum_x2 - sum_x * sum_x); float b_x = 0.0f, b_y = 0.0f; if (fabs(div) > 1e-8f) { div = 1.0f / div; b_x = (16 * sum_xy_r - sum_x * total_r) * div; b_y = (16 * sum_xy_g - sum_x * total_g) * div; } float a_x = (total_r - b_x * sum_x) / 16.0f; float a_y = (total_g - b_y * sum_x) / 16.0f; l.c[0] = a_x + b_x * min_chan_val; l.c[1] = a_y + b_y * min_chan_val; h.c[0] = a_x + b_x * max_chan_val; h.c[1] = a_y + b_y * max_chan_val; float dr = (h.c[0] - l.c[0]); float dg = (h.c[1] - l.c[1]); h.c[0] = l.c[0] + dr * (15.0f / 16.0f); h.c[1] = l.c[1] + dg * (15.0f / 16.0f); l.c[0] = l.c[0] + dr * (1.0f / 16.0f); l.c[1] = l.c[1] + dg * (1.0f / 16.0f); float d = (float)(max_chan_val - min_chan_val); float fmin_chan_val = min_chan_val + d * (1.0f / 16.0f); float fmax_chan_val = min_chan_val + d * (15.0f / 16.0f); l.c[2] = fmin_chan_val; h.c[2] = fmax_chan_val; } precise_round_565_noscale(l, h, lr, lg, lb, hr, hg, hb); } else if (flags & cEncodeBC1BoundingBox) { // Algorithm from icbc.h compress_dxt1_fast() vec3F l, h; l.c[0] = min_r * (1.0f / 255.0f); l.c[1] = min_g * (1.0f / 255.0f); l.c[2] = min_b * (1.0f / 255.0f); h.c[0] = max_r * (1.0f / 255.0f); h.c[1] = max_g * (1.0f / 255.0f); h.c[2] = max_b * (1.0f / 255.0f); const float bias = 8.0f / 255.0f; float inset_r = (h.c[0] - l.c[0] - bias) * (1.0f / 16.0f); float inset_g = (h.c[1] - l.c[1] - bias) * (1.0f / 16.0f); float inset_b = (h.c[2] - l.c[2] - bias) * (1.0f / 16.0f); l.c[0] = clampf(l.c[0] + inset_r, 0.0f, 1.0f); l.c[1] = clampf(l.c[1] + inset_g, 0.0f, 1.0f); l.c[2] = clampf(l.c[2] + inset_b, 0.0f, 1.0f); h.c[0] = clampf(h.c[0] - inset_r, 0.0f, 1.0f); h.c[1] = clampf(h.c[1] - inset_g, 0.0f, 1.0f); h.c[2] = clampf(h.c[2] - inset_b, 0.0f, 1.0f); int icov_xz = 0, icov_yz = 0; for (uint32_t i = 0; i < 16; i++) { int r = (int)pSrc_pixels[i].r - avg_r; int g = (int)pSrc_pixels[i].g - avg_g; int b = (int)pSrc_pixels[i].b - avg_b; icov_xz += r * b; icov_yz += g * b; } if (icov_xz < 0) std::swap(l.c[0], h.c[0]); if (icov_yz < 0) std::swap(l.c[1], h.c[1]); precise_round_565(l, h, lr, lg, lb, hr, hg, hb); } else if (flags & cEncodeBC1BoundingBoxInt) { // Algorithm from icbc.h compress_dxt1_fast(), but converted to integer. int inset_r = (max_r - min_r - 8) >> 4; int inset_g = (max_g - min_g - 8) >> 4; int inset_b = (max_b - min_b - 8) >> 4; min_r += inset_r; min_g += inset_g; min_b += inset_b; if ((uint32_t)(min_r | min_g | min_b) > 255U) { min_r = clampi(min_r, 0, 255); min_g = clampi(min_g, 0, 255); min_b = clampi(min_b, 0, 255); } max_r -= inset_r; max_g -= inset_g; max_b -= inset_b; if ((uint32_t)(max_r | max_g | max_b) > 255U) { max_r = clampi(max_r, 0, 255); max_g = clampi(max_g, 0, 255); max_b = clampi(max_b, 0, 255); } int icov_xz = 0, icov_yz = 0; for (uint32_t i = 0; i < 16; i++) { int r = (int)pSrc_pixels[i].r - avg_r; int g = (int)pSrc_pixels[i].g - avg_g; int b = (int)pSrc_pixels[i].b - avg_b; icov_xz += r * b; icov_yz += g * b; } int x0 = min_r; int y0 = min_g; int x1 = max_r; int y1 = max_g; if (icov_xz < 0) std::swap(x0, x1); if (icov_yz < 0) std::swap(y0, y1); lr = to_5(x0); lg = to_6(y0); lb = to_5(min_b); hr = to_5(x1); hg = to_6(y1); hb = to_5(max_b); } else { // Select 2 colors along the principle axis. (There must be a faster/simpler way.) uint32_t low_c = 0, high_c = 0; int icov[6] = { 0, 0, 0, 0, 0, 0 }; for (uint32_t i = 0; i < 16; i++) { int r = (int)pSrc_pixels[i].r - avg_r; int g = (int)pSrc_pixels[i].g - avg_g; int b = (int)pSrc_pixels[i].b - avg_b; icov[0] += r * r; icov[1] += r * g; icov[2] += r * b; icov[3] += g * g; icov[4] += g * b; icov[5] += b * b; } int saxis_r = 306, saxis_g = 601, saxis_b = 117; float xr = (float)(max_r - min_r); float xg = (float)(max_g - min_g); float xb = (float)(max_b - min_b); if (icov[2] < 0) xr = -xr; if (icov[4] < 0) xg = -xg; float cov[6]; for (uint32_t i = 0; i < 6; i++) cov[i] = (float)(icov[i]) * (1.0f / 255.0f); const uint32_t total_power_iters = (flags & cEncodeBC1Use6PowerIters) ? 6 : 4; for (uint32_t power_iter = 0; power_iter < total_power_iters; power_iter++) { float r = xr * cov[0] + xg * cov[1] + xb * cov[2]; float g = xr * cov[1] + xg * cov[3] + xb * cov[4]; float b = xr * cov[2] + xg * cov[4] + xb * cov[5]; xr = r; xg = g; xb = b; } float k = maximum(fabsf(xr), fabsf(xg), fabsf(xb)); if (k >= 2) { float m = 2048.0f / k; saxis_r = (int)(xr * m); saxis_g = (int)(xg * m); saxis_b = (int)(xb * m); } int low_dot = INT_MAX, high_dot = INT_MIN; saxis_r = (int)((uint32_t)saxis_r << 4U); saxis_g = (int)((uint32_t)saxis_g << 4U); saxis_b = (int)((uint32_t)saxis_b << 4U); for (uint32_t i = 0; i < 16; i += 4) { int dot0 = ((pSrc_pixels[i].r * saxis_r + pSrc_pixels[i].g * saxis_g + pSrc_pixels[i].b * saxis_b) & ~0xF) + i; int dot1 = ((pSrc_pixels[i + 1].r * saxis_r + pSrc_pixels[i + 1].g * saxis_g + pSrc_pixels[i + 1].b * saxis_b) & ~0xF) + i + 1; int dot2 = ((pSrc_pixels[i + 2].r * saxis_r + pSrc_pixels[i + 2].g * saxis_g + pSrc_pixels[i + 2].b * saxis_b) & ~0xF) + i + 2; int dot3 = ((pSrc_pixels[i + 3].r * saxis_r + pSrc_pixels[i + 3].g * saxis_g + pSrc_pixels[i + 3].b * saxis_b) & ~0xF) + i + 3; int min_d01 = std::min(dot0, dot1); int max_d01 = std::max(dot0, dot1); int min_d23 = std::min(dot2, dot3); int max_d23 = std::max(dot2, dot3); int min_d = std::min(min_d01, min_d23); int max_d = std::max(max_d01, max_d23); low_dot = std::min(low_dot, min_d); high_dot = std::max(high_dot, max_d); } low_c = low_dot & 15; high_c = high_dot & 15; lr = to_5(pSrc_pixels[low_c].r); lg = to_6(pSrc_pixels[low_c].g); lb = to_5(pSrc_pixels[low_c].b); hr = to_5(pSrc_pixels[high_c].r); hg = to_6(pSrc_pixels[high_c].g); hb = to_5(pSrc_pixels[high_c].b); } } static const int8_t s_adjacent_voxels[16][4] = { { 1,0,0, 3 }, // 0 { 0,1,0, 4 }, // 1 { 0,0,1, 5 }, // 2 { -1,0,0, 0 }, // 3 { 0,-1,0, 1 }, // 4 { 0,0,-1, 2 }, // 5 { 1,1,0, 9 }, // 6 { 1,0,1, 10 }, // 7 { 0,1,1, 11 }, // 8 { -1,-1,0, 6 }, // 9 { -1,0,-1, 7 }, // 10 { 0,-1,-1, 8 }, // 11 { -1,1,0, 13 }, // 12 { 1,-1,0, 12 }, // 13 { 0,-1,1, 15 }, // 14 { 0,1,-1, 14 }, // 15 }; // From icbc's high quality mode. static inline void encode_bc1_endpoint_search(const color32* pSrc_pixels, bool any_black_pixels, uint32_t flags, bc1_encode_results& results, uint32_t cur_err, const uint8_t* pForce_selectors) { int& lr = results.lr, & lg = results.lg, & lb = results.lb, & hr = results.hr, & hg = results.hg, & hb = results.hb; uint8_t* sels = results.sels; int prev_improvement_index = 0, forbidden_direction = -1; const int endpoint_search_rounds = (flags & cEncodeBC1EndpointSearchRoundsMask) >> cEncodeBC1EndpointSearchRoundsShift; for (int i = 0; i < endpoint_search_rounds; i++) { assert(s_adjacent_voxels[s_adjacent_voxels[i & 15][3]][3] == (i & 15)); if (forbidden_direction == (i & 31)) continue; const int8_t delta[3] = { s_adjacent_voxels[i & 15][0], s_adjacent_voxels[i & 15][1], s_adjacent_voxels[i & 15][2] }; int trial_lr = lr, trial_lg = lg, trial_lb = lb, trial_hr = hr, trial_hg = hg, trial_hb = hb; if ((i >> 4) & 1) { trial_lr = clampi(trial_lr + delta[0], 0, 31); trial_lg = clampi(trial_lg + delta[1], 0, 63); trial_lb = clampi(trial_lb + delta[2], 0, 31); } else { trial_hr = clampi(trial_hr + delta[0], 0, 31); trial_hg = clampi(trial_hg + delta[1], 0, 63); trial_hb = clampi(trial_hb + delta[2], 0, 31); } uint8_t trial_sels[16]; uint32_t trial_err; if (results.m_3color) { trial_err = bc1_find_sels3_fullerr( ((any_black_pixels) && ((flags & cEncodeBC1Use3ColorBlocksForBlackPixels) != 0)), pSrc_pixels, trial_lr, trial_lg, trial_lb, trial_hr, trial_hg, trial_hb, trial_sels, cur_err, pForce_selectors); } else { trial_err = bc1_find_sels4(flags, pSrc_pixels, trial_lr, trial_lg, trial_lb, trial_hr, trial_hg, trial_hb, trial_sels, cur_err, pForce_selectors); } if (trial_err < cur_err) { cur_err = trial_err; forbidden_direction = s_adjacent_voxels[i & 15][3] | (i & 16); lr = trial_lr, lg = trial_lg, lb = trial_lb, hr = trial_hr, hg = trial_hg, hb = trial_hb; memcpy(sels, trial_sels, 16); prev_improvement_index = i; } if (i - prev_improvement_index > 32) break; } } void encode_bc1(void* pDst, const uint8_t* pPixels, uint32_t flags, uint32_t total_orderings_to_try, uint32_t total_orderings_to_try3, const uint8_t* pForce_selectors) { assert(g_initialized); const color32* pSrc_pixels = (const color32*)pPixels; bc1_block* pDst_block = static_cast<bc1_block*>(pDst); int avg_r, avg_g, avg_b, min_r, min_g, min_b, max_r, max_g, max_b; const uint32_t fr = pSrc_pixels[0].r, fg = pSrc_pixels[0].g, fb = pSrc_pixels[0].b; uint32_t j; for (j = 15; j >= 1; --j) if ((pSrc_pixels[j].r != fr) || (pSrc_pixels[j].g != fg) || (pSrc_pixels[j].b != fb)) break; if (j == 0) { encode_bc1_solid_block(pDst, fr, fg, fb, (flags & (cEncodeBC1Use3ColorBlocks | cEncodeBC1Use3ColorBlocksForBlackPixels)) != 0); return; } int total_r = fr, total_g = fg, total_b = fb; max_r = fr, max_g = fg, max_b = fb; min_r = fr, min_g = fg, min_b = fb; uint32_t grayscale_flag = (fr == fg) && (fr == fb); uint32_t any_black_pixels = (fr | fg | fb) < 4; for (uint32_t i = 1; i < 16; i++) { const int r = pSrc_pixels[i].r, g = pSrc_pixels[i].g, b = pSrc_pixels[i].b; grayscale_flag &= ((r == g) && (r == b)); any_black_pixels |= ((r | g | b) < 4); max_r = std::max(max_r, r); max_g = std::max(max_g, g); max_b = std::max(max_b, b); min_r = std::min(min_r, r); min_g = std::min(min_g, g); min_b = std::min(min_b, b); total_r += r; total_g += g; total_b += b; } avg_r = (total_r + 8) >> 4, avg_g = (total_g + 8) >> 4, avg_b = (total_b + 8) >> 4; bc1_encode_results results; results.m_3color = false; uint8_t* sels = results.sels; int& lr = results.lr, & lg = results.lg, & lb = results.lb, & hr = results.hr, & hg = results.hg, & hb = results.hb; int orig_lr = 0, orig_lg = 0, orig_lb = 0, orig_hr = 0, orig_hg = 0, orig_hb = 0; lr = 0, lg = 0, lb = 0, hr = 0, hg = 0, hb = 0; const bool needs_block_error = ((flags & (cEncodeBC1UseLikelyTotalOrderings | cEncodeBC1Use3ColorBlocks | cEncodeBC1UseFullMSEEval | cEncodeBC1EndpointSearchRoundsMask)) != 0) || (any_black_pixels && ((flags & cEncodeBC1Use3ColorBlocksForBlackPixels) != 0)); uint32_t cur_err = UINT32_MAX; if (!needs_block_error) { assert((flags & cEncodeBC1TryAllInitialEndponts) == 0); encode_bc1_pick_initial(pSrc_pixels, flags, grayscale_flag != 0, min_r, min_g, min_b, max_r, max_g, max_b, avg_r, avg_g, avg_b, total_r, total_g, total_b, lr, lg, lb, hr, hg, hb); orig_lr = lr, orig_lg = lg, orig_lb = lb, orig_hr = hr, orig_hg = hg, orig_hb = hb; bc1_find_sels4_noerr(pSrc_pixels, lr, lg, lb, hr, hg, hb, sels, pForce_selectors); const uint32_t total_ls_passes = flags & cEncodeBC1TwoLeastSquaresPasses ? 2 : 1; for (uint32_t ls_pass = 0; ls_pass < total_ls_passes; ls_pass++) { int trial_lr, trial_lg, trial_lb, trial_hr, trial_hg, trial_hb; vec3F xl, xh; if (!compute_least_squares_endpoints4_rgb(pSrc_pixels, sels, &xl, &xh, total_r, total_g, total_b)) { // All selectors equal - treat it as a solid block which should always be equal or better. trial_lr = g_bc1_match5_equals_1[avg_r].m_hi; trial_lg = g_bc1_match6_equals_1[avg_g].m_hi; trial_lb = g_bc1_match5_equals_1[avg_b].m_hi; trial_hr = g_bc1_match5_equals_1[avg_r].m_lo; trial_hg = g_bc1_match6_equals_1[avg_g].m_lo; trial_hb = g_bc1_match5_equals_1[avg_b].m_lo; // In high/higher quality mode, let it try again in case the optimal tables have caused the sels to diverge. } else { precise_round_565(xl, xh, trial_hr, trial_hg, trial_hb, trial_lr, trial_lg, trial_lb); } if ((lr == trial_lr) && (lg == trial_lg) && (lb == trial_lb) && (hr == trial_hr) && (hg == trial_hg) && (hb == trial_hb)) break; bc1_find_sels4_noerr(pSrc_pixels, trial_lr, trial_lg, trial_lb, trial_hr, trial_hg, trial_hb, sels, pForce_selectors); lr = trial_lr; lg = trial_lg; lb = trial_lb; hr = trial_hr; hg = trial_hg; hb = trial_hb; } // ls_pass } else { const uint32_t total_rounds = (flags & cEncodeBC1TryAllInitialEndponts) ? 2 : 1; for (uint32_t round = 0; round < total_rounds; round++) { uint32_t modified_flags = flags; if (round == 1) { modified_flags &= ~(cEncodeBC1Use2DLS | cEncodeBC1BoundingBox); modified_flags |= cEncodeBC1BoundingBox; } int round_lr, round_lg, round_lb, round_hr, round_hg, round_hb; uint8_t round_sels[16]; encode_bc1_pick_initial(pSrc_pixels, modified_flags, grayscale_flag != 0, min_r, min_g, min_b, max_r, max_g, max_b, avg_r, avg_g, avg_b, total_r, total_g, total_b, round_lr, round_lg, round_lb, round_hr, round_hg, round_hb); int orig_round_lr = round_lr, orig_round_lg = round_lg, orig_round_lb = round_lb, orig_round_hr = round_hr, orig_round_hg = round_hg, orig_round_hb = round_hb; uint32_t round_err = bc1_find_sels4(flags, pSrc_pixels, round_lr, round_lg, round_lb, round_hr, round_hg, round_hb, round_sels, UINT32_MAX, pForce_selectors); const uint32_t total_ls_passes = flags & cEncodeBC1TwoLeastSquaresPasses ? 2 : 1; for (uint32_t ls_pass = 0; ls_pass < total_ls_passes; ls_pass++) { int trial_lr, trial_lg, trial_lb, trial_hr, trial_hg, trial_hb; vec3F xl, xh; if (!compute_least_squares_endpoints4_rgb(pSrc_pixels, round_sels, &xl, &xh, total_r, total_g, total_b)) { // All selectors equal - treat it as a solid block which should always be equal or better. trial_lr = g_bc1_match5_equals_1[avg_r].m_hi; trial_lg = g_bc1_match6_equals_1[avg_g].m_hi; trial_lb = g_bc1_match5_equals_1[avg_b].m_hi; trial_hr = g_bc1_match5_equals_1[avg_r].m_lo; trial_hg = g_bc1_match6_equals_1[avg_g].m_lo; trial_hb = g_bc1_match5_equals_1[avg_b].m_lo; // In high/higher quality mode, let it try again in case the optimal tables have caused the sels to diverge. } else { precise_round_565(xl, xh, trial_hr, trial_hg, trial_hb, trial_lr, trial_lg, trial_lb); } if ((round_lr == trial_lr) && (round_lg == trial_lg) && (round_lb == trial_lb) && (round_hr == trial_hr) && (round_hg == trial_hg) && (round_hb == trial_hb)) break; uint8_t trial_sels[16]; uint32_t trial_err = bc1_find_sels4(flags, pSrc_pixels, trial_lr, trial_lg, trial_lb, trial_hr, trial_hg, trial_hb, trial_sels, round_err, pForce_selectors); if (trial_err < round_err) { round_lr = trial_lr; round_lg = trial_lg; round_lb = trial_lb; round_hr = trial_hr; round_hg = trial_hg; round_hb = trial_hb; round_err = trial_err; memcpy(round_sels, trial_sels, 16); } else break; } // ls_pass if (round_err <= cur_err) { cur_err = round_err; lr = round_lr; lg = round_lg; lb = round_lb; hr = round_hr; hg = round_hg; hb = round_hb; orig_lr = orig_round_lr; orig_lg = orig_round_lg; orig_lb = orig_round_lb; orig_hr = orig_round_hr; orig_hg = orig_round_hg; orig_hb = orig_round_hb; memcpy(sels, round_sels, 16); } } // round } if ((cur_err) && (flags & cEncodeBC1UseLikelyTotalOrderings)) { assert(needs_block_error); const uint32_t total_iters = (flags & cEncodeBC1Iterative) ? 2 : 1; for (uint32_t iter_index = 0; iter_index < total_iters; iter_index++) { const uint32_t orig_err = cur_err; hist4 h; for (uint32_t i = 0; i < 16; i++) { assert(sels[i] < 4); h.m_hist[sels[i]]++; } const uint32_t orig_total_order_index = h.lookup_total_ordering_index(); int r0, g0, b0, r3, g3, b3; r0 = (lr << 3) | (lr >> 2); g0 = (lg << 2) | (lg >> 4); b0 = (lb << 3) | (lb >> 2); r3 = (hr << 3) | (hr >> 2); g3 = (hg << 2) | (hg >> 4); b3 = (hb << 3) | (hb >> 2); int ar = r3 - r0, ag = g3 - g0, ab = b3 - b0; int dots[16]; for (uint32_t i = 0; i < 16; i++) { int r = pSrc_pixels[i].r; int g = pSrc_pixels[i].g; int b = pSrc_pixels[i].b; int d = 0x1000000 + (r * ar + g * ag + b * ab); assert(d >= 0); dots[i] = (d << 4) + i; } std::sort(dots, dots + 16); uint32_t r_sum[17], g_sum[17], b_sum[17]; uint32_t r = 0, g = 0, b = 0; for (uint32_t i = 0; i < 16; i++) { const uint32_t p = dots[i] & 15; r_sum[i] = r; g_sum[i] = g; b_sum[i] = b; r += pSrc_pixels[p].r; g += pSrc_pixels[p].g; b += pSrc_pixels[p].b; } r_sum[16] = total_r; g_sum[16] = total_g; b_sum[16] = total_b; const uint32_t q_total = (flags & cEncodeBC1Exhaustive) ? NUM_UNIQUE_TOTAL_ORDERINGS4 : clampi(total_orderings_to_try, MIN_TOTAL_ORDERINGS, MAX_TOTAL_ORDERINGS4); for (uint32_t q = 0; q < q_total; q++) { const uint32_t s = (flags & cEncodeBC1Exhaustive) ? q : g_best_total_orderings4[orig_total_order_index][q]; int trial_lr, trial_lg, trial_lb, trial_hr, trial_hg, trial_hb; vec3F xl, xh; if ((s == TOTAL_ORDER_4_0_16) || (s == TOTAL_ORDER_4_1_16) || (s == TOTAL_ORDER_4_2_16) || (s == TOTAL_ORDER_4_3_16)) { trial_lr = g_bc1_match5_equals_1[avg_r].m_hi; trial_lg = g_bc1_match6_equals_1[avg_g].m_hi; trial_lb = g_bc1_match5_equals_1[avg_b].m_hi; trial_hr = g_bc1_match5_equals_1[avg_r].m_lo; trial_hg = g_bc1_match6_equals_1[avg_g].m_lo; trial_hb = g_bc1_match5_equals_1[avg_b].m_lo; } else { compute_least_squares_endpoints4_rgb(&xl, &xh, total_r, total_g, total_b, g_selector_factors4[s][0], g_selector_factors4[s][1], g_selector_factors4[s][2], s, r_sum, g_sum, b_sum); precise_round_565(xl, xh, trial_hr, trial_hg, trial_hb, trial_lr, trial_lg, trial_lb); } uint8_t trial_sels[16]; uint32_t trial_err = bc1_find_sels4(flags, pSrc_pixels, trial_lr, trial_lg, trial_lb, trial_hr, trial_hg, trial_hb, trial_sels, cur_err, pForce_selectors); if (trial_err < cur_err) { cur_err = trial_err; lr = trial_lr; lg = trial_lg; lb = trial_lb; hr = trial_hr; hg = trial_hg; hb = trial_hb; memcpy(sels, trial_sels, 16); } } // s if ((!cur_err) || (cur_err == orig_err)) break; } // iter_index } if (((flags & (cEncodeBC1Use3ColorBlocks | cEncodeBC1Use3ColorBlocksForBlackPixels)) != 0) && (cur_err)) { if (flags & cEncodeBC1Use3ColorBlocks) { assert(needs_block_error); try_3color_block(pSrc_pixels, flags, cur_err, avg_r, avg_g, avg_b, orig_lr, orig_lg, orig_lb, orig_hr, orig_hg, orig_hb, total_r, total_g, total_b, total_orderings_to_try3, results, pForce_selectors); } if ((any_black_pixels) && ((flags & cEncodeBC1Use3ColorBlocksForBlackPixels) != 0)) { assert(needs_block_error); try_3color_block_useblack(pSrc_pixels, flags, cur_err, results, pForce_selectors); } } if ((flags & cEncodeBC1EndpointSearchRoundsMask) && (cur_err)) { assert(needs_block_error); encode_bc1_endpoint_search(pSrc_pixels, any_black_pixels != 0, flags, results, cur_err, pForce_selectors); } if (results.m_3color) bc1_encode3(pDst_block, results.lr, results.lg, results.lb, results.hr, results.hg, results.hb, results.sels); else bc1_encode4(pDst_block, results.lr, results.lg, results.lb, results.hr, results.hg, results.hb, results.sels); } // BC3-5 void encode_bc4(void* pDst, const uint8_t* pPixels, uint32_t stride) { assert(g_initialized); uint32_t min0_v, max0_v, min1_v, max1_v, min2_v, max2_v, min3_v, max3_v; { min0_v = max0_v = pPixels[0 * stride]; min1_v = max1_v = pPixels[1 * stride]; min2_v = max2_v = pPixels[2 * stride]; min3_v = max3_v = pPixels[3 * stride]; } { uint32_t v0 = pPixels[4 * stride]; min0_v = std::min(min0_v, v0); max0_v = std::max(max0_v, v0); uint32_t v1 = pPixels[5 * stride]; min1_v = std::min(min1_v, v1); max1_v = std::max(max1_v, v1); uint32_t v2 = pPixels[6 * stride]; min2_v = std::min(min2_v, v2); max2_v = std::max(max2_v, v2); uint32_t v3 = pPixels[7 * stride]; min3_v = std::min(min3_v, v3); max3_v = std::max(max3_v, v3); } { uint32_t v0 = pPixels[8 * stride]; min0_v = std::min(min0_v, v0); max0_v = std::max(max0_v, v0); uint32_t v1 = pPixels[9 * stride]; min1_v = std::min(min1_v, v1); max1_v = std::max(max1_v, v1); uint32_t v2 = pPixels[10 * stride]; min2_v = std::min(min2_v, v2); max2_v = std::max(max2_v, v2); uint32_t v3 = pPixels[11 * stride]; min3_v = std::min(min3_v, v3); max3_v = std::max(max3_v, v3); } { uint32_t v0 = pPixels[12 * stride]; min0_v = std::min(min0_v, v0); max0_v = std::max(max0_v, v0); uint32_t v1 = pPixels[13 * stride]; min1_v = std::min(min1_v, v1); max1_v = std::max(max1_v, v1); uint32_t v2 = pPixels[14 * stride]; min2_v = std::min(min2_v, v2); max2_v = std::max(max2_v, v2); uint32_t v3 = pPixels[15 * stride]; min3_v = std::min(min3_v, v3); max3_v = std::max(max3_v, v3); } const uint32_t min_v = minimum(min0_v, min1_v, min2_v, min3_v); const uint32_t max_v = maximum(max0_v, max1_v, max2_v, max3_v); uint8_t* pDst_bytes = static_cast<uint8_t*>(pDst); pDst_bytes[0] = (uint8_t)max_v; pDst_bytes[1] = (uint8_t)min_v; if (max_v == min_v) { memset(pDst_bytes + 2, 0, 6); return; } const uint32_t delta = max_v - min_v; // min_v is now 0. Compute thresholds between values by scaling max_v. It's x14 because we're adding two x7 scale factors. const int t0 = delta * 13; const int t1 = delta * 11; const int t2 = delta * 9; const int t3 = delta * 7; const int t4 = delta * 5; const int t5 = delta * 3; const int t6 = delta * 1; // BC4 floors in its divisions, which we compensate for with the 4 bias. // This function is optimal for all possible inputs (i.e. it outputs the same results as checking all 8 values and choosing the closest one). const int bias = 4 - min_v * 14; static const uint32_t s_tran0[8] = { 1U , 7U , 6U , 5U , 4U , 3U , 2U , 0U }; static const uint32_t s_tran1[8] = { 1U << 3U, 7U << 3U, 6U << 3U, 5U << 3U, 4U << 3U, 3U << 3U, 2U << 3U, 0U << 3U }; static const uint32_t s_tran2[8] = { 1U << 6U, 7U << 6U, 6U << 6U, 5U << 6U, 4U << 6U, 3U << 6U, 2U << 6U, 0U << 6U }; static const uint32_t s_tran3[8] = { 1U << 9U, 7U << 9U, 6U << 9U, 5U << 9U, 4U << 9U, 3U << 9U, 2U << 9U, 0U << 9U }; uint64_t a0, a1, a2, a3; { const int v0 = pPixels[0 * stride] * 14 + bias; const int v1 = pPixels[1 * stride] * 14 + bias; const int v2 = pPixels[2 * stride] * 14 + bias; const int v3 = pPixels[3 * stride] * 14 + bias; a0 = s_tran0[(v0 >= t0) + (v0 >= t1) + (v0 >= t2) + (v0 >= t3) + (v0 >= t4) + (v0 >= t5) + (v0 >= t6)]; a1 = s_tran1[(v1 >= t0) + (v1 >= t1) + (v1 >= t2) + (v1 >= t3) + (v1 >= t4) + (v1 >= t5) + (v1 >= t6)]; a2 = s_tran2[(v2 >= t0) + (v2 >= t1) + (v2 >= t2) + (v2 >= t3) + (v2 >= t4) + (v2 >= t5) + (v2 >= t6)]; a3 = s_tran3[(v3 >= t0) + (v3 >= t1) + (v3 >= t2) + (v3 >= t3) + (v3 >= t4) + (v3 >= t5) + (v3 >= t6)]; } { const int v0 = pPixels[4 * stride] * 14 + bias; const int v1 = pPixels[5 * stride] * 14 + bias; const int v2 = pPixels[6 * stride] * 14 + bias; const int v3 = pPixels[7 * stride] * 14 + bias; a0 |= (uint64_t)(s_tran0[(v0 >= t0) + (v0 >= t1) + (v0 >= t2) + (v0 >= t3) + (v0 >= t4) + (v0 >= t5) + (v0 >= t6)] << 12U); a1 |= (uint64_t)(s_tran1[(v1 >= t0) + (v1 >= t1) + (v1 >= t2) + (v1 >= t3) + (v1 >= t4) + (v1 >= t5) + (v1 >= t6)] << 12U); a2 |= (uint64_t)(s_tran2[(v2 >= t0) + (v2 >= t1) + (v2 >= t2) + (v2 >= t3) + (v2 >= t4) + (v2 >= t5) + (v2 >= t6)] << 12U); a3 |= (uint64_t)(s_tran3[(v3 >= t0) + (v3 >= t1) + (v3 >= t2) + (v3 >= t3) + (v3 >= t4) + (v3 >= t5) + (v3 >= t6)] << 12U); } { const int v0 = pPixels[8 * stride] * 14 + bias; const int v1 = pPixels[9 * stride] * 14 + bias; const int v2 = pPixels[10 * stride] * 14 + bias; const int v3 = pPixels[11 * stride] * 14 + bias; a0 |= (((uint64_t)s_tran0[(v0 >= t0) + (v0 >= t1) + (v0 >= t2) + (v0 >= t3) + (v0 >= t4) + (v0 >= t5) + (v0 >= t6)]) << 24U); a1 |= (((uint64_t)s_tran1[(v1 >= t0) + (v1 >= t1) + (v1 >= t2) + (v1 >= t3) + (v1 >= t4) + (v1 >= t5) + (v1 >= t6)]) << 24U); a2 |= (((uint64_t)s_tran2[(v2 >= t0) + (v2 >= t1) + (v2 >= t2) + (v2 >= t3) + (v2 >= t4) + (v2 >= t5) + (v2 >= t6)]) << 24U); a3 |= (((uint64_t)s_tran3[(v3 >= t0) + (v3 >= t1) + (v3 >= t2) + (v3 >= t3) + (v3 >= t4) + (v3 >= t5) + (v3 >= t6)]) << 24U); } { const int v0 = pPixels[12 * stride] * 14 + bias; const int v1 = pPixels[13 * stride] * 14 + bias; const int v2 = pPixels[14 * stride] * 14 + bias; const int v3 = pPixels[15 * stride] * 14 + bias; a0 |= (((uint64_t)s_tran0[(v0 >= t0) + (v0 >= t1) + (v0 >= t2) + (v0 >= t3) + (v0 >= t4) + (v0 >= t5) + (v0 >= t6)]) << 36U); a1 |= (((uint64_t)s_tran1[(v1 >= t0) + (v1 >= t1) + (v1 >= t2) + (v1 >= t3) + (v1 >= t4) + (v1 >= t5) + (v1 >= t6)]) << 36U); a2 |= (((uint64_t)s_tran2[(v2 >= t0) + (v2 >= t1) + (v2 >= t2) + (v2 >= t3) + (v2 >= t4) + (v2 >= t5) + (v2 >= t6)]) << 36U); a3 |= (((uint64_t)s_tran3[(v3 >= t0) + (v3 >= t1) + (v3 >= t2) + (v3 >= t3) + (v3 >= t4) + (v3 >= t5) + (v3 >= t6)]) << 36U); } const uint64_t f = a0 | a1 | a2 | a3; pDst_bytes[2] = (uint8_t)f; pDst_bytes[3] = (uint8_t)(f >> 8U); pDst_bytes[4] = (uint8_t)(f >> 16U); pDst_bytes[5] = (uint8_t)(f >> 24U); pDst_bytes[6] = (uint8_t)(f >> 32U); pDst_bytes[7] = (uint8_t)(f >> 40U); } uint32_t encode_bc4_hq(void* pDst, const uint8_t* pPixels, uint32_t stride, uint32_t search_rad, uint32_t mode_flag, const uint8_t* pForce_selectors) { assert(mode_flag); uint8_t* pDst_bytes = static_cast<uint8_t*>(pDst); uint32_t min_val = 255, max_val = 0; for (uint32_t i = 0; i < 16; i++) { uint32_t val = pPixels[i * stride]; min_val = std::min(val, min_val); max_val = std::max(val, max_val); } if (min_val == max_val) { if (mode_flag & BC4_USE_MODE6_FLAG) { pDst_bytes[0] = (uint8_t)min_val; pDst_bytes[1] = (uint8_t)min_val; memset(pDst_bytes + 2, 0, 6); assert(!(pDst_bytes[0] > pDst_bytes[1])); } else { // Use an 8 value encoding if (min_val > 0) { pDst_bytes[0] = (uint8_t)min_val; pDst_bytes[1] = (uint8_t)min_val - 1; memset(pDst_bytes + 2, 0, 6); } else { static const uint8_t s_const_1_vals[8] = { 1, 0, 0x49, 0x92, 0x24, 0x49, 0x92, 0x24 }; memcpy(pDst_bytes, s_const_1_vals, 8); } assert(pDst_bytes[0] > pDst_bytes[1]); } #if defined(_DEBUG) || defined(DEBUG) { bc4_block* pBlock = (bc4_block*)pDst; uint8_t pixels[16]; unpack_bc4(pDst, pixels, 1); for (uint32_t i = 0; i < 16; i++) assert(pixels[i] == min_val); if (mode_flag & BC4_USE_MODE6_FLAG) { assert(pBlock->is_alpha6_block()); } else { assert(!pBlock->is_alpha6_block()); } } #endif return 0; } uint32_t best_err = UINT32_MAX; for (uint32_t mode = 0; mode < 2; mode++) { if ((mode_flag & (1 << mode)) == 0) continue; for (int lo_delta = -(int)search_rad; lo_delta <= (int)search_rad; lo_delta++) { for (int hi_delta = -(int)search_rad; hi_delta <= (int)search_rad; hi_delta++) { bc4_block trial_block; trial_block.m_endpoints[0] = (uint8_t)clamp<int>(max_val + hi_delta, 0, 255); trial_block.m_endpoints[1] = (uint8_t)clamp<int>(min_val + lo_delta, 0, 255); if (trial_block.m_endpoints[0] == trial_block.m_endpoints[1]) continue; if (mode == 0) { if (trial_block.is_alpha6_block()) std::swap(trial_block.m_endpoints[0], trial_block.m_endpoints[1]); } else if (!trial_block.is_alpha6_block()) std::swap(trial_block.m_endpoints[0], trial_block.m_endpoints[1]); uint8_t block_vals[8]; trial_block.get_block_values(block_vals, trial_block.m_endpoints[0], trial_block.m_endpoints[1]); uint32_t trial_err = 0; uint8_t trial_sels[16]; if (pForce_selectors) { memcpy(trial_sels, pForce_selectors, 16); for (uint32_t i = 0; i < 16; i++) trial_err += squarei(block_vals[pForce_selectors[i]] - pPixels[i * stride]); } else { for (uint32_t i = 0; i < 16; i++) { uint32_t best_index_err = UINT32_MAX; uint32_t best_index = 0; for (uint32_t j = 0; j < 8; j++) { uint32_t err = squarei(block_vals[j] - pPixels[i * stride]); if (err < best_index_err) { best_index_err = err; best_index = j; if (!err) break; } } trial_err += best_index_err; if (trial_err >= best_err) break; trial_sels[i] = (uint8_t)best_index; } // i } if (trial_err < best_err) { best_err = trial_err; uint64_t sel_vals = 0; for (uint32_t i = 0; i < 16; i++) sel_vals |= ((uint64_t)trial_sels[i] << (i * 3)); trial_block.m_selectors[0] = (uint8_t)sel_vals; trial_block.m_selectors[1] = (uint8_t)(sel_vals >> 8); trial_block.m_selectors[2] = (uint8_t)(sel_vals >> 16); trial_block.m_selectors[3] = (uint8_t)(sel_vals >> 24); trial_block.m_selectors[4] = (uint8_t)(sel_vals >> 32); trial_block.m_selectors[5] = (uint8_t)(sel_vals >> 40); memcpy(pDst_bytes, &trial_block, sizeof(bc4_block)); } // if (trial_err < best_err) } // hi_delta } // lo_delta } // mode return best_err; } void encode_bc3(void* pDst, const uint8_t* pPixels, uint32_t flags, uint32_t total_orderings_to_try) { assert(g_initialized); // 3-color blocks are not allowed with BC3 (on most GPU's). flags &= ~(cEncodeBC1Use3ColorBlocksForBlackPixels | cEncodeBC1Use3ColorBlocks); encode_bc4(pDst, pPixels + 3, 4); encode_bc1(static_cast<uint8_t*>(pDst) + 8, pPixels, flags, total_orderings_to_try); } void encode_bc3(uint32_t level, void* pDst, const uint8_t* pPixels) { assert(g_initialized); encode_bc4(pDst, pPixels + 3, 4); encode_bc1(level, static_cast<uint8_t*>(pDst) + 8, pPixels, false, false); } void encode_bc3_hq(uint32_t level, void* pDst, const uint8_t* pPixels, uint32_t alpha_search_rad, uint32_t alpha_modes) { assert(g_initialized); encode_bc4_hq(pDst, pPixels + 3, 4, alpha_search_rad, alpha_modes); encode_bc1(level, static_cast<uint8_t*>(pDst) + 8, pPixels, false, false); } void encode_bc5(void* pDst, const uint8_t* pPixels, uint32_t chan0, uint32_t chan1, uint32_t stride) { assert(g_initialized); encode_bc4(pDst, pPixels + chan0, stride); encode_bc4(static_cast<uint8_t*>(pDst) + 8, pPixels + chan1, stride); } void encode_bc5_hq(void* pDst, const uint8_t* pPixels, uint32_t chan0, uint32_t chan1, uint32_t stride, uint32_t alpha_search_rad, uint32_t alpha_modes) { assert(g_initialized); encode_bc4_hq(pDst, pPixels + chan0, stride, alpha_search_rad, alpha_modes); encode_bc4_hq(static_cast<uint8_t*>(pDst) + 8, pPixels + chan1, stride, alpha_search_rad, alpha_modes); } bool unpack_bc1_block_colors(const void* pBlock_bits, color32* c, bc1_approx_mode mode) { const bc1_block* pBlock = static_cast<const bc1_block*>(pBlock_bits); const uint32_t l = pBlock->get_low_color(); const uint32_t h = pBlock->get_high_color(); const int cr0 = (l >> 11) & 31; const int cg0 = (l >> 5) & 63; const int cb0 = l & 31; const int r0 = (cr0 << 3) | (cr0 >> 2); const int g0 = (cg0 << 2) | (cg0 >> 4); const int b0 = (cb0 << 3) | (cb0 >> 2); const int cr1 = (h >> 11) & 31; const int cg1 = (h >> 5) & 63; const int cb1 = h & 31; const int r1 = (cr1 << 3) | (cr1 >> 2); const int g1 = (cg1 << 2) | (cg1 >> 4); const int b1 = (cb1 << 3) | (cb1 >> 2); bool used_punchthrough = false; if (l > h) { c[0].set_noclamp_rgba(r0, g0, b0, 255); c[1].set_noclamp_rgba(r1, g1, b1, 255); switch (mode) { case bc1_approx_mode::cBC1Ideal: c[2].set_noclamp_rgba((r0 * 2 + r1) / 3, (g0 * 2 + g1) / 3, (b0 * 2 + b1) / 3, 255); c[3].set_noclamp_rgba((r1 * 2 + r0) / 3, (g1 * 2 + g0) / 3, (b1 * 2 + b0) / 3, 255); break; case bc1_approx_mode::cBC1IdealRound4: c[2].set_noclamp_rgba((r0 * 2 + r1 + 1) / 3, (g0 * 2 + g1 + 1) / 3, (b0 * 2 + b1 + 1) / 3, 255); c[3].set_noclamp_rgba((r1 * 2 + r0 + 1) / 3, (g1 * 2 + g0 + 1) / 3, (b1 * 2 + b0 + 1) / 3, 255); break; case bc1_approx_mode::cBC1NVidia: c[2].set_noclamp_rgba(interp_5_nv(cr0, cr1), interp_6_nv(g0, g1), interp_5_nv(cb0, cb1), 255); c[3].set_noclamp_rgba(interp_5_nv(cr1, cr0), interp_6_nv(g1, g0), interp_5_nv(cb1, cb0), 255); break; case bc1_approx_mode::cBC1AMD: c[2].set_noclamp_rgba(interp_5_6_amd(r0, r1), interp_5_6_amd(g0, g1), interp_5_6_amd(b0, b1), 255); c[3].set_noclamp_rgba(interp_5_6_amd(r1, r0), interp_5_6_amd(g1, g0), interp_5_6_amd(b1, b0), 255); break; } } else { c[0].set_noclamp_rgba(r0, g0, b0, 255); c[1].set_noclamp_rgba(r1, g1, b1, 255); switch (mode) { case bc1_approx_mode::cBC1Ideal: case bc1_approx_mode::cBC1IdealRound4: c[2].set_noclamp_rgba((r0 + r1) / 2, (g0 + g1) / 2, (b0 + b1) / 2, 255); break; case bc1_approx_mode::cBC1NVidia: c[2].set_noclamp_rgba(interp_half_5_nv(cr0, cr1), interp_half_6_nv(g0, g1), interp_half_5_nv(cb0, cb1), 255); break; case bc1_approx_mode::cBC1AMD: c[2].set_noclamp_rgba(interp_half_5_6_amd(r0, r1), interp_half_5_6_amd(g0, g1), interp_half_5_6_amd(b0, b1), 255); break; } c[3].set_noclamp_rgba(0, 0, 0, 0); used_punchthrough = true; } return used_punchthrough; } // Returns true if the block uses 3 color punchthrough alpha mode. bool unpack_bc1(const void* pBlock_bits, void* pPixels, bool set_alpha, bc1_approx_mode mode) { color32* pDst_pixels = static_cast<color32*>(pPixels); static_assert(sizeof(bc1_block) == 8, "sizeof(bc1_block) == 8"); static_assert(sizeof(bc4_block) == 8, "sizeof(bc4_block) == 8"); const bc1_block* pBlock = static_cast<const bc1_block*>(pBlock_bits); color32 c[4]; const bool used_punchthrough = unpack_bc1_block_colors(pBlock_bits, c, mode); if (set_alpha) { for (uint32_t y = 0; y < 4; y++, pDst_pixels += 4) { pDst_pixels[0] = c[pBlock->get_selector(0, y)]; pDst_pixels[1] = c[pBlock->get_selector(1, y)]; pDst_pixels[2] = c[pBlock->get_selector(2, y)]; pDst_pixels[3] = c[pBlock->get_selector(3, y)]; } } else { for (uint32_t y = 0; y < 4; y++, pDst_pixels += 4) { pDst_pixels[0].set_rgb(c[pBlock->get_selector(0, y)]); pDst_pixels[1].set_rgb(c[pBlock->get_selector(1, y)]); pDst_pixels[2].set_rgb(c[pBlock->get_selector(2, y)]); pDst_pixels[3].set_rgb(c[pBlock->get_selector(3, y)]); } } return used_punchthrough; } void unpack_bc4(const void* pBlock_bits, uint8_t* pPixels, uint32_t stride) { static_assert(sizeof(bc4_block) == 8, "sizeof(bc4_block) == 8"); const bc4_block* pBlock = static_cast<const bc4_block*>(pBlock_bits); uint8_t sel_values[8]; bc4_block::get_block_values(sel_values, pBlock->get_low_alpha(), pBlock->get_high_alpha()); const uint64_t selector_bits = pBlock->get_selector_bits(); for (uint32_t y = 0; y < 4; y++, pPixels += (stride * 4U)) { pPixels[0] = sel_values[pBlock->get_selector(0, y, selector_bits)]; pPixels[stride * 1] = sel_values[pBlock->get_selector(1, y, selector_bits)]; pPixels[stride * 2] = sel_values[pBlock->get_selector(2, y, selector_bits)]; pPixels[stride * 3] = sel_values[pBlock->get_selector(3, y, selector_bits)]; } } // Returns false if the block uses 3-color punchthrough alpha mode, which isn't supported on some GPU's for BC3. bool unpack_bc3(const void* pBlock_bits, void* pPixels, bc1_approx_mode mode) { color32* pDst_pixels = static_cast<color32*>(pPixels); bool success = true; if (unpack_bc1((const uint8_t*)pBlock_bits + sizeof(bc4_block), pDst_pixels, true, mode)) success = false; unpack_bc4(pBlock_bits, &pDst_pixels[0].a, sizeof(color32)); return success; } // writes RG void unpack_bc5(const void* pBlock_bits, void* pPixels, uint32_t chan0, uint32_t chan1, uint32_t stride) { unpack_bc4(pBlock_bits, (uint8_t*)pPixels + chan0, stride); unpack_bc4((const uint8_t*)pBlock_bits + sizeof(bc4_block), (uint8_t*)pPixels + chan1, stride); } } // namespace rgbcx