summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/bimg
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/bimg')
-rw-r--r--3rdparty/bimg/.editorconfig13
-rw-r--r--3rdparty/bimg/.travis.yml6
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/.gitignore5
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/BUILD.bazel29
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/CMakeLists.txt46
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/CONTRIBUTING.md28
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/GoogleTest-CMakeLists.txt.in15
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/LICENSE202
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/README.md71
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/WORKSPACE37
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/cmake-format.json38
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/include/astc-codec/astc-codec.h75
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/.clang-format4
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/base/BUILD.bazel49
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/base/CMakeLists.txt27
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/base/bit_stream.h77
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/base/bottom_n.h78
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/base/math_utils.h80
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/base/optional.h520
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/base/string_utils.h69
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/base/test/bit_stream_test.cpp141
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/base/test/bottom_n_test.cpp108
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/base/test/math_utils_test.cpp78
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/base/test/optional_test.cpp481
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/base/test/string_utils_test.cpp110
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/base/test/type_traits_test.cpp130
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/base/test/uint128_test.cpp140
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/base/type_traits.h172
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/base/uint128.h175
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/base/utils.h35
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/BUILD.bazel246
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/CMakeLists.txt95
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/astc_file.cc185
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/astc_file.h97
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/codec.cc132
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/codec.h41
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/endpoint_codec.cc963
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/endpoint_codec.h90
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/footprint.cc162
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/footprint.h106
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/integer_sequence_codec.cc574
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/integer_sequence_codec.h169
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/intermediate_astc_block.cc591
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/intermediate_astc_block.h128
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/logical_astc_block.cc262
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/logical_astc_block.h127
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/partition.cc601
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/partition.h97
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/physical_astc_block.cc761
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/physical_astc_block.h128
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/quantization.cc462
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/quantization.h65
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/astc_fuzzer.cc36
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/codec_test.cc181
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/endpoint_codec_test.cc463
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/footprint_test.cc97
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/image_utils.h217
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/integer_sequence_codec_test.cc336
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/intermediate_astc_block_test.cc454
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/logical_astc_block_test.cc273
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/partition_test.cc263
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/physical_astc_block_test.cc361
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/quantization_test.cc288
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/weight_infill_test.cc69
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/atlas_small_4x4.astcbin0 -> 65552 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/atlas_small_4x4.bmpbin0 -> 262282 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/atlas_small_5x5.astcbin0 -> 43280 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/atlas_small_5x5.bmpbin0 -> 262282 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/atlas_small_6x6.astcbin0 -> 29600 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/atlas_small_6x6.bmpbin0 -> 262282 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/atlas_small_8x8.astcbin0 -> 16400 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/atlas_small_8x8.bmpbin0 -> 262282 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkerboard.astcbin0 -> 80 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_10.astcbin0 -> 1616 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_11.astcbin0 -> 1952 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_12.astcbin0 -> 2320 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_4.astcbin0 -> 272 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_5.astcbin0 -> 416 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_6.astcbin0 -> 592 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_7.astcbin0 -> 800 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_8.astcbin0 -> 1040 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_9.astcbin0 -> 1312 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_10x10.astcbin0 -> 272 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_10x10.bmpbin0 -> 3210 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_10x5.astcbin0 -> 464 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_10x5.bmpbin0 -> 3210 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_10x6.astcbin0 -> 400 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_10x6.bmpbin0 -> 3210 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_10x8.astcbin0 -> 272 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_10x8.bmpbin0 -> 3210 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_12x10.astcbin0 -> 208 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_12x10.bmpbin0 -> 3210 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_12x12.astcbin0 -> 160 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_12x12.bmpbin0 -> 3210 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_4x4.astcbin0 -> 1040 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_4x4.bmpbin0 -> 3210 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_5x4.astcbin0 -> 912 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_5x4.bmpbin0 -> 3210 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_5x5.astcbin0 -> 800 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_5x5.bmpbin0 -> 3210 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_6x5.astcbin0 -> 688 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_6x5.bmpbin0 -> 3210 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_6x6.astcbin0 -> 592 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_6x6.bmpbin0 -> 3210 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_8x5.astcbin0 -> 464 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_8x5.bmpbin0 -> 3210 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_8x6.astcbin0 -> 400 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_8x6.bmpbin0 -> 3210 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_8x8.astcbin0 -> 272 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_8x8.bmpbin0 -> 3210 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_12x12.astcbin0 -> 7312 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_12x12.bmpbin0 -> 193674 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_4x4.astcbin0 -> 64528 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_4x4.bmpbin0 -> 193674 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_5x4.astcbin0 -> 51856 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_5x4.bmpbin0 -> 193674 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_6x6.astcbin0 -> 29200 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_6x6.bmpbin0 -> 193674 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_8x8.astcbin0 -> 16144 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_8x8.bmpbin0 -> 193674 bytes
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/tools/astc_inspector_cli.cc785
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/types.h74
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/weight_infill.cc122
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/src/decoder/weight_infill.h38
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/third_party/BUILD0
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/third_party/honggfuzz.BUILD39
-rwxr-xr-x3rdparty/bimg/3rdparty/astc-codec/tools/build-ci/linux/build.sh76
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/tools/build-ci/linux/continuous.cfg3
-rw-r--r--3rdparty/bimg/3rdparty/astc-codec/tools/build-ci/linux/presubmit.cfg3
-rw-r--r--3rdparty/bimg/3rdparty/astc/astc_lib.cpp223
-rw-r--r--3rdparty/bimg/3rdparty/astc/astc_lib.h6
-rw-r--r--3rdparty/bimg/3rdparty/astc/mathlib.cpp2
-rw-r--r--3rdparty/bimg/3rdparty/astc/mathlib.h2
-rw-r--r--3rdparty/bimg/3rdparty/maratis-tcl/LICENSE22
-rw-r--r--3rdparty/bimg/3rdparty/maratis-tcl/m_image.h2340
-rw-r--r--3rdparty/bimg/3rdparty/nvtt/nvcore/posh.h4
-rw-r--r--3rdparty/bimg/3rdparty/nvtt/nvtt.cpp2
-rw-r--r--3rdparty/bimg/3rdparty/tinyexr/tinyexr.h277
-rw-r--r--3rdparty/bimg/LICENSE18
-rw-r--r--3rdparty/bimg/README.md28
-rw-r--r--3rdparty/bimg/include/bimg/bimg.h3
-rw-r--r--3rdparty/bimg/include/bimg/decode.h2
-rw-r--r--3rdparty/bimg/include/bimg/encode.h8
-rw-r--r--3rdparty/bimg/makefile11
-rw-r--r--3rdparty/bimg/scripts/bimg.lua28
-rw-r--r--3rdparty/bimg/scripts/bimg_decode.lua4
-rw-r--r--3rdparty/bimg/scripts/bimg_encode.lua2
-rw-r--r--3rdparty/bimg/scripts/genie.lua2
-rw-r--r--3rdparty/bimg/scripts/texturec.lua2
-rw-r--r--3rdparty/bimg/src/bimg_p.h2
-rw-r--r--3rdparty/bimg/src/image.cpp137
-rw-r--r--3rdparty/bimg/src/image_cubemap_filter.cpp60
-rw-r--r--3rdparty/bimg/src/image_decode.cpp7
-rw-r--r--3rdparty/bimg/src/image_encode.cpp57
-rw-r--r--3rdparty/bimg/src/image_gnf.cpp2
-rw-r--r--3rdparty/bimg/tools/texturec/texturec.cpp161
156 files changed, 13898 insertions, 2813 deletions
diff --git a/3rdparty/bimg/.editorconfig b/3rdparty/bimg/.editorconfig
index 4fa189592fd..3a55382bae2 100644
--- a/3rdparty/bimg/.editorconfig
+++ b/3rdparty/bimg/.editorconfig
@@ -1,19 +1,14 @@
root = true
[*]
-indent_style = tab
-indent_size = 4
-end_of_line = lf
+charset = utf-8
+indent_style = tab
+indent_size = 4
+end_of_line = lf
max_line_length = 100
insert_final_newline = true
trim_trailing_whitespace = true
-[include/bgfx/c99/*.h]
-indent_style = space
-
-[*.ninja]
-indent_style = space
-
[*.md]
trim_trailing_whitespace = false
max_line_length = 80
diff --git a/3rdparty/bimg/.travis.yml b/3rdparty/bimg/.travis.yml
index c656c72ade1..db4d44046ea 100644
--- a/3rdparty/bimg/.travis.yml
+++ b/3rdparty/bimg/.travis.yml
@@ -11,15 +11,15 @@ addons:
sources:
- ubuntu-toolchain-r-test
packages:
- - gcc-5
- - g++-5
+ - gcc-8
+ - g++-8
- clang
before_script:
git clone https://github.com/bkaradzic/bx ../bx
script:
- - if [ "$TRAVIS_OS_NAME" == "linux" ]; then make build CXX="g++-5" CC="gcc-5"; fi
+ - if [ "$TRAVIS_OS_NAME" == "linux" ]; then make build CXX="g++-8" CC="gcc-8"; fi
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then make build; fi
branches:
diff --git a/3rdparty/bimg/3rdparty/astc-codec/.gitignore b/3rdparty/bimg/3rdparty/astc-codec/.gitignore
new file mode 100644
index 00000000000..9e281007644
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/.gitignore
@@ -0,0 +1,5 @@
+bazel-*
+.bazelrc
+build
+.vs
+.vscode
diff --git a/3rdparty/bimg/3rdparty/astc-codec/BUILD.bazel b/3rdparty/bimg/3rdparty/astc-codec/BUILD.bazel
new file mode 100644
index 00000000000..ea5a2c41936
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/BUILD.bazel
@@ -0,0 +1,29 @@
+# Copyright 2018 Google LLC
+#
+# 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
+#
+# https://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.
+
+licenses(["notice"])
+
+cc_library(
+ name = "api",
+ hdrs = ["include/astc-codec/astc-codec.h"],
+ visibility = ["//src/decoder:__pkg__"],
+)
+
+cc_library(
+ name = "astc_codec",
+ hdrs = ["include/astc-codec/astc-codec.h"],
+ includes = ["include"],
+ visibility = ["//visibility:public"],
+ deps = ["//src/decoder:codec"],
+)
diff --git a/3rdparty/bimg/3rdparty/astc-codec/CMakeLists.txt b/3rdparty/bimg/3rdparty/astc-codec/CMakeLists.txt
new file mode 100644
index 00000000000..4ecb921b50c
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/CMakeLists.txt
@@ -0,0 +1,46 @@
+# Copyright 2018 Google LLC
+#
+# 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
+#
+# https://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.
+cmake_minimum_required(VERSION 3.1.0)
+project(astc-codec)
+
+option(OPTION_ASTC_TESTS "Build all the unit tests." ON)
+
+# TODO add support for the fuzzer, it has some additional dependencies we are not
+# yet bringing in.
+option(OPTION_BUILD_FUZZER "Build the fuzzer tests." OFF)
+
+set (CMAKE_CXX_STANDARD 11)
+if(OPTION_ASTC_TESTS)
+ enable_testing()
+
+ # No need to build gmock if an external project defines it.
+ if(NOT TARGET gmock_main)
+ # We use the approach suggested by https://crascit.com/2015/07/25/cmake-gtest/ to download gtest.
+ include(ExternalProject)
+ # Download and unpack googletest at configure time
+ configure_file(GoogleTest-CMakeLists.txt.in googletest-download/CMakeLists.txt)
+ execute_process(COMMAND "${CMAKE_COMMAND}" -G "${CMAKE_GENERATOR}" .
+ WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/googletest-download")
+ execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/googletest-download")
+
+ # Prevent GoogleTest from overriding our compiler/linker options when building with Visual Studio
+ set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
+
+ # Add googletest directly to our build. This adds the following targets: gtest, gtest_main, gmock and gmock_main
+ add_subdirectory("${CMAKE_BINARY_DIR}/googletest-src" "${CMAKE_BINARY_DIR}/googletest-build")
+ endif()
+endif()
+
+add_subdirectory(src/base)
+add_subdirectory(src/decoder)
diff --git a/3rdparty/bimg/3rdparty/astc-codec/CONTRIBUTING.md b/3rdparty/bimg/3rdparty/astc-codec/CONTRIBUTING.md
new file mode 100644
index 00000000000..939e5341e74
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/CONTRIBUTING.md
@@ -0,0 +1,28 @@
+# How to Contribute
+
+We'd love to accept your patches and contributions to this project. There are
+just a few small guidelines you need to follow.
+
+## Contributor License Agreement
+
+Contributions to this project must be accompanied by a Contributor License
+Agreement. You (or your employer) retain the copyright to your contribution;
+this simply gives us permission to use and redistribute your contributions as
+part of the project. Head over to <https://cla.developers.google.com/> to see
+your current agreements on file or to sign a new one.
+
+You generally only need to submit a CLA once, so if you've already submitted one
+(even if it was for a different project), you probably don't need to do it
+again.
+
+## Code reviews
+
+All submissions, including submissions by project members, require review. We
+use GitHub pull requests for this purpose. Consult
+[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
+information on using pull requests.
+
+## Community Guidelines
+
+This project follows [Google's Open Source Community
+Guidelines](https://opensource.google.com/conduct/).
diff --git a/3rdparty/bimg/3rdparty/astc-codec/GoogleTest-CMakeLists.txt.in b/3rdparty/bimg/3rdparty/astc-codec/GoogleTest-CMakeLists.txt.in
new file mode 100644
index 00000000000..4451ecba470
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/GoogleTest-CMakeLists.txt.in
@@ -0,0 +1,15 @@
+cmake_minimum_required(VERSION 2.8.2)
+
+project(googletest-download NONE)
+
+include(ExternalProject)
+ExternalProject_Add(googletest
+ GIT_REPOSITORY https://github.com/google/googletest.git
+ GIT_TAG "release-1.8.1"
+ SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-src"
+ BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-build"
+ CONFIGURE_COMMAND ""
+ BUILD_COMMAND ""
+ INSTALL_COMMAND ""
+ TEST_COMMAND ""
+) \ No newline at end of file
diff --git a/3rdparty/bimg/3rdparty/astc-codec/LICENSE b/3rdparty/bimg/3rdparty/astc-codec/LICENSE
new file mode 100644
index 00000000000..d6456956733
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/3rdparty/bimg/3rdparty/astc-codec/README.md b/3rdparty/bimg/3rdparty/astc-codec/README.md
new file mode 100644
index 00000000000..398de14bb66
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/README.md
@@ -0,0 +1,71 @@
+# astc-codec
+
+astc-codec is a software ASTC decoder implementation, which supports the ASTC
+LDR profile.
+
+Example usage:
+
+```
+#include <astc-codec/astc-codec.h>
+
+// ...
+
+std::vector<uint8_t> astc = LoadMyASTCData();
+const size_t width = 640;
+const size_t height = 480;
+
+std::vector<uint8_t> result;
+result.resize(width * height * 4);
+
+bool success = astc_codec::ASTCDecompressToRGBA(
+ astc.data(), astc.size(), width, height, astc_codec::FootprintType::k4x4,
+ result.data(), result.size(), /* stride */ width * 4);
+```
+
+## Building
+
+### With bazel
+
+Install [Bazel](https://bazel.build/), and then run:
+
+```
+bazel build :astc_codec -c opt
+```
+
+astc-codec has been tested on Mac and Linux.
+
+### Run Tests
+
+```
+bazel test //...
+```
+
+### With CMake
+
+Install [CMake](https://cmake.org/), and the run:
+
+```
+mkdir build && cd build && cmake .. && make
+```
+
+Or open the project in your favorite IDE and import CMakeLists.txt.
+
+### Run Tests
+
+In the build directory, execute:
+
+```
+ctest
+```
+
+
+## Contributing
+
+See [CONTRIBUTING.md](CONTRIBUTING.md) for important contributing requirements.
+
+## License
+
+astc-codec project is licensed under the Apache License Version 2.0. You can
+find a copy of it in [LICENSE](LICENSE).
+
+This is not an officially supported Google product.
diff --git a/3rdparty/bimg/3rdparty/astc-codec/WORKSPACE b/3rdparty/bimg/3rdparty/astc-codec/WORKSPACE
new file mode 100644
index 00000000000..83fd282a8c0
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/WORKSPACE
@@ -0,0 +1,37 @@
+# Copyright 2018 Google LLC
+#
+# 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
+#
+# https://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.
+
+load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+git_repository(
+ name = "gtest",
+ remote = "https://github.com/google/googletest.git",
+ commit = "ba96d0b1161f540656efdaed035b3c062b60e006",
+)
+
+http_archive(
+ name = "honggfuzz",
+ url = "https://github.com/google/honggfuzz/archive/1.7.zip",
+ sha256 = "9d420326979fed4a065fa6176d5e09bd513cd2820fe216ae8b684aa6780d72b2",
+ build_file = "//third_party:honggfuzz.BUILD",
+ strip_prefix = "honggfuzz-1.7",
+)
+
+http_archive(
+ name = "benchmark",
+ url = "https://github.com/google/benchmark/archive/v1.4.1.zip",
+ sha256 = "61ae07eb5d4a0b02753419eb17a82b7d322786bb36ab62bd3df331a4d47c00a7",
+ strip_prefix = "benchmark-1.4.1",
+)
diff --git a/3rdparty/bimg/3rdparty/astc-codec/cmake-format.json b/3rdparty/bimg/3rdparty/astc-codec/cmake-format.json
new file mode 100644
index 00000000000..de38ed285bc
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/cmake-format.json
@@ -0,0 +1,38 @@
+{
+ "line_width": 120,
+ "dangle_parens": false,
+ "first_comment_is_literal": true,
+ "algorithm_order": [
+ 0,
+ 1,
+ 2,
+ 3
+ ],
+ "command_case": "lower",
+ "additional_commands": {
+ "foo": {
+ "flags": [
+ "BAR",
+ "BAZ"
+ ],
+ "kwargs": {
+ "HEADERS": "*",
+ "DEPENDS": "*",
+ "SOURCES": "*"
+ }
+ }
+ },
+ "separate_fn_name_with_space": false,
+ "always_wrap": [],
+ "separate_ctrl_name_with_space": false,
+ "max_subargs_per_line": 5,
+ "fence_pattern": "^\\s*([`~]{3}[`~]*)(.*)$",
+ "enable_markup": true,
+ "ruler_pattern": "^\\s*[^\\w\\s]{3}.*[^\\w\\s]{3}$",
+ "tab_size": 2,
+ "keyword_case": "unchanged",
+ "enum_char": ".",
+ "literal_comment_pattern": null,
+ "bullet_char": "*",
+ "line_ending": "unix"
+}
diff --git a/3rdparty/bimg/3rdparty/astc-codec/include/astc-codec/astc-codec.h b/3rdparty/bimg/3rdparty/astc-codec/include/astc-codec/astc-codec.h
new file mode 100644
index 00000000000..1d412181365
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/include/astc-codec/astc-codec.h
@@ -0,0 +1,75 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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.
+
+#ifndef ASTC_CODEC_ASTC_CODEC_H_
+#define ASTC_CODEC_ASTC_CODEC_H_
+
+#include <cstddef>
+#include <cstdint>
+
+namespace astc_codec {
+
+// These are the valid ASTC footprints according to the specification in
+// Section C.2.7.
+enum class FootprintType {
+ k4x4,
+ k5x4,
+ k5x5,
+ k6x5,
+ k6x6,
+ k8x5,
+ k8x6,
+ k10x5,
+ k10x6,
+ k8x8,
+ k10x8,
+ k10x10,
+ k12x10,
+ k12x12,
+
+ kCount
+};
+
+// Decompresses ASTC LDR image data to a RGBA32 buffer.
+//
+// Supports formats defined in the KHR_texture_compression_astc_ldr spec and
+// returns UNORM8 values. sRGB is not supported, and should be implemented
+// by the caller.
+//
+// |astc_data| - Compressed ASTC image buffer, must be at least |astc_data_size|
+// bytes long.
+// |astc_data_size| - The size of |astc_data|, in bytes.
+// |width| - Image width, in pixels.
+// |height| - Image height, in pixels.
+// |footprint| - The ASTC footprint (block size) of the compressed image buffer.
+// |out_buffer| - Pointer to a buffer where the decompressed image will be
+// stored, must be at least |out_buffer_size| bytes long.
+// |out_buffer_size| - The size of |out_buffer|, in bytes, at least
+// height*out_buffer_stride. If this is too small, this
+// function will return false and no data will be
+// decompressed.
+// |out_buffer_stride| - The stride that should be used to store rows of the
+// decoded image, must be at least 4*width bytes.
+//
+// Returns true if the decompression succeeded, or false if decompression
+// failed, or if the astc_data_size was too small for the given width, height,
+// and footprint, or if out_buffer_size is too small.
+bool ASTCDecompressToRGBA(const uint8_t* astc_data, size_t astc_data_size,
+ size_t width, size_t height, FootprintType footprint,
+ uint8_t* out_buffer, size_t out_buffer_size,
+ size_t out_buffer_stride);
+
+} // namespace astc_codec
+
+#endif // ASTC_CODEC_ASTC_CODEC_H_
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/.clang-format b/3rdparty/bimg/3rdparty/astc-codec/src/.clang-format
new file mode 100644
index 00000000000..9a00ee252a5
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/.clang-format
@@ -0,0 +1,4 @@
+BasedOnStyle: Google
+AllowShortCaseLabelsOnASingleLine: true
+AllowShortFunctionsOnASingleLine: Inline
+SpaceAfterTemplateKeyword: false
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/base/BUILD.bazel b/3rdparty/bimg/3rdparty/astc-codec/src/base/BUILD.bazel
new file mode 100644
index 00000000000..84d213138c9
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/base/BUILD.bazel
@@ -0,0 +1,49 @@
+# Copyright 2018 Google LLC
+#
+# 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
+#
+# https://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.
+
+licenses(["notice"])
+
+cc_library(
+ name = "base",
+ hdrs = [
+ "bit_stream.h",
+ "bottom_n.h",
+ "math_utils.h",
+ "optional.h",
+ "string_utils.h",
+ "type_traits.h",
+ "uint128.h",
+ "utils.h",
+ ],
+ features = ["-parse_headers"],
+ visibility = ["//src/decoder:__pkg__"],
+)
+
+cc_test(
+ name = "base_test",
+ srcs = [
+ "test/bit_stream_test.cpp",
+ "test/bottom_n_test.cpp",
+ "test/math_utils_test.cpp",
+ "test/optional_test.cpp",
+ "test/string_utils_test.cpp",
+ "test/type_traits_test.cpp",
+ "test/uint128_test.cpp",
+ ],
+ deps = [
+ "@gtest//:gtest_main",
+ ":base",
+ ],
+)
+
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/base/CMakeLists.txt b/3rdparty/bimg/3rdparty/astc-codec/src/base/CMakeLists.txt
new file mode 100644
index 00000000000..5a45f857c44
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/base/CMakeLists.txt
@@ -0,0 +1,27 @@
+# Copyright 2018 Google LLC
+#
+# 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
+#
+# https://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.
+add_library(base INTERFACE)
+target_include_directories(base INTERFACE ../..)
+
+if(OPTION_ASTC_TESTS)
+ add_executable(base_test
+ test/bit_stream_test.cpp
+ test/bottom_n_test.cpp
+ test/math_utils_test.cpp
+ test/optional_test.cpp
+ test/string_utils_test.cpp
+ test/type_traits_test.cpp
+ test/uint128_test.cpp)
+ target_link_libraries(base_test base gmock_main)
+ add_test(NAME base_test COMMAND base_test WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
+endif()
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/base/bit_stream.h b/3rdparty/bimg/3rdparty/astc-codec/src/base/bit_stream.h
new file mode 100644
index 00000000000..2cda0937c8b
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/base/bit_stream.h
@@ -0,0 +1,77 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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.
+
+#ifndef ASTC_CODEC_BASE_BIT_STREAM_H_
+#define ASTC_CODEC_BASE_BIT_STREAM_H_
+
+#include <cassert>
+#include <cstdint>
+
+namespace astc_codec {
+namespace base {
+
+// Represents a stream of bits that can be read or written in arbitrary-sized
+// chunks.
+template<typename IntType = uint64_t>
+class BitStream {
+ public:
+ // Creates an empty BitStream.
+ BitStream() = default;
+ BitStream(IntType data, uint32_t data_size)
+ : data_(data), data_size_(data_size) {
+ assert(data_size_ <= sizeof(data_) * 8);
+ }
+
+ // Return the number of bits in the stream.
+ uint32_t Bits() const { return data_size_; }
+
+ // Put |size| bits into the stream.
+ // Fails if there is not enough space in the buffer to store the bits.
+ template<typename ResultType>
+ void PutBits(ResultType x, uint32_t size) {
+ assert(data_size_ + size <= sizeof(data_) * 8);
+
+ data_ |= (IntType(x) & MaskFor(size)) << data_size_;
+ data_size_ += size;
+ }
+
+ // Get |count| bits from the stream.
+ // Returns true if |count| bits were successfully retrieved.
+ template<typename ResultType>
+ bool GetBits(uint32_t count, ResultType* result) {
+ if (count <= data_size_) {
+ *result = static_cast<ResultType>(data_ & MaskFor(count));
+ data_ = data_ >> count;
+ data_size_ -= count;
+ return true;
+ } else {
+ *result = ResultType();
+ return false;
+ }
+ }
+
+ private:
+ IntType MaskFor(uint32_t bits) const {
+ return (bits == sizeof(IntType) * 8) ? ~IntType(0)
+ : (IntType(1) << bits) - 1;
+ }
+
+ IntType data_ = IntType();
+ uint32_t data_size_ = 0;
+};
+
+} // namespace base
+} // namespace astc_codec
+
+#endif // ASTC_CODEC_BASE_BIT_STREAM_H_
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/base/bottom_n.h b/3rdparty/bimg/3rdparty/astc-codec/src/base/bottom_n.h
new file mode 100644
index 00000000000..4edc8efb850
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/base/bottom_n.h
@@ -0,0 +1,78 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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.
+
+#ifndef ASTC_CODEC_BASE_BOTTOM_N_H_
+#define ASTC_CODEC_BASE_BOTTOM_N_H_
+
+#include <algorithm>
+#include <functional>
+#include <vector>
+
+namespace astc_codec {
+namespace base {
+
+// Used to aggregate the lowest N values of data supplied.
+template<typename T, typename CompareFn = std::less<T>>
+class BottomN {
+ public:
+ typedef std::vector<T> ContainerType;
+
+ // Creates an empty BottomN with limit |max_size|.
+ BottomN(size_t max_size) : max_size_(max_size) { }
+
+ bool Empty() const { return data_.empty(); }
+ size_t Size() const { return data_.size(); }
+
+ const T& Top() const { return data_.front(); }
+
+ void Push(const T& value) {
+ if (data_.size() < max_size_ || compare_(value, Top())) {
+ data_.push_back(value);
+ std::push_heap(data_.begin(), data_.end(), compare_);
+
+ if (Size() > max_size_) {
+ PopTop();
+ }
+ }
+ }
+
+ std::vector<T> Pop() {
+ const size_t len = Size();
+ std::vector<T> result(len);
+
+ for (size_t i = 0; i < len; ++i) {
+ result[len - i - 1] = PopTop();
+ }
+
+ return result;
+ }
+
+ private:
+ T PopTop() {
+ std::pop_heap(data_.begin(), data_.end(), compare_);
+ T result = data_.back();
+ data_.pop_back();
+ return result;
+ }
+
+ ContainerType data_;
+ CompareFn compare_;
+
+ const size_t max_size_;
+};
+
+} // namespace base
+} // namespace astc_codec
+
+#endif // ASTC_CODEC_BASE_BOTTOM_N_H_
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/base/math_utils.h b/3rdparty/bimg/3rdparty/astc-codec/src/base/math_utils.h
new file mode 100644
index 00000000000..48f1a24c2e5
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/base/math_utils.h
@@ -0,0 +1,80 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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.
+
+#ifndef ASTC_CODEC_BASE_MATH_UTILS_H_
+#define ASTC_CODEC_BASE_MATH_UTILS_H_
+
+#include "src/base/uint128.h"
+
+#include <cassert>
+#include <cstdint>
+#include <type_traits>
+
+namespace astc_codec {
+namespace base {
+
+inline int Log2Floor(uint32_t n) {
+ if (n == 0) {
+ return -1;
+ }
+
+ int log = 0;
+ uint32_t value = n;
+ for (int i = 4; i >= 0; --i) {
+ int shift = (1 << i);
+ uint32_t x = value >> shift;
+ if (x != 0) {
+ value = x;
+ log += shift;
+ }
+ }
+ assert(value == 1);
+ return log;
+}
+
+inline int CountOnes(uint32_t n) {
+ n -= ((n >> 1) & 0x55555555);
+ n = ((n >> 2) & 0x33333333) + (n & 0x33333333);
+ return static_cast<int>((((n + (n >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24);
+}
+
+template<typename T>
+inline T ReverseBits(T value) {
+ uint32_t s = sizeof(value) * 8;
+ T mask = ~T(0);
+ while ((s >>= 1) > 0) {
+ mask ^= (mask << s);
+ value = ((value >> s) & mask) | ((value << s) & ~mask);
+ }
+
+ return value;
+}
+
+template<typename T>
+inline T GetBits(T source, uint32_t offset, uint32_t count) {
+ static_assert(std::is_same<T, UInt128>::value || std::is_unsigned<T>::value,
+ "T must be unsigned.");
+
+ const uint32_t total_bits = sizeof(T) * 8;
+ assert(count > 0);
+ assert(offset + count <= total_bits);
+
+ const T mask = count == total_bits ? ~T(0) : ~T(0) >> (total_bits - count);
+ return (source >> offset) & mask;
+}
+
+} // namespace base
+} // namespace astc_codec
+
+#endif // ASTC_CODEC_BASE_MATH_UTILS_H_
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/base/optional.h b/3rdparty/bimg/3rdparty/astc-codec/src/base/optional.h
new file mode 100644
index 00000000000..5ede4af9ef8
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/base/optional.h
@@ -0,0 +1,520 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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.
+
+#ifndef ASTC_CODEC_BASE_OPTIONAL_H_
+#define ASTC_CODEC_BASE_OPTIONAL_H_
+
+#include "src/base/type_traits.h"
+
+#include <cassert>
+#include <initializer_list>
+#include <type_traits>
+#include <utility>
+
+#include <cstddef>
+
+// Optional<T> - a template class to store an optional value of type T.
+//
+// Usage examples:
+//
+// Initialization and construction:
+// Optional<Foo> foo; // |foo| doesn't contain a value.
+// Optional<Foo> foo(Foo(10)); // |foo| contains a copy-constructed value.
+// Optional<Foo> foo2(foo); // |foo2| contains a copy of |foo|'s value.
+// Optional<Foo> foo3(std::move(foo2)); // Guess what?
+//
+// Assignment:
+// Foo foo_value(0);
+// Optional<Foo> foo; // |foo| is empty.
+// Optional<Foo> foo2; // |foo2| is empty.
+// foo2 = foo; // |foo2| is still empty.
+// foo = foo_value; // set value of |foo| to a copy of |foo_value|
+// foo = std::move(foo_value); // move |foo_value| into |foo|.
+// foo2 = foo; // now |foo2| has a copy of |foo|'s value.
+// foo = kNullopt; // unset |foo|, it has no value.
+//
+// Checking and accessing value:
+// if (foo) {
+// // |foo| has a value.
+// doStuff(*foo); // |*foo| is the value inside |foo|.
+// foo->callMethod(); // Same as (*foo).callMethod().
+// } else {
+// // |foo| is empty.
+// }
+//
+// foo.value() // Same as *foo
+// foo.valueOr(<default>) // Return <default> is |foo| has no value.
+//
+// In-place construction:
+//
+// Optional<Foo> foo; // |foo| is empty.
+// foo.emplace(20); // |foo| now contains a value constructed as Foo(20)
+//
+// Optional<Foo> foo(kInplace, 20); // |foo| is initialized with a value
+// // that is constructed in-place as
+// // Foo(20).
+//
+// return makeOptional<Foo>(20); // Takes Foo constructor arguments
+// // directly.
+//
+// Returning values:
+//
+// Optional<Foo> myFunc(...) {
+// if (someCondition) {
+// return Foo(10); // call Optional<Foo>(Foo&) constructor.
+// } else {
+// return {}; // call Optional<Foo>() constructor, which
+// // builds an empty value.
+// }
+// }
+//
+// Memory layout:
+// Optional<Foo> is equivalent to:
+//
+// struct {
+// bool flag;
+// Foo value;
+// };
+//
+// in terms of memory layout. This means it *doubles* the size of integral
+// types. Also:
+//
+// - Optional<Foo> can be constructed from anything that constructs a Foo.
+//
+// - Same with Optional<Foo>(kInplace, Args...) where Args... matches any
+// arguments that can be passed to a Foo constructor.
+//
+// - Comparison operators are provided. Beware: an empty Optional<Foo>
+// is always smaller than any Foo value.
+
+namespace astc_codec {
+namespace base {
+
+namespace details {
+
+// Base classes to reduce the number of instantiations of the Optional's
+// internal members.
+class OptionalFlagBase {
+ public:
+ void setConstructed(bool constructed) { mConstructed = constructed; }
+ constexpr bool constructed() const { return mConstructed; }
+ constexpr operator bool() const { return constructed(); }
+ bool hasValue() const { return constructed(); }
+
+ constexpr OptionalFlagBase(bool constructed = false)
+ : mConstructed(constructed) { }
+
+ private:
+ bool mConstructed = false;
+};
+
+template<size_t Size, size_t Align>
+class OptionalStorageBase {
+ protected:
+ using StoreT = typename std::aligned_storage<Size, Align>::type;
+ StoreT mStorage = {};
+};
+
+} // namespace details
+
+// A tag type for empty optional construction
+struct NulloptT {
+ constexpr explicit NulloptT(int) { }
+};
+
+// A tag type for inplace value construction
+struct InplaceT {
+ constexpr explicit InplaceT(int) { }
+};
+
+// Tag values for null optional and inplace construction
+constexpr NulloptT kNullopt{1};
+constexpr InplaceT kInplace{1};
+
+// Forward declaration for an early use
+template<class T>
+class Optional;
+
+// A type trait for checking if a type is an optional instantiation
+// Note: if you want to refer to the template name inside the template,
+// you need to declare this alias outside of it - because the
+// class name inside of the template stands for an instantiated template
+// E.g, for template <T> class Foo if you say 'Foo' inside the class, it
+// actually means Foo<T>;
+template<class U>
+using is_any_optional =
+ is_template_instantiation_of<typename std::decay<U>::type, Optional>;
+
+template<class T>
+class Optional
+ : private details::OptionalFlagBase,
+ private details::OptionalStorageBase<sizeof(T),
+ std::alignment_of<T>::value> {
+ // make sure all optionals are buddies - this is needed to implement
+ // conversion from optionals of other types
+ template<class U>
+ friend class Optional;
+
+ template<class U>
+ using self = Optional<U>;
+
+ using base_flag = details::OptionalFlagBase;
+ using base_storage =
+ details::OptionalStorageBase<sizeof(T), std::alignment_of<T>::value>;
+
+ public:
+ // std::optional will have this, so let's provide it
+ using value_type = T;
+
+ // make sure we forbid some Optional instantiations where things may get
+ // really messy
+ static_assert(!std::is_same<typename std::decay<T>::type, NulloptT>::value,
+ "Optional of NulloptT is not allowed");
+ static_assert(!std::is_same<typename std::decay<T>::type, InplaceT>::value,
+ "Optional of InplaceT is not allowed");
+ static_assert(!std::is_reference<T>::value,
+ "Optional references are not allowed: use a pointer instead");
+
+ // constructors
+ constexpr Optional() { }
+ constexpr Optional(NulloptT) { }
+
+ Optional(const Optional& other) : base_flag(other.constructed()) {
+ if (this->constructed()) {
+ new (&get()) T(other.get());
+ }
+ }
+ Optional(Optional&& other) : base_flag(other.constructed()) {
+ if (this->constructed()) {
+ new (&get()) T(std::move(other.get()));
+ }
+ }
+
+ // Conversion constructor from optional of similar type
+ template<class U, class = enable_if_c<!is_any_optional<U>::value &&
+ std::is_constructible<T, U>::value>>
+ Optional(const Optional<U>& other) : base_flag(other.constructed()) {
+ if (this->constructed()) {
+ new (&get()) T(other.get());
+ }
+ }
+
+ // Move-conversion constructor
+ template<class U, class = enable_if_c<!is_any_optional<U>::value &&
+ std::is_constructible<T, U>::value>>
+ Optional(Optional<U>&& other) : base_flag(other.constructed()) {
+ if (this->constructed()) {
+ new (&get()) T(std::move(other.get()));
+ }
+ }
+
+ // Construction from a raw value
+ Optional(const T& value) : base_flag(true) { new (&get()) T(value); }
+ // Move construction from a raw value
+ Optional(T&& value) : base_flag(true) { new (&get()) T(std::move(value)); }
+
+ // Inplace construction from a list of |T|'s ctor arguments
+ template<class... Args>
+ Optional(InplaceT, Args&&... args) : base_flag(true) {
+ new (&get()) T(std::forward<Args>(args)...);
+ }
+
+ // Inplace construction from an initializer list passed into |T|'s ctor
+ template<class U, class = enable_if<
+ std::is_constructible<T, std::initializer_list<U>>>>
+ Optional(InplaceT, std::initializer_list<U> il) : base_flag(true) {
+ new (&get()) T(il);
+ }
+
+ // direct assignment
+ Optional& operator=(const Optional& other) {
+ if (&other == this) {
+ return *this;
+ }
+
+ if (this->constructed()) {
+ if (other.constructed()) {
+ get() = other.get();
+ } else {
+ destruct();
+ this->setConstructed(false);
+ }
+ } else {
+ if (other.constructed()) {
+ new (&get()) T(other.get());
+ this->setConstructed(true);
+ } else {
+ ; // we're good
+ }
+ }
+ return *this;
+ }
+
+ // move assignment
+ Optional& operator=(Optional&& other) {
+ if (this->constructed()) {
+ if (other.constructed()) {
+ get() = std::move(other.get());
+ } else {
+ destruct();
+ this->setConstructed(false);
+ }
+ } else {
+ if (other.constructed()) {
+ new (&get()) T(std::move(other.get()));
+ this->setConstructed(true);
+ } else {
+ ; // we're good
+ }
+ }
+ return *this;
+ }
+
+ // conversion assignment
+ template<class U,
+ class = enable_if_convertible<typename std::decay<U>::type, T>>
+ Optional& operator=(const Optional<U>& other) {
+ if (this->constructed()) {
+ if (other.constructed()) {
+ get() = other.get();
+ } else {
+ destruct();
+ this->setConstructed(false);
+ }
+ } else {
+ if (other.constructed()) {
+ new (&get()) T(other.get());
+ this->setConstructed(true);
+ } else {
+ ; // we're good
+ }
+ }
+ return *this;
+ }
+
+ // conversion move assignment
+ template<class U,
+ class = enable_if_convertible<typename std::decay<U>::type, T>>
+ Optional& operator=(Optional<U>&& other) {
+ if (this->constructed()) {
+ if (other.constructed()) {
+ get() = std::move(other.get());
+ } else {
+ destruct();
+ this->setConstructed(false);
+ }
+ } else {
+ if (other.constructed()) {
+ new (&get()) T(std::move(other.get()));
+ this->setConstructed(true);
+ } else {
+ ; // we're good
+ }
+ }
+ return *this;
+ }
+
+ // the most complicated one: forwarding constructor for anything convertible
+ // to |T|, excluding the stuff implemented above explicitly
+ template<class U,
+ class = enable_if_c<
+ !is_any_optional<typename std::decay<U>::type>::value &&
+ std::is_convertible<typename std::decay<U>::type, T>::value>>
+ Optional& operator=(U&& other) {
+ if (this->constructed()) {
+ get() = std::forward<U>(other);
+ } else {
+ new (&get()) T(std::forward<U>(other));
+ this->setConstructed(true);
+ }
+ return *this;
+ }
+
+ // Adopt value checkers from the parent
+ using base_flag::operator bool;
+ using base_flag::hasValue;
+
+ T& value() {
+ assert(this->constructed());
+ return get();
+ }
+ constexpr const T& value() const {
+ assert(this->constructed());
+ return get();
+ }
+
+ T* ptr() { return this->constructed() ? &get() : nullptr; }
+ constexpr const T* ptr() const {
+ return this->constructed() ? &get() : nullptr;
+ }
+
+ // Value getter with fallback
+ template<class U = T,
+ class = enable_if_convertible<typename std::decay<U>::type, T>>
+ constexpr T valueOr(U&& defaultValue) const {
+ return this->constructed() ? get() : std::move(defaultValue);
+ }
+
+ // Pointer-like operators
+ T& operator*() {
+ assert(this->constructed());
+ return get();
+ }
+ constexpr const T& operator*() const {
+ assert(this->constructed());
+ return get();
+ }
+
+ T* operator->() {
+ assert(this->constructed());
+ return &get();
+ }
+ constexpr const T* operator->() const {
+ assert(this->constructed());
+ return &get();
+ }
+
+ ~Optional() {
+ if (this->constructed()) {
+ destruct();
+ }
+ }
+
+ void clear() {
+ if (this->constructed()) {
+ destruct();
+ this->setConstructed(false);
+ }
+ }
+
+ template<class U,
+ class = enable_if_convertible<typename std::decay<U>::type, T>>
+ void reset(U&& u) {
+ *this = std::forward<U>(u);
+ }
+
+ // In-place construction with possible destruction of the old value
+ template<class... Args>
+ void emplace(Args&&... args) {
+ if (this->constructed()) {
+ destruct();
+ }
+ new (&get()) T(std::forward<Args>(args)...);
+ this->setConstructed(true);
+ }
+
+ // In-place construction with possible destruction of the old value
+ // initializer-list version
+ template<class U, class = enable_if<
+ std::is_constructible<T, std::initializer_list<U>>>>
+ void emplace(std::initializer_list<U> il) {
+ if (this->constructed()) {
+ destruct();
+ }
+ new (&get()) T(il);
+ this->setConstructed(true);
+ }
+
+ private:
+ // A helper function to convert the internal raw storage to T&
+ constexpr const T& get() const {
+ return *reinterpret_cast<const T*>(
+ reinterpret_cast<const char*>(&this->mStorage));
+ }
+
+ // Same thing, mutable
+ T& get() { return const_cast<T&>(const_cast<const Optional*>(this)->get()); }
+
+ // Shortcut for a destructor call for the stored object
+ void destruct() { get().T::~T(); }
+};
+
+template<class T>
+Optional<typename std::decay<T>::type> makeOptional(T&& t) {
+ return Optional<typename std::decay<T>::type>(std::forward<T>(t));
+}
+
+template<class T, class... Args>
+Optional<typename std::decay<T>::type> makeOptional(Args&&... args) {
+ return Optional<typename std::decay<T>::type>(kInplace,
+ std::forward<Args>(args)...);
+}
+
+template<class T>
+bool operator==(const Optional<T>& l, const Optional<T>& r) {
+ return l.hasValue() ? r.hasValue() && *l == *r : !r.hasValue();
+}
+template<class T>
+bool operator==(const Optional<T>& l, NulloptT) {
+ return !l;
+}
+template<class T>
+bool operator==(NulloptT, const Optional<T>& r) {
+ return !r;
+}
+template<class T>
+bool operator==(const Optional<T>& l, const T& r) {
+ return bool(l) && *l == r;
+}
+template<class T>
+bool operator==(const T& l, const Optional<T>& r) {
+ return bool(r) && l == *r;
+}
+
+template<class T>
+bool operator!=(const Optional<T>& l, const Optional<T>& r) {
+ return !(l == r);
+}
+template<class T>
+bool operator!=(const Optional<T>& l, NulloptT) {
+ return bool(l);
+}
+template<class T>
+bool operator!=(NulloptT, const Optional<T>& r) {
+ return bool(r);
+}
+template<class T>
+bool operator!=(const Optional<T>& l, const T& r) {
+ return !l || !(*l == r);
+}
+template<class T>
+bool operator!=(const T& l, const Optional<T>& r) {
+ return !r || !(l == *r);
+}
+
+template<class T>
+bool operator<(const Optional<T>& l, const Optional<T>& r) {
+ return !r ? false : (!l ? true : *l < *r);
+}
+template<class T>
+bool operator<(const Optional<T>&, NulloptT) {
+ return false;
+}
+template<class T>
+bool operator<(NulloptT, const Optional<T>& r) {
+ return bool(r);
+}
+template<class T>
+bool operator<(const Optional<T>& l, const T& r) {
+ return !l || *l < r;
+}
+template<class T>
+bool operator<(const T& l, const Optional<T>& r) {
+ return bool(r) && l < *r;
+}
+
+} // namespace base
+} // namespace astc_codec
+
+#endif // ASTC_CODEC_BASE_OPTIONAL_H_
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/base/string_utils.h b/3rdparty/bimg/3rdparty/astc-codec/src/base/string_utils.h
new file mode 100644
index 00000000000..20fd6b85739
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/base/string_utils.h
@@ -0,0 +1,69 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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.
+
+#ifndef ASTC_CODEC_BASE_STRING_UTILS_H_
+#define ASTC_CODEC_BASE_STRING_UTILS_H_
+
+#include <limits>
+#include <string>
+#include <stdlib.h>
+
+namespace astc_codec {
+namespace base {
+
+// Iterates over a string's parts using |splitBy| as a delimiter.
+// |splitBy| must be a nonempty string well, or it's a no-op.
+// Otherwise, |func| is called on each of the splits, excluding the
+// characters that are part of |splitBy|. If two |splitBy|'s occur in a row,
+// |func| will be called on a StringView("") in between. See
+// StringUtils_unittest.cpp for the full story.
+template<class Func>
+void Split(const std::string& str, const std::string& splitBy, Func func) {
+ if (splitBy.empty()) {
+ return;
+ }
+
+ size_t splitSize = splitBy.size();
+ size_t begin = 0;
+ size_t end = str.find(splitBy);
+
+ while (true) {
+ func(str.substr(begin, end - begin));
+ if (end == std::string::npos) {
+ return;
+ }
+
+ begin = end + splitSize;
+ end = str.find(splitBy, begin);
+ }
+}
+
+static int32_t ParseInt32(const char* str, int32_t deflt) {
+ using std::numeric_limits;
+
+ char* error = nullptr;
+ int64_t value = strtol(str, &error, 0);
+ // Limit long values to int32 min/max. Needed for lp64; no-op on 32 bits.
+ if (value > std::numeric_limits<int32_t>::max()) {
+ value = std::numeric_limits<int32_t>::max();
+ } else if (value < std::numeric_limits<int32_t>::min()) {
+ value = std::numeric_limits<int32_t>::min();
+ }
+ return (error == str) ? deflt : static_cast<int32_t>(value);
+}
+
+} // namespace base
+} // namespace astc_codec
+
+#endif // ASTC_CODEC_BASE_STRING_UTILS_H_
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/base/test/bit_stream_test.cpp b/3rdparty/bimg/3rdparty/astc-codec/src/base/test/bit_stream_test.cpp
new file mode 100644
index 00000000000..0c4b3c945f8
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/base/test/bit_stream_test.cpp
@@ -0,0 +1,141 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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 "src/base/bit_stream.h"
+
+#include <gtest/gtest.h>
+
+namespace astc_codec {
+namespace base {
+
+namespace {
+ static constexpr uint64_t kAllBits = 0xFFFFFFFFFFFFFFFF;
+ static constexpr uint64_t k40Bits = 0x000000FFFFFFFFFF;
+}
+
+TEST(BitStream, Decode) {
+ {
+ BitStream<uint64_t> stream(0, 1);
+
+ uint64_t bits = kAllBits;
+ EXPECT_TRUE(stream.GetBits(1, &bits));
+ EXPECT_EQ(bits, 0);
+ EXPECT_FALSE(stream.GetBits(1, &bits));
+ }
+
+ {
+ BitStream<uint64_t> stream(0b1010101010101010, 32);
+ EXPECT_EQ(stream.Bits(), 32);
+
+ uint64_t bits = 0;
+ EXPECT_TRUE(stream.GetBits(1, &bits));
+ EXPECT_EQ(bits, 0);
+
+ EXPECT_TRUE(stream.GetBits(3, &bits));
+ EXPECT_EQ(bits, 0b101);
+
+ EXPECT_TRUE(stream.GetBits(8, &bits));
+ EXPECT_EQ(bits, 0b10101010);
+
+ EXPECT_EQ(stream.Bits(), 20);
+
+ EXPECT_TRUE(stream.GetBits(20, &bits));
+ EXPECT_EQ(bits, 0b1010);
+ EXPECT_EQ(stream.Bits(), 0);
+ }
+
+ {
+ BitStream<uint64_t> stream(kAllBits, 64);
+ EXPECT_EQ(stream.Bits(), 64);
+
+ uint64_t bits = 0;
+ EXPECT_TRUE(stream.GetBits(64, &bits));
+ EXPECT_EQ(bits, kAllBits);
+ EXPECT_EQ(stream.Bits(), 0);
+ }
+
+ {
+ BitStream<uint64_t> stream(kAllBits, 64);
+ EXPECT_EQ(stream.Bits(), 64);
+
+ uint64_t bits = 0;
+ EXPECT_TRUE(stream.GetBits(40, &bits));
+ EXPECT_EQ(bits, k40Bits);
+ EXPECT_EQ(stream.Bits(), 24);
+ }
+
+ {
+ BitStream<uint64_t> stream(kAllBits, 32);
+
+ uint64_t bits = 0;
+ EXPECT_TRUE(stream.GetBits(0, &bits));
+ EXPECT_EQ(bits, 0);
+ EXPECT_TRUE(stream.GetBits(32, &bits));
+ EXPECT_EQ(bits, k40Bits & 0xFFFFFFFF);
+ EXPECT_TRUE(stream.GetBits(0, &bits));
+ EXPECT_EQ(bits, 0);
+ EXPECT_EQ(stream.Bits(), 0);
+ }
+}
+
+TEST(BitStream, Encode) {
+ {
+ BitStream<uint64_t> stream;
+
+ stream.PutBits(0, 1);
+ stream.PutBits(0b11, 2);
+ EXPECT_EQ(stream.Bits(), 3);
+
+ uint64_t bits = 0;
+ EXPECT_TRUE(stream.GetBits(3, &bits));
+ EXPECT_EQ(bits, 0b110);
+ }
+
+ {
+ BitStream<uint64_t> stream;
+
+ uint64_t bits = 0;
+ stream.PutBits(kAllBits, 64);
+ EXPECT_EQ(stream.Bits(), 64);
+
+ EXPECT_TRUE(stream.GetBits(64, &bits));
+ EXPECT_EQ(bits, kAllBits);
+ EXPECT_EQ(stream.Bits(), 0);
+ }
+
+ {
+ BitStream<uint64_t> stream;
+ stream.PutBits(kAllBits, 40);
+
+ uint64_t bits = 0;
+ EXPECT_TRUE(stream.GetBits(40, &bits));
+ EXPECT_EQ(bits, k40Bits);
+ EXPECT_EQ(stream.Bits(), 0);
+ }
+
+ {
+ BitStream<uint64_t> stream;
+ stream.PutBits(0, 0);
+ stream.PutBits(kAllBits, 32);
+ stream.PutBits(0, 0);
+
+ uint64_t bits = 0;
+ EXPECT_TRUE(stream.GetBits(32, &bits));
+ EXPECT_EQ(bits, k40Bits & 0xFFFFFFFF);
+ EXPECT_EQ(stream.Bits(), 0);
+ }
+}
+
+} // namespace base
+} // namespace astc_codec
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/base/test/bottom_n_test.cpp b/3rdparty/bimg/3rdparty/astc-codec/src/base/test/bottom_n_test.cpp
new file mode 100644
index 00000000000..71265d756bf
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/base/test/bottom_n_test.cpp
@@ -0,0 +1,108 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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 "src/base/bottom_n.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace astc_codec {
+namespace base {
+
+using ::testing::ElementsAre;
+
+template<typename T, size_t N>
+static void pushAll(BottomN<T>& heap, const T (&arr)[N]) {
+ for (auto i : arr) {
+ heap.Push(i);
+ }
+}
+
+TEST(BottomN, Sort) {
+ {
+ BottomN<int> heap(10);
+ EXPECT_TRUE(heap.Empty());
+ int list[] = { 1,2 };
+ pushAll(heap, list);
+
+ EXPECT_EQ(heap.Size(), 2);
+ EXPECT_FALSE(heap.Empty());
+ EXPECT_THAT(heap.Pop(), ElementsAre(1, 2));
+ }
+
+ {
+ BottomN<int> heap(6);
+ int list[] = {1, 4, 3, 2, 2, 1};
+ pushAll(heap, list);
+
+ EXPECT_EQ(heap.Size(), 6);
+ EXPECT_THAT(heap.Pop(), ElementsAre(1, 1, 2, 2, 3, 4));
+ }
+}
+
+TEST(BottomN, Bounds) {
+ {
+ BottomN<int> heap(4);
+ int list[] = { 1, 2, 3, 4 };
+ pushAll(heap, list);
+ EXPECT_EQ(heap.Size(), 4);
+
+ heap.Push(0);
+ EXPECT_EQ(heap.Size(), 4);
+
+ EXPECT_THAT(heap.Pop(), ElementsAre(0, 1, 2, 3));
+ }
+
+ {
+ BottomN<int> heap(4);
+ int list[] = { 4, 3, 2,1 };
+ pushAll(heap, list);
+ EXPECT_EQ(heap.Size(), 4);
+
+ int list2[] = { 4,4,4,4 };
+ pushAll(heap, list2);
+ EXPECT_EQ(heap.Size(), 4);
+
+ EXPECT_THAT(heap.Pop(), ElementsAre(1, 2, 3, 4));
+ }
+
+ {
+ BottomN<int> heap(4);
+ int list[] = { 4, 3, 2, 1 };
+ pushAll(heap, list);
+ EXPECT_EQ(heap.Size(), 4);
+
+ int list2[] = { 5, 5, 5, 5 };
+ pushAll(heap, list2);
+ EXPECT_EQ(heap.Size(), 4);
+
+ EXPECT_THAT(heap.Pop(), ElementsAre(1, 2, 3, 4));
+ }
+
+ {
+ BottomN<int> heap(4);
+ int list[] = { 4, 3, 2, 1 };
+ pushAll(heap, list);
+ EXPECT_EQ(heap.Size(), 4);
+
+ int list2[] = { 0, 0, 0, 0 };
+ pushAll(heap, list2);
+ EXPECT_EQ(heap.Size(), 4);
+
+ EXPECT_THAT(heap.Pop(), ElementsAre(0, 0, 0, 0));
+ }
+}
+
+} // namespace base
+} // namespace astc_codec
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/base/test/math_utils_test.cpp b/3rdparty/bimg/3rdparty/astc-codec/src/base/test/math_utils_test.cpp
new file mode 100644
index 00000000000..0371e1184d3
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/base/test/math_utils_test.cpp
@@ -0,0 +1,78 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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 "src/base/math_utils.h"
+
+#include <gtest/gtest.h>
+
+namespace astc_codec {
+namespace base {
+
+TEST(MathUtils, Log2Floor) {
+ EXPECT_EQ(-1, Log2Floor(0));
+
+ for (int i = 0; i < 32; i++) {
+ uint32_t n = 1U << i;
+ EXPECT_EQ(i, Log2Floor(n));
+ if (n > 2) {
+ EXPECT_EQ(i - 1, Log2Floor(n - 1));
+ EXPECT_EQ(i, Log2Floor(n + 1));
+ }
+ }
+}
+
+TEST(MathUtils, CountOnes) {
+ EXPECT_EQ(0, CountOnes(0));
+ EXPECT_EQ(1, CountOnes(1));
+ EXPECT_EQ(32, CountOnes(static_cast<uint32_t>(~0U)));
+ EXPECT_EQ(1, CountOnes(0x8000000));
+
+ for (int i = 0; i < 32; i++) {
+ EXPECT_EQ(1, CountOnes(1U << i));
+ EXPECT_EQ(31, CountOnes(static_cast<uint32_t>(~0U) ^ (1U << i)));
+ }
+}
+
+TEST(MathUtils, ReverseBits) {
+ EXPECT_EQ(ReverseBits(0u), 0u);
+ EXPECT_EQ(ReverseBits(1u), 1u << 31);
+ EXPECT_EQ(ReverseBits(0xffffffff), 0xffffffff);
+ EXPECT_EQ(ReverseBits(0x00000001), 0x80000000);
+ EXPECT_EQ(ReverseBits(0x80000000), 0x00000001);
+ EXPECT_EQ(ReverseBits(0xaaaaaaaa), 0x55555555);
+ EXPECT_EQ(ReverseBits(0x55555555), 0xaaaaaaaa);
+ EXPECT_EQ(ReverseBits(0x7d5d7f53), 0xcafebabe);
+ EXPECT_EQ(ReverseBits(0xcafebabe), 0x7d5d7f53);
+}
+
+TEST(MathUtils, GetBits) {
+ EXPECT_EQ(GetBits(0u, 0, 1), 0u);
+ EXPECT_EQ(GetBits(0u, 0, 32), 0u);
+ EXPECT_EQ(GetBits(0x00000001u, 0, 1), 0x00000001);
+ EXPECT_EQ(GetBits(0x00000001u, 0, 32), 0x00000001);
+ EXPECT_EQ(GetBits(0x00000001u, 1, 31), 0x00000000);
+ EXPECT_EQ(GetBits(0x00000001u, 31, 1), 0x00000000);
+
+ EXPECT_DEBUG_DEATH(GetBits(0x00000000u, 1, 32), "");
+ EXPECT_DEBUG_DEATH(GetBits(0x00000000u, 32, 0), "");
+ EXPECT_DEBUG_DEATH(GetBits(0x00000000u, 32, 1), "");
+
+ EXPECT_EQ(GetBits(0XFFFFFFFFu, 0, 4), 0x0000000F);
+ EXPECT_EQ(GetBits(0XFFFFFFFFu, 16, 16), 0xFFFF);
+ EXPECT_EQ(GetBits(0x80000000u, 31, 1), 1);
+ EXPECT_EQ(GetBits(0xCAFEBABEu, 24, 8), 0xCA);
+}
+
+} // namespace base
+} // namespace astc_codec
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/base/test/optional_test.cpp b/3rdparty/bimg/3rdparty/astc-codec/src/base/test/optional_test.cpp
new file mode 100644
index 00000000000..1eeefbdf48d
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/base/test/optional_test.cpp
@@ -0,0 +1,481 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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 "src/base/optional.h"
+
+#include <gtest/gtest.h>
+
+#include <memory>
+#include <vector>
+
+namespace astc_codec {
+namespace base {
+
+TEST(Optional, TypeProperties) {
+ // Making sure optional has the correct alignment and doesn't waste too much
+ // space
+
+ static_assert(sizeof(Optional<bool>) == 2, "bad Optional<bool> size");
+ static_assert(std::alignment_of<Optional<bool>>::value ==
+ std::alignment_of<bool>::value,
+ "bad Optional<bool> alignment");
+
+ static_assert(sizeof(Optional<char>) == 2, "bad Optional<char> size");
+ static_assert(std::alignment_of<Optional<char>>::value ==
+ std::alignment_of<char>::value,
+ "bad Optional<char> alignment");
+
+ static_assert(sizeof(Optional<int16_t>) == 4, "bad Optional<int16_t> size");
+ static_assert(std::alignment_of<Optional<int16_t>>::value ==
+ std::alignment_of<int16_t>::value,
+ "bad Optional<int16_t> alignment");
+
+ static_assert(sizeof(Optional<int32_t>) == 8, "bad Optional<int32_t> size");
+ static_assert(std::alignment_of<Optional<int32_t>>::value ==
+ std::alignment_of<int32_t>::value,
+ "bad Optional<int32_t> alignment");
+
+ static_assert(sizeof(Optional<int64_t>) == 16, "bad Optional<int64_t> size");
+ static_assert(std::alignment_of<Optional<int64_t>>::value ==
+ std::alignment_of<int64_t>::value,
+ "bad Optional<int64_t> alignment");
+
+ struct S128 {
+ int64_t data[2];
+ };
+
+ static_assert(sizeof(Optional<S128>) == 3 * sizeof(int64_t),
+ "bad Optional<S128> size");
+ static_assert(std::alignment_of<Optional<S128>>::value ==
+ std::alignment_of<S128>::value,
+ "bad Optional<S128> alignment");
+}
+
+TEST(Optional, ConstructFromValue) {
+ {
+ Optional<int> o;
+ EXPECT_FALSE(o);
+ }
+ {
+ Optional<int> o = {};
+ EXPECT_FALSE(o);
+ }
+ {
+ Optional<int> o = kNullopt;
+ EXPECT_FALSE(o);
+ }
+ {
+ Optional<int> o(1);
+ EXPECT_TRUE(o);
+ EXPECT_EQ(1, *o);
+ }
+ {
+ // check the std::decay<> constructor
+ Optional<int> o = static_cast<const short&>(1);
+ EXPECT_TRUE(o);
+ EXPECT_EQ(1, *o);
+ }
+ {
+ Optional<int> o = 1;
+ EXPECT_TRUE(o);
+ EXPECT_EQ(1, *o);
+ }
+ {
+ Optional<int> o{1};
+ EXPECT_TRUE(o);
+ EXPECT_EQ(1, *o);
+ }
+ {
+ short val = 10;
+ Optional<int> o = val;
+ EXPECT_TRUE(o);
+ EXPECT_EQ(10, *o);
+ }
+ {
+ Optional<std::vector<int>> o(kInplace, 10);
+ EXPECT_TRUE(o);
+ EXPECT_EQ((std::vector<int>(10)), *o);
+ }
+ {
+ Optional<std::vector<int>> o(kInplace, {1, 2, 3, 4});
+ EXPECT_TRUE(o);
+ EXPECT_EQ((std::vector<int>{1, 2, 3, 4}), *o);
+ }
+}
+
+TEST(Optional, ConstructFromOptional) {
+ {
+ Optional<int> o = Optional<int>();
+ EXPECT_FALSE(o);
+ }
+ {
+ Optional<short> o2;
+ Optional<int> o(o2);
+ EXPECT_FALSE(o);
+ }
+ {
+ Optional<short> o2 = 42;
+ Optional<int> o(o2);
+ EXPECT_TRUE(o);
+ EXPECT_EQ(42, *o);
+ }
+ {
+ Optional<int> o(Optional<int>(1));
+ EXPECT_TRUE(o);
+ EXPECT_EQ(1, *o);
+ }
+ {
+ Optional<int> o2 = 2;
+ Optional<int> o = o2;
+ EXPECT_TRUE(o);
+ EXPECT_EQ(2, *o);
+ }
+ {
+ Optional<std::vector<int>> o2 = std::vector<int>{20, 30, 40};
+ Optional<std::vector<int>> o = o2;
+ EXPECT_TRUE(o);
+ EXPECT_EQ((std::vector<int>{20, 30, 40}), *o);
+ }
+}
+
+TEST(Optional, Assign) {
+ {
+ Optional<int> o;
+ o = 1;
+ EXPECT_TRUE(o);
+ EXPECT_EQ(1, *o);
+
+ o = 2;
+ EXPECT_TRUE(o);
+ EXPECT_EQ(2, *o);
+
+ o = kNullopt;
+ EXPECT_FALSE(o);
+
+ o = Optional<int>(10);
+ EXPECT_TRUE(o);
+ EXPECT_EQ(10, *o);
+
+ Optional<int> o2;
+ o = o2;
+ EXPECT_FALSE(o);
+
+ o = 2u;
+ EXPECT_TRUE(o);
+ EXPECT_EQ(2, *o);
+
+ o = Optional<short>();
+ EXPECT_FALSE(o);
+
+ o = Optional<short>(20);
+ EXPECT_TRUE(o);
+ EXPECT_EQ(20, *o);
+
+ Optional<short> o3(200);
+ o = o3;
+ EXPECT_TRUE(o);
+ EXPECT_EQ(200, *o);
+
+ o = {};
+ EXPECT_FALSE(o);
+
+ // check the std::decay<> assignment
+ o = static_cast<const short&>(1);
+ EXPECT_TRUE(o);
+ EXPECT_EQ(1, *o);
+ }
+}
+
+TEST(Optional, MakeOptional) {
+ {
+ auto o = makeOptional(1);
+ static_assert(std::is_same<decltype(o), Optional<int>>::value,
+ "Bad type deduction in makeOptional()");
+ EXPECT_TRUE(o);
+ EXPECT_EQ(1, *o);
+ }
+ {
+ auto o = makeOptional(std::vector<char>{'1', '2'});
+ static_assert(std::is_same<decltype(o), Optional<std::vector<char>>>::value,
+ "Bad type deduction in makeOptional()");
+ EXPECT_TRUE(o);
+ EXPECT_EQ((std::vector<char>{'1', '2'}), *o);
+ }
+ {
+ // check std::decay<> in the factory function
+ auto o = makeOptional("String");
+ static_assert(std::is_same<decltype(o), Optional<const char*>>::value,
+ "Bad type deduction in makeOptional()");
+ EXPECT_TRUE(o);
+ EXPECT_STREQ("String", *o);
+ }
+ {
+ auto o = makeOptional<std::string>("String");
+ static_assert(std::is_same<decltype(o), Optional<std::string>>::value,
+ "Bad type deduction in makeOptional()");
+ EXPECT_TRUE(o);
+ EXPECT_STREQ("String", o->c_str());
+ }
+ {
+ auto o = makeOptional<std::string>(5, 'b');
+ static_assert(std::is_same<decltype(o), Optional<std::string>>::value,
+ "Bad type deduction in makeOptional()");
+ EXPECT_TRUE(o);
+ EXPECT_STREQ("bbbbb", o->c_str());
+ }
+ {
+ auto o = makeOptional<std::string>();
+ static_assert(std::is_same<decltype(o), Optional<std::string>>::value,
+ "Bad type deduction in makeOptional()");
+ EXPECT_TRUE(o);
+ EXPECT_STREQ("", o->c_str());
+ }
+}
+
+TEST(Optional, Move) {
+ auto o = makeOptional(std::unique_ptr<int>(new int(10)));
+ {
+ decltype(o) o2 = std::move(o);
+ EXPECT_TRUE(o);
+ EXPECT_TRUE(o2);
+ EXPECT_FALSE(bool(*o));
+ EXPECT_TRUE(bool(*o2));
+ EXPECT_EQ(10, **o2);
+
+ decltype(o) o3;
+ o3 = std::move(o2);
+ EXPECT_TRUE(o2);
+ EXPECT_TRUE(o3);
+ EXPECT_FALSE(bool(*o2));
+ EXPECT_TRUE(bool(*o3));
+ EXPECT_EQ(10, **o3);
+
+ o3 = std::move(o2);
+ EXPECT_TRUE(o2);
+ EXPECT_TRUE(o3);
+ EXPECT_FALSE(bool(*o2));
+ EXPECT_FALSE(bool(*o3));
+ }
+
+ {
+ decltype(o) o1;
+ decltype(o) o2 = std::move(o1);
+ EXPECT_FALSE(o1);
+ EXPECT_FALSE(o2);
+
+ o2 = std::move(o1);
+ EXPECT_FALSE(o1);
+ EXPECT_FALSE(o2);
+
+ decltype(o) o3{kInplace, new int(20)};
+ o3 = std::move(o1);
+ EXPECT_FALSE(o1);
+ EXPECT_FALSE(o3);
+ }
+}
+
+TEST(Optional, Value) {
+ auto o = makeOptional(1);
+ EXPECT_EQ(1, o.value());
+ EXPECT_EQ(1, o.valueOr(2));
+
+ o = kNullopt;
+ EXPECT_EQ(2, o.valueOr(2));
+}
+
+TEST(Optional, Clear) {
+ auto o = makeOptional(1);
+ o.clear();
+ EXPECT_FALSE(o);
+
+ o.clear();
+ EXPECT_FALSE(o);
+}
+
+TEST(Optional, Emplace) {
+ auto o = makeOptional(std::vector<int>{1, 2, 3, 4});
+ o.emplace(3, 1);
+ EXPECT_TRUE(o);
+ EXPECT_EQ((std::vector<int>{1, 1, 1}), *o);
+ EXPECT_EQ(3U, o->capacity());
+
+ o.clear();
+ o.emplace({1, 2});
+ EXPECT_TRUE(o);
+ EXPECT_EQ((std::vector<int>{1, 2}), *o);
+ EXPECT_EQ(2U, o->capacity());
+}
+
+TEST(Optional, Reset) {
+ auto o = makeOptional(std::vector<int>{1, 2, 3, 4});
+ o.reset(std::vector<int>{4, 3});
+ EXPECT_TRUE(o);
+ EXPECT_EQ((std::vector<int>{4, 3}), *o);
+ EXPECT_EQ(2U, o->capacity());
+
+ o.clear();
+ o.reset(std::vector<int>{1});
+ EXPECT_EQ((std::vector<int>{1}), *o);
+ EXPECT_EQ(1U, o->capacity());
+}
+
+TEST(Optional, CompareEqual) {
+ EXPECT_TRUE(makeOptional(1) == makeOptional(1));
+ EXPECT_TRUE(makeOptional(1) == 1);
+ EXPECT_TRUE(1 == makeOptional(1));
+ EXPECT_FALSE(makeOptional(1) == makeOptional(2));
+ EXPECT_FALSE(makeOptional(2) == 1);
+ EXPECT_FALSE(2 == makeOptional(1));
+ EXPECT_TRUE(makeOptional(1) != makeOptional(2));
+ EXPECT_TRUE(makeOptional(1) != 2);
+ EXPECT_TRUE(1 != makeOptional(2));
+
+ EXPECT_FALSE(makeOptional(1) == kNullopt);
+ EXPECT_FALSE(makeOptional(1) == Optional<int>());
+ EXPECT_FALSE(kNullopt == makeOptional(1));
+ EXPECT_FALSE(Optional<int>() == makeOptional(1));
+ EXPECT_TRUE(makeOptional(1) != kNullopt);
+ EXPECT_TRUE(makeOptional(1) != Optional<int>());
+ EXPECT_TRUE(kNullopt != makeOptional(1));
+ EXPECT_TRUE(Optional<int>() != makeOptional(1));
+
+ EXPECT_TRUE(kNullopt == Optional<int>());
+ EXPECT_TRUE(kNullopt == Optional<char*>());
+ EXPECT_FALSE(kNullopt != Optional<int>());
+ EXPECT_FALSE(kNullopt != Optional<char*>());
+ EXPECT_TRUE(Optional<int>() == Optional<int>());
+ EXPECT_FALSE(Optional<int>() != Optional<int>());
+}
+
+TEST(Optional, CompareLess) {
+ EXPECT_TRUE(makeOptional(1) < makeOptional(2));
+ EXPECT_TRUE(1 < makeOptional(2));
+ EXPECT_TRUE(makeOptional(1) < 2);
+
+ EXPECT_FALSE(makeOptional(1) < makeOptional(1));
+ EXPECT_FALSE(1 < makeOptional(1));
+ EXPECT_FALSE(makeOptional(1) < 1);
+ EXPECT_FALSE(makeOptional(2) < makeOptional(1));
+ EXPECT_FALSE(2 < makeOptional(1));
+ EXPECT_FALSE(makeOptional(2) < 1);
+
+ EXPECT_TRUE(kNullopt < makeOptional(2));
+ EXPECT_TRUE(Optional<int>() < makeOptional(2));
+ EXPECT_TRUE(Optional<int>() < 2);
+ EXPECT_FALSE(makeOptional(2) < kNullopt);
+ EXPECT_FALSE(makeOptional(2) < Optional<int>());
+ EXPECT_FALSE(2 < Optional<int>());
+
+ EXPECT_FALSE(kNullopt < Optional<int>());
+ EXPECT_FALSE(Optional<int>() < kNullopt);
+}
+
+TEST(Optional, Destruction) {
+ // create a reference counting class to check if we delete everything
+ // we've created
+ struct Track {
+ Track(int& val) : mVal(val) { ++mVal.get(); }
+ Track(std::initializer_list<int*> vals) : mVal(**vals.begin()) {
+ ++mVal.get();
+ }
+ Track(const Track& other) : mVal(other.mVal) { ++mVal.get(); }
+ Track(Track&& other) : mVal(other.mVal) { ++mVal.get(); }
+ Track& operator=(const Track& other) {
+ --mVal.get();
+ mVal = other.mVal;
+ ++mVal.get();
+ return *this;
+ }
+ Track& operator=(Track&& other) {
+ --mVal.get();
+ mVal = other.mVal;
+ ++mVal.get();
+ return *this;
+ }
+
+ ~Track() { --mVal.get(); }
+
+ std::reference_wrapper<int> mVal;
+ };
+
+ int counter = 0;
+ {
+ auto o = makeOptional(Track(counter));
+ EXPECT_EQ(1, counter);
+ }
+ EXPECT_EQ(0, counter);
+
+ {
+ auto o = makeOptional(Track(counter));
+ EXPECT_EQ(1, counter);
+ o.clear();
+ EXPECT_EQ(0, counter);
+ }
+ EXPECT_EQ(0, counter);
+
+ {
+ auto o = makeOptional(Track(counter));
+ EXPECT_EQ(1, counter);
+ int counter2 = 0;
+ o.emplace(counter2);
+ EXPECT_EQ(0, counter);
+ EXPECT_EQ(1, counter2);
+ o = Track(counter);
+ EXPECT_EQ(1, counter);
+ EXPECT_EQ(0, counter2);
+
+ auto o2 = o;
+ EXPECT_EQ(2, counter);
+ EXPECT_EQ(0, counter2);
+ }
+ EXPECT_EQ(0, counter);
+
+ {
+ auto o = makeOptional(Track(counter));
+ auto o2 = std::move(o);
+ EXPECT_EQ(2, counter);
+ o = o2;
+ EXPECT_EQ(2, counter);
+ }
+ EXPECT_EQ(0, counter);
+
+ int counter2 = 0;
+ {
+ Optional<Track> o;
+ o.emplace(counter);
+ EXPECT_EQ(1, counter);
+
+ o.emplace(counter2);
+ EXPECT_EQ(0, counter);
+ EXPECT_EQ(1, counter2);
+ }
+ EXPECT_EQ(0, counter);
+ EXPECT_EQ(0, counter2);
+
+ {
+ Optional<Track> o;
+ o.emplace({&counter});
+ EXPECT_EQ(1, counter);
+
+ counter2 = 0;
+ o.emplace({&counter2});
+ EXPECT_EQ(0, counter);
+ EXPECT_EQ(1, counter2);
+ }
+ EXPECT_EQ(0, counter);
+ EXPECT_EQ(0, counter2);
+}
+
+} // namespace base
+} // namespace astc_codec
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/base/test/string_utils_test.cpp b/3rdparty/bimg/3rdparty/astc-codec/src/base/test/string_utils_test.cpp
new file mode 100644
index 00000000000..209da54d863
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/base/test/string_utils_test.cpp
@@ -0,0 +1,110 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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 "src/base/string_utils.h"
+
+#include <gtest/gtest.h>
+
+#include <list>
+#include <string>
+#include <vector>
+
+namespace astc_codec {
+namespace base {
+
+TEST(StringUtils, Split) {
+ std::vector<std::string> results;
+
+ auto testFunc = [&results](std::string&& s) {
+ results.push_back(std::move(s));
+ };
+
+ Split("", "abc", testFunc);
+ EXPECT_EQ(results.size(), 1);
+
+ Split("abc", "", testFunc);
+ EXPECT_EQ(results.size(), 1);
+
+ results.clear();
+ Split("abc", "a", testFunc);
+ EXPECT_EQ(results.size(), 2);
+ EXPECT_EQ(results[0], "");
+ EXPECT_EQ(results[1], "bc");
+
+ results.clear();
+ Split("aaa", "a", testFunc);
+ EXPECT_EQ(4, results.size());
+ EXPECT_EQ("", results[0]);
+ EXPECT_EQ("", results[1]);
+ EXPECT_EQ("", results[2]);
+ EXPECT_EQ("", results[3]);
+
+ results.clear();
+ Split("1a2a3a4", "a", testFunc);
+ EXPECT_EQ(4, results.size());
+ EXPECT_EQ("1", results[0]);
+ EXPECT_EQ("2", results[1]);
+ EXPECT_EQ("3", results[2]);
+ EXPECT_EQ("4", results[3]);
+
+ results.clear();
+ Split("1a2aa3a4", "a", testFunc);
+ EXPECT_EQ(5, results.size());
+ EXPECT_EQ("1", results[0]);
+ EXPECT_EQ("2", results[1]);
+ EXPECT_EQ("", results[2]);
+ EXPECT_EQ("3", results[3]);
+ EXPECT_EQ("4", results[4]);
+
+ results.clear();
+ Split("The quick brown fox jumped over the lazy dog",
+ " ", testFunc);
+ EXPECT_EQ(9, results.size());
+ EXPECT_EQ("The", results[0]);
+ EXPECT_EQ("quick", results[1]);
+ EXPECT_EQ("brown", results[2]);
+ EXPECT_EQ("fox", results[3]);
+ EXPECT_EQ("jumped", results[4]);
+ EXPECT_EQ("over", results[5]);
+ EXPECT_EQ("the", results[6]);
+ EXPECT_EQ("lazy", results[7]);
+ EXPECT_EQ("dog", results[8]);
+
+ results.clear();
+ Split("a; b; c; d", "; ", testFunc);
+ EXPECT_EQ(4, results.size());
+ EXPECT_EQ("a", results[0]);
+ EXPECT_EQ("b", results[1]);
+ EXPECT_EQ("c", results[2]);
+ EXPECT_EQ("d", results[3]);
+}
+
+TEST(StringUtils, ParseInt32) {
+ EXPECT_EQ(ParseInt32("0", -1), 0);
+ EXPECT_EQ(ParseInt32("100", -1), 100);
+ EXPECT_EQ(ParseInt32("-100", -1), -100);
+
+ EXPECT_EQ(ParseInt32("", -1), -1);
+ EXPECT_EQ(ParseInt32("a", -1), -1);
+ EXPECT_EQ(ParseInt32("10x1", -1), 10);
+
+ EXPECT_EQ(ParseInt32("2147483647", -1), 2147483647);
+ EXPECT_EQ(ParseInt32("2147483648", -1), 2147483647);
+
+ EXPECT_EQ(ParseInt32("-2147483648", -1), -2147483648);
+ EXPECT_EQ(ParseInt32("-2147483649", -1), -2147483648);
+}
+
+} // namespace base
+} // namespace astc_codec
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/base/test/type_traits_test.cpp b/3rdparty/bimg/3rdparty/astc-codec/src/base/test/type_traits_test.cpp
new file mode 100644
index 00000000000..c724cbe7d73
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/base/test/type_traits_test.cpp
@@ -0,0 +1,130 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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 "src/base/type_traits.h"
+
+#include <gtest/gtest.h>
+
+#include <array>
+#include <functional>
+#include <list>
+#include <vector>
+
+namespace astc_codec {
+namespace base {
+
+TEST(TypeTraits, IsCallable) {
+ class C;
+ C* c = nullptr;
+
+ auto lambda = [c](bool) -> C* { return nullptr; };
+
+ static_assert(is_callable_as<void(), void()>::value, "simple function");
+ static_assert(is_callable_as<void (&)(), void()>::value,
+ "function reference");
+ static_assert(is_callable_as<void (*)(), void()>::value, "function pointer");
+ static_assert(is_callable_as<int(C&, C*), int(C&, C*)>::value,
+ "function with arguments and return type");
+ static_assert(is_callable_as<decltype(lambda), C*(bool)>::value, "lambda");
+ static_assert(is_callable_as<std::function<bool(int)>, bool(int)>::value,
+ "std::function");
+
+ static_assert(!is_callable_as<int, void()>::value,
+ "int should not be callable");
+ static_assert(!is_callable_as<C, void()>::value, "incomplete type");
+ static_assert(!is_callable_as<void(), void(int)>::value,
+ "different arguments");
+ static_assert(!is_callable_as<int(), void()>::value,
+ "different return types");
+ static_assert(!is_callable_as<int(), short()>::value,
+ "slightly different return types");
+ static_assert(!is_callable_as<int(int), int(int, int)>::value,
+ "more arguments");
+ static_assert(!is_callable_as<int(int, int), int(int)>::value,
+ "less arguments");
+
+ static_assert(!is_callable_as<int(int), int>::value,
+ "bad required signature");
+
+ static_assert(is_callable_with_args<void(), void()>::value,
+ "simple function");
+ static_assert(is_callable_with_args<void (&)(), void()>::value,
+ "function reference");
+ static_assert(is_callable_with_args<void (*)(), void()>::value,
+ "function pointer");
+ static_assert(is_callable_with_args<int(C&, C*), int(C&, C*)>::value,
+ "function with arguments and return type");
+ static_assert(is_callable_with_args<decltype(lambda), C*(bool)>::value,
+ "lambda");
+ static_assert(
+ is_callable_with_args<std::function<bool(int)>, bool(int)>::value,
+ "std::function");
+
+ static_assert(!is_callable_with_args<int, void()>::value,
+ "int should not be callable");
+ static_assert(!is_callable_with_args<C, void()>::value, "incomplete type");
+ static_assert(!is_callable_with_args<void(), void(int)>::value,
+ "different arguments");
+ static_assert(is_callable_with_args<int(), void()>::value,
+ "different return types are ignored");
+ static_assert(is_callable_with_args<int(), short()>::value,
+ "slightly different return types are ignored");
+ static_assert(!is_callable_with_args<int(int), int(int, int)>::value,
+ "more arguments");
+ static_assert(!is_callable_with_args<int(int, int), int(int)>::value,
+ "less arguments");
+
+ static_assert(!is_callable_with_args<int(int), int>::value,
+ "bad required signature");
+}
+
+TEST(TypeTraits, IsTemplateInstantiation) {
+ static_assert(!is_template_instantiation_of<int, std::vector>::value,
+ "int is not an instance of vector");
+ static_assert(!is_template_instantiation_of<std::list<std::vector<int>>,
+ std::vector>::value,
+ "list is not an instance of vector");
+
+ static_assert(
+ is_template_instantiation_of<std::vector<int>, std::vector>::value,
+ "std::vector<int> is an instance of vector");
+ static_assert(
+ is_template_instantiation_of<std::vector<std::vector<std::vector<int>>>,
+ std::vector>::value,
+ "nested std::vector<> is an instance of vector");
+}
+
+#ifndef _MSC_VER
+TEST(TypeTraits, IsRange) {
+ static_assert(is_range<std::vector<int>>::value,
+ "vector<> should be detected as a range");
+ static_assert(is_range<const std::list<std::function<void()>>>::value,
+ "const list<> should be detected as a range");
+ static_assert(is_range<std::array<std::vector<int>, 10>>::value,
+ "array<> should be detected as a range");
+ char arr[100];
+ static_assert(is_range<decltype(arr)>::value,
+ "C array should be detected as a range");
+ static_assert(is_range<decltype("string")>::value,
+ "String literal should be detected as a range");
+
+ static_assert(!is_range<int>::value, "int shouldn't be a range");
+ static_assert(!is_range<int*>::value, "int* shouldn't be a range");
+ static_assert(!is_range<const int*>::value,
+ "even const int* shouldn't be a range");
+}
+#endif
+
+} // namespace base
+} // namespace astc_codec
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/base/test/uint128_test.cpp b/3rdparty/bimg/3rdparty/astc-codec/src/base/test/uint128_test.cpp
new file mode 100644
index 00000000000..0a52244c851
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/base/test/uint128_test.cpp
@@ -0,0 +1,140 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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 "src/base/uint128.h"
+
+#include <gtest/gtest.h>
+
+namespace astc_codec {
+namespace base {
+
+TEST(UInt128, Equality) {
+ const UInt128 zero(0);
+ const UInt128 max64(~0ULL);
+
+ EXPECT_EQ(zero, zero);
+ EXPECT_NE(zero, max64);
+ EXPECT_EQ(zero, UInt128(0));
+ EXPECT_NE(zero, UInt128(1));
+ EXPECT_EQ(max64, max64);
+}
+
+TEST(UInt128, Shifting) {
+ const UInt128 max64(~0ULL);
+ const UInt128 upper64(~0ULL, 0);
+ EXPECT_EQ(upper64.HighBits(), ~0ULL);
+ EXPECT_EQ(upper64.LowBits(), 0);
+
+ EXPECT_EQ(upper64 >> 64, max64);
+
+ EXPECT_EQ(UInt128(1) << 1, UInt128(2));
+ EXPECT_EQ(UInt128(0) << 0, UInt128(0));
+ EXPECT_EQ(max64 << 0, max64);
+ EXPECT_EQ(max64 >> 0, max64);
+ EXPECT_EQ(upper64 << 0, upper64);
+ EXPECT_EQ(upper64 >> 0, upper64);
+
+ {
+ const UInt128 bit63 = UInt128(1ULL << 62) << 1;
+ EXPECT_EQ(bit63.LowBits(), 1ULL << 63);
+ EXPECT_EQ(bit63.HighBits(), 0);
+ }
+
+ {
+ const UInt128 bit64 = UInt128(1ULL << 63) << 1;
+ EXPECT_EQ(bit64.LowBits(), 0);
+ EXPECT_EQ(bit64.HighBits(), 1);
+ EXPECT_EQ(bit64 >> 1, UInt128(1ULL << 63));
+ }
+
+ {
+ const UInt128 overshift = max64 << 128;
+ EXPECT_EQ(overshift.HighBits(), 0);
+ EXPECT_EQ(overshift.LowBits(), 0);
+ }
+
+ {
+ const UInt128 overlap = upper64 >> 32;
+ EXPECT_EQ(overlap.HighBits(), 0x00000000FFFFFFFF);
+ EXPECT_EQ(overlap.LowBits(), 0xFFFFFFFF00000000);
+ }
+
+ {
+ const UInt128 overlap = max64 << 32;
+ EXPECT_EQ(overlap.HighBits(), 0x00000000FFFFFFFF);
+ EXPECT_EQ(overlap.LowBits(), 0xFFFFFFFF00000000);
+ }
+}
+
+TEST(UInt128, LargeShift) {
+ const UInt128 base(0xFF);
+ EXPECT_EQ(base << 64, UInt128(0xFFULL, 0));
+ EXPECT_EQ(base << 72, UInt128(0xFF00ULL, 0));
+ EXPECT_EQ(base << 80, UInt128(0xFF0000ULL, 0));
+ EXPECT_EQ(base << 88, UInt128(0xFF000000ULL, 0));
+ EXPECT_EQ(base << 96, UInt128(0xFF00000000ULL, 0));
+ EXPECT_EQ(base << 104, UInt128(0xFF0000000000ULL, 0));
+ EXPECT_EQ(base << 112, UInt128(0xFF000000000000ULL, 0));
+ EXPECT_EQ(base << 120, UInt128(0xFF00000000000000ULL, 0));
+
+ const UInt128 upper(0xFF00000000000000ULL, 0);
+ EXPECT_EQ(upper >> 64, UInt128(0, 0xFF00000000000000ULL));
+ EXPECT_EQ(upper >> 72, UInt128(0, 0xFF000000000000ULL));
+ EXPECT_EQ(upper >> 80, UInt128(0, 0xFF0000000000ULL));
+ EXPECT_EQ(upper >> 88, UInt128(0, 0xFF00000000ULL));
+ EXPECT_EQ(upper >> 96, UInt128(0, 0xFF000000ULL));
+ EXPECT_EQ(upper >> 104, UInt128(0, 0xFF0000ULL));
+ EXPECT_EQ(upper >> 112, UInt128(0, 0xFF00ULL));
+ EXPECT_EQ(upper >> 120, UInt128(0, 0xFFULL));
+}
+
+TEST(UInt128, BooleanOperators) {
+ const UInt128 allOnes(~0ULL, ~0ULL);
+ EXPECT_EQ(allOnes.HighBits(), ~0ULL);
+ EXPECT_EQ(allOnes.LowBits(), ~0ULL);
+
+ EXPECT_EQ(~allOnes, UInt128(0));
+ EXPECT_EQ(~UInt128(0), allOnes);
+
+ EXPECT_EQ(UInt128(0xFFFF00) & UInt128(0x00FFFF), UInt128(0x00FF00));
+ EXPECT_EQ(UInt128(0xFFFF00) | UInt128(0x00FFFF), UInt128(0xFFFFFF));
+ EXPECT_EQ(UInt128(0xFFFF00) ^ UInt128(0x00FFFF), UInt128(0xFF00FF));
+}
+
+TEST(UInt128, Addition) {
+ const UInt128 bit63(1ULL << 63);
+
+ EXPECT_EQ(UInt128(1) + 1, UInt128(2));
+ EXPECT_EQ(bit63 + bit63, UInt128(1) << 64);
+
+ const UInt128 carryUp = UInt128(~0ULL) + 1;
+ EXPECT_EQ(carryUp.HighBits(), 1);
+ EXPECT_EQ(carryUp.LowBits(), 0);
+
+ const UInt128 allOnes(~0ULL, ~0ULL);
+ EXPECT_EQ(allOnes + 1, UInt128(0));
+}
+
+TEST(UInt128, Subtraction) {
+ const UInt128 bit64 = UInt128(1) << 64;
+ EXPECT_EQ(bit64 - 1, UInt128(~0ULL));
+
+ EXPECT_EQ(UInt128(1) - 1, UInt128(0));
+
+ const UInt128 allOnes(~0ULL, ~0ULL);
+ EXPECT_EQ(UInt128(0) - 1, allOnes);
+}
+
+} // namespace base
+} // namespace astc_codec
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/base/type_traits.h b/3rdparty/bimg/3rdparty/astc-codec/src/base/type_traits.h
new file mode 100644
index 00000000000..917125d26f1
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/base/type_traits.h
@@ -0,0 +1,172 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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.
+
+#ifndef ASTC_CODEC_BASE_TYPE_TRAITS_H_
+#define ASTC_CODEC_BASE_TYPE_TRAITS_H_
+
+#include <iterator>
+#include <type_traits>
+
+namespace astc_codec {
+namespace base {
+
+namespace details {
+
+// a simple helper class for SFINAE below.
+template<class X = void>
+struct dummy {
+ using type = X;
+};
+
+} // namespace details
+
+// add some convenience shortcuts for an overly complex std::enable_if syntax
+
+// Use 'enable_if<Predicate,Type>' instead of
+// 'typename std::enable_if<Predicate::value,Type>::type'
+template<class Predicate, class Type = void*>
+using enable_if = typename std::enable_if<Predicate::value, Type>::type;
+
+// Use 'enable_if_c<BooleanFlag,Type>' instead of
+// 'typename std::enable_if<BooleanFlag,Type>::type'
+template<bool predicate, class Type = void*>
+using enable_if_c = typename std::enable_if<predicate, Type>::type;
+
+// Use 'enable_if_convertible<From,To,Type>' instead of
+// 'typename std::enable_if<std::is_convertible<From,To>::value, Type>::type'
+template<class From, class To, class Type = void*>
+using enable_if_convertible = enable_if<std::is_convertible<From, To>>;
+
+// -----------------------------------------------------------------------------
+// A predicate for checking if some object is callable with a specific
+// signature. Examples:
+//
+// is_callable_as<int, void()>::value == false.
+// is_callable_as<strcmp, void()>::value == false.
+// is_callable_as<strcmp, int(const char*, const char*)>::value == true
+//
+template<class F, class Signature, class X = void>
+struct is_callable_as : std::false_type {};
+
+// This specialization is SFINAE-d out if template arguments can't be combined
+// into a call expression F(), or if the result of that call is not |R|
+template<class F, class R, class... Args>
+struct is_callable_as<F, R(Args...),
+ typename std::enable_if<std::is_same<
+ typename details::dummy<decltype(std::declval<F>()(
+ std::declval<Args>()...))>::type,
+ R>::value>::type> : std::true_type {};
+
+//
+// A similar predicate to only check arguments of the function call and ignore
+// the specified return type
+//
+// is_callable_as<strcmp, int(const char*, const char*)>::value == true
+// is_callable_as<strcmp, void(const char*, const char*)>::value == false
+// is_callable_with_args<strcmp, void(const char*, const char*)>::value == true
+//
+template<class F, class Signature, class X = void>
+struct is_callable_with_args : std::false_type {};
+
+template<class F, class R, class... Args>
+struct is_callable_with_args<
+ F, R(Args...),
+ typename std::enable_if<
+ !std::is_same<typename details::dummy<decltype(
+ std::declval<F>()(std::declval<Args>()...))>::type,
+ F>::value>::type> : std::true_type {};
+
+// -----------------------------------------------------------------------------
+// Check if a type |T| is any instantiation of a template |U|. Examples:
+//
+// is_template_instantiation_of<int, std::vector>::value == false
+// is_template_instantiation_of<
+// std::list<std::vector<int>>, std::vector>::value == false
+// is_template_instantiation_of<std::vector<int>, std::vector>::value == true
+// is_template_instantiation_of<
+// std::vector<std::vector<int>>, std::vector>::value == true
+//
+template<class T, template<class...> class U>
+struct is_template_instantiation_of : std::false_type {};
+
+template<template<class...> class U, class... Args>
+struct is_template_instantiation_of<U<Args...>, U> : std::true_type {};
+// -----------------------------------------------------------------------------
+
+//
+// is_range<T> - check if type |T| is a range-like type.
+//
+// It makes sure that expressions std::begin(t) and std::end(t) are well-formed
+// and those return the same type.
+//
+// Note: with expression SFINAE from C++14 is_range_helper<> could be renamed to
+// is_range<> with no extra code. C++11 needs an extra level of enable_if<>
+// to make it work when the type isn't a range.
+//
+
+namespace details {
+
+template<class T>
+using is_range_helper = std::is_same<
+ decltype(std::begin(
+ std::declval<typename std::add_lvalue_reference<T>::type>())),
+ decltype(
+ std::end(std::declval<typename std::add_lvalue_reference<T>::type>()))>;
+
+} // namespace details
+
+template<class T, class = void>
+struct is_range : std::false_type {};
+
+template<class T>
+struct is_range<
+ T, typename std::enable_if<details::is_range_helper<T>::value>::type>
+ : std::true_type {};
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// A class to incapsulate integer sequence 0, 1, ..., <num_args>
+// Seq<int...>
+// Useful to pass function parameters in an array/tuple to call it later.
+//
+
+template<int...>
+struct Seq {};
+
+// A 'maker' class to construct Seq<int...> given only <num_args>
+// value.
+// MakeSeq<N, S...> works this way, e.g.
+//
+// MakeSeq<2> inherits MakeSeq<2 - 1, 2 - 1> == MakeSeq<1, 1>
+// MakeSeq<1, 1> : MakeSeq<1 - 1, 1 - 1, 1> == MakeSeq<0, 0, 1>
+// MakeSeq<0, 0, 1> == MakeSeq<0, S...> and defines |type| = Seq<0, 1>
+
+template<int N, int... S>
+struct MakeSeq : MakeSeq<N - 1, N - 1, S...> {};
+
+template<int... S>
+struct MakeSeq<0, S...> {
+ using type = Seq<S...>;
+};
+
+//
+// MakeSeqT alias to quickly create Seq<...>:
+// MakeSeqT<3> == Seq<0, 1, 2>
+template<int... S>
+using MakeSeqT = typename MakeSeq<S...>::type;
+
+} // namespace base
+} // namespace astc_codec
+
+#endif // ASTC_CODEC_BASE_TYPE_TRAITS_H_
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/base/uint128.h b/3rdparty/bimg/3rdparty/astc-codec/src/base/uint128.h
new file mode 100644
index 00000000000..481e4eabbd9
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/base/uint128.h
@@ -0,0 +1,175 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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.
+
+#ifndef ASTC_CODEC_BASE_UINT128_H_
+#define ASTC_CODEC_BASE_UINT128_H_
+
+#include <cassert>
+#include <cstdint>
+
+namespace astc_codec {
+namespace base {
+
+class UInt128 {
+ public:
+ UInt128() = default;
+ UInt128(uint64_t low) : low_(low) { }
+ UInt128(uint64_t high, uint64_t low) : low_(low), high_(high) { }
+ UInt128(const UInt128& other) : low_(other.low_), high_(other.high_) { }
+
+ uint64_t LowBits() const { return low_; }
+ uint64_t HighBits() const { return high_; }
+
+ // Allow explicit casts to uint64_t.
+ explicit operator uint64_t() const { return low_; }
+
+ // Copy operators.
+ UInt128& operator=(const UInt128& other) {
+ high_ = other.high_;
+ low_ = other.low_;
+ return *this;
+ }
+
+ // Equality operators.
+ bool operator==(const UInt128& other) const {
+ return high_ == other.high_ && low_ == other.low_;
+ }
+
+ bool operator!=(const UInt128& other) const {
+ return high_ != other.high_ || low_ != other.low_;
+ }
+
+ // Shifting.
+ UInt128& operator<<=(int shift) {
+ high_ = shift >= 64 ? (shift >= 128 ? 0 : low_ << (shift - 64))
+ : high_ << shift;
+
+ if (shift > 0 && shift < 64) {
+ const uint64_t overlappingBits = low_ >> (64 - shift);
+ high_ |= overlappingBits;
+ }
+
+ low_ = shift >= 64 ? 0 : low_ << shift;
+ return *this;
+ }
+
+ UInt128 operator<<(int shift) const {
+ UInt128 result = *this;
+ result <<= shift;
+ return result;
+ }
+
+ UInt128& operator>>=(int shift) {
+ low_ = shift >= 64 ? (shift >= 128 ? 0 : high_ >> (shift - 64))
+ : low_ >> shift;
+
+ if (shift > 0 && shift < 64) {
+ const uint64_t overlappingBits = high_ << (64 - shift);
+ low_ |= overlappingBits;
+ }
+
+ high_ = shift >= 64 ? 0 : high_ >> shift;
+
+ return *this;
+ }
+
+ UInt128 operator>>(int shift) const {
+ UInt128 result = *this;
+ result >>= shift;
+ return result;
+ }
+
+ // Binary operations.
+ UInt128& operator|=(const UInt128& other) {
+ high_ |= other.high_;
+ low_ |= other.low_;
+ return *this;
+ }
+
+ UInt128 operator|(const UInt128& other) const {
+ UInt128 result = *this;
+ result |= other;
+ return result;
+ }
+
+ UInt128& operator&=(const UInt128& other) {
+ high_ &= other.high_;
+ low_ &= other.low_;
+ return *this;
+ }
+
+ UInt128 operator&(const UInt128& other) const {
+ UInt128 result = *this;
+ result &= other;
+ return result;
+ }
+
+ UInt128& operator^=(const UInt128& other) {
+ high_ ^= other.high_;
+ low_ ^= other.low_;
+ return *this;
+ }
+
+ UInt128 operator^(const UInt128& other) const {
+ UInt128 result = *this;
+ result ^= other;
+ return result;
+ }
+
+ UInt128 operator~() const {
+ UInt128 result = *this;
+ result.high_ = ~high_;
+ result.low_ = ~low_;
+ return result;
+ }
+
+ // Addition/subtraction.
+ UInt128& operator+=(const UInt128& other) {
+ const uint64_t carry =
+ (((low_ & other.low_) & 1) + (low_ >> 1) + (other.low_ >> 1)) >> 63;
+ high_ += other.high_ + carry;
+ low_ += other.low_;
+ return *this;
+ }
+
+ UInt128 operator+(const UInt128& other) const {
+ UInt128 result = *this;
+ result += other;
+ return result;
+ }
+
+ UInt128& operator-=(const UInt128& other) {
+ low_ -= other.low_;
+ const uint64_t carry =
+ (((low_ & other.low_) & 1) + (low_ >> 1) + (other.low_ >> 1)) >> 63;
+ high_ -= other.high_ + carry;
+ return *this;
+ }
+
+ UInt128 operator-(const UInt128& other) const {
+ UInt128 result = *this;
+ result -= other;
+ return result;
+ }
+
+ private:
+ // TODO(google): Different order for little endian.
+ uint64_t low_ = 0;
+ uint64_t high_ = 0;
+};
+
+} // namespace base
+} // namespace astc_codec
+
+#endif // ASTC_CODEC_BASE_UINT128_H_
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/base/utils.h b/3rdparty/bimg/3rdparty/astc-codec/src/base/utils.h
new file mode 100644
index 00000000000..0a4fabdc7a4
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/base/utils.h
@@ -0,0 +1,35 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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.
+
+#ifndef ASTC_CODEC_BASE_UTILS_H_
+#define ASTC_CODEC_BASE_UTILS_H_
+
+#include <cassert>
+#include <cstdio>
+#include <cstdlib>
+
+#ifdef NDEBUG
+#define UTILS_RELEASE_ASSERT(x) \
+ do { \
+ const bool result = (x); \
+ if (!result) { \
+ fprintf(stderr, "Error: UTILS_RELEASE_ASSERT failed: %s\n", #x); \
+ abort(); \
+ } \
+ } while (false)
+#else
+#define UTILS_RELEASE_ASSERT(x) assert(x)
+#endif
+
+#endif // ASTC_CODEC_BASE_UTILS_H_
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/BUILD.bazel b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/BUILD.bazel
new file mode 100644
index 00000000000..ccc764eea83
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/BUILD.bazel
@@ -0,0 +1,246 @@
+# Copyright 2018 Google LLC
+#
+# 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
+#
+# https://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.
+
+licenses(["notice"])
+
+cc_library(
+ name = "footprint",
+ srcs = ["footprint.cc"],
+ hdrs = ["footprint.h"],
+ deps = [
+ "//:api",
+ "//src/base",
+ ],
+)
+
+cc_library(
+ name = "astc_utils",
+ srcs = [
+ "astc_file.cc",
+ "endpoint_codec.cc",
+ "integer_sequence_codec.cc",
+ "intermediate_astc_block.cc",
+ "logical_astc_block.cc",
+ "partition.cc",
+ "physical_astc_block.cc",
+ "quantization.cc",
+ "weight_infill.cc",
+ ],
+ hdrs = [
+ "astc_file.h",
+ "endpoint_codec.h",
+ "integer_sequence_codec.h",
+ "intermediate_astc_block.h",
+ "logical_astc_block.h",
+ "partition.h",
+ "physical_astc_block.h",
+ "quantization.h",
+ "types.h",
+ "weight_infill.h",
+ ],
+ copts = [
+ "-Wno-unused-variable",
+ "-O3",
+ ],
+ deps = [
+ ":footprint",
+ "//src/base",
+ ],
+)
+
+cc_library(
+ name = "codec",
+ srcs = ["codec.cc"],
+ hdrs = ["codec.h"],
+ visibility = ["//:__pkg__"],
+ deps = [
+ ":astc_utils",
+ ":footprint",
+ "//src/base",
+ ],
+)
+
+cc_binary(
+ name = "astc_inspector_cli",
+ srcs = ["tools/astc_inspector_cli.cc"],
+ deps = [
+ ":astc_utils",
+ "//src/base",
+ ],
+)
+
+################################################################################
+##
+## Testing
+##
+################################################################################
+
+cc_library(
+ name = "test",
+ testonly = 1,
+ hdrs = ["test/image_utils.h"],
+ deps = ["@gtest//:gtest"],
+)
+
+cc_test(
+ name = "physical_astc_block_test",
+ size = "small",
+ srcs = ["test/physical_astc_block_test.cc"],
+ deps = [
+ ":astc_utils",
+ "@gtest//:gtest_main",
+ "//src/base",
+ ],
+)
+
+cc_test(
+ name = "partition_test",
+ size = "medium",
+ srcs = ["test/partition_test.cc"],
+ deps = [
+ ":astc_utils",
+ "@gtest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "integer_sequence_codec_test",
+ size = "small",
+ srcs = ["test/integer_sequence_codec_test.cc"],
+ deps = [
+ ":astc_utils",
+ "@gtest//:gtest_main",
+ "//src/base",
+ ],
+)
+
+cc_test(
+ name = "intermediate_astc_block_test",
+ size = "small",
+ srcs = ["test/intermediate_astc_block_test.cc"],
+ data = glob([
+ "testdata/checkered_*.astc",
+ ]),
+ deps = [
+ ":astc_utils",
+ ":test",
+ "@gtest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "quantization_test",
+ size = "medium",
+ srcs = ["test/quantization_test.cc"],
+ deps = [
+ ":astc_utils",
+ "@gtest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "weight_infill_test",
+ size = "small",
+ srcs = ["test/weight_infill_test.cc"],
+ deps = [
+ ":astc_utils",
+ ":footprint",
+ "@gtest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "endpoint_codec_test",
+ size = "small",
+ srcs = ["test/endpoint_codec_test.cc"],
+ data = [
+ ":testdata/checkerboard.astc",
+ ],
+ deps = [
+ ":astc_utils",
+ ":test",
+ "@gtest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "logical_astc_block_test",
+ size = "large",
+ srcs = ["test/logical_astc_block_test.cc"],
+ data = glob([
+ "testdata/atlas_small_*.astc",
+ "testdata/atlas_small_*.bmp",
+ "testdata/footprint_*.astc",
+ "testdata/footprint_*.bmp",
+ "testdata/rgb_*.astc",
+ "testdata/rgb_*.bmp",
+ ]),
+ deps = [
+ ":astc_utils",
+ ":test",
+ "@gtest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "codec_test",
+ size = "large",
+ srcs = ["test/codec_test.cc"],
+ data = glob([
+ "testdata/atlas_small_*.astc",
+ "testdata/atlas_small_*.bmp",
+ ]),
+ deps = [
+ ":codec",
+ ":test",
+ "@gtest//:gtest_main",
+ "//:api",
+ ],
+)
+
+cc_test(
+ name = "footprint_test",
+ size = "small",
+ srcs = ["test/footprint_test.cc"],
+ deps = [
+ ":footprint",
+ "@gtest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "astc_fuzzer",
+ srcs = ["test/astc_fuzzer.cc"],
+ copts = select({
+ # Clang-only flags. TODO: Find a better way to detect GCC/clang.
+ "@bazel_tools//src/conditions:darwin_x86_64": [
+ "-fsanitize-coverage=trace-pc-guard,indirect-calls,trace-cmp",
+ "-fsanitize-coverage=bb",
+ ],
+ "@bazel_tools//src/conditions:darwin": [
+ "-fsanitize-coverage=trace-pc-guard,indirect-calls,trace-cmp",
+ "-fsanitize-coverage=bb",
+ ],
+ # GCC-only flags.
+ "//conditions:default": [
+ "-finstrument-functions"
+ ],
+ }),
+ deps = [
+ ":codec",
+ "@honggfuzz//:honggfuzz",
+ "@benchmark//:benchmark",
+ ],
+ linkstatic = 1,
+)
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/CMakeLists.txt b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/CMakeLists.txt
new file mode 100644
index 00000000000..e82a692c40e
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/CMakeLists.txt
@@ -0,0 +1,95 @@
+# Copyright 2018 Google LLC
+#
+# 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
+#
+# https://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.
+add_library(footprint footprint.cc)
+target_link_libraries(footprint base)
+
+add_library(astc_utils
+ astc_file.cc
+ endpoint_codec.cc
+ integer_sequence_codec.cc
+ intermediate_astc_block.cc
+ logical_astc_block.cc
+ partition.cc
+ physical_astc_block.cc
+ quantization.cc
+ weight_infill.cc)
+target_link_libraries(astc_utils PRIVATE base footprint)
+target_include_directories(astc_utils PRIVATE ../..)
+
+add_library(astc-codec codec.cc)
+target_link_libraries(astc-codec PRIVATE astc_utils)
+target_include_directories(astc-codec PUBLIC ../../include)
+target_include_directories(astc-codec PRIVATE ../..)
+
+add_executable(astc_inspector_cli tools/astc_inspector_cli.cc)
+target_include_directories(astc_inspector_cli PRIVATE ../..)
+target_link_libraries(astc_inspector_cli PRIVATE astc_utils)
+
+#
+# Testing
+#
+if(OPTION_ASTC_TESTS)
+ # Note that we will execute all the tests in the project directory.
+ # We do this to ensure the unit tests can pick up the required test data
+
+ # Create interface library exposing the root as an include directory
+ add_library(codec_test_dependencies INTERFACE)
+ target_include_directories(codec_test_dependencies INTERFACE ../..)
+
+ add_executable(physical_astc_block_test test/physical_astc_block_test.cc)
+ add_test(NAME physical_astc_block_test COMMAND physical_astc_block_test WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
+ target_link_libraries(physical_astc_block_test astc_utils codec_test_dependencies gmock_main)
+
+ add_executable(partition_test test/partition_test.cc)
+ add_test(NAME partition_test COMMAND partition_test WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
+ target_link_libraries(partition_test PRIVATE astc_utils codec_test_dependencies gmock_main)
+
+ add_executable(integer_sequence_codec_test test/integer_sequence_codec_test.cc)
+ target_link_libraries(integer_sequence_codec_test PRIVATE astc_utils codec_test_dependencies gmock_main)
+
+ add_executable(intermediate_astc_block_test test/intermediate_astc_block_test.cc)
+ add_test(NAME intermediate_astc_block_test COMMAND intermediate_astc_block_test WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
+ target_link_libraries(intermediate_astc_block_test PRIVATE astc_utils codec_test_dependencies gmock_main)
+
+ add_executable(quantization_test test/quantization_test.cc)
+ add_test(NAME quantization_test COMMAND quantization_test WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
+ target_link_libraries(quantization_test PRIVATE astc_utils codec_test_dependencies gmock_main)
+
+ add_executable(weight_infill_test test/weight_infill_test.cc)
+ add_test(NAME weight_infill_test COMMAND weight_infill_test WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
+ target_link_libraries(weight_infill_test PRIVATE astc_utils footprint codec_test_dependencies gmock_main)
+
+ add_executable(endpoint_codec_test test/endpoint_codec_test.cc)
+ add_test(NAME endpoint_codec_test COMMAND endpoint_codec_test WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
+ target_link_libraries(endpoint_codec_test PRIVATE astc_utils codec_test_dependencies gmock_main)
+
+ add_executable(logical_astc_block_test test/logical_astc_block_test.cc)
+ add_test(NAME logical_astc_block_test COMMAND logical_astc_block_test WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
+ target_link_libraries(logical_astc_block_test PRIVATE astc_utils codec_test_dependencies gmock_main)
+
+ add_executable(codec_test test/codec_test.cc)
+ add_test(NAME codec_test COMMAND codec_test WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
+
+ target_link_libraries(codec_test PRIVATE astc-codec codec_test_dependencies gmock_main)
+
+ add_executable(footprint_test test/footprint_test.cc)
+ add_test(NAME footprint_test COMMAND footprint_test WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
+ target_link_libraries(footprint_test PRIVATE footprint codec_test_dependencies gmock_main)
+
+ if(OPTION_BUILD_FUZZER)
+ message(FATAL_ERROR "Not yet supported due to missing dependencies")
+ add_executable(astc_fuzzer test/astc_fuzzer.cc codec_test_dependencies gmock_main)
+ target_link_libraries(astc_fuzzer PRIVATE astc-codec honggfuzz benchmark)
+ endif()
+endif()
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/astc_file.cc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/astc_file.cc
new file mode 100644
index 00000000000..9999ee23ddd
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/astc_file.cc
@@ -0,0 +1,185 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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 "src/decoder/astc_file.h"
+
+#include <cstring>
+#include <fstream>
+#include <memory>
+#include <sstream>
+
+namespace astc_codec {
+
+namespace {
+static constexpr size_t kASTCHeaderSize = 16;
+
+// Reads a value of size T from the buffer at the current offset, then
+// increments the offset.
+template<typename T>
+inline T ReadVal(const char* file_data, size_t& offset) {
+ T x;
+ memcpy(&x, &file_data[offset], sizeof(T));
+ offset += sizeof(T);
+ return x;
+}
+} // namespace
+
+ASTCFile::ASTCFile(Header&& header, std::string&& blocks)
+ : header_(std::move(header)), blocks_(std::move(blocks)) {}
+
+std::unique_ptr<ASTCFile> ASTCFile::LoadFromMemory(const char* data,
+ size_t length,
+ std::string* error) {
+ if (length < kASTCHeaderSize) {
+ *error = "Incomplete header.";
+ return nullptr;
+ }
+
+ base::Optional<Header> header_opt = ParseHeader(data);
+ if (!header_opt) {
+ *error = "Invalid ASTC header.";
+ return nullptr;
+ }
+
+ Header header = header_opt.value();
+
+ if (header.block_width_ == 0 || header.block_height_ == 0) {
+ *error = "Invalid block size.";
+ return nullptr;
+ }
+
+ std::string blocks(data + kASTCHeaderSize, data + length);
+
+ // Check that this file has the expected number of blocks.
+ const size_t expected_block_count =
+ ((header.width_ + header.block_width_ - 1) / header.block_width_) *
+ ((header.height_ + header.block_height_ - 1) / header.block_height_);
+
+ if (blocks.size() % PhysicalASTCBlock::kSizeInBytes != 0 ||
+ blocks.size() / PhysicalASTCBlock::kSizeInBytes != expected_block_count) {
+ std::stringstream ss;
+ ss << "Unexpected file length " << blocks.size() << " expected "
+ << kASTCHeaderSize +
+ expected_block_count * PhysicalASTCBlock::kSizeInBytes
+ << " bytes.";
+ *error = ss.str();
+ return nullptr;
+ }
+
+ return std::unique_ptr<ASTCFile>(
+ new ASTCFile(std::move(header), std::move(blocks)));
+}
+
+std::unique_ptr<ASTCFile> ASTCFile::LoadFile(const std::string& path,
+ std::string* error) {
+ std::ifstream is(path, std::ios::binary);
+ if (!is) {
+ *error = "File not found: " + path;
+ return nullptr;
+ }
+
+ char header_data[kASTCHeaderSize] = {};
+ if (!is.read(header_data, kASTCHeaderSize)) {
+ *error = "Failed to load ASTC header.";
+ return nullptr;
+ }
+
+ base::Optional<Header> header_opt = ParseHeader(header_data);
+ if (!header_opt) {
+ *error = "Invalid ASTC header.";
+ return nullptr;
+ }
+
+ Header header = header_opt.value();
+
+ std::string blocks;
+ {
+ std::ostringstream ss;
+ ss << is.rdbuf();
+ blocks = ss.str();
+ }
+
+ // Check that this file has the expected number of blocks.
+ const size_t expected_block_count =
+ ((header.width_ + header.block_width_ - 1) / header.block_width_) *
+ ((header.height_ + header.block_height_ - 1) / header.block_height_);
+
+ if (blocks.size() % PhysicalASTCBlock::kSizeInBytes != 0 ||
+ blocks.size() / PhysicalASTCBlock::kSizeInBytes != expected_block_count) {
+ std::stringstream ss;
+ ss << "Unexpected file length " << blocks.size() << " expected "
+ << kASTCHeaderSize +
+ expected_block_count * PhysicalASTCBlock::kSizeInBytes
+ << " bytes.";
+ *error = ss.str();
+ return nullptr;
+ }
+
+ return std::unique_ptr<ASTCFile>(
+ new ASTCFile(std::move(header), std::move(blocks)));
+}
+
+base::Optional<Footprint> ASTCFile::GetFootprint() const {
+ return Footprint::FromDimensions(int(header_.block_width_), int(header_.block_height_));
+}
+
+std::string ASTCFile::GetFootprintString() const {
+ std::stringstream footprint;
+ footprint << header_.block_width_ << "x" << header_.block_height_;
+ return footprint.str();
+}
+
+const std::string& ASTCFile::GetRawBlockData() const {
+ return blocks_;
+}
+
+PhysicalASTCBlock ASTCFile::GetBlock(size_t block_idx) const {
+ const size_t sz = PhysicalASTCBlock::kSizeInBytes;
+ const size_t offset = PhysicalASTCBlock::kSizeInBytes * block_idx;
+ assert(offset <= blocks_.size() - sz);
+ return PhysicalASTCBlock(blocks_.substr(offset, sz));
+}
+
+base::Optional<ASTCFile::Header> ASTCFile::ParseHeader(const char* header) {
+ size_t offset = 0;
+ // TODO(google): Handle endianness.
+ const uint32_t magic = ReadVal<uint32_t>(header, offset);
+ if (magic != 0x5CA1AB13) {
+ return {};
+ }
+
+ const uint32_t block_width = ReadVal<uint8_t>(header, offset);
+ const uint32_t block_height = ReadVal<uint8_t>(header, offset);
+ const uint32_t block_depth = ReadVal<uint8_t>(header, offset);
+
+ uint32_t width = 0;
+ width |= ReadVal<uint8_t>(header, offset);
+ width |= ReadVal<uint8_t>(header, offset) << 8;
+ width |= ReadVal<uint8_t>(header, offset) << 16;
+
+ uint32_t height = 0;
+ height |= ReadVal<uint8_t>(header, offset);
+ height |= ReadVal<uint8_t>(header, offset) << 8;
+ height |= ReadVal<uint8_t>(header, offset) << 16;
+
+ uint32_t depth = 0;
+ depth |= ReadVal<uint8_t>(header, offset);
+ depth |= ReadVal<uint8_t>(header, offset) << 8;
+ depth |= ReadVal<uint8_t>(header, offset) << 16;
+ assert(offset == kASTCHeaderSize);
+
+ return Header(width, height, depth, block_width, block_height, block_depth);
+}
+
+} // namespace astc_codec
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/astc_file.h b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/astc_file.h
new file mode 100644
index 00000000000..c31c2ba7d51
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/astc_file.h
@@ -0,0 +1,97 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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.
+
+#ifndef ASTC_CODEC_DECODER_ASTC_FILE_H_
+#define ASTC_CODEC_DECODER_ASTC_FILE_H_
+
+#include "src/base/optional.h"
+#include "src/decoder/footprint.h"
+#include "src/decoder/physical_astc_block.h"
+
+#include <memory>
+#include <string>
+
+namespace astc_codec {
+
+// A thin wrapper around a .astc file on disk. This class simply reads the ASTC
+// header, and stores the block data in memory.
+class ASTCFile {
+ private:
+ struct Header {
+ Header(size_t width, size_t height, size_t depth, size_t block_width,
+ size_t block_height, size_t block_depth)
+ : width_(width),
+ height_(height),
+ depth_(depth),
+ block_width_(block_width),
+ block_height_(block_height),
+ block_depth_(block_depth) {}
+
+ size_t width_;
+ size_t height_;
+ size_t depth_;
+
+ size_t block_width_;
+ size_t block_height_;
+ size_t block_depth_;
+ };
+
+ ASTCFile(ASTCFile::Header&& header, std::string&& blocks);
+
+ public:
+ // Load an ASTC file from memory.
+ // If loading failed, nullptr is returned and an error string is populated
+ // in the error parameter.
+ static std::unique_ptr<ASTCFile> LoadFromMemory(const char* data,
+ size_t length,
+ std::string* error);
+
+ // Load an ASTC file from file.
+ // If loading failed, nullptr is returned and an error string is populated
+ // in the error parameter.
+ static std::unique_ptr<ASTCFile> LoadFile(const std::string& path,
+ std::string* error);
+
+ // Returns the footprint for the file, if it is considered to be a valid
+ // footprint.
+ base::Optional<Footprint> GetFootprint() const;
+
+ // Returns the string of the form "NxM" where N and M are the width and height
+ // of the block footprint, respectively.
+ std::string GetFootprintString() const;
+
+ // Get the raw block data for the astc file.
+ const std::string& GetRawBlockData() const;
+
+ // Returns the physical block at the associated block index.
+ PhysicalASTCBlock GetBlock(size_t block_idx) const;
+
+ size_t GetWidth() const { return header_.width_; }
+ size_t GetHeight() const { return header_.height_; }
+ size_t GetDepth() const { return header_.depth_; }
+
+ size_t NumBlocks() const {
+ return blocks_.size() / PhysicalASTCBlock::kSizeInBytes;
+ }
+
+ private:
+ static base::Optional<ASTCFile::Header> ParseHeader(const char* header);
+
+ const Header header_;
+ const std::string blocks_;
+};
+
+} // namespace astc_codec
+
+#endif // ASTC_CODEC_DECODER_ASTC_FILE_H_
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/codec.cc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/codec.cc
new file mode 100644
index 00000000000..baa50b4c762
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/codec.cc
@@ -0,0 +1,132 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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 "src/decoder/codec.h"
+#include "src/base/uint128.h"
+#include "src/decoder/logical_astc_block.h"
+#include "src/decoder/physical_astc_block.h"
+
+#include <cstring>
+
+namespace astc_codec {
+
+namespace {
+static constexpr size_t kBytesPerPixelUNORM8 = 4;
+}
+
+bool DecompressToImage(const uint8_t* astc_data, size_t astc_data_size,
+ size_t width, size_t height, Footprint footprint,
+ uint8_t* out_buffer, size_t out_buffer_size,
+ size_t out_buffer_stride) {
+ const size_t block_width = footprint.Width();
+ const size_t block_height = footprint.Height();
+ assert(block_width != 0);
+ assert(block_height != 0);
+
+ if (width == 0 || height == 0) {
+ return false;
+ }
+
+ const size_t blocks_wide = (width + block_width - 1) / block_width;
+ assert(blocks_wide != 0);
+
+ // Check that this buffer has the expected number of blocks.
+ const size_t expected_block_count =
+ ((width + block_width - 1) / block_width) *
+ ((height + block_height - 1) / block_height);
+
+ if (astc_data_size % PhysicalASTCBlock::kSizeInBytes != 0 ||
+ astc_data_size / PhysicalASTCBlock::kSizeInBytes !=
+ expected_block_count) {
+ // TODO(google): Expose error?
+ return false;
+ }
+
+ if (kBytesPerPixelUNORM8 * width > out_buffer_stride ||
+ out_buffer_stride * height < out_buffer_size) {
+ // Output buffer too small.
+ return false;
+ }
+
+ base::UInt128 block;
+ static_assert(sizeof(block) == PhysicalASTCBlock::kSizeInBytes,
+ "Block size mismatch");
+
+ for (size_t i0 = 0; i0 < astc_data_size; i0 += PhysicalASTCBlock::kSizeInBytes) {
+ const size_t block_index = i0 / PhysicalASTCBlock::kSizeInBytes;
+ const size_t block_x = block_index % blocks_wide;
+ const size_t block_y = block_index / blocks_wide;
+ block = *(base::UInt128*)(astc_data + i0);
+
+ PhysicalASTCBlock physical_block(block);
+ auto lb = UnpackLogicalBlock(footprint, physical_block);
+ if (!lb) {
+ return false;
+ }
+
+ LogicalASTCBlock logical_block = lb.value();
+
+ for (size_t y = 0; y < block_height; ++y) {
+ const size_t py = block_height * block_y + y;
+ uint8_t* out_row = out_buffer + py * out_buffer_stride;
+
+ for (size_t x = 0; x < block_width; ++x) {
+ const size_t px = block_width * block_x + x;
+
+ // Skip out of bounds.
+ if (px >= width || py >= height) {
+ continue;
+ }
+
+ uint8_t* pixel = out_row + px * kBytesPerPixelUNORM8;
+ const RgbaColor decoded_color = logical_block.ColorAt(int(x), int(y));
+ for (size_t i = 0; i < kBytesPerPixelUNORM8; ++i) {
+ pixel[i] = static_cast<uint8_t>(decoded_color[i]);
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+bool DecompressToImage(const ASTCFile& file, uint8_t* out_buffer,
+ size_t out_buffer_size, size_t out_buffer_stride) {
+ base::Optional<Footprint> footprint = file.GetFootprint();
+ if (!footprint) {
+ return false;
+ }
+
+ return DecompressToImage(
+ reinterpret_cast<const uint8_t*>(file.GetRawBlockData().c_str()),
+ file.GetRawBlockData().size(), file.GetWidth(), file.GetHeight(),
+ footprint.value(), out_buffer, out_buffer_size, out_buffer_stride);
+}
+
+bool ASTCDecompressToRGBA(const uint8_t* astc_data, size_t astc_data_size,
+ size_t width, size_t height, FootprintType footprint,
+ uint8_t* out_buffer, size_t out_buffer_size,
+ size_t out_buffer_stride) {
+ base::Optional<Footprint> footprint_opt =
+ Footprint::FromFootprintType(footprint);
+ if (!footprint_opt) {
+ return false;
+ }
+
+ return DecompressToImage(astc_data, astc_data_size, width, height,
+ footprint_opt.value(), out_buffer, out_buffer_size,
+ out_buffer_stride);
+}
+
+} // namespace astc_codec
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/codec.h b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/codec.h
new file mode 100644
index 00000000000..eed601a41b8
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/codec.h
@@ -0,0 +1,41 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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.
+
+#ifndef ASTC_CODEC_DECODER_CODEC_H_
+#define ASTC_CODEC_DECODER_CODEC_H_
+
+#include "src/decoder/astc_file.h"
+#include "src/decoder/footprint.h"
+
+#include <string>
+
+namespace astc_codec {
+
+// Decompresses ASTC blocks to an image buffer.
+// Returns true if the decompression succeeded and the out buffer has been
+// filled.
+bool DecompressToImage(const uint8_t* astc_data, size_t astc_data_size,
+ size_t width, size_t height, Footprint footprint,
+ uint8_t* out_buffer, size_t out_buffer_size,
+ size_t out_buffer_stride);
+
+// Decompresses an ASTC file to an image buffer.
+// Returns true if the decompression succeeded and the out buffer has been
+// filled.
+bool DecompressToImage(const ASTCFile& file, uint8_t* out_buffer,
+ size_t out_buffer_size, size_t out_buffer_stride);
+
+} // namespace astc_codec
+
+#endif // ASTC_CODEC_DECODER_CODEC_H_
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/endpoint_codec.cc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/endpoint_codec.cc
new file mode 100644
index 00000000000..10970213a41
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/endpoint_codec.cc
@@ -0,0 +1,963 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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 "src/decoder/endpoint_codec.h"
+#include "src/decoder/quantization.h"
+
+#include <algorithm>
+#include <array>
+#include <numeric>
+#include <utility>
+
+namespace astc_codec {
+
+namespace {
+
+template<typename T>
+T Clamp(T value, T min, T max) {
+ return value < min ? min : (value > max ? max : value);
+}
+
+// This is the 'blue_contract' function defined in Section C.2.14 of the ASTC
+// specification.
+template<typename ArrayType>
+void BlueContract(ArrayType* const cptr) {
+ ArrayType& c = *cptr;
+ c[0] = (c[0] + c[2]) >> 1;
+ c[1] = (c[1] + c[2]) >> 1;
+}
+
+// Returns the inverse of values in BlueContract, subjected to the constraint
+// that the new values are stored in the range [0, 255].
+template<typename ArrayType>
+ArrayType InvertBlueContract(const ArrayType& c) {
+ ArrayType result = c;
+ result[0] = Clamp(2 * c[0] - c[2], 0, 255);
+ result[1] = Clamp(2 * c[1] - c[2], 0, 255);
+ return result;
+}
+
+// This is the 'bit_transfer_signed' function defined in Section C.2.14 of the
+// ASTC specification.
+void BitTransferSigned(int* const a, int* const b) {
+ *b >>= 1;
+ *b |= *a & 0x80;
+ *a >>= 1;
+ *a &= 0x3F;
+ if ((*a & 0x20) != 0) {
+ *a -= 0x40;
+ }
+}
+
+// Takes two values, |a| in the range [-32, 31], and |b| in the range [0, 255],
+// and returns the two values in [0, 255] that will reconstruct |a| and |b| when
+// passed to the BitTransferSigned function.
+void InvertBitTransferSigned(int* const a, int* const b) {
+ assert(*a >= -32); assert(*a < 32);
+ assert(*b >= 0); assert(*b < 256);
+
+ if (*a < 0) {
+ *a += 0x40;
+ }
+ *a <<= 1;
+ *a |= (*b & 0x80);
+ *b <<= 1;
+ *b &= 0xff;
+}
+
+template<typename ContainerType>
+void Quantize(ContainerType* const c, size_t max_value) {
+ for (auto& x : *c) {
+ x = QuantizeCEValueToRange(x, int(max_value));
+ }
+}
+
+template<typename ArrayType>
+ArrayType QuantizeColor(const ArrayType& c, size_t max_value) {
+ ArrayType result = c;
+ Quantize(&result, max_value);
+ return result;
+}
+
+template<typename ContainerType>
+void Unquantize(ContainerType* const c, size_t max_value) {
+ for (auto& x : *c) {
+ x = UnquantizeCEValueFromRange(x, int(max_value));
+ }
+}
+
+template<typename ArrayType>
+ArrayType UnquantizeColor(const ArrayType& c, size_t max_value) {
+ ArrayType result = c;
+ Unquantize(&result, max_value);
+ return result;
+}
+
+// Returns the average of the three RGB channels.
+template<typename ContainerType>
+int AverageRGB(const ContainerType& c) {
+ // Each channel can be in the range [0, 255], and we need to divide by three.
+ // However, we want to round the error properly. Both (x + 1) / 3 and
+ // (x + 2) / 3 are relatively imprecise when it comes to rounding, so instead
+ // we increase the precision by multiplying our numerator by some arbitrary
+ // number. Here, we choose 256 to get 8 additional bits and maintain
+ // performance since it turns into a shift rather than a multiply. Our
+ // denominator then becomes 3 * 256 = 768.
+ return (std::accumulate(c.begin(), c.begin() + 3, 0) * 256 + 384) / 768;
+}
+
+// Returns the sum of squared differences between each element of |a| and |b|,
+// which are assumed to contain the same number of elements.
+template<typename ContainerType>
+const typename ContainerType::value_type SquaredError(
+ const ContainerType& a, const ContainerType& b,
+ size_t num_channels = std::tuple_size<ContainerType>::value) {
+ using ValueTy = typename ContainerType::value_type;
+ static_assert(std::is_signed<ValueTy>::value,
+ "Value type assumed to be signed to avoid branch below.");
+ ValueTy result = ValueTy(0);
+ for (size_t i = 0; i < num_channels; ++i) {
+ ValueTy error = a[i] - b[i];
+ result += error * error;
+ }
+ return result;
+}
+
+constexpr int MaxValuesForModes(ColorEndpointMode mode_a,
+ ColorEndpointMode mode_b) {
+ return (NumColorValuesForEndpointMode(mode_a) >
+ NumColorValuesForEndpointMode(mode_b))
+ ? NumColorValuesForEndpointMode(mode_a)
+ : NumColorValuesForEndpointMode(mode_b);
+}
+
+// This function takes the two colors in |endpoint_low| and |endpoint_high| and
+// encodes them into |vals| according to the ASTC spec in section C.2.14. It
+// assumes that the two colors are close enough to grayscale that the encoding
+// should use the ColorEndpointMode kLDRLumaBaseOffset or kLDRLumaDirect. Which
+// one is chosen depends on which produces smaller error for the given
+// quantization value stored in |max_value|
+bool EncodeColorsLuma(const RgbaColor& endpoint_low,
+ const RgbaColor& endpoint_high,
+ int max_value, ColorEndpointMode* const astc_mode,
+ std::vector<int>* const vals) {
+ assert(vals->size() ==
+ size_t(NumValuesForEncodingMode(EndpointEncodingMode::kDirectLuma)));
+ int avg1 = AverageRGB(endpoint_low);
+ int avg2 = AverageRGB(endpoint_high);
+
+ // For the offset mode, L1 is strictly greater than L2, so if we are using
+ // it to encode the color values, we need to swap the weights and
+ // endpoints so that the larger of the two is the second endpoint.
+ bool needs_weight_swap = false;
+ if (avg1 > avg2) {
+ needs_weight_swap = true;
+ std::swap(avg1, avg2);
+ }
+ assert(avg1 <= avg2);
+
+ // Now, the first endpoint is based on the low-order six bits of the first
+ // value, and the high order two bits of the second value. The low order
+ // six bits of the second value are used as the (strictly positive) offset
+ // from the first value.
+ const int offset = std::min(avg2 - avg1, 0x3F);
+ const int quant_off_low =
+ QuantizeCEValueToRange((avg1 & 0x3F) << 2, max_value);
+ const int quant_off_high =
+ QuantizeCEValueToRange((avg1 & 0xC0) | offset, max_value);
+
+ const int quant_low = QuantizeCEValueToRange(avg1, max_value);
+ const int quant_high = QuantizeCEValueToRange(avg2, max_value);
+
+ RgbaColor unquant_off_low, unquant_off_high;
+ RgbaColor unquant_low, unquant_high;
+
+ (*vals)[0] = quant_off_low;
+ (*vals)[1] = quant_off_high;
+ DecodeColorsForMode(
+ *vals, max_value, ColorEndpointMode::kLDRLumaBaseOffset,
+ &unquant_off_low, &unquant_off_high);
+
+ (*vals)[0] = quant_low;
+ (*vals)[1] = quant_high;
+ DecodeColorsForMode(*vals, max_value, ColorEndpointMode::kLDRLumaDirect,
+ &unquant_low, &unquant_high);
+
+ const auto calculate_error =
+ [needs_weight_swap, &endpoint_low, &endpoint_high]
+ (const RgbaColor& low, const RgbaColor& high) {
+ int error = 0;
+ if (needs_weight_swap) {
+ error += SquaredError(low, endpoint_high);
+ error += SquaredError(high, endpoint_low);
+ } else {
+ error += SquaredError(low, endpoint_low);
+ error += SquaredError(high, endpoint_high);
+ }
+ return error;
+ };
+
+ const int direct_error = calculate_error(unquant_low, unquant_high);
+ const int off_error = calculate_error(unquant_off_low, unquant_off_high);
+
+ if (direct_error <= off_error) {
+ (*vals)[0] = quant_low;
+ (*vals)[1] = quant_high;
+ *astc_mode = ColorEndpointMode::kLDRLumaDirect;
+ } else {
+ (*vals)[0] = quant_off_low;
+ (*vals)[1] = quant_off_high;
+ *astc_mode = ColorEndpointMode::kLDRLumaBaseOffset;
+ }
+
+ return needs_weight_swap;
+}
+
+class QuantizedEndpointPair {
+ public:
+ QuantizedEndpointPair(const RgbaColor& c_low, const RgbaColor& c_high,
+ int max_value)
+ : orig_low_(c_low),
+ orig_high_(c_high),
+ quant_low_(QuantizeColor(c_low, max_value)),
+ quant_high_(QuantizeColor(c_high, max_value)),
+ unquant_low_(UnquantizeColor(quant_low_, max_value)),
+ unquant_high_(UnquantizeColor(quant_high_, max_value)) { }
+
+ const RgbaColor& QuantizedLow() const { return quant_low_; }
+ const RgbaColor& QuantizedHigh() const { return quant_high_; }
+
+ const RgbaColor& UnquantizedLow() const { return unquant_low_; }
+ const RgbaColor& UnquantizedHigh() const { return unquant_high_; }
+
+ const RgbaColor& OriginalLow() const { return orig_low_; }
+ const RgbaColor& OriginalHigh() const { return orig_high_; }
+
+ private:
+ RgbaColor orig_low_;
+ RgbaColor orig_high_;
+
+ RgbaColor quant_low_;
+ RgbaColor quant_high_;
+
+ RgbaColor unquant_low_;
+ RgbaColor unquant_high_;
+};
+
+class CEEncodingOption {
+ public:
+ CEEncodingOption() { }
+ CEEncodingOption(
+ int squared_error, const QuantizedEndpointPair* quantized_endpoints,
+ bool swap_endpoints, bool blue_contract, bool use_offset_mode)
+ : squared_error_(squared_error),
+ quantized_endpoints_(quantized_endpoints),
+ swap_endpoints_(swap_endpoints),
+ blue_contract_(blue_contract),
+ use_offset_mode_(use_offset_mode) { }
+
+ // Returns true if able to generate valid |astc_mode| and |vals|. In some
+ // instances, such as if the endpoints reprsent a base/offset pair, we may not
+ // be able to guarantee blue-contract encoding due to how the base/offset pair
+ // are represented and the specifics of the decoding procedure. Similarly,
+ // some direct RGBA encodings also may not be able to emit blue-contract modes
+ // due to an unlucky combination of channels. In these instances, this
+ // function will return false, and all pointers will remain unmodified.
+ bool Pack(bool with_alpha, ColorEndpointMode* const astc_mode,
+ std::vector<int>* const vals, bool* const needs_weight_swap) const {
+ auto unquantized_low = quantized_endpoints_->UnquantizedLow();
+ auto unquantized_high = quantized_endpoints_->UnquantizedHigh();
+
+ // In offset mode, we do BitTransferSigned before analyzing the values
+ // of the endpoints in order to determine whether or not we're going to
+ // be using blue-contract mode.
+ if (use_offset_mode_) {
+ for (size_t i = 0; i < std::tuple_size<RgbaColor>::value; ++i) {
+ BitTransferSigned(&unquantized_high[i], &unquantized_low[i]);
+ }
+ }
+
+ // Define variables as outlined in the ASTC spec C.2.14 for the RGB[A]
+ // direct and base-offset modes
+ int s0 = 0, s1 = 0;
+ for (int i = 0; i < 3; ++i) {
+ s0 += unquantized_low[i];
+ s1 += unquantized_high[i];
+ }
+
+ // Can we guarantee a blue-contract mode if we want it? In other words,
+ // if we swap which endpoint is high and which endpoint is low, can we
+ // guarantee that we will hit the corresponding decode path?
+ bool swap_vals = false;
+ if (use_offset_mode_) {
+ if (blue_contract_) {
+ swap_vals = s1 >= 0;
+ } else {
+ swap_vals = s1 < 0;
+ }
+
+ // In offset mode, we have two different measurements that swap the
+ // endpoints prior to encoding, so we don't need to swap them here.
+ // If we need to swap them to guarantee a blue-contract mode, then
+ // abort and wait until we get the other error measurement.
+ if (swap_vals) {
+ return false;
+ }
+ } else {
+ if (blue_contract_) {
+ // If we want a blue_contract path, but s1 == s0, then swapping the
+ // values will have no effect.
+ if (s1 == s0) {
+ return false;
+ }
+
+ swap_vals = s1 > s0;
+ // If we're encoding blue contract mode directly, then we implicitly
+ // swap the endpoints during decode, meaning that we need to take
+ // note of that here.
+ *needs_weight_swap = !(*needs_weight_swap);
+ } else {
+ swap_vals = s1 < s0;
+ }
+ }
+
+ const auto* quantized_low = &(quantized_endpoints_->QuantizedLow());
+ const auto* quantized_high = &(quantized_endpoints_->QuantizedHigh());
+
+ if (swap_vals) {
+ assert(!use_offset_mode_);
+ std::swap(quantized_low, quantized_high);
+ *needs_weight_swap = !(*needs_weight_swap);
+ }
+
+ (*vals)[0] = quantized_low->at(0);
+ (*vals)[1] = quantized_high->at(0);
+ (*vals)[2] = quantized_low->at(1);
+ (*vals)[3] = quantized_high->at(1);
+ (*vals)[4] = quantized_low->at(2);
+ (*vals)[5] = quantized_high->at(2);
+
+ if (use_offset_mode_) {
+ *astc_mode = ColorEndpointMode::kLDRRGBBaseOffset;
+ } else {
+ *astc_mode = ColorEndpointMode::kLDRRGBDirect;
+ }
+
+ if (with_alpha) {
+ (*vals)[6] = quantized_low->at(3);
+ (*vals)[7] = quantized_high->at(3);
+
+ if (use_offset_mode_) {
+ *astc_mode = ColorEndpointMode::kLDRRGBABaseOffset;
+ } else {
+ *astc_mode = ColorEndpointMode::kLDRRGBADirect;
+ }
+ }
+
+ // If we swapped them to measure, then they need to be swapped after
+ // decoding
+ if (swap_endpoints_) {
+ *needs_weight_swap = !(*needs_weight_swap);
+ }
+
+ return true;
+ }
+
+ bool BlueContract() const { return blue_contract_; }
+ int Error() const { return squared_error_; }
+
+ private:
+ int squared_error_;
+ const QuantizedEndpointPair* quantized_endpoints_;
+ bool swap_endpoints_;
+ bool blue_contract_;
+ bool use_offset_mode_;
+};
+
+bool EncodeColorsRGBA(const RgbaColor& endpoint_low_rgba,
+ const RgbaColor& endpoint_high_rgba,
+ int max_value, bool with_alpha,
+ ColorEndpointMode* const astc_mode,
+ std::vector<int>* const vals) {
+ const size_t num_channels = with_alpha ? std::tuple_size<RgbaColor>::value : 3;
+ // The difficulty of encoding into this mode is determining whether or
+ // not we'd like to use the 'blue contract' function to reconstruct
+ // the endpoints and whether or not we'll be more accurate by using the
+ // base/offset color modes instead of quantizing the color channels
+ // directly. With that in mind, we:
+ // 1. Generate the inverted values for blue-contract and offset modes.
+ // 2. Quantize all of the different endpoints.
+ // 3. Unquantize each sets and decide which one gives least error
+ // 4. Encode the values correspondingly.
+
+ // 1. Generate the inverted values for blue-contract and offset modes.
+ const auto inv_bc_low = InvertBlueContract(endpoint_low_rgba);
+ const auto inv_bc_high = InvertBlueContract(endpoint_high_rgba);
+
+ RgbaColor direct_base, direct_offset;
+ for (size_t i = 0; i < std::tuple_size<RgbaColor>::value; ++i) {
+ direct_base[i] = endpoint_low_rgba[i];
+ direct_offset[i] =
+ Clamp(endpoint_high_rgba[i] - endpoint_low_rgba[i], -32, 31);
+ InvertBitTransferSigned(&direct_offset[i], &direct_base[i]);
+ }
+
+ RgbaColor inv_bc_base, inv_bc_offset;
+ for (size_t i = 0; i < std::tuple_size<RgbaColor>::value; ++i) {
+ // Remember, for blue-contract'd offset modes, the base is compared
+ // against the second endpoint and not the first.
+ inv_bc_base[i] = inv_bc_high[i];
+ inv_bc_offset[i] = Clamp(inv_bc_low[i] - inv_bc_high[i], -32, 31);
+ InvertBitTransferSigned(&inv_bc_offset[i], &inv_bc_base[i]);
+ }
+
+ // The order of the endpoints for offset modes may determine how well they
+ // approximate the given endpoints. It may be that the quantization value
+ // produces more accurate values for the base than the offset or
+ // vice/versa. For this reason, we need to generate quantized versions of
+ // the endpoints as if they were swapped to see if we get better error
+ // out of it.
+
+ RgbaColor direct_base_swapped, direct_offset_swapped;
+ for (size_t i = 0; i < std::tuple_size<RgbaColor>::value; ++i) {
+ direct_base_swapped[i] = endpoint_high_rgba[i];
+ direct_offset_swapped[i] =
+ Clamp(endpoint_low_rgba[i] - endpoint_high_rgba[i], -32, 31);
+ InvertBitTransferSigned(&direct_offset_swapped[i], &direct_base_swapped[i]);
+ }
+
+ RgbaColor inv_bc_base_swapped, inv_bc_offset_swapped;
+ for (size_t i = 0; i < std::tuple_size<RgbaColor>::value; ++i) {
+ // Remember, for blue-contract'd offset modes, the base is compared
+ // against the second endpoint and not the first. Hence, the swapped
+ // version will compare the base against the first endpoint.
+ inv_bc_base_swapped[i] = inv_bc_low[i];
+ inv_bc_offset_swapped[i] = Clamp(inv_bc_high[i] - inv_bc_low[i], -32, 31);
+ InvertBitTransferSigned(&inv_bc_offset_swapped[i], &inv_bc_base_swapped[i]);
+ }
+
+ // 2. Quantize the endpoints directly.
+ const QuantizedEndpointPair direct_quantized(
+ endpoint_low_rgba, endpoint_high_rgba, max_value);
+ const QuantizedEndpointPair bc_quantized(
+ inv_bc_low, inv_bc_high, max_value);
+
+ const QuantizedEndpointPair offset_quantized(
+ direct_base, direct_offset, max_value);
+ const QuantizedEndpointPair bc_offset_quantized(
+ inv_bc_base, inv_bc_offset, max_value);
+
+ const QuantizedEndpointPair offset_swapped_quantized(
+ direct_base_swapped, direct_offset_swapped, max_value);
+ const QuantizedEndpointPair bc_offset_swapped_quantized(
+ inv_bc_base_swapped, inv_bc_offset_swapped, max_value);
+
+ // 3. Unquantize each set and decide which one gives least error.
+ std::array<CEEncodingOption, 6> errors;
+ auto errors_itr = errors.begin();
+
+ // 3.1 regular unquantized error
+ {
+ const auto rgba_low = direct_quantized.UnquantizedLow();
+ const auto rgba_high = direct_quantized.UnquantizedHigh();
+
+ const int sq_rgb_error =
+ SquaredError(rgba_low, endpoint_low_rgba, num_channels) +
+ SquaredError(rgba_high, endpoint_high_rgba, num_channels);
+
+ const bool swap_endpoints = false;
+ const bool blue_contract = false;
+ const bool offset_mode = false;
+ *(errors_itr++) = CEEncodingOption(
+ sq_rgb_error, &direct_quantized,
+ swap_endpoints, blue_contract, offset_mode);
+ }
+
+ // 3.2 Compute blue-contract'd error.
+ {
+ auto bc_low = bc_quantized.UnquantizedLow();
+ auto bc_high = bc_quantized.UnquantizedHigh();
+ BlueContract(&bc_low);
+ BlueContract(&bc_high);
+
+ const int sq_bc_error =
+ SquaredError(bc_low, endpoint_low_rgba, num_channels) +
+ SquaredError(bc_high, endpoint_high_rgba, num_channels);
+
+ const bool swap_endpoints = false;
+ const bool blue_contract = true;
+ const bool offset_mode = false;
+ *(errors_itr++) = CEEncodingOption(
+ sq_bc_error, &bc_quantized,
+ swap_endpoints, blue_contract, offset_mode);
+ }
+
+ // 3.3 Compute base/offset unquantized error.
+ const auto compute_base_offset_error =
+ [num_channels, &errors_itr, &endpoint_low_rgba, &endpoint_high_rgba]
+ (const QuantizedEndpointPair& pair, bool swapped) {
+ auto base = pair.UnquantizedLow();
+ auto offset = pair.UnquantizedHigh();
+
+ for (size_t i = 0; i < num_channels; ++i) {
+ BitTransferSigned(&offset[i], &base[i]);
+ offset[i] = Clamp(base[i] + offset[i], 0, 255);
+ }
+
+ int base_offset_error = 0;
+ // If we swapped the endpoints going in, then without blue contract
+ // we should be comparing the base against the high endpoint.
+ if (swapped) {
+ base_offset_error =
+ SquaredError(base, endpoint_high_rgba, num_channels) +
+ SquaredError(offset, endpoint_low_rgba, num_channels);
+ } else {
+ base_offset_error =
+ SquaredError(base, endpoint_low_rgba, num_channels) +
+ SquaredError(offset, endpoint_high_rgba, num_channels);
+ }
+
+ const bool blue_contract = false;
+ const bool offset_mode = true;
+ *(errors_itr++) = CEEncodingOption(
+ base_offset_error, &pair, swapped, blue_contract, offset_mode);
+ };
+
+ compute_base_offset_error(offset_quantized, false);
+
+ // 3.4 Compute base/offset blue-contract error.
+ const auto compute_base_offset_blue_contract_error =
+ [num_channels, &errors_itr, &endpoint_low_rgba, &endpoint_high_rgba]
+ (const QuantizedEndpointPair& pair, bool swapped) {
+ auto base = pair.UnquantizedLow();
+ auto offset = pair.UnquantizedHigh();
+
+ for (size_t i = 0; i < num_channels; ++i) {
+ BitTransferSigned(&offset[i], &base[i]);
+ offset[i] = Clamp(base[i] + offset[i], 0, 255);
+ }
+
+ BlueContract(&base);
+ BlueContract(&offset);
+
+ int sq_bc_error = 0;
+ // Remember, for blue-contract'd offset modes, the base is compared
+ // against the second endpoint and not the first. So, we compare
+ // against the first if we swapped the endpoints going in.
+ if (swapped) {
+ sq_bc_error =
+ SquaredError(base, endpoint_low_rgba, num_channels) +
+ SquaredError(offset, endpoint_high_rgba, num_channels);
+ } else {
+ sq_bc_error =
+ SquaredError(base, endpoint_high_rgba, num_channels) +
+ SquaredError(offset, endpoint_low_rgba, num_channels);
+ }
+
+ const bool blue_contract = true;
+ const bool offset_mode = true;
+ *(errors_itr++) = CEEncodingOption(sq_bc_error, &pair,
+ swapped, blue_contract, offset_mode);
+ };
+
+ compute_base_offset_blue_contract_error(bc_offset_quantized, false);
+
+ // 3.5 Compute swapped base/offset error.
+ compute_base_offset_error(offset_swapped_quantized, true);
+
+ // 3.6 Compute swapped base/offset blue-contract error.
+ compute_base_offset_blue_contract_error(
+ bc_offset_swapped_quantized, true);
+
+ std::sort(errors.begin(), errors.end(),
+ [](const CEEncodingOption& a, const CEEncodingOption& b) {
+ return a.Error() < b.Error();
+ });
+
+ // 4. Encode the values correspondingly.
+ // For this part, we go through each measurement in order of increasing
+ // error. Based on the properties of each measurement, we decide how to
+ // best encode the quantized endpoints that produced that error value. If
+ // for some reason we cannot encode that metric, then we skip it and move
+ // to the next one.
+ for (const auto& measurement : errors) {
+ bool needs_weight_swap = false;
+ if (measurement.Pack(with_alpha, astc_mode, vals, &needs_weight_swap)) {
+ // Make sure that if we ask for a blue-contract mode that we get it *and*
+ // if we don't ask for it then we don't get it.
+ assert(!(measurement.BlueContract() ^
+ UsesBlueContract(max_value, *astc_mode, *vals)));
+
+ // We encoded what we got.
+ return needs_weight_swap;
+ }
+ }
+
+ assert(false && "Shouldn't have reached this point -- some combination of "
+ "endpoints should be possible to encode!");
+ return false;
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+
+bool UsesBlueContract(int max_value, ColorEndpointMode mode,
+ const std::vector<int>& vals) {
+ assert(vals.size() >= size_t(NumColorValuesForEndpointMode(mode)));
+
+ switch (mode) {
+ case ColorEndpointMode::kLDRRGBDirect:
+ case ColorEndpointMode::kLDRRGBADirect: {
+ constexpr int kNumVals = MaxValuesForModes(
+ ColorEndpointMode::kLDRRGBDirect, ColorEndpointMode::kLDRRGBADirect);
+ std::array<int, kNumVals> v {};
+ std::copy(vals.begin(), vals.end(), v.begin());
+ Unquantize(&v, max_value);
+
+ const int s0 = v[0] + v[2] + v[4];
+ const int s1 = v[1] + v[3] + v[5];
+
+ return s0 > s1;
+ }
+
+ case ColorEndpointMode::kLDRRGBBaseOffset:
+ case ColorEndpointMode::kLDRRGBABaseOffset: {
+ constexpr int kNumVals = MaxValuesForModes(
+ ColorEndpointMode::kLDRRGBBaseOffset,
+ ColorEndpointMode::kLDRRGBABaseOffset);
+ std::array<int, kNumVals> v {};
+ std::copy(vals.begin(), vals.end(), v.begin());
+ Unquantize(&v, max_value);
+
+ BitTransferSigned(&v[1], &v[0]);
+ BitTransferSigned(&v[3], &v[2]);
+ BitTransferSigned(&v[5], &v[4]);
+
+ return v[1] + v[3] + v[5] < 0;
+ }
+
+ default:
+ return false;
+ }
+}
+
+bool EncodeColorsForMode(
+ const RgbaColor& endpoint_low_rgba, const RgbaColor& endpoint_high_rgba,
+ int max_value, EndpointEncodingMode encoding_mode,
+ ColorEndpointMode* const astc_mode, std::vector<int>* const vals) {
+ bool needs_weight_swap = false;
+ vals->resize(NumValuesForEncodingMode(encoding_mode));
+
+ switch (encoding_mode) {
+ case EndpointEncodingMode::kDirectLuma:
+ return EncodeColorsLuma(
+ endpoint_low_rgba, endpoint_high_rgba, max_value, astc_mode, vals);
+
+ case EndpointEncodingMode::kDirectLumaAlpha: {
+ // TODO(google): See if luma-alpha base-offset is better
+ const int avg1 = AverageRGB(endpoint_low_rgba);
+ const int avg2 = AverageRGB(endpoint_high_rgba);
+
+ (*vals)[0] = QuantizeCEValueToRange(avg1, max_value);
+ (*vals)[1] = QuantizeCEValueToRange(avg2, max_value);
+ (*vals)[2] = QuantizeCEValueToRange(endpoint_low_rgba[3], max_value);
+ (*vals)[3] = QuantizeCEValueToRange(endpoint_high_rgba[3], max_value);
+ *astc_mode = ColorEndpointMode::kLDRLumaAlphaDirect;
+ }
+ break;
+
+ case EndpointEncodingMode::kBaseScaleRGB:
+ case EndpointEncodingMode::kBaseScaleRGBA: {
+ RgbaColor base = endpoint_high_rgba;
+ RgbaColor scaled = endpoint_low_rgba;
+
+ // Similar to luma base-offset, the scaled value is strictly less than
+ // the base value here according to the decode procedure. In this case,
+ // if the base is larger than the scale then we need to swap.
+ int num_channels_ge = 0;
+ for (int i = 0; i < 3; ++i) {
+ num_channels_ge +=
+ static_cast<int>(endpoint_high_rgba[i] >= endpoint_low_rgba[i]);
+ }
+
+ if (num_channels_ge < 2) {
+ needs_weight_swap = true;
+ std::swap(base, scaled);
+ }
+
+ // Since the second endpoint is just a direct copy of the RGB values, we
+ // can start by quantizing them.
+ const auto q_base = QuantizeColor(base, max_value);
+ const auto uq_base = UnquantizeColor(q_base, max_value);
+
+ // The first endpoint (scaled) is defined by piecewise multiplying the
+ // second endpoint (base) by the scale factor and then dividing by 256.
+ // This means that the inverse operation is to first piecewise multiply
+ // the first endpoint by 256 and then divide by the unquantized second
+ // endpoint. We take the average of each of each of these scale values as
+ // our final scale value.
+ // TODO(google): Is this the best way to determine the scale factor?
+ int num_samples = 0;
+ int scale_sum = 0;
+ for (int i = 0; i < 3; ++i) {
+ int x = uq_base[i];
+ if (x != 0) {
+ ++num_samples;
+ scale_sum += (scaled[i] * 256) / x;
+ }
+ }
+
+ (*vals)[0] = q_base[0];
+ (*vals)[1] = q_base[1];
+ (*vals)[2] = q_base[2];
+ if (num_samples > 0) {
+ const int avg_scale = Clamp(scale_sum / num_samples, 0, 255);
+ (*vals)[3] = QuantizeCEValueToRange(avg_scale, max_value);
+ } else {
+ // In this case, all of the base values are zero, so we can use whatever
+ // we want as the scale -- it won't affect the outcome.
+ (*vals)[3] = max_value;
+ }
+ *astc_mode = ColorEndpointMode::kLDRRGBBaseScale;
+
+ if (encoding_mode == EndpointEncodingMode::kBaseScaleRGBA) {
+ (*vals)[4] = QuantizeCEValueToRange(scaled[3], max_value);
+ (*vals)[5] = QuantizeCEValueToRange(base[3], max_value);
+ *astc_mode = ColorEndpointMode::kLDRRGBBaseScaleTwoA;
+ }
+ }
+ break;
+
+ case EndpointEncodingMode::kDirectRGB:
+ case EndpointEncodingMode::kDirectRGBA:
+ return EncodeColorsRGBA(
+ endpoint_low_rgba, endpoint_high_rgba, max_value,
+ encoding_mode == EndpointEncodingMode::kDirectRGBA, astc_mode, vals);
+
+ default:
+ assert(false && "Unimplemented color encoding.");
+ }
+
+ return needs_weight_swap;
+}
+
+// These decoding procedures follow the code outlined in Section C.2.14 of
+// the ASTC specification.
+void DecodeColorsForMode(const std::vector<int>& vals,
+ int max_value, ColorEndpointMode mode,
+ RgbaColor* const endpoint_low_rgba,
+ RgbaColor* const endpoint_high_rgba) {
+ assert(vals.size() >= size_t(NumColorValuesForEndpointMode(mode)));
+ switch (mode) {
+ case ColorEndpointMode::kLDRLumaDirect: {
+ const int l0 = UnquantizeCEValueFromRange(vals[0], max_value);
+ const int l1 = UnquantizeCEValueFromRange(vals[1], max_value);
+
+ *endpoint_low_rgba = {{ l0, l0, l0, 255 }};
+ *endpoint_high_rgba = {{ l1, l1, l1, 255 }};
+ }
+ break;
+
+ case ColorEndpointMode::kLDRLumaBaseOffset: {
+ const int v0 = UnquantizeCEValueFromRange(vals[0], max_value);
+ const int v1 = UnquantizeCEValueFromRange(vals[1], max_value);
+
+ const int l0 = (v0 >> 2) | (v1 & 0xC0);
+ const int l1 = std::min(l0 + (v1 & 0x3F), 0xFF);
+
+ *endpoint_low_rgba = {{ l0, l0, l0, 255 }};
+ *endpoint_high_rgba = {{ l1, l1, l1, 255 }};
+ }
+ break;
+
+ case ColorEndpointMode::kLDRLumaAlphaDirect: {
+ constexpr int kNumVals =
+ NumColorValuesForEndpointMode(ColorEndpointMode::kLDRLumaAlphaDirect);
+
+ std::array<int, kNumVals> v;
+ std::copy(vals.begin(), vals.end(), v.begin());
+ Unquantize(&v, max_value);
+
+ *endpoint_low_rgba = {{ v[0], v[0], v[0], v[2] }};
+ *endpoint_high_rgba = {{ v[1], v[1], v[1], v[3] }};
+ }
+ break;
+
+ case ColorEndpointMode::kLDRLumaAlphaBaseOffset: {
+ constexpr int kNumVals = NumColorValuesForEndpointMode(
+ ColorEndpointMode::kLDRLumaAlphaBaseOffset);
+
+ std::array<int, kNumVals> v;
+ std::copy(vals.begin(), vals.end(), v.begin());
+ Unquantize(&v, max_value);
+
+ BitTransferSigned(&v[1], &v[0]);
+ BitTransferSigned(&v[3], &v[2]);
+
+ *endpoint_low_rgba = {{ v[0], v[0], v[0], v[2] }};
+ const int high_luma = v[0] + v[1];
+ *endpoint_high_rgba = {{ high_luma, high_luma, high_luma, v[2] + v[3] }};
+
+ for (auto& c : *endpoint_low_rgba) { c = Clamp(c, 0, 255); }
+ for (auto& c : *endpoint_high_rgba) { c = Clamp(c, 0, 255); }
+ }
+ break;
+
+ case ColorEndpointMode::kLDRRGBBaseScale: {
+ constexpr int kNumVals =
+ NumColorValuesForEndpointMode(ColorEndpointMode::kLDRRGBBaseScale);
+
+ std::array<int, kNumVals> v;
+ std::copy(vals.begin(), vals.end(), v.begin());
+ Unquantize(&v, max_value);
+
+ *endpoint_high_rgba = {{ v[0], v[1], v[2], 255 }};
+ for (int i = 0; i < 3; ++i) {
+ const int x = endpoint_high_rgba->at(i);
+ endpoint_low_rgba->at(i) = (x * v[3]) >> 8;
+ }
+ endpoint_low_rgba->at(3) = 255;
+ }
+ break;
+
+ case ColorEndpointMode::kLDRRGBDirect: {
+ constexpr int kNumVals =
+ NumColorValuesForEndpointMode(ColorEndpointMode::kLDRRGBDirect);
+
+ std::array<int, kNumVals> v;
+ std::copy(vals.begin(), vals.end(), v.begin());
+ Unquantize(&v, max_value);
+
+ const int s0 = v[0] + v[2] + v[4];
+ const int s1 = v[1] + v[3] + v[5];
+
+ *endpoint_low_rgba = {{ v[0], v[2], v[4], 255 }};
+ *endpoint_high_rgba = {{ v[1], v[3], v[5], 255 }};
+
+ if (s1 < s0) {
+ std::swap(*endpoint_low_rgba, *endpoint_high_rgba);
+ BlueContract(endpoint_low_rgba);
+ BlueContract(endpoint_high_rgba);
+ }
+ }
+ break;
+
+ case ColorEndpointMode::kLDRRGBBaseOffset: {
+ constexpr int kNumVals =
+ NumColorValuesForEndpointMode(ColorEndpointMode::kLDRRGBBaseOffset);
+
+ std::array<int, kNumVals> v;
+ std::copy(vals.begin(), vals.end(), v.begin());
+ Unquantize(&v, max_value);
+
+ BitTransferSigned(&v[1], &v[0]);
+ BitTransferSigned(&v[3], &v[2]);
+ BitTransferSigned(&v[5], &v[4]);
+
+ *endpoint_low_rgba = {{ v[0], v[2], v[4], 255 }};
+ *endpoint_high_rgba = {{ v[0] + v[1], v[2] + v[3], v[4] + v[5], 255 }};
+
+ if (v[1] + v[3] + v[5] < 0) {
+ std::swap(*endpoint_low_rgba, *endpoint_high_rgba);
+ BlueContract(endpoint_low_rgba);
+ BlueContract(endpoint_high_rgba);
+ }
+
+ for (auto& c : *endpoint_low_rgba) { c = Clamp(c, 0, 255); }
+ for (auto& c : *endpoint_high_rgba) { c = Clamp(c, 0, 255); }
+ }
+ break;
+
+ case ColorEndpointMode::kLDRRGBBaseScaleTwoA: {
+ constexpr int kNumVals = NumColorValuesForEndpointMode(
+ ColorEndpointMode::kLDRRGBBaseScaleTwoA);
+
+ std::array<int, kNumVals> v;
+ std::copy(vals.begin(), vals.end(), v.begin());
+ Unquantize(&v, max_value);
+
+ // Base
+ *endpoint_low_rgba = *endpoint_high_rgba = {{ v[0], v[1], v[2], 255 }};
+
+ // Scale
+ for (int i = 0; i < 3; ++i) {
+ auto& x = endpoint_low_rgba->at(i);
+ x = (x * v[3]) >> 8;
+ }
+
+ // Two A
+ endpoint_low_rgba->at(3) = v[4];
+ endpoint_high_rgba->at(3) = v[5];
+ }
+ break;
+
+ case ColorEndpointMode::kLDRRGBADirect: {
+ constexpr int kNumVals =
+ NumColorValuesForEndpointMode(ColorEndpointMode::kLDRRGBADirect);
+
+ std::array<int, kNumVals> v;
+ std::copy(vals.begin(), vals.end(), v.begin());
+ Unquantize(&v, max_value);
+
+ const int s0 = v[0] + v[2] + v[4];
+ const int s1 = v[1] + v[3] + v[5];
+
+ *endpoint_low_rgba = {{ v[0], v[2], v[4], v[6] }};
+ *endpoint_high_rgba = {{ v[1], v[3], v[5], v[7] }};
+
+ if (s1 < s0) {
+ std::swap(*endpoint_low_rgba, *endpoint_high_rgba);
+ BlueContract(endpoint_low_rgba);
+ BlueContract(endpoint_high_rgba);
+ }
+ }
+ break;
+
+ case ColorEndpointMode::kLDRRGBABaseOffset: {
+ constexpr int kNumVals =
+ NumColorValuesForEndpointMode(ColorEndpointMode::kLDRRGBABaseOffset);
+
+ std::array<int, kNumVals> v;
+ std::copy(vals.begin(), vals.end(), v.begin());
+ Unquantize(&v, max_value);
+
+ BitTransferSigned(&v[1], &v[0]);
+ BitTransferSigned(&v[3], &v[2]);
+ BitTransferSigned(&v[5], &v[4]);
+ BitTransferSigned(&v[7], &v[6]);
+
+ *endpoint_low_rgba = {{ v[0], v[2], v[4], v[6] }};
+ *endpoint_high_rgba = {{
+ v[0] + v[1], v[2] + v[3], v[4] + v[5], v[6] + v[7] }};
+
+ if (v[1] + v[3] + v[5] < 0) {
+ std::swap(*endpoint_low_rgba, *endpoint_high_rgba);
+ BlueContract(endpoint_low_rgba);
+ BlueContract(endpoint_high_rgba);
+ }
+
+ for (auto& c : *endpoint_low_rgba) { c = Clamp(c, 0, 255); }
+ for (auto& c : *endpoint_high_rgba) { c = Clamp(c, 0, 255); }
+ }
+ break;
+
+ default:
+ // Unimplemented color encoding.
+ // TODO(google): Is this the correct error handling?
+ *endpoint_high_rgba = *endpoint_low_rgba = {{ 0, 0, 0, 0 }};
+ }
+}
+
+} // namespace astc_codec
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/endpoint_codec.h b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/endpoint_codec.h
new file mode 100644
index 00000000000..a1232d06ef1
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/endpoint_codec.h
@@ -0,0 +1,90 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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.
+
+#ifndef ASTC_CODEC_DECODER_ENDPOINT_CODEC_H_
+#define ASTC_CODEC_DECODER_ENDPOINT_CODEC_H_
+
+#include "src/decoder/physical_astc_block.h"
+#include "src/decoder/types.h"
+
+#include <array>
+#include <vector>
+
+namespace astc_codec {
+
+// We use a special distinction for encode modes used to pass to the
+// EncodeColorsForMode function below. The reason is that some of the color
+// modes have sub-modes (like blue-contract) that change whether or not it is
+// useful to encode an endpoint pair using one mode versus another. To avoid
+// this problem, we approach the problem of encoding by specifying some
+// high-level encoding modes. These eventually choose one of the low level
+// ColorEndpointModes from Section C.2.14 when used in EncodeColorsForMode.
+enum class EndpointEncodingMode {
+ kDirectLuma,
+ kDirectLumaAlpha,
+ kBaseScaleRGB,
+ kBaseScaleRGBA,
+ kDirectRGB,
+ kDirectRGBA
+};
+
+// Returns the number of values in the encoded endpoint pair after encoding
+// to a specific high-level encoding mode.
+constexpr int NumValuesForEncodingMode(EndpointEncodingMode mode) {
+ return
+ mode == EndpointEncodingMode::kDirectLuma ? 2 :
+ mode == EndpointEncodingMode::kDirectLumaAlpha ? 4 :
+ mode == EndpointEncodingMode::kBaseScaleRGB ? 4 :
+ mode == EndpointEncodingMode::kBaseScaleRGBA ? 6 :
+ mode == EndpointEncodingMode::kDirectRGB ? 6 : 8;
+}
+
+// Fills |vals| with the quantized endpoint colors values defined in the ASTC
+// specification. The values are quantized to the range [0, max_value]. These
+// quantization limits can be obtained by querying the associated functions in
+// integer_sequence_codec. The returned |astc_mode| will be the ASTC mode used
+// to encode the resulting sequence.
+//
+// The |encoding_mode| is used to determine the way that we encode the values.
+// Each encoding mode is used to determine which ASTC mode best corresponds
+// to the pair of endpoints. It is a necessary hint to the encoding function
+// in order to process the endpoints. Each encoding mode gurantees a certain
+// number of values generated per endpoints.
+//
+// The return value will be true if the endpoints have been switched in order to
+// reap the most benefit from the way the hardware decodes the given mode. In
+// this case, the associated weights that interpolate this color must also be
+// switched. In other words, for each w, it should change to 64 - w.
+bool EncodeColorsForMode(
+ const RgbaColor& endpoint_low_rgba, const RgbaColor& endpoint_high_rgba,
+ int max_value, EndpointEncodingMode encoding_mode,
+ ColorEndpointMode* astc_mode, std::vector<int>* vals);
+
+// Decodes the color values quantized to the range [0, max_value] into RGBA
+// endpoints for the given mode. This function is the inverse of
+// EncodeColorsForMode -- see that function for details. This function should
+// work on all LDR endpoint modes, but no HDR modes.
+void DecodeColorsForMode(const std::vector<int>& vals,
+ int max_value, ColorEndpointMode mode,
+ RgbaColor* endpoint_low_rgba,
+ RgbaColor* endpoint_high_rgba);
+
+// Returns true if the quantized |vals| in the range [0, max_value] use the
+// 'blue_contract' modification during decoding for the given |mode|.
+bool UsesBlueContract(int max_value, ColorEndpointMode mode,
+ const std::vector<int>& vals);
+
+} // namespace astc_codec
+
+#endif // ASTC_CODEC_DECODER_ENDPOINT_CODEC_H_
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/footprint.cc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/footprint.cc
new file mode 100644
index 00000000000..e4f076e3ae7
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/footprint.cc
@@ -0,0 +1,162 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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 "src/decoder/footprint.h"
+#include "src/base/string_utils.h"
+
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace astc_codec {
+
+namespace {
+
+// Encodes the width and height into an integer so that we can use a switch
+// statement instead of a costly lookup map.
+constexpr int EncodeDims(int width, int height) {
+ return (width << 16) | height;
+}
+
+} // namespace
+
+base::Optional<FootprintType>
+Footprint::GetValidFootprintForDimensions(int width, int height) {
+ switch (EncodeDims(width, height)) {
+ case EncodeDims(4, 4): return FootprintType::k4x4;
+ case EncodeDims(5, 4): return FootprintType::k5x4;
+ case EncodeDims(5, 5): return FootprintType::k5x5;
+ case EncodeDims(6, 5): return FootprintType::k6x5;
+ case EncodeDims(6, 6): return FootprintType::k6x6;
+ case EncodeDims(8, 5): return FootprintType::k8x5;
+ case EncodeDims(8, 6): return FootprintType::k8x6;
+ case EncodeDims(8, 8): return FootprintType::k8x8;
+ case EncodeDims(10, 5): return FootprintType::k10x5;
+ case EncodeDims(10, 6): return FootprintType::k10x6;
+ case EncodeDims(10, 8): return FootprintType::k10x8;
+ case EncodeDims(10, 10): return FootprintType::k10x10;
+ case EncodeDims(12, 10): return FootprintType::k12x10;
+ case EncodeDims(12, 12): return FootprintType::k12x12;
+ default: return {};
+ }
+}
+
+int Footprint::GetWidthForFootprint(FootprintType footprint) {
+ switch (footprint) {
+ case FootprintType::k4x4: return 4;
+ case FootprintType::k5x4: return 5;
+ case FootprintType::k5x5: return 5;
+ case FootprintType::k6x5: return 6;
+ case FootprintType::k6x6: return 6;
+ case FootprintType::k8x5: return 8;
+ case FootprintType::k8x6: return 8;
+ case FootprintType::k10x5: return 10;
+ case FootprintType::k10x6: return 10;
+ case FootprintType::k8x8: return 8;
+ case FootprintType::k10x8: return 10;
+ case FootprintType::k10x10: return 10;
+ case FootprintType::k12x10: return 12;
+ case FootprintType::k12x12: return 12;
+ default:
+ assert(false);
+ return -1;
+ }
+}
+
+int Footprint::GetHeightForFootprint(FootprintType footprint) {
+ switch (footprint) {
+ case FootprintType::k4x4: return 4;
+ case FootprintType::k5x4: return 4;
+ case FootprintType::k5x5: return 5;
+ case FootprintType::k6x5: return 5;
+ case FootprintType::k6x6: return 6;
+ case FootprintType::k8x5: return 5;
+ case FootprintType::k8x6: return 6;
+ case FootprintType::k10x5: return 5;
+ case FootprintType::k10x6: return 6;
+ case FootprintType::k8x8: return 8;
+ case FootprintType::k10x8: return 8;
+ case FootprintType::k10x10: return 10;
+ case FootprintType::k12x10: return 10;
+ case FootprintType::k12x12: return 12;
+ default:
+ assert(false);
+ return -1;
+ }
+}
+
+Footprint::Footprint(FootprintType footprint)
+ : footprint_(footprint), width_(GetWidthForFootprint(footprint)),
+ height_(GetHeightForFootprint(footprint)) { }
+
+////////////////////////////////////////////////////////////////////////////////
+
+base::Optional<Footprint> Footprint::Parse(const char* footprint_string) {
+ assert(footprint_string && footprint_string[0] != '\0');
+
+ std::vector<std::string> dimension_strings;
+ base::Split(footprint_string, "x", [&dimension_strings](std::string&& s) {
+ dimension_strings.push_back(std::move(s));
+ });
+
+ if (dimension_strings.size() != 2) {
+ assert(false && "Invalid format for footprint");
+ return {};
+ }
+
+ const int width = base::ParseInt32(dimension_strings[0].c_str(), 0);
+ const int height = base::ParseInt32(dimension_strings[1].c_str(), 0);
+
+ assert(width > 0 && height > 0 && "Invalid width or height.");
+
+ return FromDimensions(width, height);
+}
+
+base::Optional<Footprint> Footprint::FromDimensions(int width, int height) {
+ base::Optional<FootprintType> valid_footprint =
+ GetValidFootprintForDimensions(width, height);
+ if (valid_footprint) {
+ return Footprint(valid_footprint.value());
+ } else {
+ return {};
+ }
+}
+
+// Returns a Footprint for the given FootprintType.
+base::Optional<Footprint> Footprint::FromFootprintType(FootprintType type) {
+ if (type >= FootprintType::k4x4 && type < FootprintType::kCount) {
+ return Footprint(type);
+ } else {
+ return {};
+ }
+}
+
+size_t Footprint::StorageRequirements(int width, int height) const {
+ const int blocks_wide = (width + width_ - 1) / width_;
+ const int blocks_high = (height + height_ - 1) / height_;
+
+ constexpr size_t kASTCBlockSizeInBytes = 16;
+ return blocks_wide * blocks_high * kASTCBlockSizeInBytes;
+}
+
+// Returns bits/pixel for a given footprint.
+float Footprint::Bitrate() const {
+ const int kASTCBlockBitCount = 128;
+ const int footprint_pixel_count = width_ * height_;
+ return static_cast<float>(kASTCBlockBitCount) /
+ static_cast<float>(footprint_pixel_count);
+}
+
+} // namespace astc_codec
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/footprint.h b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/footprint.h
new file mode 100644
index 00000000000..47302cc3950
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/footprint.h
@@ -0,0 +1,106 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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.
+
+#ifndef ASTC_CODEC_DECODER_FOOTPRINT_H_
+#define ASTC_CODEC_DECODER_FOOTPRINT_H_
+
+#include "include/astc-codec/astc-codec.h"
+#include "src/base/optional.h"
+
+#include <cstddef>
+
+namespace astc_codec {
+
+// An ASTC texture can be encoded with varying choices in block size. A set of
+// predefined block sizes are specified in the ASTC specification. These are
+// referred to in the literature as "footprints" available to an encoder when
+// constructing an ASTC bitstream. This class provides various utility functions
+// for interacting with these footprints.
+class Footprint {
+ public:
+ Footprint() = delete;
+ Footprint(const Footprint& footprint) = default;
+
+ // Return the footprint type.
+ FootprintType Type() const { return footprint_; }
+
+ // Return logical descriptions of the dimensions.
+ int Width() const { return width_; }
+ int Height() const { return height_; }
+
+ // Returns the number of pixels for a block with this footprint.
+ int NumPixels() const { return width_ * height_; }
+
+ // Returns the number of bytes needed to store an ASTC encoded image with the
+ // given width and height.
+ size_t StorageRequirements(int width, int height) const;
+
+ // Returns the number of bits used per pixel.
+ float Bitrate() const;
+
+ static constexpr int NumValidFootprints() {
+ return static_cast<int>(FootprintType::kCount);
+ }
+
+ bool operator==(const Footprint& other) const {
+ return footprint_ == other.footprint_;
+ }
+
+ // These are the valid and available ASTC footprints.
+ static Footprint Get4x4() { return Footprint(FootprintType::k4x4); }
+ static Footprint Get5x4() { return Footprint(FootprintType::k5x4); }
+ static Footprint Get5x5() { return Footprint(FootprintType::k5x5); }
+ static Footprint Get6x5() { return Footprint(FootprintType::k6x5); }
+ static Footprint Get6x6() { return Footprint(FootprintType::k6x6); }
+ static Footprint Get8x5() { return Footprint(FootprintType::k8x5); }
+ static Footprint Get8x6() { return Footprint(FootprintType::k8x6); }
+ static Footprint Get8x8() { return Footprint(FootprintType::k8x8); }
+ static Footprint Get10x5() { return Footprint(FootprintType::k10x5); }
+ static Footprint Get10x6() { return Footprint(FootprintType::k10x6); }
+ static Footprint Get10x8() { return Footprint(FootprintType::k10x8); }
+ static Footprint Get10x10() { return Footprint(FootprintType::k10x10); }
+ static Footprint Get12x10() { return Footprint(FootprintType::k12x10); }
+ static Footprint Get12x12() { return Footprint(FootprintType::k12x12); }
+
+ // Constructs a footprint from a string of the form "NxM", or no value if
+ // width and height are not a valid footprint.
+ static base::Optional<Footprint> Parse(const char* footprint_string);
+
+ // Returns a footprint corresponding to a block of the given width and height,
+ // or no value if it does not.
+ static base::Optional<Footprint> FromDimensions(int width, int height);
+
+ // Returns a Footprint for the given FootprintType.
+ static base::Optional<Footprint> FromFootprintType(FootprintType type);
+
+ private:
+ // The only constructor.
+ explicit Footprint(FootprintType footprint);
+
+ // Returns the valid footprint for the width and height if possible.
+ static base::Optional<FootprintType> GetValidFootprintForDimensions(
+ int width, int height);
+
+ // Returns the associated dimension for the given valid footprint.
+ static int GetWidthForFootprint(FootprintType footprint);
+ static int GetHeightForFootprint(FootprintType footprint);
+
+ FootprintType footprint_;
+ int width_;
+ int height_;
+};
+
+} // namespace astc_codec
+
+#endif // ASTC_CODEC_DECODER_FOOTPRINT_H_
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/integer_sequence_codec.cc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/integer_sequence_codec.cc
new file mode 100644
index 00000000000..cb17773e155
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/integer_sequence_codec.cc
@@ -0,0 +1,574 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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 "src/decoder/integer_sequence_codec.h"
+#include "src/base/math_utils.h"
+#include "src/base/utils.h"
+
+#include <algorithm>
+#include <iostream>
+
+namespace astc_codec {
+
+namespace {
+
+// Tables of trit and quint encodings generated by the implementation in
+// http://cs/aosp-master/external/skia/src/utils/SkTextureCompressor_ASTC.cpp
+//
+// These tables are used to decode the blocks of values encoded using the ASTC
+// integer sequence encoding. The theory is that five trits (values that can
+// take any number in the range [0, 2]) can take on a total of 3^5 = 243 total
+// values, which can be stored in eight bits. These eight bits are used to
+// decode the five trits based on the ASTC specification in Section C.2.12.
+// For simplicity, we have stored a look-up table here so that we don't need
+// to implement the decoding logic. Similarly, seven bits are used to decode
+// three quints (since 5^3 = 125 < 128).
+static const std::array<int, 5> kTritEncodings[256] = {
+ {{ 0, 0, 0, 0, 0 }}, {{ 1, 0, 0, 0, 0 }}, {{ 2, 0, 0, 0, 0 }},
+ {{ 0, 0, 2, 0, 0 }}, {{ 0, 1, 0, 0, 0 }}, {{ 1, 1, 0, 0, 0 }},
+ {{ 2, 1, 0, 0, 0 }}, {{ 1, 0, 2, 0, 0 }}, {{ 0, 2, 0, 0, 0 }},
+ {{ 1, 2, 0, 0, 0 }}, {{ 2, 2, 0, 0, 0 }}, {{ 2, 0, 2, 0, 0 }},
+ {{ 0, 2, 2, 0, 0 }}, {{ 1, 2, 2, 0, 0 }}, {{ 2, 2, 2, 0, 0 }},
+ {{ 2, 0, 2, 0, 0 }}, {{ 0, 0, 1, 0, 0 }}, {{ 1, 0, 1, 0, 0 }},
+ {{ 2, 0, 1, 0, 0 }}, {{ 0, 1, 2, 0, 0 }}, {{ 0, 1, 1, 0, 0 }},
+ {{ 1, 1, 1, 0, 0 }}, {{ 2, 1, 1, 0, 0 }}, {{ 1, 1, 2, 0, 0 }},
+ {{ 0, 2, 1, 0, 0 }}, {{ 1, 2, 1, 0, 0 }}, {{ 2, 2, 1, 0, 0 }},
+ {{ 2, 1, 2, 0, 0 }}, {{ 0, 0, 0, 2, 2 }}, {{ 1, 0, 0, 2, 2 }},
+ {{ 2, 0, 0, 2, 2 }}, {{ 0, 0, 2, 2, 2 }}, {{ 0, 0, 0, 1, 0 }},
+ {{ 1, 0, 0, 1, 0 }}, {{ 2, 0, 0, 1, 0 }}, {{ 0, 0, 2, 1, 0 }},
+ {{ 0, 1, 0, 1, 0 }}, {{ 1, 1, 0, 1, 0 }}, {{ 2, 1, 0, 1, 0 }},
+ {{ 1, 0, 2, 1, 0 }}, {{ 0, 2, 0, 1, 0 }}, {{ 1, 2, 0, 1, 0 }},
+ {{ 2, 2, 0, 1, 0 }}, {{ 2, 0, 2, 1, 0 }}, {{ 0, 2, 2, 1, 0 }},
+ {{ 1, 2, 2, 1, 0 }}, {{ 2, 2, 2, 1, 0 }}, {{ 2, 0, 2, 1, 0 }},
+ {{ 0, 0, 1, 1, 0 }}, {{ 1, 0, 1, 1, 0 }}, {{ 2, 0, 1, 1, 0 }},
+ {{ 0, 1, 2, 1, 0 }}, {{ 0, 1, 1, 1, 0 }}, {{ 1, 1, 1, 1, 0 }},
+ {{ 2, 1, 1, 1, 0 }}, {{ 1, 1, 2, 1, 0 }}, {{ 0, 2, 1, 1, 0 }},
+ {{ 1, 2, 1, 1, 0 }}, {{ 2, 2, 1, 1, 0 }}, {{ 2, 1, 2, 1, 0 }},
+ {{ 0, 1, 0, 2, 2 }}, {{ 1, 1, 0, 2, 2 }}, {{ 2, 1, 0, 2, 2 }},
+ {{ 1, 0, 2, 2, 2 }}, {{ 0, 0, 0, 2, 0 }}, {{ 1, 0, 0, 2, 0 }},
+ {{ 2, 0, 0, 2, 0 }}, {{ 0, 0, 2, 2, 0 }}, {{ 0, 1, 0, 2, 0 }},
+ {{ 1, 1, 0, 2, 0 }}, {{ 2, 1, 0, 2, 0 }}, {{ 1, 0, 2, 2, 0 }},
+ {{ 0, 2, 0, 2, 0 }}, {{ 1, 2, 0, 2, 0 }}, {{ 2, 2, 0, 2, 0 }},
+ {{ 2, 0, 2, 2, 0 }}, {{ 0, 2, 2, 2, 0 }}, {{ 1, 2, 2, 2, 0 }},
+ {{ 2, 2, 2, 2, 0 }}, {{ 2, 0, 2, 2, 0 }}, {{ 0, 0, 1, 2, 0 }},
+ {{ 1, 0, 1, 2, 0 }}, {{ 2, 0, 1, 2, 0 }}, {{ 0, 1, 2, 2, 0 }},
+ {{ 0, 1, 1, 2, 0 }}, {{ 1, 1, 1, 2, 0 }}, {{ 2, 1, 1, 2, 0 }},
+ {{ 1, 1, 2, 2, 0 }}, {{ 0, 2, 1, 2, 0 }}, {{ 1, 2, 1, 2, 0 }},
+ {{ 2, 2, 1, 2, 0 }}, {{ 2, 1, 2, 2, 0 }}, {{ 0, 2, 0, 2, 2 }},
+ {{ 1, 2, 0, 2, 2 }}, {{ 2, 2, 0, 2, 2 }}, {{ 2, 0, 2, 2, 2 }},
+ {{ 0, 0, 0, 0, 2 }}, {{ 1, 0, 0, 0, 2 }}, {{ 2, 0, 0, 0, 2 }},
+ {{ 0, 0, 2, 0, 2 }}, {{ 0, 1, 0, 0, 2 }}, {{ 1, 1, 0, 0, 2 }},
+ {{ 2, 1, 0, 0, 2 }}, {{ 1, 0, 2, 0, 2 }}, {{ 0, 2, 0, 0, 2 }},
+ {{ 1, 2, 0, 0, 2 }}, {{ 2, 2, 0, 0, 2 }}, {{ 2, 0, 2, 0, 2 }},
+ {{ 0, 2, 2, 0, 2 }}, {{ 1, 2, 2, 0, 2 }}, {{ 2, 2, 2, 0, 2 }},
+ {{ 2, 0, 2, 0, 2 }}, {{ 0, 0, 1, 0, 2 }}, {{ 1, 0, 1, 0, 2 }},
+ {{ 2, 0, 1, 0, 2 }}, {{ 0, 1, 2, 0, 2 }}, {{ 0, 1, 1, 0, 2 }},
+ {{ 1, 1, 1, 0, 2 }}, {{ 2, 1, 1, 0, 2 }}, {{ 1, 1, 2, 0, 2 }},
+ {{ 0, 2, 1, 0, 2 }}, {{ 1, 2, 1, 0, 2 }}, {{ 2, 2, 1, 0, 2 }},
+ {{ 2, 1, 2, 0, 2 }}, {{ 0, 2, 2, 2, 2 }}, {{ 1, 2, 2, 2, 2 }},
+ {{ 2, 2, 2, 2, 2 }}, {{ 2, 0, 2, 2, 2 }}, {{ 0, 0, 0, 0, 1 }},
+ {{ 1, 0, 0, 0, 1 }}, {{ 2, 0, 0, 0, 1 }}, {{ 0, 0, 2, 0, 1 }},
+ {{ 0, 1, 0, 0, 1 }}, {{ 1, 1, 0, 0, 1 }}, {{ 2, 1, 0, 0, 1 }},
+ {{ 1, 0, 2, 0, 1 }}, {{ 0, 2, 0, 0, 1 }}, {{ 1, 2, 0, 0, 1 }},
+ {{ 2, 2, 0, 0, 1 }}, {{ 2, 0, 2, 0, 1 }}, {{ 0, 2, 2, 0, 1 }},
+ {{ 1, 2, 2, 0, 1 }}, {{ 2, 2, 2, 0, 1 }}, {{ 2, 0, 2, 0, 1 }},
+ {{ 0, 0, 1, 0, 1 }}, {{ 1, 0, 1, 0, 1 }}, {{ 2, 0, 1, 0, 1 }},
+ {{ 0, 1, 2, 0, 1 }}, {{ 0, 1, 1, 0, 1 }}, {{ 1, 1, 1, 0, 1 }},
+ {{ 2, 1, 1, 0, 1 }}, {{ 1, 1, 2, 0, 1 }}, {{ 0, 2, 1, 0, 1 }},
+ {{ 1, 2, 1, 0, 1 }}, {{ 2, 2, 1, 0, 1 }}, {{ 2, 1, 2, 0, 1 }},
+ {{ 0, 0, 1, 2, 2 }}, {{ 1, 0, 1, 2, 2 }}, {{ 2, 0, 1, 2, 2 }},
+ {{ 0, 1, 2, 2, 2 }}, {{ 0, 0, 0, 1, 1 }}, {{ 1, 0, 0, 1, 1 }},
+ {{ 2, 0, 0, 1, 1 }}, {{ 0, 0, 2, 1, 1 }}, {{ 0, 1, 0, 1, 1 }},
+ {{ 1, 1, 0, 1, 1 }}, {{ 2, 1, 0, 1, 1 }}, {{ 1, 0, 2, 1, 1 }},
+ {{ 0, 2, 0, 1, 1 }}, {{ 1, 2, 0, 1, 1 }}, {{ 2, 2, 0, 1, 1 }},
+ {{ 2, 0, 2, 1, 1 }}, {{ 0, 2, 2, 1, 1 }}, {{ 1, 2, 2, 1, 1 }},
+ {{ 2, 2, 2, 1, 1 }}, {{ 2, 0, 2, 1, 1 }}, {{ 0, 0, 1, 1, 1 }},
+ {{ 1, 0, 1, 1, 1 }}, {{ 2, 0, 1, 1, 1 }}, {{ 0, 1, 2, 1, 1 }},
+ {{ 0, 1, 1, 1, 1 }}, {{ 1, 1, 1, 1, 1 }}, {{ 2, 1, 1, 1, 1 }},
+ {{ 1, 1, 2, 1, 1 }}, {{ 0, 2, 1, 1, 1 }}, {{ 1, 2, 1, 1, 1 }},
+ {{ 2, 2, 1, 1, 1 }}, {{ 2, 1, 2, 1, 1 }}, {{ 0, 1, 1, 2, 2 }},
+ {{ 1, 1, 1, 2, 2 }}, {{ 2, 1, 1, 2, 2 }}, {{ 1, 1, 2, 2, 2 }},
+ {{ 0, 0, 0, 2, 1 }}, {{ 1, 0, 0, 2, 1 }}, {{ 2, 0, 0, 2, 1 }},
+ {{ 0, 0, 2, 2, 1 }}, {{ 0, 1, 0, 2, 1 }}, {{ 1, 1, 0, 2, 1 }},
+ {{ 2, 1, 0, 2, 1 }}, {{ 1, 0, 2, 2, 1 }}, {{ 0, 2, 0, 2, 1 }},
+ {{ 1, 2, 0, 2, 1 }}, {{ 2, 2, 0, 2, 1 }}, {{ 2, 0, 2, 2, 1 }},
+ {{ 0, 2, 2, 2, 1 }}, {{ 1, 2, 2, 2, 1 }}, {{ 2, 2, 2, 2, 1 }},
+ {{ 2, 0, 2, 2, 1 }}, {{ 0, 0, 1, 2, 1 }}, {{ 1, 0, 1, 2, 1 }},
+ {{ 2, 0, 1, 2, 1 }}, {{ 0, 1, 2, 2, 1 }}, {{ 0, 1, 1, 2, 1 }},
+ {{ 1, 1, 1, 2, 1 }}, {{ 2, 1, 1, 2, 1 }}, {{ 1, 1, 2, 2, 1 }},
+ {{ 0, 2, 1, 2, 1 }}, {{ 1, 2, 1, 2, 1 }}, {{ 2, 2, 1, 2, 1 }},
+ {{ 2, 1, 2, 2, 1 }}, {{ 0, 2, 1, 2, 2 }}, {{ 1, 2, 1, 2, 2 }},
+ {{ 2, 2, 1, 2, 2 }}, {{ 2, 1, 2, 2, 2 }}, {{ 0, 0, 0, 1, 2 }},
+ {{ 1, 0, 0, 1, 2 }}, {{ 2, 0, 0, 1, 2 }}, {{ 0, 0, 2, 1, 2 }},
+ {{ 0, 1, 0, 1, 2 }}, {{ 1, 1, 0, 1, 2 }}, {{ 2, 1, 0, 1, 2 }},
+ {{ 1, 0, 2, 1, 2 }}, {{ 0, 2, 0, 1, 2 }}, {{ 1, 2, 0, 1, 2 }},
+ {{ 2, 2, 0, 1, 2 }}, {{ 2, 0, 2, 1, 2 }}, {{ 0, 2, 2, 1, 2 }},
+ {{ 1, 2, 2, 1, 2 }}, {{ 2, 2, 2, 1, 2 }}, {{ 2, 0, 2, 1, 2 }},
+ {{ 0, 0, 1, 1, 2 }}, {{ 1, 0, 1, 1, 2 }}, {{ 2, 0, 1, 1, 2 }},
+ {{ 0, 1, 2, 1, 2 }}, {{ 0, 1, 1, 1, 2 }}, {{ 1, 1, 1, 1, 2 }},
+ {{ 2, 1, 1, 1, 2 }}, {{ 1, 1, 2, 1, 2 }}, {{ 0, 2, 1, 1, 2 }},
+ {{ 1, 2, 1, 1, 2 }}, {{ 2, 2, 1, 1, 2 }}, {{ 2, 1, 2, 1, 2 }},
+ {{ 0, 2, 2, 2, 2 }}, {{ 1, 2, 2, 2, 2 }}, {{ 2, 2, 2, 2, 2 }},
+ {{ 2, 1, 2, 2, 2 }}
+};
+
+static const std::array<int, 3> kQuintEncodings[128] = {
+ {{ 0, 0, 0 }}, {{ 1, 0, 0 }}, {{ 2, 0, 0 }}, {{ 3, 0, 0 }}, {{ 4, 0, 0 }},
+ {{ 0, 4, 0 }}, {{ 4, 4, 0 }}, {{ 4, 4, 4 }}, {{ 0, 1, 0 }}, {{ 1, 1, 0 }},
+ {{ 2, 1, 0 }}, {{ 3, 1, 0 }}, {{ 4, 1, 0 }}, {{ 1, 4, 0 }}, {{ 4, 4, 1 }},
+ {{ 4, 4, 4 }}, {{ 0, 2, 0 }}, {{ 1, 2, 0 }}, {{ 2, 2, 0 }}, {{ 3, 2, 0 }},
+ {{ 4, 2, 0 }}, {{ 2, 4, 0 }}, {{ 4, 4, 2 }}, {{ 4, 4, 4 }}, {{ 0, 3, 0 }},
+ {{ 1, 3, 0 }}, {{ 2, 3, 0 }}, {{ 3, 3, 0 }}, {{ 4, 3, 0 }}, {{ 3, 4, 0 }},
+ {{ 4, 4, 3 }}, {{ 4, 4, 4 }}, {{ 0, 0, 1 }}, {{ 1, 0, 1 }}, {{ 2, 0, 1 }},
+ {{ 3, 0, 1 }}, {{ 4, 0, 1 }}, {{ 0, 4, 1 }}, {{ 4, 0, 4 }}, {{ 0, 4, 4 }},
+ {{ 0, 1, 1 }}, {{ 1, 1, 1 }}, {{ 2, 1, 1 }}, {{ 3, 1, 1 }}, {{ 4, 1, 1 }},
+ {{ 1, 4, 1 }}, {{ 4, 1, 4 }}, {{ 1, 4, 4 }}, {{ 0, 2, 1 }}, {{ 1, 2, 1 }},
+ {{ 2, 2, 1 }}, {{ 3, 2, 1 }}, {{ 4, 2, 1 }}, {{ 2, 4, 1 }}, {{ 4, 2, 4 }},
+ {{ 2, 4, 4 }}, {{ 0, 3, 1 }}, {{ 1, 3, 1 }}, {{ 2, 3, 1 }}, {{ 3, 3, 1 }},
+ {{ 4, 3, 1 }}, {{ 3, 4, 1 }}, {{ 4, 3, 4 }}, {{ 3, 4, 4 }}, {{ 0, 0, 2 }},
+ {{ 1, 0, 2 }}, {{ 2, 0, 2 }}, {{ 3, 0, 2 }}, {{ 4, 0, 2 }}, {{ 0, 4, 2 }},
+ {{ 2, 0, 4 }}, {{ 3, 0, 4 }}, {{ 0, 1, 2 }}, {{ 1, 1, 2 }}, {{ 2, 1, 2 }},
+ {{ 3, 1, 2 }}, {{ 4, 1, 2 }}, {{ 1, 4, 2 }}, {{ 2, 1, 4 }}, {{ 3, 1, 4 }},
+ {{ 0, 2, 2 }}, {{ 1, 2, 2 }}, {{ 2, 2, 2 }}, {{ 3, 2, 2 }}, {{ 4, 2, 2 }},
+ {{ 2, 4, 2 }}, {{ 2, 2, 4 }}, {{ 3, 2, 4 }}, {{ 0, 3, 2 }}, {{ 1, 3, 2 }},
+ {{ 2, 3, 2 }}, {{ 3, 3, 2 }}, {{ 4, 3, 2 }}, {{ 3, 4, 2 }}, {{ 2, 3, 4 }},
+ {{ 3, 3, 4 }}, {{ 0, 0, 3 }}, {{ 1, 0, 3 }}, {{ 2, 0, 3 }}, {{ 3, 0, 3 }},
+ {{ 4, 0, 3 }}, {{ 0, 4, 3 }}, {{ 0, 0, 4 }}, {{ 1, 0, 4 }}, {{ 0, 1, 3 }},
+ {{ 1, 1, 3 }}, {{ 2, 1, 3 }}, {{ 3, 1, 3 }}, {{ 4, 1, 3 }}, {{ 1, 4, 3 }},
+ {{ 0, 1, 4 }}, {{ 1, 1, 4 }}, {{ 0, 2, 3 }}, {{ 1, 2, 3 }}, {{ 2, 2, 3 }},
+ {{ 3, 2, 3 }}, {{ 4, 2, 3 }}, {{ 2, 4, 3 }}, {{ 0, 2, 4 }}, {{ 1, 2, 4 }},
+ {{ 0, 3, 3 }}, {{ 1, 3, 3 }}, {{ 2, 3, 3 }}, {{ 3, 3, 3 }}, {{ 4, 3, 3 }},
+ {{ 3, 4, 3 }}, {{ 0, 3, 4 }}, {{ 1, 3, 4 }}
+};
+
+// A cached table containing the max ranges for values encoded using ASTC's
+// Bounded Integer Sequence Encoding. These are the numbers between 1 and 255
+// that can be represented exactly as a number in the ranges
+// [0, 2^k), [0, 3 * 2^k), and [0, 5 * 2^k).
+static const std::array<int, kNumPossibleRanges> kMaxRanges = []() {
+ std::array<int, kNumPossibleRanges> ranges;
+
+ // Initialize the table that we need for determining value encodings.
+ auto next_max_range = ranges.begin();
+ auto add_val = [&next_max_range](int val) {
+ if (val <= 0 || (1 << kLog2MaxRangeForBits) <= val) {
+ return;
+ }
+
+ *(next_max_range++) = val;
+ };
+
+ for (int i = 0; i <= kLog2MaxRangeForBits; ++i) {
+ add_val(3 * (1 << i) - 1);
+ add_val(5 * (1 << i) - 1);
+ add_val((1 << i) - 1);
+ }
+
+ assert(std::distance(next_max_range, ranges.end()) == 0);
+ std::sort(ranges.begin(), ranges.end());
+ return ranges;
+}();
+
+// Returns true if x == 0 or if x is a power of two. This function is only used
+// in the GetCountsForRange function, where we need to have it return true
+// on zero since we can have single trit/quint ISE encodings according to
+// Table C.2.7.
+template<typename T,
+ typename std::enable_if<std::is_integral<T>::value, T>::type = 0>
+inline constexpr bool IsPow2(T x) { return (x & (x - 1)) == 0; }
+
+// For the ISE block encoding, these arrays determine how many bits are
+// used after each value to store the interleaved quint/trit block.
+const int kInterleavedQuintBits[3] = { 3, 2, 2 };
+const int kInterleavedTritBits[5] = { 2, 2, 1, 2, 1 };
+
+// Some template meta programming to get around the fact that MSVC
+// will not allow (ValRange == 5) ? 3 : 5 as a template parameter
+template<int ValRange>
+struct DecodeBlockSize {
+ enum { value = (ValRange == 5 ? 3 : 5) };
+};
+
+// Decodes either a trit or quint block using the BISE (Bounded Integer Sequence
+// Encoding) defined in Section C.2.12 of the ASTC specification. ValRange is
+// expected to be either 3 or 5 depending on whether or not we're encoding trits
+// or quints respectively. In other words, it is the remaining factor in whether
+// the passed blocks contain encoded values of the form 3*2^k or 5*2^k.
+template<int ValRange>
+std::array<int, /* kNumVals = */ DecodeBlockSize<ValRange>::value> DecodeISEBlock(
+ uint64_t block_bits, int num_bits) {
+ static_assert(ValRange == 3 || ValRange == 5,
+ "We only know about trits and quints");
+
+ // We either have three quints or five trits
+ constexpr const int kNumVals = (ValRange == 5) ? 3 : 5;
+
+ // Depending on whether or not we're using quints or trits will determine
+ // the positions of the interleaved bits in the encoded block.
+ constexpr const int* const kInterleavedBits =
+ (ValRange == 5) ? kInterleavedQuintBits : kInterleavedTritBits;
+
+ // Set up the bits for reading
+ base::BitStream<base::UInt128> block_bit_src(block_bits, sizeof(block_bits) * 8);
+
+ // Decode the block
+ std::array<int, kNumVals> m;
+ uint64_t encoded = 0;
+ uint32_t encoded_bits_read = 0;
+ for (int i = 0; i < kNumVals; ++i) {
+ {
+ uint64_t bits = 0;
+ const bool result = block_bit_src.GetBits(num_bits, &bits);
+ assert(result);
+ (void)result;
+
+ m[i] = static_cast<int>(bits);
+ }
+
+ uint64_t encoded_bits;
+ {
+ const bool result = block_bit_src.GetBits(kInterleavedBits[i], &encoded_bits);
+ assert(result);
+ (void)result;
+ }
+ encoded |= encoded_bits << encoded_bits_read;
+ encoded_bits_read += kInterleavedBits[i];
+ }
+
+ // Make sure that our encoded trit/quint doesn't exceed its bounds
+ assert(ValRange != 3 || encoded < 256);
+ assert(ValRange != 5 || encoded < 128);
+
+ const int* const kEncodings = (ValRange == 5) ?
+ kQuintEncodings[encoded].data() : kTritEncodings[encoded].data();
+
+ std::array<int, kNumVals> result;
+ for (int i = 0; i < kNumVals; ++i) {
+ assert(m[i] < 1 << num_bits);
+ result[i] = kEncodings[i] << num_bits | m[i];
+ }
+ return result;
+}
+
+// Encode a single trit or quint block using the BISE (Bounded Integer Sequence
+// Encoding) defined in Section C.2.12 of the ASTC specification. ValRange is
+// expected to be either 3 or 5 depending on whether or not we're encoding trits
+// or quints respectively. In other words, it is the remaining factor in whether
+// the passed blocks contain encoded values of the form 3*2^k or 5*2^k.
+template <int ValRange>
+void EncodeISEBlock(const std::vector<int>& vals, int bits_per_val,
+ base::BitStream<base::UInt128>* bit_sink) {
+ static_assert(ValRange == 3 || ValRange == 5,
+ "We only know about trits and quints");
+
+ // We either have three quints or five trits
+ constexpr const int kNumVals = (ValRange == 5) ? 3 : 5;
+
+ // Three quints in seven bits or five trits in eight bits
+ constexpr const int kNumEncodedBitsPerBlock = (ValRange == 5) ? 7 : 8;
+
+ // Depending on whether or not we're using quints or trits will determine
+ // the positions of the interleaved bits in the encoding
+ constexpr const int* const kInterleavedBits =
+ (ValRange == 5) ? kInterleavedQuintBits : kInterleavedTritBits;
+
+ // ISE blocks can only have up to a specific number of values...
+ assert(vals.size() <= kNumVals);
+
+ // Split up into bits and non bits. Non bits are used to find the quint/trit
+ // encoding that we need.
+ std::array<int, kNumVals> non_bits = {{ 0 }};
+ std::array<int, kNumVals> bits = {{ 0 }};
+ for (size_t i = 0; i < vals.size(); ++i) {
+ bits[i] = vals[i] & ((1 << bits_per_val) - 1);
+ non_bits[i] = vals[i] >> bits_per_val;
+ assert(non_bits[i] < ValRange);
+ }
+
+ // We only need to add as many bits as necessary, so let's limit it based
+ // on the computation described in Section C.2.22 of the ASTC specification
+ const int total_num_bits =
+ int(((vals.size() * kNumEncodedBitsPerBlock + kNumVals - 1) / kNumVals)
+ + vals.size() * bits_per_val);
+ int bits_added = 0;
+
+ // The number of bits used for the quint/trit encoding is necessary to know
+ // in order to properly select the encoding we need to represent.
+ int num_encoded_bits = 0;
+ for (int i = 0; i < kNumVals; ++i) {
+ bits_added += bits_per_val;
+ if (bits_added >= total_num_bits) {
+ break;
+ }
+
+ num_encoded_bits += kInterleavedBits[i];
+ bits_added += kInterleavedBits[i];
+ if (bits_added >= total_num_bits) {
+ break;
+ }
+ }
+ bits_added = 0;
+ assert(num_encoded_bits <= kNumEncodedBitsPerBlock);
+
+ // TODO(google): The faster way to do this would be to construct trees out
+ // of the quint/trit encoding patterns, or just invert the decoding logic.
+ // Here we go from the end backwards because it makes our tests are more
+ // deterministic.
+ int non_bit_encoding = -1;
+ for (int j = (1 << num_encoded_bits) - 1; j >= 0; --j) {
+ bool matches = true;
+
+ // We don't need to match all trits here, just the ones that correspond
+ // to the values that we passed in
+ for (size_t i = 0; i < kNumVals; ++i) {
+ if ((ValRange == 5 && kQuintEncodings[j][i] != non_bits[i]) ||
+ (ValRange == 3 && kTritEncodings[j][i] != non_bits[i])) {
+ matches = false;
+ break;
+ }
+ }
+
+ if (matches) {
+ non_bit_encoding = j;
+ break;
+ }
+ }
+
+ assert(non_bit_encoding >= 0);
+
+ // Now pack the bits into the block
+ for (size_t i = 0; i < vals.size(); ++i) {
+ // First add the base bits for this value
+ if (bits_added + bits_per_val <= total_num_bits) {
+ bit_sink->PutBits(bits[i], bits_per_val);
+ bits_added += bits_per_val;
+ }
+
+ // Now add the interleaved bits from the quint/trit
+ int num_int_bits = kInterleavedBits[i];
+ int int_bits = non_bit_encoding & ((1 << num_int_bits) - 1);
+ if (bits_added + num_int_bits <= total_num_bits) {
+ bit_sink->PutBits(int_bits, num_int_bits);
+ bits_added += num_int_bits;
+ non_bit_encoding >>= num_int_bits;
+ }
+ }
+}
+
+inline void CHECK_COUNTS(int trits, int quints) {
+ assert(trits == 0 || quints == 0); // Either trits or quints
+ assert(trits == 0 || trits == 1); // At most one trit
+ assert(quints == 0 || quints == 1); // At most one quint
+ (void)trits; (void)quints;
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+
+std::array<int, kNumPossibleRanges>::const_iterator ISERangeBegin() {
+ return kMaxRanges.cbegin();
+}
+
+std::array<int, kNumPossibleRanges>::const_iterator ISERangeEnd() {
+ return kMaxRanges.cend();
+}
+
+void IntegerSequenceCodec::GetCountsForRange(
+ int range, int* const trits, int* const quints, int* const bits) {
+ // Make sure the passed pointers are valid
+ assert(trits != nullptr);
+ assert(quints != nullptr);
+ assert(bits != nullptr);
+
+ // These are generally errors -- there should never be any ASTC values
+ // outside of this range
+ UTILS_RELEASE_ASSERT(range > 0);
+ UTILS_RELEASE_ASSERT(range < 1 << kLog2MaxRangeForBits);
+
+ *bits = 0;
+ *trits = 0;
+ *quints = 0;
+
+ // Search through the numbers of the form 2^n, 3 * 2^n and 5 * 2^n
+ const int max_vals_for_range =
+ *std::lower_bound(kMaxRanges.begin(), kMaxRanges.end(), range) + 1;
+
+ // Make sure we found something
+ assert(max_vals_for_range > 1);
+
+ // Find out what kind of range it is
+ if ((max_vals_for_range % 3 == 0) && IsPow2(max_vals_for_range / 3)) {
+ *bits = base::Log2Floor(max_vals_for_range / 3);
+ *trits = 1;
+ *quints = 0;
+ } else if ((max_vals_for_range % 5 == 0) && IsPow2(max_vals_for_range / 5)) {
+ *bits = base::Log2Floor(max_vals_for_range / 5);
+ *trits = 0;
+ *quints = 1;
+ } else if (IsPow2(max_vals_for_range)) {
+ *bits = base::Log2Floor(max_vals_for_range);
+ *trits = 0;
+ *quints = 0;
+ }
+
+ // If we set any of these values then we're done.
+ if ((*bits | *trits | *quints) != 0) {
+ CHECK_COUNTS(*trits, *quints);
+ }
+}
+
+// Returns the overall bit count for a range of val_count values encoded
+// using the specified number of trits, quints and straight bits (respectively)
+int IntegerSequenceCodec::GetBitCount(int num_vals,
+ int trits, int quints, int bits) {
+ CHECK_COUNTS(trits, quints);
+
+ // See section C.2.22 for the formula used here.
+ const int trit_bit_count = ((num_vals * 8 * trits) + 4) / 5;
+ const int quint_bit_count = ((num_vals * 7 * quints) + 2) / 3;
+ const int base_bit_count = num_vals * bits;
+ return trit_bit_count + quint_bit_count + base_bit_count;
+}
+
+IntegerSequenceCodec::IntegerSequenceCodec(int range) {
+ int trits, quints, bits;
+ GetCountsForRange(range, &trits, &quints, &bits);
+ InitializeWithCounts(trits, quints, bits);
+}
+
+IntegerSequenceCodec::IntegerSequenceCodec(
+ int trits, int quints, int bits) {
+ InitializeWithCounts(trits, quints, bits);
+}
+
+void IntegerSequenceCodec::InitializeWithCounts(
+ int trits, int quints, int bits) {
+ CHECK_COUNTS(trits, quints);
+
+ if (trits > 0) {
+ encoding_ = EncodingMode::kTritEncoding;
+ } else if (quints > 0) {
+ encoding_ = EncodingMode::kQuintEncoding;
+ } else {
+ encoding_ = EncodingMode::kBitEncoding;
+ }
+
+ bits_ = bits;
+}
+
+int IntegerSequenceCodec::NumValsPerBlock() const {
+ const std::array<int, 3> kNumValsByEncoding = {{ 5, 3, 1 }};
+ return kNumValsByEncoding[static_cast<int>(encoding_)];
+}
+
+int IntegerSequenceCodec::EncodedBlockSize() const {
+ const std::array<int, 3> kExtraBlockSizeByEncoding = {{ 8, 7, 0 }};
+ const int num_vals = NumValsPerBlock();
+ return kExtraBlockSizeByEncoding[static_cast<int>(encoding_)]
+ + num_vals * bits_;
+}
+
+std::vector<int> IntegerSequenceDecoder::Decode(
+ int num_vals, base::BitStream<base::UInt128> *bit_src) const {
+ int trits = (encoding_ == kTritEncoding)? 1 : 0;
+ int quints = (encoding_ == kQuintEncoding)? 1 : 0;
+ const int total_num_bits = GetBitCount(num_vals, trits, quints, bits_);
+ const int bits_per_block = EncodedBlockSize();
+ assert(bits_per_block < 64);
+
+ int bits_left = total_num_bits;
+ std::vector<int> result;
+ while (bits_left > 0) {
+ uint64_t block_bits;
+ {
+ const bool result0 = bit_src->GetBits(std::min(bits_left, bits_per_block), &block_bits);
+ assert(result0);
+ (void)result0;
+ }
+
+ switch (encoding_) {
+ case kTritEncoding: {
+ auto trit_vals = DecodeISEBlock<3>(block_bits, bits_);
+ result.insert(result.end(), trit_vals.begin(), trit_vals.end());
+ }
+ break;
+
+ case kQuintEncoding: {
+ auto quint_vals = DecodeISEBlock<5>(block_bits, bits_);
+ result.insert(result.end(), quint_vals.begin(), quint_vals.end());
+ }
+ break;
+
+ case kBitEncoding:
+ result.push_back(static_cast<int>(block_bits));
+ break;
+ }
+
+ bits_left -= bits_per_block;
+ }
+
+ // Resize result to only contain as many values as requested
+ assert(result.size() >= static_cast<size_t>(num_vals));
+ result.resize(num_vals);
+
+ // Encoded all the values
+ return result;
+}
+
+void IntegerSequenceEncoder::Encode(base::BitStream<base::UInt128>* bit_sink) const {
+ // Go through all of the values and chop them up into blocks. The properties
+ // of the trit and quint encodings mean that if we need to encode fewer values
+ // in a block than the number of values encoded in the block then we need to
+ // consider the last few values to be zero.
+
+ auto next_val = vals_.begin();
+ while (next_val != vals_.end()) {
+ switch (encoding_) {
+ case kTritEncoding: {
+ std::vector<int> trit_vals;
+ for (int i = 0; i < 5; ++i) {
+ if (next_val != vals_.end()) {
+ trit_vals.push_back(*next_val);
+ ++next_val;
+ }
+ }
+
+ EncodeISEBlock<3>(trit_vals, bits_, bit_sink);
+ }
+ break;
+
+ case kQuintEncoding: {
+ std::vector<int> quint_vals;
+ for (int i = 0; i < 3; ++i) {
+ if (next_val != vals_.end()) {
+ quint_vals.push_back(*next_val);
+ ++next_val;
+ }
+ }
+
+ EncodeISEBlock<5>(quint_vals, bits_, bit_sink);
+ }
+ break;
+
+ case kBitEncoding: {
+ bit_sink->PutBits(*next_val, EncodedBlockSize());
+ ++next_val;
+ }
+ break;
+ }
+ }
+}
+
+} // namespace astc_codec
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/integer_sequence_codec.h b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/integer_sequence_codec.h
new file mode 100644
index 00000000000..a815e096634
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/integer_sequence_codec.h
@@ -0,0 +1,169 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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.
+
+#ifndef ASTC_CODEC_DECODER_INTEGER_SEQUENCE_CODEC_H_
+#define ASTC_CODEC_DECODER_INTEGER_SEQUENCE_CODEC_H_
+
+#include "src/base/bit_stream.h"
+#include "src/base/uint128.h"
+
+#include <array>
+#include <string>
+#include <vector>
+
+namespace astc_codec {
+
+// The maximum number of bits that we would need to encode an ISE value. The
+// ASTC specification does not give a maximum number, however unquantized color
+// values have a maximum range of 255, meaning that we can't feasibly have more
+// than eight bits per value.
+constexpr int kLog2MaxRangeForBits = 8;
+
+// Ranges can take any of the the forms 2^k, 3*2^k, or 5*2^k for k up to
+// kLog2MaxRangeForBits. Hence we have three types of ranges. Since the
+// maximum encoded value is 255, k won't go larger than 8. We don't have quints
+// that accompany [6, 8]-bits, as (5 * 2^6 = 320 > 255) and we don't have trits
+// that accompany [7, 8]-bits, as (3 * 2^7 = 384 > 255). But we do have trits
+// and quints that accompany no bits. Hence we have a total of
+// 3 * kLog2MaxRangeForBits - 3 - 2 + 2 total ranges.
+constexpr int kNumPossibleRanges = 3 * kLog2MaxRangeForBits - 3;
+
+// Returns an iterator through the available ASTC ranges.
+std::array<int, kNumPossibleRanges>::const_iterator ISERangeBegin();
+std::array<int, kNumPossibleRanges>::const_iterator ISERangeEnd();
+
+// Base class for ASTC integer sequence encoders and decoders. These codecs
+// operate on sequences of integers and produce bit patterns that pack the
+// integers based on the encoding scheme specified in the ASTC specification
+// Section C.2.12. The resulting bit pattern is a sequence of encoded blocks.
+// All blocks in a sequence are one of the following encodings:
+//
+// (1 -- bit encoding) one encoded value of the form 2^k
+// (2 -- trit encoding) five encoded values of the form 3*2^k
+// (3 -- quint encoding) three encoded values of the form 5*2^k
+//
+// The layouts of each block are designed such that the blocks can be truncated
+// during encoding in order to support variable length input sequences (i.e. a
+// sequence of values that are encoded using trit encoded blocks does not
+// need to have a multiple-of-five length).
+class IntegerSequenceCodec {
+ public:
+ // Returns the number of trits, quints, and bits needed to encode values in
+ // [0, range]. This is used to determine the layout of ISE encoded bit
+ // streams. The returned array holds the number of trits, quints, and bits
+ // respectively. range is expected to be within the interval [1, 5242879]
+ static void GetCountsForRange(int range, int* trits, int* quints, int* bits);
+
+ // Returns the number of bits needed to encode the given number of values with
+ // respect to the number of trits, quints, and bits specified in ise_counts
+ // (in that order). It is expected that either trits or quints can be
+ // nonzero, but not both, and neither can be larger than one. Anything else is
+ // undefined.
+ static int GetBitCount(int num_vals, int trits, int quints, int bits);
+
+ // Convenience function that returns the number of bits needed to encoded
+ // num_vals within the range [0, range] (inclusive).
+ static inline int GetBitCountForRange(int num_vals, int range) {
+ int trits, quints, bits;
+ GetCountsForRange(range, &trits, &quints, &bits);
+ return GetBitCount(num_vals, trits, quints, bits);
+ }
+
+ protected:
+ explicit IntegerSequenceCodec(int range);
+ IntegerSequenceCodec(int trits, int quints, int bits);
+
+ // The encoding mode -- since having trits and quints are mutually exclusive,
+ // we can store the encoding we decide on in this enum.
+ enum EncodingMode {
+ kTritEncoding = 0,
+ kQuintEncoding,
+ kBitEncoding,
+ };
+
+ EncodingMode encoding_;
+ int bits_;
+
+ // Returns the number of values stored in a single ISE block. Since quints and
+ // trits are packed three/five to a bit pattern (respectively), each sequence
+ // is chunked into blocks in order to encode it. For only bit-encodings, the
+ // block size is one.
+ int NumValsPerBlock() const;
+
+ // Returns the size of a single ISE block in bits (see NumValsPerBlock).
+ int EncodedBlockSize() const;
+
+ private:
+ // Determines the encoding mode.
+ void InitializeWithCounts(int trits, int quints, int bits);
+};
+
+// The integer sequence decoder. The decoder only remembers the given encoding
+// but each invocation of Decode operates independently on the input bits.
+class IntegerSequenceDecoder : public IntegerSequenceCodec {
+ public:
+ // Creates a decoder that decodes values within [0, range] (inclusive).
+ explicit IntegerSequenceDecoder(int range)
+ : IntegerSequenceCodec(range) { }
+
+ // Creates a decoder based on the number of trits, quints, and bits expected
+ // in the bit stream passed to Decode.
+ IntegerSequenceDecoder(int trits, int quints, int bits)
+ : IntegerSequenceCodec(trits, quints, bits) { }
+
+ // Decodes num_vals from the bit_src. The number of bits read is dependent
+ // on the number of bits required to encode num_vals based on the calculation
+ // provided in Section C.2.22 of the ASTC specification. The return value
+ // always contains exactly num_vals.
+ std::vector<int> Decode(int num_vals,
+ base::BitStream<base::UInt128>* bit_src) const;
+};
+
+// The integer sequence encoder. The encoder accepts values one by one and
+// places them into a temporary array that it holds. When needed the user
+// may call Encode to produce an encoded bit stream of the associated values.
+class IntegerSequenceEncoder : public IntegerSequenceCodec {
+ public:
+ // Creates an encoder that encodes values within [0, range] (inclusive).
+ explicit IntegerSequenceEncoder(int range)
+ : IntegerSequenceCodec(range) { }
+
+ // Creates an encoder based on the number of trits, quints, and bits for
+ // the bit stream produced by Encode.
+ IntegerSequenceEncoder(int trits, int quints, int bits)
+ : IntegerSequenceCodec(trits, quints, bits) { }
+
+ // Adds a value to the encoding sequence.
+ void AddValue(int val) {
+ // Make sure it's within bounds
+ assert(encoding_ != EncodingMode::kTritEncoding || val < 3 * (1 << bits_));
+ assert(encoding_ != EncodingMode::kQuintEncoding || val < 5 * (1 << bits_));
+ assert(encoding_ != EncodingMode::kBitEncoding || val < (1 << bits_));
+ vals_.push_back(val);
+ }
+
+ // Writes the encoding for vals_ to the bit_sink. Multiple calls to Encode
+ // will produce the same result.
+ void Encode(base::BitStream<base::UInt128>* bit_sink) const;
+
+ // Removes all of the previously added values to the encoder.
+ void Reset() { vals_.clear(); }
+
+ private:
+ std::vector<int> vals_;
+};
+
+} // namespace astc_codec
+
+#endif // ASTC_CODEC_DECODER_INTEGER_SEQUENCE_CODEC_H_
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/intermediate_astc_block.cc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/intermediate_astc_block.cc
new file mode 100644
index 00000000000..bd67e988559
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/intermediate_astc_block.cc
@@ -0,0 +1,591 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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 "src/decoder/intermediate_astc_block.h"
+#include "src/decoder/integer_sequence_codec.h"
+#include "src/base/bit_stream.h"
+#include "src/base/math_utils.h"
+#include "src/base/optional.h"
+#include "src/base/uint128.h"
+
+#include <algorithm>
+#include <numeric>
+#include <sstream>
+
+namespace astc_codec {
+
+namespace {
+
+constexpr int kEndpointRange_ReturnInvalidWeightDims = -1;
+constexpr int kEndpointRange_ReturnNotEnoughColorBits = -2;
+
+base::UInt128 PackVoidExtentBlock(uint16_t r, uint16_t g, uint16_t b,
+ uint16_t a, std::array<uint16_t, 4> coords) {
+ base::BitStream<base::UInt128> bit_sink;
+
+ // Put void extent mode...
+ bit_sink.PutBits(0xDFC, 12);
+
+ // Each of the coordinates goes in 13 bits at a time.
+ for (auto coord : coords) {
+ assert(coord < 1 << 13);
+ bit_sink.PutBits(coord, 13);
+ }
+ assert(bit_sink.Bits() == 64);
+
+ // Then we add R, G, B, and A in order
+ bit_sink.PutBits(r, 16);
+ bit_sink.PutBits(g, 16);
+ bit_sink.PutBits(b, 16);
+ bit_sink.PutBits(a, 16);
+
+ assert(bit_sink.Bits() == 128);
+
+ base::UInt128 result;
+ bit_sink.GetBits(128, &result);
+ return result;
+}
+
+base::Optional<std::string> GetEncodedWeightRange(int range,
+ std::array<int, 3>* const r) {
+ const std::array<std::array<int, 3>, 12> kValidRangeEncodings =
+ {{ {{ 0, 1, 0 }}, {{ 1, 1, 0 }}, {{ 0, 0, 1 }},
+ {{ 1, 0, 1 }}, {{ 0, 1, 1 }}, {{ 1, 1, 1 }},
+ {{ 0, 1, 0 }}, {{ 1, 1, 0 }}, {{ 0, 0, 1 }},
+ {{ 1, 0, 1 }}, {{ 0, 1, 1 }}, {{ 1, 1, 1 }} }};
+
+ // If our range is larger than all available ranges, this is an error.
+ const int smallest_range = kValidWeightRanges.front();
+ const int largest_range = kValidWeightRanges.back();
+ if (range < smallest_range || largest_range < range) {
+ std::stringstream strm;
+ strm << "Could not find block mode. Invalid weight range: "
+ << range << " not in [" << smallest_range << ", "
+ << largest_range << std::endl;
+ return strm.str();
+ }
+
+ // Find the upper bound on the range, otherwise.
+ const auto range_iter = std::lower_bound(
+ kValidWeightRanges.cbegin(), kValidWeightRanges.cend(), range);
+ auto enc_iter = kValidRangeEncodings.cbegin();
+ enc_iter += std::distance(kValidWeightRanges.cbegin(), range_iter);
+ *r = *enc_iter;
+ return {};
+}
+
+struct BlockModeInfo {
+ int min_weight_grid_dim_x;
+ int max_weight_grid_dim_x;
+ int min_weight_grid_dim_y;
+ int max_weight_grid_dim_y;
+ int r0_bit_pos;
+ int r1_bit_pos;
+ int r2_bit_pos;
+ int weight_grid_x_offset_bit_pos;
+ int weight_grid_y_offset_bit_pos;
+ bool require_single_plane_low_prec;
+};
+
+constexpr int kNumBlockModes = 10;
+const std::array<BlockModeInfo, kNumBlockModes> kBlockModeInfo {{
+ { 4, 7, 2, 5, 4, 0, 1, 7, 5, false }, // B+4 A+2
+ { 8, 11, 2, 5, 4, 0, 1, 7, 5, false }, // B+8 A+2
+ { 2, 5, 8, 11, 4, 0, 1, 5, 7, false }, // A+2 B+8
+ { 2, 5, 6, 7, 4, 0, 1, 5, 7, false }, // A+2 B+6
+ { 2, 3, 2, 5, 4, 0, 1, 7, 5, false }, // B+2 A+2
+ { 12, 12, 2, 5, 4, 2, 3, -1, 5, false }, // 12 A+2
+ { 2, 5, 12, 12, 4, 2, 3, 5, -1, false }, // A+2 12
+ { 6, 6, 10, 10, 4, 2, 3, -1, -1, false }, // 6 10
+ { 10, 10, 6, 6, 4, 2, 3, -1, -1, false }, // 10 6
+ { 6, 9, 6, 9, 4, 2, 3, 5, 9, true } // A+6 B+6
+}};
+
+// These are the bits that must be set for ASTC to recognize a given
+// block mode. They are the 1's set in table C.2.8 of the spec.
+const std::array<int, kNumBlockModes> kBlockModeMask = {{
+ 0x0, 0x4, 0x8, 0xC, 0x10C, 0x0, 0x80, 0x180, 0x1A0, 0x100
+}};
+
+static base::Optional<std::string> PackBlockMode(int dim_x, int dim_y, int range,
+ bool dual_plane,
+ base::BitStream<base::UInt128>* const bit_sink) {
+ // We need to set the high precision bit if our range is too high...
+ bool high_prec = range > 7;
+
+ std::array<int, 3> r = {};
+ const auto result = GetEncodedWeightRange(range, &r);
+ if (result) {
+ return result;
+ }
+
+ // The high two bits of R must not be zero. If this happens then it's
+ // an illegal encoding according to Table C.2.7 that should have gotten
+ // caught in GetEncodedWeightRange
+ assert((r[1] | r[2]) > 0);
+
+ // Just go through the table and see if any of the modes can handle
+ // the given dimensions.
+ for (int mode = 0; mode < kNumBlockModes; ++mode) {
+ const BlockModeInfo& block_mode = kBlockModeInfo[mode];
+
+ bool is_valid_mode = true;
+ is_valid_mode &= block_mode.min_weight_grid_dim_x <= dim_x;
+ is_valid_mode &= dim_x <= block_mode.max_weight_grid_dim_x;
+ is_valid_mode &= block_mode.min_weight_grid_dim_y <= dim_y;
+ is_valid_mode &= dim_y <= block_mode.max_weight_grid_dim_y;
+ is_valid_mode &= !(block_mode.require_single_plane_low_prec && dual_plane);
+ is_valid_mode &= !(block_mode.require_single_plane_low_prec && high_prec);
+
+ if (!is_valid_mode) {
+ continue;
+ }
+
+ // Initialize to the bits we must set.
+ uint32_t encoded_mode = kBlockModeMask[mode];
+ auto setBit = [&encoded_mode](const uint32_t value, const uint32_t offset) {
+ encoded_mode = (encoded_mode & ~(1 << offset)) | ((value & 1) << offset);
+ };
+
+ // Set all the bits we need to set
+ setBit(r[0], block_mode.r0_bit_pos);
+ setBit(r[1], block_mode.r1_bit_pos);
+ setBit(r[2], block_mode.r2_bit_pos);
+
+ // Find our width and height offset from the base width and height weight
+ // grid dimension for the given block mode. These are the 1-2 bits that
+ // get encoded in the block mode used to calculate the final weight grid
+ // width and height.
+ const int offset_x = dim_x - block_mode.min_weight_grid_dim_x;
+ const int offset_y = dim_y - block_mode.min_weight_grid_dim_y;
+
+ // If we don't have an offset position then our offset better be zero.
+ // If this isn't the case, then this isn't a viable block mode and we
+ // should have caught this sooner.
+ assert(block_mode.weight_grid_x_offset_bit_pos >= 0 || offset_x == 0);
+ assert(block_mode.weight_grid_y_offset_bit_pos >= 0 || offset_y == 0);
+
+ encoded_mode |= offset_x << block_mode.weight_grid_x_offset_bit_pos;
+ encoded_mode |= offset_y << block_mode.weight_grid_y_offset_bit_pos;
+
+ if (!block_mode.require_single_plane_low_prec) {
+ setBit(high_prec, 9);
+ setBit(dual_plane, 10);
+ }
+
+ // Make sure that the mode is the first thing the bit sink is writing to
+ assert(bit_sink->Bits() == 0);
+ bit_sink->PutBits(encoded_mode, 11);
+
+ return {};
+ }
+
+ return std::string("Could not find viable block mode");
+}
+
+// Returns true if all endpoint modes are equal.
+bool SharedEndpointModes(const IntermediateBlockData& data) {
+ return std::accumulate(
+ data.endpoints.begin(), data.endpoints.end(), true,
+ [&data](const bool& a, const IntermediateEndpointData& b) {
+ return a && b.mode == data.endpoints[0].mode;
+ });
+}
+
+// Returns the starting bit (between 0 and 128) where the extra CEM and
+// dual plane info is stored in the ASTC block.
+int ExtraConfigBitPosition(const IntermediateBlockData& data) {
+ const bool has_dual_channel = data.dual_plane_channel.hasValue();
+ const int num_weights = data.weight_grid_dim_x * data.weight_grid_dim_y *
+ (has_dual_channel ? 2 : 1);
+ const int num_weight_bits =
+ IntegerSequenceCodec::GetBitCountForRange(num_weights, data.weight_range);
+
+ int extra_config_bits = 0;
+ if (!SharedEndpointModes(data)) {
+ const int num_encoded_cem_bits = 2 + int(data.endpoints.size()) * 3;
+ extra_config_bits = num_encoded_cem_bits - 6;
+ }
+
+ if (has_dual_channel) {
+ extra_config_bits += 2;
+ }
+
+ return 128 - num_weight_bits - extra_config_bits;
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+
+base::Optional<IntermediateBlockData> UnpackIntermediateBlock(
+ const PhysicalASTCBlock& pb) {
+ if (pb.IsIllegalEncoding()) {
+ return {};
+ }
+
+ if (pb.IsVoidExtent()) {
+ return {};
+ }
+
+ // Non void extent? Then let's try to decode everything else.
+ IntermediateBlockData data;
+
+ // All blocks have color values...
+ const base::UInt128 color_bits_mask =
+ (base::UInt128(1) << pb.NumColorBits().value()) - 1;
+ const base::UInt128 color_bits =
+ (pb.GetBlockBits() >> pb.ColorStartBit().value()) & color_bits_mask;
+ base::BitStream<base::UInt128> bit_src(color_bits, 128);
+
+ IntegerSequenceDecoder color_decoder(pb.ColorValuesRange().value());
+ const int num_colors_in_block = pb.NumColorValues().value();
+ std::vector<int> colors = color_decoder.Decode(num_colors_in_block, &bit_src);
+
+ // Decode simple info
+ const auto weight_dims = pb.WeightGridDims();
+ data.weight_grid_dim_x = weight_dims->at(0);
+ data.weight_grid_dim_y = weight_dims->at(1);
+ data.weight_range = pb.WeightRange().value();
+
+ data.partition_id = pb.PartitionID();
+ data.dual_plane_channel = pb.DualPlaneChannel();
+
+ auto colors_iter = colors.begin();
+ for (int i = 0; i < pb.NumPartitions().value(); ++i) {
+ IntermediateEndpointData ep_data;
+ ep_data.mode = pb.GetEndpointMode(i).value();
+
+ const int num_colors = NumColorValuesForEndpointMode(ep_data.mode);
+ ep_data.colors.insert(ep_data.colors.end(), colors_iter,
+ colors_iter + num_colors);
+ colors_iter += num_colors;
+
+ data.endpoints.push_back(ep_data);
+ }
+ assert(colors_iter == colors.end());
+ data.endpoint_range = pb.ColorValuesRange().value();
+
+ // Finally decode the weights
+ const base::UInt128 weight_bits_mask =
+ (base::UInt128(1) << pb.NumWeightBits().value()) - 1;
+ const base::UInt128 weight_bits =
+ base::ReverseBits(pb.GetBlockBits()) & weight_bits_mask;
+ bit_src = base::BitStream<base::UInt128>(weight_bits, 128);
+
+ IntegerSequenceDecoder weight_decoder(data.weight_range);
+ int num_weights = data.weight_grid_dim_x * data.weight_grid_dim_y;
+ num_weights *= pb.IsDualPlane() ? 2 : 1;
+ data.weights = weight_decoder.Decode(num_weights, &bit_src);
+
+ return data;
+}
+
+int EndpointRangeForBlock(const IntermediateBlockData& data) {
+ // First check to see if we exceed the number of bits allotted for weights, as
+ // specified in C.2.24. If so, then the endpoint range is meaningless, but not
+ // because we had an overzealous color endpoint mode, so return a different
+ // error code.
+ if (IntegerSequenceCodec::GetBitCountForRange(
+ data.weight_grid_dim_x * data.weight_grid_dim_y *
+ (data.dual_plane_channel.hasValue() ? 2 : 1),
+ data.weight_range) > 96) {
+ return kEndpointRange_ReturnInvalidWeightDims;
+ }
+
+ const int num_partitions = int(data.endpoints.size());
+
+ // Calculate the number of bits that we would write prior to getting to the
+ // color endpoint data
+ const int bits_written =
+ 11 // Block mode
+ + 2 // Num partitions
+ + ((num_partitions > 1) ? 10 : 0) // Partition ID
+ + ((num_partitions == 1) ? 4 : 6); // Shared CEM bits
+
+ // We can determine the range based on how many bits we have between the start
+ // of the color endpoint data and the next section, which is the extra config
+ // bit position
+ const int color_bits_available = ExtraConfigBitPosition(data) - bits_written;
+
+ int num_color_values = 0;
+ for (const auto& ep_data : data.endpoints) {
+ num_color_values += NumColorValuesForEndpointMode(ep_data.mode);
+ }
+
+ // There's no way any valid ASTC encoding has no room left for any color
+ // values. If we hit this then something is wrong in the caller -- abort.
+ // According to section C.2.24, the smallest number of bits available is
+ // ceil(13*C/5), where C is the number of color endpoint integers needed.
+ const int bits_needed = (13 * num_color_values + 4) / 5;
+ if (color_bits_available < bits_needed) {
+ return kEndpointRange_ReturnNotEnoughColorBits;
+ }
+
+ int color_value_range = 255;
+ for (; color_value_range > 1; --color_value_range) {
+ const int bits_for_range = IntegerSequenceCodec::GetBitCountForRange(
+ num_color_values, color_value_range);
+ if (bits_for_range <= color_bits_available) {
+ break;
+ }
+ }
+
+ return color_value_range;
+}
+
+base::Optional<VoidExtentData> UnpackVoidExtent(const PhysicalASTCBlock& pb) {
+ if (pb.IsIllegalEncoding()) {
+ return {};
+ }
+
+ if (!pb.IsVoidExtent()) {
+ return {};
+ }
+
+ // All blocks have color values...
+ const base::UInt128 color_bits_mask =
+ (base::UInt128(1) << pb.NumColorBits().value()) - 1;
+ const uint64_t color_bits = (
+ (pb.GetBlockBits() >> pb.ColorStartBit().value()) & color_bits_mask).LowBits();
+
+ assert(pb.NumColorValues().value() == 4);
+ VoidExtentData data;
+ data.r = static_cast<uint16_t>((color_bits >> 0) & 0xFFFF);
+ data.g = static_cast<uint16_t>((color_bits >> 16) & 0xFFFF);
+ data.b = static_cast<uint16_t>((color_bits >> 32) & 0xFFFF);
+ data.a = static_cast<uint16_t>((color_bits >> 48) & 0xFFFF);
+
+ const auto void_extent_coords = pb.VoidExtentCoords();
+ if (void_extent_coords) {
+ data.coords[0] = uint16_t(void_extent_coords->at(0));
+ data.coords[1] = uint16_t(void_extent_coords->at(1));
+ data.coords[2] = uint16_t(void_extent_coords->at(2));
+ data.coords[3] = uint16_t(void_extent_coords->at(3));
+ } else {
+ uint16_t all_ones = (1 << 13) - 1;
+ for (auto& coord : data.coords) {
+ coord = all_ones;
+ }
+ }
+
+ return data;
+}
+
+// Packs the given intermediate block into a physical block. Returns false if
+// the provided values in the intermediate block emit an illegal ASTC
+// encoding.
+base::Optional<std::string> Pack(const IntermediateBlockData& data,
+ base::UInt128* pb) {
+ if (data.weights.size() !=
+ size_t(data.weight_grid_dim_x * data.weight_grid_dim_y *
+ (data.dual_plane_channel.hasValue() ? 2 : 1))) {
+ return std::string("Incorrect number of weights!");
+ }
+
+ // If it's not a void extent block, then it gets a bit more tricky...
+ base::BitStream<base::UInt128> bit_sink;
+
+ // First we need to encode the block mode.
+ const auto error_string = PackBlockMode(
+ data.weight_grid_dim_x, data.weight_grid_dim_y, data.weight_range,
+ data.dual_plane_channel.hasValue(), &bit_sink);
+ if (error_string) {
+ return error_string;
+ }
+
+ // Next, we place the number of partitions minus one.
+ const int num_partitions = int(data.endpoints.size());
+ bit_sink.PutBits(num_partitions - 1, 2);
+
+ // If we have more than one partition, then we also have a partition ID.
+ if (num_partitions > 1) {
+ const int id = data.partition_id.value();
+ assert(id >= 0);
+ bit_sink.PutBits(id, 10);
+ }
+
+ // Take a detour, let's encode the weights so that we know how many bits they
+ // consume.
+ base::BitStream<base::UInt128> weight_sink;
+
+ IntegerSequenceEncoder weight_enc(data.weight_range);
+ for (auto weight : data.weights) {
+ weight_enc.AddValue(weight);
+ }
+ weight_enc.Encode(&weight_sink);
+
+ const int num_weight_bits = weight_sink.Bits();
+ assert(num_weight_bits ==
+ IntegerSequenceCodec::GetBitCountForRange(
+ int(data.weights.size()), data.weight_range));
+
+ // Let's continue... how much after the color data do we need to write?
+ int extra_config = 0;
+
+ // Determine if all endpoint pairs share the same endpoint mode
+ assert(data.endpoints.size() > 0);
+ bool shared_endpoint_mode = SharedEndpointModes(data);
+
+ // The first part of the endpoint mode (CEM) comes directly after the
+ // partition info, if it exists. If there is no partition info, the CEM comes
+ // right after the block mode. In the single-partition case, we just write out
+ // the entire singular CEM, but in the multi-partition case, if all CEMs are
+ // the same then their shared CEM is specified directly here, too. In both
+ // cases, shared_endpoint_mode is true (in the singular case,
+ // shared_endpoint_mode is trivially true).
+ if (shared_endpoint_mode) {
+ if (num_partitions > 1) {
+ bit_sink.PutBits(0, 2);
+ }
+ bit_sink.PutBits(static_cast<int>(data.endpoints[0].mode), 4);
+ } else {
+ // Here, the CEM is not shared across all endpoint pairs, and we need to
+ // figure out what to place here, and what to place in the extra config
+ // bits before the weight data...
+
+ // Non-shared config modes must all be within the same class (out of four)
+ // See Section C.2.11
+ int min_class = 2; // We start with 2 here instead of three because it's
+ // the highest that can be encoded -- even if all modes
+ // are class 3.
+ int max_class = 0;
+ for (const auto& ep_data : data.endpoints) {
+ const int ep_mode_class = static_cast<int>(ep_data.mode) >> 2;
+ min_class = std::min(min_class, ep_mode_class);
+ max_class = std::max(max_class, ep_mode_class);
+ }
+
+ assert(max_class >= min_class);
+
+ if (max_class - min_class > 1) {
+ return std::string("Endpoint modes are invalid");
+ }
+
+ // Construct the CEM mode -- six of its bits will fit here, but otherwise
+ // the rest will go in the extra configuration bits.
+ base::BitStream<uint32_t> cem_encoder;
+
+ // First encode the base class
+ assert(min_class >= 0);
+ assert(min_class < 3);
+ cem_encoder.PutBits(min_class + 1, 2);
+
+ // Next, encode the class selector bits -- this is simply the offset
+ // from the base class
+ for (const auto& ep_data : data.endpoints) {
+ const int ep_mode_class = static_cast<int>(ep_data.mode) >> 2;
+ const int class_selector_bit = ep_mode_class - min_class;
+ assert(class_selector_bit == 0 || class_selector_bit == 1);
+ cem_encoder.PutBits(class_selector_bit, 1);
+ }
+
+ // Finally, we need to choose from each class which actual mode
+ // we belong to and encode those.
+ for (const auto& ep_data : data.endpoints) {
+ const int ep_mode = static_cast<int>(ep_data.mode) & 3;
+ assert(ep_mode < 4);
+ cem_encoder.PutBits(ep_mode, 2);
+ }
+ assert(cem_encoder.Bits() == uint32_t(2 + num_partitions * 3));
+
+ uint32_t encoded_cem;
+ cem_encoder.GetBits(2 + num_partitions * 3, &encoded_cem);
+
+ // Since only six bits fit here before the color endpoint data, the rest
+ // need to go in the extra config data.
+ extra_config = encoded_cem >> 6;
+
+ // Write out the six bits we had
+ bit_sink.PutBits(encoded_cem, 6);
+ }
+
+ // If we have a dual-plane channel, we can tack that onto our extra config
+ // data
+ if (data.dual_plane_channel.hasValue()) {
+ const int channel = data.dual_plane_channel.value();
+ assert(channel < 4);
+ extra_config <<= 2;
+ extra_config |= channel;
+ }
+
+ // Get the range of endpoint values. It can't be -1 because we should have
+ // checked for that much earlier.
+ const int color_value_range = data.endpoint_range
+ ? data.endpoint_range.value()
+ : EndpointRangeForBlock(data);
+
+ assert(color_value_range != kEndpointRange_ReturnInvalidWeightDims);
+ if (color_value_range == kEndpointRange_ReturnNotEnoughColorBits) {
+ return { "Intermediate block emits illegal color range" };
+ }
+
+ IntegerSequenceEncoder color_enc(color_value_range);
+ for (const auto& ep_data : data.endpoints) {
+ for (int color : ep_data.colors) {
+ if (color > color_value_range) {
+ return { "Color outside available color range!" };
+ }
+
+ color_enc.AddValue(color);
+ }
+ }
+ color_enc.Encode(&bit_sink);
+
+ // Now we need to skip some bits to get to the extra configuration bits. The
+ // number of bits we need to skip depends on where we are in the stream and
+ // where we need to get to.
+ const int extra_config_bit_position = ExtraConfigBitPosition(data);
+ const int extra_config_bits =
+ 128 - num_weight_bits - extra_config_bit_position;
+ assert(extra_config_bits >= 0);
+ assert(extra_config < 1 << extra_config_bits);
+
+ // Make sure the color encoder didn't write more than we thought it would.
+ int bits_to_skip = extra_config_bit_position - bit_sink.Bits();
+ assert(bits_to_skip >= 0);
+
+ while (bits_to_skip > 0) {
+ const int skipping = std::min(32, bits_to_skip);
+ bit_sink.PutBits(0, skipping);
+ bits_to_skip -= skipping;
+ }
+
+ // Finally, write out the rest of the config bits.
+ bit_sink.PutBits(extra_config, extra_config_bits);
+
+ // We should be right up to the weight bits...
+ assert(bit_sink.Bits() == uint32_t(128 - num_weight_bits));
+
+ // Flush out our bit writer and write out the weight bits
+ base::UInt128 astc_bits;
+ bit_sink.GetBits(128 - num_weight_bits, &astc_bits);
+
+ base::UInt128 rev_weight_bits;
+ weight_sink.GetBits(weight_sink.Bits(), &rev_weight_bits);
+
+ astc_bits |= base::ReverseBits(rev_weight_bits);
+
+ // And we're done! Whew!
+ *pb = astc_bits;
+ return PhysicalASTCBlock(*pb).IsIllegalEncoding();
+}
+
+base::Optional<std::string> Pack(const VoidExtentData& data,
+ base::UInt128* pb) {
+ *pb = PackVoidExtentBlock(data.r, data.g, data.b, data.a, data.coords);
+ return PhysicalASTCBlock(*pb).IsIllegalEncoding();
+}
+
+} // namespace astc_codec
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/intermediate_astc_block.h b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/intermediate_astc_block.h
new file mode 100644
index 00000000000..13eef213a4b
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/intermediate_astc_block.h
@@ -0,0 +1,128 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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.
+
+#ifndef ASTC_CODEC_DECODER_INTERMEDIATE_ASTC_BLOCK_H_
+#define ASTC_CODEC_DECODER_INTERMEDIATE_ASTC_BLOCK_H_
+
+#include "src/base/optional.h"
+#include "src/base/uint128.h"
+#include "src/decoder/physical_astc_block.h"
+
+#include <array>
+#include <vector>
+
+namespace astc_codec {
+
+// From Table C.2.7 -- These are the only valid ranges that weight
+// values can take.
+constexpr std::array<int, 12> kValidWeightRanges =
+{{ 1, 2, 3, 4, 5, 7, 9, 11, 15, 19, 23, 31 }};
+
+// Void extent data are all the ASTC blocks that are labeled for having a
+// constant color. In the ASTC spec, some of these blocks may optionally
+// have "void extent coordinates" that describe how far in texture space
+// the constant color should span. If these coordinates are not valid,
+// then the coordinates are all set to a fully saturated bit mask
+// ((1 << 13) - 1) and the block is treated as a singular constant color.
+// We call both types of these blocks "void extent" to remove confusion
+// in our code.
+struct VoidExtentData {
+ uint16_t r;
+ uint16_t g;
+ uint16_t b;
+ uint16_t a;
+ std::array<uint16_t, 4> coords;
+};
+
+// Intermediate endpoint data. Really this is just an endpoint mode
+// and a couple of values that represent the data used to decode the
+// RGB values from the color endpoint mode.
+struct IntermediateEndpointData {
+ ColorEndpointMode mode;
+ std::vector<int> colors;
+};
+
+// This is an unpacked physical ASTC block, but it does not have enough
+// information to be worked with logically. It is simply a container of
+// all of the unpacked ASTC information. It is used as a staging area
+// for the information that is later Pack()'d into a PhysicalASTCBlock.
+struct IntermediateBlockData {
+ int weight_grid_dim_x;
+ int weight_grid_dim_y;
+ int weight_range;
+
+ // Quantized, non-interpolated weights
+ std::vector<int> weights;
+
+ // The 10-bit partition ID if we need one
+ base::Optional<int> partition_id;
+
+ // The dual-plane channel in [0, 3] if it exists.
+ base::Optional<int> dual_plane_channel;
+
+ // The quantized/encoded endpoint values for this block. The range of each
+ // endpoint value is specified by |endpoint_range|, if it exists. If not, the
+ // range can be queried by calling EndpointRangeForBlock
+ std::vector<IntermediateEndpointData> endpoints;
+
+ // The range [0, endpoint_range] that any one endpoint value can take. Users
+ // should not write to this value themselves. If it is empty at the time
+ // someone calls Pack(), it will be automatically inferred. Otherwise, it is
+ // set by Unpack() based on what the underlying encoding specified.
+ base::Optional<int> endpoint_range;
+};
+
+// Returns the maximum value that a given endpoint value can take according to
+// the other settings in the block. Ignores the |endpoint_range| member
+// variable. Returns negative values on error:
+// -1 : Too many bits required to store weight grid
+// -2 : There are too few bits allocated for color endpoint data according to
+// C.2.24 in the ASTC spec
+int EndpointRangeForBlock(const IntermediateBlockData& data);
+inline int EndpointRangeForBlock(const VoidExtentData& data);
+
+// Unpacks the physical ASTC block into the intermediate block. Returns false
+// if the physical block is an error encoded block, or if the physical block
+// is a void extent block. On error the contents of ib are undefined.
+base::Optional<IntermediateBlockData> UnpackIntermediateBlock(
+ const PhysicalASTCBlock& pb);
+
+// Unpacks the physical ASTC block into a void extent block. Returns false
+// if the physical block is an error encoded block, or if the physical block
+// is an intermediate block. On error the contents of ib are undefined.
+base::Optional<VoidExtentData> UnpackVoidExtent(const PhysicalASTCBlock& pb);
+
+// Packs the given intermediate block into a physical block. Returns an error
+// string if the provided values in the intermediate block emit an illegal ASTC
+// encoding. In this case the results in the physical block are undefined.
+base::Optional<std::string> Pack(const IntermediateBlockData& data,
+ base::UInt128* pb);
+
+// Packs the given void extent block into a physical block. Returns an error
+// string if the provided values in the void extent block emit an illegal ASTC
+// encoding. In this case the results in the physical block are undefined.
+base::Optional<std::string> Pack(const VoidExtentData& data, base::UInt128* pb);
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Impl
+
+inline int EndpointRangeForBlock(const VoidExtentData&) {
+ // Void extent blocks use 16-bit ARGB definitions
+ return (1 << 16) - 1;
+}
+
+} // namespace astc_codec
+
+#endif // ASTC_CODEC_DECODER_INTERMEDIATE_ASTC_BLOCK_H_
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/logical_astc_block.cc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/logical_astc_block.cc
new file mode 100644
index 00000000000..4889ce2faa7
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/logical_astc_block.cc
@@ -0,0 +1,262 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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 "src/decoder/logical_astc_block.h"
+#include "src/decoder/endpoint_codec.h"
+#include "src/decoder/footprint.h"
+#include "src/decoder/integer_sequence_codec.h"
+#include "src/decoder/quantization.h"
+#include "src/decoder/weight_infill.h"
+
+namespace astc_codec {
+
+namespace {
+
+Partition GenerateSinglePartition(Footprint footprint) {
+ return Partition { footprint, /* num_parts = */ 1, /* partition_id = */ 0,
+ std::vector<int>(footprint.NumPixels(), 0) };
+}
+
+static std::vector<EndpointPair> DecodeEndpoints(const IntermediateBlockData& block) {
+ const int endpoint_range = block.endpoint_range
+ ? block.endpoint_range.value()
+ : EndpointRangeForBlock(block);
+ assert(endpoint_range > 0);
+
+ std::vector<EndpointPair> endpoints;
+ for (const auto& eps : block.endpoints) {
+ RgbaColor decmp_one_rgba, decmp_two_rgba;
+ DecodeColorsForMode(eps.colors, endpoint_range, eps.mode,
+ &decmp_one_rgba, &decmp_two_rgba);
+ endpoints.emplace_back(decmp_one_rgba, decmp_two_rgba);
+ }
+ return endpoints;
+}
+
+static std::vector<EndpointPair> DecodeEndpoints(const VoidExtentData& block) {
+ EndpointPair eps;
+ eps.first[0] = eps.second[0] = (block.r * 255) / 65535;
+ eps.first[1] = eps.second[1] = (block.g * 255) / 65535;
+ eps.first[2] = eps.second[2] = (block.b * 255) / 65535;
+ eps.first[3] = eps.second[3] = (block.a * 255) / 65535;
+
+ std::vector<EndpointPair> endpoints;
+ endpoints.emplace_back(eps);
+ return endpoints;
+}
+
+Partition ComputePartition(const Footprint& footprint,
+ const IntermediateBlockData& block) {
+ if (block.partition_id) {
+ const int part_id = block.partition_id.value();
+ const int num_parts = int(block.endpoints.size());
+ return GetASTCPartition(footprint, num_parts, part_id);
+ } else {
+ return GenerateSinglePartition(footprint);
+ }
+}
+
+Partition ComputePartition(const Footprint& footprint, const VoidExtentData&) {
+ return GenerateSinglePartition(footprint);
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+
+LogicalASTCBlock::LogicalASTCBlock(const Footprint& footprint)
+ : endpoints_(1),
+ weights_(footprint.NumPixels(), 0),
+ partition_(GenerateSinglePartition(footprint)) { }
+
+LogicalASTCBlock::LogicalASTCBlock(const Footprint& footprint,
+ const IntermediateBlockData& block)
+ : endpoints_(DecodeEndpoints(block)),
+ partition_(ComputePartition(footprint, block)) {
+ CalculateWeights(footprint, block);
+}
+
+LogicalASTCBlock::LogicalASTCBlock(const Footprint& footprint,
+ const VoidExtentData& block)
+ : endpoints_(DecodeEndpoints(block)),
+ partition_(ComputePartition(footprint, block)) {
+ CalculateWeights(footprint, block);
+}
+
+void LogicalASTCBlock::CalculateWeights(const Footprint& footprint,
+ const IntermediateBlockData& block) {
+ const int grid_size_x = block.weight_grid_dim_x;
+ const int grid_size_y = block.weight_grid_dim_y;
+ const int weight_grid_size = grid_size_x * grid_size_y;
+
+ // Either we have a dual plane and we have twice as many weights as
+ // specified or we don't
+ assert(block.dual_plane_channel
+ ? block.weights.size() == size_t(weight_grid_size * 2)
+ : block.weights.size() == size_t(weight_grid_size));
+
+ std::vector<int> unquantized;
+ unquantized.reserve(weight_grid_size);
+
+ // According to C.2.16, if we have dual-plane weights, then we have two
+ // weights per texel -- one adjacent to the other. Hence, we have to skip
+ // some when we decode the separate weight values.
+ const int weight_frequency = (block.dual_plane_channel) ? 2 : 1;
+ const int weight_range = block.weight_range;
+
+ for (int i = 0; i < weight_grid_size; ++i) {
+ const int weight = block.weights[i * weight_frequency];
+ unquantized.push_back(UnquantizeWeightFromRange(weight, weight_range));
+ }
+ weights_ = InfillWeights(unquantized, footprint, grid_size_x, grid_size_y);
+
+ if (block.dual_plane_channel) {
+ SetDualPlaneChannel(block.dual_plane_channel.value());
+ for (int i = 0; i < weight_grid_size; ++i) {
+ const int weight = block.weights[i * weight_frequency + 1];
+ unquantized[i] = UnquantizeWeightFromRange(weight, weight_range);
+ }
+ dual_plane_->weights =
+ InfillWeights(unquantized, footprint, grid_size_x, grid_size_y);
+ }
+}
+
+void LogicalASTCBlock::CalculateWeights(const Footprint& footprint,
+ const VoidExtentData&) {
+ weights_ = std::vector<int>(footprint.NumPixels(), 0);
+}
+
+void LogicalASTCBlock::SetWeightAt(int x, int y, int weight) {
+ assert(weight >= 0);
+ assert(weight <= 64);
+ weights_.at(y * GetFootprint().Width() + x) = weight;
+}
+
+int LogicalASTCBlock::WeightAt(int x, int y) const {
+ return weights_.at(y * GetFootprint().Width() + x);
+}
+
+void LogicalASTCBlock::SetDualPlaneWeightAt(int channel, int x, int y,
+ int weight) {
+ assert(weight >= 0);
+ assert(weight <= 64);
+
+ // If it's not a dual plane, then this has no logical meaning
+ assert(IsDualPlane());
+
+ // If it is a dual plane and the passed channel matches the query, then we
+ // return the specialized weights
+ if (dual_plane_->channel == channel) {
+ dual_plane_->weights.at(y * GetFootprint().Width() + x) = weight;
+ } else {
+ // If the channel is not the special channel, then return the general weight
+ SetWeightAt(x, y, weight);
+ }
+}
+
+int LogicalASTCBlock::DualPlaneWeightAt(int channel, int x, int y) const {
+ // If it's not a dual plane, then we just return the weight for all channels
+ if (!IsDualPlane()) {
+ // TODO(google): Log warning, Requesting dual-plane channel for a non
+ // dual-plane block!
+ return WeightAt(x, y);
+ }
+
+ // If it is a dual plane and the passed channel matches the query, then we
+ // return the specialized weights
+ if (dual_plane_->channel == channel) {
+ return dual_plane_->weights.at(y * GetFootprint().Width() + x);
+ }
+
+ // If the channel is not the special channel, then return the general weight
+ return WeightAt(x, y);
+}
+
+void LogicalASTCBlock::SetDualPlaneChannel(int channel) {
+ if (channel < 0) {
+ dual_plane_.clear();
+ } else if (dual_plane_) {
+ dual_plane_->channel = channel;
+ } else {
+ dual_plane_ = DualPlaneData {channel, weights_};
+ }
+}
+
+RgbaColor LogicalASTCBlock::ColorAt(int x, int y) const {
+ const auto footprint = GetFootprint();
+ assert(x >= 0); assert(x < footprint.Width());
+ assert(y >= 0); assert(y < footprint.Height());
+
+ const int texel_idx = y * footprint.Width() + x;
+ const int part = partition_.assignment[texel_idx];
+ const auto& endpoints = endpoints_[part];
+
+ RgbaColor result;
+ for (int channel = 0; channel < 4; ++channel) {
+ const int weight = (dual_plane_ && dual_plane_->channel == channel)
+ ? dual_plane_->weights[texel_idx]
+ : weights_[texel_idx];
+ const int p0 = endpoints.first[channel];
+ const int p1 = endpoints.second[channel];
+
+ assert(p0 >= 0); assert(p0 < 256);
+ assert(p1 >= 0); assert(p1 < 256);
+
+ // According to C.2.19
+ const int c0 = (p0 << 8) | p0;
+ const int c1 = (p1 << 8) | p1;
+ const int c = (c0 * (64 - weight) + c1 * weight + 32) / 64;
+ // TODO(google): Handle conversion to sRGB or FP16 per C.2.19.
+ const int quantized = ((c * 255) + 32767) / 65536;
+ assert(quantized < 256);
+ result[channel] = quantized;
+ }
+
+ return result;
+}
+
+void LogicalASTCBlock::SetPartition(const Partition& p) {
+ assert(p.footprint == partition_.footprint &&
+ "New partitions may not be for a different footprint");
+ partition_ = p;
+ endpoints_.resize(p.num_parts);
+}
+
+void LogicalASTCBlock::SetEndpoints(const EndpointPair& eps, int subset) {
+ assert(subset < partition_.num_parts);
+ assert(size_t(subset) < endpoints_.size());
+
+ endpoints_[subset] = eps;
+}
+
+base::Optional<LogicalASTCBlock> UnpackLogicalBlock(
+ const Footprint& footprint, const PhysicalASTCBlock& pb) {
+ if (pb.IsVoidExtent()) {
+ base::Optional<VoidExtentData> ve = UnpackVoidExtent(pb);
+ if (!ve) {
+ return {};
+ }
+
+ return LogicalASTCBlock(footprint, ve.value());
+ } else {
+ base::Optional<IntermediateBlockData> ib = UnpackIntermediateBlock(pb);
+ if (!ib) {
+ return {};
+ }
+
+ return LogicalASTCBlock(footprint, ib.value());
+ }
+}
+
+} // namespace astc_codec
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/logical_astc_block.h b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/logical_astc_block.h
new file mode 100644
index 00000000000..2243eb4e5e4
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/logical_astc_block.h
@@ -0,0 +1,127 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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.
+
+#ifndef ASTC_CODEC_DECODER_LOGICAL_ASTC_BLOCK_H_
+#define ASTC_CODEC_DECODER_LOGICAL_ASTC_BLOCK_H_
+
+#include "src/base/optional.h"
+#include "src/decoder/footprint.h"
+#include "src/decoder/intermediate_astc_block.h"
+#include "src/decoder/partition.h"
+#include "src/decoder/physical_astc_block.h"
+
+#include <array>
+#include <utility>
+#include <vector>
+
+namespace astc_codec {
+
+// A logical ASTC block holds the endpoints, indices, and partition information
+// of a compressed block. These values generally do not adhere to any
+// quality-for-bitrate-imposed limits and are solely logical entities for
+// determining the best representation of a given block.
+class LogicalASTCBlock {
+ public:
+ LogicalASTCBlock(const LogicalASTCBlock&) = default;
+ LogicalASTCBlock(LogicalASTCBlock&&) = default;
+
+ // Unpack an intermediate block into a logical one.
+ LogicalASTCBlock(const Footprint& footprint,
+ const IntermediateBlockData& block);
+
+ // Unpack a void extent intermediate block into a logical one.
+ LogicalASTCBlock(const Footprint& footprint, const VoidExtentData& block);
+
+ // Create a new, empty ASTC block
+ explicit LogicalASTCBlock(const Footprint& footprint);
+
+ // Returns the footprint associated with this block. The footprint is defined
+ // via the partition, because the partition definition is dependent on the
+ // footprint.
+ const Footprint& GetFootprint() const { return partition_.footprint; }
+
+ // Returns the unquantized and infilled weight in the range [0, 64] for the
+ // given texel location. Assumes that the block is a single-plane block,
+ // meaning that weights are used equally across all channels.
+ void SetWeightAt(int x, int y, int weight);
+ int WeightAt(int x, int y) const;
+
+ // Returns the unquantized and infilled weight in the range [0, 64] for the
+ // given channel at the given texel location. If the block does not have a
+ // dual-plane channel then the reference-returning version will fail, as it
+ // cannot return a reference to a value that (potentially) doesn't exist.
+ void SetDualPlaneWeightAt(int channel, int x, int y, int weight);
+ int DualPlaneWeightAt(int channel, int x, int y) const;
+
+ // Returns the color as it would be in the given pixel coordinates of the
+ // block. Fails if the coordinates are outside of the range of the block
+ // footprint
+ RgbaColor ColorAt(int x, int y) const;
+
+ // Sets the current partition for the block. |p|'s footprint must match the
+ // return value of GetFootprint() or else this call will fail.
+ void SetPartition(const Partition& p);
+
+ // Sets the endpoints for the given subset.
+ void SetEndpoints(const EndpointPair& eps, int subset);
+ void SetEndpoints(const Endpoint& ep1, const Endpoint& ep2, int subset) {
+ SetEndpoints(std::make_pair(ep1, ep2), subset);
+ }
+
+ // Sets the dual plane channel for the block. Value must be within the range
+ // [0, 3]. If a negative value is passed, then the dual-plane data for the
+ // block is removed, and the block is treated as a single-plane block.
+ void SetDualPlaneChannel(int channel);
+ bool IsDualPlane() const { return dual_plane_.hasValue(); }
+
+ private:
+ // A block may have up to four endpoint pairs.
+ std::vector<EndpointPair> endpoints_;
+
+ // Weights are stored as values in the interval [0, 64].
+ std::vector<int> weights_;
+
+ // The partition information for this block. This determines the
+ // appropriate subsets that each pixel should belong to.
+ Partition partition_;
+
+ // Dual plane data holds both the channel and the weights that describe
+ // the dual plane data for the given block. If a block has a dual plane, then
+ // we need to know both the channel and the weights associated with it.
+ struct DualPlaneData {
+ int channel;
+ std::vector<int> weights;
+ };
+
+ // The dual-plane data is optional from a logical representation of the block.
+ base::Optional<DualPlaneData> dual_plane_;
+
+ // Calculates the unquantized and interpolated weights from the encoded weight
+ // values and possibly dual-plane weights specified in the passed ASTC block.
+ void CalculateWeights(const Footprint& footprint,
+ const IntermediateBlockData& block);
+
+ // Calculates the weights for a VoidExtentBlock.
+ void CalculateWeights(const Footprint& footprint,
+ const VoidExtentData& block);
+};
+
+// Unpacks the physical ASTC block into a logical block. Returns false if the
+// physical block is an error encoded block.
+base::Optional<LogicalASTCBlock> UnpackLogicalBlock(
+ const Footprint& footprint, const PhysicalASTCBlock& pb);
+
+} // namespace astc_codec
+
+#endif // ASTC_CODEC_DECODER_LOGICAL_ASTC_BLOCK_H_
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/partition.cc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/partition.cc
new file mode 100644
index 00000000000..92c3ee0bdc4
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/partition.cc
@@ -0,0 +1,601 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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 "src/decoder/partition.h"
+#include "src/base/bottom_n.h"
+#include "src/base/utils.h"
+#include "src/decoder/footprint.h"
+
+#include <algorithm>
+#include <array>
+#include <limits>
+#include <memory>
+#include <numeric>
+#include <queue>
+#include <set>
+#include <unordered_set>
+#include <utility>
+
+namespace astc_codec {
+
+namespace {
+
+// The maximum number of partitions supported by ASTC is four.
+constexpr int kMaxNumSubsets = 4;
+
+// Partition selection function based on the ASTC specification.
+// See section C.2.21
+int SelectASTCPartition(int seed, int x, int y, int z, int partitioncount,
+ int num_pixels) {
+ if (partitioncount <= 1) {
+ return 0;
+ }
+
+ if (num_pixels < 31) {
+ x <<= 1;
+ y <<= 1;
+ z <<= 1;
+ }
+
+ seed += (partitioncount - 1) * 1024;
+
+ uint32_t rnum = seed;
+ rnum ^= rnum >> 15;
+ rnum -= rnum << 17;
+ rnum += rnum << 7;
+ rnum += rnum << 4;
+ rnum ^= rnum >> 5;
+ rnum += rnum << 16;
+ rnum ^= rnum >> 7;
+ rnum ^= rnum >> 3;
+ rnum ^= rnum << 6;
+ rnum ^= rnum >> 17;
+
+ uint8_t seed1 = rnum & 0xF;
+ uint8_t seed2 = (rnum >> 4) & 0xF;
+ uint8_t seed3 = (rnum >> 8) & 0xF;
+ uint8_t seed4 = (rnum >> 12) & 0xF;
+ uint8_t seed5 = (rnum >> 16) & 0xF;
+ uint8_t seed6 = (rnum >> 20) & 0xF;
+ uint8_t seed7 = (rnum >> 24) & 0xF;
+ uint8_t seed8 = (rnum >> 28) & 0xF;
+ uint8_t seed9 = (rnum >> 18) & 0xF;
+ uint8_t seed10 = (rnum >> 22) & 0xF;
+ uint8_t seed11 = (rnum >> 26) & 0xF;
+ uint8_t seed12 = ((rnum >> 30) | (rnum << 2)) & 0xF;
+
+ seed1 *= seed1;
+ seed2 *= seed2;
+ seed3 *= seed3;
+ seed4 *= seed4;
+ seed5 *= seed5;
+ seed6 *= seed6;
+ seed7 *= seed7;
+ seed8 *= seed8;
+ seed9 *= seed9;
+ seed10 *= seed10;
+ seed11 *= seed11;
+ seed12 *= seed12;
+
+ int sh1, sh2, sh3;
+ if (seed & 1) {
+ sh1 = (seed & 2 ? 4 : 5);
+ sh2 = (partitioncount == 3 ? 6 : 5);
+ } else {
+ sh1 = (partitioncount == 3 ? 6 : 5);
+ sh2 = (seed & 2 ? 4 : 5);
+ }
+ sh3 = (seed & 0x10) ? sh1 : sh2;
+
+ seed1 >>= sh1;
+ seed2 >>= sh2;
+ seed3 >>= sh1;
+ seed4 >>= sh2;
+ seed5 >>= sh1;
+ seed6 >>= sh2;
+ seed7 >>= sh1;
+ seed8 >>= sh2;
+
+ seed9 >>= sh3;
+ seed10 >>= sh3;
+ seed11 >>= sh3;
+ seed12 >>= sh3;
+
+ int a = seed1 * x + seed2 * y + seed11 * z + (rnum >> 14);
+ int b = seed3 * x + seed4 * y + seed12 * z + (rnum >> 10);
+ int c = seed5 * x + seed6 * y + seed9 * z + (rnum >> 06);
+ int d = seed7 * x + seed8 * y + seed10 * z + (rnum >> 02);
+
+ a &= 0x3F;
+ b &= 0x3F;
+ c &= 0x3F;
+ d &= 0x3F;
+
+ if (partitioncount <= 3) {
+ d = 0;
+ }
+ if (partitioncount <= 2) {
+ c = 0;
+ }
+
+ if (a >= b && a >= c && a >= d) {
+ return 0;
+ } else if (b >= c && b >= d) {
+ return 1;
+ } else if (c >= d) {
+ return 2;
+ } else {
+ return 3;
+ }
+}
+
+// A partition hash that we can pass to containers like std::unordered_set
+struct PartitionHasher {
+ size_t operator()(const Partition& part) const {
+ // The issue here is that if we have two different partitions, A and B, then
+ // their hash should be equal if A and B are equal. We define the distance
+ // between A and B using PartitionMetric, but internally that finds a 1-1
+ // mapping from labels in A to labels in B.
+ //
+ // With that in mind, when we define a hash for partitions, we need to find
+ // a 1-1 mapping to a 'universal' labeling scheme. Here we define that as
+ // the heuristic: the first encountered label will be 0, the second will be
+ // 1, etc. This creates a unique 1-1 mapping scheme from any partition.
+ //
+ // Note, we can't use this heuristic for the PartitionMetric, as it will
+ // generate very large discrepancies between similar labellings (for example
+ // 000...001 vs 011...111). We are just looking for a boolean distinction
+ // whether or not two partitions are different and don't care how different
+ // they are.
+ std::array<int, kMaxNumSubsets> mapping {{ -1, -1, -1, -1 }};
+ int next_subset = 0;
+ for (int subset : part.assignment) {
+ if (mapping[subset] < 0) {
+ mapping[subset] = next_subset++;
+ }
+ }
+ assert(next_subset <= kMaxNumSubsets);
+
+ // The return value will be the hash of the assignment according to this
+ // mapping
+ const size_t seed0 = 0;
+ return std::accumulate(part.assignment.begin(), part.assignment.end(), seed0,
+ [&mapping](size_t seed, const int& subset) {
+ std::hash<size_t> hasher;
+ const int s = mapping[subset];
+ return hasher(seed) ^ hasher(static_cast<size_t>(s));
+ });
+ }
+};
+
+// Construct a VP-Tree of partitions. Since our PartitionMetric satisfies
+// the triangle inequality, we can use this general higher-dimensional space
+// partitioning tree to organize our partitions.
+//
+// TODO(google): !SPEED! Right now this tree stores an actual linked
+// structure of pointers which is likely very slow during construction and
+// very not cache-coherent during traversal, so it'd probably be good to
+// switch to a flattened binary tree structure if performance becomes an
+// issue.
+class PartitionTree {
+ public:
+ // Unclear what it means to have an uninitialized tree, so delete default
+ // constructors, but allow the tree to be moved
+ PartitionTree() = delete;
+ PartitionTree(const PartitionTree&) = delete;
+ PartitionTree(PartitionTree&& t) = default;
+
+ // Generate a PartitionTree from iterators over |Partition|s
+ template<typename Itr>
+ PartitionTree(Itr begin, Itr end) : parts_(begin, end) {
+ std::vector<int> part_indices(parts_.size());
+ std::iota(part_indices.begin(), part_indices.end(), 0);
+ root_ = std::unique_ptr<PartitionTreeNode>(
+ new PartitionTreeNode(parts_, part_indices));
+ }
+
+ // Search for the k-nearest partitions that are closest to part based on
+ // the result of PartitionMetric
+ void Search(const Partition& part, int k,
+ std::vector<const Partition*>* const results,
+ std::vector<int>* const distances) const {
+ ResultHeap heap(k);
+ SearchNode(root_, part, &heap);
+
+ results->clear();
+ if (nullptr != distances) {
+ distances->clear();
+ }
+
+ std::vector<ResultNode> search_results = heap.Pop();
+ for (const auto& result : search_results) {
+ results->push_back(&parts_[result.part_idx]);
+ if (nullptr != distances) {
+ distances->push_back(result.distance);
+ }
+ }
+
+ assert(results->size() == size_t(k));
+ }
+
+ private:
+ // Heap elements to be stored while searching the tree. The two relevant
+ // pieces of information are the partition index and it's distance from the
+ // queried partition.
+ struct ResultNode {
+ int part_idx;
+ int distance;
+
+ // Heap based on distance from query point.
+ bool operator<(const ResultNode& other) const {
+ return distance < other.distance;
+ }
+ };
+
+ using ResultHeap = base::BottomN<ResultNode>;
+
+ struct PartitionTreeNode {
+ int part_idx;
+ int split_dist;
+
+ std::unique_ptr<PartitionTreeNode> left;
+ std::unique_ptr<PartitionTreeNode> right;
+
+ PartitionTreeNode(const std::vector<Partition> &parts,
+ const std::vector<int> &part_indices)
+ : split_dist(-1) {
+ assert(part_indices.size() > 0);
+
+ right.reset(nullptr);
+ left.reset(nullptr);
+
+ // Store the first node as our vantage point
+ part_idx = part_indices[0];
+ const Partition& vantage_point = parts[part_indices[0]];
+
+ // Calculate the distances of the remaining nodes against the vantage
+ // point.
+ std::vector<std::pair<int, int>> part_dists;
+ for (size_t i = 1; i < part_indices.size(); ++i) {
+ const int idx = part_indices[i];
+ const int dist = PartitionMetric(vantage_point, parts[idx]);
+ if (dist > 0) {
+ part_dists.push_back(std::make_pair(idx, dist));
+ }
+ }
+
+ // If there are no more different parts, then this is a leaf node
+ if (part_dists.empty()) {
+ return;
+ }
+
+ struct OrderBySecond {
+ typedef std::pair<int, int> PairType;
+ bool operator()(const PairType& lhs, const PairType& rhs) {
+ return lhs.second < rhs.second;
+ }
+ };
+
+ // We want to partition the set such that the points are ordered
+ // based on their distances from the vantage point. We can do this
+ // using the partial sort of nth element.
+ std::nth_element(
+ part_dists.begin(), part_dists.begin() + part_dists.size() / 2,
+ part_dists.end(), OrderBySecond());
+
+ // Once that's done, our split position is in the middle
+ const auto split_iter = part_dists.begin() + part_dists.size() / 2;
+ split_dist = split_iter->second;
+
+ // Recurse down the right and left sub-trees with the indices of the
+ // parts that are farther and closer respectively
+ std::vector<int> right_indices;
+ for (auto itr = split_iter; itr != part_dists.end(); ++itr) {
+ right_indices.push_back(itr->first);
+ }
+
+ if (!right_indices.empty()) {
+ right.reset(new PartitionTreeNode(parts, right_indices));
+ }
+
+ std::vector<int> left_indices;
+ for (auto itr = part_dists.begin(); itr != split_iter; ++itr) {
+ left_indices.push_back(itr->first);
+ }
+
+ if (!left_indices.empty()) {
+ left.reset(new PartitionTreeNode(parts, left_indices));
+ }
+ }
+ };
+
+ void SearchNode(const std::unique_ptr<PartitionTreeNode>& node,
+ const Partition& p, ResultHeap* const heap) const {
+ if (nullptr == node) {
+ return;
+ }
+
+ // Calculate distance against current node
+ const int dist = PartitionMetric(parts_[node->part_idx], p);
+
+ // Push it onto the heap and remove the top-most nodes to maintain
+ // closest k indices.
+ ResultNode result;
+ result.part_idx = node->part_idx;
+ result.distance = dist;
+ heap->Push(result);
+
+ // If the split distance is uninitialized, it means we have no children.
+ if (node->split_dist < 0) {
+ assert(nullptr == node->left);
+ assert(nullptr == node->right);
+ return;
+ }
+
+ // Next we need to check the left and right trees if their distance
+ // is closer/farther than the farthest element on the heap
+ const int tau = heap->Top().distance;
+ if (dist + tau < node->split_dist || dist - tau < node->split_dist) {
+ SearchNode(node->left, p, heap);
+ }
+
+ if (dist + tau > node->split_dist || dist - tau > node->split_dist) {
+ SearchNode(node->right, p, heap);
+ }
+ }
+
+ std::vector<Partition> parts_;
+ std::unique_ptr<PartitionTreeNode> root_;
+};
+
+// A helper function that generates all of the partitions for each number of
+// subsets in ASTC blocks and stores them in a PartitionTree for fast retrieval.
+const int kNumASTCPartitionIDBits = 10;
+PartitionTree GenerateASTCPartitionTree(Footprint footprint) {
+ std::unordered_set<Partition, PartitionHasher> parts;
+ for (int num_parts = 2; num_parts <= kMaxNumSubsets; ++num_parts) {
+ for (int id = 0; id < (1 << kNumASTCPartitionIDBits); ++id) {
+ Partition part = GetASTCPartition(footprint, num_parts, id);
+
+ // Make sure we're not using a degenerate partition assignment that wastes
+ // an endpoint pair...
+ bool valid_part = true;
+ for (int i = 0; i < num_parts; ++i) {
+ if (std::find(part.assignment.begin(), part.assignment.end(), i) ==
+ part.assignment.end()) {
+ valid_part = false;
+ break;
+ }
+ }
+
+ if (valid_part) {
+ parts.insert(std::move(part));
+ }
+ }
+ }
+
+ return PartitionTree(parts.begin(), parts.end());
+}
+
+// To avoid needing any fancy boilerplate for mapping from a width, height
+// tuple, we can define a simple encoding for the block mode:
+constexpr int EncodeDims(int width, int height) {
+ return (width << 16) | height;
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+
+int PartitionMetric(const Partition& a, const Partition& b) {
+ // Make sure that one partition is at least a subset of the other...
+ UTILS_RELEASE_ASSERT(a.footprint == b.footprint);
+
+ // Make sure that the number of parts is within our limits. ASTC has a maximum
+ // of four subsets per block according to the specification.
+ UTILS_RELEASE_ASSERT(a.num_parts <= kMaxNumSubsets);
+ UTILS_RELEASE_ASSERT(b.num_parts <= kMaxNumSubsets);
+
+ const int w = a.footprint.Width();
+ const int h = b.footprint.Height();
+
+ struct PairCount {
+ int a;
+ int b;
+ int count;
+
+ // Comparison needed for sort below.
+ bool operator>(const PairCount& other) const {
+ return count > other.count;
+ }
+ };
+
+ // Since we need to find the smallest mapping from labels in A to labels in B,
+ // we need to store each label pair in a structure that can later be sorted.
+ // The maximum number of subsets in an ASTC block is four, meaning that
+ // between the two partitions, we can have up to sixteen different pairs.
+ std::array<PairCount, 16> pair_counts;
+ for (int y = 0; y < 4; ++y) {
+ for (int x = 0; x < 4; ++x) {
+ const int idx = y * 4 + x;
+ pair_counts[idx].a = x;
+ pair_counts[idx].b = y;
+ pair_counts[idx].count = 0;
+ }
+ }
+
+ // Count how many times we see each pair of assigned values (order matters!)
+ for (int y = 0; y < h; ++y) {
+ for (int x = 0; x < w; ++x) {
+ const int idx = y * w + x;
+
+ const int a_val = a.assignment[idx];
+ const int b_val = b.assignment[idx];
+
+ assert(a_val >= 0);
+ assert(b_val >= 0);
+
+ assert(a_val < 4);
+ assert(b_val < 4);
+
+ ++(pair_counts[b_val * 4 + a_val].count);
+ }
+ }
+
+ // Sort the pairs in descending order based on their count
+ std::sort(pair_counts.begin(), pair_counts.end(), std::greater<PairCount>());
+
+ // Now assign pairs one by one until we have no more pairs to assign. Once
+ // a value from A is assigned to a value in B, it can no longer be reassigned,
+ // so we can keep track of this in a matrix. Similarly, to keep the assignment
+ // one-to-one, once a value in B has been assigned to, it cannot be assigned
+ // to again.
+ std::array<std::array<bool, kMaxNumSubsets>, kMaxNumSubsets> assigned { };
+
+ int pixels_matched = 0;
+ for (const auto& pair_count : pair_counts) {
+ bool is_assigned = false;
+ for (int i = 0; i < kMaxNumSubsets; ++i) {
+ is_assigned |= assigned.at(pair_count.a).at(i);
+ is_assigned |= assigned.at(i).at(pair_count.b);
+ }
+
+ if (!is_assigned) {
+ assigned.at(pair_count.a).at(pair_count.b) = true;
+ pixels_matched += pair_count.count;
+ }
+ }
+
+ // The difference is the number of pixels that had an assignment versus the
+ // total number of pixels.
+ return w * h - pixels_matched;
+}
+
+// Generates the partition assignment for the given block attributes.
+Partition GetASTCPartition(const Footprint& footprint, int num_parts,
+ int partition_id) {
+ // Partitions must have at least one subset but may have at most four
+ assert(num_parts >= 0);
+ assert(num_parts <= kMaxNumSubsets);
+
+ // Partition ID can be no more than 10 bits.
+ assert(partition_id >= 0);
+ assert(partition_id < 1 << 10);
+
+ Partition part = {footprint, num_parts, partition_id, /* assignment = */ {}};
+ part.assignment.reserve(footprint.NumPixels());
+
+ // Maintain column-major order so that we match all of the image processing
+ // algorithms that depend on this class.
+ for (int y = 0; y < footprint.Height(); ++y) {
+ for (int x = 0; x < footprint.Width(); ++x) {
+ const int p = SelectASTCPartition(partition_id, x, y, 0, num_parts,
+ footprint.NumPixels());
+ part.assignment.push_back(p);
+ }
+ }
+
+ return part;
+}
+
+const std::vector<const Partition*> FindKClosestASTCPartitions(
+ const Partition& candidate, int k) {
+ const int encoded_dims = EncodeDims(candidate.footprint.Width(),
+ candidate.footprint.Height());
+
+ int index = 0;
+ switch (encoded_dims) {
+ case EncodeDims(4, 4): index = 0; break;
+ case EncodeDims(5, 4): index = 1; break;
+ case EncodeDims(5, 5): index = 2; break;
+ case EncodeDims(6, 5): index = 3; break;
+ case EncodeDims(6, 6): index = 4; break;
+ case EncodeDims(8, 5): index = 5; break;
+ case EncodeDims(8, 6): index = 6; break;
+ case EncodeDims(8, 8): index = 7; break;
+ case EncodeDims(10, 5): index = 8; break;
+ case EncodeDims(10, 6): index = 9; break;
+ case EncodeDims(10, 8): index = 10; break;
+ case EncodeDims(10, 10): index = 11; break;
+ case EncodeDims(12, 10): index = 12; break;
+ case EncodeDims(12, 12): index = 13; break;
+ default:
+ assert(false && "Unknown footprint dimensions. This should have been caught sooner.");
+ break;
+ }
+
+ static const auto* const kASTCPartitionTrees =
+ new std::array<PartitionTree, Footprint::NumValidFootprints()> {{
+ GenerateASTCPartitionTree(Footprint::Get4x4()),
+ GenerateASTCPartitionTree(Footprint::Get5x4()),
+ GenerateASTCPartitionTree(Footprint::Get5x5()),
+ GenerateASTCPartitionTree(Footprint::Get6x5()),
+ GenerateASTCPartitionTree(Footprint::Get6x6()),
+ GenerateASTCPartitionTree(Footprint::Get8x5()),
+ GenerateASTCPartitionTree(Footprint::Get8x6()),
+ GenerateASTCPartitionTree(Footprint::Get8x8()),
+ GenerateASTCPartitionTree(Footprint::Get10x5()),
+ GenerateASTCPartitionTree(Footprint::Get10x6()),
+ GenerateASTCPartitionTree(Footprint::Get10x8()),
+ GenerateASTCPartitionTree(Footprint::Get10x10()),
+ GenerateASTCPartitionTree(Footprint::Get12x10()),
+ GenerateASTCPartitionTree(Footprint::Get12x12()),
+ }};
+
+ const PartitionTree& parts_vptree = kASTCPartitionTrees->at(index);
+ std::vector<const Partition*> results;
+ parts_vptree.Search(candidate, k, &results, nullptr);
+ return results;
+}
+
+// Returns the valid ASTC partition that is closest to the candidate based on
+// the PartitionMetric defined above.
+const Partition& FindClosestASTCPartition(const Partition& candidate) {
+ // Given a candidate, the closest valid partition will likely not be an exact
+ // match. Consider all of the texels for which this valid partition differs
+ // with the candidate.
+ //
+ // If the valid partition has more subsets than the candidate, then all of the
+ // highest subset will be included in the mismatched texels. Since the number
+ // of possible partitions with increasing subsets grows exponentially, the
+ // chance that a valid partition with fewer subsets appears within the first
+ // few closest partitions is relatively high. Empirically, we can usually find
+ // a partition with at most |candidate.num_parts| number of subsets within the
+ // first four closest partitions.
+ constexpr int kSearchItems = 4;
+
+ const std::vector<const Partition*> results =
+ FindKClosestASTCPartitions(candidate, kSearchItems);
+
+ // Optimistically, look for result with the same number of subsets.
+ for (const auto& result : results) {
+ if (result->num_parts == candidate.num_parts) {
+ return *result;
+ }
+ }
+
+ // If all else fails, then at least find the result with fewer subsets than
+ // we asked for.
+ for (const auto& result : results) {
+ if (result->num_parts < candidate.num_parts) {
+ return *result;
+ }
+ }
+
+ assert(false &&
+ "Could not find partition with acceptable number of subsets!");
+ return *(results[0]);
+}
+
+} // namespace astc_codec
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/partition.h b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/partition.h
new file mode 100644
index 00000000000..4f64acb8e9f
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/partition.h
@@ -0,0 +1,97 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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.
+
+#ifndef ASTC_CODEC_DECODER_PARTITION_H_
+#define ASTC_CODEC_DECODER_PARTITION_H_
+
+#include "src/base/optional.h"
+#include "src/decoder/footprint.h"
+
+#include <vector>
+
+namespace astc_codec {
+
+struct Partition;
+
+// Determines the "difference" between any two partitions of the same size.
+// This metric attempts to find the best one to one mapping from the labels in
+// partition a against the labels in partition b. Once that mapping is found, it
+// returns the number of pixels that are mismatched between the two. Each
+// partition is expected to start in the upper left corner of the block and
+// proceed in raster-scan order. Two partitions are equal if the mapping is
+// bijective. This metric is a metric in the mathematical sense. In other words
+// it has the following properties:
+//
+// 1) PartitionMetric(a, b) >= 0
+// 2) PartitionMetric(a, b) == PartitionMetric(b, a)
+// 3) PartitionMetric(a, b) == 0 iff a == b
+// 4) PartitionMetric(a, b) + PartitionMetric(b, c) >= PartitionMetric(a, c)
+//
+// Throws an error if one partition's footprint is not equal to the other.
+int PartitionMetric(const Partition& a, const Partition& b);
+
+// A partition is a way to divide up an ASTC block into disjoint subsets such
+// that each subset uses a different set of endpoints. This is used to increase
+// the compression quality of blocks. One way to store such a partition is to
+// assign an ID to use with a predetermined decoding method. Here we store the
+// logical representation of partitions by keeping a per-pixel label. All pixels
+// that share a label belong to the same subset.
+struct Partition {
+ // The footprint width and height of this partition. This determines the size
+ // of the assignment array.
+ Footprint footprint;
+
+ // The number of subsets in this partition. The values in the partition
+ // assignment fall within the range [0, num_parts). The maximum number of
+ // parts supported is four.
+ int num_parts;
+
+ // The 10-bit partition ID as stored in bits 13-22 of multi-part ASTC blocks.
+ // (See Section C.2.9) If there is no guarantee that this partition is a valid
+ // ASTC partition, this should be set to absl::nullopt.
+ base::Optional<int> partition_id;
+
+ // A value in the range [0, num_parts) corresponding to the label for
+ // the given texel (x, y) in [0, footprint_width) x [0, footprint_height)
+ // using a raster-order layout.
+ std::vector<int> assignment;
+
+ // Returns true only if their "distance" is zero, i.e. if they have compatible
+ // subset assignments.
+ bool operator==(const Partition& other) const {
+ return PartitionMetric(*this, other) == 0;
+ }
+};
+
+// Generates the ASTC partition assignment for the given block attributes.
+Partition GetASTCPartition(const Footprint& footprint, int num_parts,
+ int partition_id);
+
+// Returns the |k| valid ASTC partitions that are closest to the candidate based
+// on the PartitionMetric defined above.
+const std::vector<const Partition*> FindKClosestASTCPartitions(
+ const Partition& candidate, int k);
+
+// Returns the valid ASTC partition closest to the candidate with at most as
+// many subsets as the |candidate|. Note: this is not a deterministic function,
+// as the underlying valid partitions are sorted using a hash map and a distance
+// function whose range is the natural numbers. The chances that two or more
+// partitions are equally 'closest' is possible, in which case this function
+// makes no guarantees about which one it will return. For more control, use
+// FindKClosestASTCPartitions above.
+const Partition& FindClosestASTCPartition(const Partition& candidate);
+
+} // namespace astc_codec
+
+#endif // ASTC_CODEC_DECODER_PARTITION_H_
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/physical_astc_block.cc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/physical_astc_block.cc
new file mode 100644
index 00000000000..41369499700
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/physical_astc_block.cc
@@ -0,0 +1,761 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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 "src/decoder/physical_astc_block.h"
+#include "src/base/math_utils.h"
+#include "src/base/optional.h"
+#include "src/base/uint128.h"
+#include "src/decoder/integer_sequence_codec.h"
+
+#include <array>
+#include <cmath>
+
+namespace astc_codec {
+
+namespace {
+
+static_assert(static_cast<int>(ColorEndpointMode::kNumColorEndpointModes) == 16,
+ "There are only sixteen color endpoint modes defined in the "
+ "ASTC specification. If this is false, then the enum may be "
+ "incorrect.");
+
+constexpr int kASTCBlockSizeBits = 128;
+//constexpr int kASTCBlockSizeBytes = kASTCBlockSizeBits / 8;
+constexpr uint32_t kVoidExtentMaskBits = 9;
+constexpr uint32_t kVoidExtentMask = 0x1FC;
+constexpr int kWeightGridMinBitLength = 24;
+constexpr int kWeightGridMaxBitLength = 96;
+constexpr int kMaxNumPartitions = 4;
+constexpr int kMaxNumWeights = 64;
+
+// These are the overall block modes defined in table C.2.8. There are 10
+// weight grid encoding schemes + void extent.
+enum class BlockMode {
+ kB4_A2,
+ kB8_A2,
+ kA2_B8,
+ kA2_B6,
+ kB2_A2,
+ k12_A2,
+ kA2_12,
+ k6_10,
+ k10_6,
+ kA6_B6,
+ kVoidExtent,
+};
+
+struct WeightGridProperties {
+ int width;
+ int height;
+ int range;
+};
+
+// Local function prototypes
+base::Optional<BlockMode> DecodeBlockMode(const base::UInt128 astc_bits);
+base::Optional<WeightGridProperties> DecodeWeightProps(
+ const base::UInt128 astc_bits, std::string* error);
+std::array<int, 4> DecodeVoidExtentCoords(const base::UInt128 astc_bits);
+bool DecodeDualPlaneBit(const base::UInt128 astc_bits);
+int DecodeNumPartitions(const base::UInt128 astc_bits);
+int DecodeNumWeightBits(const base::UInt128 astc_bits);
+int DecodeDualPlaneBitStartPos(const base::UInt128 astc_bits);
+ColorEndpointMode DecodeEndpointMode(const base::UInt128 astc_bits,
+ int partition);
+int DecodeNumColorValues(const base::UInt128 astc_bits);
+
+// Returns the block mode, if it's valid.
+base::Optional<BlockMode> DecodeBlockMode(const base::UInt128 astc_bits) {
+ using Result = base::Optional<BlockMode>;
+ const uint64_t low_bits = astc_bits.LowBits();
+ if (base::GetBits(low_bits, 0, kVoidExtentMaskBits) == kVoidExtentMask) {
+ return Result(BlockMode::kVoidExtent);
+ }
+
+ if (base::GetBits(low_bits, 0, 2) != 0) {
+ const uint64_t mode_bits = base::GetBits(low_bits, 2, 2);
+ switch (mode_bits) {
+ case 0: return Result(BlockMode::kB4_A2);
+ case 1: return Result(BlockMode::kB8_A2);
+ case 2: return Result(BlockMode::kA2_B8);
+ case 3: return base::GetBits(low_bits, 8, 1) ?
+ Result(BlockMode::kB2_A2) : Result(BlockMode::kA2_B6);
+ }
+ } else {
+ const uint64_t mode_bits = base::GetBits(low_bits, 5, 4);
+ if ((mode_bits & 0xC) == 0x0) {
+ if (base::GetBits(low_bits, 0, 4) == 0) {
+ // Reserved.
+ return Result();
+ } else {
+ return Result(BlockMode::k12_A2);
+ }
+ } else if ((mode_bits & 0xC) == 0x4) {
+ return Result(BlockMode::kA2_12);
+ } else if (mode_bits == 0xC) {
+ return Result(BlockMode::k6_10);
+ } else if (mode_bits == 0xD) {
+ return Result(BlockMode::k10_6);
+ } else if ((mode_bits & 0xC) == 0x8) {
+ return Result(BlockMode::kA6_B6);
+ }
+ }
+
+ return Result();
+}
+
+base::Optional<WeightGridProperties> DecodeWeightProps(
+ const base::UInt128 astc_bits, std::string* error) {
+ auto block_mode = DecodeBlockMode(astc_bits);
+ if (!block_mode) {
+ *error = "Reserved block mode";
+ return {};
+ }
+
+ // The dimensions of the weight grid and their range
+ WeightGridProperties props;
+
+ // Determine the weight extents based on the block mode
+ const uint32_t low_bits =
+ static_cast<uint32_t>(astc_bits.LowBits() & 0xFFFFFFFF);
+ switch (block_mode.value()) {
+ case BlockMode::kB4_A2: {
+ int a = base::GetBits(low_bits, 5, 2);
+ int b = base::GetBits(low_bits, 7, 2);
+ props.width = b + 4;
+ props.height = a + 2;
+ }
+ break;
+
+ case BlockMode::kB8_A2: {
+ int a = base::GetBits(low_bits, 5, 2);
+ int b = base::GetBits(low_bits, 7, 2);
+ props.width = b + 8;
+ props.height = a + 2;
+ }
+ break;
+
+ case BlockMode::kA2_B8: {
+ int a = base::GetBits(low_bits, 5, 2);
+ int b = base::GetBits(low_bits, 7, 2);
+ props.width = a + 2;
+ props.height = b + 8;
+ }
+ break;
+
+ case BlockMode::kA2_B6: {
+ int a = base::GetBits(low_bits, 5, 2);
+ int b = base::GetBits(low_bits, 7, 1);
+ props.width = a + 2;
+ props.height = b + 6;
+ }
+ break;
+
+ case BlockMode::kB2_A2: {
+ int a = base::GetBits(low_bits, 5, 2);
+ int b = base::GetBits(low_bits, 7, 1);
+ props.width = b + 2;
+ props.height = a + 2;
+ }
+ break;
+
+ case BlockMode::k12_A2: {
+ int a = base::GetBits(low_bits, 5, 2);
+ props.width = 12;
+ props.height = a + 2;
+ }
+ break;
+
+ case BlockMode::kA2_12: {
+ int a = base::GetBits(low_bits, 5, 2);
+ props.width = a + 2;
+ props.height = 12;
+ }
+ break;
+
+ case BlockMode::k6_10: {
+ props.width = 6;
+ props.height = 10;
+ }
+ break;
+
+ case BlockMode::k10_6: {
+ props.width = 10;
+ props.height = 6;
+ }
+ break;
+
+ case BlockMode::kA6_B6: {
+ int a = base::GetBits(low_bits, 5, 2);
+ int b = base::GetBits(low_bits, 9, 2);
+ props.width = a + 6;
+ props.height = b + 6;
+ }
+ break;
+
+ // Void extent blocks have no weight grid.
+ case BlockMode::kVoidExtent:
+ *error = "Void extent block has no weight grid";
+ return {};
+
+ // We have a valid block mode which isn't a void extent? We
+ // should be able to decode the weight grid dimensions.
+ default:
+ assert(false && "Error decoding weight grid");
+ *error = "Internal error";
+ return {};
+ }
+
+ // Determine the weight range based on the block mode
+ uint32_t r = base::GetBits(low_bits, 4, 1);
+ switch (block_mode.value()) {
+ case BlockMode::kB4_A2:
+ case BlockMode::kB8_A2:
+ case BlockMode::kA2_B8:
+ case BlockMode::kA2_B6:
+ case BlockMode::kB2_A2: {
+ r |= base::GetBits(low_bits, 0, 2) << 1;
+ }
+ break;
+
+ case BlockMode::k12_A2:
+ case BlockMode::kA2_12:
+ case BlockMode::k6_10:
+ case BlockMode::k10_6:
+ case BlockMode::kA6_B6: {
+ r |= base::GetBits(low_bits, 2, 2) << 1;
+ }
+ break;
+
+ // We have a valid block mode which doesn't have weights? We
+ // should have caught this earlier.
+ case BlockMode::kVoidExtent:
+ default:
+ assert(false && "Error decoding weight grid");
+ *error = "Internal error";
+ return {};
+ }
+
+ // Decode the range...
+ // High bit is in bit 9 unless we're using a particular block mode
+ uint32_t h = base::GetBits(low_bits, 9, 1);
+ if (block_mode == BlockMode::kA6_B6) {
+ h = 0;
+ }
+
+ // Figure out the range of the weights (Table C.2.7)
+ constexpr std::array<int, 16> kWeightRanges = {{
+ -1, -1, 1, 2, 3, 4, 5, 7, -1, -1, 9, 11, 15, 19, 23, 31
+ }};
+
+ assert(((h << 3) | r) < kWeightRanges.size());
+
+ props.range = kWeightRanges.at((h << 3) | r);
+ if (props.range < 0) {
+ *error = "Reserved range for weight bits";
+ return {};
+ }
+
+ // Error checking -- do we have too many weights?
+ int num_weights = props.width * props.height;
+ if (DecodeDualPlaneBit(astc_bits)) {
+ num_weights *= 2;
+ }
+
+ if (kMaxNumWeights < num_weights) {
+ *error = "Too many weights specified";
+ return {};
+ }
+
+ // Do we have too many weight bits?
+ const int bit_count =
+ IntegerSequenceCodec::GetBitCountForRange(num_weights, props.range);
+
+ if (bit_count < kWeightGridMinBitLength) {
+ *error = "Too few bits required for weight grid";
+ return {};
+ }
+
+ if (kWeightGridMaxBitLength < bit_count) {
+ *error = "Too many bits required for weight grid";
+ return {};
+ }
+
+ return props;
+}
+
+// Returns the four 13-bit integers that define the range of texture
+// coordinates present in a void extent block as defined in Section
+// C.2.23 of the specification. The coordinates returned are of
+// the form (min_s, max_s, min_t, max_t)
+std::array<int, 4> DecodeVoidExtentCoords(const base::UInt128 astc_bits) {
+ const uint64_t low_bits = astc_bits.LowBits();
+
+ std::array<int, 4> coords;
+ for (int i = 0; i < 4; ++i) {
+ coords[i] = static_cast<int>(base::GetBits(low_bits, 12 + 13 * i, 13));
+ }
+
+ return coords;
+}
+
+bool DecodeDualPlaneBit(const base::UInt128 astc_bits) {
+ base::Optional<BlockMode> block_mode = DecodeBlockMode(astc_bits);
+
+ // Void extent blocks certainly aren't dual-plane.
+ if (block_mode == BlockMode::kVoidExtent) {
+ return false;
+ }
+
+ // One special block mode doesn't have any dual plane bit
+ if (block_mode == BlockMode::kA6_B6) {
+ return false;
+ }
+
+ // Otherwise, dual plane is determined by the 10th bit.
+ constexpr int kDualPlaneBitPosition = 10;
+ return base::GetBits(astc_bits, kDualPlaneBitPosition, 1) != 0;
+}
+
+int DecodeNumPartitions(const base::UInt128 astc_bits) {
+ constexpr int kNumPartitionsBitPosition = 11;
+ constexpr int kNumPartitionsBitLength = 2;
+
+ // Non-void extent blocks
+ const uint64_t low_bits = astc_bits.LowBits();
+ const int num_partitions = 1 + static_cast<int>(
+ base::GetBits(low_bits,
+ kNumPartitionsBitPosition,
+ kNumPartitionsBitLength));
+ assert(num_partitions > 0);
+ assert(num_partitions <= kMaxNumPartitions);
+
+ return num_partitions;
+}
+
+int DecodeNumWeightBits(const base::UInt128 astc_bits) {
+ std::string error;
+ auto maybe_weight_props = DecodeWeightProps(astc_bits, &error);
+ if (!maybe_weight_props.hasValue()) {
+ return 0; // No weights? No weight bits...
+ }
+
+ const auto weight_props = maybe_weight_props.value();
+
+ // Figure out the number of weights
+ int num_weights = weight_props.width * weight_props.height;
+ if (DecodeDualPlaneBit(astc_bits)) {
+ num_weights *= 2;
+ }
+
+ // The number of bits is determined by the number of values
+ // that are going to be encoded using the given ise_counts.
+ return IntegerSequenceCodec::GetBitCountForRange(
+ num_weights, weight_props.range);
+}
+
+// Returns the number of bits after the weight data used to
+// store additional CEM bits.
+int DecodeNumExtraCEMBits(const base::UInt128 astc_bits) {
+ const int num_partitions = DecodeNumPartitions(astc_bits);
+
+ // Do we only have one partition?
+ if (num_partitions == 1) {
+ return 0;
+ }
+
+ // Do we have a shared CEM?
+ constexpr int kSharedCEMBitPosition = 23;
+ constexpr int kSharedCEMBitLength = 2;
+ const base::UInt128 shared_cem =
+ base::GetBits(astc_bits, kSharedCEMBitPosition, kSharedCEMBitLength);
+ if (shared_cem == 0) {
+ return 0;
+ }
+
+ const std::array<int, 4> extra_cem_bits_for_partition = {{ 0, 2, 5, 8 }};
+ return extra_cem_bits_for_partition[num_partitions - 1];
+}
+
+// Returns the starting position of the dual plane channel. This comes
+// before the weight data and extra CEM bits.
+int DecodeDualPlaneBitStartPos(const base::UInt128 astc_bits) {
+ const int start_pos = kASTCBlockSizeBits
+ - DecodeNumWeightBits(astc_bits)
+ - DecodeNumExtraCEMBits(astc_bits);
+
+ if (DecodeDualPlaneBit(astc_bits)) {
+ return start_pos - 2;
+ } else {
+ return start_pos;
+ }
+}
+
+// Decodes a CEM mode based on the partition number.
+ColorEndpointMode DecodeEndpointMode(const base::UInt128 astc_bits,
+ int partition) {
+ int num_partitions = DecodeNumPartitions(astc_bits);
+ assert(partition >= 0);
+ assert(partition < num_partitions);
+
+ // Do we only have one partition?
+ uint64_t low_bits = astc_bits.LowBits();
+ if (num_partitions == 1) {
+ uint64_t cem = base::GetBits(low_bits, 13, 4);
+ return static_cast<ColorEndpointMode>(cem);
+ }
+
+ // More than one partition ... do we have a shared CEM?
+ if (DecodeNumExtraCEMBits(astc_bits) == 0) {
+ const uint64_t shared_cem = base::GetBits(low_bits, 25, 4);
+ return static_cast<ColorEndpointMode>(shared_cem);
+ }
+
+ // More than one partition and no shared CEM...
+ uint64_t cem = base::GetBits(low_bits, 23, 6);
+ const int base_cem = static_cast<int>(((cem & 0x3) - 1) * 4);
+ cem >>= 2; // Skip the base CEM bits
+
+ // The number of extra CEM bits at the end of the weight grid is
+ // determined by the number of partitions and what the base cem mode is...
+ const int num_extra_cem_bits = DecodeNumExtraCEMBits(astc_bits);
+ const int extra_cem_start_pos = kASTCBlockSizeBits
+ - num_extra_cem_bits
+ - DecodeNumWeightBits(astc_bits);
+
+ base::UInt128 extra_cem =
+ base::GetBits(astc_bits, extra_cem_start_pos, num_extra_cem_bits);
+ cem |= extra_cem.LowBits() << 4;
+
+ // Decode C and M per Figure C.4
+ int c = -1, m = -1;
+ for (int i = 0; i < num_partitions; ++i) {
+ if (i == partition) {
+ c = cem & 0x1;
+ }
+ cem >>= 1;
+ }
+
+ for (int i = 0; i < num_partitions; ++i) {
+ if (i == partition) {
+ m = cem & 0x3;
+ }
+ cem >>= 2;
+ }
+
+ assert(c >= 0);
+ assert(m >= 0);
+
+ // Compute the mode based on C and M
+ const int mode = base_cem + 4 * c + m;
+ assert(mode < static_cast<int>(ColorEndpointMode::kNumColorEndpointModes));
+ return static_cast<ColorEndpointMode>(mode);
+}
+
+int DecodeNumColorValues(const base::UInt128 astc_bits) {
+ int num_color_values = 0;
+ auto num_partitions = DecodeNumPartitions(astc_bits);
+ for (int i = 0; i < num_partitions; ++i) {
+ ColorEndpointMode endpoint_mode = DecodeEndpointMode(astc_bits, i);
+ num_color_values += NumColorValuesForEndpointMode(endpoint_mode);
+ }
+
+ return num_color_values;
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+
+static_assert(sizeof(PhysicalASTCBlock) == PhysicalASTCBlock::kSizeInBytes,
+ "The size of the struct should be the size of the block so that"
+ "we can effectively use them contiguously in memory.");
+
+PhysicalASTCBlock::PhysicalASTCBlock(const base::UInt128 astc_block)
+ : astc_bits_(astc_block) {}
+
+PhysicalASTCBlock::PhysicalASTCBlock(const std::string& encoded_block)
+ : astc_bits_([&encoded_block]() {
+ assert(encoded_block.size() == PhysicalASTCBlock::kSizeInBytes);
+ base::UInt128 astc_bits = 0;
+ int shift = 0;
+ for (const unsigned char c : encoded_block) {
+ astc_bits |= base::UInt128(static_cast<uint64_t>(c)) << shift;
+ shift += 8;
+ }
+ return astc_bits;
+ }())
+{ }
+
+base::Optional<std::string> PhysicalASTCBlock::IsIllegalEncoding() const {
+ // If the block is not a void extent block, then it must have
+ // weights specified. DecodeWeightProps will return the weight specifications
+ // if they exist and are legal according to C.2.24, and will otherwise be
+ // empty.
+ base::Optional<BlockMode> block_mode = DecodeBlockMode(astc_bits_);
+ if (block_mode != BlockMode::kVoidExtent) {
+ std::string error;
+ auto maybe_weight_props = DecodeWeightProps(astc_bits_, &error);
+ if (!maybe_weight_props.hasValue()) {
+ return error;
+ }
+ }
+
+ // Check void extent blocks...
+ if (block_mode == BlockMode::kVoidExtent) {
+ // ... for reserved bits incorrectly set
+ if (base::GetBits(astc_bits_, 10, 2) != 0x3) {
+ return std::string("Reserved bits set for void extent block");
+ }
+
+ // ... for incorrectly defined texture coordinates
+ std::array<int, 4> coords = DecodeVoidExtentCoords(astc_bits_);
+
+ bool coords_all_1s = true;
+ for (const auto coord : coords) {
+ coords_all_1s &= coord == ((1 << 13) - 1);
+ }
+
+ if (!coords_all_1s && (coords[0] >= coords[1] || coords[2] >= coords[3])) {
+ return std::string("Void extent texture coordinates are invalid");
+ }
+ }
+
+ // If the number of color values exceeds a threshold and it isn't a void
+ // extent block then we've run into an error
+ if (block_mode != BlockMode::kVoidExtent) {
+ int num_color_vals = DecodeNumColorValues(astc_bits_);
+ if (num_color_vals > 18) {
+ return std::string("Too many color values");
+ }
+
+ // The maximum number of available color bits is the number of
+ // bits between the dual plane bits and the base CEM. This must
+ // be larger than a threshold defined in C.2.24.
+
+ // Dual plane bit starts after weight bits and CEM
+ const int num_partitions = DecodeNumPartitions(astc_bits_);
+ const int dual_plane_start_pos = DecodeDualPlaneBitStartPos(astc_bits_);
+ const int color_start_bit = (num_partitions == 1) ? 17 : 29;
+
+ const int required_color_bits = ((13 * num_color_vals) + 4) / 5;
+ const int available_color_bits = dual_plane_start_pos - color_start_bit;
+ if (available_color_bits < required_color_bits) {
+ return std::string("Not enough color bits");
+ }
+
+ // If we have four partitions and a dual plane then we have a problem.
+ if (num_partitions == 4 && DecodeDualPlaneBit(astc_bits_)) {
+ return std::string("Both four partitions and dual plane specified");
+ }
+ }
+
+ // Otherwise we're OK
+ return { };
+}
+
+bool PhysicalASTCBlock::IsVoidExtent() const {
+ // If it's an error block, it's not a void extent block.
+ if (IsIllegalEncoding()) {
+ return false;
+ }
+
+ return DecodeBlockMode(astc_bits_) == BlockMode::kVoidExtent;
+}
+
+base::Optional<std::array<int, 4>> PhysicalASTCBlock::VoidExtentCoords() const {
+ if (IsIllegalEncoding() || !IsVoidExtent()) {
+ return { };
+ }
+
+ // If void extent coords are all 1's then these are not valid void extent
+ // coords
+ const uint64_t ve_mask = 0xFFFFFFFFFFFFFDFFULL;
+ const uint64_t const_blk_mode = 0xFFFFFFFFFFFFFDFCULL;
+ if ((ve_mask & astc_bits_.LowBits()) == const_blk_mode) {
+ return {};
+ }
+
+ return DecodeVoidExtentCoords(astc_bits_);
+}
+
+bool PhysicalASTCBlock::IsDualPlane() const {
+ // If it's an error block, then we aren't a dual plane block
+ if (IsIllegalEncoding()) {
+ return false;
+ }
+
+ return DecodeDualPlaneBit(astc_bits_);
+}
+
+// Returns the number of weight bits present in this block
+base::Optional<int> PhysicalASTCBlock::NumWeightBits() const {
+ // If it's an error block, then we have no weight bits.
+ if (IsIllegalEncoding()) return { };
+
+ // If it's a void extent block, we have no weight bits
+ if (IsVoidExtent()) return { };
+
+ return DecodeNumWeightBits(astc_bits_);
+}
+
+base::Optional<int> PhysicalASTCBlock::WeightStartBit() const {
+ if (IsIllegalEncoding()) return { };
+ if (IsVoidExtent()) return { };
+
+ return kASTCBlockSizeBits - DecodeNumWeightBits(astc_bits_);
+}
+
+base::Optional<std::array<int, 2>> PhysicalASTCBlock::WeightGridDims() const {
+ std::string error;
+ auto weight_props = DecodeWeightProps(astc_bits_, &error);
+
+ if (!weight_props.hasValue()) return { };
+ if (IsIllegalEncoding()) return { };
+
+ const auto props = weight_props.value();
+ return {{{ props.width, props.height }}};
+}
+
+base::Optional<int> PhysicalASTCBlock::WeightRange() const {
+ std::string error;
+ auto weight_props = DecodeWeightProps(astc_bits_, &error);
+
+ if (!weight_props.hasValue()) return { };
+ if (IsIllegalEncoding()) return { };
+
+ return weight_props.value().range;
+}
+
+base::Optional<int> PhysicalASTCBlock::DualPlaneChannel() const {
+ if (!IsDualPlane()) return { };
+
+ int dual_plane_start_pos = DecodeDualPlaneBitStartPos(astc_bits_);
+ auto plane_bits = base::GetBits(astc_bits_, dual_plane_start_pos, 2);
+ return base::Optional<int>(static_cast<int>(plane_bits.LowBits()));
+}
+
+base::Optional<int> PhysicalASTCBlock::ColorStartBit() const {
+ if (IsVoidExtent()) {
+ return 64;
+ }
+
+ auto num_partitions = NumPartitions();
+ if (!num_partitions) return { };
+
+ return (num_partitions == 1) ? 17 : 29;
+}
+
+base::Optional<int> PhysicalASTCBlock::NumColorValues() const {
+ // If we have a void extent block, then we have four color values
+ if (IsVoidExtent()) {
+ return 4;
+ }
+
+ // If we have an illegal encoding, then we have no color values
+ if (IsIllegalEncoding()) return { };
+
+ return DecodeNumColorValues(astc_bits_);
+}
+
+void PhysicalASTCBlock::GetColorValuesInfo(int* const color_bits,
+ int* const color_range) const {
+ // Figure out the range possible for the number of values we have...
+ const int dual_plane_start_pos = DecodeDualPlaneBitStartPos(astc_bits_);
+ const int max_color_bits = dual_plane_start_pos - ColorStartBit().value();
+ const int num_color_values = NumColorValues().value();
+ for (int range = 255; range > 0; --range) {
+ const int bitcount =
+ IntegerSequenceCodec::GetBitCountForRange(num_color_values, range);
+ if (bitcount <= max_color_bits) {
+ if (color_bits != nullptr) {
+ *color_bits = bitcount;
+ }
+
+ if (color_range != nullptr) {
+ *color_range = range;
+ }
+ return;
+ }
+ }
+
+ assert(false &&
+ "This means that even if we have a range of one there aren't "
+ "enough bits to store the color values, and our encoding is "
+ "illegal.");
+}
+
+base::Optional<int> PhysicalASTCBlock::NumColorBits() const {
+ if (IsIllegalEncoding()) return { };
+
+ if (IsVoidExtent()) {
+ return 64;
+ }
+
+ int color_bits;
+ GetColorValuesInfo(&color_bits, nullptr);
+ return color_bits;
+}
+
+base::Optional<int> PhysicalASTCBlock::ColorValuesRange() const {
+ if (IsIllegalEncoding()) return { };
+
+ if (IsVoidExtent()) {
+ return (1 << 16) - 1;
+ }
+
+ int color_range;
+ GetColorValuesInfo(nullptr, &color_range);
+ return color_range;
+}
+
+base::Optional<int> PhysicalASTCBlock::NumPartitions() const {
+ // Error blocks have no partitions
+ if (IsIllegalEncoding()) return { };
+
+ // Void extent blocks have no partitions either
+ if (DecodeBlockMode(astc_bits_) == BlockMode::kVoidExtent) {
+ return { };
+ }
+
+ // All others have some number of partitions
+ return DecodeNumPartitions(astc_bits_);
+}
+
+base::Optional<int> PhysicalASTCBlock::PartitionID() const {
+ auto num_partitions = NumPartitions();
+ if (!num_partitions || num_partitions == 1) return { };
+
+ const uint64_t low_bits = astc_bits_.LowBits();
+ return static_cast<int>(base::GetBits(low_bits, 13, 10));
+}
+
+base::Optional<ColorEndpointMode> PhysicalASTCBlock::GetEndpointMode(
+ int partition) const {
+ // Error block?
+ if (IsIllegalEncoding()) return { };
+
+ // Void extent blocks have no endpoint modes
+ if (DecodeBlockMode(astc_bits_) == BlockMode::kVoidExtent) {
+ return { };
+ }
+
+ // Do we even have a CEM for this partition?
+ if (partition < 0 || DecodeNumPartitions(astc_bits_) <= partition) {
+ return { };
+ }
+
+ return DecodeEndpointMode(astc_bits_, partition);
+}
+
+} // namespace astc_codec
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/physical_astc_block.h b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/physical_astc_block.h
new file mode 100644
index 00000000000..1b04bddad41
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/physical_astc_block.h
@@ -0,0 +1,128 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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.
+
+#ifndef ASTC_CODEC_DECODER_PHYSICAL_ASTC_BLOCK_H_
+#define ASTC_CODEC_DECODER_PHYSICAL_ASTC_BLOCK_H_
+
+// The logic in this file is based on the ASTC specification, which can be
+// found here:
+// https://www.opengl.org/registry/specs/KHR/texture_compression_astc_hdr.txt
+
+#include "src/base/optional.h"
+#include "src/base/uint128.h"
+#include "src/decoder/types.h"
+
+#include <string>
+
+namespace astc_codec {
+
+// A PhysicalASTCBlock contains all 128 bits and the logic for decoding the
+// various internals of an ASTC block.
+class PhysicalASTCBlock {
+ public:
+ // The physical size in bytes of an ASTC block
+ static const size_t kSizeInBytes = 16;
+
+ // Initializes an ASTC block based on the encoded string.
+ explicit PhysicalASTCBlock(const std::string& encoded_block);
+ explicit PhysicalASTCBlock(const base::UInt128 astc_block);
+
+ // Returns the 128 bits of this ASTC block.
+ base::UInt128 GetBlockBits() const { return astc_bits_; }
+
+ // Weights are stored in a grid that may not have the same dimensions
+ // as the block dimensions. This allows us to see what the physical
+ // dimensions are of the grid.
+ base::Optional<std::array<int, 2>> WeightGridDims() const;
+
+ // The weight range is the maximum value a weight can take in the
+ // weight grid.
+ base::Optional<int> WeightRange() const;
+
+ // Returns true if the block encoding specifies a void-extent block. This
+ // kind of block stores a single color to be used for every pixel in the
+ // block.
+ bool IsVoidExtent() const;
+
+ // Returns the values (min_s, max_s, min_t, max_t) as defined in the void
+ // extent block as the range of texture coordinates for which this block is
+ // defined. (See Section C.2.23)
+ base::Optional<std::array<int, 4>> VoidExtentCoords() const;
+
+ // Returns true if the block contains two separate weight grids. One used
+ // for the channel returned by DualPlaneChannel() and one used by the other
+ // channels.
+ bool IsDualPlane() const;
+
+ // Returns the channel used as the "dual plane". The return value is only
+ // meaningful if IsDualPlane() returns true...
+ base::Optional<int> DualPlaneChannel() const;
+
+ // Returns a reason that the encoding doesn't adhere to the specification.
+ // If the encoding is legal, then this returns a nullptr. This allows us to
+ // still use code of the form:
+ //
+ // if (IsIllegalEncoding()) {
+ // ... error ...
+ // }
+ // ... no error ...
+ //
+ // However, it also helps with debugging since we can find problems with
+ // encodings a lot faster.
+ base::Optional<std::string> IsIllegalEncoding() const;
+
+ // Returns the number of weight bits present in this block.
+ base::Optional<int> NumWeightBits() const;
+
+ // Returns the starting position within the range [0, 127] of the
+ // weight data within the block.
+ base::Optional<int> WeightStartBit() const;
+
+ // Returns the number of endpoint pairs used in this block.
+ base::Optional<int> NumPartitions() const;
+
+ // Returns the seed used to determine the partition for a given
+ // (x, y) coordinate within the block. Determined using the
+ // block size and the function as described in the specification.
+ base::Optional<int> PartitionID() const;
+
+ // Returns the color endpoint mode for the given partition index.
+ base::Optional<ColorEndpointMode> GetEndpointMode(int partition) const;
+
+ // Returns the starting position within the range [0, 127] of the
+ // color data within the block.
+ base::Optional<int> ColorStartBit() const;
+
+ // Returns the number of integers used to represent the color endpoints.
+ base::Optional<int> NumColorValues() const;
+
+ // Returns the number of bits used to represent the color endpoints.
+ base::Optional<int> NumColorBits() const;
+
+ // Returns the maximum value that each of the encoded integers used to
+ // represent the color endpoints can take.
+ base::Optional<int> ColorValuesRange() const;
+
+ private:
+ const base::UInt128 astc_bits_;
+
+ // The logic to return the number of color bits and the color values range
+ // is very similar, so it's probably best to abstract it away into its own
+ // function.
+ void GetColorValuesInfo(int* color_bits, int* color_range) const;
+};
+
+} // namespace astc_codec
+
+#endif // ASTC_CODEC_DECODER_PHYSICAL_ASTC_BLOCK_H_
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/quantization.cc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/quantization.cc
new file mode 100644
index 00000000000..233d13cd230
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/quantization.cc
@@ -0,0 +1,462 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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 "src/decoder/quantization.h"
+#include "src/base/math_utils.h"
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <map>
+#include <memory>
+#include <vector>
+
+namespace astc_codec {
+
+namespace {
+
+// Trit unquantization procedure as described in Section C.2.13
+int GetUnquantizedTritValue(int trit, int bits, int range) {
+ int a = (bits & 1) ? 0x1FF : 0;
+ int b = 0, c = 0;
+ switch (range) {
+ case 5: {
+ b = 0;
+ c = 204;
+ }
+ break;
+
+ case 11: {
+ int x = (bits >> 1) & 0x1;
+ b = (x << 1) | (x << 2) | (x << 4) | (x << 8);
+ c = 93;
+ }
+ break;
+
+ case 23: {
+ int x = (bits >> 1) & 0x3;
+ b = x | (x << 2) | (x << 7);
+ c = 44;
+ }
+ break;
+
+ case 47: {
+ int x = (bits >> 1) & 0x7;
+ b = x | (x << 6);
+ c = 22;
+ }
+ break;
+
+ case 95: {
+ int x = (bits >> 1) & 0xF;
+ b = (x >> 2) | (x << 5);
+ c = 11;
+ }
+ break;
+
+ case 191: {
+ int x = (bits >> 1) & 0x1F;
+ b = (x >> 4) | (x << 4);
+ c = 5;
+ }
+ break;
+
+ default:
+ assert(false && "Illegal trit encoding");
+ break;
+ }
+
+ int t = trit * c + b;
+ t ^= a;
+ t = (a & 0x80) | (t >> 2);
+ return t;
+}
+
+// Quint unquantization procedure as described in Section C.2.13
+int GetUnquantizedQuintValue(int quint, int bits, int range) {
+ int a = (bits & 1) ? 0x1FF : 0;
+ int b = 0, c = 0;
+ switch (range) {
+ case 9: {
+ b = 0;
+ c = 113;
+ }
+ break;
+
+ case 19: {
+ int x = (bits >> 1) & 0x1;
+ b = (x << 2) | (x << 3) | (x << 8);
+ c = 54;
+ }
+ break;
+
+ case 39: {
+ int x = (bits >> 1) & 0x3;
+ b = (x >> 1) | (x << 1) | (x << 7);
+ c = 26;
+ }
+ break;
+
+ case 79: {
+ int x = (bits >> 1) & 0x7;
+ b = (x >> 1) | (x << 6);
+ c = 13;
+ }
+ break;
+
+ case 159: {
+ int x = (bits >> 1) & 0xF;
+ b = (x >> 3) | (x << 5);
+ c = 6;
+ }
+ break;
+
+ default:
+ assert(false && "Illegal quint encoding");
+ break;
+ }
+
+ int t = quint * c + b;
+ t ^= a;
+ t = (a & 0x80) | (t >> 2);
+ return t;
+}
+
+// Trit unquantization procedure as described in Section C.2.17. In the code
+// below, the variables a, b, and c correspond to the columns A, B, and C in
+// the specification.
+int GetUnquantizedTritWeight(int trit, int bits, int range) {
+ int a = (bits & 1) ? 0x7F : 0;
+ int b = 0, c = 0;
+ switch (range) {
+ case 2:
+ return (std::array<int, 3> {{ 0, 32, 63 }})[trit];
+
+ case 5:
+ c = 50;
+ b = 0;
+ break;
+
+ case 11: {
+ c = 23;
+ b = (bits >> 1) & 1;
+ b |= (b << 2) | (b << 6);
+ }
+ break;
+
+ case 23: {
+ c = 11;
+ b = (bits >> 1) & 0x3;
+ b |= (b << 5);
+ }
+ break;
+
+ default:
+ assert(false && "Illegal trit encoding");
+ break;
+ }
+
+ int t = trit * c + b;
+ t ^= a;
+ t = (a & 0x20) | (t >> 2);
+ return t;
+}
+
+// Quint unquantization procedure as described in Section C.2.17. In the code
+// below, the variables a, b, and c correspond to the columns A, B, and C in
+// the specification.
+int GetUnquantizedQuintWeight(int quint, int bits, int range) {
+ int a = (bits & 1) ? 0x7F : 0;
+ int b = 0, c = 0;
+ switch (range) {
+ case 4:
+ return (std::array<int, 5> {{ 0, 16, 32, 47, 63 }})[quint];
+
+ case 9:
+ c = 28;
+ b = 0;
+ break;
+
+ case 19: {
+ c = 13;
+ b = (bits >> 1) & 0x1;
+ b = (b << 1) | (b << 6);
+ }
+ break;
+
+ default:
+ assert(false && "Illegal quint encoding");
+ break;
+ }
+
+ int t = quint * c + b;
+ t ^= a;
+ t = (a & 0x20) | (t >> 2);
+ return t;
+}
+
+// A Quantization map allows us to convert to/from values that are quantized
+// according to the ASTC spec.
+class QuantizationMap {
+ public:
+ int Quantize(size_t x) const {
+ return x < quantization_map_.size() ? quantization_map_.at(x) : 0;
+ }
+
+ int Unquantize(size_t x) const {
+ return x < unquantization_map_.size() ? unquantization_map_.at(x) : 0;
+ }
+
+ protected:
+ QuantizationMap() { }
+ std::vector<int> quantization_map_;
+ std::vector<int> unquantization_map_;
+
+ void GenerateQuantizationMap() {
+ assert(unquantization_map_.size() > 1);
+ quantization_map_.clear();
+
+ // TODO(google) For weights, we don't need quantization values all the
+ // way up to 256, but it doesn't hurt -- just wastes memory, but the code
+ // is much cleaner this way
+ for (int i = 0; i < 256; ++i) {
+ int best_idx = 0;
+ int best_idx_score = 256;
+ int idx = 0;
+ for (int unquantized_val : unquantization_map_) {
+ const int diff = i - unquantized_val;
+ const int idx_score = diff * diff;
+ if (idx_score < best_idx_score) {
+ best_idx = idx;
+ best_idx_score = idx_score;
+ }
+ idx++;
+ }
+
+ quantization_map_.push_back(best_idx);
+ }
+ }
+};
+
+template<int (*UnquantizationFunc)(int, int, int)>
+class TritQuantizationMap : public QuantizationMap {
+ public:
+ explicit TritQuantizationMap(int range) : QuantizationMap() {
+ assert((range + 1) % 3 == 0);
+ const int num_bits_pow_2 = (range + 1) / 3;
+ const int num_bits =
+ num_bits_pow_2 == 0 ? 0 : base::Log2Floor(num_bits_pow_2);
+
+ for (int trit = 0; trit < 3; ++trit) {
+ for (int bits = 0; bits < (1 << num_bits); ++bits) {
+ unquantization_map_.push_back(UnquantizationFunc(trit, bits, range));
+ }
+ }
+
+ GenerateQuantizationMap();
+ }
+};
+
+template<int (*UnquantizationFunc)(int, int, int)>
+class QuintQuantizationMap : public QuantizationMap {
+ public:
+ explicit QuintQuantizationMap(int range) : QuantizationMap() {
+ assert((range + 1) % 5 == 0);
+ const int num_bits_pow_2 = (range + 1) / 5;
+ const int num_bits =
+ num_bits_pow_2 == 0 ? 0 : base::Log2Floor(num_bits_pow_2);
+
+ for (int quint = 0; quint < 5; ++quint) {
+ for (int bits = 0; bits < (1 << num_bits); ++bits) {
+ unquantization_map_.push_back(UnquantizationFunc(quint, bits, range));
+ }
+ }
+
+ GenerateQuantizationMap();
+ }
+};
+
+template<int TotalUnquantizedBits>
+class BitQuantizationMap : public QuantizationMap {
+ public:
+ explicit BitQuantizationMap<TotalUnquantizedBits>(int range)
+ : QuantizationMap() {
+ // Make sure that if we're using bits then we have a positive power of two.
+ assert(base::CountOnes(range + 1) == 1);
+
+ const int num_bits = base::Log2Floor(range + 1);
+ for (int bits = 0; bits <= range; ++bits) {
+ // Need to replicate bits until we fill up the bits
+ size_t unquantized = bits;
+ int num_unquantized_bits = num_bits;
+ while (num_unquantized_bits < TotalUnquantizedBits) {
+ const int num_dst_bits_to_shift_up =
+ std::min(num_bits, TotalUnquantizedBits - num_unquantized_bits);
+ const int num_src_bits_to_shift_down =
+ num_bits - num_dst_bits_to_shift_up;
+ unquantized <<= num_dst_bits_to_shift_up;
+ unquantized |= bits >> num_src_bits_to_shift_down;
+ num_unquantized_bits += num_dst_bits_to_shift_up;
+ }
+ assert(num_unquantized_bits == TotalUnquantizedBits);
+
+ unquantization_map_.push_back(int(unquantized));
+
+ // Fill half of the quantization map with the previous value for bits
+ // and the other half with the current value for bits
+ if (bits > 0) {
+ const size_t prev_unquant = unquantization_map_.at(bits - 1);
+ while (quantization_map_.size() <= (prev_unquant + unquantized) / 2) {
+ quantization_map_.push_back(bits - 1);
+ }
+ }
+ while (quantization_map_.size() <= unquantized) {
+ quantization_map_.push_back(bits);
+ }
+ }
+
+ assert(quantization_map_.size() == 1 << TotalUnquantizedBits);
+ }
+};
+
+using QMap = std::shared_ptr<QuantizationMap>;
+
+// Returns the quantization map for quantizing color values in [0, 255] with the
+// smallest range that can accommodate |r|
+static const QuantizationMap* GetQuantMapForValueRange(int r) {
+ // Endpoint values can be quantized using bits, trits, or quints. Here we
+ // store the quantization maps for each of the ranges that are supported by
+ // such an encoding. That way we can choose the proper quantization procedure
+ // based on the range of values rather than by having complicated switches and
+ // logic. We must use a std::map here instead of a std::unordered_map because
+ // of the assumption made in std::upper_bound about the iterators being from a
+ // poset.
+ static const auto* const kASTCEndpointQuantization = new std::map<int, QMap> {
+ { 5, QMap(new TritQuantizationMap<GetUnquantizedTritValue>(5)) },
+ { 7, QMap(new BitQuantizationMap<8>(7)) },
+ { 9, QMap(new QuintQuantizationMap<GetUnquantizedQuintValue>(9)) },
+ { 11, QMap(new TritQuantizationMap<GetUnquantizedTritValue>(11)) },
+ { 15, QMap(new BitQuantizationMap<8>(15)) },
+ { 19, QMap(new QuintQuantizationMap<GetUnquantizedQuintValue>(19)) },
+ { 23, QMap(new TritQuantizationMap<GetUnquantizedTritValue>(23)) },
+ { 31, QMap(new BitQuantizationMap<8>(31)) },
+ { 39, QMap(new QuintQuantizationMap<GetUnquantizedQuintValue>(39)) },
+ { 47, QMap(new TritQuantizationMap<GetUnquantizedTritValue>(47)) },
+ { 63, QMap(new BitQuantizationMap<8>(63)) },
+ { 79, QMap(new QuintQuantizationMap<GetUnquantizedQuintValue>(79)) },
+ { 95, QMap(new TritQuantizationMap<GetUnquantizedTritValue>(95)) },
+ { 127, QMap(new BitQuantizationMap<8>(127)) },
+ { 159, QMap(new QuintQuantizationMap<GetUnquantizedQuintValue>(159)) },
+ { 191, QMap(new TritQuantizationMap<GetUnquantizedTritValue>(191)) },
+ { 255, QMap(new BitQuantizationMap<8>(255)) },
+ };
+
+ assert(r < 256);
+ auto itr = kASTCEndpointQuantization->upper_bound(r);
+ if (itr != kASTCEndpointQuantization->begin()) {
+ return (--itr)->second.get();
+ }
+ return nullptr;
+}
+
+// Returns the quantization map for weight values in [0, 63] with the smallest
+// range that can accommodate |r|
+static const QuantizationMap* GetQuantMapForWeightRange(int r) {
+ // Similar to endpoint quantization, weights can also be stored using trits,
+ // quints, or bits. Here we store the quantization maps for each of the ranges
+ // that are supported by such an encoding.
+ static const auto* const kASTCWeightQuantization = new std::map<int, QMap> {
+ { 1, QMap(new BitQuantizationMap<6>(1)) },
+ { 2, QMap(new TritQuantizationMap<GetUnquantizedTritWeight>(2)) },
+ { 3, QMap(new BitQuantizationMap<6>(3)) },
+ { 4, QMap(new QuintQuantizationMap<GetUnquantizedQuintWeight>(4)) },
+ { 5, QMap(new TritQuantizationMap<GetUnquantizedTritWeight>(5)) },
+ { 7, QMap(new BitQuantizationMap<6>(7)) },
+ { 9, QMap(new QuintQuantizationMap<GetUnquantizedQuintWeight>(9)) },
+ { 11, QMap(new TritQuantizationMap<GetUnquantizedTritWeight>(11)) },
+ { 15, QMap(new BitQuantizationMap<6>(15)) },
+ { 19, QMap(new QuintQuantizationMap<GetUnquantizedQuintWeight>(19)) },
+ { 23, QMap(new TritQuantizationMap<GetUnquantizedTritWeight>(23)) },
+ { 31, QMap(new BitQuantizationMap<6>(31)) },
+ };
+
+ assert(r < 32);
+ auto itr = kASTCWeightQuantization->upper_bound(r);
+ if (itr != kASTCWeightQuantization->begin()) {
+ return (--itr)->second.get();
+ }
+ return nullptr;
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+
+int QuantizeCEValueToRange(int value, int range_max_value) {
+ assert(range_max_value >= kEndpointRangeMinValue);
+ assert(range_max_value <= 255);
+ assert(value >= 0);
+ assert(value <= 255);
+
+ const QuantizationMap* map = GetQuantMapForValueRange(range_max_value);
+ return map ? map->Quantize(value) : 0;
+}
+
+int UnquantizeCEValueFromRange(int value, int range_max_value) {
+ assert(range_max_value >= kEndpointRangeMinValue);
+ assert(range_max_value <= 255);
+ assert(value >= 0);
+ assert(value <= range_max_value);
+
+ const QuantizationMap* map = GetQuantMapForValueRange(range_max_value);
+ return map ? map->Unquantize(value) : 0;
+}
+
+int QuantizeWeightToRange(int weight, int range_max_value) {
+ assert(range_max_value >= 1);
+ assert(range_max_value <= kWeightRangeMaxValue);
+ assert(weight >= 0);
+ assert(weight <= 64);
+
+ // The quantization maps that define weight unquantization expect values in
+ // the range [0, 64), but the specification quantizes them to the range
+ // [0, 64] according to C.2.17. This is a slight hack similar to the one in
+ // the unquantization procedure to return the passed in unquantized value to
+ // [0, 64) prior to running it through the quantization procedure.
+ if (weight > 33) {
+ weight -= 1;
+ }
+ const QuantizationMap* map = GetQuantMapForWeightRange(range_max_value);
+ return map ? map->Quantize(weight) : 0;
+}
+
+int UnquantizeWeightFromRange(int weight, int range_max_value) {
+ assert(range_max_value >= 1);
+ assert(range_max_value <= kWeightRangeMaxValue);
+ assert(weight >= 0);
+ assert(weight <= range_max_value);
+ const QuantizationMap* map = GetQuantMapForWeightRange(range_max_value);
+ int dq = map ? map->Unquantize(weight) : 0;
+
+ // Quantized weights are returned in the range [0, 64), but they should be
+ // returned in the range [0, 64], so according to C.2.17 we need to add one
+ // to the result.
+ assert(dq < 64);
+ if (dq > 32) {
+ dq += 1;
+ }
+ return dq;
+}
+
+} // namespace astc_codec
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/quantization.h b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/quantization.h
new file mode 100644
index 00000000000..5f7239f14b9
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/quantization.h
@@ -0,0 +1,65 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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.
+
+#ifndef ASTC_CODEC_DECODER_QUANTIZATION_H_
+#define ASTC_CODEC_DECODER_QUANTIZATION_H_
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// ASTC Quantization procedures.
+//
+// The values stored in ASTC blocks tend to be stored in a range much more
+// restricted than the logical range used. For example, sometimes weights are
+// stored in the range from [0, 3] but are used in the range [0, 64]. The
+// process of translating a value to or from this range is known as quantization
+// and dequantization. The ranges to which these values can be (de)quantized
+// are defined by ISERange[Begin|End]() in integer_sequence_codec.h
+
+namespace astc_codec {
+
+// The minimum possible range for a pair of endpoints. If endpoints are
+// quantized to something smaller than this, then it would constitute an
+// illegal ASTC encoding.
+constexpr int kEndpointRangeMinValue = 5;
+
+// The maximum possible range for a weight value. If weights are quantized to
+// something larger than this, then it would constitute an illegal ASTC
+// encoding.
+constexpr int kWeightRangeMaxValue = 31;
+
+// Quantizes a value in the range [0, 255] to [0, |range|]. The quantized values
+// have no correlation to the input values, and there should be no implicit
+// assumptions made about their ordering. Valid values of |range_max_value| are
+// in the interval [5, 255]
+int QuantizeCEValueToRange(int value, int range_max_value);
+
+// Unquantizes a value in the range [0, |range|] to [0, 255]. Performs the
+// inverse procedure of QuantizeValueToRange. Valid values of |range_max_value|
+// are in the interval [5, 255]
+int UnquantizeCEValueFromRange(int value, int range_max_value);
+
+// Quantizes a weight in the range [0, 64] to [0, |range_max_value|]. The
+// quantized values have no correlation to the input values, and there should
+// be no implicit assumptions made about their ordering. Valid values of
+// |range_max_value| are in the interval [1, 31]
+int QuantizeWeightToRange(int weight, int range_max_value);
+
+// Unquantizes a weight in the range [0, |range_max_value|] to [0, 64]. Performs
+// the inverse procedure of QuantizeWeightToRange. Valid values of
+// |range_max_value| are in the interval [1, 31]
+int UnquantizeWeightFromRange(int weight, int range_max_value);
+
+} // namespace astc_codec
+
+#endif // ASTC_CODEC_DECODER_QUANTIZATION_H_
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/astc_fuzzer.cc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/astc_fuzzer.cc
new file mode 100644
index 00000000000..f152675dc4b
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/astc_fuzzer.cc
@@ -0,0 +1,36 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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.
+
+// ASTC fuzzing wrapper to help with fuzz testing.
+
+#include "src/decoder/codec.h"
+
+#include <benchmark/benchmark.h>
+
+#include <vector>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ std::string error;
+ std::unique_ptr<astc_codec::ASTCFile> file =
+ astc_codec::ASTCFile::LoadFromMemory(reinterpret_cast<const char*>(data),
+ size, &error);
+ if (file) {
+ std::vector<uint8_t> out_buffer(file->GetWidth() * file->GetHeight() * 4);
+ bool result = astc_codec::DecompressToImage(
+ *file, out_buffer.data(), out_buffer.size(), file->GetWidth() * 4);
+ benchmark::DoNotOptimize(result);
+ }
+
+ return 0;
+}
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/codec_test.cc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/codec_test.cc
new file mode 100644
index 00000000000..936eed3e613
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/codec_test.cc
@@ -0,0 +1,181 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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 "src/decoder/codec.h"
+#include "include/astc-codec/astc-codec.h"
+#include "src/decoder/test/image_utils.h"
+
+#include <gtest/gtest.h>
+
+#include <string>
+
+namespace astc_codec {
+
+static void PrintTo(FootprintType footprint, std::ostream* os) {
+ switch (footprint) {
+ case FootprintType::k4x4: *os << "FootprintType::k4x4"; break;
+ case FootprintType::k5x4: *os << "FootprintType::k5x4"; break;
+ case FootprintType::k5x5: *os << "FootprintType::k5x5"; break;
+ case FootprintType::k6x5: *os << "FootprintType::k6x5"; break;
+ case FootprintType::k6x6: *os << "FootprintType::k6x6"; break;
+ case FootprintType::k8x5: *os << "FootprintType::k8x5"; break;
+ case FootprintType::k8x6: *os << "FootprintType::k8x6"; break;
+ case FootprintType::k10x5: *os << "FootprintType::k10x5"; break;
+ case FootprintType::k10x6: *os << "FootprintType::k10x6"; break;
+ case FootprintType::k8x8: *os << "FootprintType::k8x8"; break;
+ case FootprintType::k10x8: *os << "FootprintType::k10x8"; break;
+ case FootprintType::k10x10: *os << "FootprintType::k10x10"; break;
+ case FootprintType::k12x10: *os << "FootprintType::k12x10"; break;
+ case FootprintType::k12x12: *os << "FootprintType::k12x12"; break;
+ default:
+ *os << "<Unexpected FootprintType "
+ << static_cast<uint32_t>(footprint) << ">";
+ }
+}
+
+namespace {
+
+using ::testing::TestWithParam;
+using ::testing::ValuesIn;
+
+ImageBuffer LoadGoldenImageWithAlpha(std::string basename) {
+ const std::string filename =
+ std::string("src/decoder/testdata/") + basename + ".bmp";
+ ImageBuffer result;
+ LoadGoldenBmp(filename, &result);
+ EXPECT_EQ(result.BytesPerPixel(), 4);
+ return result;
+}
+
+struct ImageTestParams {
+ std::string image_name;
+ FootprintType footprint;
+ size_t width;
+ size_t height;
+};
+
+static void PrintTo(const ImageTestParams& params, std::ostream* os) {
+ *os << "ImageTestParams(" << params.image_name << ", " << params.width << "x"
+ << params.height << ", ";
+ PrintTo(params.footprint, os);
+ *os << ")";
+}
+
+TEST(CodecTest, InvalidInput) {
+ const size_t valid_width = 16;
+ const size_t valid_height = 16;
+ const size_t valid_stride = valid_width * 4;
+
+ const std::vector<uint8_t> data(256);
+ std::vector<uint8_t> output(valid_width * valid_height * 4);
+
+ // Invalid footprint.
+ EXPECT_FALSE(ASTCDecompressToRGBA(
+ data.data(), data.size(), valid_width, valid_height,
+ FootprintType::kCount, output.data(), output.size(), valid_stride));
+
+ // Fail for 0 width or height.
+ EXPECT_FALSE(ASTCDecompressToRGBA(data.data(), data.size(), 0, valid_height,
+ FootprintType::k4x4, output.data(),
+ output.size(), valid_stride));
+ EXPECT_FALSE(ASTCDecompressToRGBA(data.data(), data.size(), valid_width, 0,
+ FootprintType::k4x4, output.data(),
+ output.size(), valid_stride));
+
+ // Fail for data size that's not a multiple of block size.
+ EXPECT_FALSE(ASTCDecompressToRGBA(
+ data.data(), data.size() - 1, valid_width, valid_height,
+ FootprintType::k4x4, output.data(), output.size(), valid_stride));
+ // Fail for data size that doesn't match the block count.
+ EXPECT_FALSE(ASTCDecompressToRGBA(
+ data.data(), data.size() - 16, valid_width, valid_height,
+ FootprintType::k4x4, output.data(), output.size(), valid_stride));
+
+ // Fail for invalid stride.
+ EXPECT_FALSE(ASTCDecompressToRGBA(
+ data.data(), data.size(), valid_width, valid_height, FootprintType::k4x4,
+ output.data(), output.size(), valid_stride - 1));
+
+ // Fail for invalid output size.
+ EXPECT_FALSE(ASTCDecompressToRGBA(
+ data.data(), data.size(), valid_width, valid_height, FootprintType::k4x4,
+ output.data(), output.size() - 1, valid_stride));
+}
+
+class CodecTest : public TestWithParam<ImageTestParams> {};
+
+TEST_P(CodecTest, PublicAPI) {
+ const auto& params = GetParam();
+ const std::string astc = LoadASTCFile(params.image_name);
+
+ ImageBuffer our_decoded_image;
+ our_decoded_image.Allocate(params.width, params.height, 4);
+ ASSERT_TRUE(ASTCDecompressToRGBA(
+ reinterpret_cast<const uint8_t*>(astc.data()), astc.size(), params.width,
+ params.height, params.footprint, our_decoded_image.Data().data(),
+ our_decoded_image.DataSize(), our_decoded_image.Stride()));
+
+ // Check that the decoded image is *very* similar to the library decoding
+ // of an ASTC texture. They may not be exact due to differences in how we
+ // convert a 16-bit float to an 8-bit integer.
+ ImageBuffer decoded_image = LoadGoldenImageWithAlpha(params.image_name);
+ CompareSumOfSquaredDifferences(decoded_image, our_decoded_image, 1.0);
+}
+
+TEST_P(CodecTest, DecompressToImage) {
+ const auto& params = GetParam();
+
+ std::string error;
+ std::unique_ptr<ASTCFile> image_file = ASTCFile::LoadFile(
+ std::string("src/decoder/testdata/") + params.image_name + ".astc",
+ &error);
+ ASSERT_TRUE(image_file) << "Failed to load " << params.image_name << ": "
+ << error;
+
+ ASSERT_TRUE(image_file->GetFootprint());
+ EXPECT_EQ(params.footprint, image_file->GetFootprint().value().Type());
+
+ ImageBuffer our_decoded_image;
+ our_decoded_image.Allocate(image_file->GetWidth(), image_file->GetHeight(),
+ 4);
+
+ ASSERT_TRUE(DecompressToImage(*image_file, our_decoded_image.Data().data(),
+ our_decoded_image.DataSize(),
+ our_decoded_image.Stride()));
+
+ // Check that the decoded image is *very* similar to the library decoding
+ // of an ASTC texture. They may not be exact due to differences in how we
+ // convert a 16-bit float to an 8-bit integer.
+ ImageBuffer decoded_image = LoadGoldenImageWithAlpha(params.image_name);
+ CompareSumOfSquaredDifferences(decoded_image, our_decoded_image, 1.0);
+}
+
+// Test to make sure that reading out color values from blocks in a real-world
+// image isn't terribly wrong, either.
+std::vector<ImageTestParams> GetTransparentImageTestParams() {
+ return {
+ // image_name astc footprint width height
+ { "atlas_small_4x4", FootprintType::k4x4, 256, 256 },
+ { "atlas_small_5x5", FootprintType::k5x5, 256, 256 },
+ { "atlas_small_6x6", FootprintType::k6x6, 256, 256 },
+ { "atlas_small_8x8", FootprintType::k8x8, 256, 256 },
+ };
+}
+
+INSTANTIATE_TEST_CASE_P(Transparent, CodecTest,
+ ValuesIn(GetTransparentImageTestParams()));
+
+} // namespace
+
+} // namespace astc_codec
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/endpoint_codec_test.cc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/endpoint_codec_test.cc
new file mode 100644
index 00000000000..de7d9cab483
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/endpoint_codec_test.cc
@@ -0,0 +1,463 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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 "src/decoder/endpoint_codec.h"
+#include "src/decoder/intermediate_astc_block.h"
+#include "src/decoder/test/image_utils.h"
+
+#include <random>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+namespace astc_codec {
+
+namespace {
+
+using ::testing::AllOf;
+using ::testing::AnyOf;
+using ::testing::Each;
+using ::testing::Eq;
+using ::testing::Ge;
+using ::testing::Le;
+using ::testing::Ne;
+using ::testing::Pointwise;
+using ::testing::SizeIs;
+using ::testing::Pair;
+
+constexpr std::array<EndpointEncodingMode, 6> kEndpointEncodingModes = {{
+ EndpointEncodingMode::kDirectLuma,
+ EndpointEncodingMode::kDirectLumaAlpha,
+ EndpointEncodingMode::kBaseScaleRGB,
+ EndpointEncodingMode::kBaseScaleRGBA,
+ EndpointEncodingMode::kDirectRGB,
+ EndpointEncodingMode::kDirectRGBA }};
+
+const std::array<std::pair<RgbaColor, RgbaColor>, 3> kBlueContractPairs = {{
+ std::make_pair(RgbaColor{{ 22, 18, 30, 59 }},
+ RgbaColor{{ 162, 148, 155, 59 }}),
+ std::make_pair(RgbaColor{{ 22, 30, 27, 36 }},
+ RgbaColor{{ 228, 221, 207, 36 }}),
+ std::make_pair(RgbaColor{{ 54, 60, 55, 255 }},
+ RgbaColor{{ 23, 30, 27, 255 }})
+ }};
+
+// Used to directly initialize std::pairs of colors with initializer lists
+// e.g. MakeColors({{ r, g, b, a }}, {{ r, g, b, a }});
+std::pair<RgbaColor, RgbaColor> MakeColors(RgbaColor&& a, RgbaColor&& b) {
+ return std::make_pair(a, b);
+}
+
+// Returns |high| and |low| as they would be decoded using the quantization
+// factor |quant| for the ColorEndpointMode |mode|.
+std::pair<RgbaColor, RgbaColor> TestColors(
+ RgbaColor low, RgbaColor high, int quant, EndpointEncodingMode mode) {
+ ColorEndpointMode astc_mode;
+ std::vector<int> encoded;
+ const bool needs_swap =
+ EncodeColorsForMode(low, high, quant, mode, &astc_mode, &encoded);
+
+ RgbaColor decoded_low, decoded_high;
+ DecodeColorsForMode(encoded, quant, astc_mode, &decoded_low, &decoded_high);
+
+ if (needs_swap) {
+ return std::make_pair(decoded_high, decoded_low);
+ } else {
+ return std::make_pair(decoded_low, decoded_high);
+ }
+}
+
+// Returns true if the argument tuple entries only differ by at most x.
+MATCHER_P(IsCloseTo, x, "") {
+ const auto& a = ::testing::get<0>(arg);
+ const auto& b = ::testing::get<1>(arg);
+ return (a > b) ? ((a - b) <= x) : ((b - a) <= x);
+}
+
+// Test to make sure that the range of values that we get as they are
+// quantized remains within what we pass as |quant|.
+TEST(EndpointCodecTest, QuantRanges) {
+ const RgbaColor low {{ 0, 0, 0, 0 }};
+ const RgbaColor high {{ 255, 255, 255, 255 }};
+
+ std::vector<int> result;
+ for (const auto& mode : kEndpointEncodingModes) {
+ for (int i = 5; i < 256; ++i) {
+ ColorEndpointMode astc_mode;
+ const bool needs_swap =
+ EncodeColorsForMode(low, high, i, mode, &astc_mode, &result);
+ EXPECT_EQ(result.size(), NumValuesForEncodingMode(mode)) << i;
+ EXPECT_EQ(result.size(), NumColorValuesForEndpointMode(astc_mode)) << i;
+
+ // ASTC mode shouldn't use base/offset when endpoints are so far apart.
+ EXPECT_THAT(astc_mode, Ne(ColorEndpointMode::kLDRRGBBaseOffset));
+ EXPECT_THAT(astc_mode, Ne(ColorEndpointMode::kLDRRGBABaseOffset));
+
+ EXPECT_THAT(result, Each(AllOf(Ge(0), Le(i))))
+ << "Mode: " << static_cast<int>(mode);
+ // We don't care if we need to swap the weights in this test
+ EXPECT_TRUE(needs_swap || !needs_swap);
+ }
+ }
+}
+
+// Test to make sure that each mode that directly encodes colors can effectively
+// encode both black and white
+TEST(EndpointCodecTest, ExtremeDirectEncodings) {
+ const RgbaColor kWhite {{ 255, 255, 255, 255 }};
+ const RgbaColor kBlack {{ 0, 0, 0, 255 }};
+
+ std::vector<int> encoded;
+ for (const auto& mode : kEndpointEncodingModes) {
+ for (int i = 5; i < 256; ++i) {
+ const auto expected = std::make_pair(kWhite, kBlack);
+ EXPECT_EQ(TestColors(kWhite, kBlack, i, mode), expected)
+ << "Range: " << i << ", Mode: " << static_cast<int>(mode);
+ }
+ }
+}
+
+// According to the spec, this is used for colors close to gray. The values
+// chosen here were according to the spec.
+TEST(EndpointCodecTest, UsesBlueContract) {
+ std::vector<int> vals = { 132, 127, 116, 112, 183, 180, 31, 22 };
+ EXPECT_TRUE(UsesBlueContract(255, ColorEndpointMode::kLDRRGBDirect, vals));
+ EXPECT_TRUE(UsesBlueContract(255, ColorEndpointMode::kLDRRGBADirect, vals));
+
+ // For the offset modes the only way to trigger the blue contract mode is if
+ // we force the subtraction in the decoding procedure (See section C.2.14 of
+ // the spec), so we need to set the 7th bit to 1 for all of the odd-numbered
+ // values
+ vals[1] &= 0xBF;
+ vals[3] &= 0xBF;
+ vals[5] &= 0xBF;
+ vals[7] &= 0xBF;
+
+ EXPECT_FALSE(
+ UsesBlueContract(255, ColorEndpointMode::kLDRRGBBaseOffset, vals));
+ EXPECT_FALSE(
+ UsesBlueContract(255, ColorEndpointMode::kLDRRGBABaseOffset, vals));
+
+ vals[1] |= 0x40;
+ vals[3] |= 0x40;
+ vals[5] |= 0x40;
+ vals[7] |= 0x40;
+
+ EXPECT_TRUE(
+ UsesBlueContract(255, ColorEndpointMode::kLDRRGBBaseOffset, vals));
+ EXPECT_TRUE(
+ UsesBlueContract(255, ColorEndpointMode::kLDRRGBABaseOffset, vals));
+
+ // All other LDR endpoint modes should return no blue contract
+ for (int max_val : { 255, 127, 11 }) {
+ for (auto mode : { ColorEndpointMode::kLDRLumaDirect,
+ ColorEndpointMode::kLDRLumaBaseOffset,
+ ColorEndpointMode::kLDRLumaAlphaDirect,
+ ColorEndpointMode::kLDRLumaAlphaBaseOffset,
+ ColorEndpointMode::kLDRRGBBaseScale,
+ ColorEndpointMode::kLDRRGBBaseScaleTwoA }) {
+ EXPECT_FALSE(UsesBlueContract(max_val, mode, vals));
+ }
+ }
+}
+
+// Make sure that encoding and decoding for the direct luminance mode works.
+TEST(EndpointCodecTest, LumaDirect) {
+ const auto mode = EndpointEncodingMode::kDirectLuma;
+
+ // With a 255 quantizer, all greys should be exact.
+ for (int i = 0; i < 255; ++i) {
+ for (int j = 0; j < 255; ++j) {
+ EXPECT_EQ(TestColors({{ i, i, i, 255 }}, {{ j, j, j, 255 }}, 255, mode),
+ MakeColors({{ i, i, i, 255 }}, {{ j, j, j, 255 }}));
+ }
+ }
+
+ // If we have almost grey, then they should encode to grey.
+ EXPECT_EQ(TestColors({{ 247, 248, 246, 255 }}, {{ 2, 3, 1, 255 }}, 255, mode),
+ MakeColors({{ 247, 247, 247, 255 }}, {{ 2, 2, 2, 255 }}));
+
+ EXPECT_EQ(TestColors({{ 80, 80, 50, 255 }}, {{ 99, 255, 6, 255 }}, 255, mode),
+ MakeColors({{ 70, 70, 70, 255 }}, {{ 120, 120, 120, 255 }}));
+
+ // If we have almost greys and a really small quantizer, it should be white
+ // and black (literally).
+ EXPECT_EQ(TestColors({{ 247, 248, 246, 255 }}, {{ 2, 3, 1, 255 }}, 15, mode),
+ MakeColors({{ 255, 255, 255, 255 }}, {{ 0, 0, 0, 255 }}));
+
+ // The average of 64, 127, and 192 is 127.666..., so it should round to
+ // 130 instead of 125.
+ EXPECT_EQ(TestColors({{ 64, 127, 192, 255 }}, {{ 0, 0, 0, 255 }}, 63, mode),
+ MakeColors({{ 130, 130, 130, 255 }}, {{ 0, 0, 0, 255 }}));
+
+ // If we have almost grey, then they should encode to grey -- similar to
+ // direct encoding since the encoded colors differ by < 63.
+ EXPECT_EQ(TestColors({{ 80, 80, 50, 255 }}, {{ 99, 255, 6, 255 }}, 255, mode),
+ MakeColors({{ 70, 70, 70, 255 }}, {{ 120, 120, 120, 255 }}));
+
+ // Low precision colors should still encode pretty well with base/offset.
+ EXPECT_EQ(TestColors({{ 35, 36, 38, 255 }}, {{ 42, 43, 40, 255 }}, 47, mode),
+ MakeColors({{ 38, 38, 38, 255 }}, {{ 43, 43, 43, 255 }}));
+
+ EXPECT_EQ(TestColors({{ 39, 42, 40, 255 }}, {{ 18, 20, 21, 255 }}, 39, mode),
+ MakeColors({{ 39, 39, 39, 255 }}, {{ 19, 19, 19, 255 }}));
+}
+
+// Test encoding and decoding for the base-offset luminance mode.
+TEST(EndpointCodecTest, LumaAlphaDirect) {
+ const auto mode = EndpointEncodingMode::kDirectLumaAlpha;
+
+ // With a 255 quantizer, all greys should be exact.
+ for (int i = 0; i < 255; ++i) {
+ for (int j = 0; j < 255; ++j) {
+ EXPECT_EQ(TestColors({{ i, i, i, j }}, {{ j, j, j, i }}, 255, mode),
+ MakeColors({{ i, i, i, j }}, {{ j, j, j, i }}));
+ }
+ }
+
+ // If we have almost grey, then they should encode to grey.
+ EXPECT_EQ(TestColors({{ 247, 248, 246, 250 }}, {{ 2, 3, 1, 172 }}, 255, mode),
+ MakeColors({{ 247, 247, 247, 250 }}, {{ 2, 2, 2, 172 }}));
+
+ EXPECT_EQ(TestColors({{ 80, 80, 50, 0 }}, {{ 99, 255, 6, 255 }}, 255, mode),
+ MakeColors({{ 70, 70, 70, 0 }}, {{ 120, 120, 120, 255 }}));
+
+ // If we have almost greys and a really small quantizer, it should be white
+ // and black (literally).
+ EXPECT_EQ(TestColors({{ 247, 248, 246, 253 }}, {{ 2, 3, 1, 3 }}, 15, mode),
+ MakeColors({{ 255, 255, 255, 255 }}, {{ 0, 0, 0, 0 }}));
+
+ // The average of 64, 127, and 192 is 127.666..., so it should round to
+ // 130 instead of 125. The alpha in this case is independent.
+ EXPECT_EQ(TestColors({{ 64, 127, 192, 127 }}, {{ 0, 0, 0, 20 }}, 63, mode),
+ MakeColors({{ 130, 130, 130, 125 }}, {{ 0, 0, 0, 20 }}));
+}
+
+// Test encoding for the direct RGB mode.
+TEST(EndpointCodecTest, RGBDirect) {
+ const auto mode = EndpointEncodingMode::kDirectRGB;
+
+ // Colors should be encoded exactly with a 255 quantizer.
+ std::mt19937 random(0xdeadbeef);
+ std::uniform_int_distribution<int> byte_distribution(0, 255);
+
+ for (int i = 0; i < 100; ++i) {
+ RgbaColor low, high;
+ for (auto& x : high) { x = byte_distribution(random); }
+ for (auto& x : low) { x = byte_distribution(random); }
+ high[3] = low[3] = 255; // RGB Direct mode has opaque alpha.
+
+ EXPECT_EQ(TestColors(low, high, 255, mode), std::make_pair(low, high))
+ << "Random iter: " << i;
+ }
+
+ // For each of the following tests, order of endpoints shouldn't have any
+ // bearing on the quantization properties, so we should be able to switch
+ // endpoints as we see fit and have them generate the same flipped encoded
+ // pairs.
+
+ EXPECT_EQ(TestColors({{ 64, 127, 192, 255 }}, {{ 0, 0, 0, 255 }}, 63, mode),
+ MakeColors({{ 65, 125, 190, 255 }}, {{ 0, 0, 0, 255 }}));
+
+ EXPECT_EQ(TestColors({{ 0, 0, 0, 255 }}, {{ 64, 127, 192, 255 }}, 63, mode),
+ MakeColors({{ 0, 0, 0, 255 }}, {{ 65, 125, 190, 255 }}));
+
+ EXPECT_EQ(TestColors({{ 1, 2, 94, 255 }}, {{ 168, 255, 13, 255 }}, 7, mode),
+ MakeColors({{ 0, 0, 109, 255 }}, {{ 182, 255, 0, 255 }}));
+
+ // Colors close to grey will likely need a blue contract.
+ EXPECT_EQ(TestColors(kBlueContractPairs[0].first,
+ kBlueContractPairs[0].second, 31, mode),
+ MakeColors({{ 24, 20, 33, 255 }}, {{ 160, 148, 156, 255 }}));
+
+ EXPECT_EQ(TestColors(kBlueContractPairs[0].second,
+ kBlueContractPairs[0].first, 31, mode),
+ MakeColors({{ 160, 148, 156, 255 }}, {{ 24, 20, 33, 255 }}));
+
+ EXPECT_EQ(TestColors(kBlueContractPairs[1].first,
+ kBlueContractPairs[1].second, 7, mode),
+ MakeColors({{ 18, 36, 36, 255 }}, {{ 237, 219, 219, 255 }}));
+
+ EXPECT_EQ(TestColors(kBlueContractPairs[1].second,
+ kBlueContractPairs[1].first, 7, mode),
+ MakeColors({{ 237, 219, 219, 255 }}, {{ 18, 36, 36, 255 }}));
+
+ // Colors close to grey (and each other) will likely need a blue contract AND
+ // use the offset mode for encoding
+ EXPECT_EQ(TestColors(kBlueContractPairs[2].first,
+ kBlueContractPairs[2].second, 31, mode),
+ MakeColors({{ 53, 59, 53, 255 }}, {{ 24, 30, 26, 255 }}));
+
+ EXPECT_EQ(TestColors(kBlueContractPairs[2].second,
+ kBlueContractPairs[2].first, 31, mode),
+ MakeColors({{ 24, 30, 26, 255 }}, {{ 53, 59, 53, 255 }}));
+
+ // Colors close to each other, but not to grey will likely only use the offset
+ // mode and not the blue-contract modes.
+ EXPECT_EQ(TestColors({{ 22, 148, 30, 59 }}, {{ 162, 18, 155, 59 }}, 31, mode),
+ MakeColors({{ 24, 148, 33, 255 }}, {{ 165, 16, 156, 255 }}));
+
+ EXPECT_EQ(TestColors({{ 162, 18, 155, 59 }}, {{ 22, 148, 30, 59 }}, 31, mode),
+ MakeColors({{ 165, 16, 156, 255 }}, {{ 24, 148, 33, 255 }}));
+}
+
+// Make sure that certain endpoint pairs result in the blue-contract path as
+// we'd expect, such that we can make sure that we're hitting all of the encode
+// paths.
+TEST(EndpointCodecTest, RGBDirectMakesBlueContract) {
+ constexpr int kEndpointRange = 31;
+ for (const auto& endpoint_pair : kBlueContractPairs) {
+ ColorEndpointMode astc_mode;
+ std::vector<int> vals;
+ bool needs_swap = EncodeColorsForMode(
+ endpoint_pair.first, endpoint_pair.second,
+ kEndpointRange, EndpointEncodingMode::kDirectRGB, &astc_mode, &vals);
+ (void)(needs_swap); // Don't really care.
+
+ EXPECT_TRUE(UsesBlueContract(kEndpointRange, astc_mode, vals));
+ }
+}
+
+// Make sure that encoding and decoding for the RGB base-scale mode works.
+TEST(EndpointCodecTest, RGBBaseScale) {
+ const auto mode = EndpointEncodingMode::kBaseScaleRGB;
+ const auto close_to = [](RgbaColor c, int x) {
+ return Pointwise(IsCloseTo(x), c);
+ };
+
+ // Identical colors should be encoded with a 255 scale factor. Since ASTC
+ // decodes the scaled color by doing (x * s) >> 8, the decoded color will be
+ // multiplied by 255/256. This might cause rounding errors sometimes, so we
+ // check that every channel only deviates by 1.
+ std::mt19937 random(0xdeadbeef);
+ std::uniform_int_distribution<int> byte_distribution(0, 255);
+
+ for (int i = 0; i < 100; ++i) {
+ RgbaColor color{{byte_distribution(random), byte_distribution(random),
+ byte_distribution(random), 255}};
+ const auto test_result = TestColors(color, color, 255, mode);
+ EXPECT_THAT(test_result, Pair(close_to(color, 1), close_to(color, 1)));
+ }
+
+ // Make sure that if we want to scale by e.g. 1/4 then we can do that exactly:
+ const RgbaColor low = {{ 20, 4, 40, 255 }};
+ const RgbaColor high = {{ 80, 16, 160, 255 }};
+ EXPECT_THAT(TestColors(low, high, 255, mode),
+ Pair(close_to(low, 0), close_to(high, 0)));
+
+ // And if we quantize it, then we get roughly the same thing. The scale factor
+ // should be representable with most quantization levels. The problem is that
+ // if we're off on the 'high' color, then we will be off on the 'low' color.
+ EXPECT_THAT(TestColors(low, high, 127, mode),
+ Pair(close_to(low, 1), close_to(high, 1)));
+
+ EXPECT_THAT(TestColors(low, high, 63, mode),
+ Pair(close_to(low, 1), close_to(high, 2)));
+
+ EXPECT_THAT(TestColors(low, high, 31, mode),
+ Pair(close_to(low, 1), close_to(high, 4)));
+
+ EXPECT_THAT(TestColors(low, high, 15, mode),
+ Pair(close_to(low, 2), close_to(high, 8)));
+}
+
+// Make sure that encoding and decoding for the RGB base-offset mode works.
+// Since we don't have a decoder, this is currently only a test that should work
+// based on reasoning about what's written in the spec.
+// TODO(krajcevski): Write an encoder.
+TEST(EndpointCodecTest, RGBBaseOffset) {
+ const auto test_colors = [](const RgbaColor& low, const RgbaColor& high) {
+ const RgbaColor diff = {{ high[0] - low[0], high[1] - low[1],
+ high[2] - low[2], high[3] - low[3] }};
+
+ std::vector<int> vals;
+ for (int i = 0; i < 3; ++i) {
+ // If the base is "large", then it grabs it's most significant bit from
+ // the offset value. Hence, we need to save it here.
+ const bool is_large = low[i] >= 128;
+ vals.push_back((low[i] * 2) & 0xFF);
+ vals.push_back(diff[i] * 2);
+
+ // Give the "large" bases their bits back.
+ if (is_large) {
+ vals.back() |= 0x80;
+ }
+ }
+
+ RgbaColor dec_low, dec_high;
+ DecodeColorsForMode(vals, 255, ColorEndpointMode::kLDRRGBBaseOffset,
+ &dec_low, &dec_high);
+
+ EXPECT_THAT(std::make_pair(dec_low, dec_high), Pair(Eq(low), Eq(high)));
+ };
+
+ // Test the "direct encoding" path.
+ test_colors({{ 80, 16, 112, 255 }}, {{ 87, 18, 132, 255 }});
+ test_colors({{ 80, 74, 82, 255 }}, {{ 90, 92, 110, 255 }});
+ test_colors({{ 0, 0, 0, 255 }}, {{ 2, 2, 2, 255 }});
+
+ // Identical endpoints should always encode exactly, provided they satisfy the
+ // requirements for the base encoding.
+ std::mt19937 random(0xdeadbeef);
+ std::uniform_int_distribution<int> byte_distribution(0, 255);
+ for (int i = 0; i < 100; ++i) {
+ RgbaColor color{{byte_distribution(random), byte_distribution(random),
+ byte_distribution(random), 255}};
+ if ((color[0] | color[1] | color[2]) & 1) {
+ continue;
+ }
+ test_colors(color, color);
+ }
+
+ // TODO(google): Test the "blue contract" path.
+}
+
+// Make sure that we can decode colors that are given to us straight out of the
+// ASTC codec.
+TEST(EndpointCodecTest, DecodeCheckerboard) {
+ const RgbaColor kWhite {{ 255, 255, 255, 255 }};
+ const RgbaColor kBlack {{ 0, 0, 0, 255 }};
+
+ const std::string astc = LoadASTCFile("checkerboard");
+ for (int i = 0; i < astc.size(); i += 16) {
+ base::UInt128 block;
+ memcpy(&block, &astc[i], sizeof(block));
+
+ const auto intermediate = UnpackIntermediateBlock(PhysicalASTCBlock(block));
+ ASSERT_TRUE(intermediate) << "Block is void extent???";
+
+ const auto block_data = &intermediate.value();
+ ASSERT_THAT(block_data->endpoints, SizeIs(Eq(1)));
+
+ const int color_range = EndpointRangeForBlock(*block_data);
+ const auto& endpoints = block_data->endpoints[0];
+
+ RgbaColor low, high;
+ DecodeColorsForMode(endpoints.colors, color_range, endpoints.mode,
+ &low, &high);
+
+ // Expect that the endpoints are black and white, but either order.
+ EXPECT_THAT(std::make_pair(low, high),
+ AnyOf(
+ Pair(Eq(kWhite), Eq(kBlack)),
+ Pair(Eq(kBlack), Eq(kWhite))));
+ }
+}
+
+} // namespace
+
+} // namespace astc_codec
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/footprint_test.cc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/footprint_test.cc
new file mode 100644
index 00000000000..6aef98a767b
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/footprint_test.cc
@@ -0,0 +1,97 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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 "src/decoder/footprint.h"
+
+#include <array>
+#include <tuple>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+namespace astc_codec {
+
+namespace {
+
+TEST(FootprintTest, ParseAstcFootprintString) {
+ using ASTCTestPair = std::pair<std::string, Footprint>;
+ const std::array<ASTCTestPair, Footprint::NumValidFootprints()>
+ valid_footprints {{
+ std::make_pair("4x4", Footprint::Get4x4()),
+ std::make_pair("5x4", Footprint::Get5x4()),
+ std::make_pair("5x5", Footprint::Get5x5()),
+ std::make_pair("6x5", Footprint::Get6x5()),
+ std::make_pair("6x6", Footprint::Get6x6()),
+ std::make_pair("8x5", Footprint::Get8x5()),
+ std::make_pair("8x6", Footprint::Get8x6()),
+ std::make_pair("8x8", Footprint::Get8x8()),
+ std::make_pair("10x5", Footprint::Get10x5()),
+ std::make_pair("10x6", Footprint::Get10x6()),
+ std::make_pair("10x8", Footprint::Get10x8()),
+ std::make_pair("10x10", Footprint::Get10x10()),
+ std::make_pair("12x10", Footprint::Get12x10()),
+ std::make_pair("12x12", Footprint::Get12x12())
+ }};
+
+ for (const auto& test : valid_footprints) {
+ base::Optional<Footprint> footprint = Footprint::Parse(test.first.c_str());
+ EXPECT_TRUE(footprint);
+ EXPECT_EQ(test.second, footprint.value());
+ }
+
+ EXPECT_DEBUG_DEATH(EXPECT_FALSE(Footprint::Parse("")), "");
+ EXPECT_DEBUG_DEATH(EXPECT_FALSE(Footprint::Parse("3")), "");
+ EXPECT_DEBUG_DEATH(EXPECT_FALSE(Footprint::Parse("x")), "");
+ // Validly formed but out-of-bounds dimensions do not assert, otherwise
+ // malformed ASTC files could crash the decoder in debug builds.
+ EXPECT_FALSE(Footprint::Parse("9999999999x10"));
+ EXPECT_DEBUG_DEATH(EXPECT_FALSE(Footprint::Parse("ax8")), "");
+ EXPECT_DEBUG_DEATH(EXPECT_FALSE(Footprint::Parse("2x3x4")), "");
+ EXPECT_DEBUG_DEATH(EXPECT_FALSE(Footprint::Parse("-3x4")), "");
+ EXPECT_FALSE(Footprint::Parse("10x4"));
+}
+
+TEST(FootprintTest, Bitrates) {
+ EXPECT_NEAR(Footprint::Get4x4().Bitrate(), 8.f, 0.01f);
+ EXPECT_NEAR(Footprint::Get5x4().Bitrate(), 6.4f, 0.01f);
+ EXPECT_NEAR(Footprint::Get5x5().Bitrate(), 5.12f, 0.01f);
+ EXPECT_NEAR(Footprint::Get6x5().Bitrate(), 4.27f, 0.01f);
+ EXPECT_NEAR(Footprint::Get6x6().Bitrate(), 3.56f, 0.01f);
+ EXPECT_NEAR(Footprint::Get8x5().Bitrate(), 3.20f, 0.01f);
+ EXPECT_NEAR(Footprint::Get8x6().Bitrate(), 2.67f, 0.01f);
+ EXPECT_NEAR(Footprint::Get8x8().Bitrate(), 2.00f, 0.01f);
+ EXPECT_NEAR(Footprint::Get10x5().Bitrate(), 2.56f, 0.01f);
+ EXPECT_NEAR(Footprint::Get10x6().Bitrate(), 2.13f, 0.01f);
+ EXPECT_NEAR(Footprint::Get10x8().Bitrate(), 1.60f, 0.01f);
+ EXPECT_NEAR(Footprint::Get10x10().Bitrate(), 1.28f, 0.01f);
+ EXPECT_NEAR(Footprint::Get12x10().Bitrate(), 1.07f, 0.01f);
+ EXPECT_NEAR(Footprint::Get12x12().Bitrate(), 0.89f, 0.01f);
+}
+
+TEST(FootprintTest, StorageRequirements) {
+ auto footprint = Footprint::Get10x8();
+ EXPECT_EQ(footprint.Width(), 10);
+ EXPECT_EQ(footprint.Height(), 8);
+
+ // If we have 8x8 blocks, then we have 64*16 = 1024 bytes.
+ EXPECT_EQ(footprint.StorageRequirements(80, 64), 1024);
+
+ // If our block is a little smaller this still counts because we need to
+ // cover a partial block with a fully encoded one.
+ EXPECT_EQ(footprint.StorageRequirements(79, 63), 1024);
+}
+
+} // namespace
+
+} // namespace astc_codec
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/image_utils.h b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/image_utils.h
new file mode 100644
index 00000000000..1e5000a301c
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/image_utils.h
@@ -0,0 +1,217 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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 <gtest/gtest.h>
+
+#include <fstream>
+#include <vector>
+
+static constexpr size_t kMaxVectorOutput = 128;
+
+class ImageBuffer {
+ public:
+ static constexpr size_t Align = 4;
+
+ void Allocate(size_t width, size_t height, size_t bytes_per_pixel) {
+ width_ = width;
+ height_ = height;
+ bytes_per_pixel_ = bytes_per_pixel;
+ stride_ = AlignBytes(width * bytes_per_pixel);
+ data_.resize(stride_ * height);
+ }
+
+ uint8_t* operator()(size_t x, size_t y) {
+ assert(x < width_ && y < height_);
+ return &data_[y * Stride() + x * bytes_per_pixel_];
+ }
+
+ size_t Stride() const { return stride_; }
+ size_t BytesPerPixel() const { return bytes_per_pixel_; }
+
+ std::vector<uint8_t>& Data() { return data_; }
+ const std::vector<uint8_t>& Data() const { return data_; }
+ size_t DataSize() const { return data_.size(); }
+
+ private:
+ size_t AlignBytes(size_t bytes) const {
+ return (bytes + (Align - 1)) / Align * Align;
+ }
+
+ size_t width_ = 0;
+ size_t height_ = 0;
+ size_t stride_ = 0;
+ size_t bytes_per_pixel_ = 0;
+ std::vector<uint8_t> data_;
+};
+
+namespace std {
+static void PrintTo(const vector<uint8_t>& vec, ostream* os) {
+ ios::fmtflags origFlags(os->flags());
+
+ *os << '{';
+ size_t count = 0;
+ for (vector<uint8_t>::const_iterator it = vec.begin(); it != vec.end();
+ ++it, ++count) {
+ if (count > 0) {
+ *os << ", ";
+ }
+
+ if (count == kMaxVectorOutput) {
+ *os << "... ";
+ break;
+ }
+
+ if ((count % 16) == 0) {
+ *os << "\n";
+ }
+
+ if (*it == 0) {
+ *os << " ";
+ } else {
+ *os << "0x" << std::hex << std::uppercase << std::setw(2)
+ << std::setfill('0') << int(*it) << std::dec;
+ }
+ }
+
+ *os << '}';
+
+ os->flags(origFlags);
+}
+} // namespace std
+
+static inline std::string LoadFile(const std::string& path) {
+ std::ifstream is(path, std::ios::binary);
+ EXPECT_TRUE(is) << "Failed to load file " << path;
+ if (!is) {
+ return "";
+ }
+
+ std::ostringstream ss;
+ ss << is.rdbuf();
+ return ss.str();
+}
+
+static inline std::string LoadASTCFile(const std::string& basename) {
+ const std::string filename =
+ std::string("src/decoder/testdata/") + basename + ".astc";
+
+ std::string result = LoadFile(filename);
+ // Don't parse the header here, we already know what kind of astc encoding it
+ // is.
+ if (result.size() < 16) {
+ return "";
+ } else {
+ return result.substr(16);
+ }
+}
+
+static inline void LoadGoldenBmp(const std::string& path, ImageBuffer* result) {
+ constexpr size_t kBmpHeaderSize = 54;
+
+ SCOPED_TRACE(testing::Message() << "LoadGoldenBmp " << path);
+
+ const std::string data = LoadFile(path);
+ ASSERT_FALSE(data.empty()) << "Failed to open golden image: " << path;
+
+ ASSERT_GE(data.size(), kBmpHeaderSize);
+ ASSERT_EQ('B', data[0]);
+ ASSERT_EQ('M', data[1]);
+
+ uint32_t dataPos = *reinterpret_cast<const uint32_t*>(&data[0x0A]);
+ uint32_t imageSize = *reinterpret_cast<const uint32_t*>(&data[0x22]);
+ const uint16_t bitsPerPixel = *reinterpret_cast<const uint16_t*>(&data[0x1C]);
+ int width = *reinterpret_cast<const int*>(&data[0x12]);
+ int height = *reinterpret_cast<const int*>(&data[0x16]);
+
+ SCOPED_TRACE(testing::Message()
+ << "dataPos=" << dataPos << ", imageSize=" << imageSize
+ << ", bitsPerPixel=" << bitsPerPixel << ", width=" << width
+ << ", height=" << height);
+
+ if (height < 0) {
+ height = -height;
+ }
+
+ if (imageSize == 0) {
+ imageSize = width * height * 3;
+ }
+
+ if (dataPos < kBmpHeaderSize) {
+ dataPos = kBmpHeaderSize;
+ }
+
+ ASSERT_TRUE(bitsPerPixel == 24 || bitsPerPixel == 32)
+ << "BMP bits per pixel mismatch, expected 24 or 32";
+
+ result->Allocate(width, height, bitsPerPixel == 24 ? 3 : 4);
+ ASSERT_LE(imageSize, result->DataSize());
+
+ std::vector<uint8_t>& resultData = result->Data();
+ const size_t stride = result->Stride();
+
+ // Copy the data row-by-row to make sure that stride is right.
+ for (size_t row = 0; row < static_cast<size_t>(height); ++row) {
+ memcpy(&resultData[row * stride], &data[dataPos + row * stride],
+ width * bitsPerPixel / 8);
+ }
+
+ if (bitsPerPixel == 32) {
+ // Swizzle the data from ABGR to ARGB.
+ for (size_t row = 0; row < static_cast<size_t>(height); ++row) {
+ uint8_t* rowData = resultData.data() + row * stride;
+
+ for (size_t i = 3; i < stride; i += 4) {
+ const uint8_t b = rowData[i - 3];
+ rowData[i - 3] = rowData[i - 1];
+ rowData[i - 1] = b;
+ }
+ }
+ } else {
+ // Swizzle the data from BGR to RGB.
+ for (size_t row = 0; row < static_cast<size_t>(height); ++row) {
+ uint8_t* rowData = resultData.data() + row * stride;
+
+ for (size_t i = 2; i < stride; i += 3) {
+ const uint8_t tmp = rowData[i - 2];
+ rowData[i - 2] = rowData[i];
+ rowData[i] = tmp;
+ }
+ }
+ }
+}
+
+static inline void CompareSumOfSquaredDifferences(const ImageBuffer& golden,
+ const ImageBuffer& image,
+ double threshold) {
+ ASSERT_EQ(golden.DataSize(), image.DataSize());
+ ASSERT_EQ(golden.Stride(), image.Stride());
+ ASSERT_EQ(golden.BytesPerPixel(), image.BytesPerPixel());
+
+ const std::vector<uint8_t>& image_data = image.Data();
+ const std::vector<uint8_t>& golden_data = golden.Data();
+
+ double sum = 0.0;
+ for (size_t i = 0; i < image_data.size(); ++i) {
+ const double diff = static_cast<double>(image_data[i]) - golden_data[i];
+ sum += diff * diff;
+ }
+
+ EXPECT_LE(sum, threshold * image_data.size())
+ << "Per pixel " << (sum / image_data.size())
+ << ", expected <= " << threshold;
+ if (sum > threshold * image_data.size()) {
+ // Fall back to comparison which will dump first chunk of vector.
+ EXPECT_EQ(golden_data, image_data);
+ }
+}
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/integer_sequence_codec_test.cc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/integer_sequence_codec_test.cc
new file mode 100644
index 00000000000..120e8b07b73
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/integer_sequence_codec_test.cc
@@ -0,0 +1,336 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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 "src/decoder/integer_sequence_codec.h"
+#include "src/base/uint128.h"
+
+#include <random>
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+using astc_codec::base::UInt128;
+using astc_codec::base::BitStream;
+using astc_codec::IntegerSequenceCodec;
+using astc_codec::IntegerSequenceEncoder;
+using astc_codec::IntegerSequenceDecoder;
+
+namespace {
+
+// Make sure that the counts returned for a specific range match what's
+// expected. In particular, make sure that it fits with Table C.2.7
+TEST(ASTCIntegerSequenceCodecTest, TestGetCountsForRange) {
+ std::array<int, 3> kExpectedCounts[31] = {
+ {{ 0, 0, 1 }}, // 1
+ {{ 1, 0, 0 }}, // 2
+ {{ 0, 0, 2 }}, // 3
+ {{ 0, 1, 0 }}, // 4
+ {{ 1, 0, 1 }}, // 5
+ {{ 0, 0, 3 }}, // 6
+ {{ 0, 0, 3 }}, // 7
+ {{ 0, 1, 1 }}, // 8
+ {{ 0, 1, 1 }}, // 9
+ {{ 1, 0, 2 }}, // 10
+ {{ 1, 0, 2 }}, // 11
+ {{ 0, 0, 4 }}, // 12
+ {{ 0, 0, 4 }}, // 13
+ {{ 0, 0, 4 }}, // 14
+ {{ 0, 0, 4 }}, // 15
+ {{ 0, 1, 2 }}, // 16
+ {{ 0, 1, 2 }}, // 17
+ {{ 0, 1, 2 }}, // 18
+ {{ 0, 1, 2 }}, // 19
+ {{ 1, 0, 3 }}, // 20
+ {{ 1, 0, 3 }}, // 21
+ {{ 1, 0, 3 }}, // 22
+ {{ 1, 0, 3 }}, // 23
+ {{ 0, 0, 5 }}, // 24
+ {{ 0, 0, 5 }}, // 25
+ {{ 0, 0, 5 }}, // 26
+ {{ 0, 0, 5 }}, // 27
+ {{ 0, 0, 5 }}, // 28
+ {{ 0, 0, 5 }}, // 29
+ {{ 0, 0, 5 }}, // 30
+ {{ 0, 0, 5 }}, // 31
+ };
+
+ int t, q, b;
+ for (int i = 1; i < 32; ++i) {
+ IntegerSequenceCodec::GetCountsForRange(i, &t, &q, &b);
+ EXPECT_EQ(t, kExpectedCounts[i - 1][0]);
+ EXPECT_EQ(q, kExpectedCounts[i - 1][1]);
+ EXPECT_EQ(b, kExpectedCounts[i - 1][2]);
+ }
+
+ ASSERT_DEATH(IntegerSequenceCodec::GetCountsForRange(0, &t, &q, &b), "");
+ ASSERT_DEATH(IntegerSequenceCodec::GetCountsForRange(256, &t, &q, &b), "");
+
+ IntegerSequenceCodec::GetCountsForRange(1, &t, &q, &b);
+ EXPECT_EQ(t, 0);
+ EXPECT_EQ(q, 0);
+ EXPECT_EQ(b, 1);
+}
+
+// Test to make sure that we're calculating the number of bits needed to
+// encode a given number of values based on the range of the values.
+TEST(ASTCIntegerSequenceCodecTest, TestNumBitsForCounts) {
+ int trits = 0;
+ int quints = 0;
+ int bits = 0;
+
+ // A range of one should have single bits, so n 1-bit values should be n bits.
+ trits = 0;
+ quints = 0;
+ bits = 1;
+ for (int i = 0; i < 64; ++i) {
+ EXPECT_EQ(IntegerSequenceCodec::GetBitCount(i, trits, quints, bits), i);
+ EXPECT_EQ(IntegerSequenceCodec::GetBitCountForRange(i, 1), i);
+ }
+
+ // Similarly, N two-bit values should be 2n bits...
+ trits = 0;
+ quints = 0;
+ bits = 2;
+ for (int i = 0; i < 64; ++i) {
+ int bit_counts = IntegerSequenceCodec::GetBitCount(i, trits, quints, bits);
+ EXPECT_EQ(bit_counts, 2 * i);
+ EXPECT_EQ(IntegerSequenceCodec::GetBitCountForRange(i, 3), 2 * i);
+ }
+
+ // Trits are a bit more complicated -- there are five trits in a block, so
+ // if we encode 15 values with 3 bits each in trits, we'd get three blocks,
+ // each with eight bits of trits.
+ trits = 1;
+ quints = 0;
+ bits = 3;
+ EXPECT_EQ(IntegerSequenceCodec::GetBitCount(15, trits, quints, bits),
+ 8 * 3 + 15 * 3);
+ EXPECT_EQ(IntegerSequenceCodec::GetBitCountForRange(15, 23),
+ IntegerSequenceCodec::GetBitCount(15, trits, quints, bits));
+
+ // However, if instead we encode 13 values, we don't need to use the remaining
+ // two values, so we only need bits as they will be encoded. As it turns out,
+ // this means we can avoid three bits in the final block (one for the high
+ // order trit encoding and two for one of the values), resulting in 47 bits.
+ trits = 1;
+ quints = 0;
+ bits = 2;
+ EXPECT_EQ(IntegerSequenceCodec::GetBitCount(13, trits, quints, bits), 47);
+ EXPECT_EQ(IntegerSequenceCodec::GetBitCountForRange(13, 11),
+ IntegerSequenceCodec::GetBitCount(13, trits, quints, bits));
+
+ // Quints have a similar property -- if we encode six values using a quint and
+ // four bits, then we have two quint blocks each with three values and a seven
+ // bit encoded quint triplet...
+ trits = 0;
+ quints = 1;
+ bits = 4;
+ EXPECT_EQ(IntegerSequenceCodec::GetBitCount(6, trits, quints, bits),
+ 7 * 2 + 6 * 4);
+ EXPECT_EQ(IntegerSequenceCodec::GetBitCountForRange(6, 79),
+ IntegerSequenceCodec::GetBitCount(6, trits, quints, bits));
+
+ // If we have fewer values than blocks we can again avoid about 2 + nbits
+ // bits...
+ trits = 0;
+ quints = 1;
+ bits = 3;
+ EXPECT_EQ(IntegerSequenceCodec::GetBitCount(7, trits, quints, bits),
+ /* first two quint blocks */ 7 * 2 +
+ /* first two blocks of bits */ 6 * 3 +
+ /* last quint block without the high order four bits */ 3 +
+ /* last block with one set of three bits */ 3);
+}
+
+// Tests that the encoder knows how to encode values of the form 5*2^k.
+TEST(ASTCIntegerSequenceCodecTest, TestQuintCodec) {
+ // In this case, k = 4
+
+ // Setup bit src/sink
+ BitStream<UInt128> bit_sink;
+
+ const int kValueRange = 79;
+ IntegerSequenceEncoder enc(kValueRange);
+ enc.AddValue(3);
+ enc.AddValue(79);
+ enc.AddValue(37);
+ enc.Encode(&bit_sink);
+
+ // quint: 1000101 m0: 0011 m1: 1111 m2: 0101
+ // 100 0100 0111 1101 0010
+ // interleaved 10m200m1101m0
+ // should be 100 1010 0111 1101 0011 = 0x4A7D3
+ EXPECT_EQ(bit_sink.Bits(), 19);
+
+ uint64_t encoded = 0;
+ bit_sink.GetBits(19, &encoded);
+ EXPECT_EQ(encoded, 0x4A7D3);
+
+ // Now check that decoding it works as well
+ BitStream<UInt128> bit_src(encoded, 19);
+
+ IntegerSequenceDecoder dec(kValueRange);
+ auto decoded_vals = dec.Decode(3, &bit_src);
+ ASSERT_EQ(decoded_vals.size(), 3);
+ EXPECT_EQ(decoded_vals[0], 3);
+ EXPECT_EQ(decoded_vals[1], 79);
+ EXPECT_EQ(decoded_vals[2], 37);
+}
+
+// Tests that the encoder knows how to encode values of the form 3*2^k.
+TEST(ASTCIntegerSequenceCodecTest, TestTritCodec) {
+ uint64_t encoded = 0;
+
+ // Setup bit src/sink
+ BitStream<UInt128> bit_sink(encoded, 0);
+
+ const int kValueRange = 11;
+ IntegerSequenceEncoder enc(kValueRange);
+ enc.AddValue(7);
+ enc.AddValue(5);
+ enc.AddValue(3);
+ enc.AddValue(6);
+ enc.AddValue(10);
+ enc.Encode(&bit_sink);
+
+ EXPECT_EQ(bit_sink.Bits(), 18);
+
+ bit_sink.GetBits(18, &encoded);
+ EXPECT_EQ(encoded, 0x37357);
+
+ // Now check that decoding it works as well
+ BitStream<UInt128> bit_src(encoded, 19);
+
+ IntegerSequenceDecoder dec(kValueRange);
+ auto decoded_vals = dec.Decode(5, &bit_src);
+ ASSERT_EQ(decoded_vals.size(), 5);
+ EXPECT_EQ(decoded_vals[0], 7);
+ EXPECT_EQ(decoded_vals[1], 5);
+ EXPECT_EQ(decoded_vals[2], 3);
+ EXPECT_EQ(decoded_vals[3], 6);
+ EXPECT_EQ(decoded_vals[4], 10);
+}
+
+// Test a specific quint encoding/decoding. This test makes sure that the way we
+// encode and decode integer sequences matches what we should expect out of the
+// reference ASTC encoder.
+TEST(ASTCIntegerSequenceCodecTest, TestDecodeThenEncode) {
+ std::vector<int> vals = {{ 16, 18, 17, 4, 7, 14, 10, 0 }};
+ const uint64_t kValEncoding = 0x2b9c83dc;
+
+ BitStream<UInt128> bit_src(kValEncoding, 64);
+ IntegerSequenceDecoder dec(19);
+ auto decoded_vals = dec.Decode(8, &bit_src);
+ ASSERT_EQ(decoded_vals.size(), vals.size());
+ for (size_t i = 0; i < decoded_vals.size(); ++i) {
+ EXPECT_EQ(decoded_vals[i], vals[i]);
+ }
+
+ // Setup bit src/sink
+ BitStream<UInt128> bit_sink;
+ IntegerSequenceEncoder enc(19);
+ for (const auto& v : vals) {
+ enc.AddValue(v);
+ }
+ enc.Encode(&bit_sink);
+ EXPECT_EQ(bit_sink.Bits(), 35);
+
+ uint64_t encoded = 0;
+ EXPECT_TRUE(bit_sink.GetBits(35, &encoded));
+ EXPECT_EQ(encoded, kValEncoding)
+ << std::hex << encoded << " -- " << kValEncoding;
+}
+
+// Same as the previous test, except it uses a trit encoding rather than a
+// quint encoding.
+TEST(ASTCIntegerSequenceCodecTest, TestDecodeThenEncodeTrits) {
+ std::vector<int> vals = {{ 6, 0, 0, 2, 0, 0, 0, 0, 8, 0, 0, 0, 0, 8, 8, 0 }};
+ const uint64_t kValEncoding = 0x0004c0100001006ULL;
+
+ BitStream<UInt128> bit_src(kValEncoding, 64);
+ IntegerSequenceDecoder dec(11);
+ auto decoded_vals = dec.Decode(vals.size(), &bit_src);
+ ASSERT_EQ(decoded_vals.size(), vals.size());
+ for (size_t i = 0; i < decoded_vals.size(); ++i) {
+ EXPECT_EQ(decoded_vals[i], vals[i]);
+ }
+
+ // Setup bit src/sink
+ BitStream<UInt128> bit_sink;
+ IntegerSequenceEncoder enc(11);
+ for (const auto& v : vals) {
+ enc.AddValue(v);
+ }
+ enc.Encode(&bit_sink);
+ EXPECT_EQ(bit_sink.Bits(), 58);
+
+ uint64_t encoded = 0;
+ EXPECT_TRUE(bit_sink.GetBits(58, &encoded));
+ EXPECT_EQ(encoded, kValEncoding)
+ << std::hex << encoded << " -- " << kValEncoding;
+}
+
+// Generate a random sequence of integer codings with different ranges to test
+// the reciprocability of our codec (encoded sequences should be able to
+// decoded)
+TEST(ASTCIntegerSequenceCodecTest, TestRandomReciprocation) {
+ std::mt19937 mt(0xbad7357);
+ std::uniform_int_distribution<int> rand(0, 255);
+
+ for (int test = 0; test < 1600; ++test) {
+ // Generate a random number of values and a random range
+ int num_vals = 4 + rand(mt) % 44; // Up to 48 weights in a grid
+ int range = 1 + rand(mt) % 63;
+
+ // If this produces a bit pattern larger than our buffer, then ignore
+ // it... we already know what our bounds are for the integer sequences
+ int num_bits = IntegerSequenceCodec::GetBitCountForRange(num_vals, range);
+ if (num_bits >= 64) {
+ continue;
+ }
+
+ std::vector<int> generated_vals(num_vals);
+ for (auto& val : generated_vals) {
+ val = rand(mt) % (range + 1);
+ }
+
+ // Encode the values using the
+ BitStream<UInt128> bit_sink;
+
+ // Add them to the encoder
+ IntegerSequenceEncoder enc(range);
+ for (int v : generated_vals) {
+ enc.AddValue(v);
+ }
+ enc.Encode(&bit_sink);
+
+ uint64_t encoded = 0;
+ bit_sink.GetBits(bit_sink.Bits(), &encoded);
+ ASSERT_GE(encoded, 0);
+ EXPECT_LT(encoded, 1ULL << num_bits);
+
+ BitStream<UInt128> bit_src(encoded, 64);
+
+ IntegerSequenceDecoder dec(range);
+ auto decoded_vals = dec.Decode(num_vals, &bit_src);
+
+ ASSERT_EQ(decoded_vals.size(), generated_vals.size());
+ for (size_t i = 0; i < decoded_vals.size(); ++i) {
+ EXPECT_EQ(decoded_vals[i], generated_vals[i]);
+ }
+ }
+}
+
+} // namespace
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/intermediate_astc_block_test.cc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/intermediate_astc_block_test.cc
new file mode 100644
index 00000000000..41861cb97b7
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/intermediate_astc_block_test.cc
@@ -0,0 +1,454 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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 "src/decoder/intermediate_astc_block.h"
+#include "src/decoder/test/image_utils.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <string>
+
+namespace astc_codec {
+
+namespace {
+
+using ::testing::ElementsAre;
+using ::testing::Eq;
+using ::testing::HasSubstr;
+using ::testing::SizeIs;
+using ::testing::TestWithParam;
+using ::testing::ValuesIn;
+
+// Test to make sure that unpacking an error block returns false.
+TEST(IntermediateASTCBlockTest, TestUnpackError) {
+ const PhysicalASTCBlock kErrorBlock(base::UInt128(0));
+ EXPECT_FALSE(UnpackVoidExtent(kErrorBlock));
+ EXPECT_FALSE(UnpackIntermediateBlock(kErrorBlock));
+}
+
+// Test to make sure that if we don't populate our weight data in the
+// intermediate block than the resulting color range should error due to the
+// mismatch.
+TEST(IntermediateASTCBlockTest, TestEndpointRangeErrorOnNotSettingWeights) {
+ IntermediateBlockData data;
+ data.weight_range = 15;
+ for (auto& ep : data.endpoints) {
+ ep.mode = ColorEndpointMode::kLDRRGBDirect;
+ }
+ data.weight_grid_dim_x = 6;
+ data.weight_grid_dim_y = 6;
+ EXPECT_EQ(-1, EndpointRangeForBlock(data));
+
+ base::UInt128 dummy;
+ auto err_str = Pack(data, &dummy);
+ EXPECT_TRUE(err_str.hasValue());
+ EXPECT_THAT(err_str.value(), HasSubstr("Incorrect number of weights"));
+}
+
+// Test to make sure that if we run out of bits, then we should say so.
+TEST(IntermediateASTCBlockTest, TestEndpointRangeErrorOnNotEnoughBits) {
+ IntermediateBlockData data;
+ data.weight_range = 1;
+ data.partition_id = 0;
+ data.endpoints.resize(3);
+ for (auto& ep : data.endpoints) {
+ ep.mode = ColorEndpointMode::kLDRRGBDirect;
+ }
+ data.weight_grid_dim_x = 8;
+ data.weight_grid_dim_y = 8;
+ EXPECT_EQ(-2, EndpointRangeForBlock(data));
+
+ // Resize the weights to get past the error that they do not match the grid
+ // dimensions.
+ data.weights.resize(64);
+
+ base::UInt128 dummy;
+ auto err_str = Pack(data, &dummy);
+ EXPECT_TRUE(err_str.hasValue());
+ EXPECT_THAT(err_str.value(), HasSubstr("illegal color range"));
+}
+
+// Test to make sure that as we increase the number of weights, we decrease the
+// allowable range of colors
+TEST(IntermediateASTCBlockTest, TestEndpointRangeForBlock) {
+ IntermediateBlockData data;
+ data.weight_range = 2;
+ data.endpoints.resize(2);
+ data.dual_plane_channel.clear();
+ for (auto& ep : data.endpoints) {
+ ep.mode = ColorEndpointMode::kLDRRGBDirect;
+ }
+
+ // Weight params control how many weights are present in a block
+ struct WeightParams {
+ int width;
+ int height;
+
+ // We should sort based on number of weights for these params
+ int NumWeights() const { return width * height; }
+ bool operator<(const WeightParams& other) const {
+ return NumWeights() < other.NumWeights();
+ }
+ };
+
+ std::vector<WeightParams> weight_params;
+ for (int y = 2; y < 8; ++y) {
+ for (int x = 2; x < 8; ++x) {
+ weight_params.emplace_back(WeightParams{x, y});
+ }
+ }
+
+ // Sort weights from fewest to largest such that the allowable color range
+ // should be monotonically decreasing
+ std::sort(weight_params.begin(), weight_params.end());
+
+ // Keep track of the largest available color range and measure that it
+ // decreases as we add more weights to our block
+ int last_color_range = 255;
+ for (const auto& params : weight_params) {
+ data.weight_grid_dim_x = params.width;
+ data.weight_grid_dim_y = params.height;
+
+ const int color_range = EndpointRangeForBlock(data);
+ EXPECT_LE(color_range, last_color_range);
+ last_color_range = std::min(color_range, last_color_range);
+ }
+
+ // Make sure that we actually changed it at some point.
+ EXPECT_LT(last_color_range, 255);
+}
+
+// Test to make sure that unpacking an legitimate ASTC block returns the encoded
+// values that we expect.
+TEST(IntermediateASTCBlockTest, TestUnpackNonVoidExtentBlock) {
+ PhysicalASTCBlock blk(0x0000000001FE000173ULL);
+ auto b = UnpackIntermediateBlock(blk);
+ ASSERT_TRUE(b);
+
+ const auto& data = b.value();
+ EXPECT_EQ(data.weight_grid_dim_x, 6);
+ EXPECT_EQ(data.weight_grid_dim_y, 5);
+ EXPECT_EQ(data.weight_range, 7);
+
+ EXPECT_FALSE(data.partition_id);
+ EXPECT_FALSE(data.dual_plane_channel);
+
+ ASSERT_EQ(data.weights.size(), 30);
+ for (auto weight : data.weights) {
+ EXPECT_EQ(weight, 0);
+ }
+
+ ASSERT_EQ(data.endpoints.size(), 1);
+ for (const auto& ep_data : data.endpoints) {
+ EXPECT_EQ(ep_data.mode, ColorEndpointMode::kLDRLumaDirect);
+ ASSERT_EQ(ep_data.colors.size(), 2);
+ EXPECT_EQ(ep_data.colors[0], 0);
+ EXPECT_EQ(ep_data.colors[1], 255);
+ }
+}
+
+// Make sure that we can pack blocks that aren't void extent blocks. (In other
+// words, can we actually deal with intermediate ASTC data).
+TEST(IntermediateASTCBlockTest, TestPackNonVoidExtentBlock) {
+ IntermediateBlockData data;
+
+ data.weight_grid_dim_x = 6;
+ data.weight_grid_dim_y = 5;
+ data.weight_range = 7;
+
+ data.partition_id = {};
+ data.dual_plane_channel = {};
+
+ data.weights.resize(30);
+ for (auto& weight : data.weights) {
+ weight = 0;
+ }
+
+ data.endpoints.resize(1);
+ for (auto& ep_data : data.endpoints) {
+ ep_data.mode = ColorEndpointMode::kLDRLumaDirect;
+ ep_data.colors.resize(2);
+ ep_data.colors[0] = 0;
+ ep_data.colors[1] = 255;
+ }
+
+ base::UInt128 packed;
+ auto error_str = Pack(data, &packed);
+ ASSERT_FALSE(error_str) << (error_str ? error_str.value() : std::string(""));
+ EXPECT_EQ(packed, 0x0000000001FE000173ULL);
+}
+
+// Make sure that we can unpack void extent blocks
+TEST(IntermediateASTCBlockTest, TestUnpackVoidExtentBlock) {
+ PhysicalASTCBlock void_extent_block(0xFFFFFFFFFFFFFDFCULL);
+
+ auto b = UnpackVoidExtent(void_extent_block);
+ ASSERT_TRUE(b);
+
+ const auto& data = b.value();
+ EXPECT_EQ(data.r, 0);
+ EXPECT_EQ(data.g, 0);
+ EXPECT_EQ(data.b, 0);
+ EXPECT_EQ(data.a, 0);
+ for (const auto& coord : data.coords) {
+ EXPECT_EQ(coord, (1 << 13) - 1);
+ }
+
+ base::UInt128 more_interesting(0xdeadbeefdeadbeefULL, 0xFFF8003FFE000DFCULL);
+ b = UnpackVoidExtent(PhysicalASTCBlock(more_interesting));
+ ASSERT_TRUE(b);
+
+ const auto& other_data = b.value();
+ EXPECT_EQ(other_data.r, 0xbeef);
+ EXPECT_EQ(other_data.g, 0xdead);
+ EXPECT_EQ(other_data.b, 0xbeef);
+ EXPECT_EQ(other_data.a, 0xdead);
+ EXPECT_EQ(other_data.coords[0], 0);
+ EXPECT_EQ(other_data.coords[1], 8191);
+ EXPECT_EQ(other_data.coords[2], 0);
+ EXPECT_EQ(other_data.coords[3], 8191);
+}
+
+// Make sure that we can pack void extent blocks and void extent data.
+TEST(IntermediateASTCBlockTest, TestPackVoidExtentBlock) {
+ VoidExtentData data;
+ data.r = 0;
+ data.g = 0;
+ data.b = 0;
+ data.a = 0;
+ for (auto& coord : data.coords) {
+ coord = (1 << 13) - 1;
+ }
+
+ base::UInt128 packed;
+ auto error_str = Pack(data, &packed);
+ ASSERT_FALSE(error_str) << (error_str ? error_str.value() : std::string(""));
+ EXPECT_EQ(packed, 0xFFFFFFFFFFFFFDFCULL);
+
+ data.r = 0xbeef;
+ data.g = 0xdead;
+ data.b = 0xbeef;
+ data.a = 0xdead;
+ data.coords[0] = 0;
+ data.coords[1] = 8191;
+ data.coords[2] = 0;
+ data.coords[3] = 8191;
+
+ error_str = Pack(data, &packed);
+ ASSERT_FALSE(error_str) << (error_str ? error_str.value() : std::string(""));
+ EXPECT_EQ(packed,
+ base::UInt128(0xdeadbeefdeadbeefULL, 0xFFF8003FFE000DFCULL));
+}
+
+// Make sure that the color endpoint mode is properly repacked. This test case
+// was created as a bug during testing.
+TEST(IntermediateASTCBlockTest, TestPackUnpackWithSameCEM) {
+ base::UInt128 orig(0xe8e8eaea20000980ULL, 0x20000200cb73f045ULL);
+
+ auto b = UnpackIntermediateBlock(PhysicalASTCBlock(orig));
+ ASSERT_TRUE(b);
+
+ base::UInt128 repacked;
+ auto err_str = Pack(b.value(), &repacked);
+ ASSERT_FALSE(err_str) << (err_str ? err_str.value() : std::string(""));
+
+ EXPECT_EQ(repacked, orig);
+
+ // Test case #2
+ orig = base::UInt128(0x3300c30700cb01c5ULL, 0x0573907b8c0f6879ULL);
+ b = UnpackIntermediateBlock(PhysicalASTCBlock(orig));
+ ASSERT_TRUE(b);
+
+ err_str = Pack(b.value(), &repacked);
+ ASSERT_FALSE(err_str) << (err_str ? err_str.value() : std::string(""));
+ EXPECT_EQ(repacked, orig);
+}
+
+// Test that we can encode/decode a block that uses a very large gap
+// between weight and endpoint data.
+TEST(IntermediateASTCBlockTest, TestPackingWithLargeGap) {
+ // We can construct this block by doing the following:
+ // -- choose a block mode that only gives 24 weight bits
+ // -- choose the smallest endpoint mode: grayscale direct
+ // -- make sure there are no partitions
+ const base::UInt128 orig(0xBEDEAD0000000000ULL, 0x0000000001FE032EULL);
+ const auto b = UnpackIntermediateBlock(PhysicalASTCBlock(orig));
+ ASSERT_TRUE(b);
+
+ const auto& data = b.value();
+ EXPECT_EQ(data.weight_grid_dim_x, 2);
+ EXPECT_EQ(data.weight_grid_dim_y, 3);
+ EXPECT_EQ(data.weight_range, 15);
+
+ EXPECT_FALSE(data.partition_id);
+ EXPECT_FALSE(data.dual_plane_channel);
+
+ ASSERT_EQ(data.endpoints.size(), 1);
+ EXPECT_EQ(data.endpoints.at(0).mode, ColorEndpointMode::kLDRLumaDirect);
+
+ ASSERT_EQ(data.endpoints.at(0).colors.size(), 2);
+ EXPECT_EQ(data.endpoints.at(0).colors.at(0), 255);
+ EXPECT_EQ(data.endpoints.at(0).colors.at(1), 0);
+
+ // Now encode it again
+ base::UInt128 repacked;
+ const auto err_str = Pack(b.value(), &repacked);
+ EXPECT_EQ(orig, repacked) << (err_str ? err_str.value() : std::string(""));
+}
+
+// Take a block that is encoded using direct luma with full byte values and see
+// if we properly set the endpoint range.
+TEST(IntermediateASTCBlockTest, TestEndpointRange) {
+ PhysicalASTCBlock blk(0x0000000001FE000173ULL);
+ EXPECT_TRUE(blk.ColorValuesRange().hasValue());
+ EXPECT_EQ(blk.ColorValuesRange().valueOr(0), 255);
+
+ auto b = UnpackIntermediateBlock(blk);
+ ASSERT_TRUE(b);
+
+ const auto& data = b.value();
+ ASSERT_THAT(data.endpoints, SizeIs(1));
+ EXPECT_THAT(data.endpoints[0].mode, Eq(ColorEndpointMode::kLDRLumaDirect));
+ EXPECT_THAT(data.endpoints[0].colors, ElementsAre(0, 255));
+ EXPECT_TRUE(data.endpoint_range.hasValue());
+ EXPECT_EQ(data.endpoint_range.valueOr(0), 255);
+}
+
+struct ImageTestParams {
+ std::string image_name;
+ int checkered_dim;
+};
+
+static void PrintTo(const ImageTestParams& params, std::ostream* os) {
+ *os << "ImageTestParams(" << params.image_name << ")";
+}
+
+class IntermediateASTCBlockTest : public TestWithParam<ImageTestParams> { };
+
+// Test whether or not a real-world ASTC implementation can be unpacked and
+// then repacked into the same implementation. In conjunction with the other
+// tests, we make sure that we can recreate ASTC blocks that we have previously
+// unpacked.
+TEST_P(IntermediateASTCBlockTest, TestPackUnpack) {
+ const auto& params = GetParam();
+ const int astc_dim = 8;
+ const int img_dim = params.checkered_dim * astc_dim;
+ const std::string astc = LoadASTCFile(params.image_name);
+
+ // Make sure that unpacking and repacking all of the blocks works...
+ const int kNumASTCBlocks = (img_dim / astc_dim) * (img_dim / astc_dim);
+ for (int i = 0; i < kNumASTCBlocks; ++i) {
+ base::UInt128 block_bits;
+ memcpy(&block_bits, astc.data() + PhysicalASTCBlock::kSizeInBytes * i,
+ PhysicalASTCBlock::kSizeInBytes);
+
+ const PhysicalASTCBlock block(block_bits);
+
+ base::UInt128 repacked;
+ if (block.IsVoidExtent()) {
+ auto b = UnpackVoidExtent(block);
+ ASSERT_TRUE(b);
+
+ auto err_str = Pack(b.value(), &repacked);
+ ASSERT_FALSE(err_str) << (err_str ? err_str.value() : std::string(""));
+ } else {
+ auto b = UnpackIntermediateBlock(block);
+ ASSERT_TRUE(b);
+
+ // Check to see that we properly set the endpoint range when we decoded
+ // the block.
+ auto& block_data = b.value();
+ EXPECT_EQ(block_data.endpoint_range, block.ColorValuesRange());
+
+ // Reset the endpoint range here to see if we correctly reconstruct it
+ // below
+ block_data.endpoint_range = {};
+
+ auto err_str = Pack(b.value(), &repacked);
+ ASSERT_FALSE(err_str) << (err_str ? err_str.value() : std::string(""));
+ }
+
+ // You would expect the following line to be enough:
+ // EXPECT_EQ(repacked, block.GetBlockBits())
+ // ... except that the ASTC encoder makes some interesting decisions
+ // about how to encode the same logical bits. One example is that
+ // sometimes if all partitions share an endpoint mode, the encoded
+ // block will not use the shared CEM mode, and rather list each
+ // partition's mode explicitly. For that reason, we just need to make as
+ // close of an approximation as possible that we decode to the same
+ // physical values.
+
+ PhysicalASTCBlock pb(repacked);
+ ASSERT_FALSE(pb.IsIllegalEncoding());
+
+ base::UInt128 pb_color_mask =
+ (base::UInt128(1) << pb.NumColorBits().value()) - 1;
+ base::UInt128 pb_color_bits =
+ pb.GetBlockBits() >> pb.ColorStartBit().value();
+ pb_color_bits &= pb_color_mask;
+
+ base::UInt128 b_color_mask =
+ (base::UInt128(1) << pb.NumColorBits().value()) - 1;
+ base::UInt128 b_color_bits =
+ block.GetBlockBits() >> block.ColorStartBit().value();
+ b_color_bits &= b_color_mask;
+
+ EXPECT_EQ(pb_color_mask, b_color_mask);
+ EXPECT_EQ(pb_color_bits, b_color_bits);
+
+ EXPECT_EQ(pb.IsVoidExtent(), block.IsVoidExtent());
+ EXPECT_EQ(pb.VoidExtentCoords(), block.VoidExtentCoords());
+
+ EXPECT_EQ(pb.WeightGridDims(), block.WeightGridDims());
+ EXPECT_EQ(pb.WeightRange(), block.WeightRange());
+ EXPECT_EQ(pb.NumWeightBits(), block.NumWeightBits());
+ EXPECT_EQ(pb.WeightStartBit(), block.WeightStartBit());
+
+ EXPECT_EQ(pb.IsDualPlane(), block.IsDualPlane());
+ EXPECT_EQ(pb.DualPlaneChannel(), block.DualPlaneChannel());
+
+ EXPECT_EQ(pb.NumPartitions(), block.NumPartitions());
+ EXPECT_EQ(pb.PartitionID(), block.PartitionID());
+
+ EXPECT_EQ(pb.NumColorValues(), block.NumColorValues());
+ EXPECT_EQ(pb.ColorValuesRange(), block.ColorValuesRange());
+
+ for (int j = 0; j < pb.NumPartitions().valueOr(0); ++j) {
+ EXPECT_EQ(pb.GetEndpointMode(j), block.GetEndpointMode(j));
+ }
+ }
+}
+
+std::vector<ImageTestParams> GetImageTestParams() {
+ return {
+ // image_name checkered_dim
+ { "checkered_4", 4 },
+ { "checkered_5", 5 },
+ { "checkered_6", 6 },
+ { "checkered_7", 7 },
+ { "checkered_8", 8 },
+ { "checkered_9", 9 },
+ { "checkered_10", 10 },
+ { "checkered_11", 11 },
+ { "checkered_12", 12 },
+ };
+}
+
+INSTANTIATE_TEST_CASE_P(Checkered, IntermediateASTCBlockTest,
+ ValuesIn(GetImageTestParams()));
+
+} // namespace
+
+} // namespace astc_codec
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/logical_astc_block_test.cc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/logical_astc_block_test.cc
new file mode 100644
index 00000000000..ed85f3fdcf9
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/logical_astc_block_test.cc
@@ -0,0 +1,273 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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 "src/decoder/logical_astc_block.h"
+#include "src/decoder/test/image_utils.h"
+
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+#include <fstream>
+#include <string>
+
+namespace astc_codec {
+
+namespace {
+
+using ::testing::Eq;
+using ::testing::ElementsAre;
+using ::testing::TestWithParam;
+using ::testing::ValuesIn;
+
+ImageBuffer LoadGoldenImageWithAlpha(std::string basename) {
+ const std::string filename = std::string("src/decoder/testdata/") + basename + ".bmp";
+ ImageBuffer result;
+ LoadGoldenBmp(filename, &result);
+ EXPECT_EQ(result.BytesPerPixel(), 4);
+ return result;
+}
+
+ImageBuffer LoadGoldenImage(std::string basename) {
+ const std::string filename = std::string("src/decoder/testdata/") + basename + ".bmp";
+ ImageBuffer result;
+ LoadGoldenBmp(filename, &result);
+ EXPECT_EQ(result.BytesPerPixel(), 3);
+ return result;
+}
+
+struct ImageTestParams {
+ std::string image_name;
+ bool has_alpha;
+ Footprint footprint;
+ int width;
+ int height;
+};
+
+static void PrintTo(const ImageTestParams& params, std::ostream* os) {
+ *os << "ImageTestParams(" << params.image_name << ", "
+ << params.width << "x" << params.height << ", "
+ << (params.has_alpha ? "RGBA" : "RGB") << ", "
+ << "footprint " << params.footprint.Width() << "x"
+ << params.footprint.Height() << ")";
+}
+
+class LogicalASTCBlockTest : public TestWithParam<ImageTestParams> { };
+
+// Test to make sure that reading out color values from blocks is not
+// terribly wrong. To do so, we compress an image and then decompress it
+// using our logical blocks and the library. The difference between the
+// decoded images should be minimal.
+TEST_P(LogicalASTCBlockTest, ImageWithFootprint) {
+ const auto& params = GetParam();
+ const std::string astc = LoadASTCFile(params.image_name);
+
+ ImageBuffer our_decoded_image;
+ our_decoded_image.Allocate(params.width, params.height, params.has_alpha ? 4 : 3);
+
+ const int block_width = params.footprint.Width();
+ const int block_height = params.footprint.Height();
+
+ base::UInt128 block;
+ for (int i = 0; i < astc.size(); i += 16) {
+ const int block_index = i / 16;
+ const int blocks_wide =
+ (params.width + block_width - 1) / block_width;
+ const int block_x = block_index % blocks_wide;
+ const int block_y = block_index / blocks_wide;
+ memcpy(&block, astc.data() + i, sizeof(block));
+
+ PhysicalASTCBlock physical_block(block);
+ if (physical_block.IsVoidExtent()) {
+ auto ve = UnpackVoidExtent(physical_block);
+ ASSERT_TRUE(ve) << "ASTC encoder produced invalid block!";
+ } else {
+ auto ib = UnpackIntermediateBlock(physical_block);
+ ASSERT_TRUE(ib) << "ASTC encoder produced invalid block!";
+ }
+
+ // Make sure that the library doesn't produce incorrect ASTC blocks.
+ // This is covered in more depth in other tests in
+ // intermediate_astc_block_test and physical_astc_block_test
+ auto lb = UnpackLogicalBlock(params.footprint, physical_block);
+ ASSERT_TRUE(lb) << "ASTC encoder produced invalid block!";
+
+ LogicalASTCBlock logical_block = lb.value();
+ const size_t color_size = params.has_alpha ? 4 : 3;
+
+ for (int y = 0; y < block_height; ++y) {
+ for (int x = 0; x < block_width; ++x) {
+ const int px = block_width * block_x + x;
+ const int py = block_height * block_y + y;
+
+ // Skip out of bounds.
+ if (px >= params.width || py >= params.height) {
+ continue;
+ }
+
+ uint8_t* pixel = our_decoded_image(px, py);
+ const RgbaColor decoded_color = logical_block.ColorAt(x, y);
+ ASSERT_LE(color_size, decoded_color.size());
+
+ for (int c = 0; c < color_size; ++c) {
+ // All of the pixels should also be 8-bit values.
+ ASSERT_GE(decoded_color[c], 0);
+ ASSERT_LT(decoded_color[c], 256);
+ pixel[c] = decoded_color[c];
+ }
+ }
+ }
+ }
+
+ // Check that the decoded image is *very* similar to the library decoding
+ // of an ASTC texture. They may not be exact due to differences in how we
+ // convert a 16-bit float to an 8-bit integer.
+ ImageBuffer decoded_image = params.has_alpha ? LoadGoldenImageWithAlpha(params.image_name) : LoadGoldenImage(params.image_name);
+ CompareSumOfSquaredDifferences(decoded_image, our_decoded_image, 1.0);
+}
+
+// Test to make sure that a simple gradient image can be compressed and decoded
+// by our logical block representation. This should work with every footprint.
+std::vector<ImageTestParams> GetSyntheticImageTestParams() {
+ return {
+ // image_name alpha astc footprint width height
+ { "footprint_4x4", false, Footprint::Get4x4(), 32, 32 },
+ { "footprint_5x4", false, Footprint::Get5x4(), 32, 32 },
+ { "footprint_5x5", false, Footprint::Get5x5(), 32, 32 },
+ { "footprint_6x5", false, Footprint::Get6x5(), 32, 32 },
+ { "footprint_6x6", false, Footprint::Get6x6(), 32, 32 },
+ { "footprint_8x5", false, Footprint::Get8x5(), 32, 32 },
+ { "footprint_8x6", false, Footprint::Get8x6(), 32, 32 },
+ { "footprint_10x5", false, Footprint::Get10x5(), 32, 32 },
+ { "footprint_10x6", false, Footprint::Get10x6(), 32, 32 },
+ { "footprint_8x8", false, Footprint::Get8x8(), 32, 32 },
+ { "footprint_10x8", false, Footprint::Get10x8(), 32, 32 },
+ { "footprint_10x10", false, Footprint::Get10x10(), 32, 32 },
+ { "footprint_12x10", false, Footprint::Get12x10(), 32, 32 },
+ { "footprint_12x12", false, Footprint::Get12x12(), 32, 32 },
+ };
+}
+
+INSTANTIATE_TEST_CASE_P(Synthetic, LogicalASTCBlockTest,
+ ValuesIn(GetSyntheticImageTestParams()));
+
+// Test to make sure that reading out color values from blocks in a real-world
+// image isn't terribly wrong, either.
+std::vector<ImageTestParams> GetRealWorldImageTestParams() {
+ return {
+ // image_name alpha astc footprint width height
+ { "rgb_4x4", false, Footprint::Get4x4(), 224, 288 },
+ { "rgb_6x6", false, Footprint::Get6x6(), 224, 288 },
+ { "rgb_8x8", false, Footprint::Get8x8(), 224, 288 },
+ { "rgb_12x12", false, Footprint::Get12x12(), 224, 288 },
+ { "rgb_5x4", false, Footprint::Get5x4(), 224, 288 }
+ };
+}
+
+INSTANTIATE_TEST_CASE_P(RealWorld, LogicalASTCBlockTest,
+ ValuesIn(GetRealWorldImageTestParams()));
+
+// Test to make sure that reading out color values from blocks in a real-world
+// image isn't terribly wrong, either.
+std::vector<ImageTestParams> GetTransparentImageTestParams() {
+ return {
+ // image_name alpha astc footprint width height
+ { "atlas_small_4x4", true, Footprint::Get4x4(), 256, 256 },
+ { "atlas_small_5x5", true, Footprint::Get5x5(), 256, 256 },
+ { "atlas_small_6x6", true, Footprint::Get6x6(), 256, 256 },
+ { "atlas_small_8x8", true, Footprint::Get8x8(), 256, 256 },
+ };
+}
+
+INSTANTIATE_TEST_CASE_P(Transparent, LogicalASTCBlockTest,
+ ValuesIn(GetTransparentImageTestParams()));
+
+// Test to make sure that if we set our endpoints then it's reflected in our
+// color selection
+TEST(LogicalASTCBlockTest, SetEndpoints) {
+ LogicalASTCBlock logical_block(Footprint::Get8x8());
+
+ // Setup a weight checkerboard
+ for (int j = 0; j < 8; ++j) {
+ for (int i = 0; i < 8; ++i) {
+ if (((i ^ j) & 1) == 1) {
+ logical_block.SetWeightAt(i, j, 0);
+ } else {
+ logical_block.SetWeightAt(i, j, 64);
+ }
+ }
+ }
+
+ // Now set the colors to something ridiculous
+ logical_block.SetEndpoints({{ 123, 45, 67, 89 }}, {{ 101, 121, 31, 41 }}, 0);
+
+ // For each pixel, we expect it to mirror the endpoints in a checkerboard
+ // pattern
+ for (int j = 0; j < 8; ++j) {
+ for (int i = 0; i < 8; ++i) {
+ if (((i ^ j) & 1) == 1) {
+ EXPECT_THAT(logical_block.ColorAt(i, j), ElementsAre(123, 45, 67, 89));
+ } else {
+ EXPECT_THAT(logical_block.ColorAt(i, j), ElementsAre(101, 121, 31, 41));
+ }
+ }
+ }
+}
+
+// Test whether or not setting weight values under different circumstances is
+// supported and reflected in the query functions.
+TEST(LogicalASTCBlockTest, SetWeightVals) {
+ LogicalASTCBlock logical_block(Footprint::Get4x4());
+
+ EXPECT_THAT(logical_block.GetFootprint(), Eq(Footprint::Get4x4()));
+
+ // Not a dual plane by default
+ EXPECT_FALSE(logical_block.IsDualPlane());
+ logical_block.SetWeightAt(2, 3, 2);
+
+ // Set the dual plane
+ logical_block.SetDualPlaneChannel(0);
+ EXPECT_TRUE(logical_block.IsDualPlane());
+
+ // This shouldn't have reset our weight
+ const LogicalASTCBlock other_block = logical_block;
+ EXPECT_THAT(other_block.WeightAt(2, 3), Eq(2));
+ EXPECT_THAT(other_block.DualPlaneWeightAt(0, 2, 3), Eq(2));
+
+ // If we set the dual plane weight, it shouldn't change the original weight
+ // value or the other channels
+ logical_block.SetDualPlaneWeightAt(0, 2, 3, 1);
+ EXPECT_THAT(logical_block.WeightAt(2, 3), Eq(2));
+ EXPECT_THAT(logical_block.DualPlaneWeightAt(0, 2, 3), Eq(1));
+ for (int i = 1; i < 4; ++i) {
+ EXPECT_THAT(logical_block.DualPlaneWeightAt(i, 2, 3), Eq(2));
+ }
+
+ // Remove the dual plane
+ logical_block.SetDualPlaneChannel(-1);
+ EXPECT_FALSE(logical_block.IsDualPlane());
+
+ // Now the original dual plane weight should be reset back to the others. Note
+ // that we have to call DualPlaneWeightAt from a const logical block since
+ // returning a reference to a weight that doesn't exist is illegal.
+ const LogicalASTCBlock other_block2 = logical_block;
+ EXPECT_THAT(logical_block.WeightAt(2, 3), Eq(2));
+ for (int i = 0; i < 4; ++i) {
+ EXPECT_EQ(logical_block.WeightAt(2, 3),
+ other_block2.DualPlaneWeightAt(i, 2, 3));
+ }
+}
+
+} // namespace
+
+} // namespace astc_codec
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/partition_test.cc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/partition_test.cc
new file mode 100644
index 00000000000..63adfb500ff
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/partition_test.cc
@@ -0,0 +1,263 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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 "src/decoder/partition.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <array>
+#include <random>
+#include <string>
+#include <vector>
+
+namespace {
+
+using ::testing::ElementsAreArray;
+using ::testing::Eq;
+using ::testing::Le;
+using ::testing::Not;
+
+using astc_codec::Footprint;
+using astc_codec::Partition;
+using astc_codec::PartitionMetric;
+using astc_codec::GetASTCPartition;
+using astc_codec::FindClosestASTCPartition;
+
+// Test to make sure that a simple difference between two partitions where
+// most of the values are the same returns what we expect.
+TEST(PartitionTest, TestSimplePartitionMetric) {
+ Partition a = {Footprint::Get6x6(), /* num_parts = */ 2,
+ /* partition_id = */ {}, /* assignment = */ {}};
+ Partition b = a;
+
+ a.assignment = {
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ };
+
+ b.assignment = {
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ };
+
+ const int dist = PartitionMetric(a, b);
+ EXPECT_EQ(dist, 2);
+}
+
+// Test to make sure that if one partition is a subset of another that we still
+// return the proper difference against the subset of the larger one.
+TEST(PartitionDeathTest, TestPartitionMetric) {
+ Partition a = {Footprint::Get4x4(), /* num_parts = */ 2,
+ /* partition_id = */ {}, /* assignment = */ {}};
+ Partition b = {Footprint::Get6x6(), /* num_parts = */ 2,
+ /* partition_id = */ {}, /* assignment = */ {}};
+
+ a.assignment = {{
+ 1, 1, 1, 1,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 1,
+ }};
+
+ b.assignment = {{
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 1, 0,
+ 0, 0, 1, 1, 0, 0,
+ }};
+
+ EXPECT_DEATH(PartitionMetric(a, b), "");
+}
+
+// Test to make sure that even if we have different numbers of subsets for each
+// partition, that the returned value is what we'd expect.
+TEST(PartitionTest, TestDiffPartsPartitionMetric) {
+ Partition a = {Footprint::Get4x4(), /* num_parts = */ 2,
+ /* partition_id = */ {}, /* assignment = */ {}};
+ Partition b = {Footprint::Get4x4(), /* num_parts = */ 3,
+ /* partition_id = */ {}, /* assignment = */ {}};
+
+ a.assignment = {{
+ 2, 2, 2, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 1,
+ }};
+
+ b.assignment = {{
+ 1, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0
+ }};
+
+ const int dist = PartitionMetric(a, b);
+ EXPECT_EQ(dist, 3);
+}
+
+// An additional sanity check test that makes sure that we're not always mapping
+// zero to zero in our tests.
+TEST(PartitionTest, TestDiffMappingPartitionMetric) {
+ Partition a = {Footprint::Get4x4(), /* num_parts = */ 2,
+ /* partition_id = */ {}, /* assignment = */ {}};
+ Partition b = {Footprint::Get4x4(), /* num_parts = */ 3,
+ /* partition_id = */ {}, /* assignment = */ {}};
+
+ a.assignment = {{
+ 0, 1, 2, 2,
+ 2, 2, 2, 2,
+ 2, 2, 2, 2,
+ 2, 2, 2, 2,
+ }};
+
+ b.assignment = {{
+ 1, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ }};
+
+ const int dist = PartitionMetric(a, b);
+ EXPECT_EQ(dist, 1);
+}
+
+// Finally, if we grab an ASTC partition and modify it a tad, the closest
+// partition should still be the same ASTC partition.
+TEST(PartitionTest, TestFindingASTCPartition) {
+ const Partition astc = GetASTCPartition(Footprint::Get12x12(), 3, 0x3CB);
+ Partition almost_astc = astc;
+ almost_astc.assignment[0]++;
+
+ const Partition& closest_astc = FindClosestASTCPartition(almost_astc);
+ EXPECT_EQ(astc, closest_astc);
+}
+
+// Test a partition that was obtained from the reference ASTC encoder. We should
+// be able to match it exactly
+TEST(PartitionTest, TestSpecificPartition) {
+ const Partition astc = GetASTCPartition(Footprint::Get10x6(), 3, 557);
+ EXPECT_THAT(astc.assignment, ElementsAreArray(std::array<int, 60> {{
+ 0, 0, 0, 0, 1, 1, 1, 2, 2, 2,
+ 0, 0, 0, 0, 1, 1, 1, 2, 2, 2,
+ 0, 0, 0, 0, 1, 1, 1, 2, 2, 2,
+ 0, 0, 0, 0, 1, 1, 1, 2, 2, 2,
+ 0, 0, 0, 0, 1, 1, 1, 2, 2, 2,
+ 0, 0, 0, 0, 1, 1, 1, 2, 2, 2 }}));
+}
+
+// Make sure that when we match against this specific partition, it'll return a
+// partition with the same number of subsets
+TEST(PartitionTest, EstimatedPartitionSubsets) {
+ Partition partition = {
+ /* footprint = */ Footprint::Get6x6(),
+ /* num_parts = */ 2,
+ /* partition_id = */ {},
+ /* assignment = */ {
+ 0, 0, 1, 1, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1
+ }};
+
+ const Partition astc = FindClosestASTCPartition(partition);
+ EXPECT_THAT(astc.num_parts, Eq(partition.num_parts));
+}
+
+// Make sure that regardless of what partition we match against, it'll return a
+// partition with at most a fewer number of subsets
+TEST(PartitionTest, EstimatedPartitionFewerSubsets) {
+ std::mt19937 random(0xdeadbeef);
+ auto randUniform = [&random](int max) {
+ std::uniform_int_distribution<> dist(0, max - 1);
+ return dist(random);
+ };
+
+ constexpr int kNumFootprints = Footprint::NumValidFootprints();
+ const auto kFootprints = std::array<Footprint, kNumFootprints> {{
+ Footprint::Get4x4(),
+ Footprint::Get5x4(),
+ Footprint::Get5x5(),
+ Footprint::Get6x5(),
+ Footprint::Get6x6(),
+ Footprint::Get8x5(),
+ Footprint::Get8x6(),
+ Footprint::Get8x8(),
+ Footprint::Get10x5(),
+ Footprint::Get10x6(),
+ Footprint::Get10x8(),
+ Footprint::Get10x10(),
+ Footprint::Get12x10(),
+ Footprint::Get12x12()
+ }};
+
+ constexpr int kNumTests = 200;
+ for (int i = 0; i < kNumTests; ++i) {
+ const auto& footprint = kFootprints[randUniform(kNumFootprints)];
+ const int num_parts = 2 + randUniform(3);
+ Partition partition = {
+ footprint,
+ num_parts,
+ /* partition_id = */ {},
+ /* assignment = */ std::vector<int>(footprint.NumPixels(), 0)};
+
+ for (auto& p : partition.assignment) {
+ p = randUniform(num_parts);
+ }
+
+ const Partition astc = FindClosestASTCPartition(partition);
+ EXPECT_THAT(astc.num_parts, Le(partition.num_parts))
+ << "Test #" << i << ": "
+ << "Selected partition with ID " << astc.partition_id.value();
+ }
+}
+
+// Make sure that we generate unique partitions that are close to the
+// candidates.
+TEST(PartitionTest, UniquePartitionResults) {
+ Partition partition = {
+ /* footprint = */ Footprint::Get6x6(),
+ /* num_parts = */ 2,
+ /* partition_id = */ {},
+ /* assignment = */ {
+ 0, 1, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1
+ }};
+
+ const auto parts = FindKClosestASTCPartitions(partition, 2);
+ EXPECT_THAT(*parts[0], Not(Eq(*parts[1])));
+}
+
+// TODO(google): Verify somehow that the assignment generated from
+// GetASTCPartition actually matches what's in the spec. The selection
+// function was more or less copy/pasted though so it's unclear how to
+// measure that against e.g. the ASTC encoder.
+
+} // namespace
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/physical_astc_block_test.cc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/physical_astc_block_test.cc
new file mode 100644
index 00000000000..8eafe461296
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/physical_astc_block_test.cc
@@ -0,0 +1,361 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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 "src/decoder/physical_astc_block.h"
+#include "src/base/uint128.h"
+
+#include <gtest/gtest.h>
+
+#include <string>
+#include <vector>
+
+using astc_codec::PhysicalASTCBlock;
+using astc_codec::ColorEndpointMode;
+using astc_codec::base::UInt128;
+
+namespace {
+
+static const PhysicalASTCBlock kErrorBlock(UInt128(0));
+
+// Test to make sure that each of the constructors work and that
+// they produce the same block encodings, since the ASTC blocks
+// are little-endian
+TEST(PhysicalASTCBlockTest, TestConstructors) {
+ // Little-endian reading of bytes
+ PhysicalASTCBlock blk1(0x0000000001FE000173ULL);
+ PhysicalASTCBlock blk2(
+ std::string("\x73\x01\x00\xFE\x01\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16));
+ EXPECT_EQ(blk1.GetBlockBits(), blk2.GetBlockBits());
+}
+
+// Test to see if we properly decode the maximum value that a weight
+// can take in an ASTC block based on the block mode encoding. We test
+// against a valid case and various error cases
+TEST(PhysicalASTCBlockTest, TestWeightRange) {
+ PhysicalASTCBlock blk1(0x0000000001FE000173ULL);
+ auto weight_range = blk1.WeightRange();
+ ASSERT_TRUE(weight_range);
+ EXPECT_EQ(weight_range.value(), 7);
+
+ // If we flip the high bit then we should have a range of 31,
+ // although then we have too many bits and this should error.
+ PhysicalASTCBlock blk2(0x0000000001FE000373ULL);
+ EXPECT_FALSE(blk2.WeightRange());
+
+ // One bit per weight -- range of 1
+ PhysicalASTCBlock non_shared_cem(0x4000000000800D44ULL);
+ weight_range = non_shared_cem.WeightRange();
+ ASSERT_TRUE(weight_range);
+ EXPECT_EQ(weight_range.value(), 1);
+
+ // Error blocks have no weight range
+ EXPECT_FALSE(kErrorBlock.WeightRange());
+}
+
+// Test to see if we properly decode the weight grid width and height
+// in an ASTC block based on the block mode encoding. We test against
+// a valid case and various error cases
+TEST(PhysicalASTCBlockTest, TestWeightDims) {
+ PhysicalASTCBlock blk1(0x0000000001FE000173ULL);
+ auto weight_dims = blk1.WeightGridDims();
+ EXPECT_TRUE(weight_dims);
+ EXPECT_EQ(weight_dims.value()[0], 6);
+ EXPECT_EQ(weight_dims.value()[1], 5);
+
+ // If we flip the high bit then we should have a range of 31,
+ // although then we have too many bits for the weight grid
+ // and this should error.
+ PhysicalASTCBlock blk2(0x0000000001FE000373ULL);
+ EXPECT_FALSE(blk2.WeightGridDims());
+ EXPECT_EQ(blk2.IsIllegalEncoding().value(),
+ "Too many bits required for weight grid");
+
+ // Dual plane block with 3x5 weight dims
+ PhysicalASTCBlock blk3(0x0000000001FE0005FFULL);
+ weight_dims = blk3.WeightGridDims();
+ ASSERT_TRUE(weight_dims);
+ EXPECT_EQ(weight_dims->at(0), 3);
+ EXPECT_EQ(weight_dims->at(1), 5);
+
+ // Error blocks shouldn't have any weight dims
+ EXPECT_FALSE(kErrorBlock.WeightGridDims());
+
+ PhysicalASTCBlock non_shared_cem(0x4000000000800D44ULL);
+ weight_dims = non_shared_cem.WeightGridDims();
+ ASSERT_TRUE(weight_dims);
+ EXPECT_EQ(weight_dims->at(0), 8);
+ EXPECT_EQ(weight_dims->at(1), 8);
+}
+
+// Test to see whether or not the presence of a dual-plane bit
+// is decoded properly. Error encodings are tested to *not* return
+// that they have dual planes.
+TEST(PhysicalASTCBlockTest, TestDualPlane) {
+ PhysicalASTCBlock blk1(0x0000000001FE000173ULL);
+ EXPECT_FALSE(blk1.IsDualPlane());
+ EXPECT_FALSE(kErrorBlock.IsDualPlane());
+
+ // If we flip the dual plane bit, we will have too many bits
+ // for the weight grid and this should error
+ PhysicalASTCBlock blk2(0x0000000001FE000573ULL);
+ EXPECT_FALSE(blk2.IsDualPlane());
+ EXPECT_FALSE(blk2.WeightGridDims());
+ EXPECT_EQ(blk2.IsIllegalEncoding().value(),
+ "Too many bits required for weight grid");
+
+ // A dual plane with 3x5 weight grid should be supported
+ PhysicalASTCBlock blk3(0x0000000001FE0005FFULL);
+ EXPECT_TRUE(blk3.IsDualPlane());
+
+ // If we use the wrong block mode, then a valid block
+ // shouldn't have any dual plane
+ PhysicalASTCBlock blk4(0x0000000001FE000108ULL);
+ EXPECT_FALSE(blk4.IsDualPlane());
+ EXPECT_FALSE(blk4.IsIllegalEncoding());
+}
+
+// Make sure that we properly calculate the number of bits used to encode
+// the weight grid. Given error encodings or void extent blocks, this number
+// should be zero
+TEST(PhysicalASTCBlockTest, TestNumWeightBits) {
+ // 6x5 single-plane weight grid with 3-bit weights
+ // should have 90 bits for the weights.
+ PhysicalASTCBlock blk1(0x0000000001FE000173ULL);
+ EXPECT_EQ(90, blk1.NumWeightBits());
+
+ // Error block has no weight bits
+ EXPECT_FALSE(kErrorBlock.NumWeightBits());
+
+ // Void extent blocks have no weight bits
+ EXPECT_FALSE(PhysicalASTCBlock(0xFFF8003FFE000DFCULL).NumWeightBits());
+
+ // If we flip the dual plane bit, we will have too many bits
+ // for the weight grid and this should error and return no bits
+ PhysicalASTCBlock blk2(0x0000000001FE000573ULL);
+ EXPECT_FALSE(blk2.NumWeightBits());
+
+ // 3x5 dual-plane weight grid with 3-bit weights
+ // should have 90 bits for the weights.
+ PhysicalASTCBlock blk3(0x0000000001FE0005FFULL);
+ EXPECT_EQ(90, blk3.NumWeightBits());
+}
+
+// Test to make sure that our weight bits start where we expect them to.
+// In other words, make sure that the calculation based on the block mode for
+// where the weight bits start is accurate.
+TEST(PhysicalASTCBlockTest, TestStartWeightBit) {
+ EXPECT_EQ(PhysicalASTCBlock(0x4000000000800D44ULL).WeightStartBit(), 64);
+
+ // Error blocks have no weight start bit
+ EXPECT_FALSE(kErrorBlock.WeightStartBit());
+
+ // Void extent blocks have no weight start bit
+ EXPECT_FALSE(PhysicalASTCBlock(0xFFF8003FFE000DFCULL).WeightStartBit());
+}
+
+// Test to make sure that we catch various different reasons for error encoding
+// of ASTC blocks, but also that certain encodings aren't errors.
+TEST(PhysicalASTCBlockTest, TestErrorBlocks) {
+ // Various valid block modes
+ EXPECT_FALSE(PhysicalASTCBlock(0x0000000001FE000173ULL).IsIllegalEncoding());
+ EXPECT_FALSE(PhysicalASTCBlock(0x0000000001FE0005FFULL).IsIllegalEncoding());
+ EXPECT_FALSE(PhysicalASTCBlock(0x0000000001FE000108ULL).IsIllegalEncoding());
+
+ // This is an error because it uses an invalid block mode
+ EXPECT_EQ(kErrorBlock.IsIllegalEncoding().value(), "Reserved block mode");
+
+ // This is an error because we have too many weight bits
+ PhysicalASTCBlock err_blk(0x0000000001FE000573ULL);
+ EXPECT_EQ(err_blk.IsIllegalEncoding().value(),
+ "Too many bits required for weight grid");
+
+ // This is an error because we have too many weights
+ PhysicalASTCBlock err_blk2 = PhysicalASTCBlock(0x0000000001FE0005A8ULL);
+ EXPECT_EQ(err_blk2.IsIllegalEncoding().value(), "Too many weights specified");
+
+ PhysicalASTCBlock err_blk3 = PhysicalASTCBlock(0x0000000001FE000588ULL);
+ EXPECT_EQ(err_blk3.IsIllegalEncoding().value(), "Too many weights specified");
+
+ // This is an error because we have too few weights
+ PhysicalASTCBlock err_blk4 = PhysicalASTCBlock(0x0000000001FE00002ULL);
+ EXPECT_EQ(err_blk4.IsIllegalEncoding().value(),
+ "Too few bits required for weight grid");
+
+ // Four partitions, dual plane -- should be error
+ // 2x2 weight grid, 3 bits per weight
+ PhysicalASTCBlock dual_plane_four_parts(0x000000000000001D1FULL);
+ EXPECT_FALSE(dual_plane_four_parts.NumPartitions());
+ EXPECT_EQ(dual_plane_four_parts.IsIllegalEncoding().value(),
+ "Both four partitions and dual plane specified");
+}
+
+// Test to make sure that we properly identify and can manipulate void-extent
+// blocks. These are ASTC blocks that only define a single color for the entire
+// block.
+TEST(PhysicalASTCBlockTest, TestVoidExtentBlocks) {
+ // Various valid block modes that aren't void extent blocks
+ EXPECT_FALSE(PhysicalASTCBlock(0x0000000001FE000173ULL).IsVoidExtent());
+ EXPECT_FALSE(PhysicalASTCBlock(0x0000000001FE0005FFULL).IsVoidExtent());
+ EXPECT_FALSE(PhysicalASTCBlock(0x0000000001FE000108ULL).IsVoidExtent());
+
+ // Error block is not a void extent block
+ EXPECT_FALSE(kErrorBlock.IsVoidExtent());
+
+ // Void extent block is void extent block...
+ UInt128 void_extent_encoding(0, 0xFFF8003FFE000DFCULL);
+ EXPECT_FALSE(PhysicalASTCBlock(void_extent_encoding).IsIllegalEncoding());
+ EXPECT_TRUE(PhysicalASTCBlock(void_extent_encoding).IsVoidExtent());
+
+ // If we modify the high 64 bits it shouldn't change anything
+ void_extent_encoding |= UInt128(0xdeadbeefdeadbeef, 0);
+ EXPECT_FALSE(PhysicalASTCBlock(void_extent_encoding).IsIllegalEncoding());
+ EXPECT_TRUE(PhysicalASTCBlock(void_extent_encoding).IsVoidExtent());
+}
+
+TEST(PhysicalASTCBlockTest, TestVoidExtentCoordinates) {
+ // The void extent block should have texture coordinates from 0-8191
+ auto coords = PhysicalASTCBlock(0xFFF8003FFE000DFCULL).VoidExtentCoords();
+ EXPECT_EQ(coords->at(0), 0);
+ EXPECT_EQ(coords->at(1), 8191);
+ EXPECT_EQ(coords->at(2), 0);
+ EXPECT_EQ(coords->at(3), 8191);
+
+ // If we set the coords to all 1's then it's still a void extent
+ // block, but there aren't any void extent coords.
+ EXPECT_FALSE(PhysicalASTCBlock(0xFFFFFFFFFFFFFDFCULL).IsIllegalEncoding());
+ EXPECT_TRUE(PhysicalASTCBlock(0xFFFFFFFFFFFFFDFCULL).IsVoidExtent());
+ EXPECT_FALSE(PhysicalASTCBlock(0xFFFFFFFFFFFFFDFCULL).VoidExtentCoords());
+
+ // If we set the void extent coords to something where the coords are
+ // >= each other, then the encoding is illegal.
+ EXPECT_TRUE(PhysicalASTCBlock(0x0008004002001DFCULL).IsIllegalEncoding());
+ EXPECT_TRUE(PhysicalASTCBlock(0x0007FFC001FFFDFCULL).IsIllegalEncoding());
+}
+
+// Test to see if we can properly identify the number of partitions in a block
+// In particular -- we need to make sure we properly identify single and
+// multi-partition blocks, but also that void extent and error blocks don't
+// return valid numbers of partitions
+TEST(PhysicalASTCBlockTest, TestNumPartitions) {
+ // Various valid block modes, but all single partition
+ EXPECT_EQ(PhysicalASTCBlock(0x0000000001FE000173ULL).NumPartitions(), 1);
+ EXPECT_EQ(PhysicalASTCBlock(0x0000000001FE0005FFULL).NumPartitions(), 1);
+ EXPECT_EQ(PhysicalASTCBlock(0x0000000001FE000108ULL).NumPartitions(), 1);
+
+ // Two to four partitions don't have enough bits for color.
+ EXPECT_FALSE(PhysicalASTCBlock(0x000000000000000973ULL).NumPartitions());
+ EXPECT_FALSE(PhysicalASTCBlock(0x000000000000001173ULL).NumPartitions());
+ EXPECT_FALSE(PhysicalASTCBlock(0x000000000000001973ULL).NumPartitions());
+
+ // Test against having more than one partition
+ PhysicalASTCBlock non_shared_cem(0x4000000000800D44ULL);
+ EXPECT_EQ(non_shared_cem.NumPartitions(), 2);
+}
+
+// Test the color endpoint modes specified for how the endpoints are encoded.
+// In particular, test that shared color endpoint modes work for multi-partition
+// blocks and that non-shared color endpoint modes also work.
+TEST(PhysicalASTCBlockTest, TestColorEndpointModes) {
+ // Four partitions -- one shared CEM
+ const auto blk1 = PhysicalASTCBlock(0x000000000000001961ULL);
+ for (int i = 0; i < 4; ++i) {
+ EXPECT_EQ(blk1.GetEndpointMode(i), ColorEndpointMode::kLDRLumaDirect);
+ }
+
+ // Void extent blocks have no endpoint modes
+ EXPECT_FALSE(PhysicalASTCBlock(0xFFF8003FFE000DFCULL).GetEndpointMode(0));
+
+ // Test out of range partitions
+ EXPECT_FALSE(PhysicalASTCBlock(0x0000000001FE000173ULL).GetEndpointMode(1));
+ EXPECT_FALSE(PhysicalASTCBlock(0x0000000001FE000173ULL).GetEndpointMode(-1));
+ EXPECT_FALSE(PhysicalASTCBlock(0x0000000001FE000173ULL).GetEndpointMode(100));
+
+ // Error blocks have no endpoint modes
+ EXPECT_FALSE(kErrorBlock.GetEndpointMode(0));
+
+ // Test non-shared CEMs
+ PhysicalASTCBlock non_shared_cem(0x4000000000800D44ULL);
+ EXPECT_EQ(non_shared_cem.GetEndpointMode(0),
+ ColorEndpointMode::kLDRLumaDirect);
+ EXPECT_EQ(non_shared_cem.GetEndpointMode(1),
+ ColorEndpointMode::kLDRLumaBaseOffset);
+}
+
+// Make sure that if we have more than one partition then we have proper
+// partition IDs (these determine which pixels correspond to which partition)
+TEST(PhysicalASTCBlockTest, TestPartitionID) {
+ // Valid partitions
+ EXPECT_EQ(PhysicalASTCBlock(0x4000000000FFED44ULL).PartitionID(), 0x3FF);
+ EXPECT_EQ(PhysicalASTCBlock(0x4000000000AAAD44ULL).PartitionID(), 0x155);
+
+ // Error blocks have no partition IDs
+ EXPECT_FALSE(kErrorBlock.PartitionID());
+
+ // Void extent blocks have no endpoint modes
+ EXPECT_FALSE(PhysicalASTCBlock(0xFFF8003FFE000DFCULL).PartitionID());
+}
+
+// Make sure that we're properly attributing the number of bits associated with
+// the encoded color values.
+TEST(PhysicalASTCBlockTest, TestNumColorBits) {
+ // If we're using a direct luma channel, then the number of color bits is 16
+ EXPECT_EQ(PhysicalASTCBlock(0x0000000001FE000173ULL).NumColorValues(), 2);
+ EXPECT_EQ(PhysicalASTCBlock(0x0000000001FE000173ULL).NumColorBits(), 16);
+
+ // Error blocks have nothing
+ EXPECT_FALSE(kErrorBlock.NumColorValues());
+ EXPECT_FALSE(kErrorBlock.NumColorBits());
+
+ // Void extent blocks have four color values and 64 bits of color
+ EXPECT_EQ(PhysicalASTCBlock(0xFFF8003FFE000DFCULL).NumColorValues(), 4);
+ EXPECT_EQ(PhysicalASTCBlock(0xFFF8003FFE000DFCULL).NumColorBits(), 64);
+}
+
+// Make sure that we're properly decoding the range of values that each of the
+// encoded color values can take
+TEST(PhysicalASTCBlockTest, TestColorValuesRange) {
+ // If we're using a direct luma channel, then we use two color values up to
+ // a full byte each.
+ EXPECT_EQ(PhysicalASTCBlock(0x0000000001FE000173ULL).ColorValuesRange(), 255);
+
+ // Error blocks have nothing
+ EXPECT_FALSE(kErrorBlock.ColorValuesRange());
+
+ // Void extent blocks have four color values and 64 bits of color, so the
+ // color range for each is sixteen bits.
+ EXPECT_EQ(PhysicalASTCBlock(0xFFF8003FFE000DFCULL).ColorValuesRange(),
+ (1 << 16) - 1);
+}
+
+// Test that we know where the color data starts. This is different mostly
+// depending on whether or not the block is single-partition or void extent.
+TEST(PhysicalASTCBlockTest, TestColorStartBits) {
+ // Void extent blocks start at bit 64
+ EXPECT_EQ(PhysicalASTCBlock(0xFFF8003FFE000DFCULL).ColorStartBit(), 64);
+
+ // Error blocks don't start anywhere
+ EXPECT_FALSE(kErrorBlock.ColorStartBit());
+
+ // Single partition blocks start at bit 17
+ EXPECT_EQ(PhysicalASTCBlock(0x0000000001FE000173ULL).ColorStartBit(), 17);
+ EXPECT_EQ(PhysicalASTCBlock(0x0000000001FE0005FFULL).ColorStartBit(), 17);
+ EXPECT_EQ(PhysicalASTCBlock(0x0000000001FE000108ULL).ColorStartBit(), 17);
+
+ // Multi-partition blocks start at bit 29
+ EXPECT_EQ(PhysicalASTCBlock(0x4000000000FFED44ULL).ColorStartBit(), 29);
+ EXPECT_EQ(PhysicalASTCBlock(0x4000000000AAAD44ULL).ColorStartBit(), 29);
+}
+
+} // namespace
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/quantization_test.cc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/quantization_test.cc
new file mode 100644
index 00000000000..f882876e7cc
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/quantization_test.cc
@@ -0,0 +1,288 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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 "src/decoder/quantization.h"
+#include "src/decoder/integer_sequence_codec.h"
+
+#include <gtest/gtest.h>
+
+#include <functional>
+#include <string>
+#include <vector>
+
+namespace astc_codec {
+
+namespace {
+
+// Make sure that we never exceed the maximum range that we pass in.
+TEST(QuantizationTest, TestQuantizeMaxRange) {
+ for (int i = kEndpointRangeMinValue; i < 256; ++i) {
+ EXPECT_LE(QuantizeCEValueToRange(255, i), i);
+ }
+
+ for (int i = 1; i < kWeightRangeMaxValue; ++i) {
+ EXPECT_LE(QuantizeWeightToRange(64, i), i);
+ }
+}
+
+// Make sure that whenever we unquantize and requantize a value we get back
+// what we started with.
+TEST(QuantizationTest, TestReversibility) {
+ for (auto itr = ISERangeBegin(); itr != ISERangeEnd(); itr++) {
+ const int range = *itr;
+ if (range <= kWeightRangeMaxValue) {
+ for (int j = 0; j <= range; ++j) {
+ const int q = UnquantizeWeightFromRange(j, range);
+ EXPECT_EQ(QuantizeWeightToRange(q, range), j);
+ }
+ }
+
+ if (range >= kEndpointRangeMinValue) {
+ for (int j = 0; j <= range; ++j) {
+ const int q = UnquantizeCEValueFromRange(j, range);
+ EXPECT_EQ(QuantizeCEValueToRange(q, range), j);
+ }
+ }
+ }
+}
+
+// Make sure that whenever we quantize a non-maximal value it gets sent to the
+// proper range
+TEST(QuantizationTest, TestQuantizationRange) {
+ for (auto itr = ISERangeBegin(); itr != ISERangeEnd(); itr++) {
+ const int range = *itr;
+ if (range >= kEndpointRangeMinValue) {
+ EXPECT_LE(QuantizeCEValueToRange(0, range), range);
+ EXPECT_LE(QuantizeCEValueToRange(4, range), range);
+ EXPECT_LE(QuantizeCEValueToRange(15, range), range);
+ EXPECT_LE(QuantizeCEValueToRange(22, range), range);
+ EXPECT_LE(QuantizeCEValueToRange(66, range), range);
+ EXPECT_LE(QuantizeCEValueToRange(91, range), range);
+ EXPECT_LE(QuantizeCEValueToRange(126, range), range);
+ }
+
+ if (range <= kWeightRangeMaxValue) {
+ EXPECT_LE(QuantizeWeightToRange(0, range), range);
+ EXPECT_LE(QuantizeWeightToRange(4, range), range);
+ EXPECT_LE(QuantizeWeightToRange(15, range), range);
+ EXPECT_LE(QuantizeWeightToRange(22, range), range);
+ }
+ }
+}
+
+// Make sure that whenever we unquantize a value it remains within [0, 255]
+TEST(QuantizationTest, TestUnquantizationRange) {
+ EXPECT_LT(UnquantizeCEValueFromRange(2, 7), 256);
+ EXPECT_LT(UnquantizeCEValueFromRange(7, 7), 256);
+ EXPECT_LT(UnquantizeCEValueFromRange(39, 63), 256);
+ EXPECT_LT(UnquantizeCEValueFromRange(66, 79), 256);
+ EXPECT_LT(UnquantizeCEValueFromRange(91, 191), 256);
+ EXPECT_LT(UnquantizeCEValueFromRange(126, 255), 256);
+ EXPECT_LT(UnquantizeCEValueFromRange(255, 255), 256);
+
+ EXPECT_LE(UnquantizeWeightFromRange(0, 1), 64);
+ EXPECT_LE(UnquantizeWeightFromRange(2, 7), 64);
+ EXPECT_LE(UnquantizeWeightFromRange(7, 7), 64);
+ EXPECT_LE(UnquantizeWeightFromRange(29, 31), 64);
+}
+
+// When we quantize a value, it should use the largest quantization range that
+// does not exceed the desired range.
+TEST(QuantizationTest, TestUpperBoundRanges) {
+ auto expected_range_itr = ISERangeBegin();
+ for (int desired_range = 1; desired_range < 256; ++desired_range) {
+ if (desired_range == *(expected_range_itr + 1)) {
+ ++expected_range_itr;
+ }
+ const int expected_range = *expected_range_itr;
+ ASSERT_LE(expected_range, desired_range);
+
+ if (desired_range >= kEndpointRangeMinValue) {
+ EXPECT_EQ(QuantizeCEValueToRange(0, desired_range),
+ QuantizeCEValueToRange(0, expected_range));
+
+ EXPECT_EQ(QuantizeCEValueToRange(208, desired_range),
+ QuantizeCEValueToRange(208, expected_range));
+
+ EXPECT_EQ(QuantizeCEValueToRange(173, desired_range),
+ QuantizeCEValueToRange(173, expected_range));
+
+ EXPECT_EQ(QuantizeCEValueToRange(13, desired_range),
+ QuantizeCEValueToRange(13, expected_range));
+
+ EXPECT_EQ(QuantizeCEValueToRange(255, desired_range),
+ QuantizeCEValueToRange(255, expected_range));
+ }
+
+ if (desired_range <= kWeightRangeMaxValue) {
+ EXPECT_EQ(QuantizeWeightToRange(0, desired_range),
+ QuantizeWeightToRange(0, expected_range));
+
+ EXPECT_EQ(QuantizeWeightToRange(63, desired_range),
+ QuantizeWeightToRange(63, expected_range));
+
+ EXPECT_EQ(QuantizeWeightToRange(12, desired_range),
+ QuantizeWeightToRange(12, expected_range));
+
+ EXPECT_EQ(QuantizeWeightToRange(23, desired_range),
+ QuantizeWeightToRange(23, expected_range));
+ }
+ }
+
+ // Make sure that we covered all the possible ranges
+ ASSERT_EQ(std::next(expected_range_itr), ISERangeEnd());
+}
+
+// Make sure that quantizing to the largest range is the identity function.
+TEST(QuantizationTest, TestIdentity) {
+ for (int i = 0; i < 256; ++i) {
+ EXPECT_EQ(QuantizeCEValueToRange(i, 255), i);
+ }
+
+ // Note: This doesn't apply to weights since there's a weird hack to convert
+ // values from [0, 31] to [0, 64].
+}
+
+// Make sure that bit quantization is monotonic with respect to the input,
+// since quantizing and dequantizing bits is a matter of truncation and bit
+// replication
+TEST(QuantizationTest, TestMonotonicBitPacking) {
+ for (int num_bits = 3; num_bits < 8; ++num_bits) {
+ const int range = (1 << num_bits) - 1;
+ int last_quant_val = -1;
+ for (int i = 0; i < 256; ++i) {
+ const int quant_val = QuantizeCEValueToRange(i, range);
+ EXPECT_LE(last_quant_val, quant_val);
+ last_quant_val = quant_val;
+ }
+
+ // Also expect the last quantization val to be equal to the range
+ EXPECT_EQ(last_quant_val, range);
+
+ if (range <= kWeightRangeMaxValue) {
+ last_quant_val = -1;
+ for (int i = 0; i <= 64; ++i) {
+ const int quant_val = QuantizeWeightToRange(i, range);
+ EXPECT_LE(last_quant_val, quant_val);
+ last_quant_val = quant_val;
+ }
+ EXPECT_EQ(last_quant_val, range);
+ }
+ }
+}
+
+// Make sure that bit quantization reflects that quantized values below the bit
+// replication threshold get mapped to zero
+TEST(QuantizationTest, TestSmallBitPacking) {
+ for (int num_bits = 1; num_bits <= 8; ++num_bits) {
+ const int range = (1 << num_bits) - 1;
+
+ // The largest number that should map to zero is one less than half of the
+ // smallest representation w.r.t. range. For example: if we have a range
+ // of 7, it means that we have 3 total bits abc for quantized values. If we
+ // unquantize to 8 bits, it means that our resulting value will be abcabcab.
+ // Hence, we map 000 to 0 and 001 to 0b00100100 = 36. The earliest value
+ // that should not map to zero with three bits is therefore 0b00001111 = 15.
+ // This ends up being (1 << (8 - 3 - 1)) - 1. We don't use 0b00011111 = 31
+ // because this would "round up" to 1 during quantization. This value is not
+ // necessarily the largest, but it is the largest that we can *guarantee*
+ // should map to zero.
+
+ if (range >= kEndpointRangeMinValue) {
+ constexpr int cev_bits = 8;
+ const int half_max_quant_bits = std::max(0, cev_bits - num_bits - 1);
+ const int largest_cev_to_zero = (1 << half_max_quant_bits) - 1;
+ EXPECT_EQ(QuantizeCEValueToRange(largest_cev_to_zero, range), 0)
+ << " Largest CEV to zero: " << largest_cev_to_zero
+ << " Range: " << range;
+ }
+
+ if (range <= kWeightRangeMaxValue) {
+ constexpr int weight_bits = 6;
+ const int half_max_quant_bits = std::max(0, weight_bits - num_bits - 1);
+ const int largest_weight_to_zero = (1 << half_max_quant_bits) - 1;
+ EXPECT_EQ(QuantizeWeightToRange(largest_weight_to_zero, range), 0)
+ << " Largest weight to zero: " << largest_weight_to_zero
+ << " Range: " << range;
+ }
+ }
+}
+
+// Test specific quint and trit weight encodings with values that were obtained
+// using the reference ASTC codec.
+TEST(QuantizationTest, TestSpecificQuintTritPackings) {
+ std::vector<int> vals = { 4, 6, 4, 6, 7, 5, 7, 5 };
+ std::vector<int> quantized;
+
+ // Test a quint packing
+ std::transform(
+ vals.begin(), vals.end(), std::back_inserter(quantized),
+ std::bind(UnquantizeWeightFromRange, std::placeholders::_1, 9));
+ const std::vector<int> quintExpected = {14, 21, 14, 21, 43, 50, 43, 50 };
+ EXPECT_EQ(quantized, quintExpected);
+
+ // Test a trit packing
+ std::transform(
+ vals.begin(), vals.end(), quantized.begin(),
+ std::bind(UnquantizeWeightFromRange, std::placeholders::_1, 11));
+ const std::vector<int> tritExpected = { 5, 23, 5, 23, 41, 59, 41, 59 };
+ EXPECT_EQ(quantized, tritExpected);
+}
+
+// Make sure that we properly die when we pass in values below the minimum
+// allowed ranges for our quantization intervals.
+TEST(QuantizationDeathTest, TestInvalidMinRange) {
+ for (int i = 0; i < kEndpointRangeMinValue; ++i) {
+ EXPECT_DEBUG_DEATH(QuantizeCEValueToRange(0, i), "");
+ EXPECT_DEBUG_DEATH(UnquantizeCEValueFromRange(0, i), "");
+ }
+
+ EXPECT_DEBUG_DEATH(QuantizeWeightToRange(0, 0), "");
+ EXPECT_DEBUG_DEATH(UnquantizeWeightFromRange(0, 0), "");
+}
+
+// Make sure that we properly die when we pass in bogus values.
+TEST(QuantizationDeathTest, TestOutOfRange) {
+ EXPECT_DEBUG_DEATH(QuantizeCEValueToRange(-1, 10), "");
+ EXPECT_DEBUG_DEATH(QuantizeCEValueToRange(256, 7), "");
+ EXPECT_DEBUG_DEATH(QuantizeCEValueToRange(10000, 17), "");
+
+ EXPECT_DEBUG_DEATH(UnquantizeCEValueFromRange(-1, 10), "");
+ EXPECT_DEBUG_DEATH(UnquantizeCEValueFromRange(8, 7), "");
+ EXPECT_DEBUG_DEATH(UnquantizeCEValueFromRange(-1000, 17), "");
+
+ EXPECT_DEBUG_DEATH(QuantizeCEValueToRange(0, -7), "");
+ EXPECT_DEBUG_DEATH(UnquantizeCEValueFromRange(0, -17), "");
+
+ EXPECT_DEBUG_DEATH(QuantizeCEValueToRange(0, 257), "");
+ EXPECT_DEBUG_DEATH(UnquantizeCEValueFromRange(0, 256), "");
+
+ EXPECT_DEBUG_DEATH(QuantizeWeightToRange(-1, 10), "");
+ EXPECT_DEBUG_DEATH(QuantizeWeightToRange(256, 7), "");
+ EXPECT_DEBUG_DEATH(QuantizeWeightToRange(10000, 17), "");
+
+ EXPECT_DEBUG_DEATH(UnquantizeWeightFromRange(-1, 10), "");
+ EXPECT_DEBUG_DEATH(UnquantizeWeightFromRange(8, 7), "");
+ EXPECT_DEBUG_DEATH(UnquantizeWeightFromRange(-1000, 17), "");
+
+ EXPECT_DEBUG_DEATH(QuantizeWeightToRange(0, -7), "");
+ EXPECT_DEBUG_DEATH(UnquantizeWeightFromRange(0, -17), "");
+
+ EXPECT_DEBUG_DEATH(QuantizeWeightToRange(0, 32), "");
+ EXPECT_DEBUG_DEATH(UnquantizeWeightFromRange(0, 64), "");
+}
+
+} // namespace
+
+} // namespace astc_codec
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/weight_infill_test.cc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/weight_infill_test.cc
new file mode 100644
index 00000000000..79c77455441
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/test/weight_infill_test.cc
@@ -0,0 +1,69 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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 "src/decoder/weight_infill.h"
+#include "src/decoder/footprint.h"
+
+#include <gtest/gtest.h>
+
+#include <vector>
+
+namespace astc_codec {
+
+namespace {
+
+// Make sure that the physical size of the bit representations for certain
+// dimensions of weight grids matches our expectations
+TEST(ASTCWeightInfillTest, TestGetBitCount) {
+ // Bit encodings
+ EXPECT_EQ(32, CountBitsForWeights(4, 4, 3));
+ EXPECT_EQ(48, CountBitsForWeights(4, 4, 7));
+ EXPECT_EQ(24, CountBitsForWeights(2, 4, 7));
+ EXPECT_EQ(8, CountBitsForWeights(2, 4, 1));
+
+ // Trit encodings
+ EXPECT_EQ(32, CountBitsForWeights(4, 5, 2));
+ EXPECT_EQ(26, CountBitsForWeights(4, 4, 2));
+ EXPECT_EQ(52, CountBitsForWeights(4, 5, 5));
+ EXPECT_EQ(42, CountBitsForWeights(4, 4, 5));
+
+ // Quint encodings
+ EXPECT_EQ(21, CountBitsForWeights(3, 3, 4));
+ EXPECT_EQ(38, CountBitsForWeights(4, 4, 4));
+ EXPECT_EQ(49, CountBitsForWeights(3, 7, 4));
+ EXPECT_EQ(52, CountBitsForWeights(4, 3, 19));
+ EXPECT_EQ(70, CountBitsForWeights(4, 4, 19));
+}
+
+// Make sure that we bilerp our weights properly
+TEST(ASTCWeightInfillTest, TestInfillBilerp) {
+ std::vector<int> weights = InfillWeights(
+ {{ 1, 3, 5, 3, 5, 7, 5, 7, 9 }}, Footprint::Get5x5(), 3, 3);
+
+ std::vector<int> expected_weights = {
+ 1, 2, 3, 4, 5,
+ 2, 3, 4, 5, 6,
+ 3, 4, 5, 6, 7,
+ 4, 5, 6, 7, 8,
+ 5, 6, 7, 8, 9 };
+
+ ASSERT_EQ(weights.size(), expected_weights.size());
+ for (int i = 0; i < weights.size(); ++i) {
+ EXPECT_EQ(weights[i], expected_weights[i]);
+ }
+}
+
+} // namespace
+
+} // namespace astc_codec
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/atlas_small_4x4.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/atlas_small_4x4.astc
new file mode 100644
index 00000000000..0dd080b12d2
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/atlas_small_4x4.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/atlas_small_4x4.bmp b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/atlas_small_4x4.bmp
new file mode 100644
index 00000000000..32412b3e535
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/atlas_small_4x4.bmp
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/atlas_small_5x5.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/atlas_small_5x5.astc
new file mode 100644
index 00000000000..2831c90ed51
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/atlas_small_5x5.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/atlas_small_5x5.bmp b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/atlas_small_5x5.bmp
new file mode 100644
index 00000000000..95b0186a9d8
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/atlas_small_5x5.bmp
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/atlas_small_6x6.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/atlas_small_6x6.astc
new file mode 100644
index 00000000000..b2cc9c2d52d
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/atlas_small_6x6.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/atlas_small_6x6.bmp b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/atlas_small_6x6.bmp
new file mode 100644
index 00000000000..796812e2f05
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/atlas_small_6x6.bmp
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/atlas_small_8x8.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/atlas_small_8x8.astc
new file mode 100644
index 00000000000..a8805f28517
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/atlas_small_8x8.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/atlas_small_8x8.bmp b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/atlas_small_8x8.bmp
new file mode 100644
index 00000000000..8f92698c01f
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/atlas_small_8x8.bmp
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkerboard.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkerboard.astc
new file mode 100644
index 00000000000..79acdca3732
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkerboard.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_10.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_10.astc
new file mode 100644
index 00000000000..e3b196578c3
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_10.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_11.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_11.astc
new file mode 100644
index 00000000000..c80c6a46018
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_11.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_12.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_12.astc
new file mode 100644
index 00000000000..a82583a9ed5
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_12.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_4.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_4.astc
new file mode 100644
index 00000000000..ac60716458f
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_4.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_5.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_5.astc
new file mode 100644
index 00000000000..0389c53d54b
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_5.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_6.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_6.astc
new file mode 100644
index 00000000000..210dd340e12
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_6.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_7.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_7.astc
new file mode 100644
index 00000000000..8ea10f84f47
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_7.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_8.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_8.astc
new file mode 100644
index 00000000000..3708882dd52
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_8.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_9.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_9.astc
new file mode 100644
index 00000000000..b5c962a14fe
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/checkered_9.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_10x10.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_10x10.astc
new file mode 100644
index 00000000000..6799cd08644
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_10x10.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_10x10.bmp b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_10x10.bmp
new file mode 100644
index 00000000000..7ded6adf872
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_10x10.bmp
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_10x5.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_10x5.astc
new file mode 100644
index 00000000000..cbfe4f26448
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_10x5.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_10x5.bmp b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_10x5.bmp
new file mode 100644
index 00000000000..68202f8516f
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_10x5.bmp
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_10x6.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_10x6.astc
new file mode 100644
index 00000000000..53e37b4a982
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_10x6.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_10x6.bmp b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_10x6.bmp
new file mode 100644
index 00000000000..3dffe72e97e
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_10x6.bmp
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_10x8.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_10x8.astc
new file mode 100644
index 00000000000..b9613e58174
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_10x8.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_10x8.bmp b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_10x8.bmp
new file mode 100644
index 00000000000..b7d75f7377e
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_10x8.bmp
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_12x10.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_12x10.astc
new file mode 100644
index 00000000000..54f59193542
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_12x10.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_12x10.bmp b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_12x10.bmp
new file mode 100644
index 00000000000..2007a3dc393
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_12x10.bmp
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_12x12.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_12x12.astc
new file mode 100644
index 00000000000..d94d0a6c053
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_12x12.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_12x12.bmp b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_12x12.bmp
new file mode 100644
index 00000000000..57e24a22969
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_12x12.bmp
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_4x4.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_4x4.astc
new file mode 100644
index 00000000000..fdf3cd6b5f7
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_4x4.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_4x4.bmp b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_4x4.bmp
new file mode 100644
index 00000000000..fcb4e0775eb
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_4x4.bmp
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_5x4.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_5x4.astc
new file mode 100644
index 00000000000..0683059469e
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_5x4.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_5x4.bmp b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_5x4.bmp
new file mode 100644
index 00000000000..9cae5f9379b
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_5x4.bmp
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_5x5.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_5x5.astc
new file mode 100644
index 00000000000..03c3395ab95
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_5x5.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_5x5.bmp b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_5x5.bmp
new file mode 100644
index 00000000000..c5ddeec4f38
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_5x5.bmp
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_6x5.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_6x5.astc
new file mode 100644
index 00000000000..d0d455d0bfb
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_6x5.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_6x5.bmp b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_6x5.bmp
new file mode 100644
index 00000000000..94a005dcee5
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_6x5.bmp
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_6x6.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_6x6.astc
new file mode 100644
index 00000000000..47e038efd95
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_6x6.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_6x6.bmp b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_6x6.bmp
new file mode 100644
index 00000000000..fb1f41c424f
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_6x6.bmp
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_8x5.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_8x5.astc
new file mode 100644
index 00000000000..4ef49d657dd
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_8x5.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_8x5.bmp b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_8x5.bmp
new file mode 100644
index 00000000000..6d4f6093f3d
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_8x5.bmp
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_8x6.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_8x6.astc
new file mode 100644
index 00000000000..113550954cc
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_8x6.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_8x6.bmp b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_8x6.bmp
new file mode 100644
index 00000000000..35e71219452
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_8x6.bmp
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_8x8.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_8x8.astc
new file mode 100644
index 00000000000..bdeb9c9b79e
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_8x8.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_8x8.bmp b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_8x8.bmp
new file mode 100644
index 00000000000..3b3a1c89641
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/footprint_8x8.bmp
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_12x12.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_12x12.astc
new file mode 100644
index 00000000000..cc0fc3d3c01
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_12x12.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_12x12.bmp b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_12x12.bmp
new file mode 100644
index 00000000000..50538ded11e
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_12x12.bmp
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_4x4.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_4x4.astc
new file mode 100644
index 00000000000..84527d3ab76
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_4x4.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_4x4.bmp b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_4x4.bmp
new file mode 100644
index 00000000000..fae4f9191dc
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_4x4.bmp
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_5x4.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_5x4.astc
new file mode 100644
index 00000000000..377938de67e
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_5x4.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_5x4.bmp b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_5x4.bmp
new file mode 100644
index 00000000000..a0c1fdbb89d
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_5x4.bmp
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_6x6.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_6x6.astc
new file mode 100644
index 00000000000..8a70c0fda6f
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_6x6.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_6x6.bmp b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_6x6.bmp
new file mode 100644
index 00000000000..e1a5dcd8c27
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_6x6.bmp
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_8x8.astc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_8x8.astc
new file mode 100644
index 00000000000..1bca381d578
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_8x8.astc
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_8x8.bmp b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_8x8.bmp
new file mode 100644
index 00000000000..12b6eb24be7
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/testdata/rgb_8x8.bmp
Binary files differ
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/tools/astc_inspector_cli.cc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/tools/astc_inspector_cli.cc
new file mode 100644
index 00000000000..105f57498af
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/tools/astc_inspector_cli.cc
@@ -0,0 +1,785 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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.
+
+// astc_inspector_cli collects the various statistics of a stream of ASTC data
+// stored in an ASTC file.
+//
+// Example usage:
+// To dump statistics about an ASTC file, use:
+// astc_inspector_cli <filename>
+//
+// To dump statistics on a specific block in an ASTC file, use:
+// astc_inspector_cli <filename> <number>
+
+#include <algorithm>
+#include <array>
+#include <fstream>
+#include <functional>
+#include <iomanip>
+#include <iostream>
+#include <memory>
+#include <numeric>
+#include <sstream>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include "src/base/string_utils.h"
+#include "src/decoder/astc_file.h"
+#include "src/decoder/endpoint_codec.h"
+#include "src/decoder/intermediate_astc_block.h"
+#include "src/decoder/partition.h"
+#include "src/decoder/quantization.h"
+#include "src/decoder/weight_infill.h"
+
+using astc_codec::ASTCFile;
+using astc_codec::ColorEndpointMode;
+using astc_codec::IntermediateBlockData;
+using astc_codec::PhysicalASTCBlock;
+using astc_codec::RgbaColor;
+using astc_codec::VoidExtentData;
+using astc_codec::base::Optional;
+
+namespace {
+
+constexpr int kNumEndpointModes =
+ static_cast<int>(ColorEndpointMode::kNumColorEndpointModes);
+constexpr std::array<const char*, kNumEndpointModes> kModeStrings {{
+ "kLDRLumaDirect", "kLDRLumaBaseOffset", "kHDRLumaLargeRange",
+ "kHDRLumaSmallRange", "kLDRLumaAlphaDirect", "kLDRLumaAlphaBaseOffset",
+ "kLDRRGBBaseScale", "kHDRRGBBaseScale", "kLDRRGBDirect",
+ "kLDRRGBBaseOffset", "kLDRRGBBaseScaleTwoA", "kHDRRGBDirect",
+ "kLDRRGBADirect", "kLDRRGBABaseOffset", "kHDRRGBDirectLDRAlpha",
+ "kHDRRGBDirectHDRAlpha" }};
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// A generic stat that should be tracked via an instance of ASTCFileStats.
+class Stat {
+ public:
+ explicit Stat(const std::vector<IntermediateBlockData>* blocks, size_t total)
+ : blocks_(blocks), total_(total) { }
+ virtual ~Stat() { }
+
+ virtual std::ostream& PrintToStream(std::ostream& out) const = 0;
+
+ protected:
+ // Utility function to iterate over all of the blocks that are not void-extent
+ // blocks. FoldFn optionally allows a value to accumulate. It should be of the
+ // type:
+ // (const IntermediateBlockData&, T x) -> T
+ template<typename T, typename FoldFn>
+ T IterateBlocks(T initial, FoldFn f) const {
+ T result = initial;
+ for (const auto& block : *blocks_) {
+ result = f(block, std::move(result));
+ }
+ return result;
+ }
+
+ size_t NumBlocks() const { return total_; }
+
+ private:
+ const std::vector<IntermediateBlockData>* const blocks_;
+ const size_t total_;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Computes the number of void extent blocks.
+class VoidExtentCount : public Stat {
+ public:
+ VoidExtentCount(const std::vector<IntermediateBlockData>* blocks,
+ size_t total, std::string description)
+ : Stat(blocks, total), description_(std::move(description)),
+ count_(total - blocks->size()) { }
+
+ std::ostream& PrintToStream(std::ostream& out) const override {
+ return out << description_ << ": " << count_
+ << " (" << (count_ * 100 / NumBlocks()) << "%)" << std::endl;
+ };
+
+ private:
+ const std::string description_;
+ const size_t count_;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Computes a per-block stat and reports it as an average over all blocks.
+class PerBlockAverage : public Stat {
+ public:
+ PerBlockAverage(const std::vector<IntermediateBlockData>* blocks,
+ size_t total, std::string description,
+ const std::function<int(const IntermediateBlockData&)> &fn)
+ : Stat(blocks, total),
+ description_(std::move(description)) {
+ int sum = 0;
+ size_t count = 0;
+ for (const auto& block : *blocks) {
+ sum += fn(block);
+ ++count;
+ }
+ average_ = sum / count;
+ }
+
+ std::ostream& PrintToStream(std::ostream& out) const override {
+ return out << description_ << ": " << average_ << std::endl;
+ }
+
+ private:
+ size_t average_;
+ std::string description_;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Computes a per-block true or false value and reports how many blocks return
+// true with a percentage of total blocks.
+class PerBlockPredicate : public Stat {
+ public:
+ PerBlockPredicate(const std::vector<IntermediateBlockData>* blocks,
+ size_t total, std::string description,
+ const std::function<bool(const IntermediateBlockData&)> &fn)
+ : Stat(blocks, total),
+ description_(std::move(description)),
+ count_(std::count_if(blocks->begin(), blocks->end(), fn)) { }
+
+ std::ostream& PrintToStream(std::ostream& out) const override {
+ return out << description_ << ": " << count_
+ << " (" << (count_ * 100 / NumBlocks()) << "%)" << std::endl;
+ };
+
+ private:
+ const std::string description_;
+ const size_t count_;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Returns a histogram of the number of occurrences of each endpoint mode in
+// the list of blocks. Note, due to multi-subset blocks, the sum of these
+// values will not match the total number of blocks.
+class ModeCountsStat : public Stat {
+ public:
+ explicit ModeCountsStat(const std::vector<IntermediateBlockData>* blocks,
+ size_t total)
+ : Stat(blocks, total),
+ mode_counts_(IterateBlocks<ModeArray>(
+ {}, [](const IntermediateBlockData& data, ModeArray&& m) {
+ auto result = m;
+ for (const auto& ep : data.endpoints) {
+ result[static_cast<int>(ep.mode)]++;
+ }
+ return result;
+ })) { }
+
+ std::ostream& PrintToStream(std::ostream& out) const override {
+ const size_t total_modes_used =
+ std::accumulate(mode_counts_.begin(), mode_counts_.end(), 0);
+
+ out << "Endpoint modes used: " << std::endl;
+ for (size_t i = 0; i < kNumEndpointModes; ++i) {
+ out << " ";
+ out << std::setw(30) << std::left << std::setfill('.') << kModeStrings[i];
+ out << std::setw(8) << std::right << std::setfill('.') << mode_counts_[i];
+
+ std::stringstream pct;
+ pct << " (" << (mode_counts_[i] * 100 / total_modes_used) << "%)";
+
+ out << std::setw(6) << std::right << std::setfill(' ') << pct.str();
+ out << std::endl;
+ }
+
+ return out;
+ }
+
+ private:
+ using ModeArray = std::array<int, kNumEndpointModes>;
+ const ModeArray mode_counts_;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Counts the number of unique endpoints used across all blocks.
+class UniqueEndpointsCount : public Stat {
+ public:
+ explicit UniqueEndpointsCount(
+ const std::vector<IntermediateBlockData>* blocks, size_t total)
+ : Stat(blocks, total),
+ unique_endpoints_(IterateBlocks<UniqueEndpointSet>(
+ UniqueEndpointSet(),
+ [](const IntermediateBlockData& data, UniqueEndpointSet&& eps) {
+ UniqueEndpointSet result(eps);
+ for (const auto& ep : data.endpoints) {
+ RgbaColor ep_one, ep_two;
+ DecodeColorsForMode(ep.colors, data.endpoint_range.value(),
+ ep.mode, &ep_one, &ep_two);
+ result.insert(PackEndpoint(ep_one));
+ result.insert(PackEndpoint(ep_two));
+ }
+ return result;
+ })) { }
+
+ std::ostream& PrintToStream(std::ostream& out) const override {
+ out << "Num unique endpoints: " << unique_endpoints_.size() << std::endl;
+ return out;
+ }
+
+ private:
+ static uint32_t PackEndpoint(const RgbaColor& color) {
+ uint32_t result = 0;
+ for (const int& c : color) {
+ constexpr int kSaturatedChannelValue = 0xFF;
+ assert(c >= 0);
+ assert(c <= kSaturatedChannelValue);
+ result <<= 8;
+ result |= c;
+ }
+ return result;
+ }
+
+ using UniqueEndpointSet = std::unordered_set<uint32_t>;
+ const UniqueEndpointSet unique_endpoints_;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Computes a histogram of the number of occurrences of 1-4 subset partitions.
+class PartitionCountStat : public Stat {
+ public:
+ explicit PartitionCountStat(const std::vector<IntermediateBlockData>* blocks,
+ size_t total)
+ : Stat(blocks, total)
+ , part_counts_(IterateBlocks<PartCount>(
+ {}, [](const IntermediateBlockData& data, PartCount&& m) {
+ PartCount result = m;
+ result[data.endpoints.size() - 1]++;
+ return result;
+ })) { }
+
+ std::ostream& PrintToStream(std::ostream& out) const override {
+ out << "Num partitions used: " << std::endl;
+ for (size_t i = 0; i < part_counts_.size(); ++i) {
+ out << " " << i + 1 << ": " << part_counts_[i] << std::endl;
+ }
+ return out;
+ }
+
+ private:
+ using PartCount = std::array<int, 4>;
+ const PartCount part_counts_;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// For each block that uses dual-plane mode, computes and stores the dual-plane
+// channels in a vector. Outputs the number of each channel used across all
+// blocks
+class DualChannelStat : public Stat {
+ private:
+ static constexpr auto kNumDualPlaneChannels =
+ std::tuple_size<astc_codec::Endpoint>::value;
+ using CountsArray = std::array<int, kNumDualPlaneChannels>;
+
+ public:
+ explicit DualChannelStat(const std::vector<IntermediateBlockData>* blocks,
+ size_t total)
+ : Stat(blocks, total),
+ dual_channels_(IterateBlocks(
+ std::vector<int>(),
+ [](const IntermediateBlockData& data, std::vector<int>&& input) {
+ auto result = input;
+ if (data.dual_plane_channel) {
+ result.push_back(data.dual_plane_channel.value());
+ }
+ return result;
+ })) { }
+
+ std::ostream& PrintToStream(std::ostream& out) const override {
+ // Similar to the number of partitions, the number of dual plane blocks
+ // can be determined by parsing the next four fields and summing them.
+ const int num_dual_plane_blocks = dual_channels_.size();
+ out << "Number of dual-plane blocks: " << num_dual_plane_blocks
+ << " (" << (num_dual_plane_blocks * 100) / NumBlocks() << "%)"
+ << std::endl;
+
+ CountsArray counts = GetCounts();
+ assert(counts.size() == kNumDualPlaneChannels);
+
+ for (size_t i = 0; i < counts.size(); ++i) {
+ out << " " << i << ": " << counts[i] << std::endl;
+ }
+ return out;
+ }
+
+ private:
+ CountsArray GetCounts() const {
+ CountsArray counts;
+ for (size_t i = 0; i < kNumDualPlaneChannels; ++i) {
+ counts[i] =
+ std::count_if(dual_channels_.begin(), dual_channels_.end(),
+ [i](int channel) { return i == channel; });
+ }
+ return counts;
+ }
+
+ const std::vector<int> dual_channels_;
+};
+
+
+// Stores the intermediate block representations of the blocks associated with
+// an ASTCFile. Also provides various facilities for extracting aggregate data
+// from these blocks.
+class ASTCFileStats {
+ public:
+ explicit ASTCFileStats(const std::unique_ptr<ASTCFile>& astc_file) {
+ const size_t total = astc_file->NumBlocks();
+
+ for (size_t block_idx = 0; block_idx < astc_file->NumBlocks(); ++block_idx) {
+ const PhysicalASTCBlock pb = astc_file->GetBlock(block_idx);
+ assert(!pb.IsIllegalEncoding());
+ if (pb.IsIllegalEncoding()) {
+ std::cerr << "WARNING: Block " << block_idx << " has illegal encoding." << std::endl;
+ continue;
+ }
+
+ if (!pb.IsVoidExtent()) {
+ Optional<IntermediateBlockData> block = UnpackIntermediateBlock(pb);
+ if (!block) {
+ std::cerr << "WARNING: Block " << block_idx << " failed to unpack." << std::endl;
+ continue;
+ }
+
+ blocks_.push_back(block.value());
+ }
+ }
+
+ stats_.emplace_back(new UniqueEndpointsCount(&blocks_, total));
+ stats_.emplace_back(new VoidExtentCount(
+ &blocks_, total, "Num void extent blocks"));
+
+ stats_.emplace_back(new PerBlockAverage(
+ &blocks_, total, "Average weight range",
+ [](const IntermediateBlockData& b) { return b.weight_range; }));
+
+ stats_.emplace_back(new PerBlockAverage(
+ &blocks_, total, "Average number of weights",
+ [](const IntermediateBlockData& b) { return b.weights.size(); }));
+
+ stats_.emplace_back(new PerBlockPredicate(
+ &blocks_, total, "Num blocks that use blue contract mode",
+ [](const IntermediateBlockData& block) {
+ for (const auto& ep : block.endpoints) {
+ if (UsesBlueContract(
+ block.endpoint_range.valueOr(255), ep.mode, ep.colors)) {
+ return true;
+ }
+ }
+
+ return false;
+ }));
+
+ stats_.emplace_back(new ModeCountsStat(&blocks_, total));
+
+ stats_.emplace_back(new PerBlockPredicate(
+ &blocks_, total, "Num multi-part blocks",
+ [](const IntermediateBlockData& block) {
+ return block.endpoints.size() > 1;
+ }));
+ stats_.emplace_back(new PartitionCountStat(&blocks_, total));
+
+ stats_.emplace_back(new DualChannelStat(&blocks_, total));
+ }
+
+ // Returns a sorted list of pairs of the form (part_id, count) where the
+ // |part_id| is the partition ID used for 2-subset blocks, and |count| is the
+ // number of times that particular ID was used.
+ std::vector<std::pair<int, int>> ComputePartIDHistogram() const {
+ std::vector<int> part_ids(1 << 11, 0);
+ std::iota(part_ids.begin(), part_ids.end(), 0);
+
+ // The histogram will then pair IDs with counts so that we can sort by
+ // the number of instances later on.
+ std::vector<std::pair<int, int>> part_id_histogram;
+ std::transform(part_ids.begin(), part_ids.end(),
+ std::back_inserter(part_id_histogram),
+ [](const int& x) { return std::make_pair(x, 0); });
+
+ // Actually count the IDs in the list of blocks.
+ for (const auto& block : blocks_) {
+ if (block.endpoints.size() == 2) {
+ const int id = block.partition_id.value();
+ assert(part_id_histogram[id].first == id);
+ part_id_histogram[id].second++;
+ }
+ }
+
+ struct OrderBySecondGreater {
+ typedef std::pair<int, int> PairType;
+ bool operator()(const PairType& lhs, const PairType& rhs) {
+ return lhs.second > rhs.second;
+ }
+ };
+
+ // Sort by descending numbers of occurrence for each partition ID
+ std::sort(part_id_histogram.begin(), part_id_histogram.end(),
+ OrderBySecondGreater());
+
+ return part_id_histogram;
+ }
+
+ // Weights range from 2x2 - 12x12. For simplicity define buckets for every
+ // pair in [0, 12]^2.
+ constexpr static int kResolutionBuckets = 13;
+ // Returns a linear array of buckets over all pairs of grid resolutions,
+ // x-major in memory.
+ std::vector<int> ComputeWeightResolutionHistogram() const {
+ // Allocate one bucket for every grid resolution.
+ std::vector<int> resolution_histogram(
+ kResolutionBuckets * kResolutionBuckets, 0);
+
+ // Count the weight resolutions in the list of blocks.
+ for (const auto& block : blocks_) {
+ const int dim_x = block.weight_grid_dim_x;
+ const int dim_y = block.weight_grid_dim_y;
+ assert(dim_x > 0);
+ assert(dim_x < kResolutionBuckets);
+ assert(dim_y > 0);
+ assert(dim_y < kResolutionBuckets);
+ ++resolution_histogram[dim_x + dim_y * kResolutionBuckets];
+ }
+
+ return resolution_histogram;
+ }
+
+ // Runs through each defined statistic and prints it out to stdout. Also
+ // prints a histogram of partition ids used for the given blocks.
+ void PrintStats() const {
+ for (const auto& stat : stats_) {
+ stat->PrintToStream(std::cout);
+ }
+
+ // We also want to find if there are any 2-subset partition IDs that are
+ // used disproportionately often. Since partition IDs are 11 bits long, we
+ // can have as many as (1 << 11) used IDs in a given sequence of blocks.
+ const auto part_id_histogram = ComputePartIDHistogram();
+ const int total_part_ids = std::accumulate(
+ part_id_histogram.begin(), part_id_histogram.end(), 0,
+ [](const int& x, const std::pair<int, int>& hist) {
+ return x + hist.second;
+ });
+
+ if (total_part_ids > 0) {
+ // Display numbers until we either:
+ // A. Display the top 90% of used partitions
+ // B. Reach a point where the remaining partition IDs constitute < 1% of
+ // the total number of IDs used.
+ const auto prepare_part_entry = []() -> std::ostream& {
+ return std::cout << std::setw(6) << std::left << std::setfill('.');
+ };
+ int part_accum = 0;
+ std::cout << "Two subset partition ID histogram: " << std::endl;
+ std::cout << " ";
+ prepare_part_entry() << "ID" << "Count" << std::endl;
+ for (const auto& hist : part_id_histogram) {
+ part_accum += hist.second;
+ if ((hist.second * 100 / total_part_ids) < 1 ||
+ (100 * (total_part_ids - part_accum)) / total_part_ids < 10) {
+ const int num_to_display = (total_part_ids - part_accum);
+ std::cout << " rest: " << num_to_display
+ << " (" << (num_to_display * 100 / total_part_ids)
+ << "%)" << std::endl;
+ break;
+ } else {
+ std::cout << " ";
+ prepare_part_entry() << hist.first << hist.second
+ << " (" << (hist.second * 100 / total_part_ids)
+ << "%)" << std::endl;
+ }
+ }
+ }
+
+ // Build the 2D histogram of resolutions.
+ std::vector<int> weight_histogram = ComputeWeightResolutionHistogram();
+ // Labels the weight resolution table.
+ std::cout << "Weight resolutions:" << std::endl;
+ const auto prepare_weight_entry = []() -> std::ostream& {
+ return std::cout << std::setw(6) << std::left << std::setfill(' ');
+ };
+ prepare_weight_entry() << "H W";
+ for (int resolution_x = 2; resolution_x < kResolutionBuckets;
+ ++resolution_x) {
+ prepare_weight_entry() << resolution_x;
+ }
+ std::cout << std::endl;
+
+ // Displays table; skips rows/cols {0, 1} since they will always be empty.
+ for (int resolution_y = 2; resolution_y < kResolutionBuckets;
+ ++resolution_y) {
+ prepare_weight_entry() << resolution_y;
+ for (int resolution_x = 2; resolution_x < kResolutionBuckets;
+ ++resolution_x) {
+ const int count =
+ weight_histogram[resolution_x + resolution_y * kResolutionBuckets];
+ prepare_weight_entry();
+ if (!count) {
+ std::cout << "*";
+ } else {
+ std::cout << count;
+ }
+ }
+ std::cout << std::endl;
+ }
+ }
+
+ size_t NumBlocks() const { return blocks_.size(); }
+
+ private:
+ std::vector<std::unique_ptr<Stat>> stats_;
+ std::vector<IntermediateBlockData> blocks_;
+};
+
+std::ostream& operator<<(std::ostream& stream, const RgbaColor& color) {
+ stream << "{";
+ constexpr int kNumChannels = std::tuple_size<RgbaColor>::value;
+ for (int i = 0; i < kNumChannels; ++i) {
+ stream << color[i];
+ if (i < (kNumChannels - 1)) {
+ stream << ", ";
+ }
+ }
+ return stream << "}";
+}
+
+void PrintStatsForBlock(const PhysicalASTCBlock& pb,
+ astc_codec::Footprint footprint) {
+ const auto print_void_extent = [&pb](const VoidExtentData& void_extent_data) {
+ std::cout << "Void extent block:" << std::endl;
+ std::cout << " 16-bit RGBA: {"
+ << void_extent_data.r << ", "
+ << void_extent_data.g << ", "
+ << void_extent_data.b << ", "
+ << void_extent_data.a << "}" << std::endl;
+ if (pb.VoidExtentCoords()) {
+ std::cout << " Extent (S): {"
+ << void_extent_data.coords[0] << ", "
+ << void_extent_data.coords[1] << "}" << std::endl;
+ std::cout << " Extent (T): {"
+ << void_extent_data.coords[2] << ", "
+ << void_extent_data.coords[3] << "}" << std::endl;
+ } else {
+ std::cout << " No valid extent data" << std::endl;
+ }
+ };
+
+ const auto print_endpoint_data =
+ [](ColorEndpointMode mode, int endpoint_range,
+ const std::vector<int>& encoded_vals) {
+ std::cout << " Endpoint mode: "
+ << kModeStrings[static_cast<int>(mode)] << std::endl;
+ std::cout << " Uses blue-contract mode: "
+ << (UsesBlueContract(endpoint_range, mode, encoded_vals)
+ ? "true" : "false")
+ << std::endl;
+
+ RgbaColor endpoint_low, endpoint_high;
+ DecodeColorsForMode(encoded_vals, endpoint_range, mode,
+ &endpoint_low, &endpoint_high);
+
+ std::cout << " Low endpoint: " << endpoint_low << std::endl;
+ std::cout << " High endpoint: " << endpoint_high << std::endl;
+ };
+
+ const auto print_color_data =
+ [&print_endpoint_data, &footprint](const IntermediateBlockData& ib_data) {
+ const int endpoint_range = ib_data.endpoint_range.value();
+ std::cout << "Endpoint range: " << endpoint_range << std::endl;
+
+ const int num_parts = ib_data.endpoints.size();
+ if (ib_data.partition_id.hasValue()) {
+ const int part_id = ib_data.partition_id.value();
+ std::cout << "Parititon ID: " << part_id << std::endl;
+
+ const auto part = GetASTCPartition(footprint, num_parts, part_id);
+ assert(part.assignment.size() == footprint.Height() * footprint.Width());
+
+ std::cout << "Assignment:" << std::endl;
+ for (int y = 0; y < footprint.Height(); ++y) {
+ std::cout << " ";
+ for (int x = 0; x < footprint.Width(); ++x) {
+ const int texel_index = y * footprint.Width() + x;
+ std::cout << " " << part.assignment[texel_index];
+ }
+ std::cout << std::endl;
+ }
+ } else {
+ std::cout << "Single partition" << std::endl;
+ }
+
+ int endpoint_index = 0;
+ for (const auto& ep_data : ib_data.endpoints) {
+ if (num_parts == 1) {
+ std::cout << "Endpoints:" << std::endl;
+ } else {
+ std::cout << "Endpoint " << (endpoint_index++) << ": " << std::endl;
+ }
+ print_endpoint_data(ep_data.mode, endpoint_range, ep_data.colors);
+ }
+
+ if (ib_data.dual_plane_channel) {
+ std::cout << "Dual plane channel: "
+ << ib_data.dual_plane_channel.value() << std::endl;
+ } else {
+ std::cout << "Single plane" << std::endl;
+ }
+ };
+
+ const auto print_weight_data =
+ [&footprint](const IntermediateBlockData& ib_data) {
+ std::cout << "Weight grid dimensions: "
+ << ib_data.weight_grid_dim_x << "x" << ib_data.weight_grid_dim_y
+ << std::endl;
+ std::cout << "Weight range: " << ib_data.weight_range << std::endl;
+
+ std::cout << "Encoded weight grid: " << std::endl;
+ int weight_idx = 0;
+ for (int j = 0; j < ib_data.weight_grid_dim_y; ++j) {
+ std::cout << " ";
+ for (int i = 0; i < ib_data.weight_grid_dim_x; ++i) {
+ std::cout << std::setw(3) << std::left << std::setfill(' ')
+ << ib_data.weights[weight_idx++];
+ }
+ std::cout << std::endl;
+ }
+
+ std::cout << "Actual weight grid: " << std::endl;
+ std::vector<int> actual_weights = ib_data.weights;
+ for (auto& weight : actual_weights) {
+ weight = astc_codec::UnquantizeWeightFromRange(
+ weight, ib_data.weight_range);
+ }
+
+ actual_weights = astc_codec::InfillWeights(
+ actual_weights, footprint, ib_data.weight_grid_dim_x,
+ ib_data.weight_grid_dim_y);
+
+ weight_idx = 0;
+ for (int j = 0; j < footprint.Height(); ++j) {
+ std::cout << " ";
+ for (int i = 0; i < footprint.Width(); ++i) {
+ std::cout << std::setw(3) << std::left << std::setfill(' ')
+ << actual_weights[weight_idx++];
+ }
+ std::cout << std::endl;
+ }
+ };
+
+ if (pb.IsVoidExtent()) {
+ Optional<VoidExtentData> ve = astc_codec::UnpackVoidExtent(pb);
+ if (!ve) {
+ std::cerr << "ERROR: Failed to unpack void extent block." << std::endl;
+ } else {
+ print_void_extent(ve.value());
+ }
+ } else {
+ Optional<IntermediateBlockData> ib =
+ astc_codec::UnpackIntermediateBlock(pb);
+ if (!ib) {
+ std::cerr << "ERROR: Failed to unpack intermediate block." << std::endl;
+ } else {
+ const auto& ib_data = ib.value();
+ print_color_data(ib_data);
+ print_weight_data(ib_data);
+ }
+ }
+}
+
+} // namespace
+
+int main(int argc, char* argv[]) {
+ bool error = false;
+
+ std::string filename;
+ size_t block_index = 0;
+ bool has_block_index = false;
+
+ if (argc >= 2) {
+ filename = argv[1];
+
+ if (argc == 3) {
+ int32_t param = astc_codec::base::ParseInt32(argv[2], -1);
+ if (param < 0) {
+ std::cerr << "ERROR: Invalid block index." << std::endl;
+ error = true;
+ } else {
+ block_index = static_cast<size_t>(param);
+ has_block_index = true;
+ }
+ } else if (argc != 2) {
+ std::cerr << "ERROR: Too many parameters." << std::endl;
+ error = true;
+ }
+ } else {
+ error = true;
+ }
+
+ if (error) {
+ std::cout << ((argc >= 0) ? argv[0] : "astc_inspector_cli")
+ << " <filename> [<block index>]" << std::endl
+ << std::endl
+ << "Collects the various statistics of a stream of ASTC data "
+ << "stored in an ASTC file." << std::endl
+ << std::endl
+ << " filename ASTC file path." << std::endl
+ << " block index If specified, show detailed information about a block"
+ << std::endl;
+ return 1;
+ }
+
+ std::string error_string;
+ std::unique_ptr<ASTCFile> astc_file = ASTCFile::LoadFile(argv[1], &error_string);
+ if (!astc_file) {
+ std::cerr << "ERROR: " << error_string << std::endl;
+ return 2;
+ }
+
+ if (has_block_index) {
+ Optional<astc_codec::Footprint> footprint =
+ astc_codec::Footprint::Parse(astc_file->GetFootprintString().c_str());
+ if (!footprint) {
+ std::cerr << "ERROR: Invalid footprint \"" << astc_file->GetFootprintString() << "\"" << std::endl;
+ return 3;
+ }
+
+ PrintStatsForBlock(astc_file->GetBlock(block_index), footprint.value());
+ } else {
+ std::cout << "Dimensions: " << astc_file->GetWidth() << "x"
+ << astc_file->GetHeight() << ", depth " << astc_file->GetDepth()
+ << std::endl;
+
+ ASTCFileStats stats(astc_file);
+
+ std::cout << std::endl
+ << "Total bits used: " << 128 * astc_file->NumBlocks()
+ << " (" << astc_file->NumBlocks() << " blocks, "
+ << (astc_file->NumBlocks() * 16) << " bytes)"
+ << std::endl << std::endl;
+
+ stats.PrintStats();
+ }
+
+ return 0;
+}
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/types.h b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/types.h
new file mode 100644
index 00000000000..728d5adfa1a
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/types.h
@@ -0,0 +1,74 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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.
+
+#ifndef ASTC_CODEC_DECODER_ASTC_TYPES_H_
+#define ASTC_CODEC_DECODER_ASTC_TYPES_H_
+
+#include <array>
+#include <string>
+#include <utility>
+
+namespace astc_codec {
+
+// The color endpoint mode determines how the values encoded in the ASTC block
+// are interpreted in order to create the RGBA values for the given endpoint
+// pair. The order of this enum is required to match the ASTC specification in
+// Section C.2.14.
+enum class ColorEndpointMode {
+ kLDRLumaDirect = 0,
+ kLDRLumaBaseOffset,
+ kHDRLumaLargeRange,
+ kHDRLumaSmallRange,
+ kLDRLumaAlphaDirect,
+ kLDRLumaAlphaBaseOffset,
+ kLDRRGBBaseScale,
+ kHDRRGBBaseScale,
+ kLDRRGBDirect,
+ kLDRRGBBaseOffset,
+ kLDRRGBBaseScaleTwoA,
+ kHDRRGBDirect,
+ kLDRRGBADirect,
+ kLDRRGBABaseOffset,
+ kHDRRGBDirectLDRAlpha,
+ kHDRRGBDirectHDRAlpha,
+
+ // The total number of color endpoints defined by the ASTC specification.
+ // This isn't a specific endpoint mode and its sole purpose is to be used
+ // as a constant number.
+ kNumColorEndpointModes
+};
+
+// Returns the class for the given mode as defined in Section C.2.11.
+constexpr int EndpointModeClass(ColorEndpointMode mode) {
+ return static_cast<int>(mode) / 4;
+}
+
+// Returns the number of encoded color values for the given endpoint mode. The
+// number of encoded color values and their range determines the size of the
+// color data in a physical ASTC block. This information is taken from
+// Section C.2.17 of the ASTC specification.
+constexpr int NumColorValuesForEndpointMode(ColorEndpointMode mode) {
+ return (EndpointModeClass(mode) + 1) * 2;
+}
+
+// We define a number of convenience types here that give more logical meaning
+// throughout the ASTC utilities.
+using RgbColor = std::array<int, 3>;
+using RgbaColor = std::array<int, 4>;
+using Endpoint = RgbaColor;
+using EndpointPair = std::pair<Endpoint, Endpoint>;
+
+} // namespace astc_codec
+
+#endif // ASTC_CODEC_DECODER_ASTC_TYPES_H_
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/weight_infill.cc b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/weight_infill.cc
new file mode 100644
index 00000000000..62909aa6967
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/weight_infill.cc
@@ -0,0 +1,122 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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 "src/decoder/weight_infill.h"
+#include "src/decoder/integer_sequence_codec.h"
+
+#include <array>
+#include <cmath>
+#include <utility>
+
+namespace astc_codec {
+
+namespace {
+
+// The following functions are based on Section C.2.18 of the ASTC specification
+int GetScaleFactorD(int block_dim) {
+ return static_cast<int>((1024.f + static_cast<float>(block_dim >> 1)) /
+ static_cast<float>(block_dim - 1));
+}
+
+std::pair<int, int> GetGridSpaceCoordinates(
+ Footprint footprint, int s, int t, int weight_dim_x, int weight_dim_y) {
+ const int ds = GetScaleFactorD(footprint.Width());
+ const int dt = GetScaleFactorD(footprint.Height());
+
+ const int cs = ds * s;
+ const int ct = dt * t;
+
+ const int gs = (cs * (weight_dim_x - 1) + 32) >> 6;
+ const int gt = (ct * (weight_dim_y - 1) + 32) >> 6;
+
+ assert(gt < 1 << 8);
+ assert(gs < 1 << 8);
+
+ return std::make_pair(gs, gt);
+}
+
+// Returns the weight-grid values that are to be used for bilinearly
+// interpolating the weight to its final value. If the returned value
+// is equal to weight_dim_x * weight_dim_y, it may be ignored.
+std::array<int, 4> BilerpGridPointsForWeight(
+ const std::pair<int, int>& grid_space_coords, int weight_dim_x) {
+ const int js = grid_space_coords.first >> 4;
+ const int jt = grid_space_coords.second >> 4;
+
+ std::array<int, 4> result;
+ result[0] = js + weight_dim_x * jt;
+ result[1] = js + weight_dim_x * jt + 1;
+ result[2] = js + weight_dim_x * (jt + 1);
+ result[3] = js + weight_dim_x * (jt + 1) + 1;
+
+ return result;
+}
+
+std::array<int, 4> BilerpGridPointFactorsForWeight(
+ const std::pair<int, int>& grid_space_coords) {
+ const int fs = grid_space_coords.first & 0xF;
+ const int ft = grid_space_coords.second & 0xF;
+
+ std::array<int, 4> result;
+ result[3] = (fs * ft + 8) >> 4;
+ result[2] = ft - result[3];
+ result[1] = fs - result[3];
+ result[0] = 16 - fs - ft + result[3];
+
+ assert(result[0] <= 16);
+ assert(result[1] <= 16);
+ assert(result[2] <= 16);
+ assert(result[3] <= 16);
+
+ return result;
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+
+int CountBitsForWeights(int weight_dim_x, int weight_dim_y,
+ int target_weight_range) {
+ int num_weights = weight_dim_x * weight_dim_y;
+ return IntegerSequenceCodec::
+ GetBitCountForRange(num_weights, target_weight_range);
+}
+
+std::vector<int> InfillWeights(const std::vector<int>& weights,
+ Footprint footprint, int dim_x, int dim_y) {
+ std::vector<int> result;
+ result.reserve(footprint.NumPixels());
+ for (int t = 0; t < footprint.Height(); ++t) {
+ for (int s = 0; s < footprint.Width(); ++s) {
+ const auto grid_space_coords =
+ GetGridSpaceCoordinates(footprint, s, t, dim_x, dim_y);
+ const auto grid_pts =
+ BilerpGridPointsForWeight(grid_space_coords, dim_x);
+ const auto grid_factors =
+ BilerpGridPointFactorsForWeight(grid_space_coords);
+
+ int weight = 0;
+ for (int i = 0; i < 4; ++i) {
+ if (grid_pts[i] < dim_x * dim_y) {
+ weight += weights.at(grid_pts[i]) * grid_factors[i];
+ }
+ }
+ result.push_back((weight + 8) >> 4);
+ }
+ }
+
+ return result;
+}
+
+} // namespace astc_codec
diff --git a/3rdparty/bimg/3rdparty/astc-codec/src/decoder/weight_infill.h b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/weight_infill.h
new file mode 100644
index 00000000000..4a09d358bc4
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/src/decoder/weight_infill.h
@@ -0,0 +1,38 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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.
+
+#ifndef ASTC_CODEC_DECODER_WEIGHT_INFILL_H_
+#define ASTC_CODEC_DECODER_WEIGHT_INFILL_H_
+
+#include "src/decoder/footprint.h"
+
+#include <vector>
+
+namespace astc_codec {
+
+// Returns the number of bits used to represent the weight grid at the target
+// dimensions and weight range.
+int CountBitsForWeights(int weight_dim_x, int weight_dim_y,
+ int target_weight_range);
+
+// Performs weight infill of a grid of weights of size |dim_x * dim_y|. The
+// weights are fit using the algorithm laid out in Section C.2.18 of the ASTC
+// specification. Weights are expected to be passed unquantized and the returned
+// grid will be unquantized as well (i.e. each weight within the range [0, 64]).
+std::vector<int> InfillWeights(const std::vector<int>& weights,
+ Footprint footprint, int dim_x, int dim_y);
+
+} // namespace astc_codec
+
+#endif // ASTC_CODEC_DECODER_WEIGHT_INFILL_H_
diff --git a/3rdparty/bimg/3rdparty/astc-codec/third_party/BUILD b/3rdparty/bimg/3rdparty/astc-codec/third_party/BUILD
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/third_party/BUILD
diff --git a/3rdparty/bimg/3rdparty/astc-codec/third_party/honggfuzz.BUILD b/3rdparty/bimg/3rdparty/astc-codec/third_party/honggfuzz.BUILD
new file mode 100644
index 00000000000..07d9042e732
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/third_party/honggfuzz.BUILD
@@ -0,0 +1,39 @@
+config_setting(
+ name = "opt",
+ values = {"compilation_mode": "opt"}
+)
+
+cc_library(
+ name = "honggfuzz",
+ srcs = glob([
+ "libhfuzz/*.c",
+ "libhfcommon/*.c",
+ ],
+ exclude = ["libhfuzz/linux.c"],
+ ) + select({
+ "@bazel_tools//src/conditions:darwin_x86_64": [],
+ "@bazel_tools//src/conditions:darwin": [],
+ "//conditions:default": ["libhfuzz/linux.c"],
+ }),
+ hdrs = glob([
+ "libhfuzz/*.h",
+ "libhfcommon/*.h",
+ "honggfuzz.h",
+ ]),
+ copts = [
+ "-std=c11",
+ ],
+ defines = select({
+ "@bazel_tools//src/conditions:darwin_x86_64": ["_HF_ARCH_DARWIN"],
+ "@bazel_tools//src/conditions:darwin": ["_HF_ARCH_DARWIN"],
+ "//conditions:default": ["_HF_ARCH_LINUX"],
+ }) + select({
+ ":opt": [],
+ "//conditions:default": ["DEBUG=DEBUG"],
+ }) + [
+ "_GNU_SOURCE",
+ ],
+ includes = ["."],
+ visibility = ["//visibility:public"],
+ linkstatic = 1
+)
diff --git a/3rdparty/bimg/3rdparty/astc-codec/tools/build-ci/linux/build.sh b/3rdparty/bimg/3rdparty/astc-codec/tools/build-ci/linux/build.sh
new file mode 100755
index 00000000000..9f088061f22
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/tools/build-ci/linux/build.sh
@@ -0,0 +1,76 @@
+#!/bin/bash
+# Configures builds for our CI environment.
+
+# Print commands and exit on error.
+set -ex
+
+if [ "$KOKORO_BUILD_ID" ]; then
+ echo "Running job $KOKORO_JOB_NAME"
+ TARGET=`echo "$KOKORO_JOB_NAME" | awk -F "/" '{print $NF}'`
+fi
+
+if [ ! "$TARGET" ]; then
+ if [ "$1" ]; then
+ TARGET=$1
+ else
+ TARGET=release
+ fi
+fi
+
+echo "Building $TARGET target"
+
+pushd `dirname $0`/../../.. > /dev/null
+
+BUILD_RELEASE=
+BUILD_DEBUG=
+BUILD_CMAKE=
+RUN_TESTS=
+
+if [ "$TARGET" == "presubmit" ]; then
+ BUILD_DEBUG=1
+ BUILD_RELEASE=1
+ BUILD_CMAKE=1
+ RUN_TESTS=1
+fi
+
+if [ "$TARGET" == "debug" ]; then
+ BUILD_DEBUG=1
+fi
+
+if [ "$TARGET" == "release" ]; then
+ BUILD_RELEASE=1
+fi
+
+if [ "$TARGET" == "continuous" ]; then
+ BUILD_DEBUG=1
+ BUILD_RELEASE=1
+ BUILD_CMAKE=1
+ RUN_TESTS=1
+fi
+
+if [ "$BUILD_DEBUG" == "1" ]; then
+ echo "Starting debug build"
+ bazel build -c dbg //...
+
+ if [ "$RUN_TESTS" == "1" ]; then
+ bazel test -c dbg //...
+ fi
+fi
+
+if [ "$BUILD_RELEASE" == "1" ]; then
+ echo "Starting release build"
+ bazel build -c opt //...
+
+ if [ "$RUN_TESTS" == "1" ]; then
+ bazel test -c opt //...
+ fi
+fi
+
+if [ "$BUILD_CMAKE" == "1" ]; then
+ echo "Starting cmake build"
+ mkdir build
+ pushd build
+ cmake ..
+ make -j$((`nproc`+1))
+ popd
+fi
diff --git a/3rdparty/bimg/3rdparty/astc-codec/tools/build-ci/linux/continuous.cfg b/3rdparty/bimg/3rdparty/astc-codec/tools/build-ci/linux/continuous.cfg
new file mode 100644
index 00000000000..cde6840be68
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/tools/build-ci/linux/continuous.cfg
@@ -0,0 +1,3 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+build_file: "astc-codec/tools/build-ci/linux/build.sh"
diff --git a/3rdparty/bimg/3rdparty/astc-codec/tools/build-ci/linux/presubmit.cfg b/3rdparty/bimg/3rdparty/astc-codec/tools/build-ci/linux/presubmit.cfg
new file mode 100644
index 00000000000..cde6840be68
--- /dev/null
+++ b/3rdparty/bimg/3rdparty/astc-codec/tools/build-ci/linux/presubmit.cfg
@@ -0,0 +1,3 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+build_file: "astc-codec/tools/build-ci/linux/build.sh"
diff --git a/3rdparty/bimg/3rdparty/astc/astc_lib.cpp b/3rdparty/bimg/3rdparty/astc/astc_lib.cpp
index f587236e54d..e75ba64a086 100644
--- a/3rdparty/bimg/3rdparty/astc/astc_lib.cpp
+++ b/3rdparty/bimg/3rdparty/astc/astc_lib.cpp
@@ -52,6 +52,8 @@ namespace
{
{ 0, 1, 2, 3 }, // ASTC_RGBA
{ 2, 1, 0, 3 }, // ASTC_BGRA
+ { 0, 0, 0, 1 }, // ASTC_ENC_NORMAL_RA
+ { 0, 3, 6, 5 }, // ASTC_DEC_RA_NORMAL
};
void alloc_temp_buffers(compress_symbolic_block_buffers* temp_buffers)
@@ -272,197 +274,64 @@ namespace
void setup_ewp(ASTC_COMPRESS_MODE mode, int ydim, int xdim, error_weighting_params& ewp)
{
- float oplimit_autoset = 0.0;
- float dblimit_autoset_2d = 0.0;
- float bmc_autoset = 0.0;
- float mincorrel_autoset = 0.0;
+ float oplimit = 0.0;
+ float dblimit_2d = 0.0;
+ float bmc = 0.0;
+ float mincorrel = 0.0;
- int plimit_autoset = -1;
- int maxiters_autoset = 0;
- int pcdiv = 1;
+ int plimit = -1;
+ int maxiters = 0;
float log10_texels_2d = log((float)(xdim * ydim)) / log(10.0f);
if (mode == ASTC_COMPRESS_VERY_FAST)
{
- plimit_autoset = 2;
- oplimit_autoset = 1.0;
- dblimit_autoset_2d = MAX(70 - 35 * log10_texels_2d, 53 - 19 * log10_texels_2d);
- bmc_autoset = 25;
- mincorrel_autoset = 0.5;
- maxiters_autoset = 1;
-
- switch (ydim)
- {
- case 4:
- pcdiv = 240;
- break;
- case 5:
- pcdiv = 56;
- break;
- case 6:
- pcdiv = 64;
- break;
- case 8:
- pcdiv = 47;
- break;
- case 10:
- pcdiv = 36;
- break;
- case 12:
- pcdiv = 30;
- break;
- default:
- pcdiv = 30;
- break;
- }
+ plimit = 2;
+ oplimit = 1.0;
+ dblimit_2d = MAX(70 - 35 * log10_texels_2d, 53 - 19 * log10_texels_2d);
+ bmc = 25;
+ mincorrel = 0.5;
+ maxiters = 1;
}
else if (mode == ASTC_COMPRESS_FAST)
{
- plimit_autoset = 4;
- oplimit_autoset = 1.0;
- mincorrel_autoset = 0.5;
- dblimit_autoset_2d = MAX(85 - 35 * log10_texels_2d, 63 - 19 * log10_texels_2d);
- bmc_autoset = 50;
- maxiters_autoset = 1;
-
- switch (ydim)
- {
- case 4:
- pcdiv = 60;
- break;
- case 5:
- pcdiv = 27;
- break;
- case 6:
- pcdiv = 30;
- break;
- case 8:
- pcdiv = 24;
- break;
- case 10:
- pcdiv = 16;
- break;
- case 12:
- pcdiv = 20;
- break;
- default:
- pcdiv = 20;
- break;
- };
+ plimit = 4;
+ oplimit = 1.0;
+ mincorrel = 0.5;
+ dblimit_2d = MAX(85 - 35 * log10_texels_2d, 63 - 19 * log10_texels_2d);
+ bmc = 50;
+ maxiters = 1;
}
else if (mode == ASTC_COMPRESS_MEDIUM)
{
- plimit_autoset = 25;
- oplimit_autoset = 1.2f;
- mincorrel_autoset = 0.75f;
- dblimit_autoset_2d = MAX(95 - 35 * log10_texels_2d, 70 - 19 * log10_texels_2d);
- bmc_autoset = 75;
- maxiters_autoset = 2;
-
- switch (ydim)
- {
- case 4:
- pcdiv = 25;
- break;
- case 5:
- pcdiv = 15;
- break;
- case 6:
- pcdiv = 15;
- break;
- case 8:
- pcdiv = 10;
- break;
- case 10:
- pcdiv = 8;
- break;
- case 12:
- pcdiv = 6;
- break;
- default:
- pcdiv = 6;
- break;
- };
+ plimit = 25;
+ oplimit = 1.2f;
+ mincorrel = 0.75f;
+ dblimit_2d = MAX(95 - 35 * log10_texels_2d, 70 - 19 * log10_texels_2d);
+ bmc = 75;
+ maxiters = 2;
}
else if (mode == ASTC_COMPRESS_THOROUGH)
{
- plimit_autoset = 100;
- oplimit_autoset = 2.5f;
- mincorrel_autoset = 0.95f;
- dblimit_autoset_2d = MAX(105 - 35 * log10_texels_2d, 77 - 19 * log10_texels_2d);
- bmc_autoset = 95;
- maxiters_autoset = 4;
-
- switch (ydim)
- {
- case 4:
- pcdiv = 12;
- break;
- case 5:
- pcdiv = 7;
- break;
- case 6:
- pcdiv = 7;
- break;
- case 8:
- pcdiv = 5;
- break;
- case 10:
- pcdiv = 4;
- break;
- case 12:
- pcdiv = 3;
- break;
- default:
- pcdiv = 3;
- break;
- };
+ plimit = 100;
+ oplimit = 2.5f;
+ mincorrel = 0.95f;
+ dblimit_2d = MAX(105 - 35 * log10_texels_2d, 77 - 19 * log10_texels_2d);
+ bmc = 95;
+ maxiters = 4;
}
else if (mode == ASTC_COMPRESS_EXHAUSTIVE)
{
- plimit_autoset = PARTITION_COUNT;
- oplimit_autoset = 1000.0f;
- mincorrel_autoset = 0.99f;
- dblimit_autoset_2d = 999.0f;
- bmc_autoset = 100;
- maxiters_autoset = 4;
-
- switch (ydim)
- {
- case 4:
- pcdiv = 3;
- break;
- case 5:
- pcdiv = 1;
- break;
- case 6:
- pcdiv = 1;
- break;
- case 8:
- pcdiv = 1;
- break;
- case 10:
- pcdiv = 1;
- break;
- case 12:
- pcdiv = 1;
- break;
- default:
- pcdiv = 1;
- break;
- }
+ plimit = PARTITION_COUNT;
+ oplimit = 1000.0f;
+ mincorrel = 0.99f;
+ dblimit_2d = 999.0f;
+ bmc = 100;
+ maxiters = 4;
}
- int partitions_to_test = plimit_autoset;
- float dblimit_2d = dblimit_autoset_2d;
- float oplimit = oplimit_autoset;
- float mincorrel = mincorrel_autoset;
-
- int maxiters = maxiters_autoset;
ewp.max_refinement_iters = maxiters;
-
- ewp.block_mode_cutoff = bmc_autoset / 100.0f;
+ ewp.block_mode_cutoff = bmc / 100.0f;
float texel_avg_error_limit_2d;
@@ -477,6 +346,7 @@ namespace
ewp.partition_1_to_2_limit = oplimit;
ewp.lowest_correlation_cutoff = mincorrel;
+ int partitions_to_test = plimit;
if (partitions_to_test < 1)
partitions_to_test = 1;
else if (partitions_to_test > PARTITION_COUNT)
@@ -487,6 +357,18 @@ namespace
expand_block_artifact_suppression(xdim, ydim, 1, &ewp);
}
+
+ void setup_ewp(ASTC_CHANNELS channels, error_weighting_params& ewp)
+ {
+ if (channels == ASTC_ENC_NORMAL_RA)
+ {
+ ewp.rgba_weights[0] = 1.0f;
+ ewp.rgba_weights[1] = 0.01f;
+ ewp.rgba_weights[2] = 0.01f;
+ ewp.rgba_weights[3] = 1.0f;
+ ewp.ra_normal_angular_scale = 1;
+ }
+ }
}
size_t astc_compressed_size(int w, int h, int bw, int bh)
@@ -517,6 +399,7 @@ void astc_compress
error_weighting_params ewp;
init_ewp(ewp);
setup_ewp(compress_mode, block_width, block_height, ewp);
+ setup_ewp(src_channels, ewp); // For special-case encoding of some channel types such as normals
if (src_stride == 0)
src_stride = src_width * 4;
diff --git a/3rdparty/bimg/3rdparty/astc/astc_lib.h b/3rdparty/bimg/3rdparty/astc/astc_lib.h
index 373f33b82ea..527e00a083d 100644
--- a/3rdparty/bimg/3rdparty/astc/astc_lib.h
+++ b/3rdparty/bimg/3rdparty/astc/astc_lib.h
@@ -30,8 +30,10 @@ enum ASTC_DECODE_MODE
enum ASTC_CHANNELS
{
- ASTC_RGBA,
- ASTC_BGRA
+ ASTC_RGBA, // Encode/decode into RGBA
+ ASTC_BGRA, // Encode/decode into BGRA
+ ASTC_ENC_NORMAL_RA, // Encode normal XYZ as XY in RA with reconstructed Z
+ ASTC_DEC_RA_NORMAL, // Decode RA into XY and reconstructed Z
};
diff --git a/3rdparty/bimg/3rdparty/astc/mathlib.cpp b/3rdparty/bimg/3rdparty/astc/mathlib.cpp
index aa6eac1ff5b..0384b1ecfc6 100644
--- a/3rdparty/bimg/3rdparty/astc/mathlib.cpp
+++ b/3rdparty/bimg/3rdparty/astc/mathlib.cpp
@@ -61,7 +61,7 @@ float nan(int p)
return v.q;
}
-#if !defined(_MSC_VER) && (__cplusplus < 201103L)
+#if !_MSC_VER && !__clang__ && (__cplusplus < 201103L)
float fmax(float p, float q)
{
if (p != p)
diff --git a/3rdparty/bimg/3rdparty/astc/mathlib.h b/3rdparty/bimg/3rdparty/astc/mathlib.h
index 377ea0e21f2..eed87fc2edf 100644
--- a/3rdparty/bimg/3rdparty/astc/mathlib.h
+++ b/3rdparty/bimg/3rdparty/astc/mathlib.h
@@ -27,7 +27,7 @@ float cospi(float p);
float nan(int p);
-#if !defined(_MSC_VER) && (__cplusplus < 201103L)
+#if !_MSC_VER && !__clang__ && (__cplusplus < 201103L)
float fmax(float p, float q);
float fmin(float p, float q);
#endif // C++11
diff --git a/3rdparty/bimg/3rdparty/maratis-tcl/LICENSE b/3rdparty/bimg/3rdparty/maratis-tcl/LICENSE
deleted file mode 100644
index d1832710e23..00000000000
--- a/3rdparty/bimg/3rdparty/maratis-tcl/LICENSE
+++ /dev/null
@@ -1,22 +0,0 @@
-Maratis Tiny C library
-
-Copyright (c) 2015 Anael Seghezzi <www.maratis3d.com>
-
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any damages
- arising from the use of this software.
-
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely, subject to the following restrictions:
-
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would
- be appreciated but is not required.
-
- 2. Altered source versions must be plainly marked as such, and must not
- be misrepresented as being the original software.
-
- 3. This notice may not be removed or altered from any source
- distribution.
diff --git a/3rdparty/bimg/3rdparty/maratis-tcl/m_image.h b/3rdparty/bimg/3rdparty/maratis-tcl/m_image.h
deleted file mode 100644
index 9923482fcaa..00000000000
--- a/3rdparty/bimg/3rdparty/maratis-tcl/m_image.h
+++ /dev/null
@@ -1,2340 +0,0 @@
-/*======================================================================
- Maratis Tiny C Library
- version 1.0
-------------------------------------------------------------------------
- Copyright (c) 2015 Anael Seghezzi <www.maratis3d.org>
- Copyright (c) 2015 Marti Maria Saguer
-
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any damages
- arising from the use of this software.
-
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely, subject to the following restrictions:
-
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would
- be appreciated but is not required.
-
- 2. Altered source versions must be plainly marked as such, and must not
- be misrepresented as being the original software.
-
- 3. This notice may not be removed or altered from any source
- distribution.
-
-========================================================================*/
-/*
- Image manipulation :
- - transformation (re-frame, mirror, rotation)
- - conversions (float, half, ubyte, linear, greyscale...)
- - filtering (convolution, Gaussian blur, Harris)
- - scaling (pyramid, generic, bilinear)
- - morphology (flood-fill, dilate, erode, thinning)
- - edge and corner detection (Sobel, Harris)
-
- to create the implementation,
- #define M_IMAGE_IMPLEMENTATION
- in *one* C/CPP file that includes this file.
-
- optional:
- include after *m_math.h*
-
- //////////////////////////////////////////////////////
- Example: create a 256x256 float image with 1 component:
-
- struct m_image foo1 = M_IMAGE_IDENTITY();
- struct m_image foo2 = M_IMAGE_IDENTITY();
- int x, y;
-
- m_image_create(&foo1, M_FLOAT, 256, 256, 1);
- memset(foo1.data, 0, foo1.size * sizeof(float)); // clear to zero
-
- y = 128; x = 128;
- ((float *)foo1.data)[y * foo1.width + x] = 1.0f; // set (x, y) pixel to one
-
- m_image_gaussian_blur(&foo2, &foo1, 3, 3); // apply Gaussian blur
-
- m_image_destroy(&foo2);
- m_image_destroy(&foo1);
-*/
-
-#ifndef M_IMAGE_H
-#define M_IMAGE_H
-
-#include <stdint.h>
-
-#define M_IMAGE_VERSION 1
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef MIAPI
-#define MIAPI extern
-#endif
-
-#define M_VOID 0
-#define M_BOOL 1
-#define M_BYTE 2
-#define M_UBYTE 3
-#define M_SHORT 4
-#define M_USHORT 5
-#define M_INT 6
-#define M_UINT 7
-#define M_HALF 8
-#define M_FLOAT 9
-#define M_DOUBLE 10
-
-struct m_image
-{
- void *data;
- int size;
- int width;
- int height;
- int comp;
- char type;
-};
-
-/* identity, must be used before calling m_image_create */
-#define M_IMAGE_IDENTITY() {0, 0, 0, 0, 0, 0}
-
-/* fully supported types are: M_UBYTE, M_USHORT, M_HALF, M_FLOAT
- partially supported types: M_BYTE, M_SHORT, M_INT, M_UINT (no support for conversion) */
-MIAPI void m_image_create(struct m_image *image, char type, int width, int height, int comp);
-MIAPI void m_image_destroy(struct m_image *image);
-
-MIAPI void m_image_ubyte_to_float(struct m_image *dest, const struct m_image *src);
-MIAPI void m_image_ushort_to_float(struct m_image *dest, const struct m_image *src);
-MIAPI void m_image_half_to_float(struct m_image *dest, const struct m_image *src);
-MIAPI void m_image_float_to_ubyte(struct m_image *dest, const struct m_image *src);
-MIAPI void m_image_float_to_ushort(struct m_image *dest, const struct m_image *src);
-MIAPI void m_image_float_to_half(struct m_image *dest, const struct m_image *src);
-
-MIAPI void m_image_copy(struct m_image *dest, const struct m_image *src);
-MIAPI void m_image_copy_sub_image(struct m_image *dest, const struct m_image *src, int x, int y, int w, int h);
-MIAPI void m_image_reframe(struct m_image *dest, const struct m_image *src, int left, int top, int right, int bottom);
-MIAPI void m_image_extract_component(struct m_image *dest, const struct m_image *src, int c);
-MIAPI void m_image_rotate_left(struct m_image *dest, const struct m_image *src);
-MIAPI void m_image_rotate_right(struct m_image *dest, const struct m_image *src);
-MIAPI void m_image_rotate_180(struct m_image *dest, const struct m_image *src);
-MIAPI void m_image_mirror_x(struct m_image *dest, const struct m_image *src);
-MIAPI void m_image_mirror_y(struct m_image *dest, const struct m_image *src);
-
-MIAPI void m_image_premultiply(struct m_image *dest, const struct m_image *src);
-MIAPI void m_image_unpremultiply(struct m_image *dest, const struct m_image *src);
-
-/* float/half conversion */
-MIAPI float m_half2float(uint16_t h);
-MIAPI uint16_t m_float2half(float flt);
-
-/* raw processing */
-MIAPI void m_gaussian_kernel(float *dest, int size);
-MIAPI void m_sst(float *dest, const float *src, int count);
-MIAPI void m_harris_response(float *dest, const float *src, int count);
-MIAPI void m_tfm(float *dest, const float *src, int count);
-MIAPI void m_normalize(float *dest, const float *src, int size); /* dest = src / norm(src) */
-MIAPI void m_normalize_sum(float *dest, const float *src, int size); /* dest = src / sum(src) */
-MIAPI float m_mean(const float *src, int size);
-MIAPI float m_squared_distance(const float *src1, const float *src2, int size);
-MIAPI float m_convolution(const float *src1, const float *src2, int size); /* a dot product really */
-MIAPI float m_chi_squared_distance(const float *src1, const float *src2, int size); /* good at estimating signed hystograms difference */
-
-/* conversion to 1 component (float image only) */
-MIAPI void m_image_grey(struct m_image *dest, const struct m_image *src); /* from RGB src */
-MIAPI void m_image_max(struct m_image *dest, const struct m_image *src);
-MIAPI void m_image_max_abs(struct m_image *dest, const struct m_image *src);
-
-/* summed area table (also called "integral image") */
-MIAPI void m_image_summed_area(struct m_image *dest, const struct m_image *src);
-
-/* convolutions (float image only) */
-/* if alpha channel, src image must be pre-multiplied */
-MIAPI void m_image_convolution_h(struct m_image *dest, const struct m_image *src, float *kernel, int size); /* horizontal */
-MIAPI void m_image_convolution_v(struct m_image *dest, const struct m_image *src, float *kernel, int size); /* vertical */
-MIAPI void m_image_gaussian_blur(struct m_image *dest, const struct m_image *src, int dx, int dy);
-
-/* edge and corner (float 1 component image only) */
-MIAPI void m_image_sobel(struct m_image *dest, const struct m_image *src);
-MIAPI void m_image_harris(struct m_image *dest, const struct m_image *src, int radius);
-
-/* morphology (ubyte 1 component image only) */
-MIAPI int m_image_floodfill_4x(struct m_image *dest, int x, int y, unsigned char ref, unsigned char value, unsigned short *stack, int stack_size);
-MIAPI int m_image_floodfill_8x(struct m_image *dest, int x, int y, unsigned char ref, unsigned char value, unsigned short *stack, int stack_size);
-MIAPI void m_image_dilate(struct m_image *dest, const struct m_image *src);
-MIAPI void m_image_erode(struct m_image *dest, const struct m_image *src);
-MIAPI void m_image_edge_4x(struct m_image *dest, const struct m_image *src, unsigned char ref);
-MIAPI void m_image_thin(struct m_image *dest);
-
-/* non maxima suppression (float image only) */
-MIAPI void m_image_non_max_supp(struct m_image *dest, const struct m_image *src, int radius, float threshold);
-
-/* detect Harris corners
- margin: margin around the image to exclude corners
- radius: maxima radius
- threshold: Harris response threshold
- corners: corners coordinates of size max_count * 2
- max_count: maximum number of corners
- return corner count */
-MIAPI int m_image_corner_harris(const struct m_image *src, int margin, int radius, float threshold, int *corners, int max_count);
-
-/* resizing (float image only) */
-MIAPI void m_image_sub_pixel(const struct m_image *src, float x, float y, float *result);
-MIAPI void m_image_pyrdown(struct m_image *dest, const struct m_image *src);
-MIAPI void m_image_resize(struct m_image *dest, const struct m_image *src, int new_width, int new_height);
-
-#ifdef __cplusplus
-}
-#endif
-/*
-----------------------------------------------------------------------*/
-#endif /* M_IMAGE_H */
-
-#ifdef M_IMAGE_IMPLEMENTATION
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <memory.h>
-#include <math.h>
-#include <assert.h>
-
-#ifndef M_SAFE_FREE
-#define M_SAFE_FREE(p) {if (p) {free(p); (p) = NULL;}}
-#endif
-
-#ifndef M_MIN
-#define M_MIN(a, b) (((a) < (b)) ? (a) : (b))
-#endif
-#ifndef M_MAX
-#define M_MAX(a, b) (((a) > (b)) ? (a) : (b))
-#endif
-#ifndef M_ABS
-#define M_ABS(a) (((a) < 0) ? -(a) : (a))
-#endif
-#ifndef M_CLAMP
-#define M_CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
-#endif
-
-MIAPI void m_gaussian_kernel(float *dest, int size)
-{
- if(size == 3) {
- dest[0] = 0.25f;
- dest[1] = 0.50f;
- dest[2] = 0.25f;
- }
- else {
-
- float *k = dest;
- float sigma = 1.6f;
- float rs, s2;
- float sum = 0.0f;
- int radius = (size - 1) / 2;
- int r;
-
- s2 = 1.0f / expf(sigma * sigma * 2.25f);
- rs = sigma / (float)radius;
-
- /* compute gaussian kernel */
- for(r = -radius; r <= radius; r++) {
- float x = fabsf(r * rs);
- float v = (1.0f / expf(x * x)) - s2;
- *k = v;
- sum += v;
- k++;
- }
-
- /* normalize */
- if (sum > 0.0f) {
- float isum = 1.0f / sum;
- for (r = 0; r < size; r++)
- dest[r] *= isum;
- }
- }
-}
-
-MIAPI void m_sst(float *dest, const float *src, int count)
-{
- int i;
- for (i = 0; i < count; i++) {
- float dx = src[0];
- float dy = src[1];
- dest[0] = dx*dx;
- dest[1] = dy*dy;
- dest[2] = dx*dy;
- src += 2;
- dest += 3;
- }
-}
-
-MIAPI void m_harris_response(float *dest, const float *src, int count)
-{
- int i;
- for (i = 0; i < count; i++) {
- float dx2 = src[0];
- float dy2 = src[1];
- float dxy = src[2];
- *dest = (dx2 * dy2 - dxy * dxy) / (dx2 + dy2 + 1e-8f);
- src += 3;
- dest++;
- }
-}
-
-MIAPI void m_tfm(float *dest, const float *src, int count)
-{
- int i;
- for (i = 0; i < count; i++) {
-
- if (src[0] < src[1]) {
- float dx2 = src[0];
- float dy2 = src[1];
- float dxy = src[2];
- float lambda = 0.5f * (dy2 + dx2 + sqrtf((dy2 * dy2) - (2.0f * dx2 * dy2) + (dx2 * dx2) + (4.0f * dxy * dxy)));
- dest[0] = dx2 - lambda;
- dest[1] = dxy;
- }
- else {
- float dy2 = src[0];
- float dx2 = src[1];
- float dxy = src[2];
- float lambda = 0.5f * (dy2 + dx2 + sqrtf((dy2 * dy2) - (2.0f * dx2 * dy2) + (dx2 * dx2) + (4.0f * dxy * dxy)));
- dest[0] = dxy;
- dest[1] = dx2 - lambda;
- }
-
- src += 3;
- dest += 2;
- }
-}
-
-MIAPI float m_chi_squared_distance(const float *src1, const float *src2, int size)
-{
- int i;
- float score = 0;
- for (i = 0; i < size; i++) {
-
- float val1 = src1[i];
- float val2 = src2[i];
-
- /* chi squared distance */
- if ((val1 + val2) > 0) {
- float x = val2 - val1;
- score += (x * x) / (val1 + val2);
- }
- }
-
- return score * 0.5f;
-}
-
-MIAPI float m_convolution(const float *src1, const float *src2, int size)
-{
- float c = 0; int i;
- for (i = 0; i < size; i++)
- c += src1[i] * src2[i];
- return c;
-}
-
-MIAPI void m_normalize(float *dest, const float *src, int size)
-{
- float sum = 0.0f; int i;
- for(i = 0; i < size; i++)
- sum += src[i] * src[i];
-
- if (sum > 0.0f) {
- sum = 1.0f / sqrtf(sum);
- for(i = 0; i < size; i++)
- dest[i] = src[i] * sum;
- }
- else {
- memset(dest, 0, size * sizeof(float));
- }
-}
-
-MIAPI void m_normalize_sum(float *dest, const float *src, int size)
-{
- float sum = 0.0f; int i;
- for(i = 0; i < size; i++)
- sum += src[i];
-
- if (sum > 0.0f) {
- sum = 1.0f / sum;
- for(i = 0; i < size; i++)
- dest[i] = src[i] * sum;
- }
- else {
- memset(dest, 0, size * sizeof(float));
- }
-}
-
-MIAPI float m_mean(const float *src, int size)
-{
- float mean = 0; int i;
- for (i = 0; i < size; i++)
- mean += (*src++);
- return size > 0 ? mean / (float)size : 0;
-}
-
-MIAPI float m_squared_distance(const float *src1, const float *src2, int size)
-{
- float score = 0; int i;
- for (i = 0; i < size; i++) {
- float x = src2[i] - src1[i];
- score += x * x;
- }
- return score;
-}
-
-/* m_half2float / m_float2half :
- a big thanks to Marti Maria Saguer for allowing the use of this code
- under the zlib license from "Little Color Management System" (cmshalf.c) */
-
-/* This code is inspired in the paper "Fast Half Float Conversions"
- by Jeroen van der Zijp */
-
-static uint32_t m__mantissa[2048] = {
-0x00000000, 0x33800000, 0x34000000, 0x34400000, 0x34800000, 0x34a00000,
-0x34c00000, 0x34e00000, 0x35000000, 0x35100000, 0x35200000, 0x35300000,
-0x35400000, 0x35500000, 0x35600000, 0x35700000, 0x35800000, 0x35880000,
-0x35900000, 0x35980000, 0x35a00000, 0x35a80000, 0x35b00000, 0x35b80000,
-0x35c00000, 0x35c80000, 0x35d00000, 0x35d80000, 0x35e00000, 0x35e80000,
-0x35f00000, 0x35f80000, 0x36000000, 0x36040000, 0x36080000, 0x360c0000,
-0x36100000, 0x36140000, 0x36180000, 0x361c0000, 0x36200000, 0x36240000,
-0x36280000, 0x362c0000, 0x36300000, 0x36340000, 0x36380000, 0x363c0000,
-0x36400000, 0x36440000, 0x36480000, 0x364c0000, 0x36500000, 0x36540000,
-0x36580000, 0x365c0000, 0x36600000, 0x36640000, 0x36680000, 0x366c0000,
-0x36700000, 0x36740000, 0x36780000, 0x367c0000, 0x36800000, 0x36820000,
-0x36840000, 0x36860000, 0x36880000, 0x368a0000, 0x368c0000, 0x368e0000,
-0x36900000, 0x36920000, 0x36940000, 0x36960000, 0x36980000, 0x369a0000,
-0x369c0000, 0x369e0000, 0x36a00000, 0x36a20000, 0x36a40000, 0x36a60000,
-0x36a80000, 0x36aa0000, 0x36ac0000, 0x36ae0000, 0x36b00000, 0x36b20000,
-0x36b40000, 0x36b60000, 0x36b80000, 0x36ba0000, 0x36bc0000, 0x36be0000,
-0x36c00000, 0x36c20000, 0x36c40000, 0x36c60000, 0x36c80000, 0x36ca0000,
-0x36cc0000, 0x36ce0000, 0x36d00000, 0x36d20000, 0x36d40000, 0x36d60000,
-0x36d80000, 0x36da0000, 0x36dc0000, 0x36de0000, 0x36e00000, 0x36e20000,
-0x36e40000, 0x36e60000, 0x36e80000, 0x36ea0000, 0x36ec0000, 0x36ee0000,
-0x36f00000, 0x36f20000, 0x36f40000, 0x36f60000, 0x36f80000, 0x36fa0000,
-0x36fc0000, 0x36fe0000, 0x37000000, 0x37010000, 0x37020000, 0x37030000,
-0x37040000, 0x37050000, 0x37060000, 0x37070000, 0x37080000, 0x37090000,
-0x370a0000, 0x370b0000, 0x370c0000, 0x370d0000, 0x370e0000, 0x370f0000,
-0x37100000, 0x37110000, 0x37120000, 0x37130000, 0x37140000, 0x37150000,
-0x37160000, 0x37170000, 0x37180000, 0x37190000, 0x371a0000, 0x371b0000,
-0x371c0000, 0x371d0000, 0x371e0000, 0x371f0000, 0x37200000, 0x37210000,
-0x37220000, 0x37230000, 0x37240000, 0x37250000, 0x37260000, 0x37270000,
-0x37280000, 0x37290000, 0x372a0000, 0x372b0000, 0x372c0000, 0x372d0000,
-0x372e0000, 0x372f0000, 0x37300000, 0x37310000, 0x37320000, 0x37330000,
-0x37340000, 0x37350000, 0x37360000, 0x37370000, 0x37380000, 0x37390000,
-0x373a0000, 0x373b0000, 0x373c0000, 0x373d0000, 0x373e0000, 0x373f0000,
-0x37400000, 0x37410000, 0x37420000, 0x37430000, 0x37440000, 0x37450000,
-0x37460000, 0x37470000, 0x37480000, 0x37490000, 0x374a0000, 0x374b0000,
-0x374c0000, 0x374d0000, 0x374e0000, 0x374f0000, 0x37500000, 0x37510000,
-0x37520000, 0x37530000, 0x37540000, 0x37550000, 0x37560000, 0x37570000,
-0x37580000, 0x37590000, 0x375a0000, 0x375b0000, 0x375c0000, 0x375d0000,
-0x375e0000, 0x375f0000, 0x37600000, 0x37610000, 0x37620000, 0x37630000,
-0x37640000, 0x37650000, 0x37660000, 0x37670000, 0x37680000, 0x37690000,
-0x376a0000, 0x376b0000, 0x376c0000, 0x376d0000, 0x376e0000, 0x376f0000,
-0x37700000, 0x37710000, 0x37720000, 0x37730000, 0x37740000, 0x37750000,
-0x37760000, 0x37770000, 0x37780000, 0x37790000, 0x377a0000, 0x377b0000,
-0x377c0000, 0x377d0000, 0x377e0000, 0x377f0000, 0x37800000, 0x37808000,
-0x37810000, 0x37818000, 0x37820000, 0x37828000, 0x37830000, 0x37838000,
-0x37840000, 0x37848000, 0x37850000, 0x37858000, 0x37860000, 0x37868000,
-0x37870000, 0x37878000, 0x37880000, 0x37888000, 0x37890000, 0x37898000,
-0x378a0000, 0x378a8000, 0x378b0000, 0x378b8000, 0x378c0000, 0x378c8000,
-0x378d0000, 0x378d8000, 0x378e0000, 0x378e8000, 0x378f0000, 0x378f8000,
-0x37900000, 0x37908000, 0x37910000, 0x37918000, 0x37920000, 0x37928000,
-0x37930000, 0x37938000, 0x37940000, 0x37948000, 0x37950000, 0x37958000,
-0x37960000, 0x37968000, 0x37970000, 0x37978000, 0x37980000, 0x37988000,
-0x37990000, 0x37998000, 0x379a0000, 0x379a8000, 0x379b0000, 0x379b8000,
-0x379c0000, 0x379c8000, 0x379d0000, 0x379d8000, 0x379e0000, 0x379e8000,
-0x379f0000, 0x379f8000, 0x37a00000, 0x37a08000, 0x37a10000, 0x37a18000,
-0x37a20000, 0x37a28000, 0x37a30000, 0x37a38000, 0x37a40000, 0x37a48000,
-0x37a50000, 0x37a58000, 0x37a60000, 0x37a68000, 0x37a70000, 0x37a78000,
-0x37a80000, 0x37a88000, 0x37a90000, 0x37a98000, 0x37aa0000, 0x37aa8000,
-0x37ab0000, 0x37ab8000, 0x37ac0000, 0x37ac8000, 0x37ad0000, 0x37ad8000,
-0x37ae0000, 0x37ae8000, 0x37af0000, 0x37af8000, 0x37b00000, 0x37b08000,
-0x37b10000, 0x37b18000, 0x37b20000, 0x37b28000, 0x37b30000, 0x37b38000,
-0x37b40000, 0x37b48000, 0x37b50000, 0x37b58000, 0x37b60000, 0x37b68000,
-0x37b70000, 0x37b78000, 0x37b80000, 0x37b88000, 0x37b90000, 0x37b98000,
-0x37ba0000, 0x37ba8000, 0x37bb0000, 0x37bb8000, 0x37bc0000, 0x37bc8000,
-0x37bd0000, 0x37bd8000, 0x37be0000, 0x37be8000, 0x37bf0000, 0x37bf8000,
-0x37c00000, 0x37c08000, 0x37c10000, 0x37c18000, 0x37c20000, 0x37c28000,
-0x37c30000, 0x37c38000, 0x37c40000, 0x37c48000, 0x37c50000, 0x37c58000,
-0x37c60000, 0x37c68000, 0x37c70000, 0x37c78000, 0x37c80000, 0x37c88000,
-0x37c90000, 0x37c98000, 0x37ca0000, 0x37ca8000, 0x37cb0000, 0x37cb8000,
-0x37cc0000, 0x37cc8000, 0x37cd0000, 0x37cd8000, 0x37ce0000, 0x37ce8000,
-0x37cf0000, 0x37cf8000, 0x37d00000, 0x37d08000, 0x37d10000, 0x37d18000,
-0x37d20000, 0x37d28000, 0x37d30000, 0x37d38000, 0x37d40000, 0x37d48000,
-0x37d50000, 0x37d58000, 0x37d60000, 0x37d68000, 0x37d70000, 0x37d78000,
-0x37d80000, 0x37d88000, 0x37d90000, 0x37d98000, 0x37da0000, 0x37da8000,
-0x37db0000, 0x37db8000, 0x37dc0000, 0x37dc8000, 0x37dd0000, 0x37dd8000,
-0x37de0000, 0x37de8000, 0x37df0000, 0x37df8000, 0x37e00000, 0x37e08000,
-0x37e10000, 0x37e18000, 0x37e20000, 0x37e28000, 0x37e30000, 0x37e38000,
-0x37e40000, 0x37e48000, 0x37e50000, 0x37e58000, 0x37e60000, 0x37e68000,
-0x37e70000, 0x37e78000, 0x37e80000, 0x37e88000, 0x37e90000, 0x37e98000,
-0x37ea0000, 0x37ea8000, 0x37eb0000, 0x37eb8000, 0x37ec0000, 0x37ec8000,
-0x37ed0000, 0x37ed8000, 0x37ee0000, 0x37ee8000, 0x37ef0000, 0x37ef8000,
-0x37f00000, 0x37f08000, 0x37f10000, 0x37f18000, 0x37f20000, 0x37f28000,
-0x37f30000, 0x37f38000, 0x37f40000, 0x37f48000, 0x37f50000, 0x37f58000,
-0x37f60000, 0x37f68000, 0x37f70000, 0x37f78000, 0x37f80000, 0x37f88000,
-0x37f90000, 0x37f98000, 0x37fa0000, 0x37fa8000, 0x37fb0000, 0x37fb8000,
-0x37fc0000, 0x37fc8000, 0x37fd0000, 0x37fd8000, 0x37fe0000, 0x37fe8000,
-0x37ff0000, 0x37ff8000, 0x38000000, 0x38004000, 0x38008000, 0x3800c000,
-0x38010000, 0x38014000, 0x38018000, 0x3801c000, 0x38020000, 0x38024000,
-0x38028000, 0x3802c000, 0x38030000, 0x38034000, 0x38038000, 0x3803c000,
-0x38040000, 0x38044000, 0x38048000, 0x3804c000, 0x38050000, 0x38054000,
-0x38058000, 0x3805c000, 0x38060000, 0x38064000, 0x38068000, 0x3806c000,
-0x38070000, 0x38074000, 0x38078000, 0x3807c000, 0x38080000, 0x38084000,
-0x38088000, 0x3808c000, 0x38090000, 0x38094000, 0x38098000, 0x3809c000,
-0x380a0000, 0x380a4000, 0x380a8000, 0x380ac000, 0x380b0000, 0x380b4000,
-0x380b8000, 0x380bc000, 0x380c0000, 0x380c4000, 0x380c8000, 0x380cc000,
-0x380d0000, 0x380d4000, 0x380d8000, 0x380dc000, 0x380e0000, 0x380e4000,
-0x380e8000, 0x380ec000, 0x380f0000, 0x380f4000, 0x380f8000, 0x380fc000,
-0x38100000, 0x38104000, 0x38108000, 0x3810c000, 0x38110000, 0x38114000,
-0x38118000, 0x3811c000, 0x38120000, 0x38124000, 0x38128000, 0x3812c000,
-0x38130000, 0x38134000, 0x38138000, 0x3813c000, 0x38140000, 0x38144000,
-0x38148000, 0x3814c000, 0x38150000, 0x38154000, 0x38158000, 0x3815c000,
-0x38160000, 0x38164000, 0x38168000, 0x3816c000, 0x38170000, 0x38174000,
-0x38178000, 0x3817c000, 0x38180000, 0x38184000, 0x38188000, 0x3818c000,
-0x38190000, 0x38194000, 0x38198000, 0x3819c000, 0x381a0000, 0x381a4000,
-0x381a8000, 0x381ac000, 0x381b0000, 0x381b4000, 0x381b8000, 0x381bc000,
-0x381c0000, 0x381c4000, 0x381c8000, 0x381cc000, 0x381d0000, 0x381d4000,
-0x381d8000, 0x381dc000, 0x381e0000, 0x381e4000, 0x381e8000, 0x381ec000,
-0x381f0000, 0x381f4000, 0x381f8000, 0x381fc000, 0x38200000, 0x38204000,
-0x38208000, 0x3820c000, 0x38210000, 0x38214000, 0x38218000, 0x3821c000,
-0x38220000, 0x38224000, 0x38228000, 0x3822c000, 0x38230000, 0x38234000,
-0x38238000, 0x3823c000, 0x38240000, 0x38244000, 0x38248000, 0x3824c000,
-0x38250000, 0x38254000, 0x38258000, 0x3825c000, 0x38260000, 0x38264000,
-0x38268000, 0x3826c000, 0x38270000, 0x38274000, 0x38278000, 0x3827c000,
-0x38280000, 0x38284000, 0x38288000, 0x3828c000, 0x38290000, 0x38294000,
-0x38298000, 0x3829c000, 0x382a0000, 0x382a4000, 0x382a8000, 0x382ac000,
-0x382b0000, 0x382b4000, 0x382b8000, 0x382bc000, 0x382c0000, 0x382c4000,
-0x382c8000, 0x382cc000, 0x382d0000, 0x382d4000, 0x382d8000, 0x382dc000,
-0x382e0000, 0x382e4000, 0x382e8000, 0x382ec000, 0x382f0000, 0x382f4000,
-0x382f8000, 0x382fc000, 0x38300000, 0x38304000, 0x38308000, 0x3830c000,
-0x38310000, 0x38314000, 0x38318000, 0x3831c000, 0x38320000, 0x38324000,
-0x38328000, 0x3832c000, 0x38330000, 0x38334000, 0x38338000, 0x3833c000,
-0x38340000, 0x38344000, 0x38348000, 0x3834c000, 0x38350000, 0x38354000,
-0x38358000, 0x3835c000, 0x38360000, 0x38364000, 0x38368000, 0x3836c000,
-0x38370000, 0x38374000, 0x38378000, 0x3837c000, 0x38380000, 0x38384000,
-0x38388000, 0x3838c000, 0x38390000, 0x38394000, 0x38398000, 0x3839c000,
-0x383a0000, 0x383a4000, 0x383a8000, 0x383ac000, 0x383b0000, 0x383b4000,
-0x383b8000, 0x383bc000, 0x383c0000, 0x383c4000, 0x383c8000, 0x383cc000,
-0x383d0000, 0x383d4000, 0x383d8000, 0x383dc000, 0x383e0000, 0x383e4000,
-0x383e8000, 0x383ec000, 0x383f0000, 0x383f4000, 0x383f8000, 0x383fc000,
-0x38400000, 0x38404000, 0x38408000, 0x3840c000, 0x38410000, 0x38414000,
-0x38418000, 0x3841c000, 0x38420000, 0x38424000, 0x38428000, 0x3842c000,
-0x38430000, 0x38434000, 0x38438000, 0x3843c000, 0x38440000, 0x38444000,
-0x38448000, 0x3844c000, 0x38450000, 0x38454000, 0x38458000, 0x3845c000,
-0x38460000, 0x38464000, 0x38468000, 0x3846c000, 0x38470000, 0x38474000,
-0x38478000, 0x3847c000, 0x38480000, 0x38484000, 0x38488000, 0x3848c000,
-0x38490000, 0x38494000, 0x38498000, 0x3849c000, 0x384a0000, 0x384a4000,
-0x384a8000, 0x384ac000, 0x384b0000, 0x384b4000, 0x384b8000, 0x384bc000,
-0x384c0000, 0x384c4000, 0x384c8000, 0x384cc000, 0x384d0000, 0x384d4000,
-0x384d8000, 0x384dc000, 0x384e0000, 0x384e4000, 0x384e8000, 0x384ec000,
-0x384f0000, 0x384f4000, 0x384f8000, 0x384fc000, 0x38500000, 0x38504000,
-0x38508000, 0x3850c000, 0x38510000, 0x38514000, 0x38518000, 0x3851c000,
-0x38520000, 0x38524000, 0x38528000, 0x3852c000, 0x38530000, 0x38534000,
-0x38538000, 0x3853c000, 0x38540000, 0x38544000, 0x38548000, 0x3854c000,
-0x38550000, 0x38554000, 0x38558000, 0x3855c000, 0x38560000, 0x38564000,
-0x38568000, 0x3856c000, 0x38570000, 0x38574000, 0x38578000, 0x3857c000,
-0x38580000, 0x38584000, 0x38588000, 0x3858c000, 0x38590000, 0x38594000,
-0x38598000, 0x3859c000, 0x385a0000, 0x385a4000, 0x385a8000, 0x385ac000,
-0x385b0000, 0x385b4000, 0x385b8000, 0x385bc000, 0x385c0000, 0x385c4000,
-0x385c8000, 0x385cc000, 0x385d0000, 0x385d4000, 0x385d8000, 0x385dc000,
-0x385e0000, 0x385e4000, 0x385e8000, 0x385ec000, 0x385f0000, 0x385f4000,
-0x385f8000, 0x385fc000, 0x38600000, 0x38604000, 0x38608000, 0x3860c000,
-0x38610000, 0x38614000, 0x38618000, 0x3861c000, 0x38620000, 0x38624000,
-0x38628000, 0x3862c000, 0x38630000, 0x38634000, 0x38638000, 0x3863c000,
-0x38640000, 0x38644000, 0x38648000, 0x3864c000, 0x38650000, 0x38654000,
-0x38658000, 0x3865c000, 0x38660000, 0x38664000, 0x38668000, 0x3866c000,
-0x38670000, 0x38674000, 0x38678000, 0x3867c000, 0x38680000, 0x38684000,
-0x38688000, 0x3868c000, 0x38690000, 0x38694000, 0x38698000, 0x3869c000,
-0x386a0000, 0x386a4000, 0x386a8000, 0x386ac000, 0x386b0000, 0x386b4000,
-0x386b8000, 0x386bc000, 0x386c0000, 0x386c4000, 0x386c8000, 0x386cc000,
-0x386d0000, 0x386d4000, 0x386d8000, 0x386dc000, 0x386e0000, 0x386e4000,
-0x386e8000, 0x386ec000, 0x386f0000, 0x386f4000, 0x386f8000, 0x386fc000,
-0x38700000, 0x38704000, 0x38708000, 0x3870c000, 0x38710000, 0x38714000,
-0x38718000, 0x3871c000, 0x38720000, 0x38724000, 0x38728000, 0x3872c000,
-0x38730000, 0x38734000, 0x38738000, 0x3873c000, 0x38740000, 0x38744000,
-0x38748000, 0x3874c000, 0x38750000, 0x38754000, 0x38758000, 0x3875c000,
-0x38760000, 0x38764000, 0x38768000, 0x3876c000, 0x38770000, 0x38774000,
-0x38778000, 0x3877c000, 0x38780000, 0x38784000, 0x38788000, 0x3878c000,
-0x38790000, 0x38794000, 0x38798000, 0x3879c000, 0x387a0000, 0x387a4000,
-0x387a8000, 0x387ac000, 0x387b0000, 0x387b4000, 0x387b8000, 0x387bc000,
-0x387c0000, 0x387c4000, 0x387c8000, 0x387cc000, 0x387d0000, 0x387d4000,
-0x387d8000, 0x387dc000, 0x387e0000, 0x387e4000, 0x387e8000, 0x387ec000,
-0x387f0000, 0x387f4000, 0x387f8000, 0x387fc000, 0x38000000, 0x38002000,
-0x38004000, 0x38006000, 0x38008000, 0x3800a000, 0x3800c000, 0x3800e000,
-0x38010000, 0x38012000, 0x38014000, 0x38016000, 0x38018000, 0x3801a000,
-0x3801c000, 0x3801e000, 0x38020000, 0x38022000, 0x38024000, 0x38026000,
-0x38028000, 0x3802a000, 0x3802c000, 0x3802e000, 0x38030000, 0x38032000,
-0x38034000, 0x38036000, 0x38038000, 0x3803a000, 0x3803c000, 0x3803e000,
-0x38040000, 0x38042000, 0x38044000, 0x38046000, 0x38048000, 0x3804a000,
-0x3804c000, 0x3804e000, 0x38050000, 0x38052000, 0x38054000, 0x38056000,
-0x38058000, 0x3805a000, 0x3805c000, 0x3805e000, 0x38060000, 0x38062000,
-0x38064000, 0x38066000, 0x38068000, 0x3806a000, 0x3806c000, 0x3806e000,
-0x38070000, 0x38072000, 0x38074000, 0x38076000, 0x38078000, 0x3807a000,
-0x3807c000, 0x3807e000, 0x38080000, 0x38082000, 0x38084000, 0x38086000,
-0x38088000, 0x3808a000, 0x3808c000, 0x3808e000, 0x38090000, 0x38092000,
-0x38094000, 0x38096000, 0x38098000, 0x3809a000, 0x3809c000, 0x3809e000,
-0x380a0000, 0x380a2000, 0x380a4000, 0x380a6000, 0x380a8000, 0x380aa000,
-0x380ac000, 0x380ae000, 0x380b0000, 0x380b2000, 0x380b4000, 0x380b6000,
-0x380b8000, 0x380ba000, 0x380bc000, 0x380be000, 0x380c0000, 0x380c2000,
-0x380c4000, 0x380c6000, 0x380c8000, 0x380ca000, 0x380cc000, 0x380ce000,
-0x380d0000, 0x380d2000, 0x380d4000, 0x380d6000, 0x380d8000, 0x380da000,
-0x380dc000, 0x380de000, 0x380e0000, 0x380e2000, 0x380e4000, 0x380e6000,
-0x380e8000, 0x380ea000, 0x380ec000, 0x380ee000, 0x380f0000, 0x380f2000,
-0x380f4000, 0x380f6000, 0x380f8000, 0x380fa000, 0x380fc000, 0x380fe000,
-0x38100000, 0x38102000, 0x38104000, 0x38106000, 0x38108000, 0x3810a000,
-0x3810c000, 0x3810e000, 0x38110000, 0x38112000, 0x38114000, 0x38116000,
-0x38118000, 0x3811a000, 0x3811c000, 0x3811e000, 0x38120000, 0x38122000,
-0x38124000, 0x38126000, 0x38128000, 0x3812a000, 0x3812c000, 0x3812e000,
-0x38130000, 0x38132000, 0x38134000, 0x38136000, 0x38138000, 0x3813a000,
-0x3813c000, 0x3813e000, 0x38140000, 0x38142000, 0x38144000, 0x38146000,
-0x38148000, 0x3814a000, 0x3814c000, 0x3814e000, 0x38150000, 0x38152000,
-0x38154000, 0x38156000, 0x38158000, 0x3815a000, 0x3815c000, 0x3815e000,
-0x38160000, 0x38162000, 0x38164000, 0x38166000, 0x38168000, 0x3816a000,
-0x3816c000, 0x3816e000, 0x38170000, 0x38172000, 0x38174000, 0x38176000,
-0x38178000, 0x3817a000, 0x3817c000, 0x3817e000, 0x38180000, 0x38182000,
-0x38184000, 0x38186000, 0x38188000, 0x3818a000, 0x3818c000, 0x3818e000,
-0x38190000, 0x38192000, 0x38194000, 0x38196000, 0x38198000, 0x3819a000,
-0x3819c000, 0x3819e000, 0x381a0000, 0x381a2000, 0x381a4000, 0x381a6000,
-0x381a8000, 0x381aa000, 0x381ac000, 0x381ae000, 0x381b0000, 0x381b2000,
-0x381b4000, 0x381b6000, 0x381b8000, 0x381ba000, 0x381bc000, 0x381be000,
-0x381c0000, 0x381c2000, 0x381c4000, 0x381c6000, 0x381c8000, 0x381ca000,
-0x381cc000, 0x381ce000, 0x381d0000, 0x381d2000, 0x381d4000, 0x381d6000,
-0x381d8000, 0x381da000, 0x381dc000, 0x381de000, 0x381e0000, 0x381e2000,
-0x381e4000, 0x381e6000, 0x381e8000, 0x381ea000, 0x381ec000, 0x381ee000,
-0x381f0000, 0x381f2000, 0x381f4000, 0x381f6000, 0x381f8000, 0x381fa000,
-0x381fc000, 0x381fe000, 0x38200000, 0x38202000, 0x38204000, 0x38206000,
-0x38208000, 0x3820a000, 0x3820c000, 0x3820e000, 0x38210000, 0x38212000,
-0x38214000, 0x38216000, 0x38218000, 0x3821a000, 0x3821c000, 0x3821e000,
-0x38220000, 0x38222000, 0x38224000, 0x38226000, 0x38228000, 0x3822a000,
-0x3822c000, 0x3822e000, 0x38230000, 0x38232000, 0x38234000, 0x38236000,
-0x38238000, 0x3823a000, 0x3823c000, 0x3823e000, 0x38240000, 0x38242000,
-0x38244000, 0x38246000, 0x38248000, 0x3824a000, 0x3824c000, 0x3824e000,
-0x38250000, 0x38252000, 0x38254000, 0x38256000, 0x38258000, 0x3825a000,
-0x3825c000, 0x3825e000, 0x38260000, 0x38262000, 0x38264000, 0x38266000,
-0x38268000, 0x3826a000, 0x3826c000, 0x3826e000, 0x38270000, 0x38272000,
-0x38274000, 0x38276000, 0x38278000, 0x3827a000, 0x3827c000, 0x3827e000,
-0x38280000, 0x38282000, 0x38284000, 0x38286000, 0x38288000, 0x3828a000,
-0x3828c000, 0x3828e000, 0x38290000, 0x38292000, 0x38294000, 0x38296000,
-0x38298000, 0x3829a000, 0x3829c000, 0x3829e000, 0x382a0000, 0x382a2000,
-0x382a4000, 0x382a6000, 0x382a8000, 0x382aa000, 0x382ac000, 0x382ae000,
-0x382b0000, 0x382b2000, 0x382b4000, 0x382b6000, 0x382b8000, 0x382ba000,
-0x382bc000, 0x382be000, 0x382c0000, 0x382c2000, 0x382c4000, 0x382c6000,
-0x382c8000, 0x382ca000, 0x382cc000, 0x382ce000, 0x382d0000, 0x382d2000,
-0x382d4000, 0x382d6000, 0x382d8000, 0x382da000, 0x382dc000, 0x382de000,
-0x382e0000, 0x382e2000, 0x382e4000, 0x382e6000, 0x382e8000, 0x382ea000,
-0x382ec000, 0x382ee000, 0x382f0000, 0x382f2000, 0x382f4000, 0x382f6000,
-0x382f8000, 0x382fa000, 0x382fc000, 0x382fe000, 0x38300000, 0x38302000,
-0x38304000, 0x38306000, 0x38308000, 0x3830a000, 0x3830c000, 0x3830e000,
-0x38310000, 0x38312000, 0x38314000, 0x38316000, 0x38318000, 0x3831a000,
-0x3831c000, 0x3831e000, 0x38320000, 0x38322000, 0x38324000, 0x38326000,
-0x38328000, 0x3832a000, 0x3832c000, 0x3832e000, 0x38330000, 0x38332000,
-0x38334000, 0x38336000, 0x38338000, 0x3833a000, 0x3833c000, 0x3833e000,
-0x38340000, 0x38342000, 0x38344000, 0x38346000, 0x38348000, 0x3834a000,
-0x3834c000, 0x3834e000, 0x38350000, 0x38352000, 0x38354000, 0x38356000,
-0x38358000, 0x3835a000, 0x3835c000, 0x3835e000, 0x38360000, 0x38362000,
-0x38364000, 0x38366000, 0x38368000, 0x3836a000, 0x3836c000, 0x3836e000,
-0x38370000, 0x38372000, 0x38374000, 0x38376000, 0x38378000, 0x3837a000,
-0x3837c000, 0x3837e000, 0x38380000, 0x38382000, 0x38384000, 0x38386000,
-0x38388000, 0x3838a000, 0x3838c000, 0x3838e000, 0x38390000, 0x38392000,
-0x38394000, 0x38396000, 0x38398000, 0x3839a000, 0x3839c000, 0x3839e000,
-0x383a0000, 0x383a2000, 0x383a4000, 0x383a6000, 0x383a8000, 0x383aa000,
-0x383ac000, 0x383ae000, 0x383b0000, 0x383b2000, 0x383b4000, 0x383b6000,
-0x383b8000, 0x383ba000, 0x383bc000, 0x383be000, 0x383c0000, 0x383c2000,
-0x383c4000, 0x383c6000, 0x383c8000, 0x383ca000, 0x383cc000, 0x383ce000,
-0x383d0000, 0x383d2000, 0x383d4000, 0x383d6000, 0x383d8000, 0x383da000,
-0x383dc000, 0x383de000, 0x383e0000, 0x383e2000, 0x383e4000, 0x383e6000,
-0x383e8000, 0x383ea000, 0x383ec000, 0x383ee000, 0x383f0000, 0x383f2000,
-0x383f4000, 0x383f6000, 0x383f8000, 0x383fa000, 0x383fc000, 0x383fe000,
-0x38400000, 0x38402000, 0x38404000, 0x38406000, 0x38408000, 0x3840a000,
-0x3840c000, 0x3840e000, 0x38410000, 0x38412000, 0x38414000, 0x38416000,
-0x38418000, 0x3841a000, 0x3841c000, 0x3841e000, 0x38420000, 0x38422000,
-0x38424000, 0x38426000, 0x38428000, 0x3842a000, 0x3842c000, 0x3842e000,
-0x38430000, 0x38432000, 0x38434000, 0x38436000, 0x38438000, 0x3843a000,
-0x3843c000, 0x3843e000, 0x38440000, 0x38442000, 0x38444000, 0x38446000,
-0x38448000, 0x3844a000, 0x3844c000, 0x3844e000, 0x38450000, 0x38452000,
-0x38454000, 0x38456000, 0x38458000, 0x3845a000, 0x3845c000, 0x3845e000,
-0x38460000, 0x38462000, 0x38464000, 0x38466000, 0x38468000, 0x3846a000,
-0x3846c000, 0x3846e000, 0x38470000, 0x38472000, 0x38474000, 0x38476000,
-0x38478000, 0x3847a000, 0x3847c000, 0x3847e000, 0x38480000, 0x38482000,
-0x38484000, 0x38486000, 0x38488000, 0x3848a000, 0x3848c000, 0x3848e000,
-0x38490000, 0x38492000, 0x38494000, 0x38496000, 0x38498000, 0x3849a000,
-0x3849c000, 0x3849e000, 0x384a0000, 0x384a2000, 0x384a4000, 0x384a6000,
-0x384a8000, 0x384aa000, 0x384ac000, 0x384ae000, 0x384b0000, 0x384b2000,
-0x384b4000, 0x384b6000, 0x384b8000, 0x384ba000, 0x384bc000, 0x384be000,
-0x384c0000, 0x384c2000, 0x384c4000, 0x384c6000, 0x384c8000, 0x384ca000,
-0x384cc000, 0x384ce000, 0x384d0000, 0x384d2000, 0x384d4000, 0x384d6000,
-0x384d8000, 0x384da000, 0x384dc000, 0x384de000, 0x384e0000, 0x384e2000,
-0x384e4000, 0x384e6000, 0x384e8000, 0x384ea000, 0x384ec000, 0x384ee000,
-0x384f0000, 0x384f2000, 0x384f4000, 0x384f6000, 0x384f8000, 0x384fa000,
-0x384fc000, 0x384fe000, 0x38500000, 0x38502000, 0x38504000, 0x38506000,
-0x38508000, 0x3850a000, 0x3850c000, 0x3850e000, 0x38510000, 0x38512000,
-0x38514000, 0x38516000, 0x38518000, 0x3851a000, 0x3851c000, 0x3851e000,
-0x38520000, 0x38522000, 0x38524000, 0x38526000, 0x38528000, 0x3852a000,
-0x3852c000, 0x3852e000, 0x38530000, 0x38532000, 0x38534000, 0x38536000,
-0x38538000, 0x3853a000, 0x3853c000, 0x3853e000, 0x38540000, 0x38542000,
-0x38544000, 0x38546000, 0x38548000, 0x3854a000, 0x3854c000, 0x3854e000,
-0x38550000, 0x38552000, 0x38554000, 0x38556000, 0x38558000, 0x3855a000,
-0x3855c000, 0x3855e000, 0x38560000, 0x38562000, 0x38564000, 0x38566000,
-0x38568000, 0x3856a000, 0x3856c000, 0x3856e000, 0x38570000, 0x38572000,
-0x38574000, 0x38576000, 0x38578000, 0x3857a000, 0x3857c000, 0x3857e000,
-0x38580000, 0x38582000, 0x38584000, 0x38586000, 0x38588000, 0x3858a000,
-0x3858c000, 0x3858e000, 0x38590000, 0x38592000, 0x38594000, 0x38596000,
-0x38598000, 0x3859a000, 0x3859c000, 0x3859e000, 0x385a0000, 0x385a2000,
-0x385a4000, 0x385a6000, 0x385a8000, 0x385aa000, 0x385ac000, 0x385ae000,
-0x385b0000, 0x385b2000, 0x385b4000, 0x385b6000, 0x385b8000, 0x385ba000,
-0x385bc000, 0x385be000, 0x385c0000, 0x385c2000, 0x385c4000, 0x385c6000,
-0x385c8000, 0x385ca000, 0x385cc000, 0x385ce000, 0x385d0000, 0x385d2000,
-0x385d4000, 0x385d6000, 0x385d8000, 0x385da000, 0x385dc000, 0x385de000,
-0x385e0000, 0x385e2000, 0x385e4000, 0x385e6000, 0x385e8000, 0x385ea000,
-0x385ec000, 0x385ee000, 0x385f0000, 0x385f2000, 0x385f4000, 0x385f6000,
-0x385f8000, 0x385fa000, 0x385fc000, 0x385fe000, 0x38600000, 0x38602000,
-0x38604000, 0x38606000, 0x38608000, 0x3860a000, 0x3860c000, 0x3860e000,
-0x38610000, 0x38612000, 0x38614000, 0x38616000, 0x38618000, 0x3861a000,
-0x3861c000, 0x3861e000, 0x38620000, 0x38622000, 0x38624000, 0x38626000,
-0x38628000, 0x3862a000, 0x3862c000, 0x3862e000, 0x38630000, 0x38632000,
-0x38634000, 0x38636000, 0x38638000, 0x3863a000, 0x3863c000, 0x3863e000,
-0x38640000, 0x38642000, 0x38644000, 0x38646000, 0x38648000, 0x3864a000,
-0x3864c000, 0x3864e000, 0x38650000, 0x38652000, 0x38654000, 0x38656000,
-0x38658000, 0x3865a000, 0x3865c000, 0x3865e000, 0x38660000, 0x38662000,
-0x38664000, 0x38666000, 0x38668000, 0x3866a000, 0x3866c000, 0x3866e000,
-0x38670000, 0x38672000, 0x38674000, 0x38676000, 0x38678000, 0x3867a000,
-0x3867c000, 0x3867e000, 0x38680000, 0x38682000, 0x38684000, 0x38686000,
-0x38688000, 0x3868a000, 0x3868c000, 0x3868e000, 0x38690000, 0x38692000,
-0x38694000, 0x38696000, 0x38698000, 0x3869a000, 0x3869c000, 0x3869e000,
-0x386a0000, 0x386a2000, 0x386a4000, 0x386a6000, 0x386a8000, 0x386aa000,
-0x386ac000, 0x386ae000, 0x386b0000, 0x386b2000, 0x386b4000, 0x386b6000,
-0x386b8000, 0x386ba000, 0x386bc000, 0x386be000, 0x386c0000, 0x386c2000,
-0x386c4000, 0x386c6000, 0x386c8000, 0x386ca000, 0x386cc000, 0x386ce000,
-0x386d0000, 0x386d2000, 0x386d4000, 0x386d6000, 0x386d8000, 0x386da000,
-0x386dc000, 0x386de000, 0x386e0000, 0x386e2000, 0x386e4000, 0x386e6000,
-0x386e8000, 0x386ea000, 0x386ec000, 0x386ee000, 0x386f0000, 0x386f2000,
-0x386f4000, 0x386f6000, 0x386f8000, 0x386fa000, 0x386fc000, 0x386fe000,
-0x38700000, 0x38702000, 0x38704000, 0x38706000, 0x38708000, 0x3870a000,
-0x3870c000, 0x3870e000, 0x38710000, 0x38712000, 0x38714000, 0x38716000,
-0x38718000, 0x3871a000, 0x3871c000, 0x3871e000, 0x38720000, 0x38722000,
-0x38724000, 0x38726000, 0x38728000, 0x3872a000, 0x3872c000, 0x3872e000,
-0x38730000, 0x38732000, 0x38734000, 0x38736000, 0x38738000, 0x3873a000,
-0x3873c000, 0x3873e000, 0x38740000, 0x38742000, 0x38744000, 0x38746000,
-0x38748000, 0x3874a000, 0x3874c000, 0x3874e000, 0x38750000, 0x38752000,
-0x38754000, 0x38756000, 0x38758000, 0x3875a000, 0x3875c000, 0x3875e000,
-0x38760000, 0x38762000, 0x38764000, 0x38766000, 0x38768000, 0x3876a000,
-0x3876c000, 0x3876e000, 0x38770000, 0x38772000, 0x38774000, 0x38776000,
-0x38778000, 0x3877a000, 0x3877c000, 0x3877e000, 0x38780000, 0x38782000,
-0x38784000, 0x38786000, 0x38788000, 0x3878a000, 0x3878c000, 0x3878e000,
-0x38790000, 0x38792000, 0x38794000, 0x38796000, 0x38798000, 0x3879a000,
-0x3879c000, 0x3879e000, 0x387a0000, 0x387a2000, 0x387a4000, 0x387a6000,
-0x387a8000, 0x387aa000, 0x387ac000, 0x387ae000, 0x387b0000, 0x387b2000,
-0x387b4000, 0x387b6000, 0x387b8000, 0x387ba000, 0x387bc000, 0x387be000,
-0x387c0000, 0x387c2000, 0x387c4000, 0x387c6000, 0x387c8000, 0x387ca000,
-0x387cc000, 0x387ce000, 0x387d0000, 0x387d2000, 0x387d4000, 0x387d6000,
-0x387d8000, 0x387da000, 0x387dc000, 0x387de000, 0x387e0000, 0x387e2000,
-0x387e4000, 0x387e6000, 0x387e8000, 0x387ea000, 0x387ec000, 0x387ee000,
-0x387f0000, 0x387f2000, 0x387f4000, 0x387f6000, 0x387f8000, 0x387fa000,
-0x387fc000, 0x387fe000
-};
-
-static uint16_t m__offset[64] = {
-0x0000, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
-0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
-0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
-0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
-0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
-0x0400, 0x0400, 0x0000, 0x0400, 0x0400, 0x0400,
-0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
-0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
-0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
-0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
-0x0400, 0x0400, 0x0400, 0x0400
-};
-
-static uint32_t m__exponent[64] = {
-0x00000000, 0x00800000, 0x01000000, 0x01800000, 0x02000000, 0x02800000,
-0x03000000, 0x03800000, 0x04000000, 0x04800000, 0x05000000, 0x05800000,
-0x06000000, 0x06800000, 0x07000000, 0x07800000, 0x08000000, 0x08800000,
-0x09000000, 0x09800000, 0x0a000000, 0x0a800000, 0x0b000000, 0x0b800000,
-0x0c000000, 0x0c800000, 0x0d000000, 0x0d800000, 0x0e000000, 0x0e800000,
-0x0f000000, 0x47800000, 0x80000000, 0x80800000, 0x81000000, 0x81800000,
-0x82000000, 0x82800000, 0x83000000, 0x83800000, 0x84000000, 0x84800000,
-0x85000000, 0x85800000, 0x86000000, 0x86800000, 0x87000000, 0x87800000,
-0x88000000, 0x88800000, 0x89000000, 0x89800000, 0x8a000000, 0x8a800000,
-0x8b000000, 0x8b800000, 0x8c000000, 0x8c800000, 0x8d000000, 0x8d800000,
-0x8e000000, 0x8e800000, 0x8f000000, 0xc7800000
-};
-
-static uint16_t m__base[512] = {
-0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
-0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
-0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
-0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
-0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
-0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
-0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
-0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
-0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
-0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
-0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040,
-0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x0c00, 0x1000, 0x1400, 0x1800, 0x1c00,
-0x2000, 0x2400, 0x2800, 0x2c00, 0x3000, 0x3400, 0x3800, 0x3c00, 0x4000, 0x4400,
-0x4800, 0x4c00, 0x5000, 0x5400, 0x5800, 0x5c00, 0x6000, 0x6400, 0x6800, 0x6c00,
-0x7000, 0x7400, 0x7800, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00,
-0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00,
-0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00,
-0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00,
-0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00,
-0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00,
-0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00,
-0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00,
-0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00,
-0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00,
-0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00,
-0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x8000, 0x8000, 0x8000, 0x8000,
-0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
-0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
-0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
-0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
-0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
-0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
-0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
-0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
-0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
-0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8001,
-0x8002, 0x8004, 0x8008, 0x8010, 0x8020, 0x8040, 0x8080, 0x8100, 0x8200, 0x8400,
-0x8800, 0x8c00, 0x9000, 0x9400, 0x9800, 0x9c00, 0xa000, 0xa400, 0xa800, 0xac00,
-0xb000, 0xb400, 0xb800, 0xbc00, 0xc000, 0xc400, 0xc800, 0xcc00, 0xd000, 0xd400,
-0xd800, 0xdc00, 0xe000, 0xe400, 0xe800, 0xec00, 0xf000, 0xf400, 0xf800, 0xfc00,
-0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00,
-0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00,
-0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00,
-0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00,
-0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00,
-0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00,
-0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00,
-0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00,
-0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00,
-0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00,
-0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00,
-0xfc00, 0xfc00
-};
-
-static uint8_t m__shift[512] = {
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17,
-0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d,
-0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
-0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0d, 0x18, 0x18, 0x18, 0x18,
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13,
-0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
-0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
-0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18,
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
-0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
-0x18, 0x18, 0x18, 0x18, 0x0d
-};
-
-MIAPI float m_half2float(uint16_t h)
-{
- union {
- float flt;
- uint32_t num;
- } out;
-
- int n = h >> 10;
-
- out.num = m__mantissa[ (h & 0x3ff) + m__offset[n]] + m__exponent[n];
- return out.flt;
-}
-
-MIAPI uint16_t m_float2half(float flt)
-{
- union {
- float flt;
- uint32_t num;
- } in;
-
- uint32_t n, j;
-
- in.flt = flt;
- n = in.num;
- j = (n >> 23) & 0x1ff;
-
- return (uint16_t) ((uint32_t) m__base[j] + ((n & 0x007fffff) >> m__shift[j]));
-}
-
-MIAPI void m_image_create(struct m_image *image, char type, int width, int height, int comp)
-{
- int size = width * height * comp;
- assert(size > 0);
-
- /* already allocated */
- if (image->data != 0 && type == image->type && width == image->width && height == image->height && comp == image->comp)
- return;
-
- M_SAFE_FREE(image->data);
-
- switch (type) {
- case M_BYTE:
- case M_UBYTE:
- image->data = malloc(size * sizeof(uint8_t));
- break;
- case M_SHORT:
- case M_USHORT:
- case M_HALF:
- image->data = malloc(size * sizeof(uint16_t));
- break;
- case M_INT:
- case M_UINT:
- image->data = malloc(size * sizeof(uint32_t));
- break;
- case M_FLOAT:
- image->data = malloc(size * sizeof(float));
- break;
- default:
- assert(0);
- return;
- }
-
- image->type = type;
- image->width = width;
- image->height = height;
- image->comp = comp;
- image->size = size;
-}
-
-MIAPI void m_image_destroy(struct m_image *image)
-{
- M_SAFE_FREE(image->data);
- memset(image, 0, sizeof(struct m_image));
-}
-
-MIAPI void m_image_copy(struct m_image *dest, const struct m_image *src)
-{
- m_image_create(dest, src->type, src->width, src->height, src->comp);
-
- switch (dest->type) {
- case M_BYTE:
- case M_UBYTE:
- memcpy(dest->data, src->data, dest->size*sizeof(char));
- break;
- case M_SHORT:
- case M_USHORT:
- case M_HALF:
- memcpy(dest->data, src->data, dest->size*sizeof(short));
- break;
- case M_INT:
- case M_UINT:
- memcpy(dest->data, src->data, dest->size*sizeof(int));
- break;
- case M_FLOAT:
- memcpy(dest->data, src->data, dest->size*sizeof(float));
- break;
- default:
- assert(0);
- break;
- }
-}
-
-MIAPI void m_image_copy_sub_image(struct m_image *dest, const struct m_image *src, int x, int y, int w, int h)
-{
- #define M_COPY_SUBI(T)\
- {\
- T *sData = (T *)src->data + (miny * src->width + minx) * comp;\
- T *dData = (T *)dest->data;\
- int y;\
- for (y = miny; y <= maxy; y++) {\
- memcpy(dData, sData, dstep * sizeof(T));\
- dData += dstep;\
- sData += sstep;\
- }\
- }
-
- int comp = src->comp;
- int minx = M_MAX(0, x);
- int miny = M_MAX(0, y);
- int maxx = M_CLAMP(x + w - 1, 0, src->width - 1);
- int maxy = M_CLAMP(y + h - 1, 0, src->height - 1);
- int dwidth = 1 + maxx - minx;
- int dheight = 1 + maxy - miny;
- int sstep = src->width * comp;
- int dstep = dwidth * comp;
-
- m_image_create(dest, src->type, dwidth, dheight, src->comp);
-
- switch(src->type)
- {
- case M_BYTE:
- case M_UBYTE:
- M_COPY_SUBI(char);
- break;
- case M_SHORT:
- case M_USHORT:
- case M_HALF:
- M_COPY_SUBI(short);
- break;
- case M_INT:
- case M_UINT:
- M_COPY_SUBI(int);
- break;
- case M_FLOAT:
- M_COPY_SUBI(float);
- break;
- default:
- assert(0);
- break;
- }
-
- #undef M_COPY_SUBI
-}
-
-MIAPI void m_image_ubyte_to_float(struct m_image *dest, const struct m_image *src)
-{
- uint8_t *src_data;
- float *dest_data;
- float ubyte_div = 1.0f / 255.0f;
- int i;
-
- m_image_create(dest, M_FLOAT, src->width, src->height, src->comp);
-
- src_data = (uint8_t *)src->data;
- dest_data = (float *)dest->data;
- for (i = 0; i < src->size; i++)
- dest_data[i] = (float)src_data[i] * ubyte_div;
-}
-
-MIAPI void m_image_ushort_to_float(struct m_image *dest, const struct m_image *src)
-{
- uint16_t *src_data;
- float *dest_data;
- float ushort_div = 1.0f / (float)65535;
- int i;
-
- m_image_create(dest, M_FLOAT, src->width, src->height, src->comp);
-
- src_data = (uint16_t *)src->data;
- dest_data = (float *)dest->data;
- for (i = 0; i < src->size; i++)
- dest_data[i] = (float)src_data[i] * ushort_div;
-}
-
-MIAPI void m_image_half_to_float(struct m_image *dest, const struct m_image *src)
-{
- uint16_t *src_data;
- float *dest_data;
- int i;
-
- m_image_create(dest, M_FLOAT, src->width, src->height, src->comp);
-
- src_data = (uint16_t *)src->data;
- dest_data = (float *)dest->data;
- for (i = 0; i < src->size; i++)
- dest_data[i] = m_half2float(src_data[i]);
-}
-
-MIAPI void m_image_float_to_ubyte(struct m_image *dest, const struct m_image *src)
-{
- float *src_data;
- uint8_t *dest_data;
- int i;
-
- m_image_create(dest, M_UBYTE, src->width, src->height, src->comp);
-
- src_data = (float *)src->data;
- dest_data = (uint8_t *)dest->data;
- for (i = 0; i < src->size; i++) {
- int x = (int)(src_data[i] * 255);
- dest_data[i] = (uint8_t)M_CLAMP(x, 0, 255);
- }
-}
-
-MIAPI void m_image_float_to_ushort(struct m_image *dest, const struct m_image *src)
-{
- float *src_data;
- uint16_t *dest_data;
- int i;
-
- m_image_create(dest, M_USHORT, src->width, src->height, src->comp);
-
- src_data = (float *)src->data;
- dest_data = (uint16_t *)dest->data;
- for (i = 0; i < src->size; i++) {
- int x = (int)(src_data[i] * 65535);
- dest_data[i] = (uint16_t)M_CLAMP(x, 0, 65535);
- }
-}
-
-MIAPI void m_image_float_to_half(struct m_image *dest, const struct m_image *src)
-{
- float *src_data;
- uint16_t *dest_data;
- int i;
-
- m_image_create(dest, M_USHORT, src->width, src->height, src->comp);
-
- src_data = (float *)src->data;
- dest_data = (uint16_t *)dest->data;
- for (i = 0; i < src->size; i++)
- dest_data[i] = m_float2half(src_data[i]);
-}
-
-MIAPI void m_image_extract_component(struct m_image *dest, const struct m_image *src, int c)
-{
- #define M_EXTRACT(T)\
- {\
- T *dest_pixel = (T *)dest->data;\
- T *src_pixel = (T *)src->data;\
- for (i = 0; i < size; i += comp) {\
- (*dest_pixel) = src_pixel[c];\
- dest_pixel++;\
- src_pixel += comp;\
- }\
- }
-
- int width = src->width;
- int height = src->height;
- int comp = src->comp;
- int size = src->size;
- int i;
-
- if(c >= src->comp) {
- assert(0);
- return;
- }
-
- m_image_create(dest, src->type, width, height, 1);
-
- switch(src->type)
- {
- case M_BYTE:
- case M_UBYTE:
- M_EXTRACT(char);
- break;
- case M_SHORT:
- case M_USHORT:
- case M_HALF:
- M_EXTRACT(short);
- break;
- case M_INT:
- case M_UINT:
- M_EXTRACT(int);
- break;
- case M_FLOAT:
- M_EXTRACT(float);
- break;
- default:
- assert(0);
- break;
- }
-
- #undef M_EXTRACT
-}
-
-MIAPI void m_image_reframe(struct m_image *dest, const struct m_image *src, int left, int top, int right, int bottom)
-{
- #define M_REFRAME(T)\
- {\
- T *src_data;\
- T *src_pixel;\
- T *dest_pixel;\
- int c;\
- int x, y;\
- m_image_create(dest, src->type, width2, height2, comp);\
- src_data = (T *)src->data;\
- dest_pixel = (T *)dest->data;\
- for (y = 0; y < height2; y++) {\
- T *src_y;\
- int ys = y - top;\
- src_y = src_data + M_CLAMP(ys, 0, hm1) * width * comp;\
- for (x = 0; x < width2; x++) {\
- int xs = x - left;\
- src_pixel = src_y + M_CLAMP(xs, 0, wm1) * comp;\
- for (c = 0; c < comp; c++)\
- dest_pixel[c] = src_pixel[c];\
- dest_pixel += comp;\
- }\
- }\
- }
-
- if(left != 0 || top != 0 || right != 0 || bottom != 0) {
-
- int comp = src->comp;
- int width = src->width;
- int height = src->height;
- int width2 = width + left + right;
- int height2 = height + top + bottom;
- int wm1 = width - 1;
- int hm1 = height - 1;
-
- if(width2 > 0 && height2 > 0) {
-
- switch(src->type) {
- case M_BYTE:
- case M_UBYTE:
- M_REFRAME(char);
- break;
- case M_SHORT:
- case M_USHORT:
- case M_HALF:
- M_REFRAME(short);
- break;
- case M_INT:
- case M_UINT:
- M_REFRAME(int);
- break;
- case M_FLOAT:
- M_REFRAME(float);
- break;
- default:
- assert(0);
- break;
- }
- }
- else {
- assert(0);
- }
- }
- else {
- m_image_copy(dest, src);
- }
-
- #undef M_REFRAME
-}
-
-MIAPI void m_image_rotate_left(struct m_image *dest, const struct m_image *src)
-{
- #define M_ROTATE_L(T)\
- {\
- T *src_data = (T *)src->data;\
- T *dest_pixel = (T *)dest->data;\
- for (y = 0; y < width; y++)\
- for (x = 0; x < height; x++) {\
- T *src_pixel = src_data + (x * width + (width - 1 - y)) * comp;\
- for (c = 0; c < comp; c++)\
- dest_pixel[c] = src_pixel[c];\
- dest_pixel += comp;\
- }\
- }
-
- int width = src->width;
- int height = src->height;
- int comp = src->comp;
- int x, y, c;
-
- m_image_create(dest, src->type, height, width, comp);
-
- switch(src->type)
- {
- case M_BYTE:
- case M_UBYTE:
- M_ROTATE_L(char);
- break;
- case M_SHORT:
- case M_USHORT:
- case M_HALF:
- M_ROTATE_L(short);
- break;
- case M_INT:
- case M_UINT:
- M_ROTATE_L(int);
- break;
- case M_FLOAT:
- M_ROTATE_L(float);
- break;
- default:
- assert(0);
- break;
- }
-
- #undef M_ROTATE_L
-}
-
-MIAPI void m_image_rotate_right(struct m_image *dest, const struct m_image *src)
-{
- #define M_ROTATE_R(T)\
- {\
- T *src_data = (T *)src->data;\
- T *dest_pixel = (T *)dest->data;\
- for (y = 0; y < width; y++)\
- for (x = 0; x < height; x++) {\
- T *src_pixel = src_data + ((height - 1 - x) * width + y) * comp;\
- for (c = 0; c < comp; c++)\
- dest_pixel[c] = src_pixel[c];\
- dest_pixel += comp;\
- }\
- }
-
- int width = src->width;
- int height = src->height;
- int comp = src->comp;
- int x, y, c;
-
- m_image_create(dest, src->type, height, width, comp);
-
- switch(src->type)
- {
- case M_BYTE:
- case M_UBYTE:
- M_ROTATE_R(char);
- break;
- case M_SHORT:
- case M_USHORT:
- case M_HALF:
- M_ROTATE_R(short);
- break;
- case M_INT:
- case M_UINT:
- M_ROTATE_R(int);
- break;
- case M_FLOAT:
- M_ROTATE_R(float);
- break;
- default:
- assert(0);
- break;
- }
-
- #undef M_ROTATE_R
-}
-
-MIAPI void m_image_rotate_180(struct m_image *dest, const struct m_image *src)
-{
- #define M_ROTATE_180(T)\
- {\
- T *src_data = (T *)src->data;\
- T *dest_pixel = (T *)dest->data;\
- for (y = 0; y < height; y++)\
- for (x = 0; x < width; x++) {\
- T *src_pixel = src_data + ((height - 1 - y) * width + (width - 1 - x)) * comp;\
- for (c = 0; c < comp; c++)\
- dest_pixel[c] = src_pixel[c];\
- dest_pixel += comp;\
- }\
- }
-
- int width = src->width;
- int height = src->height;
- int comp = src->comp;
- int x, y, c;
-
- m_image_create(dest, src->type, width, height, comp);
-
- switch(src->type)
- {
- case M_BYTE:
- case M_UBYTE:
- M_ROTATE_180(char);
- break;
- case M_SHORT:
- case M_USHORT:
- case M_HALF:
- M_ROTATE_180(short);
- break;
- case M_INT:
- case M_UINT:
- M_ROTATE_180(int);
- break;
- case M_FLOAT:
- M_ROTATE_180(float);
- break;
- default:
- assert(0);
- break;
- }
-
- #undef M_ROTATE_180
-}
-
-MIAPI void m_image_mirror_x(struct m_image *dest, const struct m_image *src)
-{
- #define M_MIRROR_X(T)\
- {\
- T *src_data = (T *)src->data;\
- T *dest_pixel = (T *)dest->data;\
- for (y = 0; y < height; y++)\
- for (x = 0; x < width; x++) {\
- T *src_pixel = src_data + (y * width + (width - 1 - x)) * comp;\
- for (c = 0; c < comp; c++)\
- dest_pixel[c] = src_pixel[c];\
- dest_pixel += comp;\
- }\
- }
-
- int width = src->width;
- int height = src->height;
- int comp = src->comp;
- int x, y, c;
-
- m_image_create(dest, src->type, width, height, comp);
-
- switch(src->type)
- {
- case M_BYTE:
- case M_UBYTE:
- M_MIRROR_X(char);
- break;
- case M_SHORT:
- case M_USHORT:
- case M_HALF:
- M_MIRROR_X(short);
- break;
- case M_INT:
- case M_UINT:
- M_MIRROR_X(int);
- break;
- case M_FLOAT:
- M_MIRROR_X(float);
- break;
- default:
- assert(0);
- break;
- }
-
- #undef M_MIRROR_X
-}
-
-MIAPI void m_image_mirror_y(struct m_image *dest, const struct m_image *src)
-{
- #define M_MIRROR_Y(T)\
- {\
- T *src_data = (T *)src->data;\
- T *dest_pixel = (T *)dest->data;\
- for (y = 0; y < height; y++)\
- for (x = 0; x < width; x++) {\
- T *src_pixel = src_data + ((height - 1 - y) * width + x) * comp;\
- for (c = 0; c < comp; c++)\
- dest_pixel[c] = src_pixel[c];\
- dest_pixel += comp;\
- }\
- }
-
- int width = src->width;
- int height = src->height;
- int comp = src->comp;
- int x, y, c;
-
- m_image_create(dest, src->type, width, height, comp);
-
- switch(src->type)
- {
- case M_BYTE:
- case M_UBYTE:
- M_MIRROR_Y(char);
- break;
- case M_SHORT:
- case M_USHORT:
- case M_HALF:
- M_MIRROR_Y(short);
- break;
- case M_INT:
- case M_UINT:
- M_MIRROR_Y(int);
- break;
- case M_FLOAT:
- M_MIRROR_Y(float);
- break;
- default:
- assert(0);
- break;
- }
-
- #undef M_MIRROR_Y
-}
-
-MIAPI void m_image_premultiply(struct m_image *dest, const struct m_image *src)
-{
- float *dest_p, *src_p;
- int i;
-
- assert(src->size > 0 && src->type == M_FLOAT && src->comp == 4);
-
- m_image_create(dest, M_FLOAT, src->width, src->height, 4);
- dest_p = (float *)dest->data;
- src_p = (float *)src->data;
-
- for (i = 0; i < src->size; i+=4) {
- dest_p[0] = src_p[0] * src_p[3];
- dest_p[1] = src_p[1] * src_p[3];
- dest_p[2] = src_p[2] * src_p[3];
- dest_p[3] = src_p[3];
- dest_p += 4;
- src_p += 4;
- }
-}
-
-MIAPI void m_image_unpremultiply(struct m_image *dest, const struct m_image *src)
-{
- float *dest_p, *src_p;
- int i;
-
- assert(src->size > 0 && src->type == M_FLOAT && src->comp == 4);
-
- m_image_create(dest, M_FLOAT, src->width, src->height, 4);
- dest_p = (float *)dest->data;
- src_p = (float *)src->data;
-
- for (i = 0; i < src->size; i+=4) {
- float x = 1.0 / src_p[3];
- dest_p[0] = src_p[0] * x;
- dest_p[1] = src_p[1] * x;
- dest_p[2] = src_p[2] * x;
- dest_p[3] = src_p[3];
- dest_p += 4;
- src_p += 4;
- }
-}
-
-MIAPI void m_image_summed_area(struct m_image *dest, const struct m_image *src)
-{
- float *src_pixel;
- float *dest_pixel;
- int width = src->width;
- int height = src->height;
- int comp = src->comp;
- int x, y, c;
-
- assert(src->size > 0 && src->type == M_FLOAT);
- if (dest != src)
- m_image_copy(dest, src);
-
- /* horiz sum */
- dest_pixel = (float *)dest->data;
- for (y = 0; y < height; y++) {
- float *prev_pixel = dest_pixel;
- dest_pixel += comp;
-
- for (x = 1; x < width; x++) {
- for (c = 0; c < comp; c++)
- dest_pixel[c] += prev_pixel[c];
-
- prev_pixel = dest_pixel;
- dest_pixel += comp;
- }
- }
-
- /* vertical sum */
- src_pixel = (float *)dest->data;
- dest_pixel = (float *)dest->data + width * comp;
- for (y = 1; y < height; y++)
- for (x = 0; x < width; x++) {
-
- for (c = 0; c < comp; c++)
- dest_pixel[c] += src_pixel[c];
-
- src_pixel += comp;
- dest_pixel += comp;
- }
-}
-
-MIAPI void m_image_convolution_h(struct m_image *dest, const struct m_image *src, float *kernel, int size)
-{
- struct m_image copy = M_IMAGE_IDENTITY();
- float *src_data;
- float *dest_data;
- int width = src->width;
- int height = src->height;
- int comp = src->comp;
- int radius = (size - 1) / 2;
- int y, ystep, ystepc;
-
- assert(src->size > 0 && src->type == M_FLOAT);
-
- /* create source and destination images */
- m_image_reframe(&copy, src, radius, 0, radius, 0); /* apply clamped margin */
- m_image_create(dest, M_FLOAT, width, height, comp);
-
- /* clear */
- memset(dest->data, 0, dest->size * sizeof(float));
-
- src_data = (float *)copy.data;
- dest_data = (float *)dest->data;
- ystep = width * comp;
- ystepc = copy.width * comp;
-
- #pragma omp parallel for schedule(dynamic, 8)
- for (y=0; y<height; y++) {
-
- float *dest_pixel = dest_data + y * ystep;
- float *src_pixel_y = src_data + y * ystepc;
- int x;
-
- for (x = 0; x < width; x++) {
-
- float *src_pixel;
- int i, k;
-
- src_pixel = src_pixel_y + (x * comp);
-
- /* apply kernel */
- for (k = 0; k < size; k++) {
- float v = kernel[k];
- for (i = 0; i < comp; i++)
- dest_pixel[i] += (*src_pixel++) * v;
- }
-
- dest_pixel += comp;
- }
- }
-
- m_image_destroy(&copy);
-}
-
-MIAPI void m_image_convolution_v(struct m_image *dest, const struct m_image *src, float *kernel, int size)
-{
- struct m_image copy = M_IMAGE_IDENTITY();
- float *src_data;
- float *dest_data;
- int width = src->width;
- int height = src->height;
- int comp = src->comp;
- int radius = (size - 1) / 2;
- int y, ystep;
-
- assert(src->size > 0 && src->type == M_FLOAT);
-
- /* create source and destination images */
- m_image_reframe(&copy, src, 0, radius, 0, radius); /* apply clamped margin */
- m_image_create(dest, M_FLOAT, width, height, comp);
-
- /* clear */
- memset(dest->data, 0, dest->size * sizeof(float));
-
- src_data = (float *)copy.data;
- dest_data = (float *)dest->data;
- ystep = width * comp;
-
- #pragma omp parallel for schedule(dynamic, 8)
- for (y=0; y<height; y++) {
-
- float *dest_pixel = dest_data + y * ystep;
- int x;
-
- for (x = 0; x < width; x++) {
-
- float *src_pixel;
- int i, k;
-
- src_pixel = src_data + (y * width + x) * comp;
-
- /* apply kernel */
- for (k = 0; k < size; k++) {
- float v = kernel[k];
- for (i = 0; i < comp; i++)
- dest_pixel[i] += src_pixel[i] * v;
- src_pixel += ystep;
- }
-
- dest_pixel += comp;
- }
- }
-
- m_image_destroy(&copy);
-}
-
-MIAPI void m_image_gaussian_blur(struct m_image *dest, const struct m_image *src, int dx, int dy)
-{
- struct m_image tmp = M_IMAGE_IDENTITY();
- float *kernelx = NULL, *kernely = NULL;
- int kernelx_size = dx * 2 + 1;
- int kernely_size = dy * 2 + 1;
-
- assert(src->size > 0 && src->type == M_FLOAT);
-
- /* exit */
- if (dx == 0 && dy == 0) {
- m_image_copy(dest, src);
- return;
- }
-
- /* x blur */
- if (dx > 0) {
- kernelx = (float *)malloc(kernelx_size * sizeof(float));
- m_gaussian_kernel(kernelx, kernelx_size);
- if (dy > 0)
- m_image_convolution_h(&tmp, src, kernelx, kernelx_size);
- else
- m_image_convolution_h(dest, src, kernelx, kernelx_size);
- }
-
- /* y blur */
- if (dy > 0) {
- kernely = (float *)malloc(kernely_size * sizeof(float));
- m_gaussian_kernel(kernely, kernely_size);
- if (dx > 0)
- m_image_convolution_v(dest, &tmp, kernely, kernely_size);
- else
- m_image_convolution_v(dest, src, kernely, kernely_size);
- }
-
- m_image_destroy(&tmp);
- if(kernely) free(kernely);
- if(kernelx) free(kernelx);
-}
-
-MIAPI void m_image_grey(struct m_image *dest, const struct m_image *src)
-{
- float *src_pixel;
- float *dest_pixel;
- int size = src->size;
- int i, c = src->comp;
-
- assert(src->size > 0 && src->type == M_FLOAT && src->comp > 2);
-
- m_image_create(dest, M_FLOAT, src->width, src->height, 1);
-
- src_pixel = (float *)src->data;
- dest_pixel = (float *)dest->data;
-
- for (i = 0; i < size; i+=c) {
- float v = src_pixel[0] * 0.3f + src_pixel[1] * 0.5f + src_pixel[2] * 0.2f;
- *dest_pixel = v;
- dest_pixel++;
- src_pixel+=c;
- }
-}
-
-MIAPI void m_image_max(struct m_image *dest, const struct m_image *src)
-{
- float *src_pixel;
- float *dest_pixel;
- int size = src->size;
- int i, j, c = src->comp;
-
- assert(src->size > 0 && src->type == M_FLOAT);
-
- m_image_create(dest, M_FLOAT, src->width, src->height, 1);
-
- src_pixel = (float *)src->data;
- dest_pixel = (float *)dest->data;
-
- for (i = 0; i < size; i+=c) {
- float v = src_pixel[0];
- for (j = 1; j < c; j++)
- v = M_MAX(v, src_pixel[j]);
- *dest_pixel = v;
- dest_pixel++;
- src_pixel+=c;
- }
-}
-
-MIAPI void m_image_max_abs(struct m_image *dest, const struct m_image *src)
-{
- float *src_pixel;
- float *dest_pixel;
- int size = src->size;
- int i, j, c = src->comp;
-
- assert(src->size > 0 && src->type == M_FLOAT);
-
- m_image_create(dest, M_FLOAT, src->width, src->height, 1);
-
- src_pixel = (float *)src->data;
- dest_pixel = (float *)dest->data;
-
- for (i = 0; i < size; i+=c) {
- float v = fabsf(src_pixel[0]);
- for (j = 1; j < c; j++)
- v = M_MAX(v, fabsf(src_pixel[j]));
- *dest_pixel = v;
- dest_pixel++;
- src_pixel+=c;
- }
-}
-
-static float m__convolve_pixel(float *data, int width, float *kernel)
-{
- float sum = 0; int i, j;
- for (i = 0; i < 3; i++) {
- float *pixel = data + width * i;
- for (j = 0; j < 3; j++) {
- sum += (*pixel) * (*kernel);
- pixel++;
- kernel++;
- }
- }
- return sum;
-}
-
-MIAPI void m_image_sobel(struct m_image *dest, const struct m_image *src)
-{
- struct m_image copy = M_IMAGE_IDENTITY();
- float ky[9] = {-1, -2, -1, 0, 0, 0, 1, 2, 1};
- float kx[9] = {-1, 0, 1, -2, 0, 2, -1, 0, 1};
- float *src_pixel;
- float *dest_pixel;
- int width = src->width;
- int height = src->height;
- int w2 = width + 2;
- int x, y;
-
- assert(src->size > 0 && src->type == M_FLOAT && src->comp == 1);
-
- /* create source and destination images */
- m_image_reframe(&copy, src, 1, 1, 1, 1); /* apply clamped margin */
- m_image_create(dest, M_FLOAT, width, height, 2);
-
- src_pixel = (float *)copy.data;
- dest_pixel = (float *)dest->data;
-
- for (y = 0; y < height; y++) {
- for (x = 0; x < width; x++) {
- dest_pixel[0] = m__convolve_pixel(src_pixel, w2, kx);
- dest_pixel[1] = m__convolve_pixel(src_pixel, w2, ky);
- src_pixel++;
- dest_pixel += 2;
- }
- src_pixel += 2;
- }
-
- m_image_destroy(&copy);
-}
-
-MIAPI void m_image_harris(struct m_image *dest, const struct m_image *src, int radius)
-{
- struct m_image tmp1 = M_IMAGE_IDENTITY();
- struct m_image tmp2 = M_IMAGE_IDENTITY();
-
- /* sobel */
- m_image_sobel(&tmp1, src);
-
- /* sst */
- m_image_create(&tmp2, M_FLOAT, src->width, src->height, 3);
- m_sst((float *)tmp2.data, (float *)tmp1.data, src->width * src->height);
-
- /* blur */
- m_image_copy(&tmp1, &tmp2);
- m_image_gaussian_blur(&tmp2, &tmp1, radius, radius);
-
- /* harris response */
- m_image_create(dest, M_FLOAT, src->width, src->height, 1);
- m_harris_response((float *)dest->data, (float *)tmp2.data, src->width * src->height);
-
- m_image_destroy(&tmp1);
- m_image_destroy(&tmp2);
-}
-
-#define M_WRITE_PIXEL(dest, x0, y0, v) {*(dest + w * (y0) + (x0)) = v;}
-#define M_PUSH_PIXEL(x2, y2) if((stack_i+3) < stack_size && m__test_pixel(data, w, h, x2, y2, ref)) {\
- stack_i+=2;\
- stack[stack_i] = (unsigned short)(x2);\
- stack[stack_i+1] = (unsigned short)(y2);\
- M_WRITE_PIXEL(data, x2, y2, value);\
-}
-
-static int m__test_pixel(unsigned char *src, int w, int h, int x, int y, unsigned char ref)
-{
- if (! (x >= 0 && x < w && y >= 0 && y < h))
- return 0;
- return (*(src + w * y + x) == ref);
-}
-
-MIAPI int m_image_floodfill_4x(struct m_image *dest, int x, int y, unsigned char ref, unsigned char value, unsigned short *stack, int stack_size)
-{
- unsigned char *data = (unsigned char *)dest->data;
- int w = dest->width;
- int h = dest->height;
- int stack_i = 0;
-
- assert(dest->size > 0 && dest->type == M_UBYTE);
-
- if(! m__test_pixel(data, w, h, x, y, ref))
- return 0;
-
- stack[0] = (unsigned short)x;
- stack[1] = (unsigned short)y;
- M_WRITE_PIXEL(data, x, y, value);
-
- while (stack_i >= 0) {
-
- x = stack[stack_i];
- y = stack[stack_i+1];
- stack_i-=2;
-
- M_PUSH_PIXEL(x + 1, y)
- M_PUSH_PIXEL(x - 1, y)
- M_PUSH_PIXEL(x, y + 1)
- M_PUSH_PIXEL(x, y - 1)
- }
-
- return 1;
-}
-
-MIAPI int m_image_floodfill_8x(struct m_image *dest, int x, int y, unsigned char ref, unsigned char value, unsigned short *stack, int stack_size)
-{
- unsigned char *data = (unsigned char *)dest->data;
- int w = dest->width;
- int h = dest->height;
- int stack_i = 0;
-
- assert(dest->size > 0 && dest->type == M_UBYTE);
-
- if(! m__test_pixel(data, w, h, x, y, ref))
- return 0;
-
- stack[0] = (unsigned short)x;
- stack[1] = (unsigned short)y;
- M_WRITE_PIXEL(data, x, y, value);
-
- while (stack_i >= 0) {
-
- x = stack[stack_i];
- y = stack[stack_i+1];
- stack_i-=2;
-
- M_PUSH_PIXEL(x + 1, y)
- M_PUSH_PIXEL(x - 1, y)
- M_PUSH_PIXEL(x, y + 1)
- M_PUSH_PIXEL(x, y - 1)
- M_PUSH_PIXEL(x + 1, y + 1)
- M_PUSH_PIXEL(x + 1, y - 1)
- M_PUSH_PIXEL(x - 1, y + 1)
- M_PUSH_PIXEL(x - 1, y - 1)
- }
-
- return 1;
-}
-
-#undef M_WRITE_PIXEL
-#undef M_PUSH_PIXEL
-
-static void m__dilate_erode(struct m_image *dest, const struct m_image *src, unsigned char ref, unsigned char value, int copy)
-{
- unsigned char *src_data = (unsigned char *)src->data;
- unsigned char *src_pixel = src_data;
- unsigned char *dest_pixel;
- int w = src->width;
- int h = src->height;
- int y;
-
- assert(src->size > 0 && src->type == M_UBYTE);
-
- m_image_create(dest, M_UBYTE, w, h, 1);
- dest_pixel = (unsigned char *)dest->data;
- if (copy)
- memcpy(dest_pixel, src_data, dest->size * sizeof(char));
- else
- memset(dest_pixel, 0, dest->size * sizeof(char));
-
- for (y=0; y<h; y++) {
-
- int x;
- for (x=0; x<w; x++) {
-
- unsigned char c1, c2, c3, c4, c5;
- c1 = *src_pixel;
-
- if (c1 == ref) {
- c2 = x > 0 ? *(src_data + y * w + (x - 1)) : c1;
- c3 = y > 0 ? *(src_data + (y - 1) * w + x) : c1;
- c4 = (x + 1) < w ? *(src_data + y * w + x + 1) : c1;
- c5 = (y + 1) < h ? *(src_data + (y + 1) * w + x) : c1;
- if (c2 != c1 || c3 != c1 || c4 != c1 || c5 != c1)
- *dest_pixel = value;
- }
-
- src_pixel++;
- dest_pixel++;
- }
- }
-}
-
-MIAPI void m_image_dilate(struct m_image *dest, const struct m_image *src)
-{
- m__dilate_erode(dest, src, 0, 255, 1);
-}
-
-MIAPI void m_image_erode(struct m_image *dest, const struct m_image *src)
-{
- m__dilate_erode(dest, src, 255, 0, 1);
-}
-
-MIAPI void m_image_edge_4x(struct m_image *dest, const struct m_image *src, unsigned char ref)
-{
- m__dilate_erode(dest, src, ref, 255, 0);
-}
-
-/* Following C code from the article
- "Efficient Binary Image Thinning using Neighborhood Maps"
- by Joseph M. Cychosz, in "Graphics Gems IV", Academic Press, 1994
- Thins the image using Rosenfeld's parallel thinning algorithm.
-*/
-
-/* Direction m__masks:
- N S W E
-*/
-static int m__masks[] = {0200, 0002, 0040, 0010};
-
-/* True if pixel neighbor map indicates the pixel is 8-simple and
- not an end point and thus can be deleted. The neighborhood
- map is defined as an integer of bits abcdefghi with a non-zero
- bit representing a non-zero pixel. The bit assignment for the
- neighborhood is:
-
- a b c
- d e f
- g h i
-*/
-static unsigned char m__delete_map[512] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
-
-MIAPI void m_image_thin(struct m_image *dest)
-{
- unsigned char *data; /* image data */
- unsigned char ** ip; /* scanline pointers, ip[y][x] */
- unsigned char * qb; /* Neighborhood maps of previous scanline */
- int xsize, ysize; /* Image resolution */
- int x, y; /* Pixel location */
- int i; /* Pass index */
- int pc = 0; /* Pass count */
- int count = 1; /* Deleted pixel count */
- int p, q; /* Neighborhood maps of adjacent cells */
- int m; /* Deletion direction mask */
-
- assert(dest->size > 0 && dest->type == M_UBYTE);
-
- data = (unsigned char *)dest->data;
- xsize = dest->width;
- ysize = dest->height;
-
- qb = (unsigned char *)malloc(xsize * sizeof(char));
- qb[xsize-1] = 0; /* Used for lower-right pixel */
-
- /* alloc scanline pointers */
- ip = (unsigned char **)malloc(sizeof(void *) * ysize);
-
- /* set scanline pointers */
- for (y=0; y<ysize; y++) {
- ip[y] = data + y*xsize;
- }
-
- while (count) { /* Scan image while deletions */
-
- pc++;
- count = 0;
-
- for (i=0; i<4; i++) {
-
- m = m__masks[i];
-
- /* Build initial previous scan buffer */
- p = ip[0][0] != 0;
- for (x=0; x<xsize-1; x++) {
- p = ((p<<1)&0006) | (ip[0][x+1] != 0);
- qb[x] = (unsigned char)p;
- }
-
- /* Scan image for pixel deletion candidates */
- for (y=0; y<ysize-1; y++) {
- q = qb[0];
- p = ((q<<3)&0110) | (ip[y+1][0] != 0);
-
- for (x=0; x<xsize-1; x++) {
- q = qb[x];
- p = ((p<<1)&0666) | ((q<<3)&0110) | (ip[y+1][x+1] != 0);
- qb[x] = (unsigned char)p;
-
- if (((p&m) == 0) && m__delete_map[p]) {
- if (ip[y][x] != 0) {
- count++;
- ip[y][x] = 0;
- }
- }
- }
-
- /* Process right edge pixel */
- p = (p<<1)&0666;
- if ((p&m) == 0 && m__delete_map[p]) {
- if (ip[y][xsize-1] != 0) {
- count++;
- ip[y][xsize-1] = 0;
- }
- }
- }
-
- /* Process bottom scan line */
- for (x=0; x<xsize; x++) {
- q = qb[x];
- p = ((p<<1)&0666) | ((q<<3)&0110);
- if ((p&m) == 0 && m__delete_map[p]) {
- if (ip[ysize-1][x] != 0) {
- count++;
- ip[ysize-1][x] = 0;
- }
- }
- }
- }
- }
-
- free(qb);
- free(ip);
-}
-
-MIAPI void m_image_non_max_supp(struct m_image *dest, const struct m_image *src, int radius, float threshold)
-{
- float *src_data, *dest_data;
- float *src_pixel, *dest_pixel;
- int width = src->width;
- int height = src->height;
- int x, y;
-
- assert(src->size > 0 && src->type == M_FLOAT && src->comp == 1);
-
- m_image_copy(dest, src);
-
- src_data = (float *)src->data;
- dest_data = (float *)dest->data;
- src_pixel = src_data;
- dest_pixel = dest_data;
-
- for (y = 0; y < height; y++)
- for (x = 0; x < width; x++) {
-
- int minx, miny, maxx, maxy, xx, yy;
-
- if (*src_pixel < threshold) {
- *dest_pixel = 0;
- goto end;
- }
-
- minx = M_MAX(0, x - radius);
- miny = M_MAX(0, y - radius);
- maxx = M_MIN(width - 1, x + radius);
- maxy = M_MIN(height - 1, y + radius);
-
- for (yy = miny; yy <= maxy; yy++)
- for (xx = minx; xx <= maxx; xx++) {
-
- float *src_pixel2 = src_data + yy*width + xx;
- if (*src_pixel2 > *src_pixel) {
- *dest_pixel = 0;
- goto end;
- }
- }
-
- end:
- src_pixel++;
- dest_pixel++;
- }
-}
-
-MIAPI int m_image_corner_harris(const struct m_image *src, int margin, int radius, float threshold, int *corners, int max_count)
-{
- struct m_image harris = M_IMAGE_IDENTITY();
- struct m_image nms = M_IMAGE_IDENTITY();
- float *pixel;
- int width = src->width;
- int height = src->height;
- int wm = width - margin;
- int hm = height - margin;
- int x, y, count;
-
- if (width <= (margin * 2) || height <= (margin * 2))
- return 0;
-
- m_image_harris(&harris, src, radius);
- m_image_non_max_supp(&nms, &harris, radius, threshold);
-
- count = 0;
- pixel = (float *)nms.data;
-
- for (y = 0; y < height; y++)
- for (x = 0; x < width; x++) {
-
- if (count == max_count)
- goto end;
-
- if ((*pixel) > 0 && x >= margin && y >= margin && x < wm && y < hm) {
- corners[count*2] = x;
- corners[count*2+1] = y;
- count++;
- }
- pixel++;
- }
-
- end:
- m_image_destroy(&nms);
- m_image_destroy(&harris);
- return count;
-}
-
-MIAPI void m_image_sub_pixel(const struct m_image *src, float x, float y, float *result)
-{
- float *colors0, *colors1, *colors2, *colors3;
- float *src_data = (float *)src->data;
- int width = src->width;
- int height = src->height;
- int comp = src->comp;
- int c;
- float fx, fy;
- int wm = width - 1;
- int hm = height - 1;
- int ix, iy, ix2, iy2;
-
- ix = (int)x;
- iy = (int)y;
- fx = x - (float)ix;
- fy = y - (float)iy;
- fx = M_MAX(fx, 0);
- fy = M_MAX(fy, 0);
-
- ix = M_CLAMP(ix, 0, wm);
- iy = M_CLAMP(iy, 0, hm);
- ix2 = ix + 1;
- iy2 = iy + 1;
- ix2 = M_MIN(ix2, wm);
- iy2 = M_MIN(iy2, hm);
-
- colors0 = src_data + (width * iy + ix) * comp;
- colors1 = src_data + (width * iy + ix2) * comp;
- colors2 = src_data + (width * iy2 + ix) * comp;
- colors3 = src_data + (width * iy2 + ix2) * comp;
-
- for(c = 0; c < comp; c++) {
- float A = colors0[c] + (colors2[c] - colors0[c]) * fy;
- float B = colors1[c] + (colors3[c] - colors1[c]) * fy;
- result[c] = A + (B - A) * fx;
- }
-}
-
-/* slow TODO better */
-static void m__bilinear(struct m_image *dest, const struct m_image *src, float dx, float dy, float offset)
-{
- float *dest_data = (float *)dest->data;
- int width = dest->width;
- int height = dest->height;
- int comp = src->comp;
- int y, ystep = width * comp;
-
- #pragma omp parallel for schedule(dynamic, 8)
- for (y = 0; y < height; y++) {
- float *dest_pixel = dest_data + y * ystep; int x;
- for (x = 0; x < width; x++) {
- m_image_sub_pixel(src, ((float)x + 0.5f) * dx + offset, ((float)y + 0.5f) * dy + offset, dest_pixel);
- dest_pixel += comp;
- }
- }
-}
-
-MIAPI void m_image_pyrdown(struct m_image *dest, const struct m_image *src)
-{
- struct m_image tmp = M_IMAGE_IDENTITY();
- float *src_data;
- float *dest_pixel;
- int width = src->width;
- int height = src->height;
- int comp = src->comp;
- int comp2 = comp * 2;
- int ystep = width * comp * 2;
- int w2 = width / 2;
- int h2 = height / 2;
- int x, y, i;
-
- m_image_gaussian_blur(&tmp, src, 1, 1);
- m_image_create(dest, M_FLOAT, w2, h2, comp);
-
- src_data = (float *)tmp.data;
- dest_pixel = (float *)dest->data;
-
- for (y = 0; y < h2; y++) {
- float *src_pixel = src_data + y * ystep;
- for (x = 0; x < w2; x++) {
- for (i = 0; i < comp; i++)
- dest_pixel[i] = src_pixel[i];
- dest_pixel += comp;
- src_pixel += comp2;
- }
- }
-
- m_image_destroy(&tmp);
-}
-
-MIAPI void m_image_resize(struct m_image *dest, const struct m_image *src, int new_width, int new_height)
-{
- struct m_image tmp = M_IMAGE_IDENTITY();
- int width = src->width;
- int height = src->height;
- int comp = src->comp;
- float rx = (float)width / (float)new_width;
- float ry = (float)height / (float)new_height;
-
- assert(src->size > 0 && src->type == M_FLOAT);
- m_image_create(dest, M_FLOAT, new_width, new_height, comp);
-
- if (new_width < width || new_height < height) {
- float r = M_MAX(rx, ry);
- int ir = (int)r - 1;
- if (ir > 0) {
- m_image_gaussian_blur(&tmp, src, ir, ir);
- m__bilinear(dest, &tmp, rx, ry, -0.5f);
- }
- else {
- m__bilinear(dest, src, rx, ry, -0.5f);
- }
- }
- else {
- m__bilinear(dest, src, rx, ry, -0.5f);
- }
-
- m_image_destroy(&tmp);
-}
-
-#endif /* M_IMAGE_IMPLEMENTATION */
diff --git a/3rdparty/bimg/3rdparty/nvtt/nvcore/posh.h b/3rdparty/bimg/3rdparty/nvtt/nvcore/posh.h
index b597125f9ac..acf8bc975e7 100644
--- a/3rdparty/bimg/3rdparty/nvtt/nvcore/posh.h
+++ b/3rdparty/bimg/3rdparty/nvtt/nvcore/posh.h
@@ -490,12 +490,12 @@ LLVM:
# define POSH_CPU_SPARC 1
#endif
-#if defined ARM || defined __arm__ || defined _ARM
+#if defined ARM || defined __arm__ || defined _ARM || defined _M_ARM
# define POSH_CPU_STRONGARM 1
# define POSH_CPU_STRING "ARM"
#endif
-#if defined __aarch64__
+#if defined __aarch64__ || defined _M_ARM64
# define POSH_CPU_AARCH64 1
# define POSH_CPU_STRING "ARM64"
#endif
diff --git a/3rdparty/bimg/3rdparty/nvtt/nvtt.cpp b/3rdparty/bimg/3rdparty/nvtt/nvtt.cpp
index 0cc0df481eb..2ef0e104e4b 100644
--- a/3rdparty/bimg/3rdparty/nvtt/nvtt.cpp
+++ b/3rdparty/bimg/3rdparty/nvtt/nvtt.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2011-2018 Branimir Karadzic. All rights reserved.
+ * Copyright 2011-2019 Branimir Karadzic. All rights reserved.
* License: https://github.com/bkaradzic/bimg#license-bsd-2-clause
*/
diff --git a/3rdparty/bimg/3rdparty/tinyexr/tinyexr.h b/3rdparty/bimg/3rdparty/tinyexr/tinyexr.h
index 3255bbc7350..cda32f456c9 100644
--- a/3rdparty/bimg/3rdparty/tinyexr/tinyexr.h
+++ b/3rdparty/bimg/3rdparty/tinyexr/tinyexr.h
@@ -274,6 +274,12 @@ extern int LoadEXR(float **out_rgba, int *width, int *height,
const char *filename, const char **err);
// @deprecated { to be removed. }
+// Simple wrapper API for ParseEXRHeaderFromFile.
+// checking given file is a EXR file(by just look up header)
+// @return TINYEXR_SUCCEES for EXR image, TINYEXR_ERROR_INVALID_HEADER for others
+extern int IsEXR(const char *filename);
+
+// @deprecated { to be removed. }
// Saves single-frame OpenEXR image. Assume EXR image contains RGB(A) channels.
// components must be 1(Grayscale), 3(RGB) or 4(RGBA).
// Input image format is: `float x width x height`, or `float x RGB(A) x width x
@@ -7012,6 +7018,10 @@ static void swap2(unsigned short *val) {
#endif
}
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-function"
+#endif
static void cpy4(int *dst_val, const int *src_val) {
unsigned char *dst = reinterpret_cast<unsigned char *>(dst_val);
const unsigned char *src = reinterpret_cast<const unsigned char *>(src_val);
@@ -7042,6 +7052,10 @@ static void cpy4(float *dst_val, const float *src_val) {
dst[3] = src[3];
}
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
static void swap4(unsigned int *val) {
#ifdef MINIZ_LITTLE_ENDIAN
(void)val;
@@ -8854,7 +8868,8 @@ static bool getCode(int po, int rlc, long long &c, int &lc, const char *&in,
if (out + cs > oe) return false;
// Bounds check for safety
- if ((out - 1) <= ob) return false;
+ // Issue 100.
+ if ((out - 1) < ob) return false;
unsigned short s = out[-1];
while (cs-- > 0) *out++ = s;
@@ -10735,6 +10750,15 @@ static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header,
int data_width = exr_header->data_window[2] - exr_header->data_window[0] + 1;
int data_height = exr_header->data_window[3] - exr_header->data_window[1] + 1;
+ if ((data_width < 0) || (data_height < 0)) {
+ if (err) {
+ std::stringstream ss;
+ ss << "Invalid data width or data height: " << data_width << ", " << data_height << std::endl;
+ (*err) += ss.str();
+ }
+ return TINYEXR_ERROR_INVALID_DATA;
+ }
+
size_t num_blocks = offsets.size();
std::vector<size_t> channel_offset_list;
@@ -10830,6 +10854,17 @@ static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header,
}
} else { // scanline format
+ // Don't allow too large image(256GB * pixel_data_size or more). Workaround for #104.
+ size_t data_len0 = size_t(data_width) * size_t(data_height) * size_t(num_channels);
+ if ((data_len0 == 0) || (data_len0 >= 0x4000000000)) {
+ if (err) {
+ std::stringstream ss;
+ ss << "Image data size is zero or too large: width = " << data_width << ", height = " << data_height << ", channels = " << num_channels << std::endl;
+ (*err) += ss.str();
+ }
+ return TINYEXR_ERROR_INVALID_DATA;
+ }
+
exr_image->images = tinyexr::AllocateImage(
num_channels, exr_header->channels, exr_header->requested_pixel_types,
data_width, data_height);
@@ -11161,21 +11196,52 @@ int LoadEXR(float **out_rgba, int *width, int *height, const char *filename,
}
}
- if ((idxA == 0) && (idxR == -1) && (idxG == -1) && (idxB == -1)) {
- // Alpha channel only.
+ if (exr_header.num_channels == 1) {
+ // Grayscale channel only.
- if (exr_header.tiled) {
- // todo.implement this
- }
(*out_rgba) = reinterpret_cast<float *>(
malloc(4 * sizeof(float) * static_cast<size_t>(exr_image.width) *
static_cast<size_t>(exr_image.height)));
- for (int i = 0; i < exr_image.width * exr_image.height; i++) {
- const float val = reinterpret_cast<float **>(exr_image.images)[0][i];
- (*out_rgba)[4 * i + 0] = val;
- (*out_rgba)[4 * i + 1] = val;
- (*out_rgba)[4 * i + 2] = val;
- (*out_rgba)[4 * i + 3] = val;
+
+ if (exr_header.tiled) {
+
+ for (int it = 0; it < exr_image.num_tiles; it++) {
+ for (int j = 0; j < exr_header.tile_size_y; j++) {
+ for (int i = 0; i < exr_header.tile_size_x; i++) {
+ const int ii =
+ exr_image.tiles[it].offset_x * exr_header.tile_size_x + i;
+ const int jj =
+ exr_image.tiles[it].offset_y * exr_header.tile_size_y + j;
+ const int idx = ii + jj * exr_image.width;
+
+ // out of region check.
+ if (ii >= exr_image.width) {
+ continue;
+ }
+ if (jj >= exr_image.height) {
+ continue;
+ }
+ const int srcIdx = i + j * exr_header.tile_size_x;
+ unsigned char **src = exr_image.tiles[it].images;
+ (*out_rgba)[4 * idx + 0] =
+ reinterpret_cast<float **>(src)[0][srcIdx];
+ (*out_rgba)[4 * idx + 1] =
+ reinterpret_cast<float **>(src)[0][srcIdx];
+ (*out_rgba)[4 * idx + 2] =
+ reinterpret_cast<float **>(src)[0][srcIdx];
+ (*out_rgba)[4 * idx + 3] =
+ reinterpret_cast<float **>(src)[0][srcIdx];
+ }
+ }
+ }
+ } else {
+ for (int i = 0; i < exr_image.width * exr_image.height; i++) {
+ const float val = reinterpret_cast<float **>(exr_image.images)[0][i];
+ (*out_rgba)[4 * i + 0] = val;
+ (*out_rgba)[4 * i + 1] = val;
+ (*out_rgba)[4 * i + 2] = val;
+ (*out_rgba)[4 * i + 3] = val;
+ }
}
} else {
// Assume RGB(A)
@@ -11207,7 +11273,7 @@ int LoadEXR(float **out_rgba, int *width, int *height, const char *filename,
static_cast<size_t>(exr_image.height)));
if (exr_header.tiled) {
for (int it = 0; it < exr_image.num_tiles; it++) {
- for (int j = 0; j < exr_header.tile_size_y; j++)
+ for (int j = 0; j < exr_header.tile_size_y; j++) {
for (int i = 0; i < exr_header.tile_size_x; i++) {
const int ii =
exr_image.tiles[it].offset_x * exr_header.tile_size_x + i;
@@ -11237,6 +11303,7 @@ int LoadEXR(float **out_rgba, int *width, int *height, const char *filename,
(*out_rgba)[4 * idx + 3] = 1.0;
}
}
+ }
}
} else {
for (int i = 0; i < exr_image.width * exr_image.height; i++) {
@@ -11265,6 +11332,17 @@ int LoadEXR(float **out_rgba, int *width, int *height, const char *filename,
return TINYEXR_SUCCESS;
}
+int IsEXR(const char *filename) {
+ EXRVersion exr_version;
+
+ int ret = ParseEXRVersionFromFile(&exr_version, filename);
+ if (ret != TINYEXR_SUCCESS) {
+ return TINYEXR_ERROR_INVALID_HEADER;
+ }
+
+ return TINYEXR_SUCCESS;
+}
+
int ParseEXRHeaderFromMemory(EXRHeader *exr_header, const EXRVersion *version,
const unsigned char *memory, size_t size,
const char **err) {
@@ -11361,75 +11439,128 @@ int LoadEXRFromMemory(float **out_rgba, int *width, int *height,
}
}
- if (idxR == -1) {
- tinyexr::SetErrorMessage("R channel not found", err);
-
- // @todo { free exr_image }
- return TINYEXR_ERROR_INVALID_DATA;
- }
-
- if (idxG == -1) {
- tinyexr::SetErrorMessage("G channel not found", err);
- // @todo { free exr_image }
- return TINYEXR_ERROR_INVALID_DATA;
- }
+ // TODO(syoyo): Refactor removing same code as used in LoadEXR().
+ if (exr_header.num_channels == 1) {
+ // Grayscale channel only.
- if (idxB == -1) {
- tinyexr::SetErrorMessage("B channel not found", err);
- // @todo { free exr_image }
- return TINYEXR_ERROR_INVALID_DATA;
- }
+ (*out_rgba) = reinterpret_cast<float *>(
+ malloc(4 * sizeof(float) * static_cast<size_t>(exr_image.width) *
+ static_cast<size_t>(exr_image.height)));
- (*out_rgba) = reinterpret_cast<float *>(
- malloc(4 * sizeof(float) * static_cast<size_t>(exr_image.width) *
- static_cast<size_t>(exr_image.height)));
+ if (exr_header.tiled) {
- if (exr_header.tiled) {
- for (int it = 0; it < exr_image.num_tiles; it++) {
- for (int j = 0; j < exr_header.tile_size_y; j++)
- for (int i = 0; i < exr_header.tile_size_x; i++) {
- const int ii =
- exr_image.tiles[it].offset_x * exr_header.tile_size_x + i;
- const int jj =
- exr_image.tiles[it].offset_y * exr_header.tile_size_y + j;
- const int idx = ii + jj * exr_image.width;
+ for (int it = 0; it < exr_image.num_tiles; it++) {
+ for (int j = 0; j < exr_header.tile_size_y; j++) {
+ for (int i = 0; i < exr_header.tile_size_x; i++) {
+ const int ii =
+ exr_image.tiles[it].offset_x * exr_header.tile_size_x + i;
+ const int jj =
+ exr_image.tiles[it].offset_y * exr_header.tile_size_y + j;
+ const int idx = ii + jj * exr_image.width;
- // out of region check.
- if (ii >= exr_image.width) {
- continue;
- }
- if (jj >= exr_image.height) {
- continue;
- }
- const int srcIdx = i + j * exr_header.tile_size_x;
- unsigned char **src = exr_image.tiles[it].images;
- (*out_rgba)[4 * idx + 0] =
- reinterpret_cast<float **>(src)[idxR][srcIdx];
- (*out_rgba)[4 * idx + 1] =
- reinterpret_cast<float **>(src)[idxG][srcIdx];
- (*out_rgba)[4 * idx + 2] =
- reinterpret_cast<float **>(src)[idxB][srcIdx];
- if (idxA != -1) {
+ // out of region check.
+ if (ii >= exr_image.width) {
+ continue;
+ }
+ if (jj >= exr_image.height) {
+ continue;
+ }
+ const int srcIdx = i + j * exr_header.tile_size_x;
+ unsigned char **src = exr_image.tiles[it].images;
+ (*out_rgba)[4 * idx + 0] =
+ reinterpret_cast<float **>(src)[0][srcIdx];
+ (*out_rgba)[4 * idx + 1] =
+ reinterpret_cast<float **>(src)[0][srcIdx];
+ (*out_rgba)[4 * idx + 2] =
+ reinterpret_cast<float **>(src)[0][srcIdx];
(*out_rgba)[4 * idx + 3] =
- reinterpret_cast<float **>(src)[idxA][srcIdx];
- } else {
- (*out_rgba)[4 * idx + 3] = 1.0;
+ reinterpret_cast<float **>(src)[0][srcIdx];
}
}
+ }
+ } else {
+ for (int i = 0; i < exr_image.width * exr_image.height; i++) {
+ const float val = reinterpret_cast<float **>(exr_image.images)[0][i];
+ (*out_rgba)[4 * i + 0] = val;
+ (*out_rgba)[4 * i + 1] = val;
+ (*out_rgba)[4 * i + 2] = val;
+ (*out_rgba)[4 * i + 3] = val;
+ }
}
+
} else {
- for (int i = 0; i < exr_image.width * exr_image.height; i++) {
- (*out_rgba)[4 * i + 0] =
- reinterpret_cast<float **>(exr_image.images)[idxR][i];
- (*out_rgba)[4 * i + 1] =
- reinterpret_cast<float **>(exr_image.images)[idxG][i];
- (*out_rgba)[4 * i + 2] =
- reinterpret_cast<float **>(exr_image.images)[idxB][i];
- if (idxA != -1) {
- (*out_rgba)[4 * i + 3] =
- reinterpret_cast<float **>(exr_image.images)[idxA][i];
- } else {
- (*out_rgba)[4 * i + 3] = 1.0;
+ // TODO(syoyo): Support non RGBA image.
+
+ if (idxR == -1) {
+ tinyexr::SetErrorMessage("R channel not found", err);
+
+ // @todo { free exr_image }
+ return TINYEXR_ERROR_INVALID_DATA;
+ }
+
+ if (idxG == -1) {
+ tinyexr::SetErrorMessage("G channel not found", err);
+ // @todo { free exr_image }
+ return TINYEXR_ERROR_INVALID_DATA;
+ }
+
+ if (idxB == -1) {
+ tinyexr::SetErrorMessage("B channel not found", err);
+ // @todo { free exr_image }
+ return TINYEXR_ERROR_INVALID_DATA;
+ }
+
+ (*out_rgba) = reinterpret_cast<float *>(
+ malloc(4 * sizeof(float) * static_cast<size_t>(exr_image.width) *
+ static_cast<size_t>(exr_image.height)));
+
+ if (exr_header.tiled) {
+ for (int it = 0; it < exr_image.num_tiles; it++) {
+ for (int j = 0; j < exr_header.tile_size_y; j++)
+ for (int i = 0; i < exr_header.tile_size_x; i++) {
+ const int ii =
+ exr_image.tiles[it].offset_x * exr_header.tile_size_x + i;
+ const int jj =
+ exr_image.tiles[it].offset_y * exr_header.tile_size_y + j;
+ const int idx = ii + jj * exr_image.width;
+
+ // out of region check.
+ if (ii >= exr_image.width) {
+ continue;
+ }
+ if (jj >= exr_image.height) {
+ continue;
+ }
+ const int srcIdx = i + j * exr_header.tile_size_x;
+ unsigned char **src = exr_image.tiles[it].images;
+ (*out_rgba)[4 * idx + 0] =
+ reinterpret_cast<float **>(src)[idxR][srcIdx];
+ (*out_rgba)[4 * idx + 1] =
+ reinterpret_cast<float **>(src)[idxG][srcIdx];
+ (*out_rgba)[4 * idx + 2] =
+ reinterpret_cast<float **>(src)[idxB][srcIdx];
+ if (idxA != -1) {
+ (*out_rgba)[4 * idx + 3] =
+ reinterpret_cast<float **>(src)[idxA][srcIdx];
+ } else {
+ (*out_rgba)[4 * idx + 3] = 1.0;
+ }
+ }
+ }
+ } else {
+ for (int i = 0; i < exr_image.width * exr_image.height; i++) {
+ (*out_rgba)[4 * i + 0] =
+ reinterpret_cast<float **>(exr_image.images)[idxR][i];
+ (*out_rgba)[4 * i + 1] =
+ reinterpret_cast<float **>(exr_image.images)[idxG][i];
+ (*out_rgba)[4 * i + 2] =
+ reinterpret_cast<float **>(exr_image.images)[idxB][i];
+ if (idxA != -1) {
+ (*out_rgba)[4 * i + 3] =
+ reinterpret_cast<float **>(exr_image.images)[idxA][i];
+ } else {
+ (*out_rgba)[4 * i + 3] = 1.0;
+ }
}
}
}
diff --git a/3rdparty/bimg/LICENSE b/3rdparty/bimg/LICENSE
index 3df43e30a2a..1c8e1e05472 100644
--- a/3rdparty/bimg/LICENSE
+++ b/3rdparty/bimg/LICENSE
@@ -1,4 +1,4 @@
-Copyright 2010-2018 Branimir Karadzic. All rights reserved.
+Copyright 2010-2019 Branimir Karadzic
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
@@ -10,13 +10,13 @@ are permitted provided that the following conditions are met:
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
-THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS OR
-IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
-SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/3rdparty/bimg/README.md b/3rdparty/bimg/README.md
index f918ab634d4..9b8ff790f80 100644
--- a/3rdparty/bimg/README.md
+++ b/3rdparty/bimg/README.md
@@ -23,27 +23,25 @@ https://github.com/bkaradzic/bimg
<img align="right" src="http://opensource.org/trademarks/opensource/OSI-Approved-License-100x137.png">
</a>
- Copyright 2010-2018 Branimir Karadzic. All rights reserved.
+ Copyright 2010-2019 Branimir Karadzic
- https://github.com/bkaradzic/bimg
+ Redistribution and use in source and binary forms, with or without modification,
+ are permitted provided that the following conditions are met:
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
-
- 1. Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
+ 1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
- THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS OR
- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
- EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
- OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/3rdparty/bimg/include/bimg/bimg.h b/3rdparty/bimg/include/bimg/bimg.h
index 337bce8afd7..495db75f218 100644
--- a/3rdparty/bimg/include/bimg/bimg.h
+++ b/3rdparty/bimg/include/bimg/bimg.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2011-2018 Branimir Karadzic. All rights reserved.
+ * Copyright 2011-2019 Branimir Karadzic. All rights reserved.
* License: https://github.com/bkaradzic/bimg#license-bsd-2-clause
*/
@@ -425,6 +425,7 @@ namespace bimg
, uint32_t _height
, uint32_t _depth
, uint32_t _srcPitch
+ , uint32_t _dstPitch
);
///
diff --git a/3rdparty/bimg/include/bimg/decode.h b/3rdparty/bimg/include/bimg/decode.h
index d092b2f7ec7..63944f96318 100644
--- a/3rdparty/bimg/include/bimg/decode.h
+++ b/3rdparty/bimg/include/bimg/decode.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2011-2018 Branimir Karadzic. All rights reserved.
+ * Copyright 2011-2019 Branimir Karadzic. All rights reserved.
* License: https://github.com/bkaradzic/bimg#license-bsd-2-clause
*/
diff --git a/3rdparty/bimg/include/bimg/encode.h b/3rdparty/bimg/include/bimg/encode.h
index 326728d2700..1c7abff5f09 100644
--- a/3rdparty/bimg/include/bimg/encode.h
+++ b/3rdparty/bimg/include/bimg/encode.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2011-2018 Branimir Karadzic. All rights reserved.
+ * Copyright 2011-2019 Branimir Karadzic. All rights reserved.
* License: https://github.com/bkaradzic/bimg#license-bsd-2-clause
*/
@@ -18,6 +18,10 @@ namespace bimg
Highest,
Fastest,
+ NormalMapDefault, // Treat the source as a normal map
+ NormalMapHighest,
+ NormalMapFastest,
+
Count
};
};
@@ -113,6 +117,7 @@ namespace bimg
, const void* _src
, float _alphaRef
, float _scale = 1.0f
+ , uint32_t _upscale = 1
);
///
@@ -124,6 +129,7 @@ namespace bimg
, void* _src
, float _coverage
, float _alphaRef
+ , uint32_t _upscale = 1
);
///
diff --git a/3rdparty/bimg/makefile b/3rdparty/bimg/makefile
index cd11a89f7b6..fccad9729d4 100644
--- a/3rdparty/bimg/makefile
+++ b/3rdparty/bimg/makefile
@@ -1,5 +1,5 @@
#
-# Copyright 2011-2018 Branimir Karadzic. All rights reserved.
+# Copyright 2011-2019 Branimir Karadzic. All rights reserved.
# License: https://github.com/bkaradzic/bimg#license-bsd-2-clause
#
@@ -47,6 +47,7 @@ projgen: ## Generate project files for all configurations.
$(GENIE) --with-tools --xcode=ios xcode8
$(GENIE) --gcc=freebsd gmake
$(GENIE) --gcc=android-arm gmake
+ $(GENIE) --gcc=android-arm64 gmake
$(GENIE) --gcc=android-mips gmake
$(GENIE) --gcc=android-x86 gmake
$(GENIE) --gcc=asmjs gmake
@@ -64,6 +65,14 @@ android-arm-release: .build/projects/gmake-android-arm ## Build - Android ARM Re
$(MAKE) -R -C .build/projects/gmake-android-arm config=release
android-arm: android-arm-debug android-arm-release ## Build - Android ARM Debug and Release
+.build/projects/gmake-android-arm64:
+ $(GENIE) --gcc=android-arm64 gmake
+android-arm64-debug: .build/projects/gmake-android-arm64 ## Build - Android ARM64 Debug
+ $(MAKE) -R -C .build/projects/gmake-android-arm64 config=debug
+android-arm64-release: .build/projects/gmake-android-arm64 ## Build - Android ARM64 Release
+ $(MAKE) -R -C .build/projects/gmake-android-arm64 config=release
+android-arm64: android-arm64-debug android-arm64-release ## Build - Android ARM64 Debug and Release
+
.build/projects/gmake-android-mips:
$(GENIE) --gcc=android-mips gmake
android-mips-debug: .build/projects/gmake-android-mips ## Build - Android MIPS Debug
diff --git a/3rdparty/bimg/scripts/bimg.lua b/3rdparty/bimg/scripts/bimg.lua
index a70460ef092..6ea0958cb6a 100644
--- a/3rdparty/bimg/scripts/bimg.lua
+++ b/3rdparty/bimg/scripts/bimg.lua
@@ -1,30 +1,36 @@
--
--- Copyright 2010-2018 Branimir Karadzic. All rights reserved.
+-- Copyright 2010-2019 Branimir Karadzic. All rights reserved.
-- License: https://github.com/bkaradzic/bx#license-bsd-2-clause
--
-function filesexist(_srcPath, _dstPath, _files)
- for _, file in ipairs(_files) do
- file = path.getrelative(_srcPath, file)
- local filePath = path.join(_dstPath, file)
- if not os.isfile(filePath) then return false end
- end
-
- return true
-end
-
project "bimg"
kind "StaticLib"
includedirs {
path.join(BX_DIR, "include"),
path.join(BIMG_DIR, "include"),
+ path.join(BIMG_DIR, "3rdparty/astc-codec"),
+ path.join(BIMG_DIR, "3rdparty/astc-codec/include"),
}
+ local ASTC_CODEC_DIR = path.join(BIMG_DIR, "3rdparty/astc-codec")
+
files {
path.join(BIMG_DIR, "include/**"),
path.join(BIMG_DIR, "src/image.*"),
path.join(BIMG_DIR, "src/image_gnf.cpp"),
+
+ path.join(ASTC_CODEC_DIR, "src/decoder/astc_file.*"),
+ path.join(ASTC_CODEC_DIR, "src/decoder/codec.*"),
+ path.join(ASTC_CODEC_DIR, "src/decoder/endpoint_codec.*"),
+ path.join(ASTC_CODEC_DIR, "src/decoder/footprint.*"),
+ path.join(ASTC_CODEC_DIR, "src/decoder/integer_sequence_codec.*"),
+ path.join(ASTC_CODEC_DIR, "src/decoder/intermediate_astc_block.*"),
+ path.join(ASTC_CODEC_DIR, "src/decoder/logical_astc_block.*"),
+ path.join(ASTC_CODEC_DIR, "src/decoder/partition.*"),
+ path.join(ASTC_CODEC_DIR, "src/decoder/physical_astc_block.*"),
+ path.join(ASTC_CODEC_DIR, "src/decoder/quantization.*"),
+ path.join(ASTC_CODEC_DIR, "src/decoder/weight_infill.*"),
}
configuration { "linux-*" }
diff --git a/3rdparty/bimg/scripts/bimg_decode.lua b/3rdparty/bimg/scripts/bimg_decode.lua
index ca045ed1b92..67c8c43f094 100644
--- a/3rdparty/bimg/scripts/bimg_decode.lua
+++ b/3rdparty/bimg/scripts/bimg_decode.lua
@@ -1,5 +1,5 @@
--
--- Copyright 2010-2018 Branimir Karadzic. All rights reserved.
+-- Copyright 2010-2019 Branimir Karadzic. All rights reserved.
-- License: https://github.com/bkaradzic/bx#license-bsd-2-clause
--
@@ -10,8 +10,6 @@ project "bimg_decode"
path.join(BX_DIR, "include"),
path.join(BIMG_DIR, "include"),
path.join(BIMG_DIR, "3rdparty"),
- path.join(BIMG_DIR, "3rdparty/nvtt"),
- path.join(BIMG_DIR, "3rdparty/iqa/include"),
}
files {
diff --git a/3rdparty/bimg/scripts/bimg_encode.lua b/3rdparty/bimg/scripts/bimg_encode.lua
index 06649108b82..61544200345 100644
--- a/3rdparty/bimg/scripts/bimg_encode.lua
+++ b/3rdparty/bimg/scripts/bimg_encode.lua
@@ -1,5 +1,5 @@
--
--- Copyright 2010-2018 Branimir Karadzic. All rights reserved.
+-- Copyright 2010-2019 Branimir Karadzic. All rights reserved.
-- License: https://github.com/bkaradzic/bx#license-bsd-2-clause
--
diff --git a/3rdparty/bimg/scripts/genie.lua b/3rdparty/bimg/scripts/genie.lua
index 93acdfb712f..1a5171599da 100644
--- a/3rdparty/bimg/scripts/genie.lua
+++ b/3rdparty/bimg/scripts/genie.lua
@@ -1,5 +1,5 @@
--
--- Copyright 2010-2018 Branimir Karadzic. All rights reserved.
+-- Copyright 2010-2019 Branimir Karadzic. All rights reserved.
-- License: https://github.com/bkaradzic/bimg#license-bsd-2-clause
--
diff --git a/3rdparty/bimg/scripts/texturec.lua b/3rdparty/bimg/scripts/texturec.lua
index 9bb3ef63649..b094a7d8ab2 100644
--- a/3rdparty/bimg/scripts/texturec.lua
+++ b/3rdparty/bimg/scripts/texturec.lua
@@ -1,5 +1,5 @@
--
--- Copyright 2010-2018 Branimir Karadzic. All rights reserved.
+-- Copyright 2010-2019 Branimir Karadzic. All rights reserved.
-- License: https://github.com/bkaradzic/bimg#license-bsd-2-clause
--
diff --git a/3rdparty/bimg/src/bimg_p.h b/3rdparty/bimg/src/bimg_p.h
index c74531d2b5e..a9a4b2ae42f 100644
--- a/3rdparty/bimg/src/bimg_p.h
+++ b/3rdparty/bimg/src/bimg_p.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2011-2018 Branimir Karadzic. All rights reserved.
+ * Copyright 2011-2019 Branimir Karadzic. All rights reserved.
* License: https://github.com/bkaradzic/bimg#license-bsd-2-clause
*/
diff --git a/3rdparty/bimg/src/image.cpp b/3rdparty/bimg/src/image.cpp
index 6bf0d5245da..9ef2e3a6656 100644
--- a/3rdparty/bimg/src/image.cpp
+++ b/3rdparty/bimg/src/image.cpp
@@ -1,14 +1,14 @@
/*
- * Copyright 2011-2018 Branimir Karadzic. All rights reserved.
+ * Copyright 2011-2019 Branimir Karadzic. All rights reserved.
* License: https://github.com/bkaradzic/bimg#license-bsd-2-clause
*/
#include "bimg_p.h"
#include <bx/hash.h>
-#if BIMG_CONFIG_ASTC_DECODE
-# include "../3rdparty/astc/astc_lib.h"
-#endif // BIMG_CONFIG_ASTC_DECODE
+#include <astc-codec/astc-codec.h>
+
+#include <bx/debug.h>
namespace bimg
{
@@ -252,6 +252,11 @@ namespace bimg
const char* getName(TextureFormat::Enum _format)
{
+ if (_format >= TextureFormat::Count)
+ {
+ return "Unknown?!";
+ }
+
return s_textureFormatName[_format];
}
@@ -928,7 +933,7 @@ namespace bimg
xyz[1] += rgba1[5];
xyz[2] += rgba1[6];
- bx::store(dst, bx::normalize(bx::load(xyz) ) );
+ bx::store(dst, bx::normalize(bx::load<bx::Vec3>(xyz) ) );
}
}
}
@@ -1014,7 +1019,7 @@ namespace bimg
for (uint32_t zz = 0; zz < _depth; ++zz, src += _srcPitch*_height, dst += _dstPitch*_height)
{
- bx::memCopy(dst, src, pitch, _height, _srcPitch, _dstPitch);
+ bx::memCopy(dst, _dstPitch, src, _srcPitch, pitch, _height);
}
}
@@ -1060,7 +1065,7 @@ namespace bimg
{ NULL, NULL }, // ASTC10x5
{ NULL, NULL }, // Unknown
{ NULL, NULL }, // R1
- { bx::packR8, bx::unpackR8 }, // A8
+ { bx::packA8, bx::unpackA8 }, // A8
{ bx::packR8, bx::unpackR8 }, // R8
{ bx::packR8I, bx::unpackR8I }, // R8I
{ bx::packR8U, bx::unpackR8U }, // R8U
@@ -1154,16 +1159,14 @@ namespace bimg
}
}
- void imageConvert(void* _dst, uint32_t _dstBpp, PackFn _pack, const void* _src, uint32_t _srcBpp, UnpackFn _unpack, uint32_t _width, uint32_t _height, uint32_t _depth, uint32_t _srcPitch)
+ void imageConvert(void* _dst, uint32_t _dstBpp, PackFn _pack, const void* _src, uint32_t _srcBpp, UnpackFn _unpack, uint32_t _width, uint32_t _height, uint32_t _depth, uint32_t _srcPitch, uint32_t _dstPitch)
{
const uint8_t* src = (uint8_t*)_src;
uint8_t* dst = (uint8_t*)_dst;
- const uint32_t dstPitch = _width * _dstBpp / 8;
-
for (uint32_t zz = 0; zz < _depth; ++zz)
{
- for (uint32_t yy = 0; yy < _height; ++yy, src += _srcPitch, dst += dstPitch)
+ for (uint32_t yy = 0; yy < _height; ++yy, src += _srcPitch, dst += _dstPitch)
{
for (uint32_t xx = 0; xx < _width; ++xx)
{
@@ -1175,7 +1178,7 @@ namespace bimg
}
}
- bool imageConvert(bx::AllocatorI* _allocator, void* _dst, TextureFormat::Enum _dstFormat, const void* _src, TextureFormat::Enum _srcFormat, uint32_t _width, uint32_t _height, uint32_t _depth, uint32_t _srcPitch)
+ bool imageConvert(bx::AllocatorI* _allocator, void* _dst, TextureFormat::Enum _dstFormat, const void* _src, TextureFormat::Enum _srcFormat, uint32_t _width, uint32_t _height, uint32_t _depth, uint32_t _srcPitch, uint32_t _dstPitch)
{
UnpackFn unpack = s_packUnpack[_srcFormat].unpack;
PackFn pack = s_packUnpack[_dstFormat].pack;
@@ -1205,7 +1208,7 @@ namespace bimg
const uint32_t srcBpp = s_imageBlockInfo[_srcFormat].bitsPerPixel;
const uint32_t dstBpp = s_imageBlockInfo[_dstFormat].bitsPerPixel;
- imageConvert(_dst, dstBpp, pack, _src, srcBpp, unpack, _width, _height, _depth, _srcPitch);
+ imageConvert(_dst, dstBpp, pack, _src, srcBpp, unpack, _width, _height, _depth, _srcPitch, _dstPitch);
return true;
}
@@ -1220,7 +1223,10 @@ namespace bimg
return true;
}
- return imageConvert(_allocator, _dst, _dstFormat, _src, _srcFormat, _width, _height, _depth, _width*srcBpp/8);
+ const uint32_t dstBpp = s_imageBlockInfo[_dstFormat].bitsPerPixel;
+ const uint32_t dstPitch = _width * dstBpp / 8;
+
+ return imageConvert(_allocator, _dst, _dstFormat, _src, _srcFormat, _width, _height, _depth, _width*srcBpp/8, dstPitch);
}
ImageContainer* imageConvert(bx::AllocatorI* _allocator, TextureFormat::Enum _dstFormat, const ImageContainer& _input, bool _convertMips)
@@ -4226,12 +4232,12 @@ namespace bimg
uint32_t size = imageGetSize(NULL, uint16_t(_width), uint16_t(_height), 0, false, false, 1, TextureFormat::RGBA8);
void* temp = BX_ALLOC(_allocator, size);
imageDecodeToRgba8(_allocator, temp, _src, _width, _height, _width*4, _srcFormat);
- imageConvert(_allocator, dst, TextureFormat::R8, temp, TextureFormat::RGBA8, _width, _height, 1, _width*4);
+ imageConvert(_allocator, dst, TextureFormat::R8, temp, TextureFormat::RGBA8, _width, _height, 1, _width*4, _dstPitch);
BX_FREE(_allocator, temp);
}
else
{
- imageConvert(_allocator, dst, TextureFormat::R8, src, _srcFormat, _width, _height, 1, srcPitch);
+ imageConvert(_allocator, dst, TextureFormat::R8, src, _srcFormat, _width, _height, 1, srcPitch, _dstPitch);
}
}
}
@@ -4360,7 +4366,7 @@ namespace bimg
, false
);
imageDecodeToRgba32f(_allocator, rgba32f->m_data, _src, _width, _height, 1, _width*16, _srcFormat);
- imageConvert(_allocator, _dst, TextureFormat::BGRA8, rgba32f->m_data, TextureFormat::RGBA32F, _width, _height, 1, _width*16);
+ imageConvert(_allocator, _dst, TextureFormat::BGRA8, rgba32f->m_data, TextureFormat::RGBA32F, _width, _height, 1, _width*16, _dstPitch);
imageFree(rgba32f);
}
break;
@@ -4523,24 +4529,8 @@ namespace bimg
case TextureFormat::ASTC8x5:
case TextureFormat::ASTC8x6:
case TextureFormat::ASTC10x5:
-#if BIMG_CONFIG_ASTC_DECODE
- astc_decompress
- (
- (const uint8_t*) _src,
- s_imageBlockInfo[_srcFormat].blockWidth,
- s_imageBlockInfo[_srcFormat].blockHeight,
- ASTC_DECODE_LDR_LINEAR,
-
- _width,
- _height,
- (uint8_t*) _dst,
- ASTC_BGRA,
- _dstPitch
- );
-#else
- BX_WARN(false, "ASTC decoder is not implemented.");
- imageCheckerboard(_dst, _width, _height, 16, UINT32_C(0xff000000), UINT32_C(0xffffff00) );
-#endif
+ imageDecodeToRgba8(_allocator, _dst, _src, _width, _height, _dstPitch, _srcFormat);
+ imageSwizzleBgra8(_dst, _dstPitch, _width, _height, _dst, _dstPitch);
break;
case TextureFormat::RGBA8:
@@ -4554,7 +4544,7 @@ namespace bimg
{
const uint32_t srcPitch = _width * 4;
const uint32_t size = bx::uint32_min(srcPitch, _dstPitch);
- bx::memCopy(_dst, _src, size, _height, srcPitch, _dstPitch);
+ bx::memCopy(_dst, _dstPitch, _src, srcPitch, size, _height);
}
break;
@@ -4562,7 +4552,7 @@ namespace bimg
{
const uint32_t srcBpp = s_imageBlockInfo[_srcFormat].bitsPerPixel;
const uint32_t srcPitch = _width * srcBpp / 8;
- if (!imageConvert(_allocator, _dst, TextureFormat::BGRA8, _src, _srcFormat, _width, _height, 1, srcPitch) )
+ if (!imageConvert(_allocator, _dst, TextureFormat::BGRA8, _src, _srcFormat, _width, _height, 1, srcPitch, _dstPitch) )
{
// Failed to convert, just make ugly red-yellow checkerboard texture.
imageCheckerboard(_dst, _width, _height, 16, UINT32_C(0xffff0000), UINT32_C(0xffffff00) );
@@ -4580,7 +4570,7 @@ namespace bimg
{
const uint32_t srcPitch = _width * 4;
const uint32_t size = bx::uint32_min(srcPitch, _dstPitch);
- bx::memCopy(_dst, _src, size, _height, srcPitch, _dstPitch);
+ bx::memCopy(_dst, _dstPitch, _src, srcPitch, size, _height);
}
break;
@@ -4591,6 +4581,32 @@ namespace bimg
}
break;
+ case TextureFormat::ASTC4x4:
+ case TextureFormat::ASTC5x5:
+ case TextureFormat::ASTC6x6:
+ case TextureFormat::ASTC8x5:
+ case TextureFormat::ASTC8x6:
+ case TextureFormat::ASTC10x5:
+ if (!astc_codec::ASTCDecompressToRGBA(
+ (const uint8_t*)_src
+ , imageGetSize(NULL, uint16_t(_width), uint16_t(_height), 0, false, false, 1, _srcFormat)
+ , _width
+ , _height
+ , TextureFormat::ASTC4x4 == _srcFormat ? astc_codec::FootprintType::k4x4
+ : TextureFormat::ASTC5x5 == _srcFormat ? astc_codec::FootprintType::k5x5
+ : TextureFormat::ASTC6x6 == _srcFormat ? astc_codec::FootprintType::k6x6
+ : TextureFormat::ASTC8x5 == _srcFormat ? astc_codec::FootprintType::k8x5
+ : TextureFormat::ASTC8x6 == _srcFormat ? astc_codec::FootprintType::k8x6
+ : astc_codec::FootprintType::k10x5
+ , (uint8_t*)_dst
+ , _width*_height*4
+ , _dstPitch
+ ) )
+ {
+ imageCheckerboard(_dst, _width, _height, 16, UINT32_C(0xff000000), UINT32_C(0xffffff00) );
+ }
+ break;
+
default:
{
const uint32_t srcPitch = _width * 4;
@@ -4753,7 +4769,7 @@ namespace bimg
}
else
{
- imageConvert(_allocator, dst, TextureFormat::RGBA32F, src, _srcFormat, _width, _height, 1, srcPitch);
+ imageConvert(_allocator, dst, TextureFormat::RGBA32F, src, _srcFormat, _width, _height, 1, srcPitch, _dstPitch);
}
break;
}
@@ -4802,12 +4818,7 @@ namespace bimg
depth = bx::max<uint32_t>(1, depth);
const uint32_t mipSize = width/blockWidth * height/blockHeight * depth * blockSize;
- if (mipSize != width*height*depth*bpp/8)
- {
- BX_TRACE("x");
- }
-
- const uint32_t size = mipSize*numSides;
+ const uint32_t size = mipSize*numSides;
uint32_t imageSize = bx::toHostEndian(*(const uint32_t*)&data[offset], _imageContainer.m_ktxLE);
BX_CHECK(size == imageSize, "KTX: Image size mismatch %d (expected %d).", size, imageSize);
BX_UNUSED(size, imageSize);
@@ -5267,23 +5278,25 @@ namespace bimg
}
}
- if (UINT32_MAX == ddspf && UINT32_MAX == dxgiFormat)
- {
- for (uint32_t ii = 0; ii < BX_COUNTOF(s_translateDdsFourccFormat); ++ii)
- {
- if (s_translateDdsFourccFormat[ii].m_textureFormat == _format)
- {
- fourccFormat = s_translateDdsFourccFormat[ii].m_format;
- break;
- }
- }
- }
-
- if (UINT32_MAX == ddspf && UINT32_MAX == dxgiFormat && UINT32_MAX == fourccFormat)
- {
- BX_ERROR_SET(_err, BIMG_ERROR, "DDS: output format not supported.");
- return 0;
- }
+ if (UINT32_MAX == ddspf && UINT32_MAX == dxgiFormat)
+ {
+ for (uint32_t ii = 0; ii < BX_COUNTOF(s_translateDdsFourccFormat); ++ii)
+ {
+ if (s_translateDdsFourccFormat[ii].m_textureFormat == _format)
+ {
+ fourccFormat = s_translateDdsFourccFormat[ii].m_format;
+ break;
+ }
+ }
+ }
+
+ if (UINT32_MAX == ddspf
+ && UINT32_MAX == dxgiFormat
+ && UINT32_MAX == fourccFormat)
+ {
+ BX_ERROR_SET(_err, BIMG_ERROR, "DDS: output format not supported.");
+ return 0;
+ }
const uint32_t bpp = getBitsPerPixel(_format);
diff --git a/3rdparty/bimg/src/image_cubemap_filter.cpp b/3rdparty/bimg/src/image_cubemap_filter.cpp
index a967926aa1c..b8e4b372507 100644
--- a/3rdparty/bimg/src/image_cubemap_filter.cpp
+++ b/3rdparty/bimg/src/image_cubemap_filter.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2011-2018 Branimir Karadzic. All rights reserved.
+ * Copyright 2011-2019 Branimir Karadzic. All rights reserved.
* License: https://github.com/bkaradzic/bimg#license-bsd-2-clause
*/
@@ -259,22 +259,46 @@ namespace bimg
const float* src2 = (const float*)&srcData[y1*srcPitch + x0*16];
const float* src3 = (const float*)&srcData[y1*srcPitch + x1*16];
- const float tx = srcU - float(int32_t(x0) );
- const float ty = srcV - float(int32_t(y0) );
- const float omtx = 1.0f - tx;
- const float omty = 1.0f - ty;
+ const float tx = srcU - float(int32_t(x0) );
+ const float ty = srcV - float(int32_t(y0) );
+ const float omtx = 1.0f - tx;
+ const float omty = 1.0f - ty;
- float p0[4];
- bx::vec4Mul(p0, src0, omtx*omty);
-
- float p1[4];
- bx::vec4Mul(p1, src1, tx*omty);
-
- float p2[4];
- bx::vec4Mul(p2, src2, omtx*ty);
-
- float p3[4];
- bx::vec4Mul(p3, src3, tx*ty);
+ const float p0x = omtx*omty;
+ const float p0[4] =
+ {
+ src0[0] * p0x,
+ src0[1] * p0x,
+ src0[2] * p0x,
+ src0[3] * p0x,
+ };
+
+ const float p1x = tx*omty;
+ const float p1[4] =
+ {
+ src1[0] * p1x,
+ src1[1] * p1x,
+ src1[2] * p1x,
+ src1[3] * p1x,
+ };
+
+ const float p2x = omtx*ty;
+ const float p2[4] =
+ {
+ src2[0] * p2x,
+ src2[1] * p2x,
+ src2[2] * p2x,
+ src2[3] * p2x,
+ };
+
+ const float p3x = tx*ty;
+ const float p3[4] =
+ {
+ src3[0] * p3x,
+ src3[1] * p3x,
+ src3[2] * p3x,
+ src3[3] * p3x,
+ };
const float rr = p0[0] + p1[0] + p2[0] + p3[0];
const float gg = p0[1] + p1[1] + p2[1] + p3[1];
@@ -339,7 +363,7 @@ namespace bimg
ImageMip dstMip;
imageGetRawData(*output, side, 0, output->m_data, output->m_size, dstMip);
- bx::memCopy(const_cast<uint8_t*>(dstMip.m_data), srcData, dstPitch, dstWidth, srcPitch, dstPitch);
+ bx::memCopy(const_cast<uint8_t*>(dstMip.m_data), dstPitch, srcData, srcPitch, dstPitch, dstWidth);
}
return output;
@@ -954,7 +978,7 @@ namespace bimg
{
const float* normal = (const float*)&nsaMip.m_data[(yy*nsaMip.m_width+xx)*(nsaMip.m_bpp/8)];
const float solidAngle = normal[3];
- const float ndotl = bx::clamp(bx::dot(bx::load(normal), _dir), 0.0f, 1.0f);
+ const float ndotl = bx::clamp(bx::dot(bx::load<bx::Vec3>(normal), _dir), 0.0f, 1.0f);
if (ndotl >= _specularAngle)
{
diff --git a/3rdparty/bimg/src/image_decode.cpp b/3rdparty/bimg/src/image_decode.cpp
index 0ce4023e89a..e58c2703eb9 100644
--- a/3rdparty/bimg/src/image_decode.cpp
+++ b/3rdparty/bimg/src/image_decode.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2011-2018 Branimir Karadzic. All rights reserved.
+ * Copyright 2011-2019 Branimir Karadzic. All rights reserved.
* License: https://github.com/bkaradzic/bimg#license-bsd-2-clause
*/
@@ -647,7 +647,10 @@ namespace bimg
for (uint32_t ii = 0; err.isOk() && ii < numEntries; ++ii)
{
- // https://sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
+ // Reference(s):
+ // - EXIF Tags
+ // https://web.archive.org/web/20190218005249/https://sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
+ //
uint16_t tag;
bx::readHE(&reader, tag, littleEndian, &err);
diff --git a/3rdparty/bimg/src/image_encode.cpp b/3rdparty/bimg/src/image_encode.cpp
index 09586fe5105..7d24cc5d672 100644
--- a/3rdparty/bimg/src/image_encode.cpp
+++ b/3rdparty/bimg/src/image_encode.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2011-2018 Branimir Karadzic. All rights reserved.
+ * Copyright 2011-2019 Branimir Karadzic. All rights reserved.
* License: https://github.com/bkaradzic/bimg#license-bsd-2-clause
*/
@@ -30,19 +30,29 @@ namespace bimg
{
static uint32_t s_squishQuality[] =
{
+ // Standard
+ squish::kColourClusterFit, // Default
+ squish::kColourIterativeClusterFit, // Highest
+ squish::kColourRangeFit, // Fastest
+ // Normal map
squish::kColourClusterFit, // Default
squish::kColourIterativeClusterFit, // Highest
squish::kColourRangeFit, // Fastest
};
BX_STATIC_ASSERT(Quality::Count == BX_COUNTOF(s_squishQuality) );
- static const ASTC_COMPRESS_MODE s_astcQuality[] =
- {
- ASTC_COMPRESS_MEDIUM, // Default
- ASTC_COMPRESS_THOROUGH, // Highest
- ASTC_COMPRESS_FAST, // Fastest
- };
- BX_STATIC_ASSERT(Quality::Count == BX_COUNTOF(s_astcQuality));
+ static const ASTC_COMPRESS_MODE s_astcQuality[] =
+ {
+ // Standard
+ ASTC_COMPRESS_MEDIUM, // Default
+ ASTC_COMPRESS_THOROUGH, // Highest
+ ASTC_COMPRESS_FAST, // Fastest
+ // Normal map
+ ASTC_COMPRESS_MEDIUM, // Default
+ ASTC_COMPRESS_THOROUGH, // Highest
+ ASTC_COMPRESS_FAST, // Fastest
+ };
+ BX_STATIC_ASSERT(Quality::Count == BX_COUNTOF(s_astcQuality));
void imageEncodeFromRgba8(bx::AllocatorI* _allocator, void* _dst, const void* _src, uint32_t _width, uint32_t _height, uint32_t _depth, TextureFormat::Enum _format, Quality::Enum _quality, bx::Error* _err)
{
@@ -138,12 +148,19 @@ namespace bimg
case TextureFormat::ASTC8x6:
case TextureFormat::ASTC10x5:
{
- const bimg::ImageBlockInfo& astcBlockInfo = bimg::getBlockInfo(_format);
+ const bimg::ImageBlockInfo& astcBlockInfo = bimg::getBlockInfo(_format);
- ASTC_COMPRESS_MODE compress_mode = s_astcQuality[_quality];
- ASTC_DECODE_MODE decode_mode = ASTC_DECODE_LDR_LINEAR;
+ ASTC_COMPRESS_MODE compress_mode = s_astcQuality[_quality];
+ ASTC_DECODE_MODE decode_mode = ASTC_DECODE_LDR_LINEAR;
- astc_compress(_width, _height, src, ASTC_RGBA, srcPitch, astcBlockInfo.blockWidth, astcBlockInfo.blockHeight, compress_mode, decode_mode, dst);
+ if (Quality::NormalMapDefault <= _quality)
+ {
+ astc_compress(_width, _height, src, ASTC_ENC_NORMAL_RA, srcPitch, astcBlockInfo.blockWidth, astcBlockInfo.blockHeight, compress_mode, decode_mode, dst);
+ }
+ else
+ {
+ astc_compress(_width, _height, src, ASTC_RGBA, srcPitch, astcBlockInfo.blockWidth, astcBlockInfo.blockHeight, compress_mode, decode_mode, dst);
+ }
}
break;
@@ -152,7 +169,7 @@ namespace bimg
break;
case TextureFormat::RGBA8:
- bx::memCopy(_dst, _src, srcPitch, _height, srcPitch, dstPitch);
+ bx::memCopy(_dst, dstPitch, _src, srcPitch, srcPitch, _height);
break;
default:
@@ -490,7 +507,7 @@ namespace bimg
return rgba[3];
}
- float imageAlphaTestCoverage(TextureFormat::Enum _format, uint32_t _width, uint32_t _height, uint32_t _srcPitch, const void* _src, float _alphaRef, float _scale)
+ float imageAlphaTestCoverage(TextureFormat::Enum _format, uint32_t _width, uint32_t _height, uint32_t _srcPitch, const void* _src, float _alphaRef, float _scale, uint32_t _upscale)
{
UnpackFn unpack = getUnpack(_format);
if (NULL == unpack)
@@ -501,7 +518,8 @@ namespace bimg
float coverage = 0.0f;
const uint8_t* src = (const uint8_t*)_src;
const uint32_t xstep = getBitsPerPixel(_format) / 8;
- const float numSamples = 8.0f;
+ const uint32_t numSamples = _upscale;
+ const float sampleStep = 1.0f / numSamples;
for (uint32_t yy = 0, ystep = _srcPitch; yy < _height-1; ++yy, src += ystep)
{
@@ -513,9 +531,9 @@ namespace bimg
float alpha01 = _scale * getAlpha(unpack, data+ystep);
float alpha11 = _scale * getAlpha(unpack, data+ystep+xstep);
- for (float fy = 0.5f/numSamples; fy < 1.0f; fy += 1.0f)
+ for (float fy = 0.0f; fy < 1.0f; fy += sampleStep)
{
- for (float fx = 0.5f/numSamples; fx < 1.0f; fx += 1.0f)
+ for (float fx = 0.0f; fx < 1.0f; fx += sampleStep)
{
float alpha = 0.0f
+ alpha00 * (1.0f - fx) * (1.0f - fy)
@@ -536,7 +554,7 @@ namespace bimg
return coverage / float(_width*_height*numSamples*numSamples);
}
- void imageScaleAlphaToCoverage(TextureFormat::Enum _format, uint32_t _width, uint32_t _height, uint32_t _srcPitch, void* _src, float _desiredCoverage, float _alphaRef)
+ void imageScaleAlphaToCoverage(TextureFormat::Enum _format, uint32_t _width, uint32_t _height, uint32_t _srcPitch, void* _src, float _desiredCoverage, float _alphaRef, uint32_t _upscale)
{
PackFn pack = getPack(_format);
UnpackFn unpack = getUnpack(_format);
@@ -550,7 +568,7 @@ namespace bimg
float max = 4.0f;
float scale = 1.0f;
- for (uint32_t ii = 0; ii < 8; ++ii)
+ for (uint32_t ii = 0; ii < 10; ++ii)
{
float coverage = imageAlphaTestCoverage(
_format
@@ -560,6 +578,7 @@ namespace bimg
, _src
, _alphaRef
, scale
+ , _upscale
);
if (coverage < _desiredCoverage)
diff --git a/3rdparty/bimg/src/image_gnf.cpp b/3rdparty/bimg/src/image_gnf.cpp
index 77c6d0a6332..aefccb21682 100644
--- a/3rdparty/bimg/src/image_gnf.cpp
+++ b/3rdparty/bimg/src/image_gnf.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2011-2018 Branimir Karadzic. All rights reserved.
+ * Copyright 2011-2019 Branimir Karadzic. All rights reserved.
* License: https://github.com/bkaradzic/bimg#license-bsd-2-clause
*/
diff --git a/3rdparty/bimg/tools/texturec/texturec.cpp b/3rdparty/bimg/tools/texturec/texturec.cpp
index b9ea4a2291f..51025c614e3 100644
--- a/3rdparty/bimg/tools/texturec/texturec.cpp
+++ b/3rdparty/bimg/tools/texturec/texturec.cpp
@@ -1,9 +1,8 @@
/*
- * Copyright 2011-2018 Branimir Karadzic. All rights reserved.
+ * Copyright 2011-2019 Branimir Karadzic. All rights reserved.
* License: https://github.com/bkaradzic/bimg#license-bsd-2-clause
*/
-#include <stdio.h>
#include <bx/allocator.h>
#include <bx/readerwriter.h>
#include <bx/endian.h>
@@ -13,7 +12,7 @@
#include <bimg/encode.h>
#if 0
-# define DBG(_format, ...) fprintf(stderr, "" _format "\n", ##__VA_ARGS__)
+# define DBG(_format, ...) bx::printf("" _format "\n", ##__VA_ARGS__)
#else
# define DBG(...) BX_NOOP()
#endif // DEBUG
@@ -90,12 +89,8 @@ void imageRgba32fNormalize(void* _dst, uint32_t _width, uint32_t _height, uint32
const float* rgba = (const float*)&src[0];
for (uint32_t xx = 0; xx < _width; ++xx, rgba += 4, dst += 16)
{
- float xyz[3];
-
- xyz[0] = rgba[0];
- xyz[1] = rgba[1];
- xyz[2] = rgba[2];
- bx::vec3Norm( (float*)dst, xyz);
+ const bx::Vec3 xyz = bx::load<bx::Vec3>(rgba);
+ bx::store(dst, bx::normalize(xyz) );
}
}
}
@@ -161,12 +156,12 @@ bimg::ImageContainer* convert(bx::AllocatorI* _allocator, const void* _inputData
const bimg::ImageBlockInfo& inputBlockInfo = bimg::getBlockInfo(inputFormat);
const bimg::ImageBlockInfo& outputBlockInfo = bimg::getBlockInfo(outputFormat);
const uint32_t blockWidth = outputBlockInfo.blockWidth;
- const uint32_t blockHeight = outputBlockInfo.blockHeight;
- const uint32_t minBlockX = outputBlockInfo.minBlockX;
- const uint32_t minBlockY = outputBlockInfo.minBlockY;
- uint32_t outputWidth = bx::max(blockWidth * minBlockX, ( (input->m_width + blockWidth - 1) / blockWidth )*blockWidth);
- uint32_t outputHeight = bx::max(blockHeight * minBlockY, ( (input->m_height + blockHeight - 1) / blockHeight)*blockHeight);
- uint32_t outputDepth = input->m_depth;
+ const uint32_t blockHeight = outputBlockInfo.blockHeight;
+ const uint32_t minBlockX = outputBlockInfo.minBlockX;
+ const uint32_t minBlockY = outputBlockInfo.minBlockY;
+ uint32_t outputWidth = bx::max(blockWidth * minBlockX, ( (input->m_width + blockWidth - 1) / blockWidth )*blockWidth);
+ uint32_t outputHeight = bx::max(blockHeight * minBlockY, ( (input->m_height + blockHeight - 1) / blockHeight)*blockHeight);
+ uint32_t outputDepth = input->m_depth;
if (_options.mips
&& _options.mipSkip != 0)
@@ -454,6 +449,7 @@ bimg::ImageContainer* convert(bx::AllocatorI* _allocator, const void* _inputData
, rgba
);
+ bimg::Quality::Enum nmapQuality = bimg::Quality::Enum(_options.quality + bimg::Quality::NormalMapDefault);
bimg::imageEncodeFromRgba32f(_allocator
, dstData
, rgbaDst
@@ -461,7 +457,7 @@ bimg::ImageContainer* convert(bx::AllocatorI* _allocator, const void* _inputData
, dstMip.m_height
, dstMip.m_depth
, outputFormat
- , _options.quality
+ , nmapQuality
, _err
);
@@ -493,7 +489,7 @@ bimg::ImageContainer* convert(bx::AllocatorI* _allocator, const void* _inputData
, dstMip.m_height
, dstMip.m_depth
, outputFormat
- , _options.quality
+ , nmapQuality
, _err
);
}
@@ -624,10 +620,10 @@ bimg::ImageContainer* convert(bx::AllocatorI* _allocator, const void* _inputData
, bimg::TextureFormat::R8
);
temp = BX_ALLOC(_allocator, size);
- uint8_t* rgba = (uint8_t*)temp;
+ uint8_t* r8 = (uint8_t*)temp;
bimg::imageDecodeToR8(_allocator
- , rgba
+ , r8
, mip.m_data
, mip.m_width
, mip.m_height
@@ -644,8 +640,85 @@ bimg::ImageContainer* convert(bx::AllocatorI* _allocator, const void* _inputData
, mip.m_width
, mip.m_height
, mip.m_width
- , rgba
+ , r8
+ );
+
+ if (_options.mips) {
+ const float alphaRef = 0.5f;
+ float coverage = bimg::imageAlphaTestCoverage(bimg::TextureFormat::A8
+ , mip.m_width
+ , mip.m_height
+ , mip.m_width
+ , r8
+ , alphaRef
+ );
+
+ size = bimg::imageGetSize(
+ NULL
+ , uint16_t(dstMip.m_width)
+ , uint16_t(dstMip.m_height)
+ , uint16_t(dstMip.m_depth)
+ , false
+ , false
+ , 1
+ , bimg::TextureFormat::RGBA8
);
+ void* rgbaTemp = BX_ALLOC(_allocator, size);
+ uint8_t* rgba = (uint8_t*)rgbaTemp;
+
+ bimg::imageDecodeToRgba8(
+ _allocator
+ , rgba
+ , dstMip.m_data
+ , dstMip.m_width
+ , dstMip.m_height
+ , dstMip.m_width * 4
+ , bimg::TextureFormat::A8
+ );
+
+ for (uint8_t lod = 1; lod < numMips && _err->isOk(); ++lod) {
+ bimg::imageRgba8Downsample2x2(rgba
+ , dstMip.m_width
+ , dstMip.m_height
+ , dstMip.m_depth
+ , dstMip.m_width * 4
+ , bx::strideAlign(dstMip.m_width / 2, blockWidth) * 4
+ , rgba
+ );
+
+ // For each mip, upscale to original size,
+ // scale image alpha to get same coverage as mip0
+ uint32_t upsample = 1 << lod;
+ uint32_t destWidth = dstMip.m_width / 2;
+ uint32_t destHeight = dstMip.m_height / 2;
+ bimg::imageScaleAlphaToCoverage(bimg::TextureFormat::RGBA8
+ , destWidth
+ , destHeight
+ , destWidth * 4
+ , rgba
+ , coverage
+ , alphaRef
+ , upsample
+ );
+
+ bimg::imageGetRawData(*output, side, lod, output->m_data, output->m_size, dstMip);
+ dstData = const_cast<uint8_t*>(dstMip.m_data);
+
+ bimg::imageEncodeFromRgba8(
+ _allocator
+ , dstData
+ , rgba
+ , dstMip.m_width
+ , dstMip.m_height
+ , dstMip.m_depth
+ , bimg::TextureFormat::A8
+ , _options.quality
+ , _err
+ );
+ }
+
+ BX_FREE(_allocator, rgbaTemp);
+ }
}
// RGBA8
else
@@ -787,7 +860,7 @@ bimg::ImageContainer* convert(bx::AllocatorI* _allocator, const void* _inputData
, uint16_t(mip.m_height)
);
- printf("%f\n", result);
+ bx::printf("%f\n", result);
BX_FREE(_allocator, ref);
}
@@ -814,7 +887,7 @@ void help(const char* _error = NULL, bool _showHelp = true)
{
if (NULL != _error)
{
- fprintf(stderr, "Error:\n%s\n\n", _error);
+ bx::printf("Error:\n%s\n\n", _error);
if (!_showHelp)
{
@@ -822,17 +895,17 @@ void help(const char* _error = NULL, bool _showHelp = true)
}
}
- fprintf(stderr
- , "texturec, bgfx texture compiler tool, version %d.%d.%d.\n"
- "Copyright 2011-2018 Branimir Karadzic. All rights reserved.\n"
+ bx::printf(
+ "texturec, bgfx texture compiler tool, version %d.%d.%d.\n"
+ "Copyright 2011-2019 Branimir Karadzic. All rights reserved.\n"
"License: https://github.com/bkaradzic/bimg#license-bsd-2-clause\n\n"
, BIMG_TEXTUREC_VERSION_MAJOR
, BIMG_TEXTUREC_VERSION_MINOR
, BIMG_API_VERSION
);
- fprintf(stderr
- , "Usage: texturec -f <in> -o <out> [-t <texture format>]\n"
+ bx::printf(
+ "Usage: texturec -f <in> -o <out> [-t <texture format>]\n"
"\n"
"Supported file formats:\n"
@@ -846,7 +919,7 @@ void help(const char* _error = NULL, bool _showHelp = true)
" *.png (input, output) Portable Network Graphics.\n"
" *.psd (input) Photoshop Document.\n"
" *.pvr (input) PowerVR.\n"
- " *.tga (input) Targa.\n"
+ " *.tga (input) Truevision TGA.\n"
"\n"
"Options:\n"
@@ -870,7 +943,7 @@ void help(const char* _error = NULL, bool _showHelp = true)
" aspect ratio will be preserved.\n"
" --radiance <model> Radiance cubemap filter. (Lighting model: Phong, PhongBrdf, Blinn, BlinnBrdf, GGX)\n"
" --as <extension> Save as.\n"
- " --formats List all supported formats.\n"
+ " --formats List all supported formats.\n"
" --validate *DEBUG* Validate that output image produced matches after loading.\n"
"\n"
@@ -923,8 +996,8 @@ int main(int _argc, const char* _argv[])
if (cmdLine.hasArg('v', "version") )
{
- fprintf(stderr
- , "texturec, bgfx texture compiler tool, version %d.%d.%d.\n"
+ bx::printf(
+ "texturec, bgfx texture compiler tool, version %d.%d.%d.\n"
, BIMG_TEXTUREC_VERSION_MAJOR
, BIMG_TEXTUREC_VERSION_MINOR
, BIMG_API_VERSION
@@ -940,20 +1013,26 @@ int main(int _argc, const char* _argv[])
if (cmdLine.hasArg("formats"))
{
- printf("Uncompressed formats:\n");
+ bx::printf("Uncompressed formats:\n");
- for (int format = bimg::TextureFormat::Unknown + 1; format < bimg::TextureFormat::UnknownDepth; format++)
- printf(" %s\n", bimg::getName((bimg::TextureFormat::Enum) format));
+ for (int format = bimg::TextureFormat::Unknown + 1; format < bimg::TextureFormat::UnknownDepth; format++)
+ {
+ bx::printf(" %s\n", bimg::getName((bimg::TextureFormat::Enum) format));
+ }
- for (int format = bimg::TextureFormat::UnknownDepth + 1; format < bimg::TextureFormat::Count; format++)
- printf(" %s\n", bimg::getName((bimg::TextureFormat::Enum) format));
+ for (int format = bimg::TextureFormat::UnknownDepth + 1; format < bimg::TextureFormat::Count; format++)
+ {
+ bx::printf(" %s\n", bimg::getName((bimg::TextureFormat::Enum) format));
+ }
- printf("Compressed formats:\n");
+ bx::printf("Compressed formats:\n");
- for (int format = 0; format < bimg::TextureFormat::Unknown; format++)
- printf(" %s\n", bimg::getName((bimg::TextureFormat::Enum) format));
+ for (int format = 0; format < bimg::TextureFormat::Unknown; format++)
+ {
+ bx::printf(" %s\n", bimg::getName((bimg::TextureFormat::Enum) format));
+ }
- return bx::kExitSuccess;
+ return bx::kExitSuccess;
}
const char* inputFileName = cmdLine.findOption('f');
@@ -1289,7 +1368,7 @@ int main(int _argc, const char* _argv[])
}
else
{
- help(NULL, err);
+ help("Failed to create output", err);
return bx::kExitFailure;
}