diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index 0b3ad6cf..9d1bc57e 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -37,41 +37,77 @@ strategy: CXX_COMPILER: i686-linux-gnu-g++ COMPILER_FLAGS: -mfpmath=sse -msse Linux arm64 GCC: - VM_IMAGE: 'ubuntu-22.04' + VM_IMAGE: 'ubuntu-26.04' APT_PACKAGES: ninja-build g++-aarch64-linux-gnu qemu-user C_COMPILER: aarch64-linux-gnu-gcc CXX_COMPILER: aarch64-linux-gnu-g++ CRUNCH_EXE_RUNNER: qemu-aarch64 -L /usr/aarch64-linux-gnu Linux armhf GCC: - # There is an IO bug in qemu-arm from ubuntu-22.04. - VM_IMAGE: 'ubuntu-24.04' + VM_IMAGE: 'ubuntu-26.04' APT_PACKAGES: ninja-build g++-arm-linux-gnueabihf qemu-user C_COMPILER: arm-linux-gnueabihf-gcc CXX_COMPILER: arm-linux-gnueabihf-g++ CRUNCH_EXE_RUNNER: qemu-arm -L /usr/arm-linux-gnueabihf + Linux ppc64 GCC: + VM_IMAGE: 'ubuntu-26.04' + APT_PACKAGES: ninja-build g++-powerpc64-linux-gnu qemu-user + C_COMPILER: powerpc64-linux-gnu-gcc + CXX_COMPILER: powerpc64-linux-gnu-g++ + CRUNCH_EXE_RUNNER: qemu-ppc64 -L /usr/powerpc64-linux-gnu + Linux ppc64el GCC: + VM_IMAGE: 'ubuntu-26.04' + APT_PACKAGES: ninja-build g++-powerpc64le-linux-gnu qemu-user + C_COMPILER: powerpc64le-linux-gnu-gcc + CXX_COMPILER: powerpc64le-linux-gnu-g++ + CRUNCH_EXE_RUNNER: qemu-ppc64le -L /usr/powerpc64le-linux-gnu + Linux riscv64 GCC: + VM_IMAGE: 'ubuntu-26.04' + APT_PACKAGES: ninja-build g++-riscv64-linux-gnu qemu-user + C_COMPILER: riscv64-linux-gnu-gcc + CXX_COMPILER: riscv64-linux-gnu-g++ + CRUNCH_EXE_RUNNER: qemu-riscv64 -L /usr/riscv64-linux-gnu + Linux loong64 GCC: + VM_IMAGE: 'ubuntu-26.04' + APT_PACKAGES: ninja-build g++-loongarch64-linux-gnu qemu-user + C_COMPILER: loongarch64-linux-gnu-gcc + CXX_COMPILER: loongarch64-linux-gnu-g++ + CRUNCH_EXE_RUNNER: qemu-loongarch64 -L /usr/loongarch64-linux-gnu + Linux s390x GCC: + VM_IMAGE: 'ubuntu-26.04' + APT_PACKAGES: ninja-build g++-s390x-linux-gnu qemu-user + C_COMPILER: s390x-linux-gnu-gcc + CXX_COMPILER: s390x-linux-gnu-g++ + CRUNCH_EXE_RUNNER: qemu-s390x -L /usr/s390x-linux-gnu Linux amd64 Clang: VM_IMAGE: 'ubuntu-22.04' APT_PACKAGES: ninja-build C_COMPILER: clang CXX_COMPILER: clang++ Windows amd64 MinGW: - VM_IMAGE: 'ubuntu-22.04' - APT_PACKAGES: ninja-build g++-mingw-w64-x86-64 mingw-w64-x86-64-dev gcc-mingw-w64-x86-64-posix-runtime wine + VM_IMAGE: 'ubuntu-26.04' + APT_PACKAGES: ninja-build g++-mingw-w64-x86-64 mingw-w64-x86-64-dev gcc-mingw-w64-x86-64-posix-runtime wine64 SETUP_COMMANDS: sudo update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix TOOLCHAIN_FILE: cmake/cross-toolchain-mingw64.cmake + RUNTIME_FILES: /usr/lib/gcc/x86_64-w64-mingw32/13-posix/libgcc_s_seh-1.dll /usr/lib/gcc/x86_64-w64-mingw32/13-posix/libstdc++-6.dll /usr/x86_64-w64-mingw32/lib/libwinpthread-1.dll CRUNCH_EXE_RUNNER: wine - CRUNCH_EXTENSION: .exe - RUNTIME_FILES: /usr/lib/gcc/x86_64-w64-mingw32/10-posix/libgcc_s_seh-1.dll /usr/lib/gcc/x86_64-w64-mingw32/10-posix/libstdc++-6.dll /usr/x86_64-w64-mingw32/lib/libwinpthread-1.dll + CRUNCH_EXE_EXTENSION: .exe Windows i686 MinGW: - VM_IMAGE: 'ubuntu-22.04' + VM_IMAGE: 'ubuntu-26.04' APT_ARCHITECTURE: i386 - APT_PACKAGES: ninja-build g++-mingw-w64-i686 mingw-w64-i686-dev gcc-mingw-w64-i686-posix-runtime wine wine32 + APT_PACKAGES: ninja-build g++-mingw-w64-i686 mingw-w64-i686-dev gcc-mingw-w64-i686-posix-runtime wine64 SETUP_COMMANDS: sudo update-alternatives --set i686-w64-mingw32-g++ /usr/bin/i686-w64-mingw32-g++-posix TOOLCHAIN_FILE: cmake/cross-toolchain-mingw32.cmake COMPILER_FLAGS: -mfpmath=sse -msse + RUNTIME_FILES: /usr/lib/gcc/i686-w64-mingw32/13-posix/libgcc_s_dw2-1.dll /usr/lib/gcc/i686-w64-mingw32/13-posix/libstdc++-6.dll /usr/i686-w64-mingw32/lib/libwinpthread-1.dll CRUNCH_EXE_RUNNER: wine - CRUNCH_EXTENSION: .exe - RUNTIME_FILES: /usr/lib/gcc/i686-w64-mingw32/10-posix/libgcc_s_dw2-1.dll /usr/lib/gcc/i686-w64-mingw32/10-posix/libstdc++-6.dll /usr/i686-w64-mingw32/lib/libwinpthread-1.dll + CRUNCH_EXE_EXTENSION: .exe + Windows arm64 MinGW: + VM_IMAGE: 'windows-2025' + PACMAN_PACKAGES: mingw-w64-cross-mingwarm64-gcc + C_COMPILER: aarch64-w64-mingw32-gcc + CXX_COMPILER: aarch64-w64-mingw32-g++ + CRUNCH_EXE_EXTENSION: .exe + RUN_TESTS: false macOS amd64 AppleClang: VM_IMAGE: 'macOS-14' PIP_PACKAGES: colorama @@ -104,11 +140,19 @@ steps: if [ -n "${APT_PACKAGES:-}" ]; then sudo apt-get update && sudo apt-get -y -q --no-install-recommends install ${APT_PACKAGES} fi + if [ -n "${PACMAN_PACKAGES:-}" ]; then + 'C:\msys64\usr\bin\bash.exe' -lc "pacman -Sy --noconfirm && pacman -S --noconfirm ${PACMAN_PACKAGES}" + echo '##vso[task.prependpath]C:\msys64\usr\bin' + echo '##vso[task.prependpath]C:\msys64\opt\bin' + fi if [ -n "${PIP_PACKAGES:-}" ]; then pip install ${PIP_PACKAGES} fi if [ -n "${SETUP_COMMANDS:-}" ]; then - $(SETUP_COMMANDS) + $(SETUP_COMMANDS) + fi + if [ -x '/usr/lib/x86_64-linux-gnu/wine/wineserver' ]; then + echo '##vso[task.prependpath]/usr/lib/x86_64-linux-gnu/wine' fi displayName: 'Setup' - bash: | @@ -138,7 +182,7 @@ steps: set -xue EXECUTABLES="${EXECUTABLES:-crunch example1 example2 example3}" for exe_file in ${EXECUTABLES}; do - file "build/${exe_file}${CRUNCH_EXTENSION:-}" + file "build/${exe_file}${CRUNCH_EXE_EXTENSION:-}" done if [ -n "${RUNTIME_FILES:-}" ]; then cp -av ${RUNTIME_FILES} build/ diff --git a/crnlib/crn_color.h b/crnlib/crn_color.h index 076b53f1..309588b6 100644 --- a/crnlib/crn_color.h +++ b/crnlib/crn_color.h @@ -176,6 +176,20 @@ class color_quad : public helpers::rel_ops(v); + g = static_cast(v >> 8); + b = static_cast(v >> 16); + a = static_cast(v >> 24); + } + + inline uint32 get_rgba_u32() const { + return uint32(r) | + (uint32(g) << 8) | + (uint32(b) << 16) | + (uint32(a) << 24); + } + inline color_quad& operator=(const color_quad& other) { r = other.r; g = other.g; diff --git a/crnlib/crn_comp.cpp b/crnlib/crn_comp.cpp index 7a37e47b..eb3824ae 100644 --- a/crnlib/crn_comp.cpp +++ b/crnlib/crn_comp.cpp @@ -795,9 +795,19 @@ void crn_comp::optimize_color() { } } crnlib::vector unpacked_endpoints(n); + for (uint16 i = 0; i < n; i++) { - unpacked_endpoints[i].low.m_u32 = m_has_etc_color_blocks ? m_color_endpoints[i] & 0xFFFFFF : dxt1_block::unpack_color(m_color_endpoints[i] & 0xFFFF, true).m_u32; - unpacked_endpoints[i].high.m_u32 = m_has_etc_color_blocks ? m_color_endpoints[i] >> 24 : dxt1_block::unpack_color(m_color_endpoints[i] >> 16, true).m_u32; + uint32 endpoint = m_color_endpoints[i]; + + if (m_has_etc_color_blocks) { + unpacked_endpoints[i].low.set_rgba_u32(endpoint & 0xFFFFFF); + unpacked_endpoints[i].high.set_rgba_u32(endpoint >> 24); + } else { + unpacked_endpoints[i].low = + dxt1_block::unpack_color(endpoint & 0xFFFF, true); + unpacked_endpoints[i].high = + dxt1_block::unpack_color(endpoint >> 16, true); + } } optimize_color_params::result remapping_trial[4]; diff --git a/crnlib/crn_core.h b/crnlib/crn_core.h index c1f31b03..6f73f4ba 100644 --- a/crnlib/crn_core.h +++ b/crnlib/crn_core.h @@ -75,7 +75,13 @@ #define CRNLIB_USE_UNALIGNED_INT_LOADS 1 +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #define CRNLIB_LITTLE_ENDIAN_CPU 1 +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define CRNLIB_BIG_ENDIAN_CPU 1 +#else +#error "Unknown CPU endianness" +#endif #define CRNLIB_USE_PTHREADS_API 1 #define CRNLIB_USE_GCC_ATOMIC_BUILTINS 1 diff --git a/crnlib/crn_dxt1.cpp b/crnlib/crn_dxt1.cpp index ff807990..d7b2c3ea 100644 --- a/crnlib/crn_dxt1.cpp +++ b/crnlib/crn_dxt1.cpp @@ -222,6 +222,7 @@ void dxt1_endpoint_optimizer::return_solution() { m_pResults->m_enforce_selector = m_best_solution.m_enforce_selector; m_pResults->m_enforced_selector = m_best_solution.m_enforced_selector; m_pResults->m_reordered = invert_selectors; + if (invert_selectors) { m_pResults->m_low_color = m_best_solution.m_coords.m_high_color; m_pResults->m_high_color = m_best_solution.m_coords.m_low_color; @@ -234,17 +235,13 @@ void dxt1_endpoint_optimizer::return_solution() { if (invert_selectors) pInvert_table = m_best_solution.m_alpha_block ? g_invTableAlpha : g_invTableColor; - const uint alpha_thresh = m_pParams->m_pixels_have_alpha ? (m_pParams->m_dxt1a_alpha_threshold << 24U) : 0; - - const uint32* pSrc_pixels = reinterpret_cast(m_pParams->m_pPixels); + const color_quad_u8* pSrc_pixels = m_pParams->m_pPixels; uint8* pDst_selectors = m_pResults->m_pSelectors; if ((m_unique_colors.size() == 1) && (!m_pParams->m_pixels_have_alpha)) { - uint32 c = utils::read_le32(pSrc_pixels); - - CRNLIB_ASSERT(c >= alpha_thresh); + color_quad_u8 color(pSrc_pixels->r, pSrc_pixels->g, pSrc_pixels->b, 255); - c |= 0xFF000000U; + uint32 c = color.get_rgba_u32(); unique_color_hash_map::const_iterator it(m_unique_color_hash_map.find(c)); CRNLIB_ASSERT(it != m_unique_color_hash_map.end()); @@ -261,13 +258,15 @@ void dxt1_endpoint_optimizer::return_solution() { uint32 prev_color = 0; do { - uint32 c = utils::read_le32(pSrc_pixels); - pSrc_pixels++; - uint8 selector = 3; - if (c >= alpha_thresh) { - c |= 0xFF000000U; + uint8 alpha_thresh = pSrc_pixels->a; + + if (!m_pParams->m_pixels_have_alpha || (alpha_thresh >= m_pParams->m_dxt1a_alpha_threshold)) { + + color_quad_u8 color(pSrc_pixels->r, pSrc_pixels->g, pSrc_pixels->b, 255); + + uint32 c = color.get_rgba_u32(); if (c == prev_color) selector = prev_selector; @@ -285,6 +284,8 @@ void dxt1_endpoint_optimizer::return_solution() { } } + pSrc_pixels++; + *pDst_selectors++ = selector; } while (pDst_selectors != pDst_selectors_end); @@ -1708,8 +1709,8 @@ void dxt1_endpoint_optimizer::compute_internal(const params& p, results& r) { unique_color color(color_quad_u8(0), 1); for (uint i = 0; i < m_pParams->m_num_pixels; i++) { if (!m_pParams->m_pixels_have_alpha || m_pParams->m_pPixels[i].a >= m_pParams->m_dxt1a_alpha_threshold) { - color.m_color.m_u32 = m_pParams->m_pPixels[i].m_u32 | 0xFF000000; - unique_color_hash_map::insert_result ins_result(m_unique_color_hash_map.insert(color.m_color.m_u32, m_unique_colors.size())); + color.m_color = color_quad_u8(m_pParams->m_pPixels[i].r, m_pParams->m_pPixels[i].g, m_pParams->m_pPixels[i].b, 255); + unique_color_hash_map::insert_result ins_result(m_unique_color_hash_map.insert(color.m_color.get_rgba_u32(), m_unique_colors.size())); if (ins_result.second) { m_unique_colors.push_back(color); } else { diff --git a/crnlib/crn_dxt_hc.cpp b/crnlib/crn_dxt_hc.cpp index 18c9c39c..35595730 100644 --- a/crnlib/crn_dxt_hc.cpp +++ b/crnlib/crn_dxt_hc.cpp @@ -616,7 +616,7 @@ void dxt_hc::determine_color_endpoint_codebook_task_etc(uint64 data, void*) { for (int c = 0; c < 3; c++) endpoint.c[c] = results.m_block_color_unscaled.c[c] << 3 | results.m_block_color_unscaled.c[c] >> 2; endpoint.c[3] = results.m_block_inten_table; - cluster.first_endpoint = endpoint.m_u32; + cluster.first_endpoint = endpoint.get_rgba_u32(); for (uint8 d0 = delta[endpoint.c[3]][0], d1 = delta[endpoint.c[3]][1], c = 0; c < 3; c++) { uint8 q = endpoint.c[c]; cluster.color_values[0].c[c] = q <= d1 ? 0 : q - d1; diff --git a/crnlib/crn_etc.cpp b/crnlib/crn_etc.cpp index 37c5109b..802dd90f 100644 --- a/crnlib/crn_etc.cpp +++ b/crnlib/crn_etc.cpp @@ -1617,7 +1617,20 @@ uint64 pack_etc1s_block(etc1_block& dst_block, const color_quad_u8* pSrc_pixels, } } - dst_block.m_uint64 = (uint64)selector << 32 | results.m_block_inten_table << 29 | results.m_block_inten_table << 26 | 1 << 25 | (results.m_block_color_unscaled.m_u32 & 0xFFFFFF) << 3; + const uint32 color = + ((uint32)results.m_block_color_unscaled.r << 16) | + ((uint32)results.m_block_color_unscaled.g << 8) | + (uint32)results.m_block_color_unscaled.b; + + const uint64 packed = + (uint64)selector << 32 | + (uint64)results.m_block_inten_table << 29 | + (uint64)results.m_block_inten_table << 26 | + (uint64)1 << 25 | + (uint64)color << 3; + + utils::write_be64(&dst_block.m_uint64, packed); + return results.m_error; } diff --git a/crnlib/crn_ktx_texture.cpp b/crnlib/crn_ktx_texture.cpp index 5b398f63..df5acca8 100644 --- a/crnlib/crn_ktx_texture.cpp +++ b/crnlib/crn_ktx_texture.cpp @@ -279,25 +279,52 @@ bool ktx_texture::compute_pixel_info() { bool ktx_texture::read_from_stream(data_stream_serializer& serializer) { clear(); - // Read header - if (serializer.read(&m_header, 1, sizeof(m_header)) != sizeof(ktx_header)) + // Read the identifier first. It is endian-independent. + if (serializer.read(m_header.m_identifier, 1, sizeof(m_header.m_identifier)) != sizeof(m_header.m_identifier)) return false; - // Check header + // Check identifier. if (memcmp(s_ktx_file_id, m_header.m_identifier, sizeof(m_header.m_identifier))) return false; - if ((m_header.m_endianness != KTX_OPPOSITE_ENDIAN) && (m_header.m_endianness != KTX_ENDIAN)) + // Read the KTX endianness marker as raw bytes. We cannot interpret it + // until we know the file's byte order. + uint8 endianness_bytes[4]; + if (serializer.read(endianness_bytes, 1, sizeof(endianness_bytes)) != sizeof(endianness_bytes)) return false; - m_opposite_endianness = (m_header.m_endianness == KTX_OPPOSITE_ENDIAN); - if (m_opposite_endianness) { - m_header.endian_swap(); - - if ((m_header.m_glTypeSize != sizeof(uint8)) && (m_header.m_glTypeSize != sizeof(uint16)) && (m_header.m_glTypeSize != sizeof(uint32))) - return false; + static const uint8 little_endian_bytes[] = { 0x01, 0x02, 0x03, 0x04 }; + static const uint8 big_endian_bytes[] = { 0x04, 0x03, 0x02, 0x01 }; + + if (!memcmp(endianness_bytes, little_endian_bytes, sizeof(little_endian_bytes))) { + // File is little-endian. + m_opposite_endianness = !c_crnlib_little_endian_platform; + m_header.m_endianness = KTX_ENDIAN; + serializer.set_little_endian(true); + } else if (!memcmp(endianness_bytes, big_endian_bytes, sizeof(big_endian_bytes))) { + // File is big-endian. + m_opposite_endianness = c_crnlib_little_endian_platform; + m_header.m_endianness = KTX_OPPOSITE_ENDIAN; + serializer.set_little_endian(false); + } else { + return false; } + // Read the remaining header fields using the file's byte order. + if (!serializer.read_object(m_header.m_glType) || + !serializer.read_object(m_header.m_glTypeSize) || + !serializer.read_object(m_header.m_glFormat) || + !serializer.read_object(m_header.m_glInternalFormat) || + !serializer.read_object(m_header.m_glBaseInternalFormat) || + !serializer.read_object(m_header.m_pixelWidth) || + !serializer.read_object(m_header.m_pixelHeight) || + !serializer.read_object(m_header.m_pixelDepth) || + !serializer.read_object(m_header.m_numberOfArrayElements) || + !serializer.read_object(m_header.m_numberOfFaces) || + !serializer.read_object(m_header.m_numberOfMipmapLevels) || + !serializer.read_object(m_header.m_bytesOfKeyValueData)) + return false; + if (!check_header()) return false; @@ -313,14 +340,11 @@ bool ktx_texture::read_from_stream(data_stream_serializer& serializer) { return false; uint32 key_value_byte_size; - if (serializer.read(&key_value_byte_size, 1, sizeof(uint32)) != sizeof(uint32)) + if (!serializer.read_object(key_value_byte_size)) return false; num_key_value_bytes_remaining -= sizeof(uint32); - if (m_opposite_endianness) - key_value_byte_size = utils::swap32(key_value_byte_size); - if (key_value_byte_size > num_key_value_bytes_remaining) return false; @@ -426,11 +450,8 @@ bool ktx_texture::read_from_stream(data_stream_serializer& serializer) { if (!has_valid_image_size_fields) image_size = mip_depth * mip_row_blocks * mip_col_blocks * m_bytes_per_block * get_array_size() * get_num_faces(); else { - if (serializer.read(&image_size, 1, sizeof(image_size)) != sizeof(image_size)) + if (!serializer.read_object(image_size)) return false; - - if (m_opposite_endianness) - image_size = utils::swap32(image_size); } if (!image_size) @@ -501,13 +522,16 @@ bool ktx_texture::read_from_stream(data_stream_serializer& serializer) { } bool ktx_texture::write_to_stream(data_stream_serializer& serializer, bool no_keyvalue_data) { + serializer.set_little_endian(!m_big_endian); + if (!consistency_check()) { CRNLIB_ASSERT(0); return false; } memcpy(m_header.m_identifier, s_ktx_file_id, sizeof(m_header.m_identifier)); - m_header.m_endianness = m_opposite_endianness ? KTX_OPPOSITE_ENDIAN : KTX_ENDIAN; + + m_header.m_endianness = KTX_ENDIAN; if (m_block_dim == 1) { m_header.m_glTypeSize = get_ogl_type_size(m_header.m_glType); @@ -522,13 +546,22 @@ bool ktx_texture::write_to_stream(data_stream_serializer& serializer, bool no_ke m_header.m_bytesOfKeyValueData += sizeof(uint32) + ((m_key_values[i].size() + 3) & ~3); } - if (m_opposite_endianness) - m_header.endian_swap(); - - bool success = (serializer.write(&m_header, sizeof(m_header), 1) == 1); - - if (m_opposite_endianness) - m_header.endian_swap(); + bool success = true; + + success &= serializer.write_chars((const char*)m_header.m_identifier, sizeof(m_header.m_identifier)); + success &= serializer.write_value(m_header.m_endianness); + success &= serializer.write_value(m_header.m_glType); + success &= serializer.write_value(m_header.m_glTypeSize); + success &= serializer.write_value(m_header.m_glFormat); + success &= serializer.write_value(m_header.m_glInternalFormat); + success &= serializer.write_value(m_header.m_glBaseInternalFormat); + success &= serializer.write_value(m_header.m_pixelWidth); + success &= serializer.write_value(m_header.m_pixelHeight); + success &= serializer.write_value(m_header.m_pixelDepth); + success &= serializer.write_value(m_header.m_numberOfArrayElements); + success &= serializer.write_value(m_header.m_numberOfFaces); + success &= serializer.write_value(m_header.m_numberOfMipmapLevels); + success &= serializer.write_value(m_header.m_bytesOfKeyValueData); if (!success) return success; @@ -540,15 +573,9 @@ bool ktx_texture::write_to_stream(data_stream_serializer& serializer, bool no_ke for (uint i = 0; i < m_key_values.size(); i++) { uint32 key_value_size = m_key_values[i].size(); - if (m_opposite_endianness) - key_value_size = utils::swap32(key_value_size); - - success = (serializer.write(&key_value_size, sizeof(key_value_size), 1) == 1); + success = serializer.write_value(key_value_size); total_key_value_bytes += sizeof(key_value_size); - if (m_opposite_endianness) - key_value_size = utils::swap32(key_value_size); - if (!success) return false; @@ -584,13 +611,7 @@ bool ktx_texture::write_to_stream(data_stream_serializer& serializer, bool no_ke if (!image_size) return false; - if (m_opposite_endianness) - image_size = utils::swap32(image_size); - - success = (serializer.write(&image_size, sizeof(image_size), 1) == 1); - - if (m_opposite_endianness) - image_size = utils::swap32(image_size); + success = serializer.write_value(image_size); if (!success) return false; diff --git a/crnlib/crn_ktx_texture.h b/crnlib/crn_ktx_texture.h index 65698796..b009e8ca 100644 --- a/crnlib/crn_ktx_texture.h +++ b/crnlib/crn_ktx_texture.h @@ -165,6 +165,7 @@ class ktx_texture { m_block_dim = rhs.m_block_dim; m_bytes_per_block = rhs.m_bytes_per_block; m_opposite_endianness = rhs.m_opposite_endianness; + m_big_endian = rhs.m_big_endian; return *this; } @@ -178,6 +179,7 @@ class ktx_texture { m_bytes_per_block = 0; m_opposite_endianness = false; + m_big_endian = false; } // High level methods @@ -215,6 +217,7 @@ class ktx_texture { bool get_opposite_endianness() const { return m_opposite_endianness; } void set_opposite_endianness(bool flag) { m_opposite_endianness = flag; } + void set_big_endian(bool flag) { m_big_endian = flag; } uint32 get_block_dim() const { return m_block_dim; } uint32 get_bytes_per_block() const { return m_bytes_per_block; } @@ -282,6 +285,7 @@ class ktx_texture { uint32 m_bytes_per_block; bool m_opposite_endianness; + bool m_big_endian; bool compute_pixel_info(); }; diff --git a/crnlib/crn_mipmapped_texture.cpp b/crnlib/crn_mipmapped_texture.cpp index 22b7078f..eaf9d699 100644 --- a/crnlib/crn_mipmapped_texture.cpp +++ b/crnlib/crn_mipmapped_texture.cpp @@ -1553,7 +1553,7 @@ bool mipmapped_texture::read_ktx(data_stream_serializer& serializer) { return true; } -bool mipmapped_texture::write_ktx(data_stream_serializer& serializer) const { +bool mipmapped_texture::write_ktx(data_stream_serializer& serializer, crn_ktx_endianness ktx_endianness) const { if (!m_width) { set_last_error("Nothing to write"); return false; @@ -1662,6 +1662,32 @@ bool mipmapped_texture::write_ktx(data_stream_serializer& serializer) const { if (!success) return false; + bool ktx_big_endian = false; + + switch (ktx_endianness) { + case cCRNKTXLittleEndian: + ktx_big_endian = false; + break; + + case cCRNKTXBigEndian: + ktx_big_endian = true; + break; + + case cCRNKTXNativeEndian: + ktx_big_endian = !c_crnlib_little_endian_platform; + break; + + default: + CRNLIB_ASSERT(0); + return false; + } + + const bool host_big_endian = !c_crnlib_little_endian_platform; + const bool opposite_endianness = (ktx_big_endian != host_big_endian); + + kt.set_opposite_endianness(opposite_endianness); + kt.set_big_endian(ktx_big_endian); + dynamic_string fourcc_str(cVarArg, "%c%c%c%c", m_format & 0xFF, (m_format >> 8) & 0xFF, (m_format >> 16) & 0xFF, (m_format >> 24) & 0xFF); kt.add_key_value("CRNLIB_FOURCC", fourcc_str.get_ptr()); @@ -2809,7 +2835,7 @@ bool mipmapped_texture::read_crn(data_stream_serializer& serializer) { bool mipmapped_texture::write_to_file( const char* pFilename, texture_file_types::format file_format, - crn_comp_params* pComp_params, + const crn_comp_params* pComp_params, uint32* pActual_quality_level, float* pActual_bitrate, uint32 image_write_flags) { if (pActual_quality_level) @@ -2840,7 +2866,7 @@ bool mipmapped_texture::write_to_file( } else if (!texture_file_types::supports_mipmaps(file_format)) { success = write_regular_image(pFilename, image_write_flags); } else { - if (pComp_params) { + if (pComp_params && (file_format != texture_file_types::cFormatKTX)) { console::warning("mipmapped_texture::write_to_file: Ignoring CRN compression parameters (currently unsupported for this file type)."); } @@ -2857,7 +2883,13 @@ bool mipmapped_texture::write_to_file( break; } case texture_file_types::cFormatKTX: { - success = write_ktx(serializer); + crn_ktx_endianness ktx_endianness = cCRNKTXLittleEndian; + + if (pComp_params) { + ktx_endianness = pComp_params->m_ktx_endianness; + } + + success = write_ktx(serializer, ktx_endianness); break; } default: { diff --git a/crnlib/crn_mipmapped_texture.h b/crnlib/crn_mipmapped_texture.h index 648faa20..9c8fdc8b 100644 --- a/crnlib/crn_mipmapped_texture.h +++ b/crnlib/crn_mipmapped_texture.h @@ -187,7 +187,7 @@ class mipmapped_texture { bool write_dds(data_stream_serializer& serializer) const; bool read_ktx(data_stream_serializer& serializer); - bool write_ktx(data_stream_serializer& serializer) const; + bool write_ktx(data_stream_serializer& serializer, crn_ktx_endianness ktx_endianness) const; bool read_crn(data_stream_serializer& serializer); bool read_crn_from_memory(const void* pData, uint data_size, const char* pFilename); @@ -199,7 +199,7 @@ class mipmapped_texture { bool write_to_file( const char* pFilename, texture_file_types::format file_format = texture_file_types::cFormatInvalid, - crn_comp_params* pComp_params = NULL, + const crn_comp_params* pComp_params = NULL, uint32* pActual_quality_level = NULL, float* pActual_bitrate = NULL, uint32 image_write_flags = 0); diff --git a/crnlib/crn_texture_conversion.cpp b/crnlib/crn_texture_conversion.cpp index d7f205b3..760575d8 100644 --- a/crnlib/crn_texture_conversion.cpp +++ b/crnlib/crn_texture_conversion.cpp @@ -447,6 +447,12 @@ static bool convert_and_write_normal_texture(mipmapped_texture& work_tex, conver console::info("Texture format conversion took %3.3fs", t); } + const crn_comp_params* pCompParams = NULL; + + if (params.m_dst_file_type == texture_file_types::cFormatKTX) { + pCompParams = &comp_params; + } + if (params.m_write_mipmaps_to_multiple_files) { for (uint f = 0; f < work_tex.get_num_faces(); f++) { for (uint l = 0; l < work_tex.get_num_levels(); l++) { @@ -469,14 +475,14 @@ static bool convert_and_write_normal_texture(mipmapped_texture& work_tex, conver console::info("Writing texture face %u mip level %u to file %s", f, l, filename.get_ptr()); - if (!new_tex.write_to_file(filename.get_ptr(), params.m_dst_file_type, NULL, NULL, NULL)) + if (!new_tex.write_to_file(filename.get_ptr(), params.m_dst_file_type, pCompParams, NULL, NULL)) return convert_error(params, "Failed writing output file!"); } } } else { console::message("Writing texture to file: \"%s\"", params.m_dst_filename.get_ptr()); - if (!work_tex.write_to_file(params.m_dst_filename.get_ptr(), params.m_dst_file_type, NULL, NULL, NULL)) + if (!work_tex.write_to_file(params.m_dst_filename.get_ptr(), params.m_dst_file_type, pCompParams, NULL, NULL)) return convert_error(params, "Failed writing output file!"); if (!params.m_no_stats) { diff --git a/crunch/crunch.cpp b/crunch/crunch.cpp index 03c4b2f3..444800db 100644 --- a/crunch/crunch.cpp +++ b/crunch/crunch.cpp @@ -168,6 +168,8 @@ class crunch { console::message("\nOutput pixel format options:"); console::printf("-usesourceformat - Use input file's format for output format (when possible)."); + console::printf("-ktxBigEndian - Write KTX files using big-endian byte order (default=little-endian)."); + console::printf("-ktxNativeEndian - Write KTX files using the native CPU byte order."); console::message("\nAll supported texture formats (Note: .CRN only supports DXTn pixel formats):"); for (uint32 i = 0; i < pixel_format_helpers::get_num_formats(); i++) { pixel_format fmt = pixel_format_helpers::get_pixel_format_by_index(i); @@ -242,6 +244,8 @@ class crunch { {"forceprimaryencoding", 0, false}, {"usetransparentindicesforblack", 0, false}, {"usesourceformat", 0, false}, + {"ktxBigEndian", 0, false}, + {"ktxNativeEndian", 0, false}, {"rescalemode", 1, false}, {"rescale", 2, false}, @@ -873,6 +877,12 @@ class crunch { else comp_params.set_flag(cCRNCompFlagUseTransparentIndicesForBlack, false); + if (m_params.get_value_as_bool("ktxBigendian")) { + comp_params.m_ktx_endianness = cCRNKTXBigEndian; + } else if (m_params.get_value_as_bool("ktxNativeEndian")) { + comp_params.m_ktx_endianness = cCRNKTXNativeEndian; + } + return true; } diff --git a/example2/example2.cpp b/example2/example2.cpp index 910a9b4c..8aacaec1 100644 --- a/example2/example2.cpp +++ b/example2/example2.cpp @@ -78,6 +78,36 @@ static crn_uint8* read_file_into_buffer(const char* pFilename, crn_uint32& size) return pSrc_file_data; } +// Writes a 32-bit value in little-endian byte order. +// DDS files use little-endian fields regardless of the host CPU byte order. +// Do not write integer values or structures directly with fwrite(), +// because that would make the output dependent on the host endianness. +static void write_u32_le(FILE* pFile, crn_uint32 v) +{ + crn_uint8 buf[4]; + buf[0] = static_cast(v); + buf[1] = static_cast(v >> 8); + buf[2] = static_cast(v >> 16); + buf[3] = static_cast(v >> 24); + fwrite(buf, sizeof(buf), 1, pFile); +} + +// Writes a DDS header using the format's required little-endian byte order. +// DDSURFACEDESC2 consists of 32-bit fields. Serialize each field +// through the little endian writer instead of relying on the native +// memory layout of the structure. +// Do not fwrite() the structure directly: DDS files have a fixed byte order +// while the host CPU may be big-endian. +static void write_dds_header(FILE* pFile, const crnlib::DDSURFACEDESC2& h) +{ + static_assert(sizeof(crnlib::DDSURFACEDESC2) % sizeof(crn_uint32) == 0, + "DDS header must contain only 32-bit fields"); + + const crn_uint32* p = reinterpret_cast(&h); + for (size_t i = 0; i < sizeof(h) / sizeof(crn_uint32); i++) + write_u32_le(pFile, p[i]); +} + int main(int argc, char* argv[]) { printf("example2 - Version v%u.%02u Built " __DATE__ ", " __TIME__ "\n", CRNLIB_VERSION / 100, CRNLIB_VERSION % 100); @@ -166,7 +196,7 @@ int main(int argc, char* argv[]) { } // Write the 4-byte DDS signature (not endian safe, but whatever this is a sample). - fwrite(&crnlib::cDDSFileSignature, sizeof(crnlib::cDDSFileSignature), 1, pDDS_file); + write_u32_le(pDDS_file, crnlib::cDDSFileSignature); // Prepare the DDS header. crnlib::DDSURFACEDESC2 dds_desc; @@ -203,7 +233,7 @@ int main(int argc, char* argv[]) { dds_desc.dwFlags |= DDSD_LINEARSIZE; // Write the DDS header to the output file. - fwrite(&dds_desc, sizeof(dds_desc), 1, pDDS_file); + write_dds_header(pDDS_file, dds_desc); // Now transcode all face and mipmap levels into memory, one mip level at a time. void* pImages[cCRNMaxFaces][cCRNMaxLevels]; diff --git a/example3/example3.cpp b/example3/example3.cpp index 2149cbd0..4dc8175f 100644 --- a/example3/example3.cpp +++ b/example3/example3.cpp @@ -72,6 +72,36 @@ static int error(const char* pMsg, ...) { return EXIT_FAILURE; } +// Writes a 32-bit value in little-endian byte order. +// DDS files use little-endian fields regardless of the host CPU byte order. +// Do not write integer values or structures directly with fwrite(), +// because that would make the output dependent on the host endianness. +static void write_u32_le(FILE* pFile, crn_uint32 v) +{ + crn_uint8 buf[4]; + buf[0] = static_cast(v); + buf[1] = static_cast(v >> 8); + buf[2] = static_cast(v >> 16); + buf[3] = static_cast(v >> 24); + fwrite(buf, sizeof(buf), 1, pFile); +} + +// Writes a DDS header using the format's required little-endian byte order. +// DDSURFACEDESC2 consists of 32-bit fields. Serialize each field +// through the little endian writer instead of relying on the native +// memory layout of the structure. +// Do not fwrite() the structure directly: DDS files have a fixed byte order +// while the host CPU may be big-endian. +static void write_dds_header(FILE* pFile, const crnlib::DDSURFACEDESC2& h) +{ + static_assert(sizeof(crnlib::DDSURFACEDESC2) % sizeof(crn_uint32) == 0, + "DDS header must contain only 32-bit fields"); + + const crn_uint32* p = reinterpret_cast(&h); + for (size_t i = 0; i < sizeof(h) / sizeof(crn_uint32); i++) + write_u32_le(pFile, p[i]); +} + int main(int argc, char* argv[]) { printf("example3 - Version v%u.%02u Built " __DATE__ ", " __TIME__ "\n", CRNLIB_VERSION / 100, CRNLIB_VERSION % 100); @@ -241,8 +271,8 @@ int main(int argc, char* argv[]) { return error("Failed creating destination file!\n"); } - // Write the 4-byte DDS signature (not endian safe, but whatever this is a sample). - fwrite(&crnlib::cDDSFileSignature, sizeof(crnlib::cDDSFileSignature), 1, pDDS_file); + // Write the 4-byte DDS signature. + write_u32_le(pDDS_file, crnlib::cDDSFileSignature); // Prepare the DDS header. crnlib::DDSURFACEDESC2 dds_desc; @@ -263,7 +293,7 @@ int main(int argc, char* argv[]) { dds_desc.dwFlags |= DDSD_LINEARSIZE; // Write the DDS header to the output file. - fwrite(&dds_desc, sizeof(dds_desc), 1, pDDS_file); + write_dds_header(pDDS_file, dds_desc); // Write the image's compressed data to the output file. fwrite(pCompressed_data, total_compressed_size, 1, pDDS_file); diff --git a/inc/crn_decomp.h b/inc/crn_decomp.h index 1abb5558..71f20857 100644 --- a/inc/crn_decomp.h +++ b/inc/crn_decomp.h @@ -1278,23 +1278,26 @@ struct dxt1_block { utils::zero_this(this); } - // These methods assume the in-memory rep is in LE byte order. - inline uint32 get_low_color() const { - return m_low_color[0] | (m_low_color[1] << 8U); + inline uint16 get_low_color() const { + return static_cast( + static_cast(m_low_color[0]) | + (static_cast(m_low_color[1]) << 8U)); } - - inline uint32 get_high_color() const { - return m_high_color[0] | (m_high_color[1] << 8U); + + inline uint16 get_high_color() const { + return static_cast( + static_cast(m_high_color[0]) | + (static_cast(m_high_color[1]) << 8U)); } - + inline void set_low_color(uint16 c) { - m_low_color[0] = static_cast(c & 0xFF); - m_low_color[1] = static_cast((c >> 8) & 0xFF); + m_low_color[0] = static_cast(c); + m_low_color[1] = static_cast(c >> 8); } - + inline void set_high_color(uint16 c) { - m_high_color[0] = static_cast(c & 0xFF); - m_high_color[1] = static_cast((c >> 8) & 0xFF); + m_high_color[0] = static_cast(c); + m_high_color[1] = static_cast(c >> 8); } inline uint32 get_selector(uint32 x, uint32 y) const { @@ -1364,11 +1367,18 @@ struct dxt5_block { m_endpoints[1] = static_cast(i); } - uint32 get_endpoints_as_word() const { return m_endpoints[0] | (m_endpoints[1] << 8); } - - uint32 get_selectors_as_word(uint32 index) { + uint16 get_endpoints_as_word() const { + return static_cast( + static_cast(m_endpoints[0]) | + (static_cast(m_endpoints[1]) << 8)); + } + + uint16 get_selectors_as_word(uint index) const { CRND_ASSERT(index < 3); - return m_selectors[index * 2] | (m_selectors[index * 2 + 1] << 8); + + return static_cast( + static_cast(m_selectors[index * 2]) | + (static_cast(m_selectors[index * 2 + 1]) << 8)); } inline uint32 get_selector(uint32 x, uint32 y) const { @@ -3406,8 +3416,20 @@ class crn_unpacker { } uint32 color_selector_index = m_codec.decode(m_selector_delta_dm[0]); if (visible) { - pData[0] = m_color_endpoints[color_endpoint_index]; - pData[1] = m_color_selectors[color_selector_index]; + uint32 endpoint = m_color_endpoints[color_endpoint_index]; + uint32 selector = m_color_selectors[color_selector_index]; + + uint8* pBytes = reinterpret_cast(pData); + + pBytes[0] = static_cast(endpoint); + pBytes[1] = static_cast(endpoint >> 8); + pBytes[2] = static_cast(endpoint >> 16); + pBytes[3] = static_cast(endpoint >> 24); + + pBytes[4] = static_cast(selector); + pBytes[5] = static_cast(selector >> 8); + pBytes[6] = static_cast(selector >> 16); + pBytes[7] = static_cast(selector >> 24); } } } @@ -3467,10 +3489,33 @@ class crn_unpacker { uint32 alpha0_selector_index = m_codec.decode(m_selector_delta_dm[1]); if (visible) { const uint16* pAlpha0_selectors = &m_alpha_selectors[alpha0_selector_index * 3]; - pData[0] = m_alpha_endpoints[alpha0_endpoint_index] | (pAlpha0_selectors[0] << 16); - pData[1] = pAlpha0_selectors[1] | (pAlpha0_selectors[2] << 16); - pData[2] = m_color_endpoints[color_endpoint_index]; - pData[3] = m_color_selectors[color_selector_index]; + + uint32 block0 = m_alpha_endpoints[alpha0_endpoint_index] | (pAlpha0_selectors[0] << 16); + uint32 block1 = pAlpha0_selectors[1] | (pAlpha0_selectors[2] << 16); + uint32 block2 = m_color_endpoints[color_endpoint_index]; + uint32 block3 = m_color_selectors[color_selector_index]; + + uint8* pBytes = reinterpret_cast(pData); + + pBytes[0] = static_cast(block0); + pBytes[1] = static_cast(block0 >> 8); + pBytes[2] = static_cast(block0 >> 16); + pBytes[3] = static_cast(block0 >> 24); + + pBytes[4] = static_cast(block1); + pBytes[5] = static_cast(block1 >> 8); + pBytes[6] = static_cast(block1 >> 16); + pBytes[7] = static_cast(block1 >> 24); + + pBytes[8] = static_cast(block2); + pBytes[9] = static_cast(block2 >> 8); + pBytes[10] = static_cast(block2 >> 16); + pBytes[11] = static_cast(block2 >> 24); + + pBytes[12] = static_cast(block3); + pBytes[13] = static_cast(block3 >> 8); + pBytes[14] = static_cast(block3 >> 16); + pBytes[15] = static_cast(block3 >> 24); } } } @@ -3530,10 +3575,33 @@ class crn_unpacker { if (visible) { const uint16* pAlpha0_selectors = &m_alpha_selectors[alpha0_selector_index * 3]; const uint16* pAlpha1_selectors = &m_alpha_selectors[alpha1_selector_index * 3]; - pData[0] = m_alpha_endpoints[alpha0_endpoint_index] | (pAlpha0_selectors[0] << 16); - pData[1] = pAlpha0_selectors[1] | (pAlpha0_selectors[2] << 16); - pData[2] = m_alpha_endpoints[alpha1_endpoint_index] | (pAlpha1_selectors[0] << 16); - pData[3] = pAlpha1_selectors[1] | (pAlpha1_selectors[2] << 16); + + uint32 block0 = m_alpha_endpoints[alpha0_endpoint_index] | (pAlpha0_selectors[0] << 16); + uint32 block1 = pAlpha0_selectors[1] | (pAlpha0_selectors[2] << 16); + uint32 block2 = m_alpha_endpoints[alpha1_endpoint_index] | (pAlpha1_selectors[0] << 16); + uint32 block3 = pAlpha1_selectors[1] | (pAlpha1_selectors[2] << 16); + + uint8* pBytes = reinterpret_cast(pData); + + pBytes[0] = static_cast(block0); + pBytes[1] = static_cast(block0 >> 8); + pBytes[2] = static_cast(block0 >> 16); + pBytes[3] = static_cast(block0 >> 24); + + pBytes[4] = static_cast(block1); + pBytes[5] = static_cast(block1 >> 8); + pBytes[6] = static_cast(block1 >> 16); + pBytes[7] = static_cast(block1 >> 24); + + pBytes[8] = static_cast(block2); + pBytes[9] = static_cast(block2 >> 8); + pBytes[10] = static_cast(block2 >> 16); + pBytes[11] = static_cast(block2 >> 24); + + pBytes[12] = static_cast(block3); + pBytes[13] = static_cast(block3 >> 8); + pBytes[14] = static_cast(block3 >> 16); + pBytes[15] = static_cast(block3 >> 24); } } } diff --git a/inc/crnlib.h b/inc/crnlib.h index 5ecbeb8a..3a335bce 100644 --- a/inc/crnlib.h +++ b/inc/crnlib.h @@ -186,6 +186,14 @@ enum crn_dxt_compressor_type { cCRNDXTCompressorForceDWORD = 0xFFFFFFFF }; +// KTX endianness. +enum crn_ktx_endianness { + cCRNKTXLittleEndian, + cCRNKTXBigEndian, + cCRNKTXNativeEndian, + cCRNKTXForceDWORD = 0xFFFFFFFF +}; + // Progress callback function. // Processing will stop prematurely (and fail) if the callback returns false. // phase_index, total_phases - high level progress @@ -216,6 +224,7 @@ struct crn_comp_params { m_dxt1a_alpha_threshold = 128; m_dxt_quality = cCRNDXTQualityUber; m_dxt_compressor_type = cCRNDXTCompressorCRN; + m_ktx_endianness = cCRNKTXLittleEndian; m_alpha_component = 3; m_crn_adaptive_tile_color_psnr_derating = 2.0f; @@ -333,6 +342,9 @@ struct crn_comp_params { crn_dxt_quality m_dxt_quality; crn_dxt_compressor_type m_dxt_compressor_type; + // KTX endianness parameters. + crn_ktx_endianness m_ktx_endianness; + // Alpha channel's component. Defaults to 3. crn_uint32 m_alpha_component; diff --git a/test/checksums.tsv b/test/checksums.tsv index 67f1b627..3b2291d6 100644 --- a/test/checksums.tsv +++ b/test/checksums.tsv @@ -469,6 +469,64 @@ build/test/tga-to-all-transparent/sample-storage-rle-rgb8-other8-color-transpare build/test/tga-to-all-transparent/sample-storage-rle-rgb8-other8-color-transparent-8x8.dds d13b620bd08777f9f958849ed99b96293182e8bc7ff29cad31181b38036e6c4d325cc8bf038b24bf6c17403eac66756f2e1946f3739640414eaa2098ba475db0 build/test/tga-to-all-transparent/sample-storage-rle-rgb8-other8-color-transparent-8x8.ktx db575597abf955bb613d3cf85a74c86ad6d3b341e6b68daba79886598d83f60eb1ef49eca84d0a917f2b1d6f8cc2da65867d535981d92176deca2ad5708ef08b build/test/tga-to-all-opaque/sample-storage-rle-rgb8-other8-color-transparent-8x8.jpg 39e66d8f5be9f9e35e61b5c229b88d07161e1ea8087f0be61a988490ac39316980d45c0c9db48a7a67080cfd3446820d7389400327664cb4e72de428663ff8da +build/test/crunch-normal-original-default-png-to-all/sample-normal-ej01-floortrim02-128x128.tga a1fc8bb9f3e45df2b6fd4fb1c24ecb788d24bf69b2e396218892f62106cc4f2b146e937872c7a7ba58d6526d1145fd8943184bd9e012dc55bbb416d532154019 +build/test/crunch-normal-original-default-png-to-all/sample-normal-ej01-floortrim02-128x128.bmp 15c90496350c2c4319959ca183606f5cea48532fe49258328f4fbafd176b2d857ed14bd98efd6033bbf21df355f84531bd87b35ade51a33ab55da0d2ab5ae549 +build/test/crunch-normal-original-default-png-to-all/sample-normal-ej01-floortrim02-128x128.png 2342a44dbd328b69d47c322ad15dbd68d553f2878213a9ac23eb282aada4177ceed6265a8dd55c161a12df7939371cb3d18983e316c794f5523265c06772c7df +build/test/crunch-normal-original-default-png-to-all/sample-normal-ej01-floortrim02-128x128.crn 9362f01a9eca9792f878815463cadb635eb66ad6dc67299450c4feb8cd40e55663098c56d4c2ed475b59133deaa3dc6c77a264986294c83167677d5493f82b7f +build/test/crunch-normal-original-default-png-to-all/sample-normal-ej01-floortrim02-128x128.dds b8c8b937cb03e0a20b9dc619d845d640c1ecc9c9dbcbe151527f365bb66c778382ad47adfa45c646c3cc4c9a89a34a5535e702777452222697ec4a9ec00c4f82 +build/test/crunch-normal-original-default-png-to-all/sample-normal-ej01-floortrim02-128x128.ktx 8a22d1b1aec6f9fb10d37ff86c66dd43757e80a68ad6b97fcd61bc5cb22dc90ec060be7c69887f39fe2eeb37133e57f774e7d78b2a3fc656973c86f1e5c7b0fd +build/test/crunch-normal-original-default-png-to-all/sample-normal-ej01-floortrim02-128x128.jpg 6b6c5296c6ab3c0470a894f46ff94883f77bcd44dc3698c06c2cd1be9ac7e169ee5ee77c7b97e1cd3ae442a2a2968f211b589eaf9e699347c9a9e075292c629f +build/test/crunch-normal-renormalize-default-png-to-all/sample-normal-ej01-floortrim02-128x128.tga 47b5332b50ac03139586233ae1fdda6b84425a6bab806d8fb93aed5178c809b8e19b0470bd15f3c8fcca4dce2a4b46bfba11429cc855eed3164b6997e8761103 +build/test/crunch-normal-renormalize-default-png-to-all/sample-normal-ej01-floortrim02-128x128.bmp a3dab35c03a469ae837c743b6835fe90976c01779a1f1b1f546d1ac3017441a56e75ae9965e2a3f4e135115f859cd65c060c809129926ce316107bc5272440ac +build/test/crunch-normal-renormalize-default-png-to-all/sample-normal-ej01-floortrim02-128x128.png 6a07dec1cb2e306bdca19c2d9050fd21a6427b92dfa3d197213766a00cb05ac0d991adb9d54d5d839b22b8afdd5510fd2a104a961ec046fb28f9b0513d44309b +build/test/crunch-normal-renormalize-default-png-to-all/sample-normal-ej01-floortrim02-128x128.crn a6f7ef910eb3956254b650d41d80b353918b2fb838db0e0a58e8011dd6e330157a6e67682d0c5dc300630dc6d487b5d06aa49611a58bf43b72805f34045bf595 +build/test/crunch-normal-renormalize-default-png-to-all/sample-normal-ej01-floortrim02-128x128.dds df4b980c89b5d1b88a24915f68950563be601a8116640bd4db468a731ca227f8773aeee43652b5ca63bb4ab385aa6807d6d3aa2758069189888a4ddcab16c64f +build/test/crunch-normal-renormalize-default-png-to-all/sample-normal-ej01-floortrim02-128x128.ktx b4f067af5e1c5899f270fd0268ccc1a46e493f043a85f188aa905ba78654b00e53cc7cff8d27cc44a40ba68317941e04a2d8b97dc7e4d295585778ae9daa41e4 +build/test/crunch-normal-renormalize-default-png-to-all/sample-normal-ej01-floortrim02-128x128.jpg 85af5a41083e0fbd2b636b9443c360ca002d35f429edb212c4aa79b659700960b01e299fba7d16c0a2228c712eeafd326dfce3b47b25e28fda6475688c965a08 +build/test/crunch-normal-original-dxn-png-to-dxt/sample-normal-ej01-floortrim02-128x128.crn 305a8921e1b3c521da7791f6c83bb63d2369f60d52ba5559a21e848ca4b7c607e0b95d522b0eb95723dbe1fd22b51d7280633ac3ca739d87832c19be564b08cb +build/test/crunch-normal-original-dxn-png-to-dxt/sample-normal-ej01-floortrim02-128x128.dds 9ee4308a1be14dae40a8bd8f36aad218ce76371a5e7c98d0aca483a03cd1e4111a3c70b2bff817344f1afc5d7ee67bcc0c51d5336f593c9cdaea9dad0a7b41ec +build/test/crunch-normal-original-dxn-png-to-dxt/sample-normal-ej01-floortrim02-128x128.ktx 802801053a974ba2112535b3c83ff1019e393ce0bd5b8ca9790783cb4819e874f667c054eac6287549c3fd506525490c25e66db78a41b6d5fe9a58e16ca71da8 +build/test/crunch-normal-renormalize-dxn-png-to-dxt/sample-normal-ej01-floortrim02-128x128.crn 6ea08ed891e47f430e343826435b9046c3766c3f5fd410986b4661a10a4206edfe155140a8435afd71c4d06f8d25d46cdfdd4082dee07fb74eba0f6c10503f9e +build/test/crunch-normal-renormalize-dxn-png-to-dxt/sample-normal-ej01-floortrim02-128x128.dds e58aba7bce435032a8ba63da7f29470c7499a9f8023009f826b66b20ee70ea3a77a4c10a9b8b88c36f65af9aad683ba6e976f6900d2fa9e59e58600180149c2a +build/test/crunch-normal-renormalize-dxn-png-to-dxt/sample-normal-ej01-floortrim02-128x128.ktx c653da4a1cbb3b32854ae57a96bf23e7e584a52777391ca737bb450fdb6861b0cab240290a93239c90e40f2ab0b6b4b0d03a5a25b30645af57ef5313cd58c1db +build/test/crunch-endian-original-default-little-to-big/sample-normal-ej01-floortrim02-128x128-default-from-tga.ktx e92023cdc9abfa7f3c65989f65e1f45059f0f4d67967ef6b1cb1b0254c69801ab1419a72b35c10b9d3b90608aebe3ba07770182595fbaf5c1802951247c98984 +build/test/crunch-endian-original-default-little-to-big/sample-icon-unvanquished-64x64-from-tga.ktx 8e470892e19ebdca38a834f2a1d880f87800b88d81491863d0422c33087acbc908715044d1734c4330a316da29f3d11e3cbf3dfa22a67ef191fec0704bdf2728 +build/test/crunch-endian-original-default-big-to-little/sample-normal-ej01-floortrim02-128x128-default-from-ktx.tga 41e904dd50624d5c6363c9ade546c315bc43b45d7e7ea13e20846bcdfa5b32bc88b1fa18dc307fd5256238dca5fc725f8e1dee67928a04b47663a446ee7e1f33 +build/test/crunch-endian-original-default-big-to-little/sample-icon-unvanquished-64x64-from-ktx.tga e02ffd43206da4bac86bfe936e9eb49b2d652e513bfee2b36db63ce716e03642707c592624f36b899d4dcec06491aa16a9983778b7e76c10b1ba6b2abd1ca796 +build/test/crunch-endian-original-default-big-to-little/sample-normal-ej01-floortrim02-128x128-default-from-ktx.bmp 1dcc4576aeddfb003da8858b3f49f1512d74a35ec6e1d7dc79a4190b30d8a5fa05da6e606ed3b59512c74a0666f1fb99f33be14cfe8501bf51d0ebc181d5a8f6 +build/test/crunch-endian-original-default-big-to-little/sample-icon-unvanquished-64x64-from-ktx.bmp 5aeb9549fc4b8476f9c3718d4ed117f69198dcb9dd41b8b4c6c86bf2d2cad83d106aa4cf0c2083b39fd5acd3e072a49e9755a6f30e7f52c8279bbf2a671b52e6 +build/test/crunch-endian-original-default-big-to-little/sample-normal-ej01-floortrim02-128x128-default-from-ktx.png a1d080ee0d02d8cc4b0ce2e32559127fe014057bdbe1aa71e26a89ea3d965b3d8c634aa3713da5fdb0485ea60263c91704451bc0e8bdb9ef4aed1ae019f46f6f +build/test/crunch-endian-original-default-big-to-little/sample-icon-unvanquished-64x64-from-ktx.png c6639a99f31c4e6e6461805f1fa418c5a68bbba4b4e4addb32d7097fa329c7f32b6da51219e6b73401972826dd1a891584c4358bb824bd1e499231288a471c45 +build/test/crunch-endian-original-default-big-to-little/sample-normal-ej01-floortrim02-128x128-default-from-ktx.crn b43299f299d412d5779402ee65ba771d229084dfb704014fc6a86ce531ae3e896f9e5f0993595aaaf2b5fba100267b25b0490b0791d6d0e8f12d084bb7beaed2 +build/test/crunch-endian-original-default-big-to-little/sample-icon-unvanquished-64x64-from-ktx.crn 0431d7bdd5da69d7326ba878afd20a1aea21792ae86a18f14ed012f6b162e9800fc27bd90705af27e4dfe8fd7573e75601e19586f532f01355439887a49a8519 +build/test/crunch-endian-original-default-big-to-little/sample-normal-ej01-floortrim02-128x128-default-from-ktx.dds f550c5186abd109c157314eff19b9f9920a6a6392327b2360ed4d3ef58fdc40db439086db789ac1f039754a1a2e150b5451e33acea7d9f0127fdd2b67cd885e0 +build/test/crunch-endian-original-default-big-to-little/sample-icon-unvanquished-64x64-from-ktx.dds 985f251485d0735bf631eb4153969d18dc7f421befd5f7fade308f79437a3c0fed58f70ed7d3ed9942a0102b76374e9c0eff175695d138f7f5d95d42264913ed +build/test/crunch-endian-original-default-big-to-little/sample-normal-ej01-floortrim02-128x128-default-from-ktx.ktx 4303a268f79dfe3afbed1e2966028519f4034311d52d1d76c8369f48e74bb5eb84491a535efe517ca5f5df3c9bca721f44e613527e09cd18822f5c81634a0e87 +build/test/crunch-endian-original-default-big-to-little/sample-icon-unvanquished-64x64-from-ktx.ktx 233e5d029b08cc45f8b3e9853edc6dc5dd5cab49cb0f95e870b9b6db917b2ddceaa2756d55aec1785c147375bc7a37030e3a8cde77d364f73d86043e99a7969a +build/test/crunch-endian-original-default-big-to-little/sample-normal-ej01-floortrim02-128x128-default-from-ktx.jpg 30423144edff0f7435e5775ebf2c143a92ca8715e6f120b45af2bf697b06672c6dbfe354c4ffef7d659d515346440ba30cdb452f2a951da6a9874f17dd535243 +build/test/crunch-endian-original-default-big-to-little/sample-icon-unvanquished-64x64-from-ktx.jpg cf17541cf476b59dab8218a3ec79abeed4b7fd6f20d2232d77b004d95a3a600fded9549fbe3f2097ee7852dd735e87ada3101ff4e28db0e630e1c98ba89263fe +build/test/crunch-endian-original-default-little-to-big/sample-normal-ej01-floortrim02-128x128-default-from-bmp.ktx e92023cdc9abfa7f3c65989f65e1f45059f0f4d67967ef6b1cb1b0254c69801ab1419a72b35c10b9d3b90608aebe3ba07770182595fbaf5c1802951247c98984 +build/test/crunch-endian-original-default-little-to-big/sample-icon-unvanquished-64x64-from-bmp.ktx 8e470892e19ebdca38a834f2a1d880f87800b88d81491863d0422c33087acbc908715044d1734c4330a316da29f3d11e3cbf3dfa22a67ef191fec0704bdf2728 +build/test/crunch-endian-original-default-little-to-big/sample-normal-ej01-floortrim02-128x128-default-from-png.ktx e92023cdc9abfa7f3c65989f65e1f45059f0f4d67967ef6b1cb1b0254c69801ab1419a72b35c10b9d3b90608aebe3ba07770182595fbaf5c1802951247c98984 +build/test/crunch-endian-original-default-little-to-big/sample-icon-unvanquished-64x64-from-png.ktx 8e470892e19ebdca38a834f2a1d880f87800b88d81491863d0422c33087acbc908715044d1734c4330a316da29f3d11e3cbf3dfa22a67ef191fec0704bdf2728 +build/test/crunch-endian-original-default-little-to-big/sample-normal-ej01-floortrim02-128x128-default-from-crn.ktx 74b9a4c02624305621ecfc20e72ae8099826eaf30df7bc4feb3262b41a760f2895c798925bd02480d47eacb85636a4bb3f05f0207135cd95967ed64603cefdf3 +build/test/crunch-endian-original-default-little-to-big/sample-normal-ej01-floortrim02-128x128-dxn-from-crn.ktx 9bf035bcd1a5165a7f65262f41f3ee9a273eb98f9283d2f5a2eddea0d23d3d0a8679fc8b6f49241c0742658a6ef5d08124f8978f235d54ea0f88eb88a1705c4c +build/test/crunch-endian-original-default-little-to-big/sample-icon-unvanquished-64x64-from-crn.ktx eb3c7f36851cf3fb1392505d2515c2305d44717f9c2a1f73aba2a886506fb7b4417dafb1cad5e7939b08d4683f795f68631d7a32b56a177f484a1db6cbe700d6 +build/test/crunch-endian-original-default-big-to-little/sample-normal-ej01-floortrim02-128x128-dxn-from-ktx.tga ac17bbd83f3561fc5bf0d71f920419c569f4f350881d7b489b4e8615984df8033f3fcacbe85959d2c2ec3565aa18443150878459e06eb02ff9ce1d2dc8d32819 +build/test/crunch-endian-original-default-big-to-little/sample-normal-ej01-floortrim02-128x128-dxn-from-ktx.bmp 41528963fa76e4512a76b949fc115163e871b673f38f6199cc3f7ddbb4db8e5bed4473f4f21c91bf9e00c4357d55cce3163ec2fe0a49af9b9fa46a6463851c59 +build/test/crunch-endian-original-default-big-to-little/sample-normal-ej01-floortrim02-128x128-dxn-from-ktx.png 7fde6023bd5289dddccf070d6bb4f36cdcf5ef873b27c736745d138f12ecfe748be27b45f84e1b0754b5ab7e371d31e8dc70f755cbbe70016443e739fe74a09e +build/test/crunch-endian-original-default-big-to-little/sample-normal-ej01-floortrim02-128x128-dxn-from-ktx.crn 14a27e95f77bd9f138760478afbe93db16981c1cb2f65dbccdba6cc6c4601493f31ab8269354b2e291d31d2856f8e759b0fcf45cb9ec7609e17efd319c50f763 +build/test/crunch-endian-original-default-big-to-little/sample-normal-ej01-floortrim02-128x128-dxn-from-ktx.dds 9ee4308a1be14dae40a8bd8f36aad218ce76371a5e7c98d0aca483a03cd1e4111a3c70b2bff817344f1afc5d7ee67bcc0c51d5336f593c9cdaea9dad0a7b41ec +build/test/crunch-endian-original-default-big-to-little/sample-normal-ej01-floortrim02-128x128-dxn-from-ktx.ktx 802801053a974ba2112535b3c83ff1019e393ce0bd5b8ca9790783cb4819e874f667c054eac6287549c3fd506525490c25e66db78a41b6d5fe9a58e16ca71da8 +build/test/crunch-endian-original-default-big-to-little/sample-normal-ej01-floortrim02-128x128-dxn-from-ktx.jpg 698f7867e9b6d29656be30f6b0b0b0f48103212c58bafe9f8d4b1f548bfe665ae532961dce467790fd14b03b1bc49561d64d5e03fa7368446586a4ed6d1042ea +build/test/crunch-endian-original-default-little-to-big/sample-normal-ej01-floortrim02-128x128-default-from-dds.ktx bac591d3074cd7b454f01900f6281355dcd55856744af47e35451d5b65cb45b89042475894fec4a011759fdb77744913425302bc6ddfb8295199c6fa20f5cbf6 +build/test/crunch-endian-original-default-little-to-big/sample-normal-ej01-floortrim02-128x128-dxn-from-dds.ktx 96d92ada468e92e1907c2c660c7ed9c07780237a99231fce08a1326cafc1454cdaa15ab5f8e8d0d0e7c4f5e8cc8f98a7a049df4cf7c37980d2be3cf2175ee1c1 +build/test/crunch-endian-original-default-little-to-big/sample-icon-unvanquished-64x64-from-dds.ktx 895509ad26f36cf14adeb8464ecb0f28b9e2e992ab5f718663c155295cce8893b10926eb41fb216b622ffaec61d936dcac74578be0b05555757906838106eec0 +build/test/crunch-endian-original-default-little-to-big/sample-normal-ej01-floortrim02-128x128-default-from-ktx.ktx e92023cdc9abfa7f3c65989f65e1f45059f0f4d67967ef6b1cb1b0254c69801ab1419a72b35c10b9d3b90608aebe3ba07770182595fbaf5c1802951247c98984 +build/test/crunch-endian-original-default-little-to-big/sample-normal-ej01-floortrim02-128x128-dxn-from-ktx.ktx 96d92ada468e92e1907c2c660c7ed9c07780237a99231fce08a1326cafc1454cdaa15ab5f8e8d0d0e7c4f5e8cc8f98a7a049df4cf7c37980d2be3cf2175ee1c1 +build/test/crunch-endian-original-default-little-to-big/sample-icon-unvanquished-64x64-from-ktx.ktx 8e470892e19ebdca38a834f2a1d880f87800b88d81491863d0422c33087acbc908715044d1734c4330a316da29f3d11e3cbf3dfa22a67ef191fec0704bdf2728 +build/test/crunch-endian-original-default-little-to-big/sample-normal-ej01-floortrim02-128x128-default-from-jpg.ktx 66cb0b7970d8f7a779e20426704c97297a2854686c5458679cea13f0e35bf1f916e0f189e3ee207e0cd6261aa5e85df33cb1acee0e52e70256e54f59efee9ff3 +build/test/crunch-endian-original-default-little-to-big/sample-icon-unvanquished-64x64-from-jpg.ktx 6f0f507cc9155980fc2d1098831c16c588b3471bf1bdba5ef5c3a34239da49bb460f69fc066f5ce837cd0da978e68d79d03d69a5fe435013932d4bef3fe4fc71 build/test/example1-icon-png-to-dds/sample-icon-unvanquished-64x64.dds ce98b21c574b4883e4aff4a0b92fd0f533f22e00ab753686f353111fa7503287c2c4dba25f2c2670508630d79b59464578e094461bad81ad803e1d58ed5e69f8 build/test/example1-icon-png-to-crn/sample-icon-unvanquished-64x64.crn a3b526e95f44b6a4c919bc8eb79f4debf592f0bccc163c900cd1b6f9add0ce58c3db9fc8dac1828d350bbcd915823d7743a0c114a533fbc08c163fcb4846d2f5 build/test/example1-icon-crn-to-dds/sample-icon-unvanquished-64x64.dds 2aae5c5bd8c6a82d1cf3fc9d9da12fe3fc5be8651261ff1cb359e754308fd81e93bf304aeb55ada0bad50794d6e97d3e48084e5a4f48fa7caec4ed954152bae9 diff --git a/test/sample-normal-ej01-floortrim02-128x128.png b/test/sample-normal-ej01-floortrim02-128x128.png new file mode 100644 index 00000000..2ce81276 Binary files /dev/null and b/test/sample-normal-ej01-floortrim02-128x128.png differ diff --git a/test/test.py b/test/test.py index 4b44c548..720c5491 100755 --- a/test/test.py +++ b/test/test.py @@ -530,6 +530,120 @@ def start_from(items, value): mkdir(out_dir) crunch(in_path, out_path, clone_name) +for sample_name in [ + "sample-normal-ej01-floortrim02-128x128", +]: + layout_dict_list = [ + { + "name": "default", + "option": [], + }, + { + "name": "dxn", + "option": ["-dxn"], + }, + ] + + for layout in layout_dict_list: + layout_name = layout["name"] + layout_option = layout["option"] + + normal_dict_list = [ + { + "name": "original", + "option": [], + }, + { + "name": "renormalize", + "option": ["-renormalize", "-rtopmip"], + }, + ] + + for normal in normal_dict_list: + normal_name = normal["name"] + normal_option = normal["option"] + + for out_format in all_format_list: + if out_format not in dxt_format_list and layout_name == "dxn": + continue + + in_format = "png" + in_path = f"test/{sample_name}.{in_format}" + + if layout_name == "dxn": + out_dir_format = "dxt" + else: + out_dir_format = "all" + + out_dir = f"build/test/crunch-normal-{normal_name}-{layout_name}-{in_format}-to-{out_dir_format}" + out_path = f"{out_dir}/{sample_name}.{out_format}" + + option_list = [] + option_list.extend(normal_option) + option_list.extend(layout_option) + + mkdir(out_dir) + crunch(in_path, out_path, None, options=option_list) + + endian_dict_list = [ + { + "name": "little-to-big", + "option": ["-ktxBigEndian"] + }, + { + "name": "big-to-little", + "option": [], + }, + ] + +for in_format in all_format_list: + mkdir("build/test/crunch-endian-original-default-little-to-big") + + crunch( + f"build/test/crunch-normal-original-default-png-to-all/sample-normal-ej01-floortrim02-128x128.{in_format}", + f"build/test/crunch-endian-original-default-little-to-big/sample-normal-ej01-floortrim02-128x128-default-from-{in_format}.ktx", + None, + options=["-ktxBigEndian"], + ) + + if in_format in dxt_format_list: + crunch( + f"build/test/crunch-normal-original-dxn-png-to-dxt/sample-normal-ej01-floortrim02-128x128.{in_format}", + f"build/test/crunch-endian-original-default-little-to-big/sample-normal-ej01-floortrim02-128x128-dxn-from-{in_format}.ktx", + None, + options=["-ktxBigEndian", "-dxn"], + ) + + crunch( + f"build/test/crunch-icon-png-to-all/sample-icon-unvanquished-64x64.{in_format}", + f"build/test/crunch-endian-original-default-little-to-big/sample-icon-unvanquished-64x64-from-{in_format}.ktx", + None, + options=["-ktxBigEndian"], + ) + + mkdir("build/test/crunch-endian-original-default-big-to-little") + + for out_format in all_format_list: + crunch( + f"build/test/crunch-endian-original-default-little-to-big/sample-normal-ej01-floortrim02-128x128-default-from-{in_format}.ktx", + f"build/test/crunch-endian-original-default-big-to-little/sample-normal-ej01-floortrim02-128x128-default-from-ktx.{out_format}", + None, + ) + + if in_format in dxt_format_list: + crunch( + f"build/test/crunch-endian-original-default-little-to-big/sample-normal-ej01-floortrim02-128x128-dxn-from-{in_format}.ktx", + f"build/test/crunch-endian-original-default-big-to-little/sample-normal-ej01-floortrim02-128x128-dxn-from-ktx.{out_format}", + None, + options=["-dxn"], + ) + + crunch( + f"build/test/crunch-endian-original-default-little-to-big/sample-icon-unvanquished-64x64-from-{in_format}.ktx", + f"build/test/crunch-endian-original-default-big-to-little/sample-icon-unvanquished-64x64-from-ktx.{out_format}", + None, + ) + example(1, "test/sample-icon-unvanquished-64x64.png", None, None, options=["i"]) mkdir("build/test/example1-icon-png-to-dds")