From 4d82dee2407f84426ebcf2feecbbfe287eb3f6b5 Mon Sep 17 00:00:00 2001 From: drewcassidy Date: Mon, 8 Mar 2021 02:23:04 -0800 Subject: [PATCH] Major reorganization --- CMakeLists.txt | 22 +- setup.py | 8 +- src/{ => rgbcx}/BC1/BC1Block.h | 0 src/{ => rgbcx}/BC1/BC1Decoder.cpp | 0 src/{ => rgbcx}/BC1/BC1Decoder.h | 0 src/{ => rgbcx}/BC1/BC1Encoder.cpp | 4 +- src/{ => rgbcx}/BC1/BC1Encoder.h | 0 src/{ => rgbcx}/BC1/Histogram.h | 0 src/{ => rgbcx}/BC1/OrderTable.cpp | 0 src/{ => rgbcx}/BC1/OrderTable.h | 0 src/{ => rgbcx}/BC1/SingleColorTable.h | 0 src/{ => rgbcx}/BC1/Table4.h | 0 src/{ => rgbcx}/BC1/Tables.cpp | 0 src/{ => rgbcx}/BC1/Tables.h | 0 src/{ => rgbcx}/BC3/BC3Block.h | 0 src/{ => rgbcx}/BC3/BC3Decoder.cpp | 0 src/{ => rgbcx}/BC3/BC3Decoder.h | 0 src/{ => rgbcx}/BC4/BC4Block.h | 0 src/{ => rgbcx}/BC4/BC4Decoder.cpp | 0 src/{ => rgbcx}/BC4/BC4Decoder.h | 0 src/{ => rgbcx}/BC4/BC4Encoder.cpp | 0 src/{ => rgbcx}/BC4/BC4Encoder.h | 0 src/{ => rgbcx}/BC5/BC5Block.h | 0 src/{ => rgbcx}/BC5/BC5Decoder.cpp | 0 src/{ => rgbcx}/BC5/BC5Decoder.h | 0 src/{ => rgbcx}/BlockDecoder.h | 0 src/{ => rgbcx}/BlockEncoder.cpp | 0 src/{ => rgbcx}/BlockEncoder.h | 0 src/{ => rgbcx}/BlockView.h | 0 src/{ => rgbcx}/Color.cpp | 0 src/{ => rgbcx}/Color.h | 0 src/{ => rgbcx}/Interpolator.cpp | 0 src/{ => rgbcx}/Interpolator.h | 0 src/{ => rgbcx}/Matrix4x4.cpp | 0 src/{ => rgbcx}/Matrix4x4.h | 0 src/{ => rgbcx}/Vector4.h | 0 src/{ => rgbcx}/Vector4Int.h | 0 src/rgbcx/__init__.py | 1 + {python => src/rgbcx/bindings}/BC1.cpp | 6 +- .../rgbcx/bindings}/BlockEncoder.cpp | 4 +- {python => src/rgbcx/bindings}/Module.cpp | 8 +- src/{ => rgbcx}/bitwiseEnums.h | 0 src/{ => rgbcx}/ndebug.h | 0 src/{ => rgbcx}/rgbcx.cpp | 0 src/{ => rgbcx}/rgbcx.h | 0 src/{ => rgbcx}/rgbcxDecoders.h | 0 src/{ => rgbcx}/test/RedbrushAlpha.png | Bin src/{ => rgbcx}/test/bc7decomp.cpp | 0 src/{ => rgbcx}/test/bc7decomp.h | 0 src/{ => rgbcx}/test/bc7enc.c | 0 src/{ => rgbcx}/test/bc7enc.h | 0 src/{ => rgbcx}/test/dds_defs.h | 0 src/{ => rgbcx}/test/lodepng.cpp | 0 src/{ => rgbcx}/test/lodepng.h | 0 src/{ => rgbcx}/util.h | 0 src/test/test.cpp | 881 ------------------ tests/run_tests.cpp | 38 + tests/s3tc.py | 8 +- tests/test_rgbcx.py | 77 +- 59 files changed, 137 insertions(+), 920 deletions(-) rename src/{ => rgbcx}/BC1/BC1Block.h (100%) rename src/{ => rgbcx}/BC1/BC1Decoder.cpp (100%) rename src/{ => rgbcx}/BC1/BC1Decoder.h (100%) rename src/{ => rgbcx}/BC1/BC1Encoder.cpp (99%) rename src/{ => rgbcx}/BC1/BC1Encoder.h (100%) rename src/{ => rgbcx}/BC1/Histogram.h (100%) rename src/{ => rgbcx}/BC1/OrderTable.cpp (100%) rename src/{ => rgbcx}/BC1/OrderTable.h (100%) rename src/{ => rgbcx}/BC1/SingleColorTable.h (100%) rename src/{ => rgbcx}/BC1/Table4.h (100%) rename src/{ => rgbcx}/BC1/Tables.cpp (100%) rename src/{ => rgbcx}/BC1/Tables.h (100%) rename src/{ => rgbcx}/BC3/BC3Block.h (100%) rename src/{ => rgbcx}/BC3/BC3Decoder.cpp (100%) rename src/{ => rgbcx}/BC3/BC3Decoder.h (100%) rename src/{ => rgbcx}/BC4/BC4Block.h (100%) rename src/{ => rgbcx}/BC4/BC4Decoder.cpp (100%) rename src/{ => rgbcx}/BC4/BC4Decoder.h (100%) rename src/{ => rgbcx}/BC4/BC4Encoder.cpp (100%) rename src/{ => rgbcx}/BC4/BC4Encoder.h (100%) rename src/{ => rgbcx}/BC5/BC5Block.h (100%) rename src/{ => rgbcx}/BC5/BC5Decoder.cpp (100%) rename src/{ => rgbcx}/BC5/BC5Decoder.h (100%) rename src/{ => rgbcx}/BlockDecoder.h (100%) rename src/{ => rgbcx}/BlockEncoder.cpp (100%) rename src/{ => rgbcx}/BlockEncoder.h (100%) rename src/{ => rgbcx}/BlockView.h (100%) rename src/{ => rgbcx}/Color.cpp (100%) rename src/{ => rgbcx}/Color.h (100%) rename src/{ => rgbcx}/Interpolator.cpp (100%) rename src/{ => rgbcx}/Interpolator.h (100%) rename src/{ => rgbcx}/Matrix4x4.cpp (100%) rename src/{ => rgbcx}/Matrix4x4.h (100%) rename src/{ => rgbcx}/Vector4.h (100%) rename src/{ => rgbcx}/Vector4Int.h (100%) create mode 100644 src/rgbcx/__init__.py rename {python => src/rgbcx/bindings}/BC1.cpp (97%) rename {python => src/rgbcx/bindings}/BlockEncoder.cpp (97%) rename {python => src/rgbcx/bindings}/Module.cpp (93%) rename src/{ => rgbcx}/bitwiseEnums.h (100%) rename src/{ => rgbcx}/ndebug.h (100%) rename src/{ => rgbcx}/rgbcx.cpp (100%) rename src/{ => rgbcx}/rgbcx.h (100%) rename src/{ => rgbcx}/rgbcxDecoders.h (100%) rename src/{ => rgbcx}/test/RedbrushAlpha.png (100%) rename src/{ => rgbcx}/test/bc7decomp.cpp (100%) rename src/{ => rgbcx}/test/bc7decomp.h (100%) rename src/{ => rgbcx}/test/bc7enc.c (100%) rename src/{ => rgbcx}/test/bc7enc.h (100%) rename src/{ => rgbcx}/test/dds_defs.h (100%) rename src/{ => rgbcx}/test/lodepng.cpp (100%) rename src/{ => rgbcx}/test/lodepng.h (100%) rename src/{ => rgbcx}/util.h (100%) delete mode 100644 src/test/test.cpp create mode 100644 tests/run_tests.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d518b56..1c64efa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,22 +2,23 @@ cmake_minimum_required(VERSION 3.17) include(CheckIPOSupported) include(tools/CompilerWarnings.cmake) -project(python_rgbcx) +project(rgbcx) # Link to Pybind +find_package(Python COMPONENTS Interpreter Development REQUIRED) add_subdirectory(extern/pybind11) # Collect source files -file(GLOB SOURCE_FILES "src/*.cpp" "src/BC*/*.cpp") -file(GLOB HEADER_FILES "src/*.h" "src/BC*/*.h") -file(GLOB PYTHON_FILES "python/*.cpp" "python/*.h") -file(GLOB TEST_FILES "src/test/*.c" "src/test/*.cpp" "src/test/*.h") +file(GLOB SOURCE_FILES "src/rgbcx/*.cpp" "src/rgbcx/BC*/*.cpp") +file(GLOB HEADER_FILES "src/rgbcx/*.h" "src/rgbcx/BC*/*.h") +file(GLOB PYTHON_FILES "src/rgbcx/bindings/*.cpp" "src/rgbcx/bindings/*.h") +file(GLOB TEST_FILES "tests/*.cpp") # Organize source files together for some IDEs source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCE_FILES} ${HEADER_FILES} ${PYTHON_FILES}) # Add python module -pybind11_add_module(python_rgbcx +pybind11_add_module(_rgbcx ${SOURCE_FILES} ${HEADER_FILES} ${PYTHON_FILES}) @@ -27,11 +28,16 @@ add_executable(test_rgbcx ${HEADER_FILES} ${TEST_FILES}) +target_link_libraries(test_rgbcx PRIVATE pybind11::embed) + +target_compile_definitions(test_rgbcx PRIVATE -DCUSTOM_SYS_PATH="${CMAKE_HOME_DIRECTORY}/env/lib/python3.8/site-packages") +message("\"${CMAKE_HOME_DIRECTORY}/env/lib/python3.8/site-packages\"") + # Set module features, like C/C++ standards -target_compile_features(python_rgbcx PUBLIC cxx_std_20 c_std_11) +target_compile_features(_rgbcx PUBLIC cxx_std_20 c_std_11) target_compile_features(test_rgbcx PUBLIC cxx_std_20 c_std_11) -set_project_warnings(python_rgbcx) +set_project_warnings(_rgbcx) set_project_warnings(test_rgbcx) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3") diff --git a/setup.py b/setup.py index 0462010..332586b 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ import os import sys import subprocess -from setuptools import setup, Extension +from setuptools import setup, Extension, find_packages from setuptools.command.build_ext import build_ext # Convert distutils Windows platform specifiers to CMake -A arguments @@ -102,14 +102,16 @@ class CMakeBuild(build_ext): # The information here can also be placed in setup.cfg - better separation of # logic and declaration, and simpler if you include description/version in a file. setup( - name="python_rgbcx", + name="rgbcx", version="0.0.1", author="Andrew Cassidy", author_email="drewcassidy@me.com", description="", long_description="", - ext_modules=[CMakeExtension("python_rgbcx")], + ext_modules=[CMakeExtension("_rgbcx")], cmdclass={"build_ext": CMakeBuild}, + packages=find_packages('src'), + package_dir={'': 'src'}, install_requires=["Pillow"], zip_safe=False, ) diff --git a/src/BC1/BC1Block.h b/src/rgbcx/BC1/BC1Block.h similarity index 100% rename from src/BC1/BC1Block.h rename to src/rgbcx/BC1/BC1Block.h diff --git a/src/BC1/BC1Decoder.cpp b/src/rgbcx/BC1/BC1Decoder.cpp similarity index 100% rename from src/BC1/BC1Decoder.cpp rename to src/rgbcx/BC1/BC1Decoder.cpp diff --git a/src/BC1/BC1Decoder.h b/src/rgbcx/BC1/BC1Decoder.h similarity index 100% rename from src/BC1/BC1Decoder.h rename to src/rgbcx/BC1/BC1Decoder.h diff --git a/src/BC1/BC1Encoder.cpp b/src/rgbcx/BC1/BC1Encoder.cpp similarity index 99% rename from src/BC1/BC1Encoder.cpp rename to src/rgbcx/BC1/BC1Encoder.cpp index cefb8f7..0ea15c5 100644 --- a/src/BC1/BC1Encoder.cpp +++ b/src/rgbcx/BC1/BC1Encoder.cpp @@ -667,7 +667,7 @@ template void BC1Encoder::FindSelectors(Color4x4 &pixe axis *= 2; for (unsigned i = 0; i < 16; i++) { - Vector4Int pixel_vector = (Vector4Int)pixels.Get(i); + Vector4Int pixel_vector = Vector4Int::FromColorRGB(pixels.Get(i)); int dot = axis.Dot(pixel_vector); uint8_t level = (dot <= t0) + (dot < t1) + (dot < t2); uint8_t selector = 3 - level; @@ -688,7 +688,7 @@ template void BC1Encoder::FindSelectors(Color4x4 &pixe const float f = 4.0f / ((float)axis.SqrMag() + .00000125f); for (unsigned i = 0; i < 16; i++) { - Vector4Int pixel_vector = (Vector4Int)pixels.Get(i); + Vector4Int pixel_vector = Vector4Int::FromColorRGB(pixels.Get(i)); auto diff = pixel_vector - color_vectors[0]; float sel_f = (float)diff.Dot(axis) * f + 0.5f; uint8_t sel = (uint8_t)clampi((int)sel_f, 1, 3); diff --git a/src/BC1/BC1Encoder.h b/src/rgbcx/BC1/BC1Encoder.h similarity index 100% rename from src/BC1/BC1Encoder.h rename to src/rgbcx/BC1/BC1Encoder.h diff --git a/src/BC1/Histogram.h b/src/rgbcx/BC1/Histogram.h similarity index 100% rename from src/BC1/Histogram.h rename to src/rgbcx/BC1/Histogram.h diff --git a/src/BC1/OrderTable.cpp b/src/rgbcx/BC1/OrderTable.cpp similarity index 100% rename from src/BC1/OrderTable.cpp rename to src/rgbcx/BC1/OrderTable.cpp diff --git a/src/BC1/OrderTable.h b/src/rgbcx/BC1/OrderTable.h similarity index 100% rename from src/BC1/OrderTable.h rename to src/rgbcx/BC1/OrderTable.h diff --git a/src/BC1/SingleColorTable.h b/src/rgbcx/BC1/SingleColorTable.h similarity index 100% rename from src/BC1/SingleColorTable.h rename to src/rgbcx/BC1/SingleColorTable.h diff --git a/src/BC1/Table4.h b/src/rgbcx/BC1/Table4.h similarity index 100% rename from src/BC1/Table4.h rename to src/rgbcx/BC1/Table4.h diff --git a/src/BC1/Tables.cpp b/src/rgbcx/BC1/Tables.cpp similarity index 100% rename from src/BC1/Tables.cpp rename to src/rgbcx/BC1/Tables.cpp diff --git a/src/BC1/Tables.h b/src/rgbcx/BC1/Tables.h similarity index 100% rename from src/BC1/Tables.h rename to src/rgbcx/BC1/Tables.h diff --git a/src/BC3/BC3Block.h b/src/rgbcx/BC3/BC3Block.h similarity index 100% rename from src/BC3/BC3Block.h rename to src/rgbcx/BC3/BC3Block.h diff --git a/src/BC3/BC3Decoder.cpp b/src/rgbcx/BC3/BC3Decoder.cpp similarity index 100% rename from src/BC3/BC3Decoder.cpp rename to src/rgbcx/BC3/BC3Decoder.cpp diff --git a/src/BC3/BC3Decoder.h b/src/rgbcx/BC3/BC3Decoder.h similarity index 100% rename from src/BC3/BC3Decoder.h rename to src/rgbcx/BC3/BC3Decoder.h diff --git a/src/BC4/BC4Block.h b/src/rgbcx/BC4/BC4Block.h similarity index 100% rename from src/BC4/BC4Block.h rename to src/rgbcx/BC4/BC4Block.h diff --git a/src/BC4/BC4Decoder.cpp b/src/rgbcx/BC4/BC4Decoder.cpp similarity index 100% rename from src/BC4/BC4Decoder.cpp rename to src/rgbcx/BC4/BC4Decoder.cpp diff --git a/src/BC4/BC4Decoder.h b/src/rgbcx/BC4/BC4Decoder.h similarity index 100% rename from src/BC4/BC4Decoder.h rename to src/rgbcx/BC4/BC4Decoder.h diff --git a/src/BC4/BC4Encoder.cpp b/src/rgbcx/BC4/BC4Encoder.cpp similarity index 100% rename from src/BC4/BC4Encoder.cpp rename to src/rgbcx/BC4/BC4Encoder.cpp diff --git a/src/BC4/BC4Encoder.h b/src/rgbcx/BC4/BC4Encoder.h similarity index 100% rename from src/BC4/BC4Encoder.h rename to src/rgbcx/BC4/BC4Encoder.h diff --git a/src/BC5/BC5Block.h b/src/rgbcx/BC5/BC5Block.h similarity index 100% rename from src/BC5/BC5Block.h rename to src/rgbcx/BC5/BC5Block.h diff --git a/src/BC5/BC5Decoder.cpp b/src/rgbcx/BC5/BC5Decoder.cpp similarity index 100% rename from src/BC5/BC5Decoder.cpp rename to src/rgbcx/BC5/BC5Decoder.cpp diff --git a/src/BC5/BC5Decoder.h b/src/rgbcx/BC5/BC5Decoder.h similarity index 100% rename from src/BC5/BC5Decoder.h rename to src/rgbcx/BC5/BC5Decoder.h diff --git a/src/BlockDecoder.h b/src/rgbcx/BlockDecoder.h similarity index 100% rename from src/BlockDecoder.h rename to src/rgbcx/BlockDecoder.h diff --git a/src/BlockEncoder.cpp b/src/rgbcx/BlockEncoder.cpp similarity index 100% rename from src/BlockEncoder.cpp rename to src/rgbcx/BlockEncoder.cpp diff --git a/src/BlockEncoder.h b/src/rgbcx/BlockEncoder.h similarity index 100% rename from src/BlockEncoder.h rename to src/rgbcx/BlockEncoder.h diff --git a/src/BlockView.h b/src/rgbcx/BlockView.h similarity index 100% rename from src/BlockView.h rename to src/rgbcx/BlockView.h diff --git a/src/Color.cpp b/src/rgbcx/Color.cpp similarity index 100% rename from src/Color.cpp rename to src/rgbcx/Color.cpp diff --git a/src/Color.h b/src/rgbcx/Color.h similarity index 100% rename from src/Color.h rename to src/rgbcx/Color.h diff --git a/src/Interpolator.cpp b/src/rgbcx/Interpolator.cpp similarity index 100% rename from src/Interpolator.cpp rename to src/rgbcx/Interpolator.cpp diff --git a/src/Interpolator.h b/src/rgbcx/Interpolator.h similarity index 100% rename from src/Interpolator.h rename to src/rgbcx/Interpolator.h diff --git a/src/Matrix4x4.cpp b/src/rgbcx/Matrix4x4.cpp similarity index 100% rename from src/Matrix4x4.cpp rename to src/rgbcx/Matrix4x4.cpp diff --git a/src/Matrix4x4.h b/src/rgbcx/Matrix4x4.h similarity index 100% rename from src/Matrix4x4.h rename to src/rgbcx/Matrix4x4.h diff --git a/src/Vector4.h b/src/rgbcx/Vector4.h similarity index 100% rename from src/Vector4.h rename to src/rgbcx/Vector4.h diff --git a/src/Vector4Int.h b/src/rgbcx/Vector4Int.h similarity index 100% rename from src/Vector4Int.h rename to src/rgbcx/Vector4Int.h diff --git a/src/rgbcx/__init__.py b/src/rgbcx/__init__.py new file mode 100644 index 0000000..094a8f4 --- /dev/null +++ b/src/rgbcx/__init__.py @@ -0,0 +1 @@ +from _rgbcx import * diff --git a/python/BC1.cpp b/src/rgbcx/bindings/BC1.cpp similarity index 97% rename from python/BC1.cpp rename to src/rgbcx/bindings/BC1.cpp index d6b0308..a42ec67 100644 --- a/python/BC1.cpp +++ b/src/rgbcx/bindings/BC1.cpp @@ -19,9 +19,9 @@ #include -#include "../src/BC1/BC1Encoder.h" -#include "../src/BlockEncoder.h" -#include "../src/bitwiseEnums.h" +#include "../BlockEncoder.h" +#include "../bitwiseEnums.h" +#include "../BC1/BC1Encoder.h" #define STRINGIFY(x) #x #define MACRO_STRINGIFY(x) STRINGIFY(x) diff --git a/python/BlockEncoder.cpp b/src/rgbcx/bindings/BlockEncoder.cpp similarity index 97% rename from python/BlockEncoder.cpp rename to src/rgbcx/bindings/BlockEncoder.cpp index db46e9e..2913fe1 100644 --- a/python/BlockEncoder.cpp +++ b/src/rgbcx/bindings/BlockEncoder.cpp @@ -17,13 +17,13 @@ along with this program. If not, see . */ -#include "../src/BlockEncoder.h" +#include "../BlockEncoder.h" #include #include -#include "../src/bitwiseEnums.h" +#include "../bitwiseEnums.h" #define STRINGIFY(x) #x #define MACRO_STRINGIFY(x) STRINGIFY(x) diff --git a/python/Module.cpp b/src/rgbcx/bindings/Module.cpp similarity index 93% rename from python/Module.cpp rename to src/rgbcx/bindings/Module.cpp index 4eb16dd..1e45a7d 100644 --- a/python/Module.cpp +++ b/src/rgbcx/bindings/Module.cpp @@ -18,8 +18,9 @@ */ #include -#include "../src/BlockEncoder.h" -#include "../src/Interpolator.h" + +#include "../BlockEncoder.h" +#include "../Interpolator.h" #define STRINGIFY(x) #x #define MACRO_STRINGIFY(x) STRINGIFY(x) @@ -30,8 +31,7 @@ namespace rgbcx::bindings { void InitBlockEncoder(py::module_ &m); void InitBC1(py::module_ &m); -PYBIND11_MODULE(python_rgbcx, m) { - +PYBIND11_MODULE(_rgbcx, m) { m.doc() = "More Stuff"; using IType = Interpolator::Type; diff --git a/src/bitwiseEnums.h b/src/rgbcx/bitwiseEnums.h similarity index 100% rename from src/bitwiseEnums.h rename to src/rgbcx/bitwiseEnums.h diff --git a/src/ndebug.h b/src/rgbcx/ndebug.h similarity index 100% rename from src/ndebug.h rename to src/rgbcx/ndebug.h diff --git a/src/rgbcx.cpp b/src/rgbcx/rgbcx.cpp similarity index 100% rename from src/rgbcx.cpp rename to src/rgbcx/rgbcx.cpp diff --git a/src/rgbcx.h b/src/rgbcx/rgbcx.h similarity index 100% rename from src/rgbcx.h rename to src/rgbcx/rgbcx.h diff --git a/src/rgbcxDecoders.h b/src/rgbcx/rgbcxDecoders.h similarity index 100% rename from src/rgbcxDecoders.h rename to src/rgbcx/rgbcxDecoders.h diff --git a/src/test/RedbrushAlpha.png b/src/rgbcx/test/RedbrushAlpha.png similarity index 100% rename from src/test/RedbrushAlpha.png rename to src/rgbcx/test/RedbrushAlpha.png diff --git a/src/test/bc7decomp.cpp b/src/rgbcx/test/bc7decomp.cpp similarity index 100% rename from src/test/bc7decomp.cpp rename to src/rgbcx/test/bc7decomp.cpp diff --git a/src/test/bc7decomp.h b/src/rgbcx/test/bc7decomp.h similarity index 100% rename from src/test/bc7decomp.h rename to src/rgbcx/test/bc7decomp.h diff --git a/src/test/bc7enc.c b/src/rgbcx/test/bc7enc.c similarity index 100% rename from src/test/bc7enc.c rename to src/rgbcx/test/bc7enc.c diff --git a/src/test/bc7enc.h b/src/rgbcx/test/bc7enc.h similarity index 100% rename from src/test/bc7enc.h rename to src/rgbcx/test/bc7enc.h diff --git a/src/test/dds_defs.h b/src/rgbcx/test/dds_defs.h similarity index 100% rename from src/test/dds_defs.h rename to src/rgbcx/test/dds_defs.h diff --git a/src/test/lodepng.cpp b/src/rgbcx/test/lodepng.cpp similarity index 100% rename from src/test/lodepng.cpp rename to src/rgbcx/test/lodepng.cpp diff --git a/src/test/lodepng.h b/src/rgbcx/test/lodepng.h similarity index 100% rename from src/test/lodepng.h rename to src/rgbcx/test/lodepng.h diff --git a/src/util.h b/src/rgbcx/util.h similarity index 100% rename from src/util.h rename to src/rgbcx/util.h diff --git a/src/test/test.cpp b/src/test/test.cpp deleted file mode 100644 index 469b596..0000000 --- a/src/test/test.cpp +++ /dev/null @@ -1,881 +0,0 @@ -// test.cpp - Command line example/test app -#ifdef _MSC_VER -#define _CRT_SECURE_NO_WARNINGS -#endif -#pragma GCC diagnostic ignored "-Weverything" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../BC4/BC4Encoder.h" -#include "../BC1/BC1Encoder.h" -#include "../rgbcx.h" -#include "../rgbcxDecoders.h" -#include "../util.h" -#include "bc7decomp.h" -#include "bc7enc.h" -#include "dds_defs.h" -#include "lodepng.h" - -using namespace rgbcx; - -const int MAX_UBER_LEVEL = 5; - -static int print_usage() { - fprintf(stderr, "bc7enc\n"); - fprintf(stderr, - "Reads PNG files (with or without alpha channels) and packs them to BC1-5 or BC7/BPTC (default) using\nmodes 1, 6 (opaque blocks) or modes 1, 5, " - "6, and 7 (alpha blocks).\n"); - fprintf(stderr, - "By default, a DX10 DDS file and a unpacked PNG file will be written to the current\ndirectory with the .dds/_unpacked.png/_unpacked_alpha.png " - "suffixes.\n\n"); - fprintf(stderr, "Usage: bc7enc [-apng_filename] [options] input_filename.png [compressed_output.dds] [unpacked_output.png]\n\n"); - fprintf(stderr, "-apng_filename Load G channel of PNG file into alpha channel of source image\n"); - fprintf(stderr, "-g Don't write unpacked output PNG files (this disables PSNR metrics too).\n"); - fprintf(stderr, "-y Flip source image along Y axis before packing\n"); - fprintf(stderr, "-o Write output files to the current directory\n"); - fprintf(stderr, "-1 Encode to BC1. -u[0,5] controls quality vs. perf. tradeoff for RGB.\n"); - fprintf(stderr, "-3 Encode to BC3. -u[0,5] controls quality vs. perf. tradeoff for RGB.\n"); - fprintf(stderr, "-4 Encode to BC4\n"); - fprintf(stderr, "-5 Encode to BC5\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "-X# BC4/5: Set first color channel (defaults to 0 or red)\n"); - fprintf(stderr, "-Y# BC4/5: Set second color channel (defaults to 1 or green)\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "-l BC7: Use linear colorspace metrics instead of perceptual\n"); - fprintf(stderr, "-uX BC1/3/7: Higher quality levels, X ranges from [0,4] for BC7, or [0,5] for BC1-3\n"); - fprintf(stderr, "-pX BC7: Scan X partitions in mode 1, X ranges from [0,64], use 0 to disable mode 1 entirely (faster)\n"); - fprintf(stderr, "\n"); - fprintf(stderr, - "-b BC1: Enable 3-color mode for blocks containing black or very dark pixels. (Important: engine/shader MUST ignore decoded texture alpha if this " - "flag is enabled!)\n"); - fprintf(stderr, "-c BC1: Disable 3-color mode for solid color blocks\n"); - fprintf(stderr, "-n BC1: Encode/decode for NVidia GPU's\n"); - fprintf(stderr, "-m BC1: Encode/decode for AMD GPU's\n"); - fprintf(stderr, "-r BC1: Encode/decode using ideal BC1 formulas with rounding for 4-color block colors 2,3 (same as AMD Compressonator)\n"); - fprintf(stderr, "-LX BC1: Set encoding level, where 0=fastest and 19=slowest but highest quality\n"); - fprintf(stderr, "-f Force writing DX10-style DDS files (otherwise for BC1-5 it uses DX9-style DDS files)\n"); - fprintf(stderr, - "\nBy default, this tool encodes to BC1 without rounding 4-color block colors 2,3, which may not match the output of some software decoders.\n"); - fprintf(stderr, "\nFor BC4 and BC5: Not all tools support reading DX9-style BC4/BC5 format files (or BC4/5 files at all). AMD Compressonator does.\n"); - - return EXIT_FAILURE; -} -/* -struct color_quad_u8 { - uint8_t m_c[4]; - - inline color_quad_u8(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { set(r, g, b, a); } - - inline color_quad_u8(uint8_t y = 0, uint8_t a = 255) { set(y, a); } - - inline color_quad_u8 &set(uint8_t y, uint8_t a = 255) { - m_c[0] = y; - m_c[1] = y; - m_c[2] = y; - m_c[3] = a; - return *this; - } - - inline color_quad_u8 &set(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { - m_c[0] = r; - m_c[1] = g; - m_c[2] = b; - m_c[3] = a; - return *this; - } - - inline uint8_t &operator[](uint32_t i) { - assert(i < 4); - return m_c[i]; - } - inline uint8_t operator[](uint32_t i) const { - assert(i < 4); - return m_c[i]; - } - - inline int GetLuma() const { return (13938U * m_c[0] + 46869U * m_c[1] + 4729U * m_c[2] + 32768U) >> 16U; } // REC709 weightings -};*/ -using color_quad_u8 = Color; -typedef std::vector color_quad_u8_vec; - -class image_u8 { - public: - image_u8() : m_width(0), m_height(0) {} - - image_u8(uint32_t width, uint32_t height) : m_width(width), m_height(height) { m_pixels.resize(width * height); } - - inline const color_quad_u8_vec &get_pixels() const { return m_pixels; } - inline color_quad_u8_vec &get_pixels() { return m_pixels; } - - void set_pixels(const color_quad_u8_vec &pixels) { m_pixels = pixels; } - - inline uint32_t width() const { return m_width; } - inline uint32_t height() const { return m_height; } - inline uint32_t total_pixels() const { return m_width * m_height; } - - inline color_quad_u8 &operator()(uint32_t x, uint32_t y) { - assert(x < m_width && y < m_height); - return m_pixels[x + m_width * y]; - } - inline const color_quad_u8 &operator()(uint32_t x, uint32_t y) const { - assert(x < m_width && y < m_height); - return m_pixels[x + m_width * y]; - } - - image_u8 &clear() { - m_width = m_height = 0; - m_pixels.clear(); - return *this; - } - - image_u8 &init(uint32_t width, uint32_t height) { - clear(); - - m_width = width; - m_height = height; - m_pixels.resize(width * height); - return *this; - } - - image_u8 &set_all(const color_quad_u8 &p) { - for (uint32_t i = 0; i < m_pixels.size(); i++) m_pixels[i] = p; - return *this; - } - - image_u8 &crop(uint32_t new_width, uint32_t new_height) { - if ((m_width == new_width) && (m_height == new_height)) return *this; - - image_u8 new_image(new_width, new_height); - - const uint32_t w = std::min(m_width, new_width); - const uint32_t h = std::min(m_height, new_height); - - for (uint32_t y = 0; y < h; y++) - for (uint32_t x = 0; x < w; x++) new_image(x, y) = (*this)(x, y); - - return swap(new_image); - } - - image_u8 &swap(image_u8 &other) { - std::swap(m_width, other.m_width); - std::swap(m_height, other.m_height); - std::swap(m_pixels, other.m_pixels); - return *this; - } - - inline void get_block(uint32_t bx, uint32_t by, uint32_t width, uint32_t height, color_quad_u8 *pPixels) { - assert((bx * width + width) <= m_width); - assert((by * height + height) <= m_height); - - for (uint32_t y = 0; y < height; y++) memcpy(pPixels + y * width, &(*this)(bx * width, by * height + y), width * sizeof(color_quad_u8)); - } - - inline void set_block(uint32_t bx, uint32_t by, uint32_t width, uint32_t height, const color_quad_u8 *pPixels) { - assert((bx * width + width) <= m_width); - assert((by * height + height) <= m_height); - - for (uint32_t y = 0; y < height; y++) memcpy(&(*this)(bx * width, by * height + y), pPixels + y * width, width * sizeof(color_quad_u8)); - } - - image_u8 &swizzle(uint32_t r, uint32_t g, uint32_t b, uint32_t a) { - assert((r | g | b | a) <= 3); - for (uint32_t y = 0; y < m_height; y++) { - for (uint32_t x = 0; x < m_width; x++) { - color_quad_u8 tmp((*this)(x, y)); - (*this)(x, y).SetRGBA(tmp[r], tmp[g], tmp[b], tmp[a]); - } - } - - return *this; - } - - private: - color_quad_u8_vec m_pixels; - uint32_t m_width, m_height; -}; - -static bool load_png(const char *pFilename, image_u8 &img) { - img.clear(); - - std::vector pixels; - unsigned int w = 0, h = 0; - unsigned int e = lodepng::decode(pixels, w, h, pFilename); - if (e != 0) { - fprintf(stderr, "Failed loading PNG file %s\n", pFilename); - return false; - } - - img.init(w, h); - memcpy(&img.get_pixels()[0], &pixels[0], w * h * sizeof(uint32_t)); - - return true; -} - -static bool save_png(const char *pFilename, const image_u8 &img, bool save_alpha) { - const uint32_t w = img.width(); - const uint32_t h = img.height(); - - std::vector pixels; - if (save_alpha) { - pixels.resize(w * h * sizeof(color_quad_u8)); - memcpy(&pixels[0], &img.get_pixels()[0], w * h * sizeof(color_quad_u8)); - } else { - pixels.resize(w * h * 3); - unsigned char *pDst = &pixels[0]; - for (uint32_t y = 0; y < h; y++) - for (uint32_t x = 0; x < w; x++, pDst += 3) pDst[0] = img(x, y)[0], pDst[1] = img(x, y)[1], pDst[2] = img(x, y)[2]; - } - - return lodepng::encode(pFilename, pixels, w, h, save_alpha ? LCT_RGBA : LCT_RGB) == 0; -} - -class image_metrics { - public: - double m_max, m_mean, m_mean_squared, m_root_mean_squared, m_peak_snr; - - image_metrics() { clear(); } - - void clear() { memset(this, 0, sizeof(*this)); } - - void compute(const image_u8 &a, const image_u8 &b, uint32_t first_channel, uint32_t num_channels) { - const bool average_component_error = true; - - const uint32_t width = std::min(a.width(), b.width()); - const uint32_t height = std::min(a.height(), b.height()); - - assert((first_channel < 4U) && (first_channel + num_channels <= 4U)); - - // Histogram approach originally due to Charles Bloom. - double hist[256]; - memset(hist, 0, sizeof(hist)); - - for (uint32_t y = 0; y < height; y++) { - for (uint32_t x = 0; x < width; x++) { - const color_quad_u8 &ca = a(x, y); - const color_quad_u8 &cb = b(x, y); - - if (!num_channels) { - // int luma_diff = ; - unsigned index = iabs(ca.GetLuma() - cb.GetLuma()); - hist[index]++; - } else { - for (uint32_t c = 0; c < num_channels; c++) hist[iabs(ca[first_channel + c] - cb[first_channel + c])]++; - } - } - } - - m_max = 0; - double sum = 0.0f, sum2 = 0.0f; - for (uint32_t i = 0; i < 256; i++) { - if (!hist[i]) continue; - - m_max = std::max(m_max, i); - - double x = i * hist[i]; - - sum += x; - sum2 += i * x; - } - - // See http://richg42.blogspot.com/2016/09/how-to-compute-psnr-from-old-berkeley.html - double total_values = width * height; - - if (average_component_error) total_values *= clamp(num_channels, 1, 4); - - m_mean = clamp(sum / total_values, 0.0f, 255.0f); - m_mean_squared = clamp(sum2 / total_values, 0.0f, 255.0f * 255.0f); - - m_root_mean_squared = sqrt(m_mean_squared); - - if (!m_root_mean_squared) - m_peak_snr = 100.0f; - else - m_peak_snr = clamp(log10(255.0f / m_root_mean_squared) * 20.0f, 0.0f, 100.0f); - } -}; - -struct block8 { - uint64_t m_vals[1]; -}; - -typedef std::vector block8_vec; - -struct block16 { - uint64_t m_vals[2]; -}; - -typedef std::vector block16_vec; - -static bool save_dds(const char *pFilename, uint32_t width, uint32_t height, const void *pBlocks, uint32_t pixel_format_bpp, DXGI_FORMAT dxgi_format, bool srgb, - bool force_dx10_header) { - (void)srgb; - - FILE *pFile = NULL; - pFile = fopen(pFilename, "wb"); - if (!pFile) { - fprintf(stderr, "Failed creating file %s!\n", pFilename); - return false; - } - - fwrite("DDS ", 4, 1, pFile); - - DDSURFACEDESC2 desc; - memset(&desc, 0, sizeof(desc)); - - desc.dwSize = sizeof(desc); - desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS; - - desc.dwWidth = width; - desc.dwHeight = height; - - desc.ddsCaps.dwCaps = DDSCAPS_TEXTURE; - desc.ddpfPixelFormat.dwSize = sizeof(desc.ddpfPixelFormat); - - desc.ddpfPixelFormat.dwFlags |= DDPF_FOURCC; - - desc.lPitch = (((desc.dwWidth + 3) & ~3) * ((desc.dwHeight + 3) & ~3) * pixel_format_bpp) >> 3; - desc.dwFlags |= DDSD_LINEARSIZE; - - desc.ddpfPixelFormat.dwRGBBitCount = 0; - - if ((!force_dx10_header) && ((dxgi_format == DXGI_FORMAT_BC1_UNORM) || (dxgi_format == DXGI_FORMAT_BC3_UNORM) || (dxgi_format == DXGI_FORMAT_BC4_UNORM) || - (dxgi_format == DXGI_FORMAT_BC5_UNORM))) { - if (dxgi_format == DXGI_FORMAT_BC1_UNORM) - desc.ddpfPixelFormat.dwFourCC = (uint32_t)PIXEL_FMT_FOURCC('D', 'X', 'T', '1'); - else if (dxgi_format == DXGI_FORMAT_BC3_UNORM) - desc.ddpfPixelFormat.dwFourCC = (uint32_t)PIXEL_FMT_FOURCC('D', 'X', 'T', '5'); - else if (dxgi_format == DXGI_FORMAT_BC4_UNORM) - desc.ddpfPixelFormat.dwFourCC = (uint32_t)PIXEL_FMT_FOURCC('A', 'T', 'I', '1'); - else if (dxgi_format == DXGI_FORMAT_BC5_UNORM) - desc.ddpfPixelFormat.dwFourCC = (uint32_t)PIXEL_FMT_FOURCC('A', 'T', 'I', '2'); - - fwrite(&desc, sizeof(desc), 1, pFile); - } else { - desc.ddpfPixelFormat.dwFourCC = (uint32_t)PIXEL_FMT_FOURCC('D', 'X', '1', '0'); - - fwrite(&desc, sizeof(desc), 1, pFile); - - DDS_HEADER_DXT10 hdr10; - memset(&hdr10, 0, sizeof(hdr10)); - - // Not all tools support DXGI_FORMAT_BC7_UNORM_SRGB (like NVTT), but ddsview in DirectXTex pays attention to it. So not sure what to do here. - // For best compatibility just write DXGI_FORMAT_BC7_UNORM. - // hdr10.dxgiFormat = srgb ? DXGI_FORMAT_BC7_UNORM_SRGB : DXGI_FORMAT_BC7_UNORM; - hdr10.dxgiFormat = dxgi_format; // DXGI_FORMAT_BC7_UNORM; - hdr10.resourceDimension = D3D10_RESOURCE_DIMENSION_TEXTURE2D; - hdr10.arraySize = 1; - - fwrite(&hdr10, sizeof(hdr10), 1, pFile); - } - - fwrite(pBlocks, desc.lPitch, 1, pFile); - - if (fclose(pFile) == EOF) { - fprintf(stderr, "Failed writing to DDS file %s!\n", pFilename); - return false; - } - - return true; -} - -static void strip_extension(std::string &s) { - for (int32_t i = (int32_t)s.size() - 1; i >= 0; i--) { - if (s[i] == '.') { - s.resize(i); - break; - } - } -} - -static void strip_path(std::string &s) { - for (int32_t i = (int32_t)s.size() - 1; i >= 0; i--) { - if ((s[i] == '/') || (s[i] == ':') || (s[i] == '\\')) { - s.erase(0, i + 1); - break; - } - } -} - -int main(int argc, char *argv[]) { - if (argc < 2) return print_usage(); - - std::string src_filename; - std::string src_alpha_filename; - std::string dds_output_filename; - std::string png_output_filename; - std::string png_alpha_output_filename; - - bool no_output_png = false; - bool out_cur_dir = false; - - int uber_level = 0; - int max_partitions_to_scan = BC7ENC_MAX_PARTITIONS1; - bool perceptual = true; - bool y_flip = false; - uint32_t bc45_channel0 = 0; - uint32_t bc45_channel1 = 1; - - rgbcx::bc1_approx_mode bc1_mode = rgbcx::bc1_approx_mode::cBC1Ideal; - bool use_bc1_3color_mode = true; - bool use_bc1_3color_mode_for_black = false; - int bc1_quality_level = 2; - - DXGI_FORMAT dxgi_format = DXGI_FORMAT_BC7_UNORM; - uint32_t pixel_format_bpp = 8; - bool force_dx10_dds = false; - - for (int i = 1; i < argc; i++) { - const char *pArg = argv[i]; - if (pArg[0] == '-') { - switch (pArg[1]) { - case '1': { - dxgi_format = DXGI_FORMAT_BC1_UNORM; - pixel_format_bpp = 4; - printf("Compressing to BC1\n"); - break; - } - case '3': { - dxgi_format = DXGI_FORMAT_BC3_UNORM; - pixel_format_bpp = 8; - printf("Compressing to BC3\n"); - break; - } - case '4': { - dxgi_format = DXGI_FORMAT_BC4_UNORM; - pixel_format_bpp = 4; - printf("Compressing to BC4\n"); - break; - } - case '5': { - dxgi_format = DXGI_FORMAT_BC5_UNORM; - pixel_format_bpp = 8; - printf("Compressing to BC5\n"); - break; - } - case 'y': { - y_flip = true; - break; - } - case 'a': { - src_alpha_filename = pArg + 2; - break; - } - case 'X': { - bc45_channel0 = atoi(pArg + 2); - if ((bc45_channel0 < 0) || (bc45_channel0 > 3)) { - fprintf(stderr, "Invalid argument: %s\n", pArg); - return EXIT_FAILURE; - } - break; - } - case 'Y': { - bc45_channel1 = atoi(pArg + 2); - if ((bc45_channel1 < 0) || (bc45_channel1 > 3)) { - fprintf(stderr, "Invalid argument: %s\n", pArg); - return EXIT_FAILURE; - } - break; - } - case 'f': { - force_dx10_dds = true; - break; - } - case 'u': { - uber_level = atoi(pArg + 2); - if ((uber_level < 0) || (uber_level > MAX_UBER_LEVEL)) { - fprintf(stderr, "Invalid argument: %s\n", pArg); - return EXIT_FAILURE; - } - break; - } - case 'L': { - bc1_quality_level = atoi(pArg + 2); - if (((int)bc1_quality_level < (int)rgbcx::MIN_LEVEL) || ((int)bc1_quality_level > (int)(rgbcx::MAX_LEVEL + 1))) { - fprintf(stderr, "Invalid argument: %s\n", pArg); - return EXIT_FAILURE; - } - break; - } - case 'g': { - no_output_png = true; - break; - } - case 'l': { - perceptual = false; - break; - } - case 'p': { - max_partitions_to_scan = atoi(pArg + 2); - if ((max_partitions_to_scan < 0) || (max_partitions_to_scan > BC7ENC_MAX_PARTITIONS1)) { - fprintf(stderr, "Invalid argument: %s\n", pArg); - return EXIT_FAILURE; - } - break; - } - case 'n': { - bc1_mode = rgbcx::bc1_approx_mode::cBC1NVidia; - break; - } - case 'm': { - bc1_mode = rgbcx::bc1_approx_mode::cBC1AMD; - break; - } - case 'r': { - bc1_mode = rgbcx::bc1_approx_mode::cBC1IdealRound4; - break; - } - case 'o': { - out_cur_dir = true; - break; - } - case 'b': { - use_bc1_3color_mode_for_black = true; - break; - } - case 'c': { - use_bc1_3color_mode = false; - break; - } - default: { - fprintf(stderr, "Invalid argument: %s\n", pArg); - return EXIT_FAILURE; - } - } - } else { - if (!src_filename.size()) - src_filename = pArg; - else if (!dds_output_filename.size()) - dds_output_filename = pArg; - else if (!png_output_filename.size()) - png_output_filename = pArg; - else { - fprintf(stderr, "Invalid argument: %s\n", pArg); - return EXIT_FAILURE; - } - } - } - - const uint32_t bytes_per_block = (16 * pixel_format_bpp) / 8; - assert(bytes_per_block == 8 || bytes_per_block == 16); - - if (!src_filename.size()) { - fprintf(stderr, "No source filename specified!\n"); - return EXIT_FAILURE; - } - - if (!dds_output_filename.size()) { - dds_output_filename = src_filename; - strip_extension(dds_output_filename); - if (out_cur_dir) strip_path(dds_output_filename); - dds_output_filename += ".dds"; - } - - if (!png_output_filename.size()) { - png_output_filename = src_filename; - strip_extension(png_output_filename); - if (out_cur_dir) strip_path(png_output_filename); - png_output_filename += "_unpacked.png"; - } - - png_alpha_output_filename = png_output_filename; - strip_extension(png_alpha_output_filename); - png_alpha_output_filename += "_alpha.png"; - - image_u8 source_image; - if (!load_png(src_filename.c_str(), source_image)) return EXIT_FAILURE; - - printf("Source image: %s %ux%u\n", src_filename.c_str(), source_image.width(), source_image.height()); - - if (src_alpha_filename.size()) { - image_u8 source_alpha_image; - if (!load_png(src_alpha_filename.c_str(), source_alpha_image)) return EXIT_FAILURE; - - printf("Source alpha image: %s %ux%u\n", src_alpha_filename.c_str(), source_alpha_image.width(), source_alpha_image.height()); - - const uint32_t w = std::min(source_alpha_image.width(), source_image.width()); - const uint32_t h = std::min(source_alpha_image.height(), source_image.height()); - - for (uint32_t y = 0; y < h; y++) - for (uint32_t x = 0; x < w; x++) source_image(x, y)[3] = source_alpha_image(x, y)[1]; - } - -#if 0 - // HACK HACK - for (uint32_t y = 0; y < source_image.height(); y++) - for (uint32_t x = 0; x < source_image.width(); x++) - source_image(x, y)[3] = 254; -#endif - - const uint32_t orig_width = source_image.width(); - const uint32_t orig_height = source_image.height(); - - if (y_flip) { - image_u8 temp; - temp.init(orig_width, orig_height); - - for (uint32_t y = 0; y < orig_height; y++) - for (uint32_t x = 0; x < orig_width; x++) temp(x, (orig_height - 1) - y) = source_image(x, y); - - temp.swap(source_image); - } - - source_image.crop((source_image.width() + 3) & ~3, (source_image.height() + 3) & ~3); - - const uint32_t blocks_x = source_image.width() / 4; - const uint32_t blocks_y = source_image.height() / 4; - - block16_vec packed_image16(blocks_x * blocks_y); - block8_vec packed_image8(blocks_x * blocks_y); - - bc7enc_compress_block_params pack_params; - bc7enc_compress_block_params_init(&pack_params); - if (!perceptual) bc7enc_compress_block_params_init_linear_weights(&pack_params); - pack_params.m_max_partitions_mode = max_partitions_to_scan; - pack_params.m_uber_level = std::min(BC7ENC_MAX_UBER_LEVEL, uber_level); - - if (dxgi_format == DXGI_FORMAT_BC7_UNORM) { - printf("Max mode 1 partitions: %u, uber level: %u, perceptual: %u\n", pack_params.m_max_partitions_mode, pack_params.m_uber_level, perceptual); - } else { - printf("Level: %u, use 3-color mode: %u, use 3-color mode for black: %u, bc1_mode: %u\n", bc1_quality_level, use_bc1_3color_mode, - use_bc1_3color_mode_for_black, (int)bc1_mode); - } - - bc7enc_compress_block_init(); - rgbcx::init(bc1_mode); - - bool has_alpha = false; - - clock_t start_t = clock(); - uint32_t bc7_mode_hist[8]; - memset(bc7_mode_hist, 0, sizeof(bc7_mode_hist)); - -#ifdef NDEBUG - const int test_count = 1000; -#else - const int test_count = 1; -#endif - - if (dxgi_format == DXGI_FORMAT_BC4_UNORM) { - auto bc4_encoder = BC4Encoder(bc45_channel0); - Color *src = &source_image.get_pixels()[0]; - - for (int i = 0; i < test_count; i++) - bc4_encoder.EncodeImage(reinterpret_cast(&packed_image8[0]), src, source_image.width(), source_image.height()); - } else if (dxgi_format == DXGI_FORMAT_BC1_UNORM) { - auto bc1_encoder = BC1Encoder(bc1_quality_level, use_bc1_3color_mode, use_bc1_3color_mode_for_black); - Color *src = &source_image.get_pixels()[0]; - - bc1_encoder.EncodeImage(reinterpret_cast(&packed_image8[0]), src, source_image.width(), source_image.height()); - } else { - for (uint32_t by = 0; by < blocks_y; by++) { - for (uint32_t bx = 0; bx < blocks_x; bx++) { - color_quad_u8 pixels[16]; - - source_image.get_block(bx, by, 4, 4, pixels); - if (!has_alpha) { - for (uint32_t i = 0; i < 16; i++) { - if (pixels[i][3] < 255) { - has_alpha = true; - break; - } - } - } - - switch (dxgi_format) { - case DXGI_FORMAT_BC1_UNORM: { - block8 *pBlock = &packed_image8[bx + by * blocks_x]; - - rgbcx::encode_bc1(bc1_quality_level, pBlock, &pixels[0][0], use_bc1_3color_mode, use_bc1_3color_mode_for_black); - break; - } - case DXGI_FORMAT_BC3_UNORM: { - BC3Block *pBlock = reinterpret_cast(&packed_image16[bx + by * blocks_x]); - - rgbcx::encode_bc3(bc1_quality_level, pBlock, &pixels[0][0]); - break; - } - case DXGI_FORMAT_BC4_UNORM: { - block8 *pBlock = &packed_image8[bx + by * blocks_x]; - - rgbcx::encode_bc4(pBlock, &pixels[0][bc45_channel0], 4); - break; - } - case DXGI_FORMAT_BC5_UNORM: { - block16 *pBlock = &packed_image16[bx + by * blocks_x]; - - rgbcx::encode_bc5(reinterpret_cast(pBlock), &pixels[0][0], bc45_channel0, bc45_channel1, 4); - break; - } - case DXGI_FORMAT_BC7_UNORM: { - block16 *pBlock = &packed_image16[bx + by * blocks_x]; - - bc7enc_compress_block(pBlock, pixels, &pack_params); - - uint32_t mode = ((uint8_t *)pBlock)[0]; - for (uint32_t m = 0; m <= 7; m++) { - if (mode & (1 << m)) { - bc7_mode_hist[m]++; - break; - } - } - break; - } - default: { - assert(0); - break; - } - } - } - - if ((by & 127) == 0) printf("."); - } - } - - clock_t end_t = clock(); - - printf("\nTotal time: %f secs\n", (double)(end_t - start_t) / CLOCKS_PER_SEC / 1); - - if (dxgi_format == DXGI_FORMAT_BC7_UNORM) { - printf("BC7 mode histogram:\n"); - for (uint32_t i = 0; i < 8; i++) printf("%u: %u\n", i, bc7_mode_hist[i]); - } - - if (has_alpha) printf("Source image had an alpha channel.\n"); - - bool failed = false; - if (!save_dds(dds_output_filename.c_str(), orig_width, orig_height, (bytes_per_block == 16) ? (void *)&packed_image16[0] : (void *)&packed_image8[0], - pixel_format_bpp, dxgi_format, perceptual, force_dx10_dds)) - failed = true; - else - printf("Wrote DDS file %s\n", dds_output_filename.c_str()); - - if ((!no_output_png) && (png_output_filename.size())) { - clock_t start_decode_t = clock(); - - image_u8 unpacked_image(source_image.width(), source_image.height()); - - bool punchthrough_flag = false; - auto decoder_bc1 = rgbcx::BC1Decoder(); - auto decoder_bc3 = rgbcx::BC3Decoder(); - auto decoder_bc4 = rgbcx::BC4Decoder(bc45_channel0); - auto decoder_bc5 = rgbcx::BC5Decoder(); - Color *dest = &unpacked_image.get_pixels()[0]; - - switch (dxgi_format) { - case DXGI_FORMAT_BC1_UNORM: - decoder_bc1.DecodeImage(reinterpret_cast(&packed_image8[0]), dest, source_image.width(), source_image.height()); - break; - case DXGI_FORMAT_BC3_UNORM: - decoder_bc3.DecodeImage(reinterpret_cast(&packed_image16[0]), dest, source_image.width(), source_image.height()); - break; - case DXGI_FORMAT_BC4_UNORM: - decoder_bc4.DecodeImage(reinterpret_cast(&packed_image8[0]), dest, source_image.width(), source_image.height()); - break; - case DXGI_FORMAT_BC5_UNORM: - decoder_bc5.DecodeImage(reinterpret_cast(&packed_image16[0]), dest, source_image.width(), source_image.height()); - break; - default: - assert(0); - break; - } - - // for (uint32_t by = 0; by < blocks_y; by++) { - // for (uint32_t bx = 0; bx < blocks_x; bx++) { - // void *pBlock = (bytes_per_block == 16) ? (void *)&packed_image16[bx + by * blocks_x] : (void *)&packed_image8[bx + by * blocks_x]; - // - // color_quad_u8 unpacked_pixels[16]; - // for (uint32_t i = 0; i < 16; i++) unpacked_pixels[i].Set(0, 0, 0, 255); - // - // switch (dxgi_format) { - // case DXGI_FORMAT_BC1_UNORM: - // rgbcx::unpack_bc1(pBlock, unpacked_pixels, true, bc1_mode); - // break; - // case DXGI_FORMAT_BC3_UNORM: - // if (!rgbcx::unpack_bc3(pBlock, unpacked_pixels, bc1_mode)) punchthrough_flag = true; - // break; - // case DXGI_FORMAT_BC4_UNORM: - // rgbcx::unpack_bc4(pBlock, &unpacked_pixels[0][0], 4); - // break; - // case DXGI_FORMAT_BC5_UNORM: - // rgbcx::unpack_bc5(pBlock, &unpacked_pixels[0][0], 0, 1, 4); - // break; - // case DXGI_FORMAT_BC7_UNORM: - // bc7decomp::unpack_bc7((const uint8_t *)pBlock, (bc7decomp::color_rgba *)unpacked_pixels); - // break; - // default: - // assert(0); - // break; - // } - // - // unpacked_image.set_block(bx, by, 4, 4, unpacked_pixels); - // } // bx - // } // by - clock_t end_decode_t = clock(); - printf("\nDecode time: %f secs\n", (double)(end_decode_t - start_decode_t) / CLOCKS_PER_SEC); - - if ((punchthrough_flag) && (dxgi_format == DXGI_FORMAT_BC3_UNORM)) - fprintf(stderr, "Warning: BC3 mode selected, but rgbcx::unpack_bc3() returned one or more blocks using 3-color mode!\n"); - - if ((dxgi_format != DXGI_FORMAT_BC4_UNORM) && (dxgi_format != DXGI_FORMAT_BC5_UNORM)) { - image_metrics y_metrics; - y_metrics.compute(source_image, unpacked_image, 0, 0); - printf("Luma Max error: %3.0f RMSE: %f PSNR %03.02f dB\n", y_metrics.m_max, y_metrics.m_root_mean_squared, y_metrics.m_peak_snr); - - image_metrics rgb_metrics; - rgb_metrics.compute(source_image, unpacked_image, 0, 3); - printf("RGB Max error: %3.0f RMSE: %f PSNR %03.02f dB\n", rgb_metrics.m_max, rgb_metrics.m_root_mean_squared, rgb_metrics.m_peak_snr); - - image_metrics rgba_metrics; - rgba_metrics.compute(source_image, unpacked_image, 0, 4); - printf("RGBA Max error: %3.0f RMSE: %f PSNR %03.02f dB\n", rgba_metrics.m_max, rgba_metrics.m_root_mean_squared, rgba_metrics.m_peak_snr); - } - - for (uint32_t chan = 0; chan < 4; chan++) { - if (dxgi_format == DXGI_FORMAT_BC4_UNORM) { - if (chan != bc45_channel0) continue; - } else if (dxgi_format == DXGI_FORMAT_BC5_UNORM) { - if ((chan != bc45_channel0) && (chan != bc45_channel1)) continue; - } - - image_metrics c_metrics; - c_metrics.compute(source_image, unpacked_image, chan, 1); - static const char *s_chan_names[4] = {"Red ", "Green", "Blue ", "Alpha"}; - printf("%s Max error: %3.0f RMSE: %f PSNR %03.02f dB\n", s_chan_names[chan], c_metrics.m_max, c_metrics.m_root_mean_squared, c_metrics.m_peak_snr); - } - - if (bc1_mode != rgbcx::bc1_approx_mode::cBC1Ideal) printf("Note: BC1/BC3 RGB decoding was done with the specified vendor's BC1 approximations.\n"); - - if (!save_png(png_output_filename.c_str(), unpacked_image, false)) - failed = true; - else - printf("Wrote PNG file %s\n", png_output_filename.c_str()); - - if (png_alpha_output_filename.size()) { - image_u8 unpacked_image_alpha(unpacked_image); - for (uint32_t y = 0; y < unpacked_image_alpha.height(); y++) - for (uint32_t x = 0; x < unpacked_image_alpha.width(); x++) { - uint8_t alpha = unpacked_image_alpha(x, y).a; - unpacked_image_alpha(x, y).SetRGBA(alpha, alpha, alpha, 255); - } - - if (!save_png(png_alpha_output_filename.c_str(), unpacked_image_alpha, false)) - failed = true; - else - printf("Wrote PNG file %s\n", png_alpha_output_filename.c_str()); - } - } - - return failed ? EXIT_FAILURE : EXIT_SUCCESS; -} -#pragma GCC diagnostic pop \ No newline at end of file diff --git a/tests/run_tests.cpp b/tests/run_tests.cpp new file mode 100644 index 0000000..73913e8 --- /dev/null +++ b/tests/run_tests.cpp @@ -0,0 +1,38 @@ +/* Python-rgbcx Texture Compression Library + Copyright (C) 2021 Andrew Cassidy + Partially derived from rgbcx.h written by Richard Geldreich + and licenced under the public domain + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . + */ + +// This file allows for easy debugging in CLion or other IDEs that dont natively support cross-debugging between Python and C++ + +#include +namespace py = pybind11; + +#define STRINGIFY(x) #x +#define MACRO_STRINGIFY(x) STRINGIFY(x) + +int main() { + py::scoped_interpreter guard{}; + + py::module_ site = py::module_::import("site"); + py::module_ unittest = py::module_::import("unittest"); + + site.attr("addsitedir")(CUSTOM_SYS_PATH); + + py::module_ tests = py::module_::import("test_rgbcx"); + unittest.attr("main")("test_rgbcx"); +} \ No newline at end of file diff --git a/tests/s3tc.py b/tests/s3tc.py index 93379f2..62d97d4 100644 --- a/tests/s3tc.py +++ b/tests/s3tc.py @@ -40,7 +40,7 @@ class BC1Block: return f'color0: {str(self.color0)} color1: {str(self.color1)}, indices:{self.selectors}' @staticmethod - def from_bytes(data): + def frombytes(data): block = struct.unpack_from('<2H4B', data) result = BC1Block() @@ -49,7 +49,7 @@ class BC1Block: result.selectors = [bit_slice(row, 2, 4) for row in block[2:6]] return result - def to_bytes(self): + def tobytes(self): return struct.pack('<2H4B', self.color0.to_565(), self.color1.to_565(), *(bit_merge(row, 2) for row in self.selectors)) @@ -70,7 +70,7 @@ class BC4Block: return repr(self.__dict__) @staticmethod - def from_bytes(data): + def frombytes(data): block = struct.unpack_from('<2B6B', data) result = BC4Block() @@ -79,7 +79,7 @@ class BC4Block: result.selectors = triple_slice(block[2:5]) + triple_slice(block[5:8]) return result - def to_bytes(self): + def tobytes(self): return struct.pack('<2B6B', int(self.alpha0 * 0xFF), int(self.alpha1 * 0xFF), *triple_merge(self.selectors[0:2]), diff --git a/tests/test_rgbcx.py b/tests/test_rgbcx.py index 0554cf7..3b29d2b 100644 --- a/tests/test_rgbcx.py +++ b/tests/test_rgbcx.py @@ -1,7 +1,6 @@ import unittest -import python_rgbcx -import color import os +import rgbcx import s3tc from PIL import Image @@ -11,18 +10,23 @@ image_path = dir_path + "/images" # A block that should always encode greyscale, where every row of pixels is identical, and the left side is lighter than the right side greyscale = Image.open(image_path + "/blocks/greyscale.png").tobytes("raw", "RGBX") -# A block that should always encode 3-color when available +# A block that should always encode 3-color when available. +# from left to right: red, yellow, yellow, green three_color = Image.open(image_path + "/blocks/3color.png").tobytes("raw", "RGBX") # A block that should always encode 3-color with black when available +# from left to right: black, red, yellow, green three_color_black = Image.open(image_path + "/blocks/3color black.png").tobytes("raw", "RGBX") class TestBC1Encoder(unittest.TestCase): + """Test BC1 encoder with a variety of inputs with 3 color blocks disabled.""" + def setUp(self): - self.bc1_encoder = python_rgbcx.BC1Encoder(python_rgbcx.InterpolatorType.Ideal, 5, False, False) + self.bc1_encoder = rgbcx.BC1Encoder(rgbcx.InterpolatorType.Ideal, 5, False, False) def test_block_size(self): + """Ensure encoded block size is 8 bytes.""" out = self.bc1_encoder.encode_image(greyscale, 4, 4) self.assertEqual(self.bc1_encoder.block_width, 4, 'incorrect reported block width') @@ -30,43 +34,90 @@ class TestBC1Encoder(unittest.TestCase): self.assertEqual(self.bc1_encoder.block_size, 8, 'incorrect reported block size') self.assertEqual(len(out), 8, 'incorrect returned block size') - def test_block_3color(self): - out = s3tc.BC1Block.from_bytes(self.bc1_encoder.encode_image(three_color_black, 4, 4)) + def test_block_4color(self): + """Ensure encoding the greyscale test block results in a 4 color block.""" + out = s3tc.BC1Block.frombytes(self.bc1_encoder.encode_image(greyscale, 4, 4)) - self.assertFalse(out.is_3color(), "returned 3 color block with use_3color disabled") + self.assertFalse(out.is_3color(), "returned 3 color block for greyscale test block") + + def test_block_greyscale_selectors(self): + """Ensure encoding the greyscale test block results in the correct selectors.""" + out = s3tc.BC1Block.frombytes(self.bc1_encoder.encode_image(greyscale, 4, 4)) + first_row = out.selectors[0] + expected_row = [0, 2, 3, 1] + + self.assertTrue(all(row == first_row for row in out.selectors), "block has different selectors in each row") + self.assertTrue(all(row == expected_row for row in out.selectors), "block has incorrect selectors for greyscale test block") + + def test_block_3color(self): + """Ensure encoder doesn't output a 3 color block.""" + out1 = s3tc.BC1Block.frombytes(self.bc1_encoder.encode_image(three_color, 4, 4)) + out2 = s3tc.BC1Block.frombytes(self.bc1_encoder.encode_image(three_color_black, 4, 4)) + + self.assertFalse(out1.is_3color(), "returned 3 color block with use_3color disabled") + self.assertFalse(out2.is_3color(), "returned 3 color block with use_3color disabled") class TestBC1Encoder3Color(unittest.TestCase): + """Test BC1 encoder with a variety of inputs with 3 color blocks enabled.""" + def setUp(self): - self.bc1_encoder = python_rgbcx.BC1Encoder(python_rgbcx.InterpolatorType.Ideal, 5, True, False) + self.bc1_encoder = rgbcx.BC1Encoder(rgbcx.InterpolatorType.Ideal, 5, True, False) + + def test_block_4color(self): + """Ensure encoding the greyscale test block results in a 4 color block.""" + out = s3tc.BC1Block.frombytes(self.bc1_encoder.encode_image(greyscale, 4, 4)) + + self.assertFalse(out.is_3color(), "returned 3 color block for greyscale test block") def test_block_3color(self): - out = s3tc.BC1Block.from_bytes(self.bc1_encoder.encode_image(three_color, 4, 4)) + """Ensure encoding the 3 color test block results in a 3 color block.""" + out = s3tc.BC1Block.frombytes(self.bc1_encoder.encode_image(three_color, 4, 4)) self.assertTrue(out.is_3color(), "returned 4 color block with use_3color enabled") def test_block_3color_black(self): - out = s3tc.BC1Block.from_bytes(self.bc1_encoder.encode_image(three_color_black, 4, 4)) + """Ensure encoder doesn't output a 3 color block with black pixels.""" + out = s3tc.BC1Block.frombytes(self.bc1_encoder.encode_image(three_color_black, 4, 4)) self.assertFalse(out.is_3color() and any(3 in row for row in out.selectors), "returned 3 color block with black pixels with use_3color_black disabled") + def test_block_selectors(self): + """Ensure encoding the 3 color test block results in the correct selectors.""" + out = s3tc.BC1Block.frombytes(self.bc1_encoder.encode_image(three_color, 4, 4)) + first_row = out.selectors[0] + expected_row = [1, 2, 2, 0] + + self.assertTrue(all(row == first_row for row in out.selectors), "block has different selectors in each row") + self.assertTrue(all(row == expected_row for row in out.selectors), "block has incorrect selectors for 3 color test block") + class TestBC1Encoder3ColorBlack(unittest.TestCase): + """ Test BC1 encoder with a variety of inputs with 3 color blocks with black pixels enabled""" + def setUp(self): - self.bc1_encoder = python_rgbcx.BC1Encoder(python_rgbcx.InterpolatorType.Ideal, 5, True, True) + self.bc1_encoder = rgbcx.BC1Encoder(rgbcx.InterpolatorType.Ideal, 5, True, True) def test_block_3color(self): - out = s3tc.BC1Block.from_bytes(self.bc1_encoder.encode_image(three_color, 4, 4)) + out = s3tc.BC1Block.frombytes(self.bc1_encoder.encode_image(three_color, 4, 4)) self.assertTrue(out.is_3color(), "returned 4 color block with use_3color enabled") def test_block_3color_black(self): - out = s3tc.BC1Block.from_bytes(self.bc1_encoder.encode_image(three_color_black, 4, 4)) + out = s3tc.BC1Block.frombytes(self.bc1_encoder.encode_image(three_color_black, 4, 4)) self.assertTrue(out.is_3color(), "returned 4 color block with use_3color enabled") self.assertTrue(any(3 in row for row in out.selectors), "returned block without black pixels with use_3color_black enabled") + def test_block_selectors(self): + out = s3tc.BC1Block.frombytes(self.bc1_encoder.encode_image(three_color_black, 4, 4)) + first_row = out.selectors[0] + expected_row = [3, 1, 2, 0] + + self.assertTrue(all(row == first_row for row in out.selectors), "block has different selectors in each row") + self.assertTrue(all(row == expected_row for row in out.selectors), "block has incorrect selectors for 3 color black test block") + if __name__ == '__main__': unittest.main()