This commit is contained in:
Бранимир Караџић
2022-10-20 20:03:19 -07:00
parent c7f88c254d
commit 471fa663b5
94 changed files with 0 additions and 5822 deletions

View File

@@ -1,5 +0,0 @@
bazel-*
.bazelrc
build
.vs
.vscode

View File

@@ -1,29 +0,0 @@
# 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"],
)

View File

@@ -1,46 +0,0 @@
# 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)

View File

@@ -1,28 +0,0 @@
# 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/).

View File

@@ -1,15 +0,0 @@
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 ""
)

View File

@@ -1,71 +0,0 @@
# 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.

View File

@@ -1,37 +0,0 @@
# 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",
)

View File

@@ -1,38 +0,0 @@
{
"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"
}

View File

@@ -1,4 +0,0 @@
BasedOnStyle: Google
AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: Inline
SpaceAfterTemplateKeyword: false

View File

@@ -1,49 +0,0 @@
# 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",
],
)

View File

@@ -1,27 +0,0 @@
# 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()

View File

@@ -1,141 +0,0 @@
// 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

View File

@@ -1,108 +0,0 @@
// 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

View File

@@ -1,78 +0,0 @@
// 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

View File

@@ -1,481 +0,0 @@
// 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

View File

@@ -1,110 +0,0 @@
// 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

View File

@@ -1,130 +0,0 @@
// 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

View File

@@ -1,140 +0,0 @@
// 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

View File

@@ -1,246 +0,0 @@
# 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,
)

View File

@@ -1,95 +0,0 @@
# 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()

View File

@@ -1,36 +0,0 @@
// 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;
}

View File

@@ -1,181 +0,0 @@
// 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

View File

@@ -1,463 +0,0 @@
// 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

View File

@@ -1,97 +0,0 @@
// 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

View File

@@ -1,217 +0,0 @@
// 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);
}
}

View File

@@ -1,336 +0,0 @@
// 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

View File

@@ -1,454 +0,0 @@
// 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

View File

@@ -1,273 +0,0 @@
// 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

View File

@@ -1,263 +0,0 @@
// 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

View File

@@ -1,361 +0,0 @@
// 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

View File

@@ -1,288 +0,0 @@
// 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

View File

@@ -1,69 +0,0 @@
// 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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 189 KiB

View File

@@ -1,785 +0,0 @@
// 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;
}

View File

@@ -1,39 +0,0 @@
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
)

View File

@@ -1,76 +0,0 @@
#!/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

View File

@@ -1,3 +0,0 @@
# Format: //devtools/kokoro/config/proto/build.proto
build_file: "astc-codec/tools/build-ci/linux/build.sh"

View File

@@ -1,3 +0,0 @@
# Format: //devtools/kokoro/config/proto/build.proto
build_file: "astc-codec/tools/build-ci/linux/build.sh"