Update basis universal to version 1.16.3.

Enable basis universal uastc internal storage instead of etc1s for better quality.
This commit is contained in:
K. S. Ernest (iFire) Lee 2022-03-24 12:39:24 -07:00
parent d250f12243
commit 3529141b4b
44 changed files with 8806 additions and 13189 deletions

View file

@ -127,7 +127,7 @@ License: Expat
Files: ./thirdparty/basis_universal/
Comment: Basis Universal
Copyright: 2019, Binomial LLC.
Copyright: 2022, Binomial LLC.
License: Apache-2.0
Files: ./thirdparty/brotli/

View file

@ -13,16 +13,15 @@ thirdparty_obj = []
thirdparty_dir = "#thirdparty/basis_universal/"
# Sync list with upstream CMakeLists.txt
encoder_sources = [
"apg_bmp.c",
"basisu_astc_decomp.cpp",
"basisu_uastc_enc.cpp",
"basisu_backend.cpp",
"basisu_basis_file.cpp",
"basisu_bc7enc.cpp",
"basisu_opencl.cpp",
"basisu_comp.cpp",
"basisu_enc.cpp",
"basisu_etc.cpp",
"basisu_frontend.cpp",
"basisu_global_selector_palette_helpers.cpp",
"basisu_gpu_texture.cpp",
"basisu_kernels_sse.cpp",
"basisu_pvrtc1_4.cpp",
@ -31,7 +30,7 @@ encoder_sources = [
"basisu_ssim.cpp",
"basisu_uastc_enc.cpp",
"jpgd.cpp",
"lodepng.cpp",
"pvpngreader.cpp",
]
encoder_sources = [thirdparty_dir + "encoder/" + file for file in encoder_sources]
transcoder_sources = [thirdparty_dir + "transcoder/basisu_transcoder.cpp"]

View file

@ -49,8 +49,6 @@ enum BasisDecompressFormat {
//workaround for lack of ETC2 RG
#define USE_RG_AS_RGBA
basist::etc1_global_selector_codebook *sel_codebook = nullptr;
#ifdef TOOLS_ENABLED
static Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedChannels p_channels) {
Vector<uint8_t> budata;
@ -77,18 +75,14 @@ static Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::
memcpy(buimg.get_ptr(), r, vec.size());
}
//image->save_png("pepeche.png");
basisu::basis_compressor_params params;
params.m_uastc = true;
params.m_max_endpoint_clusters = 512;
params.m_max_selector_clusters = 512;
params.m_multithreading = true;
//params.m_no_hybrid_sel_cb = true; //fixme, default on this causes crashes //seems fixed?
params.m_pSel_codebook = sel_codebook;
//params.m_quality_level = 0;
//params.m_disable_hierarchical_endpoint_codebooks = true;
//params.m_no_selector_rdo = true;
params.m_auto_global_sel_pal = false;
basisu::job_pool jpool(OS::get_singleton()->get_processor_count());
params.m_pJob_pool = &jpool;
@ -225,7 +219,7 @@ static Ref<Image> basis_universal_unpacker(const Vector<uint8_t> &p_buffer) {
ptr += 4;
size -= 4;
basist::basisu_transcoder tr(nullptr);
basist::basisu_transcoder tr;
ERR_FAIL_COND_V(!tr.validate_header(ptr, size), image);
@ -267,7 +261,9 @@ static Ref<Image> basis_universal_unpacker(const Vector<uint8_t> &p_buffer) {
void register_basis_universal_types() {
#ifdef TOOLS_ENABLED
sel_codebook = new basist::etc1_global_selector_codebook(basist::g_global_selector_cb_size, basist::g_global_selector_cb);
using namespace basisu;
using namespace basist;
basisu_encoder_init();
Image::basis_universal_packer = basis_universal_packer;
#endif
Image::basis_universal_unpacker = basis_universal_unpacker;
@ -275,7 +271,6 @@ void register_basis_universal_types() {
void unregister_basis_universal_types() {
#ifdef TOOLS_ENABLED
delete sel_codebook;
Image::basis_universal_packer = nullptr;
#endif
Image::basis_universal_unpacker = nullptr;

View file

@ -20,7 +20,7 @@ Files extracted from upstream source:
## basis_universal
- Upstream: https://github.com/BinomialLLC/basis_universal
- Version: git (646a9f826131cb0b9e14b5e4740874808315f83a, 2021)
- Version: git (1531cfaf9ed5232248a0a45736686a849ca3befc, 2022)
- License: Apache 2.0
Files extracted from upstream source:

View file

@ -1,541 +0,0 @@
/*
BMP File Reader/Writer Implementation
Anton Gerdelan
Version: 3
Licence: see apg_bmp.h
C99
*/
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS 1
#endif
#include "apg_bmp.h"
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Maximum pixel dimensions of width or height of an image. Should accommodate max used in graphics APIs.
NOTE: 65536*65536 is the biggest number storable in 32 bits.
This needs to be multiplied by n_channels so actual memory indices are not uint32 but size_t to avoid overflow.
Note this will crash stb_image_write et al at maximum size which use 32bits, so reduce max size to accom. */
#define _BMP_MAX_DIMS 65536
#define _BMP_FILE_HDR_SZ 14
#define _BMP_MIN_DIB_HDR_SZ 40
#define _BMP_MIN_HDR_SZ ( _BMP_FILE_HDR_SZ + _BMP_MIN_DIB_HDR_SZ )
#define _BMP_MAX_IMAGE_FILE_SIZE (1024ULL*1024ULL*1024ULL)
#pragma pack( push, 1 ) // supported on GCC in addition to individual packing attribs
/* All BMP files, regardless of type, start with this file header */
typedef struct _bmp_file_header_t {
char file_type[2];
uint32_t file_sz;
uint16_t reserved1;
uint16_t reserved2;
uint32_t image_data_offset;
} _bmp_file_header_t;
/* Following the file header is the BMP type header. this is the most commonly used format */
typedef struct _bmp_dib_BITMAPINFOHEADER_t {
uint32_t this_header_sz;
int32_t w; // in older headers w & h these are shorts and may be unsigned
int32_t h; //
uint16_t n_planes; // must be 1
uint16_t bpp; // bits per pixel. 1,4,8,16,24,32.
uint32_t compression_method; // 16 and 32-bit images must have a value of 3 here
uint32_t image_uncompressed_sz; // not consistently used in the wild, so ignored here.
int32_t horiz_pixels_per_meter; // not used.
int32_t vert_pixels_per_meter; // not used.
uint32_t n_colours_in_palette; //
uint32_t n_important_colours; // not used.
/* NOTE(Anton) a DIB header may end here at 40-bytes. be careful using sizeof() */
/* if 'compression' value, above, is set to 3 ie the image is 16 or 32-bit, then these colour channel masks follow the headers.
these are big-endian order bit masks to assign bits of each pixel to different colours. bits used must be contiguous and not overlap. */
uint32_t bitmask_r;
uint32_t bitmask_g;
uint32_t bitmask_b;
} _bmp_dib_BITMAPINFOHEADER_t;
#pragma pack( pop )
typedef enum _bmp_compression_t {
BI_RGB = 0,
BI_RLE8 = 1,
BI_RLE4 = 2,
BI_BITFIELDS = 3,
BI_JPEG = 4,
BI_PNG = 5,
BI_ALPHABITFIELDS = 6,
BI_CMYK = 11,
BI_CMYKRLE8 = 12,
BI_CMYRLE4 = 13
} _bmp_compression_t;
/* convenience struct and file->memory function */
typedef struct _entire_file_t {
void* data;
size_t sz;
} _entire_file_t;
/*
RETURNS
- true on success. record->data is allocated memory and must be freed by the caller.
- false on any error. Any allocated memory is freed if false is returned */
static bool _read_entire_file( const char* filename, _entire_file_t* record ) {
FILE* fp = fopen( filename, "rb" );
if ( !fp ) { return false; }
fseek( fp, 0L, SEEK_END );
record->sz = (size_t)ftell( fp );
// Immediately bail on anything larger than _BMP_MAX_IMAGE_FILE_SIZE.
if (record->sz > _BMP_MAX_IMAGE_FILE_SIZE) {
fclose( fp );
return false;
}
record->data = malloc( record->sz );
if ( !record->data ) {
fclose( fp );
return false;
}
rewind( fp );
size_t nr = fread( record->data, record->sz, 1, fp );
fclose( fp );
if ( 1 != nr ) { return false; }
return true;
}
static bool _validate_file_hdr( _bmp_file_header_t* file_hdr_ptr, size_t file_sz ) {
if ( !file_hdr_ptr ) { return false; }
if ( file_hdr_ptr->file_type[0] != 'B' || file_hdr_ptr->file_type[1] != 'M' ) { return false; }
if ( file_hdr_ptr->image_data_offset > file_sz ) { return false; }
return true;
}
static bool _validate_dib_hdr( _bmp_dib_BITMAPINFOHEADER_t* dib_hdr_ptr, size_t file_sz ) {
if ( !dib_hdr_ptr ) { return false; }
if ( _BMP_FILE_HDR_SZ + dib_hdr_ptr->this_header_sz > file_sz ) { return false; }
if ( ( 32 == dib_hdr_ptr->bpp || 16 == dib_hdr_ptr->bpp ) && ( BI_BITFIELDS != dib_hdr_ptr->compression_method && BI_ALPHABITFIELDS != dib_hdr_ptr->compression_method ) ) {
return false;
}
if ( BI_RGB != dib_hdr_ptr->compression_method && BI_BITFIELDS != dib_hdr_ptr->compression_method && BI_ALPHABITFIELDS != dib_hdr_ptr->compression_method ) {
return false;
}
// NOTE(Anton) using abs() in the if-statement was blowing up on large negative numbers. switched to labs()
if ( 0 == dib_hdr_ptr->w || 0 == dib_hdr_ptr->h || labs( dib_hdr_ptr->w ) > _BMP_MAX_DIMS || labs( dib_hdr_ptr->h ) > _BMP_MAX_DIMS ) { return false; }
/* NOTE(Anton) if images reliably used n_colours_in_palette we could have done a palette/file size integrity check here.
because some always set 0 then we have to check every palette indexing as we read them */
return true;
}
/* NOTE(Anton) this could have ifdef branches on different compilers for the intrinsics versions for perf */
static uint32_t _bitscan( uint32_t dword ) {
for ( uint32_t i = 0; i < 32; i++ ) {
if ( 1 & dword ) { return i; }
dword = dword >> 1;
}
return (uint32_t)-1;
}
unsigned char* apg_bmp_read( const char* filename, int* w, int* h, unsigned int* n_chans ) {
if ( !filename || !w || !h || !n_chans ) { return NULL; }
// read in the whole file into memory first - much faster than parsing on-the-fly
_entire_file_t record;
if ( !_read_entire_file( filename, &record ) ) { return NULL; }
if ( record.sz < _BMP_MIN_HDR_SZ ) {
free( record.data );
return NULL;
}
// grab and validate the first, file, header
_bmp_file_header_t* file_hdr_ptr = (_bmp_file_header_t*)record.data;
if ( !_validate_file_hdr( file_hdr_ptr, record.sz ) ) {
free( record.data );
return NULL;
}
// grad and validate the second, DIB, header
_bmp_dib_BITMAPINFOHEADER_t* dib_hdr_ptr = (_bmp_dib_BITMAPINFOHEADER_t*)( (uint8_t*)record.data + _BMP_FILE_HDR_SZ );
if ( !_validate_dib_hdr( dib_hdr_ptr, record.sz ) ) {
free( record.data );
return NULL;
}
// bitmaps can have negative dims to indicate the image should be flipped
uint32_t width = *w = abs( dib_hdr_ptr->w );
uint32_t height = *h = abs( dib_hdr_ptr->h );
// TODO(Anton) flip image memory at the end if this is true. because doing it per row was making me write bugs.
// bool vertically_flip = dib_hdr_ptr->h > 0 ? false : true;
// channel count and palette are not well defined in the header so we make a good guess here
uint32_t n_dst_chans = 3, n_src_chans = 3;
bool has_palette = false;
switch ( dib_hdr_ptr->bpp ) {
case 32: n_dst_chans = n_src_chans = 4; break; // technically can be RGB but not supported
case 24: n_dst_chans = n_src_chans = 3; break; // technically can be RGBA but not supported
case 8: // seems to always use a BGR0 palette, even for greyscale
n_dst_chans = 3;
has_palette = true;
n_src_chans = 1;
break;
case 4: // always has a palette - needed for a MS-saved BMP
n_dst_chans = 3;
has_palette = true;
n_src_chans = 1;
break;
case 1: // 1-bpp means the palette has 3 colour channels with 2 colours i.e. monochrome but not always black & white
n_dst_chans = 3;
has_palette = true;
n_src_chans = 1;
break;
default: // this includes 2bpp and 16bpp
free( record.data );
return NULL;
} // endswitch
*n_chans = n_dst_chans;
// NOTE(Anton) some image formats are not allowed a palette - could check for a bad header spec here also
if ( dib_hdr_ptr->n_colours_in_palette > 0 ) { has_palette = true; }
#ifdef APG_BMP_DEBUG_OUTPUT
printf( "apg_bmp_debug: reading image\n|-filename `%s`\n|-dims %ux%u pixels\n|-bpp %u\n|-n_src_chans %u\n|-n_dst_chans %u\n", filename, *w, *h,
dib_hdr_ptr->bpp, n_src_chans, n_dst_chans );
#endif
uint32_t palette_offset = _BMP_FILE_HDR_SZ + dib_hdr_ptr->this_header_sz;
bool has_bitmasks = false;
if ( BI_BITFIELDS == dib_hdr_ptr->compression_method || BI_ALPHABITFIELDS == dib_hdr_ptr->compression_method ) {
has_bitmasks = true;
palette_offset += 12;
}
if ( palette_offset > record.sz ) {
free( record.data );
return NULL;
}
// work out if any padding how much to skip at end of each row
uint32_t unpadded_row_sz = width * n_src_chans;
// bit-encoded palette indices have different padding properties
if ( 4 == dib_hdr_ptr->bpp ) {
unpadded_row_sz = width % 2 > 0 ? width / 2 + 1 : width / 2; // find how many whole bytes required for this bit width
}
if ( 1 == dib_hdr_ptr->bpp ) {
unpadded_row_sz = width % 8 > 0 ? width / 8 + 1 : width / 8; // find how many whole bytes required for this bit width
}
uint32_t row_padding_sz = 0 == unpadded_row_sz % 4 ? 0 : 4 - ( unpadded_row_sz % 4 ); // NOTE(Anton) didn't expect operator precedence of - over %
// another file size integrity check: partially validate source image data size
// 'image_data_offset' is by row padded to 4 bytes and is either colour data or palette indices.
if ( file_hdr_ptr->image_data_offset + ( unpadded_row_sz + row_padding_sz ) * height > record.sz ) {
free( record.data );
return NULL;
}
// find which bit number each colour channel starts at, so we can separate colours out
uint32_t bitshift_rgba[4] = {0, 0, 0, 0}; // NOTE(Anton) noticed this was int and not uint32_t so changed it. 17 Mar 2020
uint32_t bitmask_a = 0;
if ( has_bitmasks ) {
bitmask_a = ~( dib_hdr_ptr->bitmask_r | dib_hdr_ptr->bitmask_g | dib_hdr_ptr->bitmask_b );
bitshift_rgba[0] = _bitscan( dib_hdr_ptr->bitmask_r );
bitshift_rgba[1] = _bitscan( dib_hdr_ptr->bitmask_g );
bitshift_rgba[2] = _bitscan( dib_hdr_ptr->bitmask_b );
bitshift_rgba[3] = _bitscan( bitmask_a );
}
// allocate memory for the output pixels block. cast to size_t in case width and height are both the max of 65536 and n_dst_chans > 1
unsigned char* dst_img_ptr = (unsigned char*)malloc( (size_t)width * (size_t)height * (size_t)n_dst_chans );
if ( !dst_img_ptr ) {
free( record.data );
return NULL;
}
uint8_t* palette_data_ptr = (uint8_t*)record.data + palette_offset;
uint8_t* src_img_ptr = (uint8_t*)record.data + file_hdr_ptr->image_data_offset;
size_t dst_stride_sz = width * n_dst_chans;
// == 32-bpp -> 32-bit RGBA. == 32-bit and 16-bit require bitmasks
if ( 32 == dib_hdr_ptr->bpp ) {
// check source image has enough data in it to read from
if ( (size_t)file_hdr_ptr->image_data_offset + (size_t)height * (size_t)width * (size_t)n_src_chans > record.sz ) {
free( record.data );
free( dst_img_ptr );
return NULL;
}
size_t src_byte_idx = 0;
for ( uint32_t r = 0; r < height; r++ ) {
size_t dst_pixels_idx = r * dst_stride_sz;
for ( uint32_t c = 0; c < width; c++ ) {
uint32_t pixel;
memcpy( &pixel, &src_img_ptr[src_byte_idx], 4 );
// NOTE(Anton) the below assumes 32-bits is always RGBA 1 byte per channel. 10,10,10 RGB exists though and isn't handled.
dst_img_ptr[dst_pixels_idx++] = ( uint8_t )( ( pixel & dib_hdr_ptr->bitmask_r ) >> bitshift_rgba[0] );
dst_img_ptr[dst_pixels_idx++] = ( uint8_t )( ( pixel & dib_hdr_ptr->bitmask_g ) >> bitshift_rgba[1] );
dst_img_ptr[dst_pixels_idx++] = ( uint8_t )( ( pixel & dib_hdr_ptr->bitmask_b ) >> bitshift_rgba[2] );
dst_img_ptr[dst_pixels_idx++] = ( uint8_t )( ( pixel & bitmask_a ) >> bitshift_rgba[3] );
src_byte_idx += 4;
}
src_byte_idx += row_padding_sz;
}
// == 8-bpp -> 24-bit RGB ==
} else if ( 8 == dib_hdr_ptr->bpp && has_palette ) {
// validate indices (body of image data) fits in file
if ( file_hdr_ptr->image_data_offset + height * width > record.sz ) {
free( record.data );
free( dst_img_ptr );
return NULL;
}
size_t src_byte_idx = 0;
for ( uint32_t r = 0; r < height; r++ ) {
size_t dst_pixels_idx = ( height - 1 - r ) * dst_stride_sz;
for ( uint32_t c = 0; c < width; c++ ) {
// "most palettes are 4 bytes in RGB0 order but 3 for..." - it was actually BRG0 in old images -- Anton
uint8_t index = src_img_ptr[src_byte_idx]; // 8-bit index value per pixel
if ( palette_offset + index * 4 + 2 >= record.sz ) {
free( record.data );
return dst_img_ptr;
}
dst_img_ptr[dst_pixels_idx++] = palette_data_ptr[index * 4 + 2];
dst_img_ptr[dst_pixels_idx++] = palette_data_ptr[index * 4 + 1];
dst_img_ptr[dst_pixels_idx++] = palette_data_ptr[index * 4 + 0];
src_byte_idx++;
}
src_byte_idx += row_padding_sz;
}
// == 4-bpp (16-colour) -> 24-bit RGB ==
} else if ( 4 == dib_hdr_ptr->bpp && has_palette ) {
size_t src_byte_idx = 0;
for ( uint32_t r = 0; r < height; r++ ) {
size_t dst_pixels_idx = ( height - 1 - r ) * dst_stride_sz;
for ( uint32_t c = 0; c < width; c++ ) {
if ( file_hdr_ptr->image_data_offset + src_byte_idx > record.sz ) {
free( record.data );
free( dst_img_ptr );
return NULL;
}
// handle 2 pixels at a time
uint8_t pixel_duo = src_img_ptr[src_byte_idx];
uint8_t a_index = ( 0xFF & pixel_duo ) >> 4;
uint8_t b_index = 0xF & pixel_duo;
if ( palette_offset + a_index * 4 + 2 >= record.sz ) { // invalid src image
free( record.data );
return dst_img_ptr;
}
if ( dst_pixels_idx + 3 > width * height * n_dst_chans ) { // done
free( record.data );
return dst_img_ptr;
}
dst_img_ptr[dst_pixels_idx++] = palette_data_ptr[a_index * 4 + 2];
dst_img_ptr[dst_pixels_idx++] = palette_data_ptr[a_index * 4 + 1];
dst_img_ptr[dst_pixels_idx++] = palette_data_ptr[a_index * 4 + 0];
if ( ++c >= width ) { // advance a column
c = 0;
r++;
if ( r >= height ) { // done. no need to get second pixel. eg a 1x1 pixel image.
free( record.data );
return dst_img_ptr;
}
dst_pixels_idx = ( height - 1 - r ) * dst_stride_sz;
}
if ( palette_offset + b_index * 4 + 2 >= record.sz ) { // invalid src image
free( record.data );
return dst_img_ptr;
}
if ( dst_pixels_idx + 3 > width * height * n_dst_chans ) { // done. probably redundant check since checking r >= height.
free( record.data );
return dst_img_ptr;
}
dst_img_ptr[dst_pixels_idx++] = palette_data_ptr[b_index * 4 + 2];
dst_img_ptr[dst_pixels_idx++] = palette_data_ptr[b_index * 4 + 1];
dst_img_ptr[dst_pixels_idx++] = palette_data_ptr[b_index * 4 + 0];
src_byte_idx++;
}
src_byte_idx += row_padding_sz;
}
// == 1-bpp -> 24-bit RGB ==
} else if ( 1 == dib_hdr_ptr->bpp && has_palette ) {
/* encoding method for monochrome is not well documented.
a 2x2 pixel image is stored as 4 1-bit palette indexes
the palette is stored as any 2 RGB0 colours (not necessarily B&W)
so for an image with indexes like so:
1 1
0 1
it is bit-encoded as follows, starting at MSB:
01000000 00000000 00000000 00000000 (first byte val 64)
11000000 00000000 00000000 00000000 (first byte val 192)
data is still split by row and each row padded to 4 byte multiples
*/
size_t src_byte_idx = 0;
for ( uint32_t r = 0; r < height; r++ ) {
uint8_t bit_idx = 0; // used in monochrome
size_t dst_pixels_idx = ( height - 1 - r ) * dst_stride_sz;
for ( uint32_t c = 0; c < width; c++ ) {
if ( 8 == bit_idx ) { // start reading from the next byte
src_byte_idx++;
bit_idx = 0;
}
if ( file_hdr_ptr->image_data_offset + src_byte_idx > record.sz ) {
free( record.data );
return dst_img_ptr;
}
uint8_t pixel_oct = src_img_ptr[src_byte_idx];
uint8_t bit = 128 >> bit_idx;
uint8_t masked = pixel_oct & bit;
uint8_t palette_idx = masked > 0 ? 1 : 0;
if ( palette_offset + palette_idx * 4 + 2 >= record.sz ) {
free( record.data );
return dst_img_ptr;
}
dst_img_ptr[dst_pixels_idx++] = palette_data_ptr[palette_idx * 4 + 2];
dst_img_ptr[dst_pixels_idx++] = palette_data_ptr[palette_idx * 4 + 1];
dst_img_ptr[dst_pixels_idx++] = palette_data_ptr[palette_idx * 4 + 0];
bit_idx++;
}
src_byte_idx += ( row_padding_sz + 1 ); // 1bpp is special here
}
// == 24-bpp -> 24-bit RGB == (but also should handle some other n_chans cases)
} else {
// NOTE(Anton) this only supports 1 byte per channel
if ( file_hdr_ptr->image_data_offset + height * width * n_dst_chans > record.sz ) {
free( record.data );
free( dst_img_ptr );
return NULL;
}
size_t src_byte_idx = 0;
for ( uint32_t r = 0; r < height; r++ ) {
size_t dst_pixels_idx = ( height - 1 - r ) * dst_stride_sz;
for ( uint32_t c = 0; c < width; c++ ) {
// re-orders from BGR to RGB
if ( n_dst_chans > 3 ) { dst_img_ptr[dst_pixels_idx++] = src_img_ptr[src_byte_idx + 3]; }
if ( n_dst_chans > 2 ) { dst_img_ptr[dst_pixels_idx++] = src_img_ptr[src_byte_idx + 2]; }
if ( n_dst_chans > 1 ) { dst_img_ptr[dst_pixels_idx++] = src_img_ptr[src_byte_idx + 1]; }
dst_img_ptr[dst_pixels_idx++] = src_img_ptr[src_byte_idx];
src_byte_idx += n_src_chans;
}
src_byte_idx += row_padding_sz;
}
} // endif bpp
free( record.data );
return dst_img_ptr;
}
void apg_bmp_free( unsigned char* pixels_ptr ) {
if ( !pixels_ptr ) { return; }
free( pixels_ptr );
}
unsigned int apg_bmp_write( const char* filename, unsigned char* pixels_ptr, int w, int h, unsigned int n_chans ) {
if ( !filename || !pixels_ptr ) { return 0; }
if ( 0 == w || 0 == h ) { return 0; }
if ( labs( w ) > _BMP_MAX_DIMS || labs( h ) > _BMP_MAX_DIMS ) { return 0; }
if ( n_chans != 3 && n_chans != 4 ) { return 0; }
uint32_t height = (uint32_t)labs( h );
uint32_t width = (uint32_t)labs( w );
// work out if any padding how much to skip at end of each row
const size_t unpadded_row_sz = width * n_chans;
const size_t row_padding_sz = 0 == unpadded_row_sz % 4 ? 0 : 4 - unpadded_row_sz % 4;
const size_t row_sz = unpadded_row_sz + row_padding_sz;
const size_t dst_pixels_padded_sz = row_sz * height;
const size_t dib_hdr_sz = sizeof( _bmp_dib_BITMAPINFOHEADER_t );
_bmp_file_header_t file_hdr;
{
file_hdr.file_type[0] = 'B';
file_hdr.file_type[1] = 'M';
file_hdr.file_sz = _BMP_FILE_HDR_SZ + (uint32_t)dib_hdr_sz + (uint32_t)dst_pixels_padded_sz;
file_hdr.reserved1 = 0;
file_hdr.reserved2 = 0;
file_hdr.image_data_offset = _BMP_FILE_HDR_SZ + (uint32_t)dib_hdr_sz;
}
_bmp_dib_BITMAPINFOHEADER_t dib_hdr;
{
dib_hdr.this_header_sz = _BMP_MIN_DIB_HDR_SZ; // NOTE: must be 40 and not include the bitmask memory in size here
dib_hdr.w = w;
dib_hdr.h = h;
dib_hdr.n_planes = 1;
dib_hdr.bpp = 3 == n_chans ? 24 : 32;
dib_hdr.compression_method = 3 == n_chans ? BI_RGB : BI_BITFIELDS;
dib_hdr.image_uncompressed_sz = 0;
dib_hdr.horiz_pixels_per_meter = 0;
dib_hdr.vert_pixels_per_meter = 0;
dib_hdr.n_colours_in_palette = 0;
dib_hdr.n_important_colours = 0;
// big-endian masks. only used in BI_BITFIELDS and BI_ALPHABITFIELDS ( 16 and 32-bit images )
// important note: GIMP stores BMP data in this array order for 32-bit: [A][B][G][R]
dib_hdr.bitmask_r = 0xFF000000;
dib_hdr.bitmask_g = 0x00FF0000;
dib_hdr.bitmask_b = 0x0000FF00;
}
uint8_t* dst_pixels_ptr = (uint8_t*)malloc( dst_pixels_padded_sz );
if ( !dst_pixels_ptr ) { return 0; }
{
size_t dst_byte_idx = 0;
uint8_t padding[4] = {0, 0, 0, 0};
uint8_t rgba[4] = {0, 0, 0, 0};
uint8_t bgra[4] = {0, 0, 0, 0};
for ( uint32_t row = 0; row < height; row++ ) {
size_t src_byte_idx = ( height - 1 - row ) * n_chans * width;
for ( uint32_t col = 0; col < width; col++ ) {
for ( uint32_t chan = 0; chan < n_chans; chan++ ) { rgba[chan] = pixels_ptr[src_byte_idx++]; }
if ( 3 == n_chans ) {
bgra[0] = rgba[2];
bgra[1] = rgba[1];
bgra[2] = rgba[0];
} else {
/* NOTE(Anton) RGBA with alpha channel would be better supported with an extended DIB header */
bgra[0] = rgba[3];
bgra[1] = rgba[2];
bgra[2] = rgba[1];
bgra[3] = rgba[0]; // alpha
}
memcpy( &dst_pixels_ptr[dst_byte_idx], bgra, n_chans );
dst_byte_idx += (size_t)n_chans;
} // endfor col
if ( row_padding_sz > 0 ) {
memcpy( &dst_pixels_ptr[dst_byte_idx], padding, row_padding_sz );
dst_byte_idx += row_padding_sz;
}
} // endfor row
}
{
FILE* fp = fopen( filename, "wb" );
if ( !fp ) {
free( dst_pixels_ptr );
return 0;
}
if ( 1 != fwrite( &file_hdr, _BMP_FILE_HDR_SZ, 1, fp ) ) {
free( dst_pixels_ptr );
fclose( fp );
return 0;
}
if ( 1 != fwrite( &dib_hdr, dib_hdr_sz, 1, fp ) ) {
free( dst_pixels_ptr );
fclose( fp );
return 0;
}
if ( 1 != fwrite( dst_pixels_ptr, dst_pixels_padded_sz, 1, fp ) ) {
free( dst_pixels_ptr );
fclose( fp );
return 0;
}
fclose( fp );
}
free( dst_pixels_ptr );
return 1;
}

View file

@ -1,123 +0,0 @@
/*
BMP File Reader/Writer Implementation
Anton Gerdelan
Version: 3.1 18 March 2020.
Licence: see bottom of file.
C89 ( Implementation is C99 )
Contributors:
- Anton Gerdelan - Initial code.
- Saija Sorsa - Fuzz testing.
Instructions:
- Just drop this header, and the matching .c file into your project.
- To get debug printouts during parsing define APG_BMP_DEBUG_OUTPUT.
Advantages:
- The implementation is fast, simple, and supports more formats than most BMP reader libraries.
- The reader function is fuzzed with AFL https://lcamtuf.coredump.cx/afl/.
- The reader is robust to large files and malformed files, and will return any valid partial data in an image.
- Reader supports 32bpp (with alpha channel), 24bpp, 8bpp, 4bpp, and 1bpp monochrome BMP images.
- Reader handles indexed BMP images using a colour palette.
- Writer supports 32bpp RGBA and 24bpp uncompressed RGB images.
Current Limitations:
- 16-bit images not supported (don't have any samples to test on).
- No support for interleaved channel bit layouts eg RGB101010 RGB555 RGB565.
- No support for compressed BMP images, although in practice these are not used.
- Output images with alpha channel are written in BITMAPINFOHEADER format.
For better alpha support in other apps the 124-bit v5 header could be used instead,
at the cost of some backward compatibility and bloat.
To Do:
- FUZZING
- create a unique fuzz test set for (8,4,1 BPP).
- (maybe) FEATURE Flipping the image based on negative width and height in header, and/or function arguments.
- (maybe) PERF ifdef intrinsics/asm for bitscan. Platform-specific code so won't include unless necessary.
- (maybe) FEATURE Add parameter for padding output memory to eg 4-byte alignment or n channels.
- (maybe) FEATURE Improved apps support in alpha channel writing (using v5 header).
*/
#ifndef APG_BMP_H_
#define APG_BMP_H_
#ifdef __cplusplus
extern "C" {
#endif /* CPP */
/* Reads a bitmap from a file, allocates memory for the raw image data, and returns it.
PARAMS
* w,h, - Retrieves the width and height of the BMP in pixels.
* n_chans - Retrieves the number of channels in the BMP.
RETURNS
* Tightly-packed pixel memory in RGBA order. The caller must call free() on the memory.
* NULL on any error. Any allocated memory is freed before returning NULL. */
unsigned char* apg_bmp_read( const char* filename, int* w, int* h, unsigned int* n_chans );
/* Calls free() on memory created by apg_bmp_read */
void apg_bmp_free( unsigned char* pixels_ptr );
/* Writes a bitmap to a file.
PARAMS
* filename - e.g."my_bitmap.bmp". Must not be NULL.
* pixels_ptr - Pointer to tightly-packed pixel memory in RGBA order. Must not be NULL. There must be abs(w)*abs(h)*n_chans bytes in the memory pointed to.
* w,h, - Width and height of the image in pixels.
* n_chans - The number of channels in the BMP. 3 or 4 supported for writing, which means RGB or RGBA memory, respectively.
RETURNS
* Zero on any error, non zero on success. */
unsigned int apg_bmp_write( const char* filename, unsigned char* pixels_ptr, int w, int h, unsigned int n_chans );
#ifdef __cplusplus
}
#endif /* CPP */
#endif /*_APG_BMP_H_ */
/*
-------------------------------------------------------------------------------------
This software is available under two licences - you may use it under either licence.
-------------------------------------------------------------------------------------
FIRST LICENCE OPTION
> Apache License
> Version 2.0, January 2004
> http://www.apache.org/licenses/
> Copyright 2019 Anton Gerdelan.
> Licensed under the Apache License, Version 2.0 (the "License");
> you may not use this file except in compliance with the License.
> You may obtain a copy of the License at
> http://www.apache.org/licenses/LICENSE-2.0
> Unless required by applicable law or agreed to in writing, software
> distributed under the License is distributed on an "AS IS" BASIS,
> WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> See the License for the specific language governing permissions and
> limitations under the License.
-------------------------------------------------------------------------------------
SECOND LICENCE OPTION
> This is free and unencumbered software released into the public domain.
>
> Anyone is free to copy, modify, publish, use, compile, sell, or
> distribute this software, either in source code form or as a compiled
> binary, for any purpose, commercial or non-commercial, and by any
> means.
>
> In jurisdictions that recognize copyright laws, the author or authors
> of this software dedicate any and all copyright interest in the
> software to the public domain. We make this dedication for the benefit
> of the public at large and to the detriment of our heirs and
> successors. We intend this dedication to be an overt act of
> relinquishment in perpetuity of all present and future rights to this
> software under copyright law.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
> IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
> OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> OTHER DEALINGS IN THE SOFTWARE.
>
> For more information, please refer to <http://unlicense.org>
-------------------------------------------------------------------------------------
*/

File diff suppressed because it is too large Load diff

View file

@ -1,43 +0,0 @@
#ifndef _TCUASTCUTIL_HPP
#define _TCUASTCUTIL_HPP
/*-------------------------------------------------------------------------
* drawElements Quality Program Tester Core
* ----------------------------------------
*
* Copyright 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*//*!
* \file
* \brief ASTC Utilities.
*//*--------------------------------------------------------------------*/
#include "../transcoder/basisu.h" // to pick up the iterator debug level madness
#include <vector>
#include <stdint.h>
namespace basisu_astc
{
namespace astc
{
// Unpacks a single ASTC block to pDst
// If isSRGB is true, the spec requires the decoder to scale the LDR 8-bit endpoints to 16-bit before interpolation slightly differently,
// which will lead to different outputs. So be sure to set it correctly (ideally it should match whatever the encoder did).
bool decompress(uint8_t* pDst, const uint8_t* data, bool isSRGB, int blockWidth, int blockHeight);
} // astc
} // basisu
#endif

View file

@ -49,22 +49,17 @@ namespace basisu
m_output.clear();
}
void basisu_backend::init(basisu_frontend* pFront_end, basisu_backend_params& params, const basisu_backend_slice_desc_vec& slice_descs, const basist::etc1_global_selector_codebook* pGlobal_sel_codebook)
void basisu_backend::init(basisu_frontend* pFront_end, basisu_backend_params& params, const basisu_backend_slice_desc_vec& slice_descs)
{
m_pFront_end = pFront_end;
m_params = params;
m_slices = slice_descs;
m_pGlobal_sel_codebook = pGlobal_sel_codebook;
debug_printf("basisu_backend::Init: Slices: %u, ETC1S: %u, EndpointRDOQualityThresh: %f, SelectorRDOQualityThresh: %f, UseGlobalSelCodebook: %u, GlobalSelCodebookPalBits: %u, GlobalSelCodebookModBits: %u, Use hybrid selector codebooks: %u\n",
debug_printf("basisu_backend::Init: Slices: %u, ETC1S: %u, EndpointRDOQualityThresh: %f, SelectorRDOQualityThresh: %f\n",
m_slices.size(),
params.m_etc1s,
params.m_endpoint_rdo_quality_thresh,
params.m_selector_rdo_quality_thresh,
params.m_use_global_sel_codebook,
params.m_global_sel_codebook_pal_bits,
params.m_global_sel_codebook_mod_bits,
params.m_use_hybrid_sel_codebooks);
params.m_selector_rdo_quality_thresh);
debug_printf("Frontend endpoints: %u selectors: %u\n", m_pFront_end->get_total_endpoint_clusters(), m_pFront_end->get_total_selector_clusters());
@ -106,63 +101,17 @@ namespace basisu
m_selector_palette.resize(r.get_total_selector_clusters());
if (m_params.m_use_global_sel_codebook)
for (uint32_t i = 0; i < r.get_total_selector_clusters(); i++)
{
m_global_selector_palette_desc.resize(r.get_total_selector_clusters());
etc1_selector_palette_entry& s = m_selector_palette[i];
for (int i = 0; i < static_cast<int>(r.get_total_selector_clusters()); i++)
const etc_block& selector_bits = r.get_selector_cluster_selector_bits(i);
for (uint32_t y = 0; y < 4; y++)
{
basist::etc1_selector_palette_entry& selector_pal_entry = m_selector_palette[i];
etc1_global_selector_cb_entry_desc& pal_entry_desc = m_global_selector_palette_desc[i];
pal_entry_desc.m_pal_index = r.get_selector_cluster_global_selector_entry_ids()[i].m_palette_index;
pal_entry_desc.m_mod_index = r.get_selector_cluster_global_selector_entry_ids()[i].m_modifier.get_index();
pal_entry_desc.m_was_used = true;
if (m_params.m_use_hybrid_sel_codebooks)
pal_entry_desc.m_was_used = r.get_selector_cluster_uses_global_cb_vec()[i];
if (pal_entry_desc.m_was_used)
for (uint32_t x = 0; x < 4; x++)
{
const etc_block& selector_bits = r.get_selector_cluster_selector_bits(i);
(void)selector_bits;
basist::etc1_selector_palette_entry global_pal_entry(m_pGlobal_sel_codebook->get_entry(r.get_selector_cluster_global_selector_entry_ids()[i]));
for (uint32_t y = 0; y < 4; y++)
{
for (uint32_t x = 0; x < 4; x++)
{
selector_pal_entry(x, y) = global_pal_entry(x, y);
assert(selector_bits.get_selector(x, y) == global_pal_entry(x, y));
}
}
}
else
{
const etc_block& selector_bits = r.get_selector_cluster_selector_bits(i);
for (uint32_t y = 0; y < 4; y++)
for (uint32_t x = 0; x < 4; x++)
selector_pal_entry[y * 4 + x] = static_cast<uint8_t>(selector_bits.get_selector(x, y));
}
}
}
else
{
for (uint32_t i = 0; i < r.get_total_selector_clusters(); i++)
{
basist::etc1_selector_palette_entry& s = m_selector_palette[i];
const etc_block& selector_bits = r.get_selector_cluster_selector_bits(i);
for (uint32_t y = 0; y < 4; y++)
{
for (uint32_t x = 0; x < 4; x++)
{
s[y * 4 + x] = static_cast<uint8_t>(selector_bits.get_selector(x, y));
}
s[y * 4 + x] = static_cast<uint8_t>(selector_bits.get_selector(x, y));
}
}
}
@ -388,6 +337,8 @@ namespace basisu
if (!is_video)
return;
debug_printf("basisu_backend::check_for_valid_cr_blocks\n");
uint32_t total_crs = 0;
uint32_t total_invalid_crs = 0;
@ -454,6 +405,11 @@ namespace basisu
void basisu_backend::create_encoder_blocks()
{
debug_printf("basisu_backend::create_encoder_blocks\n");
interval_timer tm;
tm.start();
basisu_frontend& r = *m_pFront_end;
const bool is_video = r.get_params().m_tex_type == basist::cBASISTexTypeVideoFrames;
@ -565,6 +521,7 @@ namespace basisu
{
if ((is_video) && (endpoint_pred == basist::CR_ENDPOINT_PRED_INDEX))
continue;
int pred_block_x = block_x + g_endpoint_preds[endpoint_pred].m_dx;
if ((pred_block_x < 0) || (pred_block_x >= (int)num_blocks_x))
continue;
@ -586,11 +543,23 @@ namespace basisu
unpack_etc1(trial_etc_block, trial_colors);
uint64_t trial_err = 0;
for (uint32_t p = 0; p < 16; p++)
if (r.get_params().m_perceptual)
{
trial_err += color_distance(r.get_params().m_perceptual, src_pixels.get_ptr()[p], trial_colors[p], false);
if (trial_err > thresh_err)
break;
for (uint32_t p = 0; p < 16; p++)
{
trial_err += color_distance(true, src_pixels.get_ptr()[p], trial_colors[p], false);
if (trial_err > thresh_err)
break;
}
}
else
{
for (uint32_t p = 0; p < 16; p++)
{
trial_err += color_distance(false, src_pixels.get_ptr()[p], trial_colors[p], false);
if (trial_err > thresh_err)
break;
}
}
if (trial_err <= thresh_err)
@ -643,6 +612,8 @@ namespace basisu
sort_selector_codebook();
check_for_valid_cr_blocks();
debug_printf("Elapsed time: %3.3f secs\n", tm.get_elapsed_secs());
}
void basisu_backend::compute_slice_crcs()
@ -670,7 +641,9 @@ namespace basisu
etc_block& output_block = *(etc_block*)gi.get_block_ptr(block_x, block_y);
output_block.set_diff_bit(true);
output_block.set_flip_bit(true);
// Setting the flip bit to false to be compatible with the Khronos KDFS.
//output_block.set_flip_bit(true);
output_block.set_flip_bit(false);
const uint32_t endpoint_index = m.m_endpoint_index;
@ -679,7 +652,7 @@ namespace basisu
const uint32_t selector_idx = m.m_selector_index;
const basist::etc1_selector_palette_entry& selectors = m_selector_palette[selector_idx];
const etc1_selector_palette_entry& selectors = m_selector_palette[selector_idx];
for (uint32_t sy = 0; sy < 4; sy++)
for (uint32_t sx = 0; sx < 4; sx++)
output_block.set_selector(sx, sy, selectors(sx, sy));
@ -707,6 +680,9 @@ namespace basisu
} // slice_index
}
//uint32_t g_color_delta_hist[255 * 3 + 1];
//uint32_t g_color_delta_bad_hist[255 * 3 + 1];
// TODO: Split this into multiple methods.
bool basisu_backend::encode_image()
{
@ -737,6 +713,12 @@ namespace basisu
uint_vec block_endpoint_indices, block_selector_indices;
interval_timer tm;
tm.start();
const int COLOR_DELTA_THRESH = 8;
const int SEL_DIFF_THRESHOLD = 11;
for (uint32_t slice_index = 0; slice_index < m_slices.size(); slice_index++)
{
//const int prev_frame_slice_index = is_video ? find_video_frame(slice_index, -1) : -1;
@ -782,7 +764,7 @@ namespace basisu
} // block_x
} // block_y
for (uint32_t block_y = 0; block_y < num_blocks_y; block_y++)
{
for (uint32_t block_x = 0; block_x < num_blocks_x; block_x++)
@ -857,68 +839,170 @@ namespace basisu
etc_block etc_blk(r.get_output_block(block_index));
const uint64_t cur_err = etc_blk.evaluate_etc1_error(src_pixels.get_ptr(), r.get_params().m_perceptual);
const uint32_t cur_inten5 = etc_blk.get_inten_table(0);
const etc1_endpoint_palette_entry& cur_endpoints = m_endpoint_palette[m.m_endpoint_index];
if (cur_err)
{
const float endpoint_remap_thresh = maximum(1.0f, m_params.m_endpoint_rdo_quality_thresh);
const uint64_t thresh_err = (uint64_t)(cur_err * endpoint_remap_thresh);
uint64_t best_trial_err = UINT64_MAX;
int best_trial_idx = 0;
//const int MAX_ENDPOINT_SEARCH_DIST = (m_params.m_compression_level >= 2) ? 64 : 32;
const int MAX_ENDPOINT_SEARCH_DIST = (m_params.m_compression_level >= 2) ? 64 : 16;
etc_block trial_etc_blk(etc_blk);
const int MAX_ENDPOINT_SEARCH_DIST = 32;
const int search_dist = minimum<int>(iabs(endpoint_delta) - 1, MAX_ENDPOINT_SEARCH_DIST);
for (int d = -search_dist; d < search_dist; d++)
if (!g_cpu_supports_sse41)
{
int trial_idx = prev_endpoint_index + d;
if (trial_idx < 0)
trial_idx += (int)r.get_total_endpoint_clusters();
else if (trial_idx >= (int)r.get_total_endpoint_clusters())
trial_idx -= (int)r.get_total_endpoint_clusters();
const uint64_t initial_best_trial_err = UINT64_MAX;
uint64_t best_trial_err = initial_best_trial_err;
int best_trial_idx = 0;
if (trial_idx == new_endpoint_index)
continue;
// Skip it if this new endpoint palette entry is actually never used.
if (!m_new_endpoint_was_used[trial_idx])
continue;
const etc1_endpoint_palette_entry& p = m_endpoint_palette[m_endpoint_remap_table_new_to_old[trial_idx]];
trial_etc_blk.set_block_color5_etc1s(p.m_color5);
trial_etc_blk.set_inten_tables_etc1s(p.m_inten5);
uint64_t trial_err = trial_etc_blk.evaluate_etc1_error(src_pixels.get_ptr(), r.get_params().m_perceptual);
if (trial_err <= thresh_err)
etc_block trial_etc_blk(etc_blk);
const int search_dist = minimum<int>(iabs(endpoint_delta) - 1, MAX_ENDPOINT_SEARCH_DIST);
for (int d = -search_dist; d < search_dist; d++)
{
if (trial_err < best_trial_err)
int trial_idx = prev_endpoint_index + d;
if (trial_idx < 0)
trial_idx += (int)r.get_total_endpoint_clusters();
else if (trial_idx >= (int)r.get_total_endpoint_clusters())
trial_idx -= (int)r.get_total_endpoint_clusters();
if (trial_idx == new_endpoint_index)
continue;
// Skip it if this new endpoint palette entry is actually never used.
if (!m_new_endpoint_was_used[trial_idx])
continue;
const etc1_endpoint_palette_entry& p = m_endpoint_palette[m_endpoint_remap_table_new_to_old[trial_idx]];
if (m_params.m_compression_level <= 1)
{
if (p.m_inten5 > cur_inten5)
continue;
int delta_r = iabs(cur_endpoints.m_color5.r - p.m_color5.r);
int delta_g = iabs(cur_endpoints.m_color5.g - p.m_color5.g);
int delta_b = iabs(cur_endpoints.m_color5.b - p.m_color5.b);
int color_delta = delta_r + delta_g + delta_b;
if (color_delta > COLOR_DELTA_THRESH)
continue;
}
trial_etc_blk.set_block_color5_etc1s(p.m_color5);
trial_etc_blk.set_inten_tables_etc1s(p.m_inten5);
uint64_t trial_err = trial_etc_blk.evaluate_etc1_error(src_pixels.get_ptr(), r.get_params().m_perceptual);
if ((trial_err < best_trial_err) && (trial_err <= thresh_err))
{
best_trial_err = trial_err;
best_trial_idx = trial_idx;
}
}
}
if (best_trial_err != UINT64_MAX)
if (best_trial_err != initial_best_trial_err)
{
m.m_endpoint_index = m_endpoint_remap_table_new_to_old[best_trial_idx];
new_endpoint_index = best_trial_idx;
endpoint_delta = new_endpoint_index - prev_endpoint_index;
total_endpoint_indices_remapped++;
}
}
else
{
m.m_endpoint_index = m_endpoint_remap_table_new_to_old[best_trial_idx];
#if BASISU_SUPPORT_SSE
uint8_t block_selectors[16];
for (uint32_t i = 0; i < 16; i++)
block_selectors[i] = (uint8_t)etc_blk.get_selector(i & 3, i >> 2);
new_endpoint_index = best_trial_idx;
const int64_t initial_best_trial_err = INT64_MAX;
int64_t best_trial_err = initial_best_trial_err;
int best_trial_idx = 0;
const int search_dist = minimum<int>(iabs(endpoint_delta) - 1, MAX_ENDPOINT_SEARCH_DIST);
for (int d = -search_dist; d < search_dist; d++)
{
int trial_idx = prev_endpoint_index + d;
if (trial_idx < 0)
trial_idx += (int)r.get_total_endpoint_clusters();
else if (trial_idx >= (int)r.get_total_endpoint_clusters())
trial_idx -= (int)r.get_total_endpoint_clusters();
endpoint_delta = new_endpoint_index - prev_endpoint_index;
if (trial_idx == new_endpoint_index)
continue;
total_endpoint_indices_remapped++;
}
}
}
// Skip it if this new endpoint palette entry is actually never used.
if (!m_new_endpoint_was_used[trial_idx])
continue;
const etc1_endpoint_palette_entry& p = m_endpoint_palette[m_endpoint_remap_table_new_to_old[trial_idx]];
if (m_params.m_compression_level <= 1)
{
if (p.m_inten5 > cur_inten5)
continue;
int delta_r = iabs(cur_endpoints.m_color5.r - p.m_color5.r);
int delta_g = iabs(cur_endpoints.m_color5.g - p.m_color5.g);
int delta_b = iabs(cur_endpoints.m_color5.b - p.m_color5.b);
int color_delta = delta_r + delta_g + delta_b;
if (color_delta > COLOR_DELTA_THRESH)
continue;
}
color_rgba block_colors[4];
etc_block::get_block_colors_etc1s(block_colors, p.m_color5, p.m_inten5);
int64_t trial_err;
if (r.get_params().m_perceptual)
{
perceptual_distance_rgb_4_N_sse41(&trial_err, block_selectors, block_colors, src_pixels.get_ptr(), 16, best_trial_err);
}
else
{
linear_distance_rgb_4_N_sse41(&trial_err, block_selectors, block_colors, src_pixels.get_ptr(), 16, best_trial_err);
}
//if (trial_err > thresh_err)
// g_color_delta_bad_hist[color_delta]++;
if ((trial_err < best_trial_err) && (trial_err <= (int64_t)thresh_err))
{
best_trial_err = trial_err;
best_trial_idx = trial_idx;
}
}
if (best_trial_err != initial_best_trial_err)
{
m.m_endpoint_index = m_endpoint_remap_table_new_to_old[best_trial_idx];
new_endpoint_index = best_trial_idx;
endpoint_delta = new_endpoint_index - prev_endpoint_index;
total_endpoint_indices_remapped++;
}
#endif // BASISU_SUPPORT_SSE
} // if (!g_cpu_supports_sse41)
} // if (cur_err)
} // if ((m_params.m_endpoint_rdo_quality_thresh > 1.0f) && (iabs(endpoint_delta) > 1) && (!block_endpoints_are_referenced(block_x, block_y)))
if (endpoint_delta < 0)
endpoint_delta += (int)r.get_total_endpoint_clusters();
delta_endpoint_histogram.inc(endpoint_delta);
}
} // if (m.m_endpoint_predictor == basist::NO_ENDPOINT_PRED_INDEX)
block_endpoint_indices.push_back(m_endpoint_remap_table_new_to_old[new_endpoint_index]);
@ -927,10 +1011,13 @@ namespace basisu
if ((!is_video) || (m.m_endpoint_predictor != basist::CR_ENDPOINT_PRED_INDEX))
{
int new_selector_index = m_selector_remap_table_old_to_new[m.m_selector_index];
const float selector_remap_thresh = maximum(1.0f, m_params.m_selector_rdo_quality_thresh); //2.5f;
int selector_history_buf_index = -1;
if (m.m_is_cr_target)
// At low comp levels this hurts compression a tiny amount, but is significantly faster so it's a good tradeoff.
if ((m.m_is_cr_target) || (m_params.m_compression_level <= 1))
{
for (uint32_t j = 0; j < selector_history_buf.size(); j++)
{
@ -944,89 +1031,99 @@ namespace basisu
}
}
}
else
// If the block is a CR target we can't override its selectors.
if ((!m.m_is_cr_target) && (selector_history_buf_index == -1))
{
const pixel_block& src_pixels = r.get_source_pixel_block(block_index);
const etc_block& etc_blk = r.get_output_block(block_index);
etc_block etc_blk = r.get_output_block(block_index);
color_rgba etc_blk_unpacked[16];
unpack_etc1(etc_blk, etc_blk_unpacked);
// This is new code - the initial release just used the endpoints from the frontend, which isn't correct/accurate.
const etc1_endpoint_palette_entry& q = m_endpoint_palette[m_endpoint_remap_table_new_to_old[new_endpoint_index]];
etc_blk.set_block_color5_etc1s(q.m_color5);
etc_blk.set_inten_tables_etc1s(q.m_inten5);
color_rgba block_colors[4];
etc_blk.get_block_colors(block_colors, 0);
const uint8_t* pCur_selectors = &m_selector_palette[m.m_selector_index][0];
uint64_t cur_err = 0;
if (r.get_params().m_perceptual)
{
for (uint32_t p = 0; p < 16; p++)
cur_err += color_distance(true, src_pixels.get_ptr()[p], etc_blk_unpacked[p], false);
cur_err += color_distance(true, src_pixels.get_ptr()[p], block_colors[pCur_selectors[p]], false);
}
else
{
for (uint32_t p = 0; p < 16; p++)
cur_err += color_distance(false, src_pixels.get_ptr()[p], etc_blk_unpacked[p], false);
cur_err += color_distance(false, src_pixels.get_ptr()[p], block_colors[pCur_selectors[p]], false);
}
const uint64_t limit_err = (uint64_t)ceilf(cur_err * selector_remap_thresh);
// Even if cur_err==limit_err, we still want to scan the history buffer because there may be equivalent entries that are cheaper to code.
uint64_t best_trial_err = UINT64_MAX;
int best_trial_idx = 0;
uint32_t best_trial_history_buf_idx = 0;
const float selector_remap_thresh = maximum(1.0f, m_params.m_selector_rdo_quality_thresh); //2.5f;
const bool use_strict_search = (m_params.m_compression_level == 0) && (selector_remap_thresh == 1.0f);
const uint64_t limit_err = (uint64_t)ceilf(cur_err * selector_remap_thresh);
for (uint32_t j = 0; j < selector_history_buf.size(); j++)
{
const int trial_idx = selector_history_buf[j];
if (use_strict_search)
const uint8_t* pSelectors = &m_selector_palette[m_selector_remap_table_new_to_old[trial_idx]][0];
if (m_params.m_compression_level <= 1)
{
if (trial_idx == new_selector_index)
// Predict if evaluating the full color error would cause an early out, by summing the abs err of the selector indices.
int sel_diff = 0;
for (uint32_t p = 0; p < 16; p += 4)
{
best_trial_err = 0;
best_trial_idx = trial_idx;
best_trial_history_buf_idx = j;
break;
sel_diff += iabs(pCur_selectors[p + 0] - pSelectors[p + 0]);
sel_diff += iabs(pCur_selectors[p + 1] - pSelectors[p + 1]);
sel_diff += iabs(pCur_selectors[p + 2] - pSelectors[p + 2]);
sel_diff += iabs(pCur_selectors[p + 3] - pSelectors[p + 3]);
if (sel_diff >= SEL_DIFF_THRESHOLD)
break;
}
if (sel_diff >= SEL_DIFF_THRESHOLD)
continue;
}
const uint64_t thresh_err = minimum(limit_err, best_trial_err);
uint64_t trial_err = 0;
// This tends to early out quickly, so SSE has a hard time competing.
if (r.get_params().m_perceptual)
{
for (uint32_t p = 0; p < 16; p++)
{
uint32_t sel = pSelectors[p];
trial_err += color_distance(true, src_pixels.get_ptr()[p], block_colors[sel], false);
if (trial_err > thresh_err)
break;
}
}
else
{
uint64_t trial_err = 0;
const uint64_t thresh_err = minimum(limit_err, best_trial_err);
color_rgba block_colors[4];
etc_blk.get_block_colors(block_colors, 0);
const uint8_t* pSelectors = &m_selector_palette[m_selector_remap_table_new_to_old[trial_idx]](0, 0);
if (r.get_params().m_perceptual)
for (uint32_t p = 0; p < 16; p++)
{
for (uint32_t p = 0; p < 16; p++)
{
uint32_t sel = pSelectors[p];
trial_err += color_distance(true, src_pixels.get_ptr()[p], block_colors[sel], false);
if (trial_err > thresh_err)
break;
}
}
else
{
for (uint32_t p = 0; p < 16; p++)
{
uint32_t sel = pSelectors[p];
trial_err += color_distance(false, src_pixels.get_ptr()[p], block_colors[sel], false);
if (trial_err > thresh_err)
break;
}
uint32_t sel = pSelectors[p];
trial_err += color_distance(false, src_pixels.get_ptr()[p], block_colors[sel], false);
if (trial_err > thresh_err)
break;
}
}
if ((trial_err < best_trial_err) && (trial_err <= thresh_err))
{
assert(trial_err <= limit_err);
best_trial_err = trial_err;
best_trial_idx = trial_idx;
best_trial_history_buf_idx = j;
}
if ((trial_err < best_trial_err) && (trial_err <= thresh_err))
{
assert(trial_err <= limit_err);
best_trial_err = trial_err;
best_trial_idx = trial_idx;
best_trial_history_buf_idx = j;
}
}
@ -1043,6 +1140,7 @@ namespace basisu
selector_history_buf_histogram.inc(best_trial_history_buf_idx);
}
} // if (m_params.m_selector_rdo_quality_thresh > 0.0f)
m.m_selector_index = m_selector_remap_table_new_to_old[new_selector_index];
@ -1164,6 +1262,14 @@ namespace basisu
} // slice_index
//for (int i = 0; i <= 255 * 3; i++)
//{
// printf("%u, %u, %f\n", g_color_delta_bad_hist[i], g_color_delta_hist[i], g_color_delta_hist[i] ? g_color_delta_bad_hist[i] / (float)g_color_delta_hist[i] : 0);
//}
double total_prep_time = tm.get_elapsed_secs();
debug_printf("basisu_backend::encode_image: Total prep time: %3.2f\n", total_prep_time);
debug_printf("Endpoint pred RDO total endpoint indices remapped: %u %3.2f%%\n",
total_endpoint_indices_remapped, total_endpoint_indices_remapped * 100.0f / get_total_blocks());
@ -1554,215 +1660,82 @@ namespace basisu
bool basisu_backend::encode_selector_palette()
{
const basisu_frontend& r = *m_pFront_end;
histogram delta_selector_pal_histogram(256);
if ((m_params.m_use_global_sel_codebook) && (!m_params.m_use_hybrid_sel_codebooks))
for (uint32_t q = 0; q < r.get_total_selector_clusters(); q++)
{
histogram global_mod_indices(1 << m_params.m_global_sel_codebook_mod_bits);
if (!q)
continue;
for (uint32_t q = 0; q < r.get_total_selector_clusters(); q++)
global_mod_indices.inc(m_global_selector_palette_desc[q].m_mod_index);
const etc1_selector_palette_entry& cur = m_selector_palette[m_selector_remap_table_new_to_old[q]];
const etc1_selector_palette_entry predictor(m_selector_palette[m_selector_remap_table_new_to_old[q - 1]]);
huffman_encoding_table global_pal_model, global_mod_model;
if (!global_mod_model.init(global_mod_indices, 16))
{
error_printf("global_mod_model.init() failed!");
return false;
}
bitwise_coder coder;
coder.init(1024 * 1024);
coder.put_bits(1, 1); // use global codebook
coder.put_bits(m_params.m_global_sel_codebook_pal_bits, 4); // pal bits
coder.put_bits(m_params.m_global_sel_codebook_mod_bits, 4); // mod bits
uint32_t mod_model_bits = 0;
if (m_params.m_global_sel_codebook_mod_bits)
mod_model_bits = coder.emit_huffman_table(global_mod_model);
uint32_t total_pal_bits = 0;
uint32_t total_mod_bits = 0;
for (uint32_t q = 0; q < r.get_total_selector_clusters(); q++)
{
const uint32_t i = m_selector_remap_table_new_to_old[q];
if (m_params.m_global_sel_codebook_pal_bits)
{
coder.put_bits(m_global_selector_palette_desc[i].m_pal_index, m_params.m_global_sel_codebook_pal_bits);
total_pal_bits += m_params.m_global_sel_codebook_pal_bits;
}
if (m_params.m_global_sel_codebook_mod_bits)
total_mod_bits += coder.put_code(m_global_selector_palette_desc[i].m_mod_index, global_mod_model);
}
coder.flush();
m_output.m_selector_palette = coder.get_bytes();
debug_printf("Modifier model bits: %u Avg per entry: %3.3f\n", mod_model_bits, mod_model_bits / float(r.get_total_selector_clusters()));
debug_printf("Palette bits: %u Avg per entry: %3.3f, Modifier bits: %u Avg per entry: %3.3f\n", total_pal_bits, total_pal_bits / float(r.get_total_selector_clusters()), total_mod_bits, total_mod_bits / float(r.get_total_selector_clusters()));
for (uint32_t j = 0; j < 4; j++)
delta_selector_pal_histogram.inc(cur.get_byte(j) ^ predictor.get_byte(j));
}
else if (m_params.m_use_hybrid_sel_codebooks)
if (!delta_selector_pal_histogram.get_total())
delta_selector_pal_histogram.inc(0);
huffman_encoding_table delta_selector_pal_model;
if (!delta_selector_pal_model.init(delta_selector_pal_histogram, 16))
{
huff2D used_global_cb_bitflag_huff2D(1, 8);
histogram global_mod_indices(1 << m_params.m_global_sel_codebook_mod_bits);
for (uint32_t s = 0; s < r.get_total_selector_clusters(); s++)
{
const uint32_t q = m_selector_remap_table_new_to_old[s];
const bool used_global_cb_flag = r.get_selector_cluster_uses_global_cb_vec()[q];
used_global_cb_bitflag_huff2D.emit(used_global_cb_flag);
global_mod_indices.inc(m_global_selector_palette_desc[q].m_mod_index);
}
huffman_encoding_table global_mod_indices_model;
if (!global_mod_indices_model.init(global_mod_indices, 16))
{
error_printf("global_mod_indices_model.init() failed!");
return false;
}
bitwise_coder coder;
coder.init(1024 * 1024);
coder.put_bits(0, 1); // use global codebook
coder.put_bits(1, 1); // uses hybrid codebooks
coder.put_bits(m_params.m_global_sel_codebook_pal_bits, 4); // pal bits
coder.put_bits(m_params.m_global_sel_codebook_mod_bits, 4); // mod bits
used_global_cb_bitflag_huff2D.start_encoding(16);
coder.emit_huffman_table(used_global_cb_bitflag_huff2D.get_encoding_table());
if (m_params.m_global_sel_codebook_mod_bits)
coder.emit_huffman_table(global_mod_indices_model);
uint32_t total_global_cb_entries = 0;
uint32_t total_pal_bits = 0;
uint32_t total_mod_bits = 0;
uint32_t total_selectors = 0;
uint32_t total_selector_bits = 0;
uint32_t total_flag_bits = 0;
for (uint32_t s = 0; s < r.get_total_selector_clusters(); s++)
{
const uint32_t q = m_selector_remap_table_new_to_old[s];
total_flag_bits += used_global_cb_bitflag_huff2D.emit_next_sym(coder);
const bool used_global_cb_flag = r.get_selector_cluster_uses_global_cb_vec()[q];
if (used_global_cb_flag)
{
total_global_cb_entries++;
total_pal_bits += coder.put_bits(r.get_selector_cluster_global_selector_entry_ids()[q].m_palette_index, m_params.m_global_sel_codebook_pal_bits);
total_mod_bits += coder.put_code(r.get_selector_cluster_global_selector_entry_ids()[q].m_modifier.get_index(), global_mod_indices_model);
}
else
{
total_selectors++;
total_selector_bits += 32;
for (uint32_t j = 0; j < 4; j++)
coder.put_bits(m_selector_palette[q].get_byte(j), 8);
}
}
coder.flush();
m_output.m_selector_palette = coder.get_bytes();
debug_printf("Total global CB entries: %u %3.2f%%\n", total_global_cb_entries, total_global_cb_entries * 100.0f / r.get_total_selector_clusters());
debug_printf("Total selector entries: %u %3.2f%%\n", total_selectors, total_selectors * 100.0f / r.get_total_selector_clusters());
debug_printf("Total pal bits: %u, mod bits: %u, selector bits: %u, flag bits: %u\n", total_pal_bits, total_mod_bits, total_selector_bits, total_flag_bits);
error_printf("delta_selector_pal_model.init() failed!");
return false;
}
else
bitwise_coder coder;
coder.init(1024 * 1024);
coder.put_bits(0, 1); // use global codebook
coder.put_bits(0, 1); // uses hybrid codebooks
coder.put_bits(0, 1); // raw bytes
coder.emit_huffman_table(delta_selector_pal_model);
for (uint32_t q = 0; q < r.get_total_selector_clusters(); q++)
{
histogram delta_selector_pal_histogram(256);
for (uint32_t q = 0; q < r.get_total_selector_clusters(); q++)
if (!q)
{
if (!q)
continue;
const basist::etc1_selector_palette_entry& cur = m_selector_palette[m_selector_remap_table_new_to_old[q]];
const basist::etc1_selector_palette_entry predictor(m_selector_palette[m_selector_remap_table_new_to_old[q - 1]]);
for (uint32_t j = 0; j < 4; j++)
delta_selector_pal_histogram.inc(cur.get_byte(j) ^ predictor.get_byte(j));
coder.put_bits(m_selector_palette[m_selector_remap_table_new_to_old[q]].get_byte(j), 8);
continue;
}
if (!delta_selector_pal_histogram.get_total())
delta_selector_pal_histogram.inc(0);
const etc1_selector_palette_entry& cur = m_selector_palette[m_selector_remap_table_new_to_old[q]];
const etc1_selector_palette_entry predictor(m_selector_palette[m_selector_remap_table_new_to_old[q - 1]]);
huffman_encoding_table delta_selector_pal_model;
if (!delta_selector_pal_model.init(delta_selector_pal_histogram, 16))
{
error_printf("delta_selector_pal_model.init() failed!");
return false;
}
for (uint32_t j = 0; j < 4; j++)
coder.put_code(cur.get_byte(j) ^ predictor.get_byte(j), delta_selector_pal_model);
}
bitwise_coder coder;
coder.flush();
m_output.m_selector_palette = coder.get_bytes();
if (m_output.m_selector_palette.size() >= r.get_total_selector_clusters() * 4)
{
coder.init(1024 * 1024);
coder.put_bits(0, 1); // use global codebook
coder.put_bits(0, 1); // uses hybrid codebooks
coder.put_bits(0, 1); // raw bytes
coder.emit_huffman_table(delta_selector_pal_model);
coder.put_bits(1, 1); // raw bytes
for (uint32_t q = 0; q < r.get_total_selector_clusters(); q++)
{
if (!q)
{
for (uint32_t j = 0; j < 4; j++)
coder.put_bits(m_selector_palette[m_selector_remap_table_new_to_old[q]].get_byte(j), 8);
continue;
}
const basist::etc1_selector_palette_entry& cur = m_selector_palette[m_selector_remap_table_new_to_old[q]];
const basist::etc1_selector_palette_entry predictor(m_selector_palette[m_selector_remap_table_new_to_old[q - 1]]);
const uint32_t i = m_selector_remap_table_new_to_old[q];
for (uint32_t j = 0; j < 4; j++)
coder.put_code(cur.get_byte(j) ^ predictor.get_byte(j), delta_selector_pal_model);
coder.put_bits(m_selector_palette[i].get_byte(j), 8);
}
coder.flush();
m_output.m_selector_palette = coder.get_bytes();
if (m_output.m_selector_palette.size() >= r.get_total_selector_clusters() * 4)
{
coder.init(1024 * 1024);
coder.put_bits(0, 1); // use global codebook
coder.put_bits(0, 1); // uses hybrid codebooks
coder.put_bits(1, 1); // raw bytes
for (uint32_t q = 0; q < r.get_total_selector_clusters(); q++)
{
const uint32_t i = m_selector_remap_table_new_to_old[q];
for (uint32_t j = 0; j < 4; j++)
coder.put_bits(m_selector_palette[i].get_byte(j), 8);
}
coder.flush();
m_output.m_selector_palette = coder.get_bytes();
}
} // if (m_params.m_use_global_sel_codebook)
}
debug_printf("Selector codebook bits: %u bytes: %u, Bits per entry: %3.1f, Avg bits/texel: %3.3f\n",
(int)m_output.m_selector_palette.size() * 8, (int)m_output.m_selector_palette.size(),

View file

@ -17,11 +17,86 @@
#include "../transcoder/basisu.h"
#include "basisu_enc.h"
#include "../transcoder/basisu_transcoder_internal.h"
#include "../transcoder/basisu_global_selector_palette.h"
#include "basisu_frontend.h"
namespace basisu
{
struct etc1_selector_palette_entry
{
etc1_selector_palette_entry()
{
clear();
}
void clear()
{
basisu::clear_obj(*this);
}
uint8_t operator[] (uint32_t i) const { assert(i < 16); return m_selectors[i]; }
uint8_t& operator[] (uint32_t i) { assert(i < 16); return m_selectors[i]; }
void set_uint32(uint32_t v)
{
for (uint32_t byte_index = 0; byte_index < 4; byte_index++)
{
uint32_t b = (v >> (byte_index * 8)) & 0xFF;
m_selectors[byte_index * 4 + 0] = b & 3;
m_selectors[byte_index * 4 + 1] = (b >> 2) & 3;
m_selectors[byte_index * 4 + 2] = (b >> 4) & 3;
m_selectors[byte_index * 4 + 3] = (b >> 6) & 3;
}
}
uint32_t get_uint32() const
{
return get_byte(0) | (get_byte(1) << 8) | (get_byte(2) << 16) | (get_byte(3) << 24);
}
uint32_t get_byte(uint32_t byte_index) const
{
assert(byte_index < 4);
return m_selectors[byte_index * 4 + 0] |
(m_selectors[byte_index * 4 + 1] << 2) |
(m_selectors[byte_index * 4 + 2] << 4) |
(m_selectors[byte_index * 4 + 3] << 6);
}
uint8_t operator()(uint32_t x, uint32_t y) const { assert((x < 4) && (y < 4)); return m_selectors[x + y * 4]; }
uint8_t& operator()(uint32_t x, uint32_t y) { assert((x < 4) && (y < 4)); return m_selectors[x + y * 4]; }
bool operator< (const etc1_selector_palette_entry& other) const
{
for (uint32_t i = 0; i < 16; i++)
{
if (m_selectors[i] < other.m_selectors[i])
return true;
else if (m_selectors[i] != other.m_selectors[i])
return false;
}
return false;
}
bool operator== (const etc1_selector_palette_entry& other) const
{
for (uint32_t i = 0; i < 16; i++)
{
if (m_selectors[i] != other.m_selectors[i])
return false;
}
return true;
}
private:
uint8_t m_selectors[16];
};
typedef basisu::vector<etc1_selector_palette_entry> etc1_selector_palette_entry_vec;
struct encoder_block
{
encoder_block()
@ -78,14 +153,11 @@ namespace basisu
float m_endpoint_rdo_quality_thresh;
float m_selector_rdo_quality_thresh;
uint32_t m_compression_level;
bool m_use_global_sel_codebook;
uint32_t m_global_sel_codebook_pal_bits;
uint32_t m_global_sel_codebook_mod_bits;
bool m_use_hybrid_sel_codebooks;
bool m_used_global_codebooks;
bool m_validate;
basisu_backend_params()
{
clear();
@ -99,12 +171,8 @@ namespace basisu
m_endpoint_rdo_quality_thresh = 0.0f;
m_selector_rdo_quality_thresh = 0.0f;
m_compression_level = 0;
m_use_global_sel_codebook = false;
m_global_sel_codebook_pal_bits = ETC1_GLOBAL_SELECTOR_CODEBOOK_MAX_PAL_BITS;
m_global_sel_codebook_mod_bits = basist::etc1_global_palette_entry_modifier::cTotalBits;
m_use_hybrid_sel_codebooks = false;
m_used_global_codebooks = false;
m_validate = true;
}
};
@ -205,7 +273,7 @@ namespace basisu
void clear();
void init(basisu_frontend *pFront_end, basisu_backend_params &params, const basisu_backend_slice_desc_vec &slice_desc, const basist::etc1_global_selector_codebook *pGlobal_sel_codebook);
void init(basisu_frontend *pFront_end, basisu_backend_params &params, const basisu_backend_slice_desc_vec &slice_desc);
uint32_t encode();
@ -217,10 +285,9 @@ namespace basisu
basisu_backend_params m_params;
basisu_backend_slice_desc_vec m_slices;
basisu_backend_output m_output;
const basist::etc1_global_selector_codebook *m_pGlobal_sel_codebook;
etc1_endpoint_palette_entry_vec m_endpoint_palette;
basist::etc1_selector_palette_entry_vec m_selector_palette;
etc1_selector_palette_entry_vec m_selector_palette;
struct etc1_global_selector_cb_entry_desc
{

File diff suppressed because it is too large Load diff

View file

@ -16,12 +16,11 @@
#include "basisu_frontend.h"
#include "basisu_backend.h"
#include "basisu_basis_file.h"
#include "../transcoder/basisu_global_selector_palette.h"
#include "../transcoder/basisu_transcoder.h"
#include "basisu_uastc_enc.h"
#define BASISU_LIB_VERSION 115
#define BASISU_LIB_VERSION_STRING "1.15"
#define BASISU_LIB_VERSION 116
#define BASISU_LIB_VERSION_STRING "1.16"
#ifndef BASISD_SUPPORT_KTX2
#error BASISD_SUPPORT_KTX2 is undefined
@ -36,6 +35,9 @@
namespace basisu
{
struct opencl_context;
typedef opencl_context* opencl_context_ptr;
const uint32_t BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION = 16384;
// Allow block's color distance to increase by 1.5 while searching for an alternative nearby endpoint.
@ -203,13 +205,9 @@ namespace basisu
struct basis_compressor_params
{
basis_compressor_params() :
m_pSel_codebook(NULL),
m_compression_level((int)BASISU_DEFAULT_COMPRESSION_LEVEL, 0, (int)BASISU_MAX_COMPRESSION_LEVEL),
m_selector_rdo_thresh(BASISU_DEFAULT_SELECTOR_RDO_THRESH, 0.0f, 1e+10f),
m_endpoint_rdo_thresh(BASISU_DEFAULT_ENDPOINT_RDO_THRESH, 0.0f, 1e+10f),
m_hybrid_sel_cb_quality_thresh(BASISU_DEFAULT_HYBRID_SEL_CB_QUALITY_THRESH, 0.0f, 1e+10f),
m_global_pal_bits(8, 0, ETC1_GLOBAL_SELECTOR_CODEBOOK_MAX_PAL_BITS),
m_global_mod_bits(8, 0, basist::etc1_global_palette_entry_modifier::cTotalBits),
m_mip_scale(1.0f, .000125f, 4.0f),
m_mip_smallest_dimension(1, 1, 16384),
m_max_endpoint_clusters(512),
@ -234,9 +232,8 @@ namespace basisu
void clear()
{
m_pSel_codebook = NULL;
m_uastc.clear();
m_use_opencl.clear();
m_status_output.clear();
m_source_filenames.clear();
@ -249,11 +246,8 @@ namespace basisu
m_y_flip.clear();
m_debug.clear();
m_validate.clear();
m_validate_etc1s.clear();
m_debug_images.clear();
m_global_sel_pal.clear();
m_auto_global_sel_pal.clear();
m_no_hybrid_sel_cb.clear();
m_perceptual.clear();
m_no_selector_rdo.clear();
m_selector_rdo_thresh.clear();
@ -269,9 +263,6 @@ namespace basisu
m_swizzle[2] = 2;
m_swizzle[3] = 3;
m_renormalize.clear();
m_hybrid_sel_cb_quality_thresh.clear();
m_global_pal_bits.clear();
m_global_mod_bits.clear();
m_disable_hierarchical_endpoint_codebooks.clear();
m_no_endpoint_rdo.clear();
@ -319,15 +310,16 @@ namespace basisu
m_ktx2_zstd_supercompression_level.clear();
m_ktx2_srgb_transfer_func.clear();
m_validate_output_data.clear();
m_pJob_pool = nullptr;
}
// Pointer to the global selector codebook, or nullptr to not use a global selector codebook
const basist::etc1_global_selector_codebook *m_pSel_codebook;
// True to generate UASTC .basis file data, otherwise ETC1S.
bool_param<false> m_uastc;
bool_param<false> m_use_opencl;
// If m_read_source_images is true, m_source_filenames (and optionally m_source_alpha_filenames) contains the filenames of PNG images to read.
// Otherwise, the compressor processes the images in m_source_images.
basisu::vector<std::string> m_source_filenames;
@ -353,20 +345,16 @@ namespace basisu
// Output debug information during compression
bool_param<false> m_debug;
bool_param<false> m_validate;
bool_param<false> m_validate_etc1s;
// m_debug_images is pretty slow
bool_param<false> m_debug_images;
// Compression level, from 0 to BASISU_MAX_COMPRESSION_LEVEL (higher is slower)
// ETC1S compression level, from 0 to BASISU_MAX_COMPRESSION_LEVEL (higher is slower).
// This parameter controls numerous internal encoding speed vs. compression efficiency/performance tradeoffs.
// Note this is NOT the same as the ETC1S quality level, and most users shouldn't change this.
param<int> m_compression_level;
bool_param<false> m_global_sel_pal;
bool_param<false> m_auto_global_sel_pal;
// Frontend/backend codec parameters
bool_param<false> m_no_hybrid_sel_cb;
// Use perceptual sRGB colorspace metrics instead of linear
bool_param<true> m_perceptual;
@ -398,13 +386,10 @@ namespace basisu
bool_param<false> m_renormalize;
// If true the front end will not use 2 level endpoint codebook searching, for slightly higher quality but much slower execution.
// Note some m_compression_level's disable this automatically.
bool_param<false> m_disable_hierarchical_endpoint_codebooks;
// Global/hybrid selector codebook parameters
param<float> m_hybrid_sel_cb_quality_thresh;
param<int> m_global_pal_bits;
param<int> m_global_mod_bits;
// mipmap generation parameters
bool_param<false> m_mip_gen;
param<float> m_mip_scale;
@ -415,9 +400,9 @@ namespace basisu
bool_param<true> m_mip_wrapping;
bool_param<true> m_mip_fast;
param<int> m_mip_smallest_dimension;
// Codebook size (quality) control.
// If m_quality_level != -1, it controls the quality level. It ranges from [0,255] or [BASISU_QUALITY_MIN, BASISU_QUALITY_MAX].
// If m_quality_level != -1, it controls the quality level. It ranges from [1,255] or [BASISU_QUALITY_MIN, BASISU_QUALITY_MAX].
// Otherwise m_max_endpoint_clusters/m_max_selector_clusters controls the codebook sizes directly.
uint32_t m_max_endpoint_clusters;
uint32_t m_max_selector_clusters;
@ -444,6 +429,7 @@ namespace basisu
param<int> m_resample_width;
param<int> m_resample_height;
param<float> m_resample_factor;
const basist::basisu_lowlevel_etc1s_transcoder *m_pGlobal_codebooks;
// KTX2 specific parameters.
@ -454,21 +440,27 @@ namespace basisu
param<int> m_ktx2_zstd_supercompression_level;
bool_param<false> m_ktx2_srgb_transfer_func;
bool_param<false> m_validate_output_data;
job_pool *m_pJob_pool;
};
// Important: basisu_encoder_init() MUST be called first before using this class.
class basis_compressor
{
BASISU_NO_EQUALS_OR_COPY_CONSTRUCT(basis_compressor);
public:
basis_compressor();
~basis_compressor();
// Note it *should* be possible to call init() multiple times with different inputs, but this scenario isn't well tested. Ideally, create 1 object, compress, then delete it.
bool init(const basis_compressor_params &params);
enum error_code
{
cECSuccess = 0,
cECFailedInitializing,
cECFailedReadingSourceImages,
cECFailedValidating,
cECFailedEncodeUASTC,
@ -495,9 +487,13 @@ namespace basisu
double get_basis_bits_per_texel() const { return m_basis_bits_per_texel; }
bool get_any_source_image_has_alpha() const { return m_any_source_image_has_alpha; }
bool get_opencl_failed() const { return m_opencl_failed; }
private:
basis_compressor_params m_params;
opencl_context_ptr m_pOpenCL_context;
basisu::vector<image> m_slice_images;
@ -509,8 +505,7 @@ namespace basisu
basisu_backend_slice_desc_vec m_slice_descs;
uint32_t m_total_blocks;
bool m_auto_global_sel_pal;
basisu_frontend m_frontend;
pixel_block_vec m_source_blocks;
@ -536,6 +531,8 @@ namespace basisu
bool m_any_source_image_has_alpha;
bool m_opencl_failed;
bool read_source_images();
bool extract_source_blocks();
bool process_frontend();
@ -550,6 +547,98 @@ namespace basisu
void get_dfd(uint8_vec& dfd, const basist::ktx2_header& hdr);
bool create_ktx2_file();
};
// Alternative simple C-style wrapper API around the basis_compressor class.
// This doesn't expose every encoder feature, but it's enough to get going.
// Important: basisu_encoder_init() MUST be called first before calling these functions.
//
// Input parameters:
// source_images: Array of "image" objects, one per mipmap level, largest mipmap level first.
// OR
// pImageRGBA: pointer to a 32-bpp RGBx or RGBA raster image, R first in memory, A last. Top scanline first in memory.
// width/height/pitch_in_pixels: dimensions of pImageRGBA
//
// flags_and_quality: Combination of the above flags logically OR'd with the ETC1S or UASTC level, i.e. "cFlagSRGB | cFlagGenMipsClamp | cFlagThreaded | 128" or "cFlagSRGB | cFlagGenMipsClamp | cFlagUASTC | cFlagThreaded | cPackUASTCLevelDefault".
// In ETC1S mode, the lower 8-bits are the ETC1S quality level which ranges from [1,255] (higher=better quality/larger files)
// In UASTC mode, the lower 8-bits are the UASTC pack level (see cPackUASTCLevelFastest, etc.). Fastest/lowest quality is 0, so be sure to set it correctly.
//
// uastc_rdo_quality: Float UASTC RDO quality level (0=no change, higher values lower quality but increase compressibility, initially try .5-1.5)
//
// pSize: Returns the output data's compressed size in bytes
//
// Return value is the compressed .basis or .ktx2 file data, or nullptr on failure. Must call basis_free() to free it.
enum
{
cFlagUseOpenCL = 1 << 8, // use OpenCL if available
cFlagThreaded = 1 << 9, // use multiple threads for compression
cFlagDebug = 1 << 10, // enable debug output
cFlagKTX2 = 1 << 11, // generate a KTX2 file
cFlagKTX2UASTCSuperCompression = 1 << 12, // use KTX2 Zstd supercompression on UASTC files
cFlagSRGB = 1 << 13, // input texture is sRGB, use perceptual colorspace metrics, also use sRGB filtering during mipmap gen, and also sets KTX2 output transfer func to sRGB
cFlagGenMipsClamp = 1 << 14, // generate mipmaps with clamp addressing
cFlagGenMipsWrap = 1 << 15, // generate mipmaps with wrap addressing
cFlagYFlip = 1 << 16, // flip source image on Y axis before compression
cFlagUASTC = 1 << 17, // use UASTC compression vs. ETC1S
cFlagUASTCRDO = 1 << 18 // use RDO postprocessing when generating UASTC files (must set uastc_rdo_quality to the quality scalar)
};
// This function accepts an array of source images.
// If more than one image is provided, it's assumed the images form a mipmap pyramid and automatic mipmap generation is disabled.
void* basis_compress(
const basisu::vector<image> &source_images,
uint32_t flags_and_quality, float uastc_rdo_quality,
size_t* pSize,
image_stats* pStats = nullptr);
// This function only accepts a single source image.
void* basis_compress(
const uint8_t* pImageRGBA, uint32_t width, uint32_t height, uint32_t pitch_in_pixels,
uint32_t flags_and_quality, float uastc_rdo_quality,
size_t* pSize,
image_stats* pStats = nullptr);
// Frees the dynamically allocated file data returned by basis_compress().
void basis_free_data(void* p);
// Parallel compression API
struct parallel_results
{
double m_total_time;
basis_compressor::error_code m_error_code;
uint8_vec m_basis_file;
uint8_vec m_ktx2_file;
basisu::vector<image_stats> m_stats;
double m_basis_bits_per_texel;
bool m_any_source_image_has_alpha;
parallel_results()
{
clear();
}
void clear()
{
m_total_time = 0.0f;
m_error_code = basis_compressor::cECFailedInitializing;
m_basis_file.clear();
m_ktx2_file.clear();
m_stats.clear();
m_basis_bits_per_texel = 0.0f;
m_any_source_image_has_alpha = false;
}
};
// Compresses an array of input textures across total_threads threads using the basis_compressor class.
// Compressing multiple textures at a time is substantially more efficient than just compressing one at a time.
// total_threads must be >= 1.
bool basis_parallel_compress(
uint32_t total_threads,
const basisu::vector<basis_compressor_params> &params_vec,
basisu::vector< parallel_results > &results_vec);
} // namespace basisu

View file

@ -13,16 +13,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "basisu_enc.h"
#include "lodepng.h"
#include "basisu_resampler.h"
#include "basisu_resampler_filters.h"
#include "basisu_etc.h"
#include "../transcoder/basisu_transcoder.h"
#include "basisu_bc7enc.h"
#include "apg_bmp.h"
#include "jpgd.h"
#include "pvpngreader.h"
#include "basisu_opencl.h"
#include <vector>
#define MINIZ_HEADER_FILE_ONLY
#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES
#include "basisu_miniz.h"
#if defined(_WIN32)
// For QueryPerformanceCounter/QueryPerformanceFrequency
#define WIN32_LEAN_AND_MEAN
@ -158,34 +162,62 @@ namespace basisu
{ 0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+007E (~)
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // U+007F
};
bool g_library_initialized;
std::mutex g_encoder_init_mutex;
// Encoder library initialization (just call once at startup)
void basisu_encoder_init()
void basisu_encoder_init(bool use_opencl, bool opencl_force_serialization)
{
std::lock_guard<std::mutex> lock(g_encoder_init_mutex);
if (g_library_initialized)
return;
detect_sse41();
basist::basisu_transcoder_init();
pack_etc1_solid_color_init();
//uastc_init();
bc7enc_compress_block_init(); // must be after uastc_init()
// Don't bother initializing the OpenCL module at all if it's been completely disabled.
if (use_opencl)
{
opencl_init(opencl_force_serialization);
}
g_library_initialized = true;
}
void error_printf(const char *pFmt, ...)
void basisu_encoder_deinit()
{
char buf[2048];
opencl_deinit();
g_library_initialized = false;
}
void error_vprintf(const char* pFmt, va_list args)
{
char buf[8192];
va_list args;
va_start(args, pFmt);
#ifdef _WIN32
vsprintf_s(buf, sizeof(buf), pFmt, args);
#else
vsnprintf(buf, sizeof(buf), pFmt, args);
#endif
va_end(args);
fprintf(stderr, "ERROR: %s", buf);
}
void error_printf(const char *pFmt, ...)
{
va_list args;
va_start(args, pFmt);
error_vprintf(pFmt, args);
va_end(args);
}
#if defined(_WIN32)
inline void query_counter(timer_ticks* pTicks)
{
@ -195,7 +227,7 @@ namespace basisu
{
QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER*>(pTicks));
}
#elif defined(__APPLE__) || defined(__OpenBSD__)
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
#include <sys/time.h>
inline void query_counter(timer_ticks* pTicks)
{
@ -284,59 +316,6 @@ namespace basisu
}
const uint32_t MAX_32BIT_ALLOC_SIZE = 250000000;
bool load_bmp(const char* pFilename, image& img)
{
int w = 0, h = 0;
unsigned int n_chans = 0;
unsigned char* pImage_data = apg_bmp_read(pFilename, &w, &h, &n_chans);
if ((!pImage_data) || (!w) || (!h) || ((n_chans != 3) && (n_chans != 4)))
{
error_printf("Failed loading .BMP image \"%s\"!\n", pFilename);
if (pImage_data)
apg_bmp_free(pImage_data);
return false;
}
if (sizeof(void *) == sizeof(uint32_t))
{
if ((w * h * n_chans) > MAX_32BIT_ALLOC_SIZE)
{
error_printf("Image \"%s\" is too large (%ux%u) to process in a 32-bit build!\n", pFilename, w, h);
if (pImage_data)
apg_bmp_free(pImage_data);
return false;
}
}
img.resize(w, h);
const uint8_t *pSrc = pImage_data;
for (int y = 0; y < h; y++)
{
color_rgba *pDst = &img(0, y);
for (int x = 0; x < w; x++)
{
pDst->r = pSrc[0];
pDst->g = pSrc[1];
pDst->b = pSrc[2];
pDst->a = (n_chans == 3) ? 255 : pSrc[3];
pSrc += n_chans;
++pDst;
}
}
apg_bmp_free(pImage_data);
return true;
}
bool load_tga(const char* pFilename, image& img)
{
@ -392,53 +371,35 @@ namespace basisu
bool load_png(const uint8_t *pBuf, size_t buf_size, image &img, const char *pFilename)
{
interval_timer tm;
tm.start();
if (!buf_size)
return false;
unsigned err = 0, w = 0, h = 0;
if (sizeof(void*) == sizeof(uint32_t))
uint32_t width = 0, height = 0, num_chans = 0;
void* pImage = pv_png::load_png(pBuf, buf_size, 4, width, height, num_chans);
if (!pBuf)
{
// Inspect the image first on 32-bit builds, to see if the image would require too much memory.
lodepng::State state;
err = lodepng_inspect(&w, &h, &state, pBuf, buf_size);
if ((err != 0) || (!w) || (!h))
return false;
const uint32_t exepected_alloc_size = w * h * sizeof(uint32_t);
// If the file is too large on 32-bit builds then just bail now, to prevent causing a memory exception.
if (exepected_alloc_size >= MAX_32BIT_ALLOC_SIZE)
{
error_printf("Image \"%s\" is too large (%ux%u) to process in a 32-bit build!\n", (pFilename != nullptr) ? pFilename : "<memory>", w, h);
return false;
}
w = h = 0;
error_printf("pv_png::load_png failed while loading image \"%s\"\n", pFilename);
return false;
}
std::vector<uint8_t> out;
err = lodepng::decode(out, w, h, pBuf, buf_size);
if ((err != 0) || (!w) || (!h))
return false;
img.grant_ownership(reinterpret_cast<color_rgba*>(pImage), width, height);
if (out.size() != (w * h * 4))
return false;
img.resize(w, h);
memcpy(img.get_ptr(), &out[0], out.size());
//debug_printf("Total load_png() time: %3.3f secs\n", tm.get_elapsed_secs());
return true;
}
bool load_png(const char* pFilename, image& img)
{
std::vector<uint8_t> buffer;
unsigned err = lodepng::load_file(buffer, std::string(pFilename));
if (err)
uint8_vec buffer;
if (!read_file_to_vec(pFilename, buffer))
{
error_printf("load_png: Failed reading file \"%s\"!\n", pFilename);
return false;
}
return load_png(buffer.data(), buffer.size(), img, pFilename);
}
@ -468,8 +429,6 @@ namespace basisu
if (strcasecmp(pExt, "png") == 0)
return load_png(pFilename, img);
if (strcasecmp(pExt, "bmp") == 0)
return load_bmp(pFilename, img);
if (strcasecmp(pExt, "tga") == 0)
return load_tga(pFilename, img);
if ( (strcasecmp(pExt, "jpg") == 0) || (strcasecmp(pExt, "jfif") == 0) || (strcasecmp(pExt, "jpeg") == 0) )
@ -482,61 +441,67 @@ namespace basisu
{
if (!img.get_total_pixels())
return false;
const uint32_t MAX_PNG_IMAGE_DIM = 32768;
if ((img.get_width() > MAX_PNG_IMAGE_DIM) || (img.get_height() > MAX_PNG_IMAGE_DIM))
return false;
std::vector<uint8_t> out;
unsigned err = 0;
void* pPNG_data = nullptr;
size_t PNG_data_size = 0;
if (image_save_flags & cImageSaveGrayscale)
{
uint8_vec g_pixels(img.get_width() * img.get_height());
uint8_t *pDst = &g_pixels[0];
uint8_vec g_pixels(img.get_total_pixels());
uint8_t* pDst = &g_pixels[0];
for (uint32_t y = 0; y < img.get_height(); y++)
for (uint32_t x = 0; x < img.get_width(); x++)
*pDst++ = img(x, y)[grayscale_comp];
err = lodepng::encode(out, (const uint8_t*)&g_pixels[0], img.get_width(), img.get_height(), LCT_GREY, 8);
pPNG_data = buminiz::tdefl_write_image_to_png_file_in_memory_ex(g_pixels.data(), img.get_width(), img.get_height(), 1, &PNG_data_size, 1, false);
}
else
{
bool has_alpha = img.has_alpha();
if ((!has_alpha) || ((image_save_flags & cImageSaveIgnoreAlpha) != 0))
bool has_alpha = false;
if ((image_save_flags & cImageSaveIgnoreAlpha) == 0)
has_alpha = img.has_alpha();
if (!has_alpha)
{
const uint64_t total_bytes = (uint64_t)img.get_width() * 3U * (uint64_t)img.get_height();
if (total_bytes > INT_MAX)
return false;
uint8_vec rgb_pixels(static_cast<size_t>(total_bytes));
uint8_t *pDst = &rgb_pixels[0];
uint8_vec rgb_pixels(img.get_total_pixels() * 3);
uint8_t* pDst = &rgb_pixels[0];
for (uint32_t y = 0; y < img.get_height(); y++)
{
const color_rgba* pSrc = &img(0, y);
for (uint32_t x = 0; x < img.get_width(); x++)
{
const color_rgba& c = img(x, y);
pDst[0] = c.r;
pDst[1] = c.g;
pDst[2] = c.b;
pDst[0] = pSrc->r;
pDst[1] = pSrc->g;
pDst[2] = pSrc->b;
pSrc++;
pDst += 3;
}
}
err = lodepng::encode(out, (const uint8_t*)& rgb_pixels[0], img.get_width(), img.get_height(), LCT_RGB, 8);
pPNG_data = buminiz::tdefl_write_image_to_png_file_in_memory_ex(rgb_pixels.data(), img.get_width(), img.get_height(), 3, &PNG_data_size, 1, false);
}
else
{
err = lodepng::encode(out, (const uint8_t*)img.get_ptr(), img.get_width(), img.get_height(), LCT_RGBA, 8);
pPNG_data = buminiz::tdefl_write_image_to_png_file_in_memory_ex(img.get_ptr(), img.get_width(), img.get_height(), 4, &PNG_data_size, 1, false);
}
}
err = lodepng::save_file(out, std::string(pFilename));
if (err)
if (!pPNG_data)
return false;
return true;
bool status = write_data_to_file(pFilename, pPNG_data, PNG_data_size);
if (!status)
{
error_printf("save_png: Failed writing to filename \"%s\"!\n", pFilename);
}
free(pPNG_data);
return status;
}
bool read_file_to_vec(const char* pFilename, uint8_vec& data)
@ -1620,7 +1585,8 @@ namespace basisu
void job_pool::job_thread(uint32_t index)
{
debug_printf("job_pool::job_thread: starting %u\n", index);
BASISU_NOTE_UNUSED(index);
//debug_printf("job_pool::job_thread: starting %u\n", index);
while (true)
{
@ -1656,7 +1622,7 @@ namespace basisu
m_no_more_jobs.notify_all();
}
debug_printf("job_pool::job_thread: exiting\n");
//debug_printf("job_pool::job_thread: exiting\n");
}
// .TGA image loading
@ -1779,6 +1745,8 @@ namespace basisu
return nullptr;
}
//const uint32_t bytes_per_line = hdr.m_width * tga_bytes_per_pixel;
const uint8_t *pSrc = pBuf + sizeof(tga_header);
uint32_t bytes_remaining = buf_size - sizeof(tga_header);

View file

@ -33,14 +33,23 @@
// If BASISU_USE_HIGH_PRECISION_COLOR_DISTANCE is 1, quality in perceptual mode will be slightly greater, but at a large increase in encoding CPU time.
#define BASISU_USE_HIGH_PRECISION_COLOR_DISTANCE (0)
#if BASISU_SUPPORT_SSE
// Declared in basisu_kernels_imp.h, but we can't include that here otherwise it would lead to circular type errors.
extern void update_covar_matrix_16x16_sse41(uint32_t num_vecs, const void* pWeighted_vecs, const void* pOrigin, const uint32_t *pVec_indices, void* pMatrix16x16);
#endif
namespace basisu
{
extern uint8_t g_hamming_dist[256];
extern const uint8_t g_debug_font8x8_basic[127 - 32 + 1][8];
// true if basisu_encoder_init() has been called and returned.
extern bool g_library_initialized;
// Encoder library initialization.
// This function MUST be called before encoding anything!
void basisu_encoder_init();
void basisu_encoder_init(bool use_opencl = false, bool opencl_force_serialization = false);
void basisu_encoder_deinit();
// basisu_kernels_sse.cpp - will be a no-op and g_cpu_supports_sse41 will always be false unless compiled with BASISU_SUPPORT_SSE=1
extern void detect_sse41();
@ -51,8 +60,9 @@ namespace basisu
const bool g_cpu_supports_sse41 = false;
#endif
void error_vprintf(const char* pFmt, va_list args);
void error_printf(const char *pFmt, ...);
// Helpers
inline uint8_t clamp255(int32_t i)
@ -170,18 +180,24 @@ namespace basisu
class running_stat
{
public:
running_stat() :
m_n(0),
m_old_m(0), m_new_m(0), m_old_s(0), m_new_s(0)
{
}
running_stat() { clear(); }
void clear()
{
m_n = 0;
m_total = 0;
m_old_m = 0;
m_new_m = 0;
m_old_s = 0;
m_new_s = 0;
m_min = 0;
m_max = 0;
}
void push(double x)
{
m_n++;
m_total += x;
if (m_n == 1)
{
m_old_m = m_new_m = x;
@ -191,6 +207,7 @@ namespace basisu
}
else
{
// See Knuth TAOCP vol 2, 3rd edition, page 232
m_new_m = m_old_m + (x - m_old_m) / m_n;
m_new_s = m_old_s + (x - m_old_m) * (x - m_new_m);
m_old_m = m_new_m;
@ -199,15 +216,23 @@ namespace basisu
m_max = basisu::maximum(x, m_max);
}
}
uint32_t get_num() const
{
return m_n;
}
double get_total() const
{
return m_total;
}
double get_mean() const
{
return (m_n > 0) ? m_new_m : 0.0;
}
// Returns sample variance
double get_variance() const
{
return ((m_n > 1) ? m_new_s / (m_n - 1) : 0.0);
@ -230,7 +255,7 @@ namespace basisu
private:
uint32_t m_n;
double m_old_m, m_new_m, m_old_s, m_new_s, m_min, m_max;
double m_total, m_old_m, m_new_m, m_old_s, m_new_s, m_min, m_max;
};
// Linear algebra
@ -401,6 +426,8 @@ namespace basisu
typedef vec<3, float> vec3F;
typedef vec<2, float> vec2F;
typedef vec<1, float> vec1F;
typedef vec<16, float> vec16F;
template <uint32_t Rows, uint32_t Cols, typename T>
class matrix
@ -504,6 +531,164 @@ namespace basisu
[pKeys](uint32_t a, uint32_t b) { return pKeys[a] < pKeys[b]; }
);
}
// 1-4 byte direct Radix sort.
template <typename T>
T* radix_sort(uint32_t num_vals, T* pBuf0, T* pBuf1, uint32_t key_ofs, uint32_t key_size)
{
assert(key_ofs < sizeof(T));
assert((key_size >= 1) && (key_size <= 4));
uint32_t hist[256 * 4];
memset(hist, 0, sizeof(hist[0]) * 256 * key_size);
#define BASISU_GET_KEY(p) (*(uint32_t *)((uint8_t *)(p) + key_ofs))
if (key_size == 4)
{
T* p = pBuf0;
T* q = pBuf0 + num_vals;
for (; p != q; p++)
{
const uint32_t key = BASISU_GET_KEY(p);
hist[key & 0xFF]++;
hist[256 + ((key >> 8) & 0xFF)]++;
hist[512 + ((key >> 16) & 0xFF)]++;
hist[768 + ((key >> 24) & 0xFF)]++;
}
}
else if (key_size == 3)
{
T* p = pBuf0;
T* q = pBuf0 + num_vals;
for (; p != q; p++)
{
const uint32_t key = BASISU_GET_KEY(p);
hist[key & 0xFF]++;
hist[256 + ((key >> 8) & 0xFF)]++;
hist[512 + ((key >> 16) & 0xFF)]++;
}
}
else if (key_size == 2)
{
T* p = pBuf0;
T* q = pBuf0 + (num_vals >> 1) * 2;
for (; p != q; p += 2)
{
const uint32_t key0 = BASISU_GET_KEY(p);
const uint32_t key1 = BASISU_GET_KEY(p + 1);
hist[key0 & 0xFF]++;
hist[256 + ((key0 >> 8) & 0xFF)]++;
hist[key1 & 0xFF]++;
hist[256 + ((key1 >> 8) & 0xFF)]++;
}
if (num_vals & 1)
{
const uint32_t key = BASISU_GET_KEY(p);
hist[key & 0xFF]++;
hist[256 + ((key >> 8) & 0xFF)]++;
}
}
else
{
assert(key_size == 1);
if (key_size != 1)
return NULL;
T* p = pBuf0;
T* q = pBuf0 + (num_vals >> 1) * 2;
for (; p != q; p += 2)
{
const uint32_t key0 = BASISU_GET_KEY(p);
const uint32_t key1 = BASISU_GET_KEY(p + 1);
hist[key0 & 0xFF]++;
hist[key1 & 0xFF]++;
}
if (num_vals & 1)
{
const uint32_t key = BASISU_GET_KEY(p);
hist[key & 0xFF]++;
}
}
T* pCur = pBuf0;
T* pNew = pBuf1;
for (uint32_t pass = 0; pass < key_size; pass++)
{
const uint32_t* pHist = &hist[pass << 8];
uint32_t offsets[256];
uint32_t cur_ofs = 0;
for (uint32_t i = 0; i < 256; i += 2)
{
offsets[i] = cur_ofs;
cur_ofs += pHist[i];
offsets[i + 1] = cur_ofs;
cur_ofs += pHist[i + 1];
}
const uint32_t pass_shift = pass << 3;
T* p = pCur;
T* q = pCur + (num_vals >> 1) * 2;
for (; p != q; p += 2)
{
uint32_t c0 = (BASISU_GET_KEY(p) >> pass_shift) & 0xFF;
uint32_t c1 = (BASISU_GET_KEY(p + 1) >> pass_shift) & 0xFF;
if (c0 == c1)
{
uint32_t dst_offset0 = offsets[c0];
offsets[c0] = dst_offset0 + 2;
pNew[dst_offset0] = p[0];
pNew[dst_offset0 + 1] = p[1];
}
else
{
uint32_t dst_offset0 = offsets[c0]++;
uint32_t dst_offset1 = offsets[c1]++;
pNew[dst_offset0] = p[0];
pNew[dst_offset1] = p[1];
}
}
if (num_vals & 1)
{
uint32_t c = (BASISU_GET_KEY(p) >> pass_shift) & 0xFF;
uint32_t dst_offset = offsets[c];
offsets[c] = dst_offset + 1;
pNew[dst_offset] = *p;
}
T* t = pCur;
pCur = pNew;
pNew = t;
}
return pCur;
}
#undef BASISU_GET_KEY
// Very simple job pool with no dependencies.
class job_pool
@ -805,17 +990,28 @@ namespace basisu
int dg = e1.g - e2.g;
int db = e1.b - e2.b;
#if 0
int delta_l = dr * 27 + dg * 92 + db * 9;
int delta_cr = dr * 128 - delta_l;
int delta_cb = db * 128 - delta_l;
uint32_t id = ((uint32_t)(delta_l * delta_l) >> 7U) +
((((uint32_t)(delta_cr * delta_cr) >> 7U) * 26U) >> 7U) +
((((uint32_t)(delta_cb * delta_cb) >> 7U) * 3U) >> 7U);
#else
int64_t delta_l = dr * 27 + dg * 92 + db * 9;
int64_t delta_cr = dr * 128 - delta_l;
int64_t delta_cb = db * 128 - delta_l;
uint32_t id = ((uint32_t)((delta_l * delta_l) >> 7U)) +
((((uint32_t)((delta_cr * delta_cr) >> 7U)) * 26U) >> 7U) +
((((uint32_t)((delta_cb * delta_cb) >> 7U)) * 3U) >> 7U);
#endif
if (alpha)
{
int da = (e1.a - e2.a) << 7;
// This shouldn't overflow if da is 255 or -255: 29.99 bits after squaring.
id += ((uint32_t)(da * da) >> 7U);
}
@ -1258,7 +1454,7 @@ namespace basisu
{
codebook.resize(codebook.size() + 1);
codebook.back() = cur.m_training_vecs;
if (node_stack.empty())
break;
@ -1295,6 +1491,9 @@ namespace basisu
uint32_t total_leaf_nodes = 1;
//interval_timer tm;
//tm.start();
while ((var_heap.size()) && (total_leaf_nodes < max_size))
{
const uint32_t node_index = var_heap.get_top_index();
@ -1315,6 +1514,8 @@ namespace basisu
}
}
//debug_printf("tree_vector_quant::generate %u: %3.3f secs\n", TrainingVectorType::num_elements, tm.get_elapsed_secs());
return true;
}
@ -1443,17 +1644,32 @@ namespace basisu
{
const uint32_t N = TrainingVectorType::num_elements;
matrix<N, N, float> cmatrix(cZero);
matrix<N, N, float> cmatrix;
// Compute covariance matrix from weighted input vectors
for (uint32_t i = 0; i < node.m_training_vecs.size(); i++)
if ((N != 16) || (!g_cpu_supports_sse41))
{
const TrainingVectorType v(m_training_vecs[node.m_training_vecs[i]].first - node.m_origin);
const TrainingVectorType w(static_cast<float>(m_training_vecs[node.m_training_vecs[i]].second) * v);
cmatrix.set_zero();
for (uint32_t x = 0; x < N; x++)
for (uint32_t y = x; y < N; y++)
cmatrix[x][y] = cmatrix[x][y] + v[x] * w[y];
// Compute covariance matrix from weighted input vectors
for (uint32_t i = 0; i < node.m_training_vecs.size(); i++)
{
const TrainingVectorType v(m_training_vecs[node.m_training_vecs[i]].first - node.m_origin);
const TrainingVectorType w(static_cast<float>(m_training_vecs[node.m_training_vecs[i]].second) * v);
for (uint32_t x = 0; x < N; x++)
for (uint32_t y = x; y < N; y++)
cmatrix[x][y] = cmatrix[x][y] + v[x] * w[y];
}
}
else
{
#if BASISU_SUPPORT_SSE
// Specialize the case with 16x16 matrices, which are quite expensive without SIMD.
// This SSE function takes pointers to void types, so do some sanity checks.
assert(sizeof(TrainingVectorType) == sizeof(float) * 16);
assert(sizeof(training_vec_with_weight) == sizeof(std::pair<vec16F, uint64_t>));
update_covar_matrix_16x16_sse41(node.m_training_vecs.size(), m_training_vecs.data(), &node.m_origin, node.m_training_vecs.data(), &cmatrix);
#endif
}
const float renorm_scale = 1.0f / node.m_weight;
@ -1632,16 +1848,19 @@ namespace basisu
}
}
// Node is unsplittable using the above algorithm - try something else to split it up.
if ((!l_weight) || (!r_weight))
{
l_children.resize(0);
new_l_child.set(0.0f);
l_ttsum = 0.0f;
l_weight = 0;
r_children.resize(0);
new_r_child.set(0.0f);
r_ttsum = 0.0f;
r_weight = 0;
TrainingVectorType firstVec;
for (uint32_t i = 0; i < node.m_training_vecs.size(); i++)
{
@ -1847,31 +2066,67 @@ namespace basisu
uint32_t max_codebook_size, uint32_t max_parent_codebook_size,
basisu::vector<uint_vec>& codebook,
basisu::vector<uint_vec>& parent_codebook,
uint32_t max_threads, job_pool *pJob_pool)
uint32_t max_threads, job_pool *pJob_pool,
bool even_odd_input_pairs_equal)
{
typedef bit_hasher<typename Quantizer::training_vec_type> training_vec_bit_hasher;
typedef std::unordered_map < typename Quantizer::training_vec_type, weighted_block_group,
training_vec_bit_hasher> group_hash;
//interval_timer tm;
//tm.start();
group_hash unique_vecs;
unique_vecs.reserve(20000);
weighted_block_group g;
g.m_indices.resize(1);
for (uint32_t i = 0; i < q.get_training_vecs().size(); i++)
if (even_odd_input_pairs_equal)
{
g.m_total_weight = q.get_training_vecs()[i].second;
g.m_indices[0] = i;
g.m_indices.resize(2);
auto ins_res = unique_vecs.insert(std::make_pair(q.get_training_vecs()[i].first, g));
assert(q.get_training_vecs().size() >= 2 && (q.get_training_vecs().size() & 1) == 0);
if (!ins_res.second)
for (uint32_t i = 0; i < q.get_training_vecs().size(); i += 2)
{
(ins_res.first)->second.m_total_weight += g.m_total_weight;
(ins_res.first)->second.m_indices.push_back(i);
assert(q.get_training_vecs()[i].first == q.get_training_vecs()[i + 1].first);
g.m_total_weight = q.get_training_vecs()[i].second + q.get_training_vecs()[i + 1].second;
g.m_indices[0] = i;
g.m_indices[1] = i + 1;
auto ins_res = unique_vecs.insert(std::make_pair(q.get_training_vecs()[i].first, g));
if (!ins_res.second)
{
(ins_res.first)->second.m_total_weight += g.m_total_weight;
(ins_res.first)->second.m_indices.push_back(i);
(ins_res.first)->second.m_indices.push_back(i + 1);
}
}
}
else
{
g.m_indices.resize(1);
for (uint32_t i = 0; i < q.get_training_vecs().size(); i++)
{
g.m_total_weight = q.get_training_vecs()[i].second;
g.m_indices[0] = i;
auto ins_res = unique_vecs.insert(std::make_pair(q.get_training_vecs()[i].first, g));
if (!ins_res.second)
{
(ins_res.first)->second.m_total_weight += g.m_total_weight;
(ins_res.first)->second.m_indices.push_back(i);
}
}
}
//debug_printf("generate_hierarchical_codebook_threaded: %u training vectors, %u unique training vectors, %3.3f secs\n", q.get_total_training_vecs(), (uint32_t)unique_vecs.size(), tm.get_elapsed_secs());
debug_printf("generate_hierarchical_codebook_threaded: %u training vectors, %u unique training vectors\n", q.get_total_training_vecs(), (uint32_t)unique_vecs.size());
Quantizer group_quant;
@ -2491,7 +2746,27 @@ namespace basisu
return *this;
}
image &crop(uint32_t w, uint32_t h, uint32_t p = UINT32_MAX, const color_rgba &background = g_black_color)
// pPixels MUST have been allocated using malloc() (basisu::vector will eventually use free() on the pointer).
image& grant_ownership(color_rgba* pPixels, uint32_t w, uint32_t h, uint32_t p = UINT32_MAX)
{
if (p == UINT32_MAX)
p = w;
clear();
if ((!p) || (!w) || (!h))
return *this;
m_pixels.grant_ownership(pPixels, p * h, p * h);
m_width = w;
m_height = h;
m_pitch = p;
return *this;
}
image &crop(uint32_t w, uint32_t h, uint32_t p = UINT32_MAX, const color_rgba &background = g_black_color, bool init_image = true)
{
if (p == UINT32_MAX)
p = w;
@ -2509,15 +2784,25 @@ namespace basisu
cur_state.swap(m_pixels);
m_pixels.resize(p * h);
for (uint32_t y = 0; y < h; y++)
if (init_image)
{
for (uint32_t x = 0; x < w; x++)
if (m_width || m_height)
{
if ((x < m_width) && (y < m_height))
m_pixels[x + y * p] = cur_state[x + y * m_pitch];
else
m_pixels[x + y * p] = background;
for (uint32_t y = 0; y < h; y++)
{
for (uint32_t x = 0; x < w; x++)
{
if ((x < m_width) && (y < m_height))
m_pixels[x + y * p] = cur_state[x + y * m_pitch];
else
m_pixels[x + y * p] = background;
}
}
}
else
{
m_pixels.set_all(background);
}
}
@ -2590,9 +2875,25 @@ namespace basisu
const image &extract_block_clamped(color_rgba *pDst, uint32_t src_x, uint32_t src_y, uint32_t w, uint32_t h) const
{
for (uint32_t y = 0; y < h; y++)
for (uint32_t x = 0; x < w; x++)
*pDst++ = get_clamped(src_x + x, src_y + y);
if (((src_x + w) > m_width) || ((src_y + h) > m_height))
{
// Slower clamping case
for (uint32_t y = 0; y < h; y++)
for (uint32_t x = 0; x < w; x++)
*pDst++ = get_clamped(src_x + x, src_y + y);
}
else
{
const color_rgba* pSrc = &m_pixels[src_x + src_y * m_pitch];
for (uint32_t y = 0; y < h; y++)
{
memcpy(pDst, pSrc, w * sizeof(color_rgba));
pSrc += m_pitch;
pDst += w;
}
}
return *this;
}
@ -2947,21 +3248,18 @@ namespace basisu
};
// Image saving/loading/resampling
bool load_png(const uint8_t* pBuf, size_t buf_size, image& img, const char* pFilename = nullptr);
bool load_png(const char* pFilename, image& img);
inline bool load_png(const std::string &filename, image &img) { return load_png(filename.c_str(), img); }
bool load_bmp(const char* pFilename, image& img);
inline bool load_bmp(const std::string &filename, image &img) { return load_bmp(filename.c_str(), img); }
bool load_tga(const char* pFilename, image& img);
inline bool load_tga(const std::string &filename, image &img) { return load_tga(filename.c_str(), img); }
bool load_jpg(const char *pFilename, image& img);
inline bool load_jpg(const std::string &filename, image &img) { return load_jpg(filename.c_str(), img); }
// Currently loads .BMP, .PNG, or .TGA.
// Currently loads .PNG, .TGA, or .JPG
bool load_image(const char* pFilename, image& img);
inline bool load_image(const std::string &filename, image &img) { return load_image(filename.c_str(), img); }
@ -3129,6 +3427,29 @@ namespace basisu
}
void fill_buffer_with_random_bytes(void *pBuf, size_t size, uint32_t seed = 1);
const uint32_t cPixelBlockWidth = 4;
const uint32_t cPixelBlockHeight = 4;
const uint32_t cPixelBlockTotalPixels = cPixelBlockWidth * cPixelBlockHeight;
struct pixel_block
{
color_rgba m_pixels[cPixelBlockHeight][cPixelBlockWidth]; // [y][x]
inline const color_rgba& operator() (uint32_t x, uint32_t y) const { assert((x < cPixelBlockWidth) && (y < cPixelBlockHeight)); return m_pixels[y][x]; }
inline color_rgba& operator() (uint32_t x, uint32_t y) { assert((x < cPixelBlockWidth) && (y < cPixelBlockHeight)); return m_pixels[y][x]; }
inline const color_rgba* get_ptr() const { return &m_pixels[0][0]; }
inline color_rgba* get_ptr() { return &m_pixels[0][0]; }
inline void clear() { clear_obj(*this); }
inline bool operator== (const pixel_block& rhs) const
{
return memcmp(m_pixels, rhs.m_pixels, sizeof(m_pixels)) == 0;
}
};
typedef basisu::vector<pixel_block> pixel_block_vec;
} // namespace basisu

View file

@ -158,6 +158,22 @@ namespace basisu
}
}
}
#if 0
for (uint32_t y = 0; y < 64; y++)
{
printf("{");
for (uint32_t x = 0; x < 256; x++)
{
printf("0x%X", g_etc1_inverse_lookup[y][x]);
if (x != 255)
printf(",");
if (((x & 63) == 63) && (x != 255))
printf("\n");
}
printf("},\n");
}
#endif
}
// Packs solid color blocks efficiently using a set of small precomputed tables.
@ -1126,6 +1142,7 @@ namespace basisu
if (!g_eval_dist_tables[inten_table][m_max_comp_spread])
continue;
}
#if 0
if (m_pParams->m_quality <= cETCQualityMedium)
{

View file

@ -490,6 +490,35 @@ namespace basisu
return dc;
}
void get_block_colors_etc1s(color_rgba* pBlock_colors) const
{
color_rgba b;
unpack_color5(b, get_base5_color(), true);
const int* pInten_table = g_etc1_inten_tables[get_inten_table(0)];
pBlock_colors[0].set(clamp255(b.r + pInten_table[0]), clamp255(b.g + pInten_table[0]), clamp255(b.b + pInten_table[0]), 255);
pBlock_colors[1].set(clamp255(b.r + pInten_table[1]), clamp255(b.g + pInten_table[1]), clamp255(b.b + pInten_table[1]), 255);
pBlock_colors[2].set(clamp255(b.r + pInten_table[2]), clamp255(b.g + pInten_table[2]), clamp255(b.b + pInten_table[2]), 255);
pBlock_colors[3].set(clamp255(b.r + pInten_table[3]), clamp255(b.g + pInten_table[3]), clamp255(b.b + pInten_table[3]), 255);
}
static void get_block_colors_etc1s(color_rgba* pBlock_colors, const color_rgba &base5_color, uint32_t inten_table)
{
color_rgba b;
b.r = (base5_color.r << 3U) | (base5_color.r >> 2U);
b.g = (base5_color.g << 3U) | (base5_color.g >> 2U);
b.b = (base5_color.b << 3U) | (base5_color.b >> 2U);
const int* pInten_table = g_etc1_inten_tables[inten_table];
pBlock_colors[0].set(clamp255(b.r + pInten_table[0]), clamp255(b.g + pInten_table[0]), clamp255(b.b + pInten_table[0]), 255);
pBlock_colors[1].set(clamp255(b.r + pInten_table[1]), clamp255(b.g + pInten_table[1]), clamp255(b.b + pInten_table[1]), 255);
pBlock_colors[2].set(clamp255(b.r + pInten_table[2]), clamp255(b.g + pInten_table[2]), clamp255(b.b + pInten_table[2]), 255);
pBlock_colors[3].set(clamp255(b.r + pInten_table[3]), clamp255(b.g + pInten_table[3]), clamp255(b.b + pInten_table[3]), 255);
}
void get_block_color(color_rgba& color, uint32_t subblock_index, uint32_t selector_index) const
{
color_rgba b;

File diff suppressed because it is too large Load diff

View file

@ -16,12 +16,14 @@
#include "basisu_enc.h"
#include "basisu_etc.h"
#include "basisu_gpu_texture.h"
#include "basisu_global_selector_palette_helpers.h"
#include "../transcoder/basisu_file_headers.h"
#include "../transcoder/basisu_transcoder.h"
namespace basisu
{
struct opencl_context;
typedef opencl_context* opencl_context_ptr;
struct vec2U
{
uint32_t m_comps[2];
@ -51,7 +53,8 @@ namespace basisu
m_use_hierarchical_endpoint_codebooks(false),
m_use_hierarchical_selector_codebooks(false),
m_num_endpoint_codebook_iterations(0),
m_num_selector_codebook_iterations(0)
m_num_selector_codebook_iterations(0),
m_opencl_failed(false)
{
}
@ -73,19 +76,12 @@ namespace basisu
m_perceptual(true),
m_debug_stats(false),
m_debug_images(false),
m_dump_endpoint_clusterization(true),
m_validate(false),
m_multithreaded(false),
m_disable_hierarchical_endpoint_codebooks(false),
m_pGlobal_sel_codebook(NULL),
m_num_global_sel_codebook_pal_bits(0),
m_num_global_sel_codebook_mod_bits(0),
m_use_hybrid_selector_codebooks(false),
m_hybrid_codebook_quality_thresh(0.0f),
m_tex_type(basist::cBASISTexType2D),
m_pGlobal_codebooks(nullptr),
m_pOpenCL_context(nullptr),
m_pJob_pool(nullptr)
{
}
@ -106,13 +102,10 @@ namespace basisu
bool m_multithreaded;
bool m_disable_hierarchical_endpoint_codebooks;
const basist::etc1_global_selector_codebook *m_pGlobal_sel_codebook;
uint32_t m_num_global_sel_codebook_pal_bits;
uint32_t m_num_global_sel_codebook_mod_bits;
bool m_use_hybrid_selector_codebooks;
float m_hybrid_codebook_quality_thresh;
basist::basis_texture_type m_tex_type;
const basist::basisu_lowlevel_etc1s_transcoder *m_pGlobal_codebooks;
opencl_context_ptr m_pOpenCL_context;
job_pool *m_pJob_pool;
};
@ -150,10 +143,7 @@ namespace basisu
uint32_t get_total_selector_clusters() const { return static_cast<uint32_t>(m_selector_cluster_block_indices.size()); }
uint32_t get_block_selector_cluster_index(uint32_t block_index) const { return m_block_selector_cluster_index[block_index]; }
const etc_block &get_selector_cluster_selector_bits(uint32_t cluster_index) const { return m_optimized_cluster_selectors[cluster_index]; }
const basist::etc1_global_selector_codebook_entry_id_vec &get_selector_cluster_global_selector_entry_ids() const { return m_optimized_cluster_selector_global_cb_ids; }
const bool_vec &get_selector_cluster_uses_global_cb_vec() const { return m_selector_cluster_uses_global_cb; }
// Returns block indices using each selector cluster
const uint_vec &get_selector_cluster_block_indices(uint32_t selector_cluster_index) const { return m_selector_cluster_block_indices[selector_cluster_index]; }
@ -161,6 +151,8 @@ namespace basisu
void reoptimize_remapped_endpoints(const uint_vec &new_block_endpoints, int_vec &old_to_new_endpoint_cluster_indices, bool optimize_final_codebook, uint_vec *pBlock_selector_indices = nullptr);
bool get_opencl_failed() const { return m_opencl_failed; }
private:
params m_params;
uint32_t m_total_blocks;
@ -192,13 +184,14 @@ namespace basisu
vec6F_quantizer m_endpoint_clusterizer;
// For each endpoint cluster: An array of which subblock indices (block_index*2+subblock) are located in that cluster.
// Array of block indices for each endpoint cluster
basisu::vector<uint_vec> m_endpoint_clusters;
// Array of block indices for each parent endpoint cluster
// Array of subblock indices for each parent endpoint cluster
// Note: Initially, each endpoint cluster will only live in a single parent cluster, in a shallow tree.
// As the endpoint clusters are manipulated this constraint gets broken.
basisu::vector<uint_vec> m_endpoint_parent_clusters;
// Each block's parent cluster index
// Each block's parent endpoint cluster index
uint8_vec m_block_parent_endpoint_cluster;
// Array of endpoint cluster indices for each parent endpoint cluster
@ -295,10 +288,7 @@ namespace basisu
// Array of selector cluster indices for each parent selector cluster
basisu::vector<uint_vec> m_selector_clusters_within_each_parent_cluster;
basist::etc1_global_selector_codebook_entry_id_vec m_optimized_cluster_selector_global_cb_ids;
bool_vec m_selector_cluster_uses_global_cb;
// Each block's selector cluster index
basisu::vector<uint32_t> m_block_selector_cluster_index;
@ -330,6 +320,8 @@ namespace basisu
std::mutex m_lock;
bool m_opencl_failed;
//-----------------------------------------------------------------------------
void init_etc1_images();
@ -351,6 +343,7 @@ namespace basisu
void find_optimal_selector_clusters_for_each_block();
uint32_t refine_block_endpoints_given_selectors();
void finalize();
bool validate_endpoint_cluster_hierarchy(bool ensure_clusters_have_same_parents) const;
bool validate_output() const;
void introduce_special_selector_clusters();
void optimize_selector_codebook();

View file

@ -1,71 +0,0 @@
// basiu_global_selector_palette_helpers.cpp
// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "basisu_global_selector_palette_helpers.h"
namespace basisu
{
uint64_t etc1_global_selector_codebook_find_best_entry(const basist::etc1_global_selector_codebook &codebook,
uint32_t num_src_pixel_blocks, const pixel_block *pSrc_pixel_blocks, const etc_block *pBlock_endpoints,
uint32_t &palette_index, basist::etc1_global_palette_entry_modifier &palette_modifier,
bool perceptual, uint32_t max_pal_entries, uint32_t max_modifiers)
{
uint64_t best_err = UINT64_MAX;
uint32_t best_pal_index = 0;
basist::etc1_global_palette_entry_modifier best_pal_modifier;
if (!max_pal_entries)
max_pal_entries = codebook.size();
if (!max_modifiers)
max_modifiers = basist::etc1_global_palette_entry_modifier::cTotalValues;
for (uint32_t pal_index = 0; pal_index < max_pal_entries; pal_index++)
{
for (uint32_t mod_index = 0; mod_index < max_modifiers; mod_index++)
{
const basist::etc1_global_palette_entry_modifier pal_modifier(mod_index);
const basist::etc1_selector_palette_entry pal_entry(codebook.get_entry(pal_index, pal_modifier));
uint64_t trial_err = 0;
for (uint32_t block_index = 0; block_index < num_src_pixel_blocks; block_index++)
{
etc_block trial_block(pBlock_endpoints[block_index]);
for (uint32_t y = 0; y < 4; y++)
for (uint32_t x = 0; x < 4; x++)
trial_block.set_selector(x, y, pal_entry(x, y));
trial_err += trial_block.evaluate_etc1_error(reinterpret_cast<const basisu::color_rgba *>(pSrc_pixel_blocks[block_index].get_ptr()), perceptual);
if (trial_err >= best_err)
break;
}
if (trial_err < best_err)
{
best_err = trial_err;
best_pal_index = pal_index;
best_pal_modifier = pal_modifier;
}
} // mod_index
} // pal_index
palette_index = best_pal_index;
palette_modifier = best_pal_modifier;
return best_err;
}
} // namespace basisu

View file

@ -1,46 +0,0 @@
// File: basisu_global_selector_palette_helpers.h
// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "../transcoder/basisu.h"
#include "basisu_etc.h"
#include "../transcoder/basisu_global_selector_palette.h"
namespace basisu
{
const uint32_t cPixelBlockWidth = 4;
const uint32_t cPixelBlockHeight = 4;
const uint32_t cPixelBlockTotalPixels = cPixelBlockWidth * cPixelBlockHeight;
struct pixel_block
{
color_rgba m_pixels[cPixelBlockHeight][cPixelBlockWidth]; // [y][x]
const color_rgba &operator() (uint32_t x, uint32_t y) const { assert((x < cPixelBlockWidth) && (y < cPixelBlockHeight)); return m_pixels[y][x]; }
color_rgba &operator() (uint32_t x, uint32_t y) { assert((x < cPixelBlockWidth) && (y < cPixelBlockHeight)); return m_pixels[y][x]; }
const color_rgba *get_ptr() const { return &m_pixels[0][0]; }
color_rgba *get_ptr() { return &m_pixels[0][0]; }
void clear() { clear_obj(*this); }
};
typedef basisu::vector<pixel_block> pixel_block_vec;
uint64_t etc1_global_selector_codebook_find_best_entry(const basist::etc1_global_selector_codebook &codebook,
uint32_t num_src_pixel_blocks, const pixel_block *pSrc_pixel_blocks, const etc_block *pBlock_endpoints,
uint32_t &palette_index, basist::etc1_global_palette_entry_modifier &palette_modifier,
bool perceptual, uint32_t max_pal_entries, uint32_t max_modifiers);
} // namespace basisu

View file

@ -15,7 +15,9 @@
#include "basisu_gpu_texture.h"
#include "basisu_enc.h"
#include "basisu_pvrtc1_4.h"
#if BASISU_USE_ASTC_DECOMPRESS
#include "basisu_astc_decomp.h"
#endif
#include "basisu_bc7enc.h"
namespace basisu
@ -1150,8 +1152,12 @@ namespace basisu
}
case texture_format::cASTC4x4:
{
#if BASISU_USE_ASTC_DECOMPRESS
const bool astc_srgb = false;
basisu_astc::astc::decompress(reinterpret_cast<uint8_t*>(pPixels), static_cast<const uint8_t*>(pBlock), astc_srgb, 4, 4);
#else
memset(pPixels, 255, 16 * sizeof(color_rgba));
#endif
break;
}
case texture_format::cATC_RGB:
@ -1498,6 +1504,8 @@ namespace basisu
header.m_pixelWidth = width;
header.m_pixelHeight = height;
header.m_glTypeSize = 1;
header.m_glInternalFormat = internal_fmt;
header.m_glBaseInternalFormat = base_internal_fmt;

View file

@ -22,4 +22,6 @@ void CPPSPMD_NAME(find_selectors_linear_rgb_4_N)(int64_t* pDistance, uint8_t* pS
void CPPSPMD_NAME(find_lowest_error_perceptual_rgb_4_N)(int64_t* pDistance, const basisu::color_rgba* pBlock_colors, const basisu::color_rgba* pSrc_pixels, uint32_t n, int64_t early_out_error);
void CPPSPMD_NAME(find_lowest_error_linear_rgb_4_N)(int64_t* pDistance, const basisu::color_rgba* pBlock_colors, const basisu::color_rgba* pSrc_pixels, uint32_t n, int64_t early_out_error);
void CPPSPMD_NAME(update_covar_matrix_16x16)(uint32_t num_vecs, const void* pWeighted_vecs, const void *pOrigin, const uint32_t* pVec_indices, void *pMatrix16x16);
#endif

View file

@ -548,6 +548,65 @@ namespace CPPSPMD_NAME(basisu_kernels_namespace)
}
};
struct update_covar_matrix_16x16 : spmd_kernel
{
void _call(
uint32_t num_vecs, const void* pWeighted_vecs_void, const void* pOrigin_void, const uint32_t* pVec_indices, void* pMatrix16x16_void)
{
const std::pair<vec16F, uint64_t>* pWeighted_vecs = static_cast< const std::pair<vec16F, uint64_t> *>(pWeighted_vecs_void);
const float* pOrigin = static_cast<const float*>(pOrigin_void);
vfloat org0 = loadu_linear_all(pOrigin), org1 = loadu_linear_all(pOrigin + 4), org2 = loadu_linear_all(pOrigin + 8), org3 = loadu_linear_all(pOrigin + 12);
vfloat mat[16][4];
vfloat vzero(zero_vfloat());
for (uint32_t i = 0; i < 16; i++)
{
store_all(mat[i][0], vzero);
store_all(mat[i][1], vzero);
store_all(mat[i][2], vzero);
store_all(mat[i][3], vzero);
}
for (uint32_t k = 0; k < num_vecs; k++)
{
const uint32_t vec_index = pVec_indices[k];
const float* pW = pWeighted_vecs[vec_index].first.get_ptr();
vfloat weight((float)pWeighted_vecs[vec_index].second);
vfloat vec[4] = { loadu_linear_all(pW) - org0, loadu_linear_all(pW + 4) - org1, loadu_linear_all(pW + 8) - org2, loadu_linear_all(pW + 12) - org3 };
vfloat wvec0 = vec[0] * weight, wvec1 = vec[1] * weight, wvec2 = vec[2] * weight, wvec3 = vec[3] * weight;
for (uint32_t j = 0; j < 16; j++)
{
vfloat vx = ((const float*)vec)[j];
store_all(mat[j][0], mat[j][0] + vx * wvec0);
store_all(mat[j][1], mat[j][1] + vx * wvec1);
store_all(mat[j][2], mat[j][2] + vx * wvec2);
store_all(mat[j][3], mat[j][3] + vx * wvec3);
} // j
} // k
float* pMatrix = static_cast<float*>(pMatrix16x16_void);
float* pDst = pMatrix;
for (uint32_t i = 0; i < 16; i++)
{
storeu_linear_all(pDst, mat[i][0]);
storeu_linear_all(pDst + 4, mat[i][1]);
storeu_linear_all(pDst + 8, mat[i][2]);
storeu_linear_all(pDst + 12, mat[i][3]);
pDst += 16;
}
}
};
} // namespace
using namespace CPPSPMD_NAME(basisu_kernels_namespace);
@ -582,3 +641,7 @@ void CPPSPMD_NAME(find_lowest_error_linear_rgb_4_N)(int64_t* pDistance, const co
spmd_call< find_lowest_error_linear_rgb_4_N >(pDistance, pBlock_colors, pSrc_pixels, n, early_out_error);
}
void CPPSPMD_NAME(update_covar_matrix_16x16)(uint32_t num_vecs, const void* pWeighted_vecs, const void* pOrigin, const uint32_t *pVec_indices, void* pMatrix16x16)
{
spmd_call < update_covar_matrix_16x16 >(num_vecs, pWeighted_vecs, pOrigin, pVec_indices, pMatrix16x16);
}

View file

@ -32,7 +32,7 @@
#error SSE4.1/SSE3/SSE4.2/SSSE3 cannot be enabled to use this file
#endif
#else
#if !__SSE4_1__ || !__SSE3__ || __SSE4_2__ || !__SSSE3__
#if !__SSE4_1__ || !__SSE3__ || !__SSSE3__
#error Please check your compiler options
#endif
#endif

View file

@ -76,6 +76,14 @@
#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1
#endif
// Using unaligned loads and stores causes errors when using UBSan. Jam it off.
#if defined(__has_feature)
#if __has_feature(undefined_behavior_sanitizer)
#undef MINIZ_USE_UNALIGNED_LOADS_AND_STORES
#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0
#endif
#endif
#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__)
// Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions).
#define MINIZ_HAS_64BIT_REGISTERS 1
@ -236,6 +244,7 @@ int mz_inflateInit2(mz_streamp pStream, int window_bits);
// MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again
// with more input data, or with more room in the output buffer (except when using single call decompression, described above).
int mz_inflate(mz_streamp pStream, int flush);
int mz_inflate2(mz_streamp pStream, int flush, int adler32_checking);
// Deinitializes a decompressor.
int mz_inflateEnd(mz_streamp pStream);
@ -880,10 +889,10 @@ int mz_inflateInit(mz_streamp pStream)
return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS);
}
int mz_inflate(mz_streamp pStream, int flush)
int mz_inflate2(mz_streamp pStream, int flush, int adler32_checking)
{
inflate_state* pState;
mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32;
mz_uint n, first_call, decomp_flags = adler32_checking ? TINFL_FLAG_COMPUTE_ADLER32 : 0;
size_t in_bytes, out_bytes, orig_avail_in;
tinfl_status status;
@ -971,6 +980,11 @@ int mz_inflate(mz_streamp pStream, int flush)
return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK;
}
int mz_inflate(mz_streamp pStream, int flush)
{
return mz_inflate2(pStream, flush, MZ_TRUE);
}
int mz_inflateEnd(mz_streamp pStream)
{
if (!pStream)
@ -1348,7 +1362,8 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex
common_exit:
r->m_num_bits = num_bits; r->m_bit_buf = bit_buf; r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start;
*pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next;
if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0))
//if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0))
if ((decomp_flags & TINFL_FLAG_COMPUTE_ADLER32) && (status >= 0))
{
const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size;
mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552;
@ -1362,7 +1377,9 @@ common_exit:
for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1;
s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552;
}
r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH;
r->m_check_adler32 = (s2 << 16) + s1;
if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32))
status = TINFL_STATUS_ADLER32_MISMATCH;
}
return status;
}
@ -2479,7 +2496,7 @@ void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int
// write dummy header
for (z = 41; z; --z) tdefl_output_buffer_putter(&z, 1, &out_buf);
// compress image data
tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER);
tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER | (level <= 3 ? TDEFL_GREEDY_PARSING_FLAG : 0));
for (y = 0; y < h; ++y) { tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); tdefl_compress_buffer(pComp, (mz_uint8*)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); }
if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) { MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; }
// write real header

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,143 @@
// basisu_opencl.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
//
// Note: Undefine or set BASISU_SUPPORT_OPENCL to 0 to completely OpenCL support.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "../transcoder/basisu.h"
#include "basisu_enc.h"
#include "basisu_etc.h"
namespace basisu
{
bool opencl_init(bool force_serialization);
void opencl_deinit();
bool opencl_is_available();
struct opencl_context;
// Each thread calling OpenCL should have its own opencl_context_ptr. This corresponds to a OpenCL command queue. (Confusingly, we only use a single OpenCL device "context".)
typedef opencl_context* opencl_context_ptr;
opencl_context_ptr opencl_create_context();
void opencl_destroy_context(opencl_context_ptr context);
#pragma pack(push, 1)
struct cl_pixel_block
{
color_rgba m_pixels[16]; // [y*4+x]
};
#pragma pack(pop)
// Must match BASISU_ETC1_CLUSTER_FIT_ORDER_TABLE_SIZE
const uint32_t OPENCL_ENCODE_ETC1S_MAX_PERMS = 165;
bool opencl_set_pixel_blocks(opencl_context_ptr pContext, uint32_t total_blocks, const cl_pixel_block* pPixel_blocks);
bool opencl_encode_etc1s_blocks(opencl_context_ptr pContext, etc_block* pOutput_blocks, bool perceptual, uint32_t total_perms);
// opencl_encode_etc1s_pixel_clusters
#pragma pack(push, 1)
struct cl_pixel_cluster
{
uint64_t m_total_pixels;
uint64_t m_first_pixel_index;
};
#pragma pack(pop)
bool opencl_encode_etc1s_pixel_clusters(
opencl_context_ptr pContext,
etc_block* pOutput_blocks,
uint32_t total_clusters,
const cl_pixel_cluster *pClusters,
uint64_t total_pixels,
const color_rgba *pPixels,
const uint32_t *pPixel_weights,
bool perceptual, uint32_t total_perms);
// opencl_refine_endpoint_clusterization
#pragma pack(push, 1)
struct cl_block_info_struct
{
uint16_t m_first_cluster_ofs;
uint16_t m_num_clusters;
uint16_t m_cur_cluster_index;
uint8_t m_cur_cluster_etc_inten;
};
struct cl_endpoint_cluster_struct
{
color_rgba m_unscaled_color;
uint8_t m_etc_inten;
uint16_t m_cluster_index;
};
#pragma pack(pop)
bool opencl_refine_endpoint_clusterization(
opencl_context_ptr pContext,
const cl_block_info_struct *pPixel_block_info,
uint32_t total_clusters,
const cl_endpoint_cluster_struct *pCluster_info,
const uint32_t *pSorted_block_indices,
uint32_t* pOutput_cluster_indices,
bool perceptual);
// opencl_find_optimal_selector_clusters_for_each_block
#pragma pack(push, 1)
struct fosc_selector_struct
{
uint32_t m_packed_selectors; // 4x4 grid of 2-bit selectors
};
struct fosc_block_struct
{
color_rgba m_etc_color5_inten; // unscaled 5-bit block color in RGB, alpha has block's intensity index
uint32_t m_first_selector; // offset into selector table
uint32_t m_num_selectors; // number of selectors to check
};
struct fosc_param_struct
{
uint32_t m_total_blocks;
int m_perceptual;
};
#pragma pack(pop)
bool opencl_find_optimal_selector_clusters_for_each_block(
opencl_context_ptr pContext,
const fosc_block_struct* pInput_block_info, // one per block
uint32_t total_input_selectors,
const fosc_selector_struct* pInput_selectors,
const uint32_t* pSelector_cluster_indices,
uint32_t* pOutput_selector_cluster_indices, // one per block
bool perceptual);
#pragma pack(push, 1)
struct ds_param_struct
{
uint32_t m_total_blocks;
int m_perceptual;
};
#pragma pack(pop)
bool opencl_determine_selectors(
opencl_context_ptr pContext,
const color_rgba* pInput_etc_color5_and_inten,
etc_block* pOutput_blocks,
bool perceptual);
} // namespace basisu

View file

@ -13,7 +13,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "basisu_uastc_enc.h"
#if BASISU_USE_ASTC_DECOMPRESS
#include "basisu_astc_decomp.h"
#endif
#include "basisu_gpu_texture.h"
#include "basisu_bc7enc.h"
@ -509,14 +513,14 @@ namespace basisu
if (pForce_selectors == nullptr)
{
int s0 = g_astc_unquant[endpoint_range][astc_results.m_endpoints[0]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[2]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[4]].m_unquant;
int s1 = g_astc_unquant[endpoint_range][astc_results.m_endpoints[1]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[3]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[5]].m_unquant;
if (s1 < s0)
{
std::swap(astc_results.m_endpoints[0], astc_results.m_endpoints[1]);
std::swap(astc_results.m_endpoints[2], astc_results.m_endpoints[3]);
std::swap(astc_results.m_endpoints[4], astc_results.m_endpoints[5]);
invert = true;
int s0 = g_astc_unquant[endpoint_range][astc_results.m_endpoints[0]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[2]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[4]].m_unquant;
int s1 = g_astc_unquant[endpoint_range][astc_results.m_endpoints[1]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[3]].m_unquant + g_astc_unquant[endpoint_range][astc_results.m_endpoints[5]].m_unquant;
if (s1 < s0)
{
std::swap(astc_results.m_endpoints[0], astc_results.m_endpoints[1]);
std::swap(astc_results.m_endpoints[2], astc_results.m_endpoints[3]);
std::swap(astc_results.m_endpoints[4], astc_results.m_endpoints[5]);
invert = true;
}
}
@ -3562,7 +3566,8 @@ namespace basisu
basist::color32 temp_block_unpacked[4][4];
success = basist::unpack_uastc(temp_block, (basist::color32 *)temp_block_unpacked, false);
VALIDATE(success);
#if BASISU_USE_ASTC_DECOMPRESS
// Now round trip to packed ASTC and back, then decode to pixels.
uint32_t astc_data[4];
@ -3590,6 +3595,7 @@ namespace basisu
VALIDATE(temp_block_unpacked[y][x].c[3] == decoded_uastc_block[y][x].a);
}
}
#endif
}
#endif

View file

@ -1,13 +1,5 @@
// cppspmd_sse.h
// Note for Basis Universal: All of the "cppspmd" code and headers are OPTIONAL to Basis Universal. if BASISU_SUPPORT_SSE is 0, it will never be included and does not impact compilation.
// SSE 2 or 4.1
// Originally written by Nicolas Guillemot, Jefferson Amstutz in the "CppSPMD" project.
// 4/20: Richard Geldreich: Macro control flow, more SIMD instruction sets, optimizations, supports using multiple SIMD instruction sets in same executable. Still a work in progress!
//
// Originally Copyright 2016 Nicolas Guillemot
// Changed from the MIT license to Apache 2.0 with permission from the author.
//
// Modifications/enhancements Copyright 2020-2021 Binomial LLC
// Copyright 2020-2022 Binomial LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -20,6 +12,11 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Notes for Basis Universal:
// All of the "cppspmd" code and headers are OPTIONAL to Basis Universal. if BASISU_SUPPORT_SSE is 0, it will never be included and does not impact compilation.
// The techniques used in this code were originally demonstrated for AVX2 by Nicolas Guillemot, Jefferson Amstutz in their "CppSPMD" project.
// This is new code for use in Basis Universal, although it uses the same general SPMD techniques in SSE 2/4.
#include <stdlib.h>
#include <stdint.h>
@ -1330,7 +1327,7 @@ struct spmd_kernel
__m128 temp = _mm_add_ps(_mm_shuffle_ps(k3210, k3210, _MM_SHUFFLE(0, 1, 2, 3)), k3210);
return _mm_cvtss_f32(_mm_add_ss(_mm_movehl_ps(temp, temp), temp));
}
CPPSPMD_FORCE_INLINE int reduce_add(vint v)
{
__m128i k3210 = blendv_mask_epi32(_mm_setzero_si128(), v.m_value, m_exec.m_mask);
@ -1668,14 +1665,16 @@ CPPSPMD_FORCE_INLINE vint uniform_shift_right_epi16(const vint& a, const vint& b
CPPSPMD_FORCE_INLINE vint undefined_vint() { return vint{ _mm_undefined_si128() }; }
CPPSPMD_FORCE_INLINE vfloat undefined_vfloat() { return vfloat{ _mm_undefined_ps() }; }
CPPSPMD_FORCE_INLINE vint zero_vint() { return vint{ _mm_setzero_si128() }; }
CPPSPMD_FORCE_INLINE vfloat zero_vfloat() { return vfloat{ _mm_setzero_ps() }; }
CPPSPMD_FORCE_INLINE vint vint_lane_set(int v0, int v1, int v2, int v3) { return vint{ _mm_set_epi32(v3, v2, v1, v0) }; }
CPPSPMD_FORCE_INLINE vfloat vfloat_lane_set(float v0, float v1, float v2, float v3) { return vfloat{ _mm_set_ps(v3, v2, v1, v0) }; }
CPPSPMD_FORCE_INLINE vint vint_lane_set_r(int v3, int v2, int v1, int v0) { return vint{ _mm_set_epi32(v3, v2, v1, v0) }; }
CPPSPMD_FORCE_INLINE vfloat vfloat_lane_set_r(float v3, float v2, float v1, float v0) { return vfloat{ _mm_set_ps(v3, v2, v1, v0) }; }
// control is an 8-bit immediate value containing 4 2-bit indices which shuffles the int32's in each 128-bit lane.
#define VINT_LANE_SHUFFLE_EPI32(a, control) vint(_mm_shuffle_epi32((a).m_value, control))
#define VFLOAT_LANE_SHUFFLE_PS(a, b, control) vfloat(_mm_shuffle_ps((a).m_value, (b).m_value, control))
// control is an 8-bit immediate value containing 4 2-bit indices which shuffles the int16's in either the high or low 64-bit lane.
#define VINT_LANE_SHUFFLELO_EPI16(a, control) vint(_mm_shufflelo_epi16((a).m_value, control))

View file

@ -23,17 +23,6 @@
// v1.04, May. 19, 2012: Code tweaks to fix VS2008 static code analysis warnings
// v2.00, March 20, 2020: Fuzzed with zzuf and afl. Fixed several issues, converted most assert()'s to run-time checks. Added chroma upsampling. Removed freq. domain upsampling. gcc/clang warnings.
//
#ifdef _MSC_VER
#ifndef BASISU_NO_ITERATOR_DEBUG_LEVEL
#if defined(_DEBUG) || defined(DEBUG)
#define _ITERATOR_DEBUG_LEVEL 1
#define _SECURE_SCL 1
#else
#define _SECURE_SCL 0
#define _ITERATOR_DEBUG_LEVEL 0
#endif
#endif
#endif
#include "jpgd.h"
#include <string.h>
@ -2085,7 +2074,7 @@ namespace jpgd {
if (setjmp(m_jmp_state))
return JPGD_FAILED;
const bool chroma_y_filtering = (m_flags & cFlagLinearChromaFiltering) && ((m_scan_type == JPGD_YH2V2) || (m_scan_type == JPGD_YH1V2));
const bool chroma_y_filtering = (m_flags & cFlagLinearChromaFiltering) && ((m_scan_type == JPGD_YH2V2) || (m_scan_type == JPGD_YH1V2)) && (m_image_x_size >= 2) && (m_image_y_size >= 2);
if (chroma_y_filtering)
{
std::swap(m_pSample_buf, m_pSample_buf_prev);
@ -2114,7 +2103,7 @@ namespace jpgd {
if (m_total_lines_left == 0)
return JPGD_DONE;
const bool chroma_y_filtering = (m_flags & cFlagLinearChromaFiltering) && ((m_scan_type == JPGD_YH2V2) || (m_scan_type == JPGD_YH1V2));
const bool chroma_y_filtering = (m_flags & cFlagLinearChromaFiltering) && ((m_scan_type == JPGD_YH2V2) || (m_scan_type == JPGD_YH1V2)) && (m_image_x_size >= 2) && (m_image_y_size >= 2);
bool get_another_mcu_row = false;
bool got_mcu_early = false;
@ -2144,7 +2133,7 @@ namespace jpgd {
{
case JPGD_YH2V2:
{
if (m_flags & cFlagLinearChromaFiltering)
if ((m_flags & cFlagLinearChromaFiltering) && (m_image_x_size >= 2) && (m_image_y_size >= 2))
{
if (m_num_buffered_scanlines == 1)
{
@ -2173,7 +2162,7 @@ namespace jpgd {
}
case JPGD_YH2V1:
{
if (m_flags & cFlagLinearChromaFiltering)
if ((m_flags & cFlagLinearChromaFiltering) && (m_image_x_size >= 2) && (m_image_y_size >= 2))
H2V1ConvertFiltered();
else
H2V1Convert();

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,48 @@
// pngreader.h - Public Domain - see unlicense at bottom of pvpngreader.cpp
#pragma once
#include <stdint.h>
namespace pv_png
{
// PNG color types
enum
{
PNG_COLOR_TYPE_GREYSCALE = 0,
PNG_COLOR_TYPE_TRUECOLOR = 2,
PNG_COLOR_TYPE_PALETTIZED = 3,
PNG_COLOR_TYPE_GREYSCALE_ALPHA = 4,
PNG_COLOR_TYPE_TRUECOLOR_ALPHA = 6
};
// PNG file description
struct png_info
{
uint32_t m_width;
uint32_t m_height;
uint32_t m_num_chans; // The number of channels, factoring in transparency. Ranges from [1-4].
uint32_t m_bit_depth; // PNG ihdr bit depth: 1, 2, 4, 8 or 16
uint32_t m_color_type; // PNG ihdr color type, PNG_COLOR_TYPE_GRAYSCALE etc.
bool m_has_gamma; // true if the PNG file had a GAMA chunk
uint32_t m_gamma_value; // PNG GAMA chunk value, scaled by 100000
bool m_has_trns; // true if the PNG file used colorkey transparency
};
// Retrieved information about the PNG file.
// Returns false on any errors.
bool get_png_info(const void* pImage_buf, size_t buf_size, png_info& info);
// Input parameters:
// pImage_buf, buf_size - pointer to PNG image data
// desired_chans - desired number of output channels. 0=auto, 1=grayscale, 2=grayscale alpha, 3=24bpp RGB, 4=32bpp RGBA
//
// Output parameters:
// width, height - PNG image resolution
// num_chans - actual number of channels in PNG, from [1,4] (factoring in transparency)
//
// Returns nullptr on any errors.
void* load_png(const void* pImage_buf, size_t buf_size, uint32_t desired_chans, uint32_t &width, uint32_t &height, uint32_t& num_chans);
}

View file

@ -21,6 +21,11 @@
#pragma warning (disable : 4127) // warning C4127: conditional expression is constant
#pragma warning (disable : 4530) // C++ exception handler used, but unwind semantics are not enabled.
// Slamming this off always for v1.16 because we've gotten rid of most std containers.
#ifndef BASISU_NO_ITERATOR_DEBUG_LEVEL
#define BASISU_NO_ITERATOR_DEBUG_LEVEL (1)
#endif
#ifndef BASISU_NO_ITERATOR_DEBUG_LEVEL
//#define _HAS_ITERATOR_DEBUGGING 0
@ -165,6 +170,10 @@ namespace basisu
{
if (n)
{
if (vec.size())
{
assert((pObjs + n) <= vec.begin() || (pObjs >= vec.end()));
}
const size_t cur_s = vec.size();
vec.resize(cur_s + n);
memcpy(&vec[cur_s], pObjs, sizeof(R) * n);
@ -173,6 +182,7 @@ namespace basisu
template<typename T> inline void append_vector(T &vec, const T &other_vec)
{
assert(&vec != &other_vec);
if (other_vec.size())
append_vector(vec, &other_vec[0], other_vec.size());
}

View file

@ -12,6 +12,12 @@
#define HAS_MALLOC_USABLE_SIZE 1
#endif
// Set to 1 to always check vector operator[], front(), and back() even in release.
#define BASISU_VECTOR_FORCE_CHECKING 0
// If 1, the vector container will not query the CRT to get the size of resized memory blocks.
#define BASISU_VECTOR_DETERMINISTIC 1
#ifdef _MSC_VER
#define BASISU_FORCE_INLINE __forceinline
#else
@ -279,7 +285,10 @@ namespace basisu
m_size = other.m_size;
if (BASISU_IS_BITWISE_COPYABLE(T))
memcpy(m_p, other.m_p, m_size * sizeof(T));
{
if ((m_p) && (other.m_p))
memcpy(m_p, other.m_p, m_size * sizeof(T));
}
else
{
T* pDst = m_p;
@ -320,7 +329,10 @@ namespace basisu
}
if (BASISU_IS_BITWISE_COPYABLE(T))
memcpy(m_p, other.m_p, other.m_size * sizeof(T));
{
if ((m_p) && (other.m_p))
memcpy(m_p, other.m_p, other.m_size * sizeof(T));
}
else
{
T* pDst = m_p;
@ -348,20 +360,81 @@ namespace basisu
// operator[] will assert on out of range indices, but in final builds there is (and will never be) any range checking on this method.
//BASISU_FORCE_INLINE const T& operator[] (uint32_t i) const { assert(i < m_size); return m_p[i]; }
//BASISU_FORCE_INLINE T& operator[] (uint32_t i) { assert(i < m_size); return m_p[i]; }
#if !BASISU_VECTOR_FORCE_CHECKING
BASISU_FORCE_INLINE const T& operator[] (size_t i) const { assert(i < m_size); return m_p[i]; }
BASISU_FORCE_INLINE T& operator[] (size_t i) { assert(i < m_size); return m_p[i]; }
#else
BASISU_FORCE_INLINE const T& operator[] (size_t i) const
{
if (i >= m_size)
{
fprintf(stderr, "operator[] invalid index: %u, max entries %u, type size %u\n", (uint32_t)i, m_size, (uint32_t)sizeof(T));
abort();
}
return m_p[i];
}
BASISU_FORCE_INLINE T& operator[] (size_t i)
{
if (i >= m_size)
{
fprintf(stderr, "operator[] invalid index: %u, max entries %u, type size %u\n", (uint32_t)i, m_size, (uint32_t)sizeof(T));
abort();
}
return m_p[i];
}
#endif
// at() always includes range checking, even in final builds, unlike operator [].
// The first element is returned if the index is out of range.
BASISU_FORCE_INLINE const T& at(size_t i) const { assert(i < m_size); return (i >= m_size) ? m_p[0] : m_p[i]; }
BASISU_FORCE_INLINE T& at(size_t i) { assert(i < m_size); return (i >= m_size) ? m_p[0] : m_p[i]; }
#if !BASISU_VECTOR_FORCE_CHECKING
BASISU_FORCE_INLINE const T& front() const { assert(m_size); return m_p[0]; }
BASISU_FORCE_INLINE T& front() { assert(m_size); return m_p[0]; }
BASISU_FORCE_INLINE const T& back() const { assert(m_size); return m_p[m_size - 1]; }
BASISU_FORCE_INLINE T& back() { assert(m_size); return m_p[m_size - 1]; }
#else
BASISU_FORCE_INLINE const T& front() const
{
if (!m_size)
{
fprintf(stderr, "front: vector is empty, type size %u\n", (uint32_t)sizeof(T));
abort();
}
return m_p[0];
}
BASISU_FORCE_INLINE T& front()
{
if (!m_size)
{
fprintf(stderr, "front: vector is empty, type size %u\n", (uint32_t)sizeof(T));
abort();
}
return m_p[0];
}
BASISU_FORCE_INLINE const T& back() const
{
if(!m_size)
{
fprintf(stderr, "back: vector is empty, type size %u\n", (uint32_t)sizeof(T));
abort();
}
return m_p[m_size - 1];
}
BASISU_FORCE_INLINE T& back()
{
if (!m_size)
{
fprintf(stderr, "back: vector is empty, type size %u\n", (uint32_t)sizeof(T));
abort();
}
return m_p[m_size - 1];
}
#endif
BASISU_FORCE_INLINE const T* get_ptr() const { return m_p; }
BASISU_FORCE_INLINE T* get_ptr() { return m_p; }
@ -952,6 +1025,8 @@ namespace basisu
// Caller is granting ownership of the indicated heap block.
// Block must have size constructed elements, and have enough room for capacity elements.
// The block must have been allocated using malloc().
// Important: This method is used in Basis Universal. If you change how this container allocates memory, you'll need to change any users of this method.
inline bool grant_ownership(T* p, uint32_t size, uint32_t capacity)
{
// To to prevent the caller from obviously shooting themselves in the foot.

View file

@ -55,7 +55,9 @@ namespace basisu
abort();
}
#ifdef _MSC_VER
#if BASISU_VECTOR_DETERMINISTIC
actual_size = desired_size;
#elif defined(_MSC_VER)
actual_size = _msize(new_p);
#elif HAS_MALLOC_USABLE_SIZE
actual_size = malloc_usable_size(new_p);
@ -82,7 +84,9 @@ namespace basisu
abort();
}
#ifdef _MSC_VER
#if BASISU_VECTOR_DETERMINISTIC
actual_size = desired_size;
#elif defined(_MSC_VER)
actual_size = _msize(new_p);
#elif HAS_MALLOC_USABLE_SIZE
actual_size = malloc_usable_size(new_p);

View file

@ -1,272 +0,0 @@
// Copyright (C) 2019-2020 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
{
0x0, 0x505, 0x5555, 0x5F5F, 0x5055050, 0x5055F5F, 0x50AAA551, 0xFAA5A5AA, 0x6AAA5095, 0x41E6FBAB, 0x19AE99F5, 0x1057AAA4, 0x54005A1A, 0x4459AEAF, 0x56015B, 0xBAA9A554,
0x4335E5E0, 0xD9FE5FBB, 0x2525256A, 0x9AE892, 0xC0D5FAF5, 0x5BA5E641, 0x7EDEC8B8, 0xBB671211, 0x4C9844EE, 0xEE042415, 0xE5663EAE, 0x90909091, 0xAAA45AFF, 0x15556E1D, 0xA6959195, 0x4BFF8BF,
0x5166AAF, 0x15490065, 0x6F5BAFAF, 0xFF00FF00, 0xD96956AA, 0x15AF6B, 0xFF5A00AA, 0xE0E557AA, 0x1A6F19BD, 0x69655555, 0xD0500158, 0xEEEDD894, 0xE4E4FE, 0xC71B7B10, 0x55AA5AAF, 0x50AA59BE,
0xE4E990E4, 0x5353B63B, 0xFEE5D0E4, 0x96AF051A, 0x3CC95A6, 0x70B5A40D, 0x9504196E, 0x4A0BD7A3, 0x11B89592, 0xAAFF4095, 0x55A5D4E0, 0xBBA55050, 0x1111666, 0xA5544000, 0xED994444, 0x5A56BF,
0x94A954B9, 0xFB651000, 0x604E633E, 0x14291A15, 0x56965956, 0xB8E0D0C0, 0x5A565A55, 0x65A61A6A, 0xE490F990, 0xCA87AAF5, 0x6060A0B, 0x24C23143, 0x55AA9A40, 0x505E1605, 0xCEC0486E, 0x156E55EA,
0x79978B0B, 0x4595F53C, 0x405C4AF7, 0xC1897D75, 0xD5F40BA6, 0x95444017, 0x14AD6935, 0x87C7A7BD, 0x4A4E8597, 0xFF1D7E55, 0x451400F9, 0x1112277B, 0x9A0590F8, 0x53E3492E, 0xE590E995, 0x7E730A9A,
0x929697E7, 0x2E781609, 0xE22317A1, 0xEDE9D884, 0xDDD75CDD, 0xAF1B6F1A, 0xE6909047, 0xA77DAD5D, 0x184C0D5D, 0xFAB56010, 0x5EA4F1D0, 0x11166B6B, 0xF51A7AD6, 0xF79950F4, 0x1B6B1B11, 0x9A6D6469,
0x441997E, 0x4546869A, 0x95AA6965, 0x155A6A, 0x6E68B0E6, 0x5A55A665, 0x1B051605, 0x601D8BE6, 0xBD2F1B06, 0x409A429B, 0x23272721, 0xB07454A9, 0x7E66A3A1, 0x1B6A5500, 0xA0E0F5A6, 0xBF5A0500,
0x55A5A9A9, 0x99D9E995, 0xE440566F, 0x6550BE99, 0x2267777B, 0xFA50FE50, 0xA657B441, 0xB4E29343, 0x555090E5, 0x45465B6B, 0xE654E6, 0xEA90469B, 0x2E05D2F4, 0x99594444, 0xF1C20746, 0x295AD2E0,
0xF990EA95, 0x804459AE, 0xA9999894, 0x1F41E4A5, 0x4040E5E5, 0x5481E1F2, 0x2AFF59F1, 0x6B6B1712, 0xA7131051, 0xF9406F16, 0x1B2B5B9E, 0x587E0F2F, 0x547E1919, 0xD0F5645B, 0xB1B1B1B, 0x5756A4FE,
0x46A9B965, 0x1529F99D, 0xE490E490, 0x4495FE, 0x985E0B06, 0x5FD2D23A, 0x5D0E95A, 0xF69103F4, 0x4029790, 0x1B062F1B, 0xEE594500, 0xB6539B5A, 0x106165BA, 0xD26B7C8D, 0x8B2A25A5, 0x55EAD5E3,
0x431FB8E1, 0xBEB4E646, 0x9A5A4545, 0x5015A6B, 0x90D4B83D, 0xDB8A99A4, 0x9E894905, 0xDD7D1101, 0xA95E00BF, 0x579FA5A5, 0xA292D145, 0x93292C96, 0xF9A995A5, 0xBFE8A450, 0xB990D15B, 0x45D1E01A,
0x4BD3F767, 0xF243479A, 0x7E420927, 0xF9E5E090, 0xA1C869F, 0x253A36, 0x9BAB569A, 0x4147031F, 0xA059AFE, 0xE0D6590F, 0xD5EAD5E6, 0x9A4B4641, 0x5AAA4406, 0x55EA90E4, 0x10179BC4, 0x44485999,
0x5156253E, 0x1F29E054, 0xCDDAA773, 0x5601AB05, 0x94FC94C0, 0x116166BB, 0xBF964006, 0x414196EB, 0x8498D9ED, 0xB5E08687, 0xBD564150, 0x2B8D9DF8, 0x7F12017E, 0x90904747, 0x50B56AB, 0xDBD19490,
0xBB5A5659, 0xBAF40E4, 0x6D649014, 0x1D29166F, 0x414F3D75, 0x6F929540, 0x565AAF05, 0xBD9884E5, 0xF5342A25, 0x157915AE, 0x1A055A55, 0x9019A19F, 0x64B96A05, 0x35689CCC, 0x996012E2, 0x5252677B,
0x156AA401, 0x25BCE483, 0xAA665555, 0xD6AF4B0F, 0x3F4BBDE0, 0x9404A9AF, 0xA590F9E4, 0x8191A5FD, 0x568190B4, 0x591A6616, 0x92C11D3E, 0x97D2E5FC, 0xF5A55A6F, 0xBEE0969B, 0x8918B4CA, 0xE0915397,
0x5243472F, 0x95EA4055, 0x55E6E0A4, 0x9AEBD181, 0xF4A25357, 0x11115666, 0xFE45FF0A, 0x8BC7D2E1, 0x800556BB, 0x757D6A96, 0xFA909A5B, 0x68962FDB, 0xEB0056AA, 0x69970241, 0xAA58AD64, 0xC4D9DED5,
0x5A5BF2F0, 0xBD0905B4, 0x197D7801, 0x8987EDC4, 0xFF40565A, 0x460978A4, 0xE4067FE4, 0x5DA23153, 0xB90565AE, 0x5E14B946, 0x4E35879F, 0xC72F8666, 0x1816472F, 0x9A5A4949, 0x64A0D1E5, 0xC7025B1A,
0x1B061B55, 0xFFAA051B, 0xAF5DEDA1, 0xAA955094, 0x6659965A, 0x99A95DAD, 0x9450A5A5, 0xA550A595, 0x6914B950, 0xEF454944, 0x906BB990, 0xD680944B, 0xE091461B, 0x5363B7BB, 0xF0743906, 0x66566A69,
0x4B85D0BC, 0x40E494A5, 0x1161B6B6, 0x519BD59, 0x5998401, 0x1651F26B, 0x5709BB1B, 0x6AE1D1B9, 0xD19297BF, 0x1A69FEE4, 0x6066B5A, 0x74A56491, 0xB4661151, 0x559191A4, 0x96756A68, 0xF5C791A6,
0x20297A15, 0x6B660100, 0x313177A2, 0x55054150, 0x6A969669, 0xF0B82111, 0x555A6996, 0xB666295A, 0x1EA95441, 0x6A166BA5, 0x8C18566D, 0x2797278A, 0x82A552BD, 0xF964BD14, 0x41540668, 0x5078785A,
0x5754FE, 0xF9E0E5FA, 0x15453D3F, 0x5A9699A9, 0xD9854147, 0x849494E9, 0x1DC39734, 0x67E797B3, 0x107066F, 0xAED9986C, 0xAB564140, 0x9B51A6B7, 0x5FD3E2F4, 0x5A5429F9, 0xF9A05161, 0x5A5A6965,
0xDDD88484, 0xFA50FA55, 0x90E5E4FA, 0x6BF166B, 0x6566665A, 0xE450A6E5, 0xEB45AA04, 0xDA9A4646, 0xD7A37235, 0x11431B97, 0xD41D6E64, 0xD3D3A1A0, 0x5D540E9, 0x627777BB, 0x5054A4BE, 0x593A05ED,
0x2EBE454B, 0x1ABA1015, 0x7C64B460, 0xC358B47F, 0x176F4293, 0xA6E417AB, 0xF611756E, 0x1F40D499, 0x84885D5F, 0x2F0B9B9B, 0x14BE05, 0xE5919590, 0x101B146E, 0x7B261190, 0xDC96F8B0, 0xF460257E,
0x34B0AFC0, 0xEB9140FE, 0xC5C589DD, 0x1F6D6865, 0xF5100195, 0xAF560607, 0x505066B5, 0x7E590999, 0x13E190E4, 0xA56ABD59, 0xC21B68D7, 0xE594E4, 0xF6576E50, 0xFFA751D1, 0x19A179CB, 0x2726797,
0xA1931C7C, 0xE1D90F1B, 0x7F2B2510, 0x6AF90055, 0x5F1E4C88, 0xE410757A, 0x95702212, 0x7B762100, 0x1B05BF6A, 0x16F05AB, 0xDDC5C9C9, 0x72BE594, 0xE490E555, 0xC5E50106, 0x816DAC16, 0x5540FA90,
0x156605FF, 0x3B372621, 0x2B57A67D, 0x6C661E16, 0x1E97A917, 0xE6E2D383, 0x1B40F91B, 0xD9A63333, 0x34E18629, 0xA71616E9, 0x84946D99, 0x1B6906AF, 0xEFDE8904, 0x88F52470, 0x50E990F8, 0x4182E1B4,
0xBAE1865B, 0xF48E4F4, 0x64A0517F, 0xA1F45902, 0x12177BF5, 0x465EBD91, 0x37A747, 0xF0A5106, 0x4C4E8A5, 0x62779E65, 0xDE494989, 0x7B6796D1, 0xC5C5C58A, 0xE4786B07, 0x6F07E0F9, 0x5554A550,
0x95559333, 0x747A6B5, 0xA4A45500, 0xE998444, 0xF5966371, 0x111116BB, 0x783A679, 0x95409AFF, 0xFF9690E4, 0x60743EBD, 0x1C5A90FD, 0x2B051EE9, 0x5B7A1624, 0xEB415701, 0x1B6B0155, 0x9BCB8586,
0x599E5C51, 0x510064BE, 0x50FA6060, 0x16066B5B, 0x54DA89D5, 0xA01468B5, 0xC1655E5, 0x55FF6657, 0xE4985E9, 0xD738BE27, 0x6938D450, 0x47D0E4FE, 0x4858986E, 0xE793431E, 0x1A05FFFD, 0x18939141,
0x15EE4620, 0x79E45151, 0x663AA556, 0xD1266DD9, 0x7E0655E0, 0xB6A7676D, 0x54A96AA5, 0x1664092B, 0x56517AA0, 0xD6402CB8, 0x40A7773C, 0x554F0646, 0x488D5F2F, 0xE4E49095, 0x1C7CB4E0, 0x7C27529A,
0xF6FAA151, 0xCC7358D6, 0xE8406D15, 0x6E074B5F, 0x638359F7, 0xD4E9A88C, 0xE888050F, 0xE6546A0B, 0xB9904EBD, 0x755061AF, 0xA371285C, 0xE95A1904, 0xCADD042D, 0x757F6ED6, 0xE4A91F06, 0x6D5D0909,
0xE49559B8, 0xF4B0569B, 0x8454B5B9, 0x2161B5B6, 0x855AADEE, 0x575B0544, 0xBFE4D086, 0xE484CBEB, 0xF9F5426F, 0xCC653366, 0xA3524656, 0x9A5989E4, 0x10451466, 0x71F1655, 0x9B90A4ED, 0x14599FF,
0x9666AA91, 0x5A99A945, 0x9685CD8F, 0xB8506A91, 0xB427E0F8, 0x50A990FE, 0xA5FA9090, 0x60D4DA80, 0x28E35CB3, 0x55E4AA, 0xD20B55A4, 0xE15F86, 0x36E6995E, 0x54036FFF, 0xA79D2250, 0xBA11A500,
0x404603AC, 0x641065A0, 0x9DD84A0A, 0x969B061B, 0x36737313, 0x7B65631A, 0xA4E4C099, 0x9590448F, 0xD57F0680, 0x6094D86D, 0x15D8E3BD, 0x757F7DD7, 0xB45B854, 0x6560FA98, 0x7A805637, 0xD68416BB,
0x7B767131, 0x90F9E8FE, 0xA4E54045, 0xE0411F6E, 0xD57D7DDD, 0x33CB1C33, 0x58ADD010, 0x9B1FA5C6, 0xA401BE95, 0xA950F994, 0xA851971E, 0x33CC33CC, 0x10F0B164, 0x151A6F01, 0x78B5660C, 0x33333225,
0x41162402, 0x5F0506CB, 0xFD96166F, 0xE4417643, 0x56A51A94, 0x5323BFEA, 0xD12DD12D, 0xA999959A, 0x547C6482, 0x499EE652, 0x4AC7D1E5, 0x2D3DAD07, 0x6B171201, 0xAF065854, 0xD6C4891D, 0xCC739CE7,
0x9D692663, 0x3E41597C, 0xF38314BB, 0x1150A4F4, 0xE1E50FA, 0xF4D60B6F, 0x5A54E590, 0x227AB5F0, 0x73A3D7FC, 0xD7420A59, 0x12015A59, 0x4F1999D5, 0xA90EE44A, 0x1065B9B5, 0xD10533E3, 0xBA918409,
0xE5409FEF, 0x4549047F, 0x6B57A6A5, 0xE94691AA, 0x111A6E7E, 0x45496BA, 0x49FD999, 0x414D5B8D, 0xAB10EF5E, 0xE9878505, 0x8C910499, 0xC0C5DA3E, 0x6F1B7298, 0x177D78D0, 0x687B5665, 0x3F470353,
0x1441A590, 0xE1965F6F, 0x5A5B4A8D, 0x47D7C98, 0xD1404115, 0xB89A053F, 0x8C4095FE, 0x4861E055, 0x3B417607, 0xF9E0E4E0, 0x65B0506, 0x93633236, 0xAA07A5E4, 0x77747080, 0x776160F0, 0x1672B05,
0xA54E0428, 0x520A9625, 0xE581065, 0x90C76D76, 0x2157B2B3, 0x5C5BE06, 0x151A5A01, 0xA9D5C081, 0xCBCD9854, 0xFDD1061F, 0xB66111B1, 0x9DC3D7B0, 0x650A7642, 0x8095734D, 0xD052011B, 0xE0A1479B,
0x9501BFF8, 0xE9D9BD0D, 0x7A017925, 0x69A67373, 0x41E0E557, 0x5F844124, 0xEAB0695E, 0x566B5040, 0xCCC9D693, 0xA79684DE, 0x6B5BC3C1, 0x9595667B, 0x9C33CA5C, 0x8984C5C5, 0x459BBDE9, 0x1F10A5F4,
0x22A55AA, 0x97C3430D, 0xAA569A55, 0x552E1E00, 0xD3C3C78B, 0x82C7521E, 0x5B0605EA, 0x5FF69268, 0xD081460B, 0xE4517F06, 0x4448C9CE, 0x2F69F940, 0x476DA470, 0x9F96FE12, 0x4D8D9E8E, 0x6A6A16B5,
0x1D05BE66, 0x84F5BD, 0x691E1B41, 0xE0939B17, 0x159059AA, 0x1E5792B9, 0x25A701A5, 0x439162E, 0xE994077C, 0x5CC396AA, 0x1D0D9AA5, 0x4A4A598D, 0x1B6F156B, 0x1A1B0F40, 0x34CB34CB, 0x6F542E,
0x32CC739C, 0x94EB9669, 0xDA8D4E1D, 0xC6C5C46E, 0x10152B3F, 0x8787F9F9, 0x5E42D064, 0x699B05E9, 0x7030295E, 0x495E09BE, 0xEE191016, 0x801D2D56, 0x3A0099F9, 0xEA09059E, 0x5BAB5100, 0x393D49C,
0x10E15DC2, 0xB056DD4, 0x3536915, 0xE0C18719, 0xEB964090, 0x6172727, 0xFD5900FA, 0xD10B78D1, 0x33332626, 0x50F990F9, 0x78600A5B, 0xE2B5401B, 0xAE5E9404, 0xF2CF0C0, 0x9E9D8080, 0x84E4F4F9,
0x41F0E59F, 0x90787E12, 0xE4E19143, 0x761D6706, 0x6560BCE5, 0x134A9BD3, 0x23768995, 0x22ADF6, 0x434A5C72, 0xD4985444, 0x70936BFF, 0xAB54E0E7, 0x45E7A682, 0x7A786D90, 0xF8546A00, 0x5F5E4540,
0x999A651E, 0xF9E297FD, 0xAF86E5, 0xD00B6E54, 0x5442878A, 0x50E940A5, 0x61F6AF1, 0x479701AA, 0xAE455E5D, 0x6560123E, 0x22D17625, 0x83071B64, 0xF9460251, 0x5F4B064A, 0x8742417C, 0x5F89C51A,
0x14A29F50, 0x5013BF6B, 0x76395676, 0x54A590F9, 0x40915AA7, 0xEB95E041, 0x7E560504, 0x65B9E4D1, 0x3F63A594, 0x17448216, 0x1A4F87F1, 0xF990E696, 0xECE89A50, 0x2266B17, 0x6A959A98, 0x50F5001A,
0xBF056A55, 0x74470FFE, 0x65251011, 0x9F7D6597, 0x51BB962, 0xA0D04297, 0xA257F0D1, 0x5B1569D5, 0x4F40959E, 0xEC5D1D0D, 0x51A1A, 0x1DF56462, 0xC4491A6F, 0x4B4A55EF, 0xFD741D5F, 0xE1526713,
0x875153E2, 0x9752A2E2, 0xEFDA8504, 0xF0E84756, 0xE0A196E9, 0x5FAF5C40, 0x9A3359CC, 0xE056062E, 0xB07B71D7, 0x5966475D, 0x66161100, 0x444A0151, 0xDAC7D6F5, 0xFBE8E314, 0x35098512, 0x1A7F7690,
0xAF970158, 0x666A6996, 0xD1D10938, 0x742969B, 0x4542A5FE, 0x6EBE50A1, 0x816E7955, 0x64E1D0C1, 0x105156AA, 0x6A9AD0F5, 0xB4909E2E, 0x55A6A959, 0x45B4999F, 0x3266CC26, 0x9B915EE, 0x9769E58B,
0x2EF59968, 0x3F2F0711, 0x79798469, 0x6161B6B6, 0xA79504B1, 0x9B92A351, 0x61C08573, 0xAB1B656F, 0x37271601, 0xE4840979, 0x45D1C1DA, 0xA4C4961B, 0x59A7F2F0, 0xEA9FC147, 0x635362B0, 0x9561EAF,
0x6B6762A1, 0x585A43D3, 0x8484819F, 0xD1C30D5D, 0x2123101D, 0xA0F8E4F9, 0x63676220, 0x17EB6A5, 0x90E63F27, 0xDD256045, 0x7B66A1A0, 0x64143F6, 0x41D479D7, 0xF1520F82, 0x12B44687, 0x1504BE1A,
0x90E45401, 0xC4C98E4F, 0x919097C, 0xA7A52919, 0xB9B62313, 0x9695C089, 0x30C5E6BD, 0xAA55669B, 0xD19F0645, 0x1150E2FF, 0x36213121, 0x1F1F0A05, 0x2A315099, 0x2A1E0414, 0xA3E3D04, 0xD5992851,
0x19A56A45, 0x5D0669E5, 0xA7C1F8C0, 0x84D1E5AA, 0x7292A464, 0x9040F5E4, 0xF185405, 0x1FAE4509, 0xF91690BE, 0x5540A540, 0xA1D2874B, 0x560B65F8, 0xC207E1E6, 0x646D0F9, 0x5A1440, 0xBB454116,
0x13597242, 0x413A4504, 0x66E7D2D2, 0x61DA6950, 0x519DF0A0, 0xD2926EB4, 0xA583060B, 0x247E1587, 0xE50590BD, 0xEFF50146, 0x6252B722, 0x4B9AF552, 0x42445A5, 0x5D0844D0, 0xD7C1D18, 0x6B53900,
0x7DD68434, 0xE6964247, 0xE0A50B5, 0x72635347, 0x669A6B06, 0x91549A65, 0x8F097CA5, 0x849458EC, 0xF9B09275, 0x71390D5A, 0x478BC9D6, 0x5D579AA5, 0x9ED08605, 0xCA1C35D3, 0x1029669, 0x1344FEA7,
0x5B468B87, 0xA7F29990, 0x60BDB855, 0x3430B574, 0x544461FF, 0xC5C9550E, 0x69E716A7, 0x112336, 0x3F11D2D7, 0x2F0796E4, 0xB5250B00, 0x33CC33DD, 0x20357676, 0x7B6F9272, 0x114B09BB, 0xA7F6C987,
0x32959833, 0x40D25BB6, 0x13170353, 0xD52E5949, 0x93626538, 0x43449A56, 0x655890BA, 0x2F56811B, 0xE5E4C88, 0xA6079500, 0xA4F90507, 0x6460A055, 0xE990906D, 0x156F56AC, 0x54CF00, 0x181D5A0A,
0x7C09E947, 0xAD9E898C, 0xFF914212, 0x6933A7CC, 0xB2935B2E, 0x4454D8A2, 0xA6A560B6, 0x519E2075, 0x575FA6A5, 0xB8B06916, 0x598B471B, 0x10686AD1, 0x45EA0170, 0xD0470B9B, 0x3B511E0B, 0x53D79D0,
0xCBCAC5C5, 0xDAD54CD0, 0x3542EE79, 0xB4AD4FD, 0x642DFF01, 0xB99109B9, 0xE1919B9F, 0x97B84162, 0xE995460A, 0x1060F5F5, 0x166DBCF1, 0x4214957A, 0x6C60626, 0x50FE4F0B, 0xB466470A, 0x808596E2,
0x70D1440D, 0x818617B6, 0xC8E8DDED, 0x40443474, 0x103E0750, 0x1559A9, 0x16E29FF, 0x54FE0447, 0x34CAB25C, 0x9B30756A, 0xB0E74B05, 0xE19051D, 0x402E7450, 0xF5E0D1AB, 0x87979B5F, 0x8707BA71,
0x90B4A491, 0x1A2F5301, 0x6C44D318, 0x8AC0A1F4, 0x5A6F0306, 0xE1159090, 0xF9A54183, 0x4CC7321C, 0x7E64868B, 0xFDE60582, 0x4BE77014, 0x1B902D01, 0x104D8FA7, 0x16A7CD, 0x94693912, 0x62E759A,
0x594BA906, 0x5D023747, 0xDF9757AD, 0x97364CCA, 0xFA011265, 0x12E16116, 0x7A615600, 0x501196F9, 0x5067E247, 0x2A75B070, 0xBC0196BE, 0x19FD8907, 0xCA8511AE, 0x7B671210, 0xB8F0966F, 0x600AE5F4,
0x4146858E, 0xA579C124, 0x19F26C13, 0x2320776, 0x595BF900, 0xFB059055, 0x6FD6E460, 0x86CAD5D, 0x948153A5, 0xC6C546FF, 0xE199AD5A, 0x656A566A, 0x81256994, 0x7C285400, 0x37CD6A37, 0x4CF4E1B,
0xD181E0B5, 0x90F89F46, 0x5AD2D072, 0xF1F44D4, 0xB5E091C6, 0xFF90E764, 0x656B9965, 0x833471C7, 0xE6470700, 0x521A517, 0x56620BF, 0x7A6458C9, 0x566959A6, 0x5A5FF3D2, 0xD050063F, 0x9AC17C39,
0xC1F03D19, 0xE7939343, 0x35312404, 0x76671223, 0xA0D05804, 0x7B773262, 0x5E2E6465, 0xE6860519, 0xDE909B5A, 0xB5C094E4, 0xAF019B15, 0x1A57027F, 0x7874E7D3, 0xB35674A, 0xD0854FB6, 0x916509FD,
0x431F91FF, 0x9B605420, 0x566978B4, 0xE8D1042D, 0x2533074, 0xEC904443, 0xD404A4D1, 0xB9984945, 0x435181E6, 0xDFD0520A, 0x37FC61D, 0x1540FA94, 0x876DB853, 0x9D686C9D, 0x5D7DE642, 0x556A6669,
0x6B166F05, 0xF0F06616, 0xE490051F, 0x147B0606, 0xFD76D9D9, 0x3B814E5D, 0x16E6460, 0x91F05406, 0x37444D34, 0x1B17BF00, 0xA8465A05, 0x12429D1C, 0x79753935, 0x639291F4, 0x6761F0F4, 0xBC789460,
0xF890D79E, 0x54780743, 0x1131367B, 0xD6487C64, 0x8E582E4F, 0x6A972A65, 0x1BA6D0E5, 0x17D6007A, 0x82590727, 0x95D0FA, 0x1540E47E, 0x56B91A0B, 0x8A85C4D4, 0x9F8205E4, 0x80D4C58B, 0x75D3E647,
0x5956D966, 0x74ED4500, 0x167EA440, 0x255E191, 0x31811515, 0x82999DDF, 0x11670BB8, 0x2BDAD965, 0x5AA5669A, 0x55BF105A, 0x88496E59, 0x5AF56600, 0x4858E751, 0xF4811BB9, 0xB501A7B0, 0x11B26DB,
0x767C9887, 0x602D7703, 0x1219F8FD, 0x464297D, 0xFF06DB95, 0x156A04BF, 0x5050A39C, 0x35CA4F94, 0x7F00EF1B, 0x68353273, 0x150663B6, 0x79666190, 0xDA650647, 0xA962959A, 0x96E596FF, 0x537E17A3,
0x57F9E440, 0x101458FF, 0xA4D4E441, 0x1898C4E0, 0x7E189481, 0xB6C71904, 0x9A95EDDD, 0x944449FF, 0x61E4C997, 0x52DF8F5, 0x6A51F46E, 0x9145AD, 0xD9A8DDD2, 0x8784E63D, 0xFF5B906E, 0x2998A559,
0xCCCC6633, 0xB954C0D0, 0x70B5663B, 0x531C8B25, 0xFFBA0191, 0xF4E35B90, 0x40FF7150, 0x1F075AE5, 0xD0015BFF, 0xDCC3D6DB, 0x4E54A07F, 0x7E9647A, 0xA19D4E1, 0x51504404, 0xE7D68A8A, 0xAF10A450,
0x71B71184, 0x79940A0E, 0x821B196E, 0x50413A5B, 0x5707962D, 0xD1B63962, 0x819DEDE4, 0xEEC5CB54, 0x251DBAD, 0x50BD6D1D, 0x20976E74, 0xDDC98A4F, 0x451079E9, 0x69146E, 0x68590311, 0x8045A9F4,
0xEEC58B96, 0x98CCC996, 0x94784451, 0xE6D6015B, 0x3035B95, 0x12E39F6, 0x50EE4058, 0x4D1C74A0, 0xA4291505, 0x936B67D4, 0x2AC1449D, 0xA4015A6D, 0xAFCB4414, 0x74A50038, 0xDED051C8, 0x347B76DA,
0x817ED01B, 0xDD2D79D2, 0x5A1A011B, 0xA040F556, 0x540246B8, 0xF2B45A06, 0x6A4999B4, 0x4B67D0BF, 0x31614701, 0x456C84C1, 0xB8F4814C, 0xFF009669, 0x4F4A4999, 0x907D95AB, 0xB7A49402, 0x526E61D,
0x5A9542D3, 0xF8792606, 0xA913569, 0x3193534B, 0x7A61D074, 0x51A452E3, 0x40E490E5, 0x4106377, 0x404A1709, 0x1562727, 0xC0B89996, 0x4440781E, 0x78FA9053, 0x5D1E00E8, 0x1C3C75D0, 0xD581AB05,
0x85C58A4, 0x44E490E4, 0xCD94CDCA, 0xB252E6D6, 0x1FC345AB, 0x40C5B905, 0x26693851, 0xEC3741D, 0x1B5869B, 0xA161510, 0xE061977B, 0x8A580510, 0xD960D554, 0xB53E091, 0x14B900FA, 0x3E094659,
0x6090906, 0xE6B47C17, 0xECEC9840, 0xF9A405FA, 0x90F994FA, 0x2B750A5, 0x803B3D25, 0x14AE405F, 0x6F97E0FD, 0xCD34E38C, 0xDED0D4AF, 0x96D1C038, 0x51E78187, 0x93D7CACD, 0xD4D052A7, 0xE6558B4F,
0xF6025766, 0xE54074B0, 0x6613252C, 0x257A75, 0x1B1266B5, 0xF956E0B6, 0x44D3E3B9, 0xC5C5E9E6, 0xEBD69599, 0x9F91D0F8, 0xB0A05253, 0x6E0F1761, 0x425FE480, 0xA5A051FF, 0xB1384DC7, 0x1CE31CD3,
0xEBF6701B, 0x6B152998, 0x35A62510, 0xD140E5E6, 0x9070791D, 0x3CA6D1, 0xDAC98985, 0x90917E97, 0x19BCF91, 0xD0C7CBC7, 0xB5466B37, 0xB111D25B, 0x9A29978C, 0x3196C50, 0xCAC5C1FF, 0x4F4A4192,
0xB14E708B, 0xD5E958D3, 0x73747E24, 0xEDE0D6A2, 0x1B91436E, 0x79252511, 0xCBC58C44, 0x7E64F890, 0x9F05B9F4, 0x1B55E0D0, 0x21D1E969, 0xF4558028, 0xF9E5C3C2, 0x1974325B, 0x6A94E0F0, 0xD101A5BD,
0x1A17075, 0x5B2D78E1, 0x17194807, 0xF5C24B1D, 0xFA40E655, 0x7A095515, 0xE106F993, 0x565A1103, 0x5A54F6E0, 0xF5E0016E, 0xD6CBC5C1, 0xF940E696, 0xAE316D90, 0x6A146A00, 0x9B96E9E4, 0x6351D86F,
0x5A466995, 0xD4460B8A, 0x2CC3744E, 0x99666696, 0x20257ABF, 0xA3F2955, 0xD5D0919A, 0x54444859, 0xFC5C606F, 0xA6653749, 0x306E696, 0x2528BD70, 0xA07BCE5, 0xF0A46662, 0xDC649440, 0x99C7874A,
0x656CBEB, 0x5151A67B, 0x60DB4B05, 0xD1444107, 0x514B74, 0x165025A, 0x5B5A1101, 0x7101179A, 0xD09A070B, 0x50A096BF, 0x47A9521D, 0x4B2D7492, 0x3F0F1B53, 0x1941ABFF, 0x7B666111, 0xD3C345BA,
0x1CA9D6, 0xE18359B9, 0x1590E6F8, 0xF4285902, 0xBF1B92D0, 0x1BE76D, 0x2A5582C3, 0x8979164B, 0x3C721B40, 0x33C0CF30, 0x2DD21EE1, 0xE9D30707, 0x192DF65, 0x1B92E7C5, 0x33333669, 0x1E0560F4,
0xFB53034B, 0x966A6699, 0x6F9797A7, 0xEF469BDB, 0x1943F5F6, 0x1DA7C70A, 0x74741E9E, 0xF5709967, 0x520B74F1, 0x4741FBAB, 0xDA6712B9, 0xCBC6C1D0, 0x67E2D64, 0x5E03B625, 0xC088D9D5, 0xF89005FA,
0x5A2516AA, 0xE5C7D9D6, 0x69FE5096, 0x435BE0D1, 0x7193DB8, 0x9CCC9967, 0x54613301, 0x461211BC, 0x730C6FAB, 0x6050A5B1, 0x7EB91141, 0x6A152F00, 0x69665AE5, 0xAE5780F8, 0x7D06F90, 0x2032D510,
0x4E0746D1, 0xA69C33CC, 0x1A462616, 0x36C6C6C6, 0xFFA59D1F, 0x9B6D74F1, 0x1197907E, 0x656A9995, 0x91742E48, 0xD00538F5, 0x441542, 0x40D2469B, 0xFF5999, 0x15A9966A, 0x94841EF9, 0xA5651D1F,
0xCC9C6633, 0x2F7D107E, 0x9B81411F, 0x9E59A669, 0x5E970007, 0x7E2E1F03, 0x29B5F244, 0x86C5D48, 0x548581B4, 0xF955CF44, 0xE35CB2CD, 0xA956B6D7, 0xE0E69125, 0x1969C59D, 0x550099EF, 0x68D99F24,
0x5FA65010, 0x2B1256FD, 0xF8244147, 0x1D56470C, 0x2162A76, 0x62F16510, 0xAA556995, 0x540669, 0x14C29726, 0x4790FC0, 0x2CC19B44, 0x6CD7759D, 0xD7F64140, 0x425F81E4, 0x5034348D, 0x65095966,
0x7261B0F5, 0xAF5681D1, 0x935A4051, 0x5262767B, 0x595EC2E8, 0xDB743847, 0x3C60A513, 0xC21F3991, 0xCB34C738, 0xD19AE995, 0xB9171781, 0x1107AB66, 0x5FC0D790, 0x2070F1F6, 0x6E5E1A09, 0xB5407E5E,
0x505494EA, 0xE4703556, 0xF890999E, 0xA65BF595, 0x2919A7F, 0x49DD3880, 0x94EF960A, 0x183D1906, 0x1500F9F9, 0x9DEE4509, 0xF5FA6313, 0xE0526850, 0xBFE60005, 0xF16E685, 0xF90065A5, 0x916AF859,
0xFF608454, 0xE6BF5094, 0x81D1E0FA, 0xA19FF91, 0x60D59CD9, 0x39524274, 0xD7994F4, 0x36295C8D, 0xE0503945, 0x2D85C545, 0xD1500658, 0x22172635, 0x338C5626, 0xDE812506, 0xDB57E8D0, 0xE9D955F6,
0x9F6C94E6, 0x9636CC33, 0x2A1A3542, 0xF95D80F5, 0xD4266F42, 0x1C9E5F9, 0x74A1C30E, 0x6B16A050, 0xFB03506A, 0x4218B469, 0x94D1422D, 0x427D3531, 0xC25BAD40, 0x6692B292, 0x5A50E9A5, 0x717CD4C3,
0x71938F59, 0x5147400A, 0x41BC6EB5, 0xD4A0D4E4, 0x1D0E5FA, 0xF6869F06, 0xA7E35252, 0xF4D1421B, 0xA4894DE7, 0x2D946B1A, 0xD0FFC684, 0x17C291E4, 0x7F525000, 0x15848139, 0x669669A5, 0xA7A76353,
0xA996966A, 0x2FE155C0, 0x1E1E42D6, 0x4CB19976, 0x1103BE69, 0x57699082, 0x71624AF, 0xE1445A09, 0x969A4504, 0x5602955E, 0x5A1548E4, 0x5B074314, 0x74A6CE66, 0x9038152E, 0xD8C4554F, 0x3EBB0657,
0xA8A65669, 0xB9B56010, 0x96B92D86, 0xE9D9CCA4, 0x33DE69A7, 0x5C8CD8E4, 0xF5D1401A, 0x4B59A46B, 0xCD258D07, 0xF3C3A475, 0x105DFC2E, 0xF7929140, 0xA4A450FE, 0x5B4598, 0x17137B75, 0x1B05F082,
0xD62474D1, 0x673AB500, 0xA9460B5E, 0x47025AD1, 0x6E6695D9, 0x1262B6F6, 0x6AE65190, 0x66A69499, 0xD0993A76, 0x22321727, 0x55C0C56F, 0x6F7D1E43, 0x352F53E, 0x6AD6662F, 0x62713623, 0x948484F9,
0x27161E04, 0xE996065B, 0xFED0411E, 0x8BC59A4B, 0x689E4B01, 0x40467E7E, 0x550E0F8, 0x1832D78D, 0x6CEC9580, 0x6A9E8F6E, 0x5340D4B1, 0x1449C6D, 0x22163530, 0xB4F6C3C6, 0xAD01DA95, 0x656F051E,
0x8151167B, 0xA0C3966B, 0x40F85B15, 0xDD4B8D2A, 0x24BB667, 0x52E193F, 0x56A7019B, 0x12625504, 0x11A16297, 0x4B59066C, 0x59E990A0, 0xA45C0B00, 0x92C34B3F, 0xD06F6325, 0xC68D18B9, 0x74783C30,
0xEA58C5CD, 0x142151F7, 0xDA649E04, 0xDA950602, 0x6E641510, 0xA7249144, 0xF46A1FF, 0xA950F611, 0x752E1FC, 0x7A460551, 0xB8E0D6D1, 0xE9D09151, 0xC745A104, 0x2072E687, 0x9F870105, 0x65A0F1FB,
0xD0D500F9, 0x1A901FE1, 0x6F4E9401, 0x33322558, 0x52532732, 0xDB16B782, 0xBAA56046, 0xBCA19396, 0x411B39B7, 0xDC847184, 0x31C8B364, 0x6A66906E, 0x3F85D8F0, 0x36EB4393, 0xD518985, 0x34392F47,
0x1FCF4E96, 0xF4A05E06, 0xBD5B102C, 0xFF056A65, 0x561BB601, 0xACC50A51, 0xA3733235, 0x7E1B53E2, 0x5251A0E5, 0xE078156F, 0x1F56E790, 0xA9965D59, 0xE0949C7D, 0xD87D560A, 0xD0649CED, 0x1065A47E,
0x257CE4C3, 0xE2409597, 0x71D49220, 0x253929F, 0x47673ECA, 0x716B51F3, 0x4C1C48C5, 0xC7C1520B, 0x7B593060, 0x4F88491E, 0xA566A956, 0x7767A34, 0x65149A00, 0xB6D75A5F, 0x96A757A6, 0xA050617B,
0xB007D141, 0x12A377B0, 0xC4E8D5F5, 0xE0C74B95, 0xC13C06F9, 0x7996213D, 0x9AC68F45, 0x65460B95, 0x99A7C292, 0x889C9C6C, 0xA4EDD440, 0xB0693DF, 0xFF50E490, 0x507A977, 0xE4147ED0, 0x80F19505,
0xFCC5066E, 0x74B64319, 0x103A1EC6, 0xB288507E, 0x56A779A0, 0x7C9056AA, 0x381294FD, 0x5D194EC1, 0xF0C19B1F, 0xD60A1DE1, 0x94C0C4DF, 0x7E66824B, 0x69670667, 0x461520C7, 0x7466071F, 0x96FD1A00,
0x10C4EA65, 0x93DE045, 0x6F01E2E5, 0xB196946, 0x504246AD, 0xB45E0F4, 0xABF1194, 0x84C94D5A, 0x111D27E, 0xF6951BBD, 0xE081166B, 0x56699965, 0xA1F16C54, 0x46107A64, 0x821C3491, 0x88D86E5B,
0xE5D2C7CB, 0x5203432F, 0xE616332B, 0xF9854938, 0xD2736742, 0x671061F7, 0x9248BDB8, 0x544106E2, 0x6134280D, 0xC65E091D, 0x5599E860, 0xECC245B4, 0xFE4564E0, 0xD500F4F4, 0x59FDC0ED, 0x44530376,
0xB152767E, 0x6A074351, 0xC565DE, 0xAD995844, 0x57F1D099, 0xB252D484, 0xD1984D8E, 0x5196A66, 0x2C59E167, 0xC0E9055F, 0xFD196F06, 0xF5F0C1C7, 0xFAD05152, 0x2126E0F5, 0xE9F42911, 0xC8339895,
0x8D405BD, 0x50E94377, 0x443AF945, 0x5011293C, 0x76A297AD, 0x1EE440C5, 0x35DAD9A0, 0xA4419590, 0x165BA619, 0xF60B9A65, 0x1F89444A, 0x71B0255, 0x476DF0A6, 0x61D4A1A, 0x469F9DB0, 0x32355C8C,
0x86091D46, 0xE795060B, 0x2296305C, 0x6E41F80, 0xF490E594, 0xF4E25699, 0xBF65AC50, 0x4A7E75DB, 0x1015F458, 0xB04ACD9E, 0xEE5478F4, 0x7F630C53, 0x4E49F5F0, 0xCDA3319C, 0xBE116D20, 0x4589D9E9,
0x7435434B, 0xFDD96656, 0x5F681E4, 0x99AD1C50, 0x552A5401, 0x68F04549, 0x73B66D2, 0x1F86D101, 0xD041160B, 0xF906FFA5, 0x689676DE, 0x5F4780BD, 0xF0939AA5, 0x1F0B5F40, 0x501B3F, 0xA8C193B5,
0x8C73CC33, 0xE24F7CD3, 0x9090A565, 0xA990D59A, 0xA1C14877, 0x946D8180, 0xF8343124, 0x14C1E915, 0x4919CBA5, 0xFF55D001, 0xF4E152A2, 0x46687EF0, 0xF05ABD48, 0xB7B37460, 0x1474C58, 0x94E490E8,
0xB9E44183, 0x78D32DE1, 0xC8E4489F, 0x5BDAA45B, 0xB5610147, 0x5162A5F9, 0x5D6191C8, 0x4595C0A6, 0xA57DC996, 0x6D9D0D1E, 0xF58BC767, 0x1A0E5509, 0x409BD2D7, 0xFF5C135, 0x33B7579, 0xEF035600,
0x779152B, 0x50B091B7, 0x363A3162, 0x491069DB, 0x9C4E0C5F, 0x945A1B12, 0x5490FF00, 0xB5A0B400, 0x1F82D64A, 0xB2C7426, 0x329CDA73, 0xA9995AA5, 0xF65876B4, 0x1E4E890E, 0x857AB576, 0xA52E054,
0x1D11D88C, 0xA35895B0, 0x6F196890, 0xC7143E70, 0x6BE401FE, 0x111BA961, 0x66070AF5, 0x5F07130F, 0xA15061B6, 0x8C73CD32, 0x6C6481D6, 0x6AD5A6C1, 0x14A5872C, 0x46B90BE4, 0xE5586946, 0x3163C58C,
0x1431B550, 0x91E94F09, 0x66F5C24B, 0xEE40461E, 0x97593992, 0x19E059F4, 0x94F92E05, 0x8CC445EA, 0x403D64A7, 0xB0FE5D8, 0x9C9B5C00, 0x7EDED9A4, 0x7CD393B5, 0xF1A50DA, 0x372B4077, 0xBF9046E7,
0x56261E6D, 0x51232570, 0x40153CAB, 0x9B6F6712, 0x51C0160A, 0xB990052F, 0x3788B955, 0x555064A0, 0x6F0B5B05, 0x9776666B, 0xAA069B45, 0xAFF81440, 0x91E4DE1A, 0x80D5C9C6, 0x1E9E5DA4, 0x2B65B450,
0x1213362B, 0xD766654A, 0x461346C, 0x197B2441, 0x5F9547EB, 0x15AF0506, 0x464C8D9D, 0x4021957A, 0xFE54E413, 0x13295035, 0x145E0D2, 0x4D4854E9, 0xBE50919B, 0xA76CF940, 0x91E1406E, 0x9A655A9A,
0x3E65005A, 0x1C0B6666, 0x2611AC57, 0xBE7D1442, 0x311E3FC, 0xA4858185, 0x6E54D2A1, 0x9140A5DE, 0x844EC85, 0xF4A81540, 0x1159BF9, 0x2896D07D, 0xD5E690EA, 0x40A47D47, 0x6F672C16, 0xF1611217,
0xE4096F65, 0x621D05EF, 0x9040979F, 0xC1C5CA46, 0x5B9606B5, 0x5101767A, 0x448C9568, 0xE4983D12, 0xCBC8B5E9, 0x1B15E402, 0xE1C60E15, 0x44E68419, 0x90D48DC, 0x7A9042BB, 0x151285D, 0x676B2613,
0x121644B4, 0x920F52E3, 0x56DE09E, 0x6E6607A4, 0x5E560B0E, 0xC20B7616, 0xAF464146, 0x8C5C2835, 0x474BC2C6, 0xBC6419CA, 0x5C4353F0, 0x31392547, 0x504295AA, 0xCC593363, 0x1C0C6DBA, 0x4D71B7F,
0xCC593633, 0xE71A250B, 0xE8CC599D, 0x894FA695, 0x4EC3513E, 0x39C0C645, 0x7B526034, 0xF91E2E06, 0x1A663699, 0x202599EF, 0xC1D1E195, 0x75E600FF, 0xD0D1ECDC, 0xD6DB6050, 0xE0E5016F, 0xF0910B15,
0xD68C6917, 0x6F1A3D00, 0xF5D61BA6, 0x336D1CA5, 0x7888941D, 0x78D0912D, 0xE1D1C34C, 0x592E4C41, 0x1539F804, 0xFB019650, 0xD5E50609, 0xE8C553F9, 0x411BEBD, 0xE580D1DA, 0x1CACD693, 0xAF15BCE1,
0x1104909C, 0x4B3B4686, 0xB3A31251, 0x50F5665, 0x1D4EC1D3, 0x2830D712, 0x401F0475, 0xC089D4DD, 0xB2425701, 0x40076F65, 0xA950F5A4, 0x9F9990BF, 0x70214431, 0x22753C31, 0xCC7369D9, 0xE9919061,
0xF45A1F1, 0xBD0690D0, 0x79A6C180, 0x2FDD105A, 0x584296CA, 0x116F906E, 0x58484C5C, 0xDF9B10E6, 0x5184486D, 0xE9D89D90, 0x69B05ED2, 0xD9841D2B, 0xC2C6C5E6, 0xF88947CD, 0x90696469, 0xC5C0C6DF,
0x677B0640, 0x4B479BC5, 0x83D1B811, 0x5B47E440, 0x1A419DEA, 0x3DD8D4A9, 0x8752B0F3, 0xD1D18189, 0xCCA632CC, 0x725CCB73, 0x6325CC33, 0x14464A25, 0x5410303F, 0xF8C12F85, 0x39D205E5, 0xFE850549,
0xC68710B5, 0x471F7842, 0xD34A4C6C, 0x4742D188, 0xBFD50A44, 0x1987777C, 0x5B61C12, 0xB2530677, 0xD995A916, 0x75B496A0, 0x31E61E7, 0x53939DED, 0x51705B00, 0xBA5A4046, 0xFF14A4, 0x2599DCF8,
0x7D47F02E, 0x9B959064, 0xDCD4E014, 0x38C22DD1, 0x65647C88, 0xD4606066, 0x3551E0D2, 0xD06157FE, 0x1131260B, 0x8B81E454, 0x1E1865FF, 0x1966A524, 0x4B4684D8, 0x3450F1B6, 0xE4FE41D1, 0x1D31F1E6,
0xC47C1F4A, 0x94656AB, 0x45D101B, 0xB681B712, 0x9BD09215, 0x77924154, 0xFCECD4A0, 0xF0F1C72D, 0x74D0834F, 0xE4908117, 0x65E0365C, 0x955A09EF, 0x6E744349, 0xF4692B13, 0x34750F6E, 0x94D198A9,
0x839C2DB2, 0xED929580, 0xEA609E65, 0x1A15F8D1, 0xFE00D6AD, 0xD5DC0141, 0xD90D3995, 0x6E6640ED, 0xA9B443CF, 0xE50681D9, 0x3F705659, 0x5F5E4844, 0x9A254A5B, 0x26618195, 0x8B945DAD, 0xDFD4E490,
0x674352C0, 0x9967C1C0, 0x5990E995, 0xDEC544BA, 0xA7DA444, 0x16457E8, 0x142DFE04, 0xA4D5C084, 0x13172539, 0x84FC3590, 0xA9734748, 0xFE07F451, 0x42119AED, 0x8F087916, 0x13A65D1, 0x82856E75,
0x3511B1B9, 0xA61BD018, 0x8619B893, 0x40BD89E5, 0x9A15F640, 0x4052464F, 0x9D87C1E0, 0x972C58F0, 0x84D8ED54, 0xE64D9C5D, 0xF421502F, 0xF24864, 0x59B8A154, 0x9A2596DF, 0x1441E6FA, 0x1BE46F90,
0xA8915051, 0xA5B62611, 0xF582841D, 0x825362B7, 0x55F367E, 0x415263BD, 0x3E463930, 0x68754A06, 0x1117F280, 0x8B16B855, 0xEC75E1C4, 0xA0771117, 0x669DB850, 0x6D171238, 0x47167213, 0x90B21746,
0xD92649C8, 0xCAC6C6F1, 0xE392C769, 0x60E7939B, 0x14130475, 0x56001A7D, 0xF657028F, 0x6F6819E, 0x29974C33, 0x49A57C5C, 0xD590E890, 0x6FF1116, 0x132B7665, 0xA0C60B56, 0x9844DF41, 0xA1B103E,
0x44845EEF, 0xA6D2F5D5, 0x55FA4600, 0xBF611264, 0x6AD53610, 0xED594A11, 0xCFC15015, 0x96264247, 0xA4F1D3DA, 0x257EC166, 0xB8D6C114, 0xEA90CF4F, 0x6D5A804, 0x3F0695E8, 0x9C90BCE9, 0xAB4701D1,
0x136357B5, 0x994D9E0E, 0x121652B2, 0x6D9FD261, 0x5CCC9733, 0x873B95E5, 0x905E9C80, 0xA051BA6F, 0x5F478589, 0x197AD19A, 0x50015DE9, 0x5BAE412F, 0x4111162B, 0x738F386, 0xE4835BAE, 0x5B55A2D0,
0x5E4B038, 0xF9F91014, 0x8FCBD1E1, 0xD98B4743, 0xB9955DAF, 0xC5373DCD, 0xBD1C8114, 0x250532F1, 0x24504E0E, 0x62D7C50, 0xE8D44154, 0xA8944044, 0x9D669995, 0x530C6FFA, 0x65167BA7, 0xE641BE,
0x811AB5D2, 0xD18A9D09, 0x6F064045, 0xBF902991, 0xC54F07FF, 0xF6962B11, 0xA4819FDB, 0x60F46A54, 0xE451815B, 0x7C197B, 0x9D44DA0C, 0x869640FF, 0x18C44D18, 0x40247F66, 0x5221F711, 0x510196FF,
0x353261EF, 0x44490E0E, 0xF994E956, 0x926C45BF, 0x7D96855F, 0x84D4C98A, 0xE06BF456, 0xC21B1641, 0x4B5CB493, 0xDA90D366, 0xE8964D44, 0x197C75DA, 0xD6D46333, 0x465E78A1, 0x1942461E, 0xC1356656,
0x58D631CC, 0xD773384C, 0x4A9940E5, 0xA0F97414, 0x53532327, 0x78107AB5, 0x3C091BA5, 0xA6469144, 0x44E45841, 0xC50D5842, 0x4A199678, 0x46365DF8, 0x5463E87, 0x8D2C1151, 0xFF5895, 0x2FD231C8,
0x6C9CD2B3, 0xD74243B2, 0xC4409CDE, 0xB8E0411F, 0x26670F05, 0x5B6697A9, 0x55464A2B, 0x2E0F569A, 0x4660E25B, 0xFE500715, 0x9C683532, 0xCB0C5949, 0x806C6592, 0x679B0156, 0xFE005F15, 0xA540FE50,
0x1F281510, 0x906D4F00, 0x57052C80, 0xA95BC0B5, 0x6C98C5CB, 0x44581E4E, 0xAD9D4846, 0x8352C7DB, 0xB21E50D9, 0xEB4607B7, 0x99AC9C77, 0x1509D9FC, 0x5460133E, 0x4244EE5A, 0xEF5A259A, 0x3439461B,
0x6B940B05, 0x59E94484, 0x5062B45E, 0x859B9363, 0xFDE48194, 0x325362D2, 0xE460FB46, 0xD78B98E7, 0x64A3D346, 0xE5B06277, 0x9F655A41, 0x676B5302, 0xEA950B7D, 0xFD158A, 0x775362B4, 0x75C21938,
0x861EB9D2, 0x589532CC, 0x1025190B, 0xD46B06FF, 0xB50E7C44, 0xCDD7A372, 0xB5B96100, 0xF2B491A1, 0x5E9960F4, 0xF1CC582E, 0x89C99575, 0x7AA1475F, 0x7B66C124, 0xD0D681EF, 0x44156E08, 0x8F1F1056,
0x6E67D240, 0x9A4C4951, 0xD451E4A7, 0xEA941B13, 0x7E470070, 0x85422E7F, 0x5B9690FE, 0xDE06E626, 0xC8854A15, 0x5A05ED4D, 0x80F46E1B, 0x9011F302, 0x4493D2D8, 0xE50D9D68, 0xFB764400, 0x64D1816F,
0x9965A616, 0xBF011811, 0x155B0252, 0x3D9DA411, 0x58D96D90, 0x5FAC14F, 0x34E09F47, 0x7470303D, 0x35316E5B, 0x84782D19, 0x6074B114, 0xDF85124F, 0x5B47B723, 0x91327673, 0x31A3C548, 0x6D04F9,
0x65DA62CC, 0x779B6270, 0x9037699, 0xD59A061B, 0xD140F5D0, 0x546F04E5, 0x197A6193, 0x150F46FA, 0xE0592151, 0x54A9D0DB, 0xAA55D851, 0x50616F64, 0x35307994, 0x1198C72F, 0x472E79D1, 0xFF01981,
0xF6195AFF, 0x58E66219, 0x69D3D67C, 0x3395C832, 0x5DA31DFF, 0xD66E7583, 0x802979D7, 0x1C419805, 0xA596916A, 0x6A464414, 0x64702547, 0xD31AB704, 0x91E6C21B, 0x41AF55, 0xC600DF64, 0x2D162960,
0xD0D9AC64, 0x921C6378, 0xD0051C18, 0xCC26969C, 0x3C856899, 0x56A9D3A3, 0xC8CCD966, 0xB42552E2, 0xFF055B5B, 0x4AC7E641, 0xE747D0DD, 0x45A5F75D, 0xFD960205, 0xA60391FD, 0x5EAD8484, 0x13D0E563,
0x11011BBF, 0xF4A4414, 0xCB5D0939, 0x1D6DE804, 0x2D6DF850, 0xB7E11431, 0x8B253410, 0x24D7A918, 0xA6591F6A, 0xFA0545, 0x10D3421A, 0x4E4CE453, 0x99C48C3D, 0x71D1C0F, 0xB4B94045, 0xE0815D05,
0x9D854214, 0xF5F06136, 0x1A58FD15, 0x4742A650, 0x7E66464A, 0xCAC5D1F2, 0x99A91441, 0xB06115FB, 0xC0F43522, 0x9CE9CDC8, 0x59EA404E, 0x5B42A707, 0x16914BD, 0x872DF087, 0x4114AE9E, 0x3B1284FE,
0x5E5E5808, 0x4680D66C, 0x364246C2, 0x65C2462E, 0x421C78A1, 0x1EB8D5EA, 0x28F24C55, 0x5021670B, 0x90BD7875, 0x71385CCA, 0xE7F07411, 0x1F11A366, 0x5FDFB852, 0x4C4DE05A, 0x9E0D9AD6, 0xE0953C84,
0xE5CC0641, 0xE01BF406, 0x51E4072A, 0x4D66B8B5, 0x272DAD04, 0xF2C3986D, 0x6D680158, 0x464B0755, 0x550047FB, 0x74B41D1, 0xAF58676C, 0x7A655241, 0x64B9505B, 0x4D1D4D8D, 0xFFA5909B, 0x2F0754C1,
0x4052256D, 0xB9D68609, 0x66615EB, 0x8A3D64D1, 0x2392D1E5, 0xF0A15ABD, 0xF7375B01, 0x699291A1, 0x607D0154, 0x6F19CDC6, 0x869DAC51, 0x11164726, 0xB0B50B1A, 0x9550E320, 0x257FF450, 0x3264669A,
0xDF5D405B, 0x819855E7, 0x6B97C0A4, 0x88394945, 0xBCF14411, 0x6ED499, 0x570193E3, 0xFA346401, 0xF479095, 0xE6E6850B, 0xB2F1615, 0x95E2160, 0x144D8A3D, 0x3932D186, 0x5B64F801, 0x1F0746E7,
0x1443EF98, 0xE8444E49, 0x4740EB50, 0xB9035B58, 0xD3D21B3E, 0xB8767699, 0x67D78366, 0xBD1187CB, 0x6C5C0C1C, 0x906D3E13, 0x9E1E6C49, 0x400598ED, 0x529106FD, 0xB4C10D2D, 0x740DC334, 0xE99458A0,
0x94C8D572, 0xD7D18228, 0x13E566E, 0x1BC3D629, 0x63136172, 0x51A1B10, 0xF305F4E0, 0x679F6813, 0xD38356EA, 0xD669C856, 0xC5CE5A4, 0xC738CB74, 0x567DB480, 0xA65F9440, 0xE207176B, 0x9D5E4A88,
0x1EFD6440, 0x1095738A, 0xF005646E, 0x19E1870, 0xEC481545, 0x2E598105, 0x55F7439F, 0xDBDB41A1, 0xE64F995, 0x6A9164A9, 0xA5191E04, 0xF4D1413C, 0x646EA056, 0xE3911263, 0x20117A65, 0xF9059304,
0x48366754, 0x12166B67, 0x868A677, 0x46DB80C0, 0x1878D69A, 0x60D26DC1, 0x405EE804, 0x84D990BF, 0xDAD54606, 0xD93365CC, 0x93B2971B, 0x31269151, 0x5F0E58C2, 0x19B96390, 0xA1AD444, 0x90286D94,
0xB1D10B7E, 0x40FAA451, 0x8276815E, 0x8669F400, 0xB5A61B12, 0x65FC9B8, 0x446AA55, 0xA11D8378, 0xAD9F4468, 0x21266E1E, 0x868544FC, 0x67520318, 0x858DD0EB, 0x86D4906F, 0x51D3A9A6, 0x9767857E,
0xD6C99C68, 0x33CC2667, 0x660795F4, 0xD1F10155, 0x86D90D16, 0xE66A1603, 0xF5F86440, 0xE98E4504, 0x17424B9A, 0xB0839F59, 0xE70438F0, 0x432A7560, 0x92D89FC5, 0x37C42399, 0x11617570, 0x249605BE,
0x101BE265, 0xEE905053, 0x4540DA59, 0x9729A4FF, 0xEE850607, 0x9FD15248, 0x50E5B323, 0x7579F946, 0x498459AF, 0x919C2C1E, 0x6AD78374, 0xB1647421, 0x9EE0B603, 0xA6D35E9, 0x96696C9E, 0x587A5003,
0x49454A9, 0x6D6D9393, 0xC98677E9, 0x4095C2EF, 0x9C608505, 0x15940C2C, 0x5B60D2DB, 0x1C8151A9, 0x60F491D2, 0x2B670453, 0x4F1964BA, 0xB4430B51, 0xA9860454, 0xB0E41E46, 0x1406B7E3, 0x562701E4,
0x20D1F6A1, 0x5352A966, 0x64E1D62F, 0x92C6ED07, 0xF882561B, 0x99195A04, 0xC934E3AC, 0xD9B91810, 0x474B04F4, 0x3D9A66, 0xCA9C10AF, 0xE1942916, 0xA125355, 0x32295C8C, 0x675A64E1, 0x9D906494,
0x6C60EB1F, 0x7874C1BC, 0x1CB53038, 0x56D0F9D1, 0xB1EB954, 0x7C781540, 0x5323619E, 0x6FE055A9, 0xE7440D22, 0x7431A347, 0x6F1BE146, 0x1015BF00, 0x62381905, 0x7025B440, 0x176AF050, 0x5D270EF1,
0x46AF4C18, 0x4B86D9ED, 0x8B177010, 0xFAD09606, 0xBFA15E50, 0x4701D72D, 0xA298A55E, 0x865CA950, 0x9B5B1227, 0xE0B9175, 0xF481F1FC, 0x7E5D0440, 0x1A697FE5, 0xDE84C5F0, 0xF9975303, 0x4CB46C54,
0x3ED0D666, 0xE6B07065, 0xF0661263, 0x117C93A0, 0xD56DB4E1, 0x2E5E0E5D, 0x873DE11F, 0x5512413E, 0x5E9CE404, 0xB4C10B7F, 0xF990E956, 0x84C95E95, 0xB5E04B1F, 0x86815BA5, 0x7A5CB26C, 0x42F41F91,
0xF4592E03, 0xF51F1300, 0x99D7810F, 0xB16051A7, 0x5B57B03E, 0xEE590B55, 0xC014FDD6, 0xF9C04505, 0x1A669669, 0x984C5F84, 0x1A6FD2E3, 0x1A075E5, 0xD80E450C, 0xD66942B6, 0xD050E31F, 0xBA605440,
0x50E7856A, 0x406E53EF, 0xF01191B, 0xBF055243, 0x9B25E9E7, 0xF892A157, 0x2D598957, 0x85896B6, 0xEF44584B, 0x170667FB, 0xD1017F38, 0xCEC58687, 0x443C6C99, 0xB5B83D3, 0xFD05D91B, 0xA6D72DD3,
0x6C98D9D0, 0xD66990E0, 0xDE850B0F, 0xE9964609, 0x8605B94B, 0x6DC0D196, 0xB194A5, 0x6F13424D, 0x9ED13215, 0x7064249F, 0x5751E0B0, 0x166F26D6, 0x559EC2F8, 0xE12C5C71, 0x59898D15, 0xBC015A05,
0x90D18B0F, 0xB0E8649C, 0x170B98D0, 0x8181F491, 0xD990498C, 0x76312925, 0x93B53402, 0xCC1C4B54, 0x2F3F5006, 0x56EC4F10, 0x1AE501E4, 0x6F4D40DB, 0xA3525D69, 0x1329E491, 0x400AF995, 0x2D5C4806,
0xB091667D, 0xCC6C1627, 0x425CD9EC, 0xF552931A, 0xA6D46580, 0xF4AE50E0, 0x1979909A, 0xF1750058, 0x3365CCD9, 0xBA166500, 0x86779739, 0x67F2611, 0x55109A0B, 0x51016A65, 0x1160BBF, 0x5A6C1401,
0xC4DC9984, 0x33534244, 0x4057113A, 0xF1F431C1, 0xF8A44BE5, 0x1A976851, 0xB9B5C28F, 0xFE099454, 0xB065B8B5, 0xC0217707, 0xF3831BD1, 0x9757401B, 0x1D293580, 0x7A951200, 0x20615AF5, 0x6272757,
0xE0FC2907, 0xD860B5C1, 0x985F89C, 0xC25C2C5F, 0x7F941C01, 0xA95A450A, 0x44D89A95, 0xCE1C0C58, 0x194184F9, 0x579BB371, 0x90111F40, 0x5F037075, 0xBB516996, 0x4EC9791, 0xCCEC9C44, 0x5D8404F8,
0x48FE9D1, 0x4792E3E2, 0x6F35E381, 0x5F0350FB, 0xFF6C56, 0x6050A5F7, 0x589625C9, 0x8F79061D, 0x11D0421F, 0x9061F184, 0x504662BE, 0x8FC7D190, 0xB4404627, 0xEA1F1B03, 0xAF59D0EA, 0x79E50E04,
0xCF80445D, 0x4191D38B, 0x8F51328, 0x6653E282, 0x8C5C3573, 0x90E05929, 0xC5910D2B, 0xAE479D46, 0x2F7C1610, 0x24B1637E, 0xCB001F55, 0x118384B5, 0xA3421710, 0xAA0725F4, 0xE9C0959A, 0xE5E74114,
0x4A44CB25, 0x1763BD90, 0x1B095404, 0xCF09D095, 0x5B8344AC, 0xC5C0F9D1, 0xA7615441, 0xE990474B, 0x63536723, 0x1E305BA6, 0xE56B066B, 0xCA817916, 0x9F798578, 0xD7692E06, 0x56F01481, 0x37215100,
0x17111401, 0xF890F955, 0xA68D68D6, 0xD64A06F0, 0x9F65D9D2, 0xBE90095F, 0x9967072F, 0xE01C947B, 0x12235709, 0x589DFD, 0xBB154A41, 0x2D5DC01F, 0xC58B5C08, 0xCB64DC4C, 0x9F9393AC, 0x7303467E,
0x599EC984, 0x46E111FC, 0x7A679404, 0xA90647FB, 0x40EE949A, 0x77076E08, 0x10736423, 0x53D90AB, 0x1F510BB, 0x85CC253A, 0xAA905666, 0x45EC0B4, 0x835121F1, 0x2E5A4247, 0x1D8664A0, 0x51442CE0,
0xDD88451E, 0xE85C0113, 0xFA409601, 0xA4211627, 0x459F50BA, 0x984C458A, 0xE65A60D7, 0x958143B2, 0xD3835BFA, 0xF727F110, 0x4F074354, 0x11113D3D, 0x1558312B, 0xFF08E6D6, 0x89BC5910, 0x99F99287,
0x40D4183F, 0xDA66A550, 0xA45CE279, 0x7F97A251, 0xB1B025F, 0x70772265, 0xF0E21E16, 0xE79959B5, 0x496669C6, 0xECD00716, 0x5619ECE1, 0x6E5C11F, 0x1101FFF8, 0x55A01DCB, 0xA5053970, 0x3D448855,
0x9F92E540, 0x35B8A4D0, 0x84119CA7, 0x5429406F, 0x90E05B56, 0x3752E1D8, 0xE1B42D87, 0x9454B0F1, 0xDD382505, 0x1904ED6F, 0xD94D0C58, 0x7C52031F, 0x14B06706, 0x79928799, 0x40149BC7, 0xE9DCD6D0,
0x104F4D1, 0xD9663448, 0xBD85D003, 0x358CCDA3, 0x66F5412B, 0x6E65066A, 0x6E19B161, 0x55D1AA50, 0x785C9D30, 0x162530BE, 0x7AE64548, 0xA51B9B0E, 0xF9A56400, 0x32CE35DB, 0x17285370, 0xB0117B76,
0x7CE04741, 0x9D0C51A4, 0xBF004D55, 0xAAD5AC11, 0x6599CB41, 0xFBD09444, 0x936291E5, 0x2D5EA056, 0x1129656A, 0x7FC09506, 0xD44149C8, 0xBA750352, 0xD2431943, 0x7967E182, 0xB813C497, 0x24219465,
0xC43C9169, 0x6FD81483, 0x6152E3C, 0x59947B6, 0x27F90D12, 0x54046D2A, 0xD521309B, 0xE6653921, 0x9164F4F1, 0x1521FB4A, 0x61C54C38, 0x416484BF, 0x2776B11F, 0x59A9D884, 0xED806B54, 0x1BB0D59B,
0x4E8D91A1, 0x782FD131, 0xC05A0377, 0x7A121126, 0xDFC5D0E0, 0xEF859E05, 0x7BB60515, 0x5DD26D89, 0x2E291157, 0x9F2F3530, 0xC08156A9, 0x6D91CDC5, 0xF1548117, 0xB52517FE, 0x2053E947, 0xAB5785C5,
0x479A91E0, 0x9F90FE01, 0x312484DE, 0x441BC1A5, 0x66B501F5, 0xC4617A5B, 0x916BB0F4, 0x20657C7A, 0xE25390FF, 0x1F3861B4, 0x550586AF, 0x6B8599D4, 0x9F4294EC, 0xF4D48147, 0xD0965233, 0x1E05CFED,
0x59176903, 0x2D2976C, 0xCD33338C, 0x1441E4AD, 0x4196C088, 0xF6079403, 0x969DC030, 0x64990D29, 0x2453F372, 0x8658909B, 0x458046FB, 0x11A185C4, 0x58FC1144, 0x31E641A, 0x8E47709E, 0xF1611277,
0x61F907A, 0xB7100F5C, 0x5A170B19, 0x160557E, 0x55B39D0, 0xC0C556EF, 0xBBF50299, 0xD1D23E12, 0x69588543, 0x4546890, 0x90FA4115, 0x11C5ED8, 0x68B74789, 0xC06F0117, 0x172790AD, 0x606DAC54,
0x8D488154, 0x8444EC98, 0x9A6592D3, 0xE5E15040, 0xDB031762, 0x2F099450, 0x404F0F5A, 0x1D09E9F4, 0x56045E1F, 0xF9C99F00, 0x136C6599, 0xB0C36E91, 0x1C2835B0, 0x60BE6B5, 0x462FB4D1, 0xCB19B6D7,
0x6C48C4D9, 0xD0D19B69, 0xA5E544FC, 0xE4851E31, 0x41E464B8, 0x59C6F9D4, 0xF0A6527E, 0x5333D1E0, 0x4862F606, 0x72370631, 0xC2B490C5, 0x4643DE5C, 0x54F9C489, 0xFC526C40, 0xB0A56895, 0x45C1C134,
0x768F1981, 0x6E295005, 0x950B6964, 0x4B4CD5E9, 0xEB131245, 0x447F844, 0x131B1D76, 0x99981A7, 0x500B1F0A, 0x91011B1B, 0x64E64709, 0xA45C8145, 0xB53001D1, 0xDB462885, 0xBE06D015, 0x4858541B,
0x6C86C3B5, 0x70A1967B, 0x969B0414, 0xEF485C04, 0x7FE151C1, 0xEBE7F444, 0x5341990B, 0x71D58060, 0xF5A61E10, 0x79F2C53, 0xE5E41F00, 0xBFF4505, 0xE01BB4E9, 0xBFE3905D, 0xDB7664D0, 0x956A065B,
0x12894E1, 0xBF14B401, 0x30F7A97, 0xA85C97E0, 0xF40643E9, 0xF8506706, 0x9B9B06AB, 0x5AA05995, 0x45448E3D, 0xE1D1907F, 0x24F4C22C, 0x82C2959A, 0xBF065243, 0x8785812D, 0x9A94A916, 0x315B2C60,
0x523291D9, 0xF2991609, 0x2830579C, 0x999973CC, 0x4B14C098, 0xC1C5053, 0xD1C1C68A, 0x64B67F3, 0xD09B801D, 0x6A7C1044, 0x48580954, 0x859F095A, 0x46533620, 0x592C852D, 0xA5419141, 0x4D16A1D0,
0xB0B153B, 0xE1944E4A, 0x21371633, 0xCEC9C4E4, 0xA5D70E08, 0x4E4C4DD, 0x591CAC04, 0x5B021DBC, 0x9967CC33, 0x81C0E5FD, 0xF651C104, 0x966760C9, 0x95E1303C, 0xEC5C6440, 0x50BF9F4, 0x3561C31E,
0x45A096AD, 0xFF92500B, 0xF6598904, 0x19F9C50B, 0x60C64D18, 0xD640584F, 0x2194E1F3, 0xF031163D, 0x101B56E0, 0xDE5921DA, 0xBF01FD1, 0x706FD94, 0x34CB30CE, 0xB857061C, 0x5225B609, 0xC43C3135,
0xC03B9350, 0xF001676F, 0xF0C18767, 0x6611391B, 0x4F89A46, 0x1F42441A, 0x649F0F46, 0xB5E0015E, 0x7966830E, 0x9740F4F7, 0xC8CC4451, 0x59BD404B, 0xCC9435E1, 0x3B15B2A0, 0x123C1607, 0x59468D90,
0x40E0D749, 0x14C1F46E, 0x448BD6A9, 0x659AF556, 0x50E7435B, 0xAED2933C, 0x56051308, 0xCE146E50, 0x1C904F10, 0xEC485E0D, 0x169A936C, 0x51633605, 0xDB57A830, 0x62957AB, 0xF4F14448, 0x45CAD5D,
0x695CC006, 0x104606B9, 0x81D1D86D, 0x8649F050, 0x20D1DB56, 0xE5E53CC0, 0xCB470256, 0xC629E494, 0x49385F8F, 0x65056E31, 0x9C86F095, 0xC8CC9865, 0x869D8118, 0xD1021F11, 0x87DAD1E2, 0xAF04D500,
0x4518464, 0xB857B085, 0x4505E834, 0xB1E25F04, 0xD8DD9084, 0x35978C33, 0x11A64257, 0x991A1E41, 0x66B06752, 0xBAF41443, 0xBD4A4544, 0xD6753B66, 0xCAC6C639, 0x154480FD, 0x99D19BC, 0x546EE442,
0xD0C0193E, 0x7491E21F, 0x24B0B3C7, 0xF981D501, 0x74438B58, 0x54C47391, 0x9EDD1180, 0xE697874F, 0x2E41F8E5, 0x468FDD9, 0x4F1E439C, 0x5542EB81, 0xA554FAE0, 0x1E43E2B0, 0x606D5898, 0x6587C4E8,
0x6B86C511, 0x6050A6D3, 0x1C523485, 0xF2530396, 0x4A2D5884, 0x6FD09193, 0x29978F84, 0xFE490999, 0x9189675E, 0x99277401, 0xDD6F2494, 0x8CD39344, 0xB5783D9, 0xC859EE, 0xAC701503, 0x8FC1D7E1,
0xA0854317, 0xD09E8444, 0x4510F2BD, 0xED484CD4, 0xDE5D0C1C, 0xC0854367, 0xD1642134, 0x503876E, 0x7C046A5E, 0xE5644035, 0x3D043173, 0x5DE8991, 0xA603D6D0, 0x70644B1B, 0x41C3F55, 0x799EC50,
0x5C1D005A, 0xB54027BB, 0x501703FB, 0xCE5E2D84, 0xC248776B, 0x34C31DB1, 0xD08146F6, 0x6194D488, 0x902D95DF, 0x56239699, 0x79566996, 0x46366797, 0x2075B449, 0xE619904B, 0xF1FC491D, 0x4026925F,
0x85CE994, 0xC62D651B, 0xE0F50B4, 0x60976F55, 0x5B7706A6, 0x49471F41, 0xF6D91352, 0x551A062F, 0x4145CFCA, 0x55B14234, 0xABF12531, 0xB915F0F4, 0x8AC7E275, 0x4679C12F, 0xE21353EC, 0x693602A5,
0x4BC256AB, 0x463D64A6, 0x5E7C6B8, 0x143983F0, 0x3646871A, 0x601D1FE, 0x4D4601FC, 0xB489D0DF, 0xF7814046, 0xE5A50BA7, 0xB34A116, 0x707C4151, 0x804466BB, 0xCF450C5C, 0x1329B9F4, 0xFE400655,
0xF4A1431B, 0x51D3202D, 0x74B0A444, 0xA6F1612, 0x6C584C8C, 0xC014F665, 0xD3982957, 0x36C136D1, 0x5C45CCD0, 0x9F459882, 0x91A753A2, 0xA9752C4C, 0x421B4297, 0x776A2313, 0x35397906, 0x48065762,
0x1129B440, 0xAF410B5D, 0x7F9582D2, 0x4B51E650, 0x891D0D77, 0xE59909E5, 0x705D234B, 0xC3DA9055, 0xF1B11501, 0x44BC94D9, 0x1FA55C48, 0x2A422657, 0xC41091A1, 0x18970E74, 0x7699099, 0x10616AD7,
0x1AE19F10, 0x475A6C31, 0xA27257E9, 0xC484582C, 0xD699665A, 0x17C94C18, 0xE1569B, 0x11855ABF, 0xA580D607, 0x64C18B7E, 0x7030A605, 0x2B3660D0, 0x165BE103, 0x809E1867, 0x47407FD7, 0x34E4858B,
0x90ED740B, 0x1B43C2F5, 0xF451527C, 0x5723E1C5, 0xE1D1065B, 0xA51A9DF4, 0x7F099A85, 0x4157030B, 0x1D9F60B8, 0xC11491FD, 0xFE850A55, 0xB990431F, 0xA0C78D16, 0x6F5A4D40, 0xFE4411E, 0x34426B56,
0x5014FE0F, 0xBD5DC51A, 0xC63637C3, 0xE1B459B2, 0xFA871B05, 0x4544D0AD, 0xE5E5093D, 0x3306C5DD, 0x47C33484, 0x6F1E146, 0xE4974F1, 0xDD48050F, 0xAB151E12, 0x5169FC92, 0x5055B27A, 0xD142AD5C,
0x4B16B0F4, 0x312BC955, 0x75B10FC, 0x4A9E0458, 0xA65D34CE, 0x90D366CF, 0x10099D29, 0x8B358419, 0x87D2B5DA, 0x31162EF5, 0xF61989C5, 0x1F4341F8, 0xBC4C6D9C, 0x11A759, 0x9553A21, 0xCFC41530,
0x1F05B610, 0x5B2F64C0, 0x117F1124, 0xF3D24D9, 0x44447874, 0x74351A06, 0x64C48979, 0x63C185B, 0x91F094A4, 0x4E1D1C48, 0x969103AE, 0x9CFC6067, 0x2FC94045, 0xED3844C4, 0x676C935B, 0x6E2091B5,
0x2D9744EC, 0x6A111227, 0x78DB90E5, 0x8045587E, 0xC4E4591E, 0x479BE1E0, 0x5B667499, 0x50E0493E, 0xFB54819C, 0x431491F8, 0xD3EF50E0, 0x7363926, 0x50FF6440, 0x44DC6C9D, 0xF6D9965A, 0x548499,
0x150A1403, 0xC7324CC7, 0xAFF0474B, 0xB479966E, 0xAD9E5451, 0xF9913C41, 0x1F09B46, 0x3E35461E, 0xA0C66C10, 0xA4D1D103, 0xA05470B4, 0x6F1A3531, 0x4047B2F5, 0x11472CE0, 0x3AE41631, 0xFD36A905,
0xF4436F91, 0x4A419766, 0x50699E67, 0x5408D93D, 0x112711AF, 0xF500642F, 0x160BD66A, 0x11B0BFE, 0xC2C55CA1, 0x31CF859, 0xC6C95401, 0xD9C0E5C1, 0xE46056AE, 0xF5E30710, 0xA5FF0150, 0x4454203F,
0xB6660959, 0x2522776, 0x2D586970, 0x7370757, 0x60953317, 0x81E7072D, 0x68149E6, 0x653834, 0x363D9D81, 0xB429113F, 0x566C0258, 0x51F92F00, 0xE19C5812, 0x11FB150, 0x59A65200, 0x2775E0F4,
0x106752A3, 0x7574326, 0x5CC34739, 0xDAD54805, 0x5E936BC7, 0xCFCAC5C9, 0x2FC42555, 0xD06994C0, 0x5CAC7400, 0x5D84D074, 0x6D668604, 0xE50B19BD, 0xE7969F55, 0xFF925005, 0x65B266B, 0x4743035E,
0x972C44C7, 0x5AB47260, 0x77123580, 0xD92DB404, 0x451BB8D1, 0xB94B4447, 0x9B955210, 0xA253815, 0x79AC5184, 0xE3D10C5C, 0x452410FB, 0xDFD3A148, 0xE0336E46, 0x4E287599, 0x12B750A9, 0xDAC0DE47,
0xC18459A9, 0xC764CD30, 0x2334675E, 0xF6490DB8, 0x49172679, 0x659B6994, 0x7A912691, 0xB2B76074, 0xB9E58704, 0xCC33D9A9, 0x207F6A7, 0xC405F801, 0x560297CF, 0x48140E5F, 0xC44E676A, 0xB191DED,
0x11035F60, 0x5B1F60B4, 0xA4EB1443, 0x5A6C06E6, 0x1915AE9, 0x33CD2696, 0x879F4444, 0xD442170B, 0xC9C9D9D6, 0xA1D4C124, 0xC40366B7, 0x22497E9, 0xCD18D7F9, 0x99FD111A, 0x9F6B53A1, 0xE051ABFC,
0xBC10071D, 0xA566A759, 0x6DBC5E5, 0x4A9DA851, 0x2266B15, 0x123D6E9B, 0x66E05449, 0xD10D3870, 0x1706E462, 0xAF0B9B51, 0xE0109D6C, 0xF144861B, 0x813ED01B, 0xB6496C10, 0x1ED00768, 0x60747C10,
0x46494376, 0x5E4C10B0, 0x573D5906, 0x5A02E5FE, 0xB3E005F2, 0xC05C4F45, 0xDBD58982, 0x6D13171F, 0x9741FA00, 0x61DB852, 0x8D46D299, 0x47421E4D, 0x74A94E6, 0x4DA31CD3, 0x681602FF, 0xC67962D7,
0x1560BBA, 0xF8849D85, 0x7DD6D5B8, 0x5B6B4640, 0x441A349F, 0x61B3601F, 0xD550E2D3, 0xCF18111B, 0x97673333, 0xC59909E4, 0x57DAE840, 0xBB910E5, 0x144484F9, 0xA85C2511, 0x4092D5EA, 0x89910391,
0x7D385500, 0x3D1D2176, 0x802FD41B, 0xA5D94890, 0x656E9099, 0xE66906E6, 0x4331A2D5, 0x4478E25B, 0xE78C41D, 0x3565D979, 0x4147133B, 0x89C6C64A, 0x45C9CDCF, 0x532315FA, 0x3E5A0D04, 0xB111DBFC,
0x63162FF7, 0x9E0B9CD0, 0xA758E46B, 0xA097421F, 0xAE544262, 0xA59A5DDD, 0x3732261D, 0x71736790, 0x8895A173, 0x687470B1, 0x57E564FE, 0x6A765404, 0xF5E21482, 0x667150E1, 0xC0A764D9, 0x89950C14,
0x3756912B, 0x3913499, 0x6FB553C3, 0x29920995, 0x750EC738, 0xC35C6992, 0xAA55E404, 0xCB4B0753, 0x20396156, 0xDB04F4C1, 0x7876825F, 0xD562319A, 0x592D121B, 0xF9360956, 0x47811B47, 0xD9BD5884,
0x170A35E, 0x844EE59A, 0x8684EDDA, 0x405F1000, 0x813641A7, 0x176681A2, 0x7DBE6440, 0x87C6A54, 0xC485D905, 0x1AB0811D, 0xDBA59291, 0xFA52817C, 0x3981E5F0, 0x5109FEE5, 0xC1284557, 0x573B2703,
0x30640659, 0xF1D3E997, 0x906253B7, 0x9076C3FD, 0xCA34D35C, 0xF481491C, 0x5976A4C0, 0x3FF1144, 0x26170649, 0x196B9065, 0x90693D09, 0xF0211A17, 0xAF016DD4, 0x4E449C48, 0x6F9A84F4, 0xB4449818,
0x606DB144, 0x916B97A6, 0xEBD0FD19, 0xAA08D541, 0xD71BE505, 0x1E6507AF, 0x3E4B1361, 0xF391619, 0x4264BC50, 0x60563321, 0x4A370617, 0x436452E7, 0xBE107604, 0xDDA9C18E, 0xBF5B0371, 0x906F1107,
0x55C5C83C, 0x31364782, 0x68DD9F1, 0xF99607E6, 0x2996367E, 0x5AF4458, 0x96582D30, 0xC4E4D98A, 0xE68719BE, 0x20C56C64, 0x5A550246, 0xB759B079, 0x64439BFB, 0xB1240D2E, 0x271350B2, 0x4314AF56,
0xA9998D72, 0x5E8D1481, 0xB65D7253, 0x669392B8, 0xF0919B6C, 0x9F9790DD, 0x60979838, 0x3D625168, 0x12196D58, 0x59E9448A, 0x30547C92, 0x8E317590, 0x7B954843, 0x323D4907, 0x5A4D5308, 0xD10490DB,
0x92CE4511, 0xB9353125, 0xA91F4050, 0xDC485D08, 0x2F11742, 0xB9B4090F, 0xD09891FF, 0x9503936, 0x80E3505F, 0xED99405F, 0xEA9058DE, 0x55019FC0, 0x9A1F412C, 0x471660DA, 0xF4D0C505, 0x4926666D,
0x4191C3EF, 0x1A0526F5, 0x55F889C7, 0xD33CC738, 0x7A66C601, 0xF701C662, 0x6E953906, 0x6E6559AD, 0x1626C57A, 0xE0075266, 0x67069A5B, 0xA5F70401, 0x47786090, 0x8111C53B, 0xB4640B1A, 0x269D5DAE,
0x50C4A474, 0x2A94987D, 0x44C05226, 0x6114423F, 0xB8474F1D, 0x8E0E05D8, 0x87B40FD2, 0x1F4392FD, 0x170E995, 0x1563DE0, 0x6F9AC5CA, 0x752A0D1D, 0xFF0A44D0, 0x7FD0D366, 0x51A62E05, 0xD0D5E8DE,
0x56113F30, 0xE4056D87, 0xAB3B5100, 0x46539C6, 0x7F676290, 0xB851130D, 0xF5E09183, 0x64AC5D2D, 0x55F9404B, 0xE256F977, 0x70617760, 0xEE48E450, 0xDD5E35C8, 0x94D400F8, 0x446A0667, 0xEA935184,
0x464B07F8, 0x8C74B6D4, 0x8D9D59A5, 0xCEC6ADD5, 0x2D0956A3, 0xA5BA5103, 0x31353123, 0x55FF4140, 0xA2440F15, 0x89266792, 0x44E1E154, 0xE5B41338, 0x2FCE9901, 0x95E6811B, 0x475C871, 0xAE9E4A44,
0x5E0D141, 0xE702D66B, 0x474707E2, 0x6486CBC5, 0x7476016E, 0xB1D29B56, 0x7A34C605, 0x51427906, 0xE7967F97, 0x65D29C90, 0x55081AD, 0xD6E64045, 0x67134368, 0x9101E6DD, 0x68957841, 0x3D3D5909,
0xE0525BAF, 0x134A4D3C, 0xC1B11909, 0x16493B76, 0x6B96D0A0, 0xBF0E79D6, 0x2C7096F9, 0x90FC04D6, 0x1742E472, 0x531CA1AC, 0xDAD08646, 0x9066669, 0xA7588D86, 0x913C54C2, 0x742A4514, 0x17111ABC,
0xDDD7EDE5, 0x6699C498, 0x75313135, 0xE4995F64, 0x2582667E, 0x9C5C4C48, 0x1D0D10D9, 0x5B4C164, 0x46E616CB, 0xD18444BF, 0xE1D4695C, 0x40095E99, 0xECE02955, 0xFF40A411, 0x54D2421B, 0xE1C7DB05,
0x80E5095D, 0x6745E907, 0xA6197007, 0x760674F9, 0x1522170A, 0x65181FF, 0xE0FDA45, 0x521C7867, 0x6B91D382, 0x657A83C1, 0x73C98C35, 0xB3CC31D6, 0x164B257B, 0x6F9605F9, 0x52F49EB5, 0x87592907,
0x1B10D6F8, 0x949FD54, 0x910315BA, 0x40F09B47, 0xB761C144, 0x6D039D5, 0x59B113F, 0xE5C6D9, 0x176990FB, 0x90F8A465, 0x6DB8D144, 0x4552E392, 0xC94396B9, 0x36315A97, 0x7E874215, 0x40116F6F,
0xDF193401, 0x70689317, 0x7E021740, 0xD5C044BD, 0xF1636E94, 0x5B0D34A1, 0x1027F689, 0x44A45590, 0xD443035F, 0xD7A4418C, 0xD949F480, 0x84C1E154, 0x6090966E, 0x40B56C1E, 0x9669C540, 0xA6305505,
0x1667E244, 0xC245885D, 0xD504A5E, 0xA4F0C549, 0x358472A8, 0xC414BF20, 0x43369729, 0xB1112711, 0x78644352, 0x811CB856, 0x6F9792F0, 0x1E1E4195, 0x62C58C14, 0x2619D3A2, 0x998056BD, 0x653F3410,
0x919184E4, 0x64906D6C, 0xE3D7996, 0x47660563, 0x50A91F03, 0x49F8C5CB, 0xFE15235E, 0x9783451, 0x716257B7, 0x1AB5351, 0x1710DC84, 0x3FA01D5A, 0x9844AD64, 0xE4D901BC, 0xEE546352, 0xF9547987,
0xF6834B47, 0xB651F6D, 0xE09C9045, 0xE491424E, 0x94F9050B, 0x7DA6091D, 0xB5C14D74, 0x123A699D, 0x1F413CC3, 0x9F434158, 0x55D8C8C4, 0x51106FF, 0xD36CB1C, 0x1541FCE1, 0xC3344ED3, 0x3E29D905,
0x150A6579, 0x502475A, 0x454B0F04, 0x65902722, 0x9EA055, 0x26568CC5, 0x7EB1443, 0x42186F96, 0x132357A9, 0x74091F13, 0xEE55480F, 0x7A0B4A45, 0xF01A8637, 0x937D68D5, 0xE2611115, 0x8A853663,
0x60D295AB, 0x1946461A, 0xBB5190A1, 0x6D514218, 0x7C6482CF, 0x8759ACC8, 0x7E86C805, 0x42D3755F, 0xF4C10529, 0x471F1400, 0x5394238C, 0x112D54A8, 0x55E450F8, 0xDC2191BA, 0xA5F93011, 0x1A2159F5,
0x4051A297, 0x66066999, 0xA644C058, 0x54EC9148, 0xFAFB0146, 0x1150FCEC, 0x4141D171, 0xF09F0444, 0x488E5E0E, 0x3D190F10, 0x406E405F, 0xFE9904AD, 0x101F5C6F, 0x454B093B, 0x40946C1F, 0xF0B966D,
0xC16464DB, 0x91E2D6F9, 0x97789074, 0x537C5C40, 0x44A533C3, 0x74102B33, 0x40D425C1, 0xD748C936, 0x786619C9, 0x40917985, 0x66594C1C, 0x2F05D2D2, 0x8C711B4, 0xF9602757, 0x1013854A, 0x7D6296D0,
0x3D99B8F0, 0x495D3243, 0x916864BE, 0x4E43B947, 0xE756A110, 0xA8D09D7D, 0xBF625095, 0xC1D48F87, 0x8BD47099, 0xDB631315, 0xE54139A0, 0xE4DC9C63, 0x846C2544, 0x4F0E04D1, 0xE0B15B26, 0x3249499D,
}

View file

@ -1,675 +0,0 @@
// basisu_global_selector_palette.h
// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
//
// TODO: NONE of this is used in .basis/.ktx2 files. It will be deleted soon.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "basisu_transcoder_internal.h"
#include <algorithm>
namespace basist
{
class etc1_global_palette_entry_modifier
{
public:
enum { cTotalBits = 15, cTotalValues = 1 << cTotalBits };
etc1_global_palette_entry_modifier(uint32_t index = 0)
{
#ifdef _DEBUG
static bool s_tested;
if (!s_tested)
{
s_tested = true;
for (uint32_t i = 0; i < cTotalValues; i++)
{
etc1_global_palette_entry_modifier m(i);
etc1_global_palette_entry_modifier n = m;
assert(n.get_index() == i);
}
}
#endif
set_index(index);
}
void set_index(uint32_t index)
{
assert(index < cTotalValues);
m_rot = index & 3;
m_flip = (index >> 2) & 1;
m_inv = (index >> 3) & 1;
m_contrast = (index >> 4) & 3;
m_shift = (index >> 6) & 1;
m_median = (index >> 7) & 1;
m_div = (index >> 8) & 1;
m_rand = (index >> 9) & 1;
m_dilate = (index >> 10) & 1;
m_shift_x = (index >> 11) & 1;
m_shift_y = (index >> 12) & 1;
m_erode = (index >> 13) & 1;
m_high_pass = (index >> 14) & 1;
}
uint32_t get_index() const
{
return m_rot | (m_flip << 2) | (m_inv << 3) | (m_contrast << 4) | (m_shift << 6) | (m_median << 7) | (m_div << 8) | (m_rand << 9) | (m_dilate << 10) | (m_shift_x << 11) | (m_shift_y << 12) | (m_erode << 13) | (m_high_pass << 14);
}
void clear()
{
basisu::clear_obj(*this);
}
uint8_t m_contrast;
bool m_rand;
bool m_median;
bool m_div;
bool m_shift;
bool m_inv;
bool m_flip;
bool m_dilate;
bool m_shift_x;
bool m_shift_y;
bool m_erode;
bool m_high_pass;
uint8_t m_rot;
};
enum modifier_types
{
cModifierContrast,
cModifierRand,
cModifierMedian,
cModifierDiv,
cModifierShift,
cModifierInv,
cModifierFlippedAndRotated,
cModifierDilate,
cModifierShiftX,
cModifierShiftY,
cModifierErode,
cModifierHighPass,
cTotalModifiers
};
#define ETC1_GLOBAL_SELECTOR_CODEBOOK_MAX_MOD_BITS (etc1_global_palette_entry_modifier::cTotalBits)
struct etc1_selector_palette_entry
{
etc1_selector_palette_entry()
{
clear();
}
void clear()
{
basisu::clear_obj(*this);
}
uint8_t operator[] (uint32_t i) const { assert(i < 16); return m_selectors[i]; }
uint8_t&operator[] (uint32_t i) { assert(i < 16); return m_selectors[i]; }
void set_uint32(uint32_t v)
{
for (uint32_t byte_index = 0; byte_index < 4; byte_index++)
{
uint32_t b = (v >> (byte_index * 8)) & 0xFF;
m_selectors[byte_index * 4 + 0] = b & 3;
m_selectors[byte_index * 4 + 1] = (b >> 2) & 3;
m_selectors[byte_index * 4 + 2] = (b >> 4) & 3;
m_selectors[byte_index * 4 + 3] = (b >> 6) & 3;
}
}
uint32_t get_uint32() const
{
return get_byte(0) | (get_byte(1) << 8) | (get_byte(2) << 16) | (get_byte(3) << 24);
}
uint32_t get_byte(uint32_t byte_index) const
{
assert(byte_index < 4);
return m_selectors[byte_index * 4 + 0] |
(m_selectors[byte_index * 4 + 1] << 2) |
(m_selectors[byte_index * 4 + 2] << 4) |
(m_selectors[byte_index * 4 + 3] << 6);
}
uint8_t operator()(uint32_t x, uint32_t y) const { assert((x < 4) && (y < 4)); return m_selectors[x + y * 4]; }
uint8_t&operator()(uint32_t x, uint32_t y) { assert((x < 4) && (y < 4)); return m_selectors[x + y * 4]; }
uint32_t calc_distance(const etc1_selector_palette_entry &other) const
{
uint32_t dist = 0;
for (uint32_t i = 0; i < 8; i++)
{
int delta = static_cast<int>(m_selectors[i]) - static_cast<int>(other.m_selectors[i]);
dist += delta * delta;
}
return dist;
}
#if 0
uint32_t calc_hamming_dist(const etc1_selector_palette_entry &other) const
{
uint32_t dist = 0;
for (uint32_t i = 0; i < 4; i++)
dist += g_hamming_dist[get_byte(i) ^ other.get_byte(i)];
return dist;
}
#endif
etc1_selector_palette_entry get_inverted() const
{
etc1_selector_palette_entry result;
for (uint32_t i = 0; i < 16; i++)
result.m_selectors[i] = 3 - m_selectors[i];
return result;
}
etc1_selector_palette_entry get_divided() const
{
etc1_selector_palette_entry result;
const uint8_t div_selector[4] = { 2, 0, 3, 1 };
for (uint32_t i = 0; i < 16; i++)
result.m_selectors[i] = div_selector[m_selectors[i]];
return result;
}
etc1_selector_palette_entry get_shifted(int delta) const
{
etc1_selector_palette_entry result;
for (uint32_t i = 0; i < 16; i++)
result.m_selectors[i] = static_cast<uint8_t>(basisu::clamp<int>(m_selectors[i] + delta, 0, 3));
return result;
}
etc1_selector_palette_entry get_randomized() const
{
uint32_t seed = get_uint32();
etc1_selector_palette_entry result;
for (uint32_t y = 0; y < 4; y++)
{
for (uint32_t x = 0; x < 4; x++)
{
int s = (*this)(x, y);
// between 0 and 10
uint32_t i = basisd_urand(seed, 6) + basisd_urand(seed, 6);
if (i == 0)
s -= 2;
else if (i == 10)
s += 2;
else if (i < 3)
s -= 1;
else if (i > 7)
s += 1;
result(x, y) = static_cast<uint8_t>(basisu::clamp<int>(s, 0, 3));
}
}
return result;
}
etc1_selector_palette_entry get_contrast(int table_index) const
{
assert(table_index < 4);
etc1_selector_palette_entry result;
static const uint8_t s_contrast_tables[4][4] =
{
{ 0, 1, 2, 3 }, // not used
{ 0, 0, 3, 3 },
{ 1, 1, 2, 2 },
{ 1, 1, 3, 3 }
};
for (uint32_t i = 0; i < 16; i++)
{
result[i] = s_contrast_tables[table_index][(*this)[i]];
}
return result;
}
etc1_selector_palette_entry get_dilated() const
{
etc1_selector_palette_entry result;
for (uint32_t y = 0; y < 4; y++)
{
for (uint32_t x = 0; x < 4; x++)
{
uint32_t max_selector = 0;
for (int yd = -1; yd <= 1; yd++)
{
int fy = y + yd;
if ((fy < 0) || (fy > 3))
continue;
for (int xd = -1; xd <= 1; xd++)
{
int fx = x + xd;
if ((fx < 0) || (fx > 3))
continue;
max_selector = basisu::maximum<uint32_t>(max_selector, (*this)(fx, fy));
}
}
result(x, y) = static_cast<uint8_t>(max_selector);
}
}
return result;
}
etc1_selector_palette_entry get_eroded() const
{
etc1_selector_palette_entry result;
for (uint32_t y = 0; y < 4; y++)
{
for (uint32_t x = 0; x < 4; x++)
{
uint32_t min_selector = 99;
for (int yd = -1; yd <= 1; yd++)
{
int fy = y + yd;
if ((fy < 0) || (fy > 3))
continue;
for (int xd = -1; xd <= 1; xd++)
{
int fx = x + xd;
if ((fx < 0) || (fx > 3))
continue;
min_selector = basisu::minimum<uint32_t>(min_selector, (*this)(fx, fy));
}
}
result(x, y) = static_cast<uint8_t>(min_selector);
}
}
return result;
}
etc1_selector_palette_entry get_shift_x() const
{
etc1_selector_palette_entry result;
for (uint32_t y = 0; y < 4; y++)
{
for (uint32_t x = 0; x < 4; x++)
{
int sx = x - 1;
if (sx < 0)
sx = 0;
result(x, y) = (*this)(sx, y);
}
}
return result;
}
etc1_selector_palette_entry get_shift_y() const
{
etc1_selector_palette_entry result;
for (uint32_t y = 0; y < 4; y++)
{
int sy = y - 1;
if (sy < 0)
sy = 3;
for (uint32_t x = 0; x < 4; x++)
result(x, y) = (*this)(x, sy);
}
return result;
}
etc1_selector_palette_entry get_median() const
{
etc1_selector_palette_entry result;
for (uint32_t y = 0; y < 4; y++)
{
for (uint32_t x = 0; x < 4; x++)
{
// ABC
// D F
// GHI
uint8_t selectors[8];
uint32_t n = 0;
for (int yd = -1; yd <= 1; yd++)
{
int fy = y + yd;
if ((fy < 0) || (fy > 3))
continue;
for (int xd = -1; xd <= 1; xd++)
{
if ((xd | yd) == 0)
continue;
int fx = x + xd;
if ((fx < 0) || (fx > 3))
continue;
selectors[n++] = (*this)(fx, fy);
}
}
std::sort(selectors, selectors + n);
result(x, y) = selectors[n / 2];
}
}
return result;
}
etc1_selector_palette_entry get_high_pass() const
{
etc1_selector_palette_entry result;
static const int kernel[3][3] =
{
{ 0, -1, 0 },
{ -1, 8, -1 },
{ 0, -1, 0 }
};
for (uint32_t y = 0; y < 4; y++)
{
for (uint32_t x = 0; x < 4; x++)
{
// ABC
// D F
// GHI
int sum = 0;
for (int yd = -1; yd <= 1; yd++)
{
int fy = y + yd;
fy = basisu::clamp<int>(fy, 0, 3);
for (int xd = -1; xd <= 1; xd++)
{
int fx = x + xd;
fx = basisu::clamp<int>(fx, 0, 3);
int k = (*this)(fx, fy);
sum += k * kernel[yd + 1][xd + 1];
}
}
sum = sum / 4;
result(x, y) = static_cast<uint8_t>(basisu::clamp<int>(sum, 0, 3));
}
}
return result;
}
etc1_selector_palette_entry get_flipped_and_rotated(bool flip, uint32_t rotation_index) const
{
etc1_selector_palette_entry temp;
if (flip)
{
for (uint32_t y = 0; y < 4; y++)
for (uint32_t x = 0; x < 4; x++)
temp(x, y) = (*this)(x, 3 - y);
}
else
{
temp = *this;
}
etc1_selector_palette_entry result;
switch (rotation_index)
{
case 0:
result = temp;
break;
case 1:
for (uint32_t y = 0; y < 4; y++)
for (uint32_t x = 0; x < 4; x++)
result(x, y) = temp(y, 3 - x);
break;
case 2:
for (uint32_t y = 0; y < 4; y++)
for (uint32_t x = 0; x < 4; x++)
result(x, y) = temp(3 - x, 3 - y);
break;
case 3:
for (uint32_t y = 0; y < 4; y++)
for (uint32_t x = 0; x < 4; x++)
result(x, y) = temp(3 - y, x);
break;
default:
assert(0);
break;
}
return result;
}
etc1_selector_palette_entry get_modified(const etc1_global_palette_entry_modifier &modifier) const
{
etc1_selector_palette_entry r(*this);
if (modifier.m_shift_x)
r = r.get_shift_x();
if (modifier.m_shift_y)
r = r.get_shift_y();
r = r.get_flipped_and_rotated(modifier.m_flip != 0, modifier.m_rot);
if (modifier.m_dilate)
r = r.get_dilated();
if (modifier.m_erode)
r = r.get_eroded();
if (modifier.m_high_pass)
r = r.get_high_pass();
if (modifier.m_rand)
r = r.get_randomized();
if (modifier.m_div)
r = r.get_divided();
if (modifier.m_shift)
r = r.get_shifted(1);
if (modifier.m_contrast)
r = r.get_contrast(modifier.m_contrast);
if (modifier.m_inv)
r = r.get_inverted();
if (modifier.m_median)
r = r.get_median();
return r;
}
etc1_selector_palette_entry apply_modifier(modifier_types mod_type, const etc1_global_palette_entry_modifier &modifier) const
{
switch (mod_type)
{
case cModifierContrast:
return get_contrast(modifier.m_contrast);
case cModifierRand:
return get_randomized();
case cModifierMedian:
return get_median();
case cModifierDiv:
return get_divided();
case cModifierShift:
return get_shifted(1);
case cModifierInv:
return get_inverted();
case cModifierFlippedAndRotated:
return get_flipped_and_rotated(modifier.m_flip != 0, modifier.m_rot);
case cModifierDilate:
return get_dilated();
case cModifierShiftX:
return get_shift_x();
case cModifierShiftY:
return get_shift_y();
case cModifierErode:
return get_eroded();
case cModifierHighPass:
return get_high_pass();
default:
assert(0);
break;
}
return *this;
}
etc1_selector_palette_entry get_modified(const etc1_global_palette_entry_modifier &modifier, uint32_t num_order, const modifier_types *pOrder) const
{
etc1_selector_palette_entry r(*this);
for (uint32_t i = 0; i < num_order; i++)
{
r = r.apply_modifier(pOrder[i], modifier);
}
return r;
}
bool operator< (const etc1_selector_palette_entry &other) const
{
for (uint32_t i = 0; i < 16; i++)
{
if (m_selectors[i] < other.m_selectors[i])
return true;
else if (m_selectors[i] != other.m_selectors[i])
return false;
}
return false;
}
bool operator== (const etc1_selector_palette_entry &other) const
{
for (uint32_t i = 0; i < 16; i++)
{
if (m_selectors[i] != other.m_selectors[i])
return false;
}
return true;
}
private:
uint8_t m_selectors[16];
};
typedef basisu::vector<etc1_selector_palette_entry> etc1_selector_palette_entry_vec;
extern const uint32_t g_global_selector_cb[];
extern const uint32_t g_global_selector_cb_size;
#define ETC1_GLOBAL_SELECTOR_CODEBOOK_MAX_PAL_BITS (12)
struct etc1_global_selector_codebook_entry_id
{
uint32_t m_palette_index;
etc1_global_palette_entry_modifier m_modifier;
etc1_global_selector_codebook_entry_id(uint32_t palette_index, const etc1_global_palette_entry_modifier &modifier) : m_palette_index(palette_index), m_modifier(modifier) { }
etc1_global_selector_codebook_entry_id() { }
void set(uint32_t palette_index, const etc1_global_palette_entry_modifier &modifier) { m_palette_index = palette_index; m_modifier = modifier; }
};
typedef basisu::vector<etc1_global_selector_codebook_entry_id> etc1_global_selector_codebook_entry_id_vec;
class etc1_global_selector_codebook
{
public:
etc1_global_selector_codebook() { }
etc1_global_selector_codebook(uint32_t N, const uint32_t *pEntries) { init(N, pEntries); }
void init(uint32_t N, const uint32_t* pEntries);
void print_code(FILE *pFile);
void clear()
{
m_palette.clear();
}
uint32_t size() const { return (uint32_t)m_palette.size(); }
const etc1_selector_palette_entry_vec &get_palette() const
{
return m_palette;
}
etc1_selector_palette_entry get_entry(uint32_t palette_index) const
{
return m_palette[palette_index];
}
etc1_selector_palette_entry get_entry(uint32_t palette_index, const etc1_global_palette_entry_modifier &modifier) const
{
return m_palette[palette_index].get_modified(modifier);
}
etc1_selector_palette_entry get_entry(const etc1_global_selector_codebook_entry_id &id) const
{
return m_palette[id.m_palette_index].get_modified(id.m_modifier);
}
etc1_selector_palette_entry_vec m_palette;
};
} // namespace basist

View file

@ -37,6 +37,14 @@
#endif
#endif
// Using unaligned loads and stores causes errors when using UBSan. Jam it off.
#if defined(__has_feature)
#if __has_feature(undefined_behavior_sanitizer)
#undef BASISD_USE_UNALIGNED_WORD_READS
#define BASISD_USE_UNALIGNED_WORD_READS 0
#endif
#endif
#define BASISD_SUPPORTED_BASIS_VERSION (0x13)
#ifndef BASISD_SUPPORT_KTX2
@ -224,32 +232,7 @@ namespace basist
return static_cast<uint16_t>(~crc);
}
const uint32_t g_global_selector_cb[] =
#include "basisu_global_selector_cb.h"
;
const uint32_t g_global_selector_cb_size = sizeof(g_global_selector_cb) / sizeof(g_global_selector_cb[0]);
void etc1_global_selector_codebook::init(uint32_t N, const uint32_t* pEntries)
{
m_palette.resize(N);
for (uint32_t i = 0; i < N; i++)
m_palette[i].set_uint32(pEntries[i]);
}
void etc1_global_selector_codebook::print_code(FILE* pFile)
{
fprintf(pFile, "{\n");
for (uint32_t i = 0; i < m_palette.size(); i++)
{
fprintf(pFile, "0x%X,", m_palette[i].get_uint32());
if ((i & 15) == 15)
fprintf(pFile, "\n");
}
fprintf(pFile, "\n}\n");
}
enum etc_constants
{
cETC1BytesPerBlock = 8U,
@ -7532,9 +7515,8 @@ namespace basist
}
#endif // BASISD_SUPPORT_PVRTC2
basisu_lowlevel_etc1s_transcoder::basisu_lowlevel_etc1s_transcoder(const etc1_global_selector_codebook* pGlobal_sel_codebook) :
basisu_lowlevel_etc1s_transcoder::basisu_lowlevel_etc1s_transcoder() :
m_pGlobal_codebook(nullptr),
m_pGlobal_sel_codebook(pGlobal_sel_codebook),
m_selector_history_buf_size(0)
{
}
@ -7641,50 +7623,8 @@ namespace basist
if (used_global_selector_cb)
{
// global selector palette
uint32_t pal_bits = sym_codec.get_bits(4);
uint32_t mod_bits = sym_codec.get_bits(4);
basist::huffman_decoding_table mod_model;
if (mod_bits)
{
if (!sym_codec.read_huffman_table(mod_model))
{
BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 6\n");
return false;
}
if (!mod_model.is_valid())
{
BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 6a\n");
return false;
}
}
for (uint32_t i = 0; i < num_selectors; i++)
{
uint32_t pal_index = 0;
if (pal_bits)
pal_index = sym_codec.get_bits(pal_bits);
uint32_t mod_index = 0;
if (mod_bits)
mod_index = sym_codec.decode_huffman(mod_model);
if (pal_index >= m_pGlobal_sel_codebook->size())
{
BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 7z\n");
return false;
}
const etc1_selector_palette_entry e(m_pGlobal_sel_codebook->get_entry(pal_index, etc1_global_palette_entry_modifier(mod_index)));
// TODO: Optimize this
for (uint32_t y = 0; y < 4; y++)
for (uint32_t x = 0; x < 4; x++)
m_local_selectors[i].set_selector(x, y, e[x + y * 4]);
m_local_selectors[i].init_flags();
}
BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: global selector codebooks are unsupported\n");
return false;
}
else
{
@ -7692,146 +7632,70 @@ namespace basist
if (used_hybrid_selector_cb)
{
const uint32_t pal_bits = sym_codec.get_bits(4);
const uint32_t mod_bits = sym_codec.get_bits(4);
BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: hybrid global selector codebooks are unsupported\n");
return false;
}
const bool used_raw_encoding = (sym_codec.get_bits(1) == 1);
basist::huffman_decoding_table uses_global_cb_bitflags_model;
if (!sym_codec.read_huffman_table(uses_global_cb_bitflags_model))
if (used_raw_encoding)
{
for (uint32_t i = 0; i < num_selectors; i++)
{
BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 7\n");
return false;
}
if (!uses_global_cb_bitflags_model.is_valid())
{
BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 7a\n");
return false;
}
basist::huffman_decoding_table global_mod_indices_model;
if (mod_bits)
{
if (!sym_codec.read_huffman_table(global_mod_indices_model))
for (uint32_t j = 0; j < 4; j++)
{
BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 8\n");
return false;
}
if (!global_mod_indices_model.is_valid())
{
BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 8a\n");
return false;
}
}
uint32_t cur_byte = sym_codec.get_bits(8);
uint32_t cur_uses_global_cb_bitflags = 0;
uint32_t uses_global_cb_bitflags_remaining = 0;
for (uint32_t q = 0; q < num_selectors; q++)
{
if (!uses_global_cb_bitflags_remaining)
{
cur_uses_global_cb_bitflags = sym_codec.decode_huffman(uses_global_cb_bitflags_model);
uses_global_cb_bitflags_remaining = 8;
}
uses_global_cb_bitflags_remaining--;
const bool used_global_cb_flag = (cur_uses_global_cb_bitflags & 1) != 0;
cur_uses_global_cb_bitflags >>= 1;
if (used_global_cb_flag)
{
const uint32_t pal_index = pal_bits ? sym_codec.get_bits(pal_bits) : 0;
const uint32_t mod_index = mod_bits ? sym_codec.decode_huffman(global_mod_indices_model) : 0;
if (pal_index >= m_pGlobal_sel_codebook->size())
{
BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 8b\n");
return false;
}
const etc1_selector_palette_entry e(m_pGlobal_sel_codebook->get_entry(pal_index, etc1_global_palette_entry_modifier(mod_index)));
for (uint32_t y = 0; y < 4; y++)
for (uint32_t x = 0; x < 4; x++)
m_local_selectors[q].set_selector(x, y, e[x + y * 4]);
}
else
{
for (uint32_t j = 0; j < 4; j++)
{
uint32_t cur_byte = sym_codec.get_bits(8);
for (uint32_t k = 0; k < 4; k++)
m_local_selectors[q].set_selector(k, j, (cur_byte >> (k * 2)) & 3);
}
for (uint32_t k = 0; k < 4; k++)
m_local_selectors[i].set_selector(k, j, (cur_byte >> (k * 2)) & 3);
}
m_local_selectors[q].init_flags();
m_local_selectors[i].init_flags();
}
}
else
{
const bool used_raw_encoding = (sym_codec.get_bits(1) == 1);
if (used_raw_encoding)
if (!sym_codec.read_huffman_table(delta_selector_pal_model))
{
for (uint32_t i = 0; i < num_selectors; i++)
BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 10\n");
return false;
}
if ((num_selectors > 1) && (!delta_selector_pal_model.is_valid()))
{
BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 10a\n");
return false;
}
uint8_t prev_bytes[4] = { 0, 0, 0, 0 };
for (uint32_t i = 0; i < num_selectors; i++)
{
if (!i)
{
for (uint32_t j = 0; j < 4; j++)
{
uint32_t cur_byte = sym_codec.get_bits(8);
for (uint32_t k = 0; k < 4; k++)
m_local_selectors[i].set_selector(k, j, (cur_byte >> (k * 2)) & 3);
}
m_local_selectors[i].init_flags();
}
}
else
{
if (!sym_codec.read_huffman_table(delta_selector_pal_model))
{
BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 10\n");
return false;
}
if ((num_selectors > 1) && (!delta_selector_pal_model.is_valid()))
{
BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_palettes: fail 10a\n");
return false;
}
uint8_t prev_bytes[4] = { 0, 0, 0, 0 };
for (uint32_t i = 0; i < num_selectors; i++)
{
if (!i)
{
for (uint32_t j = 0; j < 4; j++)
{
uint32_t cur_byte = sym_codec.get_bits(8);
prev_bytes[j] = static_cast<uint8_t>(cur_byte);
for (uint32_t k = 0; k < 4; k++)
m_local_selectors[i].set_selector(k, j, (cur_byte >> (k * 2)) & 3);
}
m_local_selectors[i].init_flags();
continue;
}
for (uint32_t j = 0; j < 4; j++)
{
int delta_byte = sym_codec.decode_huffman(delta_selector_pal_model);
uint32_t cur_byte = delta_byte ^ prev_bytes[j];
prev_bytes[j] = static_cast<uint8_t>(cur_byte);
for (uint32_t k = 0; k < 4; k++)
m_local_selectors[i].set_selector(k, j, (cur_byte >> (k * 2)) & 3);
}
m_local_selectors[i].init_flags();
continue;
}
for (uint32_t j = 0; j < 4; j++)
{
int delta_byte = sym_codec.decode_huffman(delta_selector_pal_model);
uint32_t cur_byte = delta_byte ^ prev_bytes[j];
prev_bytes[j] = static_cast<uint8_t>(cur_byte);
for (uint32_t k = 0; k < 4; k++)
m_local_selectors[i].set_selector(k, j, (cur_byte >> (k * 2)) & 3);
}
m_local_selectors[i].init_flags();
}
}
}
@ -7899,6 +7763,12 @@ namespace basist
}
m_selector_history_buf_size = sym_codec.get_bits(13);
// Check for bogus values.
if (!m_selector_history_buf_size)
{
BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::decode_tables: fail 5\n");
return false;
}
sym_codec.stop();
@ -7979,8 +7849,11 @@ namespace basist
decoder_etc_block block;
memset(&block, 0, sizeof(block));
//block.set_flip_bit(true);
// Setting the flip bit to false to be compatible with the Khronos KDFS.
block.set_flip_bit(false);
block.set_flip_bit(true);
block.set_diff_bit(true);
void* pPVRTC_work_mem = nullptr;
@ -8741,7 +8614,7 @@ namespace basist
if (!output_row_pitch_in_blocks_or_pixels)
output_row_pitch_in_blocks_or_pixels = orig_width;
if (!output_rows_in_pixels)
if (!output_rows_in_pixels)
output_rows_in_pixels = orig_height;
// Now make sure the output buffer is large enough, or we'll overwrite memory.
@ -9440,6 +9313,12 @@ namespace basist
{
switch (fmt)
{
case block_format::cUASTC_4x4:
{
memcpy(pDst_block, pSource_block, sizeof(uastc_block));
status = true;
break;
}
case block_format::cETC1:
{
if (from_alpha)
@ -9906,8 +9785,7 @@ namespace basist
return status;
}
basisu_transcoder::basisu_transcoder(const etc1_global_selector_codebook* pGlobal_sel_codebook) :
m_lowlevel_etc1s_decoder(pGlobal_sel_codebook),
basisu_transcoder::basisu_transcoder() :
m_ready_to_transcode(false)
{
}
@ -10778,6 +10656,8 @@ namespace basist
return false;
}
//const bool transcode_alpha_data_to_opaque_formats = (decode_flags & cDecodeFlagsTranscodeAlphaDataToOpaqueFormats) != 0;
if (decode_flags & cDecodeFlagsPVRTCDecodeToNextPow2)
{
BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: cDecodeFlagsPVRTCDecodeToNextPow2 currently unsupported\n");
@ -11001,6 +10881,7 @@ namespace basist
case block_format::cRGB565: return "RGB565";
case block_format::cBGR565: return "BGR565";
case block_format::cRGBA4444: return "RGBA4444";
case block_format::cUASTC_4x4: return "UASTC_4x4";
case block_format::cFXT1_RGB: return "FXT1_RGB";
case block_format::cPVRTC2_4_RGB: return "PVRTC2_4_RGB";
case block_format::cPVRTC2_4_RGBA: return "PVRTC2_4_RGBA";
@ -12567,12 +12448,8 @@ namespace basist
bits = read_bits64(blk.m_bytes, bit_ofs, basisu::minimum<int>(64, 128 - (int)bit_ofs));
else
{
#ifdef __EMSCRIPTEN__
bits = blk.m_dwords[2];
bits |= (((uint64_t)blk.m_dwords[3]) << 32U);
#else
bits = blk.m_qwords[1];
#endif
if (bit_ofs >= 64U)
bits >>= (bit_ofs - 64U);
@ -16722,8 +16599,8 @@ namespace basist
#if BASISD_SUPPORT_KTX2
const uint8_t g_ktx2_file_identifier[12] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x32, 0x30, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
ktx2_transcoder::ktx2_transcoder(basist::etc1_global_selector_codebook* pGlobal_sel_codebook) :
m_etc1s_transcoder(pGlobal_sel_codebook)
ktx2_transcoder::ktx2_transcoder() :
m_etc1s_transcoder()
{
clear();
}
@ -17334,6 +17211,7 @@ namespace basist
bool ktx2_transcoder::decompress_level_data(uint32_t level_index, basisu::uint8_vec& uncomp_data)
{
const uint8_t* pComp_data = m_levels[level_index].m_byte_offset + m_pData;
const uint64_t comp_size = m_levels[level_index].m_byte_length;
const uint64_t uncomp_size = m_levels[level_index].m_uncompressed_byte_length;
@ -17349,7 +17227,7 @@ namespace basist
return false;
}
if (!uncomp_data.try_resize(uncomp_size))
if (!uncomp_data.try_resize((size_t)uncomp_size))
{
BASISU_DEVEL_ERROR("ktx2_transcoder::decompress_level_data: Out of memory\n");
return false;
@ -17358,7 +17236,6 @@ namespace basist
if (m_header.m_supercompression_scheme == KTX2_SS_ZSTANDARD)
{
#if BASISD_SUPPORT_KTX2_ZSTD
const uint8_t* pComp_data = m_levels[level_index].m_byte_offset + m_pData;
size_t actualUncompSize = ZSTD_decompress(uncomp_data.data(), (size_t)uncomp_size, pComp_data, (size_t)comp_size);
if (ZSTD_isError(actualUncompSize))
{

View file

@ -34,7 +34,6 @@
#include "basisu_transcoder_internal.h"
#include "basisu_transcoder_uastc.h"
#include "basisu_global_selector_palette.h"
#include "basisu_file_headers.h"
namespace basist
@ -190,7 +189,7 @@ namespace basist
friend class basisu_transcoder;
public:
basisu_lowlevel_etc1s_transcoder(const basist::etc1_global_selector_codebook* pGlobal_sel_codebook);
basisu_lowlevel_etc1s_transcoder();
void set_global_codebooks(const basisu_lowlevel_etc1s_transcoder* pGlobal_codebook) { m_pGlobal_codebook = pGlobal_codebook; }
const basisu_lowlevel_etc1s_transcoder* get_global_codebooks() const { return m_pGlobal_codebook; }
@ -248,17 +247,13 @@ namespace basist
typedef basisu::vector<selector> selector_vec;
const selector_vec& get_selectors() const { return m_local_selectors; }
const etc1_global_selector_codebook* get_global_sel_codebook() const { return m_pGlobal_sel_codebook; }
private:
const basisu_lowlevel_etc1s_transcoder* m_pGlobal_codebook;
endpoint_vec m_local_endpoints;
selector_vec m_local_selectors;
const etc1_global_selector_codebook* m_pGlobal_sel_codebook;
huffman_decoding_table m_endpoint_pred_model, m_delta_endpoint_model, m_selector_model, m_selector_history_buf_rle_model;
uint32_t m_selector_history_buf_size;
@ -442,7 +437,7 @@ namespace basist
basisu_transcoder& operator= (const basisu_transcoder&);
public:
basisu_transcoder(const etc1_global_selector_codebook* pGlobal_sel_codebook);
basisu_transcoder();
// Validates the .basis file. This computes a crc16 over the entire file, so it's slow.
bool validate_file_checksums(const void* pData, uint32_t data_size, bool full_validation) const;
@ -770,7 +765,7 @@ namespace basist
class ktx2_transcoder
{
public:
ktx2_transcoder(basist::etc1_global_selector_codebook* pGlobal_sel_codebook);
ktx2_transcoder();
// Frees all allocations, resets object.
void clear();

View file

@ -20,8 +20,8 @@
#pragma warning (disable: 4127) // conditional expression is constant
#endif
#define BASISD_LIB_VERSION 115
#define BASISD_VERSION_STRING "01.15"
#define BASISD_LIB_VERSION 116
#define BASISD_VERSION_STRING "01.16"
#ifdef _DEBUG
#define BASISD_BUILD_DEBUG
@ -46,18 +46,18 @@ namespace basist
{
cETC1, // ETC1S RGB
cETC2_RGBA, // full ETC2 EAC RGBA8 block
cBC1, // DXT1 RGB
cBC3, // BC4 block followed by a four color BC1 block
cBC4, // DXT5A (alpha block only)
cBC5, // two BC4 blocks
cBC1, // DXT1 RGB
cBC3, // BC4 block followed by a four color BC1 block
cBC4, // DXT5A (alpha block only)
cBC5, // two BC4 blocks
cPVRTC1_4_RGB, // opaque-only PVRTC1 4bpp
cPVRTC1_4_RGBA, // PVRTC1 4bpp RGBA
cBC7, // Full BC7 block, any mode
cPVRTC1_4_RGBA, // PVRTC1 4bpp RGBA
cBC7, // Full BC7 block, any mode
cBC7_M5_COLOR, // RGB BC7 mode 5 color (writes an opaque mode 5 block)
cBC7_M5_ALPHA, // alpha portion of BC7 mode 5 (cBC7_M5_COLOR output data must have been written to the output buffer first to set the mode/rot fields etc.)
cETC2_EAC_A8, // alpha block of ETC2 EAC (first 8 bytes of the 16-bit ETC2 EAC RGBA format)
cASTC_4x4, // ASTC 4x4 (either color-only or color+alpha). Note that the transcoder always currently assumes sRGB is not enabled when outputting ASTC
// data. If you use a sRGB ASTC format you'll get ~1 LSB of additional error, because of the different way ASTC decoders scale 8-bit endpoints to 16-bits during unpacking.
// data. If you use a sRGB ASTC format you'll get ~1 LSB of additional error, because of the different way ASTC decoders scale 8-bit endpoints to 16-bits during unpacking.
cATC_RGB,
cATC_RGBA_INTERPOLATED_ALPHA,
@ -72,8 +72,8 @@ namespace basist
cIndices, // Used internally: Write 16-bit endpoint and selector indices directly to output (output block must be at least 32-bits)
cRGB32, // Writes RGB components to 32bpp output pixels
cRGBA32, // Writes RGB255 components to 32bpp output pixels
cA32, // Writes alpha component to 32bpp output pixels
cRGBA32, // Writes RGB255 components to 32bpp output pixels
cA32, // Writes alpha component to 32bpp output pixels
cRGB565,
cBGR565,
@ -82,6 +82,8 @@ namespace basist
cRGBA4444_ALPHA,
cRGBA4444_COLOR_OPAQUE,
cRGBA4444,
cUASTC_4x4,
cTotalBlockFormats
};

View file

@ -205,10 +205,6 @@ namespace basist
{
uint8_t m_bytes[16];
uint32_t m_dwords[4];
#ifndef __EMSCRIPTEN__
uint64_t m_qwords[2];
#endif
};
};