├── .github └── workflows │ ├── cmake-multi-platform.yml │ └── webassembly.yml ├── .gitignore ├── .vscode ├── launch.json └── settings.json ├── CMakeLists.txt ├── GoofyTC.code-workspace ├── GoofyTC └── goofy_tc.h ├── Images ├── comparison_chart.png ├── kodim17_sample.png ├── kodim18_sample.png └── lena_sample.png ├── LICENSE ├── README.md ├── Src ├── CMakeLists.txt ├── decoder.cpp ├── decoder.h ├── goofy_tc_reference.cpp ├── goofy_tc_reference.h └── main.cpp ├── ThirdParty ├── README.md ├── bc7enc │ ├── CMakeLists.txt │ ├── LICENSE │ ├── README.md │ ├── bc7decomp.cpp │ ├── bc7decomp.h │ ├── bc7enc.c │ ├── bc7enc.h │ ├── dds_defs.h │ ├── lodepng.cpp │ ├── lodepng.h │ ├── rgbcx.h │ └── test.cpp ├── icbc │ ├── README.md │ └── icbc.h ├── lodepng │ ├── LICENSE │ ├── lodepng.cpp │ └── lodepng.h ├── rgetc │ ├── rg_etc1.cpp │ └── rg_etc1.h └── ryg │ └── stb_dxt.h ├── VisualStudio └── generate17.cmd ├── cmake └── alp_add_git_repository.cmake ├── test-data ├── baboon.png ├── basis │ ├── README.md │ ├── basis_file_list.txt │ ├── etc1 │ │ ├── baboon_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── frymire_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── kodim01_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── kodim02_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── kodim03_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── kodim04_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── kodim05_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── kodim06_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── kodim07_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── kodim08_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── kodim09_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── kodim10_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── kodim11_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── kodim12_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── kodim13_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── kodim14_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── kodim15_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── kodim16_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── kodim17_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── kodim18_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── kodim19_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── kodim20_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── kodim21_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── kodim22_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── kodim23_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── kodim24_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── lena_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── monarch_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── parrot_red_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── patterns_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── pbr_bricks_albedo_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── pbr_ground_albedo_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── pbr_head_albedo_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── pbr_stones_albedo_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── pbr_stones_normal_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── peppers_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── roblox01_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── roblox02_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── roblox03_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── roblox04_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── roblox05_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── roblox06_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── sail_unpacked_rgb_ETC1_RGB_0000.png │ │ ├── serrano_unpacked_rgb_ETC1_RGB_0000.png │ │ └── tulips_unpacked_rgb_ETC1_RGB_0000.png │ ├── pack.cmd │ ├── png_file_list.txt │ ├── temp │ │ └── keep_me.txt │ └── unpack.cmd ├── frymire.png ├── kodim01.png ├── kodim02.png ├── kodim03.png ├── kodim04.png ├── kodim05.png ├── kodim06.png ├── kodim07.png ├── kodim08.png ├── kodim09.png ├── kodim10.png ├── kodim11.png ├── kodim12.png ├── kodim13.png ├── kodim14.png ├── kodim15.png ├── kodim16.png ├── kodim17.png ├── kodim18.png ├── kodim19.png ├── kodim20.png ├── kodim21.png ├── kodim22.png ├── kodim23.png ├── kodim24.png ├── lena.png ├── monarch.png ├── parrot_red.png ├── patterns.png ├── pbr_bricks_albedo.png ├── pbr_ground_albedo.png ├── pbr_head_albedo.png ├── pbr_stones_albedo.png ├── pbr_stones_normal.png ├── peppers.png ├── roblox01.png ├── roblox02.png ├── roblox03.png ├── roblox04.png ├── roblox05.png ├── roblox06.png ├── sail.png ├── serrano.png └── tulips.png └── test-results └── keep_me.txt /.github/workflows/cmake-multi-platform.yml: -------------------------------------------------------------------------------- 1 | # This starter workflow is for a CMake project running on multiple platforms. There is a different starter workflow if you just want a single platform. 2 | # See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-single-platform.yml 3 | name: CMake on multiple platforms 4 | 5 | on: 6 | push: 7 | branches: [ "master" ] 8 | pull_request: 9 | branches: [ "master" ] 10 | 11 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 12 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 13 | concurrency: 14 | group: "ci" 15 | cancel-in-progress: false 16 | 17 | jobs: 18 | build: 19 | runs-on: ${{ matrix.os }} 20 | 21 | strategy: 22 | # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable. 23 | fail-fast: false 24 | 25 | # Set up a matrix to run the following 3 configurations: 26 | # 1. 27 | # 2. 28 | # 3. 29 | # 30 | # To add more build types (Release, Debug, RelWithDebInfo, etc.) customize the build_type list. 31 | matrix: 32 | os: [ubuntu-latest, windows-latest] 33 | build_type: [Release] 34 | c_compiler: [gcc, clang, cl] 35 | include: 36 | - os: windows-latest 37 | c_compiler: cl 38 | cpp_compiler: cl 39 | - os: ubuntu-latest 40 | c_compiler: gcc 41 | cpp_compiler: g++ 42 | - os: ubuntu-latest 43 | c_compiler: clang 44 | cpp_compiler: clang++ 45 | exclude: 46 | - os: windows-latest 47 | c_compiler: gcc 48 | - os: windows-latest 49 | c_compiler: clang 50 | - os: ubuntu-latest 51 | c_compiler: cl 52 | 53 | steps: 54 | - uses: actions/checkout@v3 55 | 56 | - name: Set reusable strings 57 | # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. 58 | id: strings 59 | shell: bash 60 | run: | 61 | echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" 62 | 63 | - name: Configure CMake 64 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 65 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 66 | run: > 67 | cmake -B ${{ steps.strings.outputs.build-output-dir }} 68 | -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} 69 | -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} 70 | -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} 71 | -DENABLE_ADDRESS_SANITIZER=ON 72 | -DENABLE_LONG_TEST_RUN=ON 73 | -S ${{ github.workspace }} 74 | 75 | - name: Build 76 | # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). 77 | run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }} 78 | 79 | - name: Test on Linux 80 | if: matrix.os == 'ubuntu-latest' 81 | working-directory: ${{ github.workspace }} 82 | # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). 83 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 84 | run: ${{ steps.strings.outputs.build-output-dir }}/bin/GoofyTC 85 | 86 | - name: Test on Windows 87 | if: matrix.os == 'windows-latest' 88 | working-directory: ${{ github.workspace }} 89 | # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). 90 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 91 | run: ${{ steps.strings.outputs.build-output-dir }}/bin/Release/GoofyTC.exe 92 | -------------------------------------------------------------------------------- /.github/workflows/webassembly.yml: -------------------------------------------------------------------------------- 1 | name: "webassembly" 2 | on: 3 | push: 4 | branches: [ "master" ] 5 | pull_request: 6 | branches: [ "master" ] 7 | 8 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 9 | permissions: 10 | contents: read 11 | pages: write 12 | id-token: write 13 | 14 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 15 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 16 | concurrency: 17 | group: "pages" 18 | cancel-in-progress: false 19 | 20 | jobs: 21 | build: 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: mymindstorm/setup-emsdk@v13 25 | with: 26 | version: 3.1.37 27 | - uses: actions/checkout@v3 28 | - name: Install Qt native version (required by webassembly version) 29 | uses: jurplel/install-qt-action@v3 30 | with: 31 | aqtversion: '==3.1.*' 32 | version: '6.6.1' 33 | host: linux 34 | target: 'desktop' 35 | arch: gcc_64 36 | dir: '${{github.workspace}}/qt' 37 | install-deps: 'true' 38 | 39 | - name: Set QT_HOST_PATH 40 | run: echo "QT_HOST_PATH=${Qt6_DIR}" >> "$GITHUB_ENV" 41 | 42 | - name: Install Linux Dependencies 43 | run: sudo apt-get install -y build-essential ninja-build 44 | 45 | - name: Install Qt Webassembly version 46 | uses: jurplel/install-qt-action@v3 47 | with: 48 | aqtversion: '==3.1.*' 49 | version: '6.6.1' 50 | host: linux 51 | target: 'desktop' 52 | arch: wasm_singlethread 53 | dir: '${{github.workspace}}/qt' 54 | install-deps: 'true' 55 | 56 | - name: Make qt webassembly binaries executable 57 | run: | 58 | chmod u+x ${Qt6_DIR}/bin/* 59 | 60 | - name: Verify emcc 61 | run: emcc -v 62 | 63 | - name: Set reusable strings 64 | # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. 65 | id: strings 66 | shell: bash 67 | run: | 68 | echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" 69 | echo "install-dir=${{ github.workspace }}/install" >> "$GITHUB_OUTPUT" 70 | 71 | - name: Configure CMake for short tests 72 | env: 73 | CMAKE_PREFIX_PATH: ${{env.Qt6_DIR}}/lib/cmake 74 | run: > 75 | ${Qt6_DIR}/bin/qt-cmake 76 | -G Ninja 77 | -B ${{ steps.strings.outputs.build-output-dir }}_short 78 | -DCMAKE_BUILD_TYPE=Release 79 | -DAA_ENABLE_LONG_TEST_RUN=OFF 80 | -DAA_WWW_INSTALL_DIR=${{ steps.strings.outputs.install-dir }}/short 81 | -DFMT_INSTALL=OFF 82 | -S ${{ github.workspace }} 83 | 84 | - name: Build short tests 85 | run: cmake --build ${{ steps.strings.outputs.build-output-dir }}_short --target install 86 | 87 | - name: Configure CMake for long tests 88 | env: 89 | CMAKE_PREFIX_PATH: ${{env.Qt6_DIR}}/lib/cmake 90 | run: > 91 | ${Qt6_DIR}/bin/qt-cmake 92 | -G Ninja 93 | -B ${{ steps.strings.outputs.build-output-dir }}_long 94 | -DCMAKE_BUILD_TYPE=Release 95 | -DAA_ENABLE_LONG_TEST_RUN=ON 96 | -DAA_WWW_INSTALL_DIR=${{ steps.strings.outputs.install-dir }}/long 97 | -DFMT_INSTALL=OFF 98 | -S ${{ github.workspace }} 99 | 100 | - name: Build long tests 101 | run: cmake --build ${{ steps.strings.outputs.build-output-dir }}_long --target install 102 | 103 | - name: Create artifact 104 | uses: actions/upload-artifact@v4 105 | with: 106 | name: webassembly_tests 107 | path: ${{ steps.strings.outputs.install-dir }} 108 | if-no-files-found: error 109 | 110 | - name: Create index.html 111 | run: | 112 | echo "" >> ${{ steps.strings.outputs.install-dir }}/index.html 113 | echo " " >> ${{ steps.strings.outputs.install-dir }}/index.html 114 | echo " Goofy Tests" >> ${{ steps.strings.outputs.install-dir }}/index.html 115 | echo " " >> ${{ steps.strings.outputs.install-dir }}/index.html 116 | echo " " >> ${{ steps.strings.outputs.install-dir }}/index.html 117 | echo " " >> ${{ steps.strings.outputs.install-dir }}/index.html 121 | echo " " >> ${{ steps.strings.outputs.install-dir }}/index.html 122 | echo "" >> ${{ steps.strings.outputs.install-dir }}/index.html 123 | echo "" >> ${{ steps.strings.outputs.install-dir }}/index.html 124 | 125 | - name: Create Pages artifact 126 | uses: actions/upload-pages-artifact@v3 127 | with: 128 | path: ${{ steps.strings.outputs.install-dir }} 129 | 130 | deploy: 131 | needs: build 132 | environment: 133 | name: github-pages 134 | url: ${{ steps.deployment.outputs.page_url }} 135 | runs-on: ubuntu-latest 136 | steps: 137 | - name: Setup Pages 138 | uses: actions/configure-pages@v4 139 | 140 | - name: Deploy to GitHub Pages 141 | id: deployment 142 | uses: actions/deploy-pages@v4 143 | 144 | - name: Debug output 145 | run: echo ${{ steps.deployment.outputs.page_url }} 146 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | CMakeFiles 3 | VisualStudio/vs2017 4 | VisualStudio/x64 5 | test-results/*.tga 6 | test-results/*.ktx 7 | test-results/*.dds 8 | test-results/*results.txt 9 | test-results/sumary.txt 10 | test-data/basis/basisu.exe 11 | Images/*.psd 12 | test-data/basis/temp/*.basis 13 | /ThirdParty/extern/* 14 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "(Windows) Launch", 9 | "type": "cppvsdbg", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/build/bin/GoofyTC.exe", 12 | "args": [], 13 | "stopAtEntry": false, 14 | "cwd": "${workspaceFolder}", 15 | "environment": [], 16 | "externalConsole": false 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "C_Cpp.default.configurationProvider": "vector-of-bool.cmake-tools" 3 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | project(GoofyTC) 3 | 4 | option(AA_ENABLE_ADDRESS_SANITIZER "compiles atb with address sanitizer enabled (only debug, works only on g++ and clang)" OFF) 5 | option(AA_ENABLE_LONG_TEST_RUN "Switch this off to have way shorter tests" ON) 6 | 7 | if(EMSCRIPTEN) 8 | set(AA_WWW_INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}" CACHE PATH "path to the install directory (for webassembly files, i.e., www directory)") 9 | endif() 10 | 11 | include(cmake/alp_add_git_repository.cmake) 12 | 13 | add_subdirectory(Src) 14 | 15 | -------------------------------------------------------------------------------- /GoofyTC.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": {} 8 | } -------------------------------------------------------------------------------- /Images/comparison_chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/Images/comparison_chart.png -------------------------------------------------------------------------------- /Images/kodim17_sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/Images/kodim17_sample.png -------------------------------------------------------------------------------- /Images/kodim18_sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/Images/kodim18_sample.png -------------------------------------------------------------------------------- /Images/lena_sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/Images/lena_sample.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Sergey Makeev 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included 12 | in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Goofy - Realtime DXT1/ETC1 encoder 2 | 3 | [![Actions Status](https://github.com/SergeyMakeev/Goofy/actions/workflows/cmake-multi-platform.yml/badge.svg)](https://github.com/SergeyMakeev/Goofy/actions) 4 | [![Actions Status](https://github.com/SergeyMakeev/Goofy/actions/workflows/webassembly.yml/badge.svg)](https://github.com/SergeyMakeev/Goofy/actions) 5 | 6 | [Run WebAssembly Tests in your browser](https://sergeymakeev.com/Goofy/) 7 | 8 | 9 | ## About 10 | 11 | This is a very fast DXT/ETC encoder that I wrote, checking out the following idea. **"What if while we design a block compression algorithm, we put the compression speed before everything else?"** 12 | Of course, our compressed results should be reasonable enough; let's say it should be better than a baseline. 13 | Let's set our baseline to a texture downsampled by the factor of two in RGB565 format which gives us the same memory footprint as DXT1/BC1. 14 | 15 | Why would we need a compressor that is very-very fast but cannot compete with well-known codecs in terms of quality? 16 | 17 | I think such a compressor might be useful for several reasons: 18 | - To quickly encode an uncompressed texture on the fly. When you need to use uncompressed texture for rendering (synthesised or fetched from the Internet), it may be a good option to compress it first using Goofy to save some device memory and performance. 19 | - To make a "preview" build for massive projects. Usually, you need to compress thousands of textures before you can play or test the build. Sometimes you don't care about texture quality that much, and you only want to get you playable build as fast as possible. 20 | - Quick preview for live-sync tools. You can immediately show any texture changes using Goofy and then run a more high-quality but slow encoder in parallel to improve the final look of the texture (progressive live texture sync) 21 | 22 | ## Design Principles 23 | 24 | - Performance over quality. 25 | - One heuristics to rule them all. _In favor of speed, I can't afford to check different combinations or explore a solution space deep enough._ 26 | - SSE2 friendly. _Let's get this SSE2 thing to the extreme! I should be able to run encoder sixteen SIMD lanes wide using SSE2 instruction set._ 27 | 28 | ## Goofy Algorithm 29 | 30 | 1. Find the principal axis using the diagonal of the bounding box in RGB space. 31 | 2. Convert the principal axis to perceptual brightness using the YCoCg color model. 32 | 3. Convert all the 16 block pixel from RGB to perceptual brightness 33 | 4. Project 16 pixels to the principal axis using brightness values 34 | 5. For ETC1 encoder, get a base color as an average color of 16 pixels but adjust the brightness to get into the center of the principal axis. 35 | 36 | Of course, the devil is in the detail; there are a lot of small optimizations/tricks on how to make it fast 37 | and parallel for 16 pixels at a time. I recommend you to look at the code for this, I tried to make it as 38 | clear as possible and made a lot of comments to keep the data transformation flow clear. 39 | 40 | ETC1 always encoded using ETC1s format. 41 | 42 | 43 | **NOTE:** Due to quantization based on perceptual brightness and because of ETC1s format limitation Goofy codec doesn't fit well for Normal Maps. 44 | 45 | ## Performance and Quality 46 | 47 | All the performance timings below gathered for the following CPU: **i7-7820HQ, 2.9Ghz single thread**. 48 | To compute timings, I ran encoder 128 times and chose the fastest timing from the run to avoid noise from OS. 49 | 50 | Encoder | MP/s | RGB-PSNR (db) 51 | --- | --- | --- 52 | Baseline | n/a | 33.39 53 | Goofy DXT1 | 1429 | 37.02 54 | Goofy ETC1 | 1221 | 36.30 55 | 56 | Those numbers looks pretty good. As far as I can tell, this is the fastest CPU compressor available at the moment. 57 | https://github.com/castano/nvidia-texture-tools/wiki/RealTimeDXTCompression 58 | 59 | ## Examples of Compressed Images 60 | 61 | ![Kodim17](https://raw.githubusercontent.com/SergeyMakeev/goofy/master/Images/kodim17_sample.png) 62 | ![Kodim18](https://raw.githubusercontent.com/SergeyMakeev/goofy/master/Images/kodim18_sample.png) 63 | ![Lena](https://raw.githubusercontent.com/SergeyMakeev/goofy/master/Images/lena_sample.png) 64 | 65 | ## Comparison with other Encoders 66 | 67 | For all the encoders in the comparison, I've used the fastest available options/lowest quality. 68 | 69 | Encoder | MP/s | RGB-PSNR (db) 70 | --- | --- | --- 71 | Baseline | n/a | 33.39 72 | Goofy DXT1 | 1429 | 37.02 73 | icbc DXT1 v1.0 (SSE2 enabled, fast DXT encoding using box fitting) | 24 | 41.00 74 | rgbcx v1.08 (level0 low-quality)| 60 | 40.85 75 | ryg DXT1 (STB_DXT_NORMAL)| 43 | 40.82 76 | Goofy ETC1 | 1221 | 36.30 77 | Basisu ETC1 | n/a | 36.27 78 | rg v1.04 ETC1 (low-quality, dithering disabled) | 3 | 40.87 79 | 80 | The following chart shows the RGB-PSNR vs. Performance for every image in the test image set. 81 | ![Comparison Chart](https://raw.githubusercontent.com/SergeyMakeev/goofy/master/Images/comparison_chart.png) 82 | 83 | **Note:** As I mentioned earlier compressed Normal Map quality is way worse than photos or albedo textures. 84 | 85 | **Note:** Comparison with "Basisu" is not fair, because this library is supercompressor and target to reduce the final image size. But this is the only ETC1S codec available to compare. 86 | 87 | ## Usage 88 | 89 | Goofy is a header-only library and it's very easy to use. 90 | 91 | ```cpp 92 | 93 | // Add a preprocessor definition and include goofy header 94 | #define GOOFYTC_IMPLEMENTATION 95 | #include 96 | 97 | // You are all set 98 | void test(unsigned char* result, const unsigned char* input, unsigned int width, unsigned int height, unsigned int stride) 99 | { 100 | goofy::compressDXT1(dest, source, width, height, stride); 101 | goofy::compressETC1(dest, source, width, height, stride); 102 | } 103 | 104 | ``` 105 | 106 | ## Next steps 107 | 108 | At some point, I hope I'll make a DXT5/ETC2 alpha encoder based on this code. It should be pretty much straightforward because I can use alpha directly instead of brightness. 109 | 110 | 111 | Look like it should be easy enough to write support for ARM NEON instruction set. Lack of `_mm_movemask_epi8` analog may cause some extra troubles, but everything else should be fine. 112 | 113 | 114 | I appreciate any push requests and improvements. Feel free to ping me and/or send your PRs. 115 | 116 | ## Useful reading (in random order): 117 | 118 | 119 | Basis Universal GPU Texture Codec by Binomial LLC 120 | 121 | https://github.com/BinomialLLC/basis_universal 122 | 123 | DXT1/DXT5 compressor. Originally written by Fabian "ryg" Giesen 124 | 125 | https://github.com/nothings/stb/blob/master/stb_dxt.h 126 | 127 | rg-etc1 encoder by Rich Geldreich 128 | 129 | https://github.com/richgel999/rg-etc1 130 | 131 | Fast, single source file BC1-5 and BC7/BPTC GPU texture encoders by Rich Geldreich 132 | 133 | https://github.com/richgel999/bc7enc 134 | 135 | ICBC - A High Quality BC1 Encoder by Ignacio Castano 136 | 137 | https://github.com/castano/icbc 138 | 139 | The squish open source DXT compression library. Originally written by Simon Brown. 140 | 141 | https://github.com/Cavewhere/squish 142 | 143 | Etc2Comp - Texture to ETC2 compressor. Originally written by Colt McAnlis. 144 | 145 | https://github.com/google/etc2comp 146 | 147 | https://medium.com/@duhroach/building-a-blazing-fast-etc2-compressor-307f3e9aad99 148 | 149 | SIMD transposes by Fabian "ryg" Giesen 150 | 151 | https://fgiesen.wordpress.com/2013/07/09/simd-transposes-1/ 152 | 153 | Performance Tuning for CPU. Part 2: Advanced SIMD Optimization by Marat Dukhan 154 | 155 | https://docs.google.com/presentation/u/1/d/1I0-SiHid1hTsv7tjLST2dYW5YF5AJVfs9l4Rg9rvz48/htmlpresent 156 | 157 | A few missing SSE intrinsics by Alfred Klomp 158 | 159 | http://www.alfredklomp.com/programming/sse-intrinsics/ 160 | 161 | Accelerating Texture Compression with Intel Streaming SIMD Extensions by RADU V. (Intel) 162 | 163 | https://software.intel.com/en-us/articles/accelerating-texture-compression-with-intel-streaming-simd-extensions 164 | 165 | KTX (Khronos Texture) Library and Tools 166 | 167 | https://github.com/KhronosGroup/KTX-Software 168 | 169 | DXT Compression Techniques by Simon Brown 170 | 171 | http://sjbrown.co.uk/2006/01/19/dxt-compression-techniques/ 172 | 173 | Extreme DXT Compression by Peter Uliciansky 174 | 175 | http://www.cauldron.sk/files/extreme_dxt_compression.pdf 176 | 177 | Real-Time YCoCg-DXT Compression by J.M.P. van Waveren and Ignacio Castano 178 | 179 | https://developer.download.nvidia.com/whitepapers/2007/Real-Time-YCoCg-DXT-Compression/Real-Time%20YCoCg-DXT%20Compression.pdf 180 | 181 | Real-Time DXT Compression by J.M.P. van Waveren 182 | 183 | http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.215.7942&rep=rep1&type=pdf 184 | -------------------------------------------------------------------------------- /Src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 2 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 3 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 4 | 5 | set(PROJECT_SOURCES 6 | main.cpp 7 | decoder.cpp 8 | decoder.h 9 | goofy_tc_reference.cpp 10 | goofy_tc_reference.h 11 | ../GoofyTC/goofy_tc.h 12 | ) 13 | 14 | 15 | if(EMSCRIPTEN) 16 | alp_add_git_repository(qml_catch2_console URL https://github.com/AlpineMapsOrg/qml_catch2_console.git COMMITISH origin/main DO_NOT_ADD_SUBPROJECT) 17 | 18 | find_package(Qt6 REQUIRED COMPONENTS Core Gui) 19 | qt_standard_project_setup() 20 | 21 | include(${qml_catch2_console_SOURCE_DIR}/src/qml_catch2_console.cmake) 22 | 23 | add_qml_catch2_console_unittests(${PROJECT_NAME} ${PROJECT_SOURCES}) 24 | set(INSTALL_FILES 25 | "$/${PROJECT_NAME}.js" 26 | "$/${PROJECT_NAME}.wasm" 27 | "$/${PROJECT_NAME}.html" 28 | "$/qtloader.js" 29 | ) 30 | install(FILES ${INSTALL_FILES} DESTINATION ${AA_WWW_INSTALL_DIR}) 31 | 32 | qt_add_resources(${PROJECT_NAME} "test-data" 33 | BASE ".." 34 | FILES 35 | ../test-data/parrot_red.png 36 | ../test-data/basis/etc1/parrot_red_unpacked_rgb_ETC1_RGB_0000.png 37 | ) 38 | if (AA_ENABLE_LONG_TEST_RUN) 39 | qt_add_resources(${PROJECT_NAME} "test-data" 40 | BASE ".." 41 | FILES 42 | ../test-data/parrot_red.png 43 | ../test-data/patterns.png 44 | ../test-data/pbr_bricks_albedo.png 45 | ../test-data/pbr_ground_albedo.png 46 | ../test-data/pbr_stones_albedo.png 47 | ../test-data/pbr_stones_normal.png 48 | ../test-data/pbr_head_albedo.png 49 | ../test-data/baboon.png 50 | ../test-data/lena.png 51 | ../test-data/monarch.png 52 | ../test-data/peppers.png 53 | ../test-data/sail.png 54 | ../test-data/tulips.png 55 | ../test-data/kodim01.png 56 | ../test-data/kodim02.png 57 | ../test-data/kodim03.png 58 | ../test-data/kodim04.png 59 | ../test-data/kodim05.png 60 | ../test-data/kodim06.png 61 | ../test-data/kodim07.png 62 | ../test-data/kodim08.png 63 | ../test-data/kodim09.png 64 | ../test-data/kodim10.png 65 | ../test-data/kodim11.png 66 | ../test-data/kodim12.png 67 | ../test-data/kodim13.png 68 | ../test-data/kodim14.png 69 | ../test-data/kodim15.png 70 | ../test-data/kodim16.png 71 | ../test-data/kodim17.png 72 | ../test-data/kodim18.png 73 | ../test-data/kodim19.png 74 | ../test-data/kodim20.png 75 | ../test-data/kodim21.png 76 | ../test-data/kodim22.png 77 | ../test-data/kodim23.png 78 | ../test-data/kodim24.png 79 | ../test-data/roblox01.png 80 | ../test-data/roblox02.png 81 | ../test-data/roblox03.png 82 | ../test-data/roblox04.png 83 | ../test-data/roblox05.png 84 | ../test-data/roblox06.png 85 | # ../test-data/basis/etc1/patterns_unpacked_rgb_ETC1_RGB_0000.png // can't compile with that many files 86 | # ../test-data/basis/etc1/pbr_bricks_albedo_unpacked_rgb_ETC1_RGB_0000.png 87 | # ../test-data/basis/etc1/pbr_ground_albedo_unpacked_rgb_ETC1_RGB_0000.png 88 | # ../test-data/basis/etc1/pbr_stones_albedo_unpacked_rgb_ETC1_RGB_0000.png 89 | # ../test-data/basis/etc1/pbr_stones_normal_unpacked_rgb_ETC1_RGB_0000.png 90 | # ../test-data/basis/etc1/pbr_head_albedo_unpacked_rgb_ETC1_RGB_0000.png 91 | # ../test-data/basis/etc1/parrot_red_unpacked_rgb_ETC1_RGB_0000.png 92 | # ../test-data/basis/etc1/baboon_unpacked_rgb_ETC1_RGB_0000.png 93 | # ../test-data/basis/etc1/lena_unpacked_rgb_ETC1_RGB_0000.png 94 | # ../test-data/basis/etc1/monarch_unpacked_rgb_ETC1_RGB_0000.png 95 | # ../test-data/basis/etc1/peppers_unpacked_rgb_ETC1_RGB_0000.png 96 | # ../test-data/basis/etc1/sail_unpacked_rgb_ETC1_RGB_0000.png 97 | # ../test-data/basis/etc1/tulips_unpacked_rgb_ETC1_RGB_0000.png 98 | # ../test-data/basis/etc1/kodim01_unpacked_rgb_ETC1_RGB_0000.png 99 | # ../test-data/basis/etc1/kodim02_unpacked_rgb_ETC1_RGB_0000.png 100 | # ../test-data/basis/etc1/kodim03_unpacked_rgb_ETC1_RGB_0000.png 101 | # ../test-data/basis/etc1/kodim04_unpacked_rgb_ETC1_RGB_0000.png 102 | # ../test-data/basis/etc1/kodim05_unpacked_rgb_ETC1_RGB_0000.png 103 | # ../test-data/basis/etc1/kodim06_unpacked_rgb_ETC1_RGB_0000.png 104 | # ../test-data/basis/etc1/kodim07_unpacked_rgb_ETC1_RGB_0000.png 105 | # ../test-data/basis/etc1/kodim08_unpacked_rgb_ETC1_RGB_0000.png 106 | # ../test-data/basis/etc1/kodim09_unpacked_rgb_ETC1_RGB_0000.png 107 | # ../test-data/basis/etc1/kodim10_unpacked_rgb_ETC1_RGB_0000.png 108 | # ../test-data/basis/etc1/kodim11_unpacked_rgb_ETC1_RGB_0000.png 109 | # ../test-data/basis/etc1/kodim12_unpacked_rgb_ETC1_RGB_0000.png 110 | # ../test-data/basis/etc1/kodim13_unpacked_rgb_ETC1_RGB_0000.png 111 | # ../test-data/basis/etc1/kodim14_unpacked_rgb_ETC1_RGB_0000.png 112 | # ../test-data/basis/etc1/kodim15_unpacked_rgb_ETC1_RGB_0000.png 113 | # ../test-data/basis/etc1/kodim16_unpacked_rgb_ETC1_RGB_0000.png 114 | # ../test-data/basis/etc1/kodim17_unpacked_rgb_ETC1_RGB_0000.png 115 | # ../test-data/basis/etc1/kodim18_unpacked_rgb_ETC1_RGB_0000.png 116 | # ../test-data/basis/etc1/kodim19_unpacked_rgb_ETC1_RGB_0000.png 117 | # ../test-data/basis/etc1/kodim20_unpacked_rgb_ETC1_RGB_0000.png 118 | # ../test-data/basis/etc1/kodim21_unpacked_rgb_ETC1_RGB_0000.png 119 | # ../test-data/basis/etc1/kodim22_unpacked_rgb_ETC1_RGB_0000.png 120 | # ../test-data/basis/etc1/kodim23_unpacked_rgb_ETC1_RGB_0000.png 121 | # ../test-data/basis/etc1/kodim24_unpacked_rgb_ETC1_RGB_0000.png 122 | # ../test-data/basis/etc1/roblox01_unpacked_rgb_ETC1_RGB_0000.png 123 | # ../test-data/basis/etc1/roblox02_unpacked_rgb_ETC1_RGB_0000.png 124 | # ../test-data/basis/etc1/roblox03_unpacked_rgb_ETC1_RGB_0000.png 125 | # ../test-data/basis/etc1/roblox04_unpacked_rgb_ETC1_RGB_0000.png 126 | # ../test-data/basis/etc1/roblox05_unpacked_rgb_ETC1_RGB_0000.png 127 | # ../test-data/basis/etc1/roblox06_unpacked_rgb_ETC1_RGB_0000.png 128 | ) 129 | endif() 130 | 131 | # build standalone (no js code) 132 | # set(CMAKE_EXECUTABLE_SUFFIX ".html") 133 | target_compile_options(${PROJECT_NAME} PUBLIC -msimd128 -msse2) 134 | set_target_properties(${PROJECT_NAME} PROPERTIES QT_WASM_INITIAL_MEMORY "200MB") 135 | # target_link_options(${PROJECT_NAME} PUBLIC -s TOTAL_MEMORY=209715200) 136 | else() 137 | add_executable(${PROJECT_NAME} ${PROJECT_SOURCES}) 138 | endif() 139 | 140 | target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17) 141 | 142 | 143 | if (AA_ENABLE_ADDRESS_SANITIZER) 144 | message(NOTICE "building with address sanitizer enabled") 145 | if(MSVC) 146 | target_compile_options (${PROJECT_NAME} INTERFACE /fsanitize=address) 147 | target_link_options(${PROJECT_NAME} INTERFACE /fsanitize=address) 148 | else() 149 | target_compile_options (${PROJECT_NAME} INTERFACE -fsanitize=address) 150 | target_link_options(${PROJECT_NAME} INTERFACE -fsanitize=address) 151 | endif() 152 | endif() 153 | 154 | if (AA_ENABLE_LONG_TEST_RUN) 155 | target_compile_definitions(${PROJECT_NAME} PUBLIC "ENABLE_LONG_TEST_RUN") 156 | endif() 157 | -------------------------------------------------------------------------------- /Src/decoder.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | namespace DecoderBC { 5 | void decodeBlockDXT1(const unsigned char* source, unsigned char* target, size_t targetStide); 6 | void decodeBlockDXT5(const unsigned char* source, unsigned char* target, size_t targetStide); 7 | void decodeBlockETC1(const unsigned char* source, unsigned char* target, size_t targetStide); 8 | void decodeBlockETC2(const unsigned char* source, unsigned char* target, size_t targetStide); 9 | } // namespace DecoderBC 10 | -------------------------------------------------------------------------------- /Src/goofy_tc_reference.cpp: -------------------------------------------------------------------------------- 1 | #include "goofy_tc_reference.h" 2 | 3 | #include // min/max 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include // memcpy 9 | 10 | namespace goofyRef 11 | { 12 | 13 | // 14 | // https://docs.microsoft.com/en-us/windows/win32/direct3d10/d3d10-graphics-programming-guide-resources-block-compression#bc1 15 | // 16 | struct Dxt1 17 | { 18 | // bitmap address if using iN 19 | // +---+---+---+---+ 20 | // | A | B | C | D | 21 | // +---+---+---+---+ 22 | // | E | F | G | H | 23 | // +---+---+---+---+ 24 | // | I | J | K | L | 25 | // +---+---+---+---+ 26 | // | M | N | O | P | 27 | // +---+---+---+---+ 28 | union 29 | { 30 | struct 31 | { 32 | uint16_t c0_max; // RGB565 33 | uint16_t c1_min; // RGB565 34 | uint32_t indices; // 4x4 block of 2 bit indices 35 | }; 36 | 37 | struct 38 | { 39 | uint64_t c0_b : 5; 40 | uint64_t c0_g : 6; 41 | uint64_t c0_r : 5; 42 | 43 | uint64_t c1_b : 5; 44 | uint64_t c1_g : 6; 45 | uint64_t c1_r : 5; 46 | 47 | // C0(max) C2 C3 C1(min) 48 | // | 0 | 2 | 3 | 1 | 49 | // | 00b | 10b | 11b | 01b | 50 | 51 | uint64_t iA : 2; // LSB 52 | uint64_t iB : 2; 53 | uint64_t iC : 2; 54 | uint64_t iD : 2; 55 | uint64_t iE : 2; 56 | uint64_t iF : 2; 57 | uint64_t iG : 2; 58 | uint64_t iH : 2; 59 | uint64_t iI : 2; 60 | uint64_t iJ : 2; 61 | uint64_t iK : 2; 62 | uint64_t iL : 2; 63 | uint64_t iM : 2; 64 | uint64_t iN : 2; 65 | uint64_t iO : 2; 66 | uint64_t iP : 2; // MSB 67 | }; 68 | }; 69 | }; 70 | 71 | // 72 | // https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.html#ETC1 73 | // Note: this is Etc1S "simplified" block 74 | struct Etc1S 75 | { 76 | // 5 bit-color + lowest 3 bits zeroes (color delta) 77 | // 5 bit-color + lowest 3 bits zeroes (color delta) 78 | // 5 bit-color + lowest 3 bits zeroes (color delta) 79 | // table codeword 1 (3-bit), table codeword 2 (3-bit) (both the same), diff (1-bit always on), flip (1-bit doesn't matter) 80 | // 4x4 block of 2 bit indices (bright intensity modifiers) 81 | 82 | // bitmap address if using iN/nN 83 | // +---+---+---+---+ 84 | // | A | B | C | D | 85 | // +---+---+---+---+ 86 | // | E | F | G | H | 87 | // +---+---+---+---+ 88 | // | I | J | K | L | 89 | // +---+---+---+---+ 90 | // | M | N | O | P | 91 | // +---+---+---+---+ 92 | // 93 | // bitmap address using raw bits 94 | // addressing is vertical (bottom to top) and even/odd rows swapped due to endianness 95 | // +---+---+---+---+ 96 | // | C | G | K | O | 97 | // +---+---+---+---+ 98 | // | D | H | L | P | 99 | // +---+---+---+---+ 100 | // | A | E | I | M | 101 | // +---+---+---+---+ 102 | // | B | F | J | N | 103 | // +---+---+---+---+ 104 | union 105 | { 106 | struct 107 | { 108 | uint8_t data[8]; 109 | }; 110 | 111 | struct 112 | { 113 | uint32_t rgbAndControlByte; 114 | uint32_t indices; 115 | }; 116 | 117 | struct 118 | { 119 | uint64_t dr : 3; 120 | uint64_t r : 5; 121 | 122 | uint64_t dg : 3; 123 | uint64_t g : 5; 124 | 125 | uint64_t db : 3; 126 | uint64_t b : 5; 127 | 128 | uint64_t flip : 1; 129 | uint64_t diff : 1; 130 | uint64_t codeword2 : 3; 131 | uint64_t codeword1 : 3; 132 | 133 | // sign bit (1 = negative, 0 = positive) 134 | uint64_t nC : 1; // LSB 135 | uint64_t nG : 1; 136 | uint64_t nK : 1; 137 | uint64_t nO : 1; 138 | uint64_t nD : 1; 139 | uint64_t nH : 1; 140 | uint64_t nL : 1; 141 | uint64_t nP : 1; 142 | uint64_t nA : 1; 143 | uint64_t nE : 1; 144 | uint64_t nI : 1; 145 | uint64_t nM : 1; 146 | uint64_t nB : 1; 147 | uint64_t nF : 1; 148 | uint64_t nJ : 1; 149 | uint64_t nN : 1; // MSB 150 | 151 | // index bit (0 = small delta, 1 = huge delta) 152 | uint64_t iC : 1; // LSB 153 | uint64_t iG : 1; 154 | uint64_t iK : 1; 155 | uint64_t iO : 1; 156 | uint64_t iD : 1; 157 | uint64_t iH : 1; 158 | uint64_t iL : 1; 159 | uint64_t iP : 1; 160 | uint64_t iA : 1; 161 | uint64_t iE : 1; 162 | uint64_t iI : 1; 163 | uint64_t iM : 1; 164 | uint64_t iB : 1; 165 | uint64_t iF : 1; 166 | uint64_t iJ : 1; 167 | uint64_t iN : 1; // MSB 168 | }; 169 | 170 | }; 171 | }; 172 | 173 | struct BlockData 174 | { 175 | // dxt1 176 | uint8_t min_r; 177 | uint8_t min_g; 178 | uint8_t min_b; 179 | 180 | uint8_t max_r; 181 | uint8_t max_g; 182 | uint8_t max_b; 183 | 184 | // etc1 185 | uint8_t base_r; 186 | uint8_t base_g; 187 | uint8_t base_b; 188 | 189 | uint8_t avg_r; 190 | uint8_t avg_g; 191 | uint8_t avg_b; 192 | 193 | uint8_t brightRange; 194 | 195 | // shared 196 | int index[16]; 197 | }; 198 | 199 | // 200 | struct FColor3 201 | { 202 | union 203 | { 204 | struct 205 | { 206 | float r; 207 | float g; 208 | float b; 209 | }; 210 | 211 | struct 212 | { 213 | float y; 214 | float co; 215 | float cg; 216 | }; 217 | }; 218 | 219 | static FColor3 create(float v) 220 | { 221 | FColor3 res; 222 | res.r = v; 223 | res.g = v; 224 | res.b = v; 225 | return res; 226 | } 227 | 228 | static FColor3 create(float r_or_y, float g_or_co, float b_or_cg) 229 | { 230 | FColor3 res; 231 | res.r = r_or_y; // y 232 | res.g = g_or_co; // co 233 | res.b = b_or_cg; // cg 234 | return res; 235 | } 236 | 237 | static FColor3 min(const FColor3& a, const FColor3& b) 238 | { 239 | FColor3 res; 240 | res.r = std::min(a.r, b.r); 241 | res.g = std::min(a.g, b.g); 242 | res.b = std::min(a.b, b.b); 243 | return res; 244 | } 245 | 246 | static float brightnessYCoCg(const FColor3& a) 247 | { 248 | float Y = a.r * 0.25f + a.g * 0.5f + a.b * 0.25f; 249 | return Y; 250 | } 251 | 252 | static float brightnessSimple(const FColor3& a) 253 | { 254 | float Y = a.r * 0.25f + a.g * 0.25f + a.b * 0.25f; 255 | return Y; 256 | } 257 | 258 | static float brightness(const FColor3& a) 259 | { 260 | return brightnessYCoCg(a); 261 | 262 | // this give us a slightly better result for DXT1 compressor (+0.2 db) 263 | // but it's way worse for ETC (-0.58 db) 264 | //return brightnessSimple(a); 265 | } 266 | 267 | static FColor3 avg(const FColor3& a, const FColor3& b) 268 | { 269 | FColor3 res; 270 | res.r = (a.r + b.r) / 2.0f; 271 | res.g = (a.g + b.g) / 2.0f; 272 | res.b = (a.b + b.b) / 2.0f; 273 | return res; 274 | } 275 | 276 | static FColor3 max(const FColor3& a, const FColor3& b) 277 | { 278 | FColor3 res; 279 | res.r = std::max(a.r, b.r); 280 | res.g = std::max(a.g, b.g); 281 | res.b = std::max(a.b, b.b); 282 | return res; 283 | } 284 | 285 | static FColor3 add(const FColor3& a, const FColor3& b) 286 | { 287 | FColor3 res; 288 | res.r = a.r + b.r; 289 | res.g = a.g + b.g; 290 | res.b = a.b + b.b; 291 | return res; 292 | } 293 | 294 | static FColor3 sub(const FColor3& a, const FColor3& b) 295 | { 296 | FColor3 res; 297 | res.r = a.r - b.r; 298 | res.g = a.g - b.g; 299 | res.b = a.b - b.b; 300 | return res; 301 | } 302 | 303 | static FColor3 clamp(const FColor3& v) 304 | { 305 | FColor3 res; 306 | res.r = std::max(0.0f, std::min(255.0f, v.r)); 307 | res.g = std::max(0.0f, std::min(255.0f, v.g)); 308 | res.b = std::max(0.0f, std::min(255.0f, v.b)); 309 | return res; 310 | } 311 | 312 | static FColor3 div(const FColor3& a, float v) 313 | { 314 | FColor3 res; 315 | res.r = a.r / v; 316 | res.g = a.g / v; 317 | res.b = a.b / v; 318 | return res; 319 | } 320 | 321 | static FColor3 toYCoCg(const FColor3& v) 322 | { 323 | float co = v.r - v.b; 324 | float tmp = v.b + co / 2.0f; 325 | float cg = v.g - tmp; 326 | float y = tmp + cg / 2.0f; 327 | 328 | FColor3 res; 329 | res.co = co; 330 | res.cg = cg; 331 | res.y = y; 332 | return res; 333 | } 334 | 335 | static FColor3 toRgb(const FColor3& v) 336 | { 337 | float tmp = v.y - v.cg / 2; 338 | float g = v.cg + tmp; 339 | float b = tmp - v.co / 2; 340 | float r = b + v.co; 341 | 342 | FColor3 res; 343 | res.r = r; 344 | res.g = g; 345 | res.b = b; 346 | return res; 347 | } 348 | }; 349 | 350 | static uint8_t floatToByte(float v) 351 | { 352 | v = (v + 0.5f); 353 | 354 | if (v < 0.0f) 355 | v = 0.0f; 356 | 357 | if (v > 255.0f) 358 | v = 255.0f; 359 | 360 | return (uint8_t)v; 361 | } 362 | 363 | 364 | // generate Maya MEL script to visualize block pixels and bounds 365 | static void goofyDebugDumpBlock(const uint8_t(&blockRgba)[64], const FColor3& minColor, const FColor3& maxColor, const FColor3& avgColor, const FColor3& baseColor) 366 | { 367 | for (int y = 0; y < 4; y++) 368 | { 369 | for (int x = 0; x < 4; x++) 370 | { 371 | FColor3 pixel; 372 | pixel.r = (float)blockRgba[(y * 4 + x) * 4 + 0]; 373 | pixel.g = (float)blockRgba[(y * 4 + x) * 4 + 1]; 374 | pixel.b = (float)blockRgba[(y * 4 + x) * 4 + 2]; 375 | 376 | printf("%d_%d %3.2f, %3.2f, %3.2f (%3.6f)\n", x, y, pixel.r, pixel.g, pixel.b, FColor3::brightness(pixel)); 377 | } 378 | } 379 | 380 | printf("MIN: %3.2f, %3.2f, %3.2f (%3.6f)\n", minColor.r, minColor.g, minColor.b, FColor3::brightness(minColor)); 381 | printf("MAX: %3.2f, %3.2f, %3.2f (%3.6f)\n", maxColor.r, maxColor.g, maxColor.b, FColor3::brightness(maxColor)); 382 | 383 | printf("// ------------------------ cut here ------------------------------------\n"); 384 | printf("curve -d 1 -p %3.4f %3.4f %3.4f -p %3.4f %3.4f %3.4f;\n", minColor.r, minColor.g, minColor.b, maxColor.r, maxColor.g, maxColor.b); 385 | 386 | FColor3 etcOffset = FColor3::create(200, 200, 200); 387 | FColor3 etcMin = FColor3::sub(baseColor, etcOffset); 388 | FColor3 etcMax = FColor3::add(baseColor, etcOffset); 389 | printf("curve -d 1 -p %3.4f %3.4f %3.4f -p %3.4f %3.4f %3.4f;\n", etcMin.r, etcMin.g, etcMin.b, etcMax.r, etcMax.g, etcMax.b); 390 | 391 | printf("string $cube[];\n"); 392 | printf("$cube = `polyCube -w 2.5 -h 2.5 -d 2.5 -sx 1 -sy 1 -sz 1`;\n"); 393 | printf("rename ($cube[0], \"min_c\");\n"); 394 | printf("setAttr \"min_c.translateX\" %3.4f;\n", minColor.r); 395 | printf("setAttr \"min_c.translateY\" %3.4f;\n", minColor.g); 396 | printf("setAttr \"min_c.translateZ\" %3.4f;\n", minColor.b); 397 | printf("polyColorPerVertex -r %3.5f -g %3.5f -b %3.5f -a 1 -cdo;\n", (minColor.r / 255.0f), (minColor.g / 255.0f), (minColor.b / 255.0f)); 398 | 399 | printf("$cube = `polyCube -w 2.5 -h 2.5 -d 2.5 -sx 1 -sy 1 -sz 1`;\n"); 400 | printf("rename ($cube[0], \"max_c\");\n"); 401 | printf("setAttr \"max_c.translateX\" %3.4f;\n", maxColor.r); 402 | printf("setAttr \"max_c.translateY\" %3.4f;\n", maxColor.g); 403 | printf("setAttr \"max_c.translateZ\" %3.4f;\n", maxColor.b); 404 | printf("polyColorPerVertex -r %3.5f -g %3.5f -b %3.5f -a 1 -cdo;\n", (maxColor.r / 255.0f), (maxColor.g / 255.0f), (maxColor.b / 255.0f)); 405 | 406 | printf("$cube = `polyCube -w 2.5 -h 2.5 -d 2.5 -sx 1 -sy 1 -sz 1`;\n"); 407 | printf("rename ($cube[0], \"avg_c\");\n"); 408 | printf("setAttr \"avg_c.translateX\" %3.4f;\n", avgColor.r); 409 | printf("setAttr \"avg_c.translateY\" %3.4f;\n", avgColor.g); 410 | printf("setAttr \"avg_c.translateZ\" %3.4f;\n", avgColor.b); 411 | printf("polyColorPerVertex -r %3.5f -g %3.5f -b %3.5f -a 1 -cdo;\n", (avgColor.r / 255.0f), (avgColor.g / 255.0f), (avgColor.b / 255.0f)); 412 | 413 | printf("$cube = `polyCube -w 2.5 -h 2.5 -d 2.5 -sx 1 -sy 1 -sz 1`;\n"); 414 | printf("rename ($cube[0], \"base_c\");\n"); 415 | printf("setAttr \"base_c.translateX\" %3.4f;\n", baseColor.r); 416 | printf("setAttr \"base_c.translateY\" %3.4f;\n", baseColor.g); 417 | printf("setAttr \"base_c.translateZ\" %3.4f;\n", baseColor.b); 418 | printf("polyColorPerVertex -r %3.5f -g %3.5f -b %3.5f -a 1 -cdo;\n", (baseColor.r / 255.0f), (baseColor.g / 255.0f), (baseColor.b / 255.0f)); 419 | 420 | { 421 | printf("$cube = `polyCube -w 1.5 -h 1.5 -d 1.5 -sx 1 -sy 1 -sz 1`;\n"); 422 | printf("rename ($cube[0], \"bb0_c\");\n"); 423 | printf("setAttr \"bb0_c.translateX\" %3.4f;\n", minColor.r); 424 | printf("setAttr \"bb0_c.translateY\" %3.4f;\n", maxColor.g); 425 | printf("setAttr \"bb0_c.translateZ\" %3.4f;\n", maxColor.b); 426 | printf("polyColorPerVertex -r %3.5f -g %3.5f -b %3.5f -a 1 -cdo;\n", (minColor.r / 255.0f), (maxColor.g / 255.0f), (maxColor.b / 255.0f)); 427 | 428 | printf("$cube = `polyCube -w 1.5 -h 1.5 -d 1.5 -sx 1 -sy 1 -sz 1`;\n"); 429 | printf("rename ($cube[0], \"bb1_c\");\n"); 430 | printf("setAttr \"bb1_c.translateX\" %3.4f;\n", maxColor.r); 431 | printf("setAttr \"bb1_c.translateY\" %3.4f;\n", maxColor.g); 432 | printf("setAttr \"bb1_c.translateZ\" %3.4f;\n", minColor.b); 433 | printf("polyColorPerVertex -r %3.5f -g %3.5f -b %3.5f -a 1 -cdo;\n", (maxColor.r / 255.0f), (maxColor.g / 255.0f), (minColor.b / 255.0f)); 434 | 435 | printf("$cube = `polyCube -w 1.5 -h 1.5 -d 1.5 -sx 1 -sy 1 -sz 1`;\n"); 436 | printf("rename ($cube[0], \"bb2_c\");\n"); 437 | printf("setAttr \"bb2_c.translateX\" %3.4f;\n", minColor.r); 438 | printf("setAttr \"bb2_c.translateY\" %3.4f;\n", maxColor.g); 439 | printf("setAttr \"bb2_c.translateZ\" %3.4f;\n", minColor.b); 440 | printf("polyColorPerVertex -r %3.5f -g %3.5f -b %3.5f -a 1 -cdo;\n", (minColor.r / 255.0f), (maxColor.g / 255.0f), (minColor.b / 255.0f)); 441 | 442 | printf("$cube = `polyCube -w 1.5 -h 1.5 -d 1.5 -sx 1 -sy 1 -sz 1`;\n"); 443 | printf("rename ($cube[0], \"bb3_c\");\n"); 444 | printf("setAttr \"bb3_c.translateX\" %3.4f;\n", minColor.r); 445 | printf("setAttr \"bb3_c.translateY\" %3.4f;\n", minColor.g); 446 | printf("setAttr \"bb3_c.translateZ\" %3.4f;\n", maxColor.b); 447 | printf("polyColorPerVertex -r %3.5f -g %3.5f -b %3.5f -a 1 -cdo;\n", (minColor.r / 255.0f), (minColor.g / 255.0f), (maxColor.b / 255.0f)); 448 | 449 | printf("$cube = `polyCube -w 1.5 -h 1.5 -d 1.5 -sx 1 -sy 1 -sz 1`;\n"); 450 | printf("rename ($cube[0], \"bb4_c\");\n"); 451 | printf("setAttr \"bb4_c.translateX\" %3.4f;\n", maxColor.r); 452 | printf("setAttr \"bb4_c.translateY\" %3.4f;\n", minColor.g); 453 | printf("setAttr \"bb4_c.translateZ\" %3.4f;\n", minColor.b); 454 | printf("polyColorPerVertex -r %3.5f -g %3.5f -b %3.5f -a 1 -cdo;\n", (maxColor.r / 255.0f), (minColor.g / 255.0f), (minColor.b / 255.0f)); 455 | 456 | printf("$cube = `polyCube -w 1.5 -h 1.5 -d 1.5 -sx 1 -sy 1 -sz 1`;\n"); 457 | printf("rename ($cube[0], \"bb5_c\");\n"); 458 | printf("setAttr \"bb5_c.translateX\" %3.4f;\n", maxColor.r); 459 | printf("setAttr \"bb5_c.translateY\" %3.4f;\n", minColor.g); 460 | printf("setAttr \"bb5_c.translateZ\" %3.4f;\n", maxColor.b); 461 | printf("polyColorPerVertex -r %3.5f -g %3.5f -b %3.5f -a 1 -cdo;\n", (maxColor.r / 255.0f), (minColor.g / 255.0f), (maxColor.b / 255.0f)); 462 | } 463 | 464 | printf("$cube = `polyCube -w 255 -h 255 -d 255 -sx 1 -sy 1 -sz 1`;\n"); 465 | printf("rename ($cube[0], \"rgb_bbox\");\n"); 466 | 467 | printf("setAttr \"rgb_bbox.translateX\" %3.4f;\n", 127.5); 468 | printf("setAttr \"rgb_bbox.translateY\" %3.4f;\n", 127.5); 469 | printf("setAttr \"rgb_bbox.translateZ\" %3.4f;\n", 127.5); 470 | 471 | printf("setAttr \"rgb_bbox.overrideEnabled\" 1;\n"); 472 | printf("setAttr \"rgb_bbox.overrideShading\" 0;\n"); 473 | 474 | float sX = fabs(maxColor.r - minColor.r); 475 | float sY = fabs(maxColor.g - minColor.g); 476 | float sZ = fabs(maxColor.b - minColor.b); 477 | 478 | float cX = (maxColor.r + minColor.r) * 0.5f; 479 | float cY = (maxColor.g + minColor.g) * 0.5f; 480 | float cZ = (maxColor.b + minColor.b) * 0.5f; 481 | 482 | printf("$cube = `polyCube -w %3.5f -h %3.5f -d %3.5f -sx 1 -sy 1 -sz 1`;\n", sX, sY, sZ); 483 | printf("rename ($cube[0], \"bbox\");\n"); 484 | printf("setAttr \"bbox.translateX\" %3.4f;\n", cX); 485 | printf("setAttr \"bbox.translateY\" %3.4f;\n", cY); 486 | printf("setAttr \"bbox.translateZ\" %3.4f;\n", cZ); 487 | 488 | printf("setAttr \"bbox.overrideEnabled\" 1;\n"); 489 | printf("setAttr \"bbox.overrideShading\" 0;\n"); 490 | 491 | for (int y = 0; y < 4; y++) 492 | { 493 | for (int x = 0; x < 4; x++) 494 | { 495 | FColor3 pixel; 496 | pixel.r = (float)blockRgba[(y * 4 + x) * 4 + 0]; 497 | pixel.g = (float)blockRgba[(y * 4 + x) * 4 + 1]; 498 | pixel.b = (float)blockRgba[(y * 4 + x) * 4 + 2]; 499 | 500 | float br = FColor3::brightness(pixel); 501 | int brightness = int(br * 100.0f); 502 | 503 | printf("$cube = `polyCube -w 1 -h 1 -d 1 -sx 1 -sy 1 -sz 1`;\n"); 504 | printf("rename ($cube[0], \"p%d_%d_%d\");\n", x, y, brightness); 505 | printf("setAttr \"p%d_%d_%d.translateX\" %3.4f;\n", x, y, brightness, pixel.r); 506 | printf("setAttr \"p%d_%d_%d.translateY\" %3.4f;\n", x, y, brightness, pixel.g); 507 | printf("setAttr \"p%d_%d_%d.translateZ\" %3.4f;\n", x, y, brightness, pixel.b); 508 | printf("polyColorPerVertex -r %3.5f -g %3.5f -b %3.5f -a 1 -cdo;\n", (pixel.r / 255.0f), (pixel.g / 255.0f), (pixel.b / 255.0f)); 509 | } 510 | } 511 | printf("// ------------------------ cut here ------------------------------------\n"); 512 | } 513 | 514 | static BlockData goofyCompressBlock(const uint8_t(&blockRgba)[64], const float minBrightnessRange) 515 | { 516 | FColor3 minColor = FColor3::create(99999.0f); 517 | FColor3 maxColor = FColor3::create(-99999.0f); 518 | FColor3 avgColor = FColor3::create(0.0f); 519 | 520 | // Find max/min color 521 | for (int y = 0; y < 4; y++) 522 | { 523 | for (int x = 0; x < 4; x++) 524 | { 525 | FColor3 pixel; 526 | pixel.r = (float)blockRgba[(y * 4 + x) * 4 + 0]; 527 | pixel.g = (float)blockRgba[(y * 4 + x) * 4 + 1]; 528 | pixel.b = (float)blockRgba[(y * 4 + x) * 4 + 2]; 529 | 530 | minColor = FColor3::min(minColor, pixel); 531 | maxColor = FColor3::max(maxColor, pixel); 532 | avgColor = FColor3::add(avgColor, pixel); 533 | } 534 | } 535 | 536 | avgColor = FColor3::div(avgColor, 16.0f); 537 | 538 | // Convert to min/max brightness 539 | float maxY = FColor3::brightness(maxColor); 540 | float minY = FColor3::brightness(minColor); 541 | 542 | // Get brightness range 543 | float brightnessRange = maxY - minY; 544 | 545 | // Clamp brighness range to some value 546 | // too small brightness difference cannot be encoded anyway 547 | // 548 | // minBrightnessRange default value is 8 for DXT and 16 for ETC 549 | brightnessRange = std::max(brightnessRange, minBrightnessRange); 550 | 551 | float midPointY = (maxY + minY) * 0.5f; 552 | 553 | // This works beter than 0.25 for both dxt1 and etc1 554 | float quantizationThreshold = brightnessRange * 0.375f; 555 | 556 | // Quantization (generate indices) 557 | BlockData res; 558 | for (int y = 0; y < 4; y++) 559 | { 560 | for (int x = 0; x < 4; x++) 561 | { 562 | FColor3 pixel; 563 | pixel.r = (float)blockRgba[(y * 4 + x) * 4 + 0]; 564 | pixel.g = (float)blockRgba[(y * 4 + x) * 4 + 1]; 565 | pixel.b = (float)blockRgba[(y * 4 + x) * 4 + 2]; 566 | 567 | float pixelY = FColor3::brightness(pixel); 568 | float diff = pixelY - midPointY; 569 | 570 | float q = quantizationThreshold; 571 | 572 | /* // noise makes the codec quality worse 573 | float rand01 = ((float)rand() / RAND_MAX); 574 | float noise = (rand01 * 2.0f - 0.5f) * 0.5f; 575 | q += noise; 576 | q = std::max(0.0f, std::min(255.0f, q)); 577 | */ 578 | 579 | int idx = -1; 580 | if (diff > 0.0f) 581 | { 582 | idx = (diff < q) ? 2 : 0; 583 | } 584 | else 585 | { 586 | idx = (fabsf(diff) < q) ? 3 : 1; 587 | } 588 | assert(idx >= 0 && idx <= 3); 589 | res.index[y * 4 + x] = idx; 590 | } 591 | } 592 | 593 | // DXT 594 | res.max_r = floatToByte(maxColor.r); 595 | res.max_g = floatToByte(maxColor.g); 596 | res.max_b = floatToByte(maxColor.b); 597 | 598 | res.min_r = floatToByte(minColor.r); 599 | res.min_g = floatToByte(minColor.g); 600 | res.min_b = floatToByte(minColor.b); 601 | 602 | // ETC 603 | res.brightRange = floatToByte(brightnessRange * 0.5f); 604 | 605 | // Keep chromatic component from the average color, but override brightness 606 | float avgY = FColor3::brightness(avgColor); 607 | float diffY = midPointY - avgY; 608 | FColor3 baseColor = FColor3::add(avgColor, FColor3::create(diffY)); 609 | 610 | //goofyDebugDumpBlock(blockRgba, minColor, maxColor, avgColor, baseColor); 611 | 612 | // Old-mode (worse quality, but faster) 613 | //FColor3 mid = FColor3::avg(maxColor, minColor); 614 | res.base_r = floatToByte(baseColor.r); 615 | res.base_g = floatToByte(baseColor.g); 616 | res.base_b = floatToByte(baseColor.b); 617 | 618 | res.avg_r = floatToByte(avgColor.r); 619 | res.avg_g = floatToByte(avgColor.g); 620 | res.avg_b = floatToByte(avgColor.b); 621 | 622 | return res; 623 | } 624 | 625 | static uint32_t packRgb888ToEtc555(uint8_t __r, uint8_t __g, uint8_t __b) 626 | { 627 | uint32_t _r = __r; 628 | uint32_t _g = __g; 629 | uint32_t _b = __b; 630 | uint32_t clr = (_r & 0xf8) | ((_g & 0xf8) << 8) | ((_b & 0xf8) << 16); 631 | return clr; 632 | } 633 | 634 | static uint16_t packRgb888ToDxt565(uint8_t __r, uint8_t __g, uint8_t __b) 635 | { 636 | // in fact convert RGB888 to RGB555 (instead of RGB565) where low G bit is always 0 637 | uint32_t _r = __r; 638 | uint32_t _g = __g; 639 | uint32_t _b = __b; 640 | 641 | uint32_t r = (_r >> 3) << 11; 642 | uint32_t g = (_g >> 3) << 6; 643 | uint32_t b = (_b >> 3); 644 | uint16_t clr = r | g | b; 645 | return clr; 646 | } 647 | 648 | static uint32_t getDxtIndices(const BlockData& d) 649 | { 650 | uint32_t v = 0; 651 | for (int n = 0; n < 16; n++) 652 | { 653 | uint32_t ii = (d.index[n] & 3); 654 | v |= (ii << (n*2)); 655 | } 656 | return v; 657 | } 658 | 659 | static void goofyPackBlockDXT1(const unsigned char* inputRGBA, size_t stride, unsigned char* pResult) 660 | { 661 | unsigned char block[64]; 662 | memcpy(&block[0], inputRGBA, 16); 663 | memcpy(&block[16], inputRGBA + stride, 16); 664 | memcpy(&block[32], inputRGBA + stride * 2, 16); 665 | memcpy(&block[48], inputRGBA + stride * 3, 16); 666 | 667 | BlockData bl = goofyCompressBlock(block, 8.0f); 668 | 669 | Dxt1* pBlock = (Dxt1*)pResult; 670 | pBlock->c0_max = packRgb888ToDxt565(bl.max_r, bl.max_g, bl.max_b) | 0x20; 671 | pBlock->c1_min = packRgb888ToDxt565(bl.min_r, bl.min_g, bl.min_b); 672 | pBlock->indices = getDxtIndices(bl); 673 | } 674 | 675 | // [0] [1] [2] [3] 676 | // 0x03 [0] -8 -2 2 8 677 | // 0x27 [1] -17 -5 5 17 678 | // 0x4B [2] -29 -9 9 29 679 | // 0x6F [3] -42 -13 13 42 680 | // 0x93 [4] -60 -18 18 60 681 | // 0xB7 [5] -80 -24 24 80 682 | // 0xDB [6] -106 -33 33 106 683 | // 0xFF [7] -183 -47 47 183 684 | static uint32_t getEtc1SBlockControlByte(uint8_t brightRange) 685 | { 686 | // this is works better than standard table 687 | //int brightTable[8] = { 8, 17, 29, 42, 60, 80, 106 }; 688 | int brightTable[8] = { 10, 21, 36, 52, 75, 90, 126 }; 689 | 690 | if (brightRange <= brightTable[0]) 691 | { 692 | return 0x03000000; 693 | } 694 | else if (brightRange <= brightTable[1]) 695 | { 696 | return 0x27000000; 697 | } 698 | else if (brightRange <= brightTable[2]) 699 | { 700 | return 0x4B000000; 701 | } 702 | else if (brightRange <= brightTable[3]) 703 | { 704 | return 0x6F000000; 705 | } 706 | else if (brightRange <= brightTable[4]) 707 | { 708 | return 0x93000000; 709 | } 710 | else if (brightRange <= brightTable[5]) 711 | { 712 | return 0xB7000000; 713 | } 714 | else if (brightRange <= brightTable[6]) 715 | { 716 | return 0xDB000000; 717 | } 718 | 719 | return 0xFF000000; 720 | } 721 | 722 | static uint32_t getEtcIndices(const BlockData& d) 723 | { 724 | // DXT ETC (MSB/LSB) 725 | // 0 = brightest DXT color 01 +b (large positive value) 726 | // 1 = darkest DXT color 11 -b (large negative value) 727 | // 2 = 2/3 bright + 1/3 dark 00 +a (small positibe value) 728 | // 3 = 1/3 bright + 2/3 dark 10 -a (small negative value) 729 | 730 | uint32_t lsbRemapTable[4] = { 1, 1, 0, 0 }; 731 | uint32_t msbRemapTable[4] = { 0, 1, 0, 1 }; 732 | 733 | // ETC indices addressing is different from DXT 734 | uint32_t remapIndex[16] = { 735 | 0x8, 0xC, 0x0, 0x4, 736 | 0x9, 0xD, 0x1, 0x5, 737 | 0xA, 0xE, 0x2, 0x6, 738 | 0xB, 0xF, 0x3, 0x7 739 | }; 740 | 741 | uint32_t v = 0; 742 | for (int n = 0; n < 16; n++) 743 | { 744 | // remap index from DXT left-to-right, to ETC top-to-bottom 745 | uint32_t bitNumber = remapIndex[n]; 746 | 747 | // remap DXT index to ETC index 748 | uint32_t lsb = (lsbRemapTable[d.index[n]]) << bitNumber; 749 | uint32_t msb = (msbRemapTable[d.index[n]]) << bitNumber; 750 | 751 | v |= (lsb << 16); 752 | v |= msb; 753 | } 754 | 755 | return v; 756 | } 757 | 758 | static void goofyPackBlockETC1S(const unsigned char* inputRGBA, size_t stride, unsigned char* pResult) 759 | { 760 | unsigned char block[64]; 761 | memcpy(&block[0], inputRGBA, 16); 762 | memcpy(&block[16], inputRGBA + stride, 16); 763 | memcpy(&block[32], inputRGBA + stride * 2, 16); 764 | memcpy(&block[48], inputRGBA + stride * 3, 16); 765 | 766 | BlockData bl = goofyCompressBlock(block, 16.0f); 767 | 768 | Etc1S* pBlock = (Etc1S*)pResult; 769 | 770 | 771 | /* 772 | // constant color block optimization (worse) 773 | if ((bl.max_r - bl.min_r) < 4 && (bl.max_g - bl.min_g) < 4 && (bl.max_b - bl.min_b) < 4) 774 | { 775 | uint32_t ctrlByte = 0x03000000; 776 | pBlock->rgbAndControlByte = packRgb888ToEtc555(bl.avg_r, bl.avg_g, bl.avg_b) | ctrlByte; 777 | // since extend_5to8bits() always replicate 3 higher bits into lower bits it introduces additional quantization error +0..+7 778 | // adding -2 RGB per-pixel is an attempt to compensate for this error for a constant color block 779 | 780 | // smallest possible negative (-2) 781 | pBlock->indices = 0x0000FFFF; 782 | } 783 | else 784 | */ 785 | { 786 | uint32_t ctrlByte = getEtc1SBlockControlByte(bl.brightRange); 787 | pBlock->rgbAndControlByte = packRgb888ToEtc555(bl.base_r, bl.base_g, bl.base_b) | ctrlByte; 788 | pBlock->indices = getEtcIndices(bl); 789 | } 790 | 791 | } 792 | 793 | 794 | int compressDXT1(unsigned char* result, const unsigned char* input, unsigned int width, unsigned int height, unsigned int stride) 795 | { 796 | if (width % 4 != 0) 797 | { 798 | return -1; 799 | } 800 | 801 | if (height % 4 != 0) 802 | { 803 | return -2; 804 | } 805 | 806 | uint32_t blockW = width >> 2; 807 | uint32_t blockH = height >> 2; 808 | 809 | for (uint32_t y = 0; y < blockH; y++) 810 | { 811 | const unsigned char* input_line = input; 812 | for (uint32_t x = 0; x < blockW; x++) 813 | { 814 | goofyPackBlockDXT1(input_line, stride, result); 815 | input_line += 16; 816 | result += sizeof(Dxt1); 817 | } 818 | input += width * 4 * 4; // width * rgba(4) * 4 lines 819 | } 820 | return 0; 821 | } 822 | 823 | int compressETC1(unsigned char* result, const unsigned char* input, unsigned int width, unsigned int height, unsigned int stride) 824 | { 825 | if (width % 4 != 0) 826 | { 827 | return -1; 828 | } 829 | 830 | if (height % 4 != 0) 831 | { 832 | return -2; 833 | } 834 | 835 | uint32_t blockW = width >> 2; 836 | uint32_t blockH = height >> 2; 837 | 838 | for (uint32_t y = 0; y < blockH; y++) 839 | { 840 | const unsigned char* input_line = input; 841 | for (uint32_t x = 0; x < blockW; x++) 842 | { 843 | goofyPackBlockETC1S(input_line, stride, result); 844 | input_line += 16; 845 | result += sizeof(Etc1S); 846 | } 847 | input += width * 4 * 4; // width * rgba(4) * 4 lines 848 | } 849 | return 0; 850 | } 851 | 852 | } // namespace goofyRef 853 | -------------------------------------------------------------------------------- /Src/goofy_tc_reference.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Reference Goofy Compressor implementation (slow) 4 | // only useful to quickly iterate over the different ideas/heuristics 5 | namespace goofyRef 6 | { 7 | int compressDXT1(unsigned char* result, const unsigned char* input, unsigned int width, unsigned int height, unsigned int stride); 8 | int compressETC1(unsigned char* result, const unsigned char* input, unsigned int width, unsigned int height, unsigned int stride); 9 | } -------------------------------------------------------------------------------- /ThirdParty/README.md: -------------------------------------------------------------------------------- 1 | * lodepng 2 | https://github.com/lvandeve/lodepng 3 | 4 | * ryg 5 | https://github.com/nothings/stb/blob/master/stb_dxt.h 6 | 7 | * rgetc 8 | https://github.com/richgel999/rg-etc1 9 | 10 | * bc7enc 11 | https://github.com/richgel999/bc7enc 12 | 13 | * icbc 14 | https://github.com/castano/icbc 15 | 16 | -------------------------------------------------------------------------------- /ThirdParty/bc7enc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(bc7enc) 2 | 3 | cmake_minimum_required(VERSION 2.8) 4 | option(BUILD_X64 "build 64-bit" TRUE) 5 | 6 | message("Initial BUILD_X64=${BUILD_X64}") 7 | message("Initial CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}") 8 | 9 | if( NOT CMAKE_BUILD_TYPE ) 10 | set( CMAKE_BUILD_TYPE Release ) 11 | endif( NOT CMAKE_BUILD_TYPE ) 12 | 13 | message( ${PROJECT_NAME} " build type: " ${CMAKE_BUILD_TYPE} ) 14 | 15 | if (BUILD_X64) 16 | message("Building 64-bit") 17 | else() 18 | message("Building 32-bit") 19 | endif(BUILD_X64) 20 | 21 | if (NOT MSVC) 22 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g") 23 | set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g") 24 | 25 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") 26 | set(CMAKE_C_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") 27 | endif() 28 | 29 | # -fno-strict-aliasing shouldn't be necessary, it's here because that is what MSVC uses by default and that's what I've tested with the most. 30 | if (NOT MSVC) 31 | set(GCC_COMPILE_FLAGS "-fno-strict-aliasing -Wall -Wextra") 32 | if (NOT BUILD_X64) 33 | set(GCC_COMPILE_FLAGS "${GCC_COMPILE_FLAGS} -m32") 34 | endif() 35 | endif() 36 | 37 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GCC_COMPILE_FLAGS}") 38 | set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${GCC_COMPILE_FLAGS}") 39 | set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${GCC_COMPILE_FLAGS} -D_DEBUG") 40 | 41 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COMPILE_FLAGS}") 42 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${GCC_COMPILE_FLAGS}") 43 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${GCC_COMPILE_FLAGS} -D_DEBUG") 44 | 45 | set (BC7ENC_SRC_LIST ${COMMON_SRC_LIST} 46 | bc7enc.c 47 | bc7decomp.cpp 48 | lodepng.cpp 49 | test.cpp 50 | ) 51 | 52 | add_executable(bc7enc ${BC7ENC_SRC_LIST}) 53 | 54 | if (NOT MSVC) 55 | target_link_libraries(bc7enc m) 56 | endif() 57 | 58 | 59 | -------------------------------------------------------------------------------- /ThirdParty/bc7enc/LICENSE: -------------------------------------------------------------------------------- 1 | The following source code files are available under 2 licenses -- choose whichever you prefer: 2 | rgbcx.h 3 | bc7decomp.cpp/h 4 | bc7enc.c 5 | 6 | ALTERNATIVE A - MIT License 7 | Copyright(c) 2020 Richard Geldreich, Jr. 8 | Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | this software and associated documentation files(the "Software"), to deal in 10 | the Software without restriction, including without limitation the rights to 11 | use, copy, modify, merge, publish, distribute, sublicense, and / or sell copies 12 | of the Software, and to permit persons to whom the Software is furnished to do 13 | so, subject to the following conditions : 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | ------------------------------------------------------------------------------ 24 | ALTERNATIVE B - Public Domain(www.unlicense.org) 25 | This is free and unencumbered software released into the public domain. 26 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 27 | software, either in source code form or as a compiled binary, for any purpose, 28 | commercial or non - commercial, and by any means. 29 | In jurisdictions that recognize copyright laws, the author or authors of this 30 | software dedicate any and all copyright interest in the software to the public 31 | domain.We make this dedication for the benefit of the public at large and to 32 | the detriment of our heirs and successors.We intend this dedication to be an 33 | overt act of relinquishment in perpetuity of all present and future rights to 34 | this software under copyright law. 35 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 36 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 37 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 38 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 39 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 40 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 41 | 42 | ------------------------------------------------------------------------------ 43 | 44 | LodePNG version 20161127 45 | 46 | Copyright (c) 2005-2016 Lode Vandevenne 47 | 48 | This software is provided 'as-is', without any express or implied 49 | warranty. In no event will the authors be held liable for any damages 50 | arising from the use of this software. 51 | 52 | Permission is granted to anyone to use this software for any purpose, 53 | including commercial applications, and to alter it and redistribute it 54 | freely, subject to the following restrictions: 55 | 56 | 1. The origin of this software must not be misrepresented; you must not 57 | claim that you wrote the original software. If you use this software 58 | in a product, an acknowledgment in the product documentation would be 59 | appreciated but is not required. 60 | 61 | 2. Altered source versions must be plainly marked as such, and must not be 62 | misrepresented as being the original software. 63 | 64 | 3. This notice may not be removed or altered from any source 65 | distribution. 66 | 67 | -------------------------------------------------------------------------------- /ThirdParty/bc7enc/README.md: -------------------------------------------------------------------------------- 1 | bc7enc - Fast, single source file BC1-5 and BC7/BPTC GPU texture encoders. 2 | 3 | Features: 4 | - BC1/3 encoder (in [rgbcx.h](https://github.com/richgel999/bc7enc/blob/master/rgbcx.h)) uses a new algorithm (which we've named "prioritized cluster fit") which is 3-4x faster than traditional cluster fit (as implemented in [libsquish](https://github.com/svn2github/libsquish) with SSE2) at the same or slightly higher average quality using scalar CPU instructions. This algorithm is suitable for GPU encoder implementations. 5 | 6 | The BC1/BC3 encoder also implements [Castano's optimal endpoint rounding improvement](https://gist.github.com/castano/c92c7626f288f9e99e158520b14a61cf). 7 | 8 | rgbcx's BC1 encoder is faster than both AMD Compressonator and libsquish at the same average quality. 9 | 10 | - BC7 encoder (in bc7enc.c/.h) has perceptual colorspace metric support, and is very fast compared to ispc_texcomp (see below) for RGB textures. Important: The BC7 encoder included in this repo is still a work in progress. I took bc7enc16 and added more modes for better alpha support, but it needs more testing and development. 11 | 12 | - Full decoders for BC1-5/7. BC7 decoder is in bc7decomp.cpp/.h, BC1-5 decoders in rgbcx.h. 13 | 14 | This project is basically a demo of some of the techniques we use in Basis BC7, 15 | which is Binomial's state of the art vectorized BC7 encoder. Basis BC7 is the 16 | highest quality and fastest CPU BC7 encoder available (2-3x faster than 17 | ispc_texcomp). It supports all modes and linear/perceptual colorspace metrics. 18 | Licensees get full ISPC source code so they can customize the codec as needed. 19 | 20 | bc7enc currently only supports modes 1 and 6 for RGB, and modes 1, 5, 6, and 7 for alpha. The plan is to add all the modes. See the [bc7enc16](https://github.com/richgel999/bc7enc16) project for the previous version (which only supports modes 1 and 6). Note this readme still refers to "bc7enc16", but bc7enc is the same encoder but with more alpha modes. 21 | 22 | This codec supports a perceptual mode when encoding BC7, where it computes colorspace error in 23 | weighted YCbCr space (like etc2comp), and it also supports weighted RGBA 24 | metrics. It's particular strong in perceptual mode, beating the current state of 25 | the art CPU encoder (Intel's ispc_texcomp) by a wide margin when measured by 26 | Luma PSNR, even though it only supports 2 modes and isn't vectorized. 27 | 28 | Why only modes 1 and 6 for opaque BC7? 29 | Because with these two modes you have a complete encoder that supports both 30 | opaque and transparent textures in a small amount (~1400 lines) of 31 | understandable plain C code. Mode 6 excels on smooth blocks, and mode 1 is 32 | strong with complex blocks, and a strong encoder that combines both modes can be 33 | quite high quality. Fast mode 6-only encoders will have noticeable block 34 | artifacts which this codec avoids by fully supporting mode 1. 35 | 36 | Modes 1 and 6 are typically the most used modes on many textures using other 37 | encoders. Mode 1 has two subsets, 64 possible partitions, and 3-bit indices, 38 | while mode 6 has large 4-bit indices and high precision 7777.1 endpoints. This 39 | codec produces output that is far higher quality than any BC1 encoder, and 40 | approaches (or in perceptual mode exceeds!) the quality of other full BC7 41 | encoders. 42 | 43 | Why is bc7enc16 so fast in perceptual mode? 44 | Computing error in YCbCr space is more expensive than in RGB space, yet bc7enc16 45 | in perceptual mode is stronger than ispc_texcomp (see the benchmark below) - 46 | even without SSE/AVX vectorization and with only 2 modes to work with! 47 | 48 | Most BC7 encoders only support linear RGB colorspace metrics, which is a 49 | fundamental weakness. Some support weighted RGB metrics, which is better. With 50 | linear RGB metrics, encoding error is roughly balanced between each channel, and 51 | encoders have to work *very* hard (examining large amounts of RGB search space) 52 | to get overall quality up. With perceptual colorspace metrics, RGB error tends 53 | to become a bit unbalanced, with green quality favored more highly than red and 54 | blue, and blue quality favored the least. A perceptual encoder is tuned to 55 | prefer exploring solutions along the luma axis, where it's much less work to find 56 | solutions with less luma error. bc7enc16 is, as far as I know, the first BC7 57 | codec to support computing error in weighted YCbCr colorspace. 58 | 59 | Note: Most of the timings here (except for the ispc_texcomp "fast" mode timings at the very bottom) 60 | are for the *original* release, before I added several more optimizations. The latest version of 61 | bc7enc16.c is around 8-27% faster than the initial release at same quality (when mode 1 is enabled - 62 | there's no change with just mode 6). 63 | 64 | Some benchmarks across 31 images (kodim corpus+others): 65 | 66 | Perceptual (average REC709 Luma PSNR - higher is better quality): 67 | ``` 68 | iscp_texcomp slow vs. bc7enc16 uber4/max_partitions 64 69 | iscp_texcomp: 355.4 secs 48.6 dB 70 | bc7enc16: 122.6 secs 50.0 dB 71 | 72 | iscp_texcomp slow vs. bc7enc16 uber0/max_partitions 64 73 | iscp_texcomp: 355.4 secs 48.6 dB 74 | bc7enc16: 38.3 secs 49.6 dB 75 | 76 | iscp_texcomp basic vs. bc7enc16 uber0/max_partitions 16 77 | ispc_texcomp: 100.2 secs 48.3 dB 78 | bc7enc16: 20.8 secs 49.3 dB 79 | 80 | iscp_texcomp fast vs. bc7enc16 uber0/max_partitions 16 81 | iscp_texcomp: 41.5 secs 48.0 dB 82 | bc7enc16: 20.8 secs 49.3 dB 83 | 84 | iscp_texcomp ultrafast vs. bc7enc16 uber0/max_partitions 0 85 | iscp_texcomp: 1.9 secs 46.2 dB 86 | bc7enc16: 8.9 secs 48.4 dB 87 | 88 | Non-perceptual (average RGB PSNR): 89 | 90 | iscp_texcomp slow vs. bc7enc16 uber4/max_partitions 64 91 | iscp_texcomp: 355.4 secs 46.8 dB 92 | bc7enc16: 51 secs 46.1 dB 93 | 94 | iscp_texcomp slow vs. bc7enc16 uber0/max_partitions 64 95 | iscp_texcomp: 355.4 secs 46.8 dB 96 | bc7enc16: 29.3 secs 45.8 dB 97 | 98 | iscp_texcomp basic vs. bc7enc16 uber4/max_partitions 64 99 | iscp_texcomp: 99.9 secs 46.5 dB 100 | bc7enc16: 51 secs 46.1 dB 101 | 102 | iscp_texcomp fast vs. bc7enc16 uber1/max_partitions 16 103 | ispc_texcomp: 41.5 secs 46.1 dB 104 | bc7enc16: 19.8 secs 45.5 dB 105 | 106 | iscp_texcomp fast vs. bc7enc16 uber0/max_partitions 8 107 | ispc_texcomp: 41.5 secs 46.1 dB 108 | bc7enc16: 10.46 secs 44.4 dB 109 | 110 | iscp_texcomp ultrafast vs. bc7enc16 uber0/max_partitions 0 111 | ispc_texcomp: 1.9 secs 42.7 dB 112 | bc7enc16: 3.8 secs 42.7 dB 113 | 114 | DirectXTex CPU in "mode 6 only" mode vs. bc7enc16 uber1/max_partions 0 (mode 6 only), non-perceptual: 115 | 116 | DirectXTex: 466.4 secs 41.9 dB 117 | bc7enc16: 6.7 secs 42.8 dB 118 | 119 | DirectXTex CPU in (default - no 3 subset modes) vs. bc7enc16 uber1/max_partions 64, non-perceptual: 120 | 121 | DirectXTex: 9485.1 secs 45.6 dB 122 | bc7enc16: 36 secs 46.0 dB 123 | ``` 124 | (Note this version of DirectXTex has a key pbit bugfix which I've submitted but 125 | is still waiting to be accepted. Non-bugfixed versions will be slightly lower 126 | quality.) 127 | 128 | UPDATE: To illustrate how strong the mode 1+6 implementation is in bc7enc16, let's compare ispc_texcomp 129 | fast vs. the latest version of bc7enc16 uber4/max_partitions 64: 130 | 131 | Without filterbank optimizations: 132 | ``` 133 | Time RGB PSNR Y PSNR 134 | ispc_texcomp: 41.45 secs 46.09 dB 48.0 dB 135 | bc7enc16: 41.42 secs 46.03 dB 48.2 dB 136 | 137 | With filterbank optimizations enabled: 138 | bc7enc16: 38.78 secs 45.94 dB 48.12 dB 139 | ``` 140 | They both have virtually the same average RGB PSNR with these settings (.06 dB is basically noise), but 141 | bc7enc16 is just as fast as ispc_texcomp fast, even though it's not vectorized. Interestingly, our Y PSNR is better, 142 | although bc7enc16 wasn't using perceptual metrics in these benchmarks. 143 | 144 | This was a multithreaded benchmark (using OpenMP) on a dual Xeon workstation. 145 | ispc_texcomp was called with 64-blocks at a time and used AVX instructions. 146 | Timings are for encoding only. 147 | -------------------------------------------------------------------------------- /ThirdParty/bc7enc/bc7decomp.cpp: -------------------------------------------------------------------------------- 1 | // File: bc7decomp.c - Richard Geldreich, Jr. 3/31/2020 - MIT license or public domain (see end of file) 2 | #include "bc7decomp.h" 3 | 4 | namespace bc7decomp 5 | { 6 | 7 | const uint32_t g_bc7_weights2[4] = { 0, 21, 43, 64 }; 8 | const uint32_t g_bc7_weights3[8] = { 0, 9, 18, 27, 37, 46, 55, 64 }; 9 | const uint32_t g_bc7_weights4[16] = { 0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64 }; 10 | 11 | const uint8_t g_bc7_partition2[64 * 16] = 12 | { 13 | 0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1, 0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1, 0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1, 0,0,0,1,0,0,1,1,0,0,1,1,0,1,1,1, 0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,1, 0,0,1,1,0,1,1,1,0,1,1,1,1,1,1,1, 0,0,0,1,0,0,1,1,0,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,1,0,0,1,1,0,1,1,1, 14 | 0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1, 0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,1,0,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1, 0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1, 0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1, 15 | 0,0,0,0,1,0,0,0,1,1,1,0,1,1,1,1, 0,1,1,1,0,0,0,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,1,0,0,0,1,1,1,0, 0,1,1,1,0,0,1,1,0,0,0,1,0,0,0,0, 0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0, 0,0,0,0,1,0,0,0,1,1,0,0,1,1,1,0, 0,0,0,0,0,0,0,0,1,0,0,0,1,1,0,0, 0,1,1,1,0,0,1,1,0,0,1,1,0,0,0,1, 16 | 0,0,1,1,0,0,0,1,0,0,0,1,0,0,0,0, 0,0,0,0,1,0,0,0,1,0,0,0,1,1,0,0, 0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0, 0,0,1,1,0,1,1,0,0,1,1,0,1,1,0,0, 0,0,0,1,0,1,1,1,1,1,1,0,1,0,0,0, 0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0, 0,1,1,1,0,0,0,1,1,0,0,0,1,1,1,0, 0,0,1,1,1,0,0,1,1,0,0,1,1,1,0,0, 17 | 0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1, 0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1, 0,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0, 0,0,1,1,0,0,1,1,1,1,0,0,1,1,0,0, 0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0, 0,1,0,1,0,1,0,1,1,0,1,0,1,0,1,0, 0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1, 0,1,0,1,1,0,1,0,1,0,1,0,0,1,0,1, 18 | 0,1,1,1,0,0,1,1,1,1,0,0,1,1,1,0, 0,0,0,1,0,0,1,1,1,1,0,0,1,0,0,0, 0,0,1,1,0,0,1,0,0,1,0,0,1,1,0,0, 0,0,1,1,1,0,1,1,1,1,0,1,1,1,0,0, 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, 0,0,1,1,1,1,0,0,1,1,0,0,0,0,1,1, 0,1,1,0,0,1,1,0,1,0,0,1,1,0,0,1, 0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0, 19 | 0,1,0,0,1,1,1,0,0,1,0,0,0,0,0,0, 0,0,1,0,0,1,1,1,0,0,1,0,0,0,0,0, 0,0,0,0,0,0,1,0,0,1,1,1,0,0,1,0, 0,0,0,0,0,1,0,0,1,1,1,0,0,1,0,0, 0,1,1,0,1,1,0,0,1,0,0,1,0,0,1,1, 0,0,1,1,0,1,1,0,1,1,0,0,1,0,0,1, 0,1,1,0,0,0,1,1,1,0,0,1,1,1,0,0, 0,0,1,1,1,0,0,1,1,1,0,0,0,1,1,0, 20 | 0,1,1,0,1,1,0,0,1,1,0,0,1,0,0,1, 0,1,1,0,0,0,1,1,0,0,1,1,1,0,0,1, 0,1,1,1,1,1,1,0,1,0,0,0,0,0,0,1, 0,0,0,1,1,0,0,0,1,1,1,0,0,1,1,1, 0,0,0,0,1,1,1,1,0,0,1,1,0,0,1,1, 0,0,1,1,0,0,1,1,1,1,1,1,0,0,0,0, 0,0,1,0,0,0,1,0,1,1,1,0,1,1,1,0, 0,1,0,0,0,1,0,0,0,1,1,1,0,1,1,1 21 | }; 22 | 23 | const uint8_t g_bc7_partition3[64 * 16] = 24 | { 25 | 0,0,1,1,0,0,1,1,0,2,2,1,2,2,2,2, 0,0,0,1,0,0,1,1,2,2,1,1,2,2,2,1, 0,0,0,0,2,0,0,1,2,2,1,1,2,2,1,1, 0,2,2,2,0,0,2,2,0,0,1,1,0,1,1,1, 0,0,0,0,0,0,0,0,1,1,2,2,1,1,2,2, 0,0,1,1,0,0,1,1,0,0,2,2,0,0,2,2, 0,0,2,2,0,0,2,2,1,1,1,1,1,1,1,1, 0,0,1,1,0,0,1,1,2,2,1,1,2,2,1,1, 26 | 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2, 0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2, 0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2, 0,0,1,2,0,0,1,2,0,0,1,2,0,0,1,2, 0,1,1,2,0,1,1,2,0,1,1,2,0,1,1,2, 0,1,2,2,0,1,2,2,0,1,2,2,0,1,2,2, 0,0,1,1,0,1,1,2,1,1,2,2,1,2,2,2, 0,0,1,1,2,0,0,1,2,2,0,0,2,2,2,0, 27 | 0,0,0,1,0,0,1,1,0,1,1,2,1,1,2,2, 0,1,1,1,0,0,1,1,2,0,0,1,2,2,0,0, 0,0,0,0,1,1,2,2,1,1,2,2,1,1,2,2, 0,0,2,2,0,0,2,2,0,0,2,2,1,1,1,1, 0,1,1,1,0,1,1,1,0,2,2,2,0,2,2,2, 0,0,0,1,0,0,0,1,2,2,2,1,2,2,2,1, 0,0,0,0,0,0,1,1,0,1,2,2,0,1,2,2, 0,0,0,0,1,1,0,0,2,2,1,0,2,2,1,0, 28 | 0,1,2,2,0,1,2,2,0,0,1,1,0,0,0,0, 0,0,1,2,0,0,1,2,1,1,2,2,2,2,2,2, 0,1,1,0,1,2,2,1,1,2,2,1,0,1,1,0, 0,0,0,0,0,1,1,0,1,2,2,1,1,2,2,1, 0,0,2,2,1,1,0,2,1,1,0,2,0,0,2,2, 0,1,1,0,0,1,1,0,2,0,0,2,2,2,2,2, 0,0,1,1,0,1,2,2,0,1,2,2,0,0,1,1, 0,0,0,0,2,0,0,0,2,2,1,1,2,2,2,1, 29 | 0,0,0,0,0,0,0,2,1,1,2,2,1,2,2,2, 0,2,2,2,0,0,2,2,0,0,1,2,0,0,1,1, 0,0,1,1,0,0,1,2,0,0,2,2,0,2,2,2, 0,1,2,0,0,1,2,0,0,1,2,0,0,1,2,0, 0,0,0,0,1,1,1,1,2,2,2,2,0,0,0,0, 0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0, 0,1,2,0,2,0,1,2,1,2,0,1,0,1,2,0, 0,0,1,1,2,2,0,0,1,1,2,2,0,0,1,1, 30 | 0,0,1,1,1,1,2,2,2,2,0,0,0,0,1,1, 0,1,0,1,0,1,0,1,2,2,2,2,2,2,2,2, 0,0,0,0,0,0,0,0,2,1,2,1,2,1,2,1, 0,0,2,2,1,1,2,2,0,0,2,2,1,1,2,2, 0,0,2,2,0,0,1,1,0,0,2,2,0,0,1,1, 0,2,2,0,1,2,2,1,0,2,2,0,1,2,2,1, 0,1,0,1,2,2,2,2,2,2,2,2,0,1,0,1, 0,0,0,0,2,1,2,1,2,1,2,1,2,1,2,1, 31 | 0,1,0,1,0,1,0,1,0,1,0,1,2,2,2,2, 0,2,2,2,0,1,1,1,0,2,2,2,0,1,1,1, 0,0,0,2,1,1,1,2,0,0,0,2,1,1,1,2, 0,0,0,0,2,1,1,2,2,1,1,2,2,1,1,2, 0,2,2,2,0,1,1,1,0,1,1,1,0,2,2,2, 0,0,0,2,1,1,1,2,1,1,1,2,0,0,0,2, 0,1,1,0,0,1,1,0,0,1,1,0,2,2,2,2, 0,0,0,0,0,0,0,0,2,1,1,2,2,1,1,2, 32 | 0,1,1,0,0,1,1,0,2,2,2,2,2,2,2,2, 0,0,2,2,0,0,1,1,0,0,1,1,0,0,2,2, 0,0,2,2,1,1,2,2,1,1,2,2,0,0,2,2, 0,0,0,0,0,0,0,0,0,0,0,0,2,1,1,2, 0,0,0,2,0,0,0,1,0,0,0,2,0,0,0,1, 0,2,2,2,1,2,2,2,0,2,2,2,1,2,2,2, 0,1,0,1,2,2,2,2,2,2,2,2,2,2,2,2, 0,1,1,1,2,0,1,1,2,2,0,1,2,2,2,0, 33 | }; 34 | 35 | const uint8_t g_bc7_table_anchor_index_second_subset[64] = { 15,15,15,15,15,15,15,15, 15,15,15,15,15,15,15,15, 15, 2, 8, 2, 2, 8, 8,15, 2, 8, 2, 2, 8, 8, 2, 2, 15,15, 6, 8, 2, 8,15,15, 2, 8, 2, 2, 2,15,15, 6, 6, 2, 6, 8,15,15, 2, 2, 15,15,15,15,15, 2, 2,15 }; 36 | 37 | const uint8_t g_bc7_table_anchor_index_third_subset_1[64] = 38 | { 39 | 3, 3,15,15, 8, 3,15,15, 8, 8, 6, 6, 6, 5, 3, 3, 3, 3, 8,15, 3, 3, 6,10, 5, 8, 8, 6, 8, 5,15,15, 8,15, 3, 5, 6,10, 8,15, 15, 3,15, 5,15,15,15,15, 3,15, 5, 5, 5, 8, 5,10, 5,10, 8,13,15,12, 3, 3 40 | }; 41 | 42 | const uint8_t g_bc7_table_anchor_index_third_subset_2[64] = 43 | { 44 | 15, 8, 8, 3,15,15, 3, 8, 15,15,15,15,15,15,15, 8, 15, 8,15, 3,15, 8,15, 8, 3,15, 6,10,15,15,10, 8, 15, 3,15,10,10, 8, 9,10, 6,15, 8,15, 3, 6, 6, 8, 15, 3,15,15,15,15,15,15, 15,15,15,15, 3,15,15, 8 45 | }; 46 | 47 | inline uint32_t read_bits32(const uint8_t* pBuf, uint32_t& bit_offset, uint32_t codesize) 48 | { 49 | assert(codesize <= 32); 50 | uint32_t bits = 0; 51 | uint32_t total_bits = 0; 52 | 53 | while (total_bits < codesize) 54 | { 55 | uint32_t byte_bit_offset = bit_offset & 7; 56 | uint32_t bits_to_read = std::min(codesize - total_bits, 8 - byte_bit_offset); 57 | 58 | uint32_t byte_bits = pBuf[bit_offset >> 3] >> byte_bit_offset; 59 | byte_bits &= ((1 << bits_to_read) - 1); 60 | 61 | bits |= (byte_bits << total_bits); 62 | 63 | total_bits += bits_to_read; 64 | bit_offset += bits_to_read; 65 | } 66 | 67 | return bits; 68 | } 69 | 70 | // BC7 mode 0-7 decompression. 71 | // Instead of one monster routine to unpack all the BC7 modes, we're lumping the 3 subset, 2 subset, 1 subset, and dual plane modes together into simple shared routines. 72 | 73 | static inline uint32_t bc7_dequant(uint32_t val, uint32_t pbit, uint32_t val_bits) { assert(val < (1U << val_bits)); assert(pbit < 2); assert(val_bits >= 4 && val_bits <= 8); const uint32_t total_bits = val_bits + 1; val = (val << 1) | pbit; val <<= (8 - total_bits); val |= (val >> total_bits); assert(val <= 255); return val; } 74 | static inline uint32_t bc7_dequant(uint32_t val, uint32_t val_bits) { assert(val < (1U << val_bits)); assert(val_bits >= 4 && val_bits <= 8); val <<= (8 - val_bits); val |= (val >> val_bits); assert(val <= 255); return val; } 75 | 76 | static inline uint32_t bc7_interp2(uint32_t l, uint32_t h, uint32_t w) { assert(w < 4); return (l * (64 - g_bc7_weights2[w]) + h * g_bc7_weights2[w] + 32) >> 6; } 77 | static inline uint32_t bc7_interp3(uint32_t l, uint32_t h, uint32_t w) { assert(w < 8); return (l * (64 - g_bc7_weights3[w]) + h * g_bc7_weights3[w] + 32) >> 6; } 78 | static inline uint32_t bc7_interp4(uint32_t l, uint32_t h, uint32_t w) { assert(w < 16); return (l * (64 - g_bc7_weights4[w]) + h * g_bc7_weights4[w] + 32) >> 6; } 79 | static inline uint32_t bc7_interp(uint32_t l, uint32_t h, uint32_t w, uint32_t bits) 80 | { 81 | assert(l <= 255 && h <= 255); 82 | switch (bits) 83 | { 84 | case 2: return bc7_interp2(l, h, w); 85 | case 3: return bc7_interp3(l, h, w); 86 | case 4: return bc7_interp4(l, h, w); 87 | default: 88 | break; 89 | } 90 | return 0; 91 | } 92 | 93 | bool unpack_bc7_mode0_2(uint32_t mode, const void* pBlock_bits, color_rgba* pPixels) 94 | { 95 | //const uint32_t SUBSETS = 3; 96 | const uint32_t ENDPOINTS = 6; 97 | const uint32_t COMPS = 3; 98 | const uint32_t WEIGHT_BITS = (mode == 0) ? 3 : 2; 99 | const uint32_t ENDPOINT_BITS = (mode == 0) ? 4 : 5; 100 | const uint32_t PBITS = (mode == 0) ? 6 : 0; 101 | const uint32_t WEIGHT_VALS = 1 << WEIGHT_BITS; 102 | 103 | uint32_t bit_offset = 0; 104 | const uint8_t* pBuf = static_cast(pBlock_bits); 105 | 106 | if (read_bits32(pBuf, bit_offset, mode + 1) != (1U << mode)) return false; 107 | 108 | const uint32_t part = read_bits32(pBuf, bit_offset, (mode == 0) ? 4 : 6); 109 | 110 | color_rgba endpoints[ENDPOINTS]; 111 | for (uint32_t c = 0; c < COMPS; c++) 112 | for (uint32_t e = 0; e < ENDPOINTS; e++) 113 | endpoints[e][c] = (uint8_t)read_bits32(pBuf, bit_offset, ENDPOINT_BITS); 114 | 115 | uint32_t pbits[6]; 116 | for (uint32_t p = 0; p < PBITS; p++) 117 | pbits[p] = read_bits32(pBuf, bit_offset, 1); 118 | 119 | uint32_t weights[16]; 120 | for (uint32_t i = 0; i < 16; i++) 121 | weights[i] = read_bits32(pBuf, bit_offset, ((!i) || (i == g_bc7_table_anchor_index_third_subset_1[part]) || (i == g_bc7_table_anchor_index_third_subset_2[part])) ? (WEIGHT_BITS - 1) : WEIGHT_BITS); 122 | 123 | assert(bit_offset == 128); 124 | 125 | for (uint32_t e = 0; e < ENDPOINTS; e++) 126 | for (uint32_t c = 0; c < 4; c++) 127 | endpoints[e][c] = (uint8_t)((c == 3) ? 255 : (PBITS ? bc7_dequant(endpoints[e][c], pbits[e], ENDPOINT_BITS) : bc7_dequant(endpoints[e][c], ENDPOINT_BITS))); 128 | 129 | color_rgba block_colors[3][8]; 130 | for (uint32_t s = 0; s < 3; s++) 131 | for (uint32_t i = 0; i < WEIGHT_VALS; i++) 132 | { 133 | for (uint32_t c = 0; c < 3; c++) 134 | block_colors[s][i][c] = (uint8_t)bc7_interp(endpoints[s * 2 + 0][c], endpoints[s * 2 + 1][c], i, WEIGHT_BITS); 135 | block_colors[s][i][3] = 255; 136 | } 137 | 138 | for (uint32_t i = 0; i < 16; i++) 139 | pPixels[i] = block_colors[g_bc7_partition3[part * 16 + i]][weights[i]]; 140 | 141 | return true; 142 | } 143 | 144 | bool unpack_bc7_mode1_3_7(uint32_t mode, const void* pBlock_bits, color_rgba* pPixels) 145 | { 146 | //const uint32_t SUBSETS = 2; 147 | const uint32_t ENDPOINTS = 4; 148 | const uint32_t COMPS = (mode == 7) ? 4 : 3; 149 | const uint32_t WEIGHT_BITS = (mode == 1) ? 3 : 2; 150 | const uint32_t ENDPOINT_BITS = (mode == 7) ? 5 : ((mode == 1) ? 6 : 7); 151 | const uint32_t PBITS = (mode == 1) ? 2 : 4; 152 | const uint32_t SHARED_PBITS = (mode == 1) ? true : false; 153 | const uint32_t WEIGHT_VALS = 1 << WEIGHT_BITS; 154 | 155 | uint32_t bit_offset = 0; 156 | const uint8_t* pBuf = static_cast(pBlock_bits); 157 | 158 | if (read_bits32(pBuf, bit_offset, mode + 1) != (1U << mode)) return false; 159 | 160 | const uint32_t part = read_bits32(pBuf, bit_offset, 6); 161 | 162 | color_rgba endpoints[ENDPOINTS]; 163 | for (uint32_t c = 0; c < COMPS; c++) 164 | for (uint32_t e = 0; e < ENDPOINTS; e++) 165 | endpoints[e][c] = (uint8_t)read_bits32(pBuf, bit_offset, ENDPOINT_BITS); 166 | 167 | uint32_t pbits[4]; 168 | for (uint32_t p = 0; p < PBITS; p++) 169 | pbits[p] = read_bits32(pBuf, bit_offset, 1); 170 | 171 | uint32_t weights[16]; 172 | for (uint32_t i = 0; i < 16; i++) 173 | weights[i] = read_bits32(pBuf, bit_offset, ((!i) || (i == g_bc7_table_anchor_index_second_subset[part])) ? (WEIGHT_BITS - 1) : WEIGHT_BITS); 174 | 175 | assert(bit_offset == 128); 176 | 177 | for (uint32_t e = 0; e < ENDPOINTS; e++) 178 | for (uint32_t c = 0; c < 4; c++) 179 | endpoints[e][c] = (uint8_t)((c == ((mode == 7U) ? 4U : 3U)) ? 255 : bc7_dequant(endpoints[e][c], pbits[SHARED_PBITS ? (e >> 1) : e], ENDPOINT_BITS)); 180 | 181 | color_rgba block_colors[2][8]; 182 | for (uint32_t s = 0; s < 2; s++) 183 | for (uint32_t i = 0; i < WEIGHT_VALS; i++) 184 | { 185 | for (uint32_t c = 0; c < COMPS; c++) 186 | block_colors[s][i][c] = (uint8_t)bc7_interp(endpoints[s * 2 + 0][c], endpoints[s * 2 + 1][c], i, WEIGHT_BITS); 187 | block_colors[s][i][3] = (COMPS == 3) ? 255 : block_colors[s][i][3]; 188 | } 189 | 190 | for (uint32_t i = 0; i < 16; i++) 191 | pPixels[i] = block_colors[g_bc7_partition2[part * 16 + i]][weights[i]]; 192 | 193 | return true; 194 | } 195 | 196 | bool unpack_bc7_mode4_5(uint32_t mode, const void* pBlock_bits, color_rgba* pPixels) 197 | { 198 | const uint32_t ENDPOINTS = 2; 199 | const uint32_t COMPS = 4; 200 | const uint32_t WEIGHT_BITS = 2; 201 | const uint32_t A_WEIGHT_BITS = (mode == 4) ? 3 : 2; 202 | const uint32_t ENDPOINT_BITS = (mode == 4) ? 5 : 7; 203 | const uint32_t A_ENDPOINT_BITS = (mode == 4) ? 6 : 8; 204 | //const uint32_t WEIGHT_VALS = 1 << WEIGHT_BITS; 205 | //const uint32_t A_WEIGHT_VALS = 1 << A_WEIGHT_BITS; 206 | 207 | uint32_t bit_offset = 0; 208 | const uint8_t* pBuf = static_cast(pBlock_bits); 209 | 210 | if (read_bits32(pBuf, bit_offset, mode + 1) != (1U << mode)) return false; 211 | 212 | const uint32_t comp_rot = read_bits32(pBuf, bit_offset, 2); 213 | const uint32_t index_mode = (mode == 4) ? read_bits32(pBuf, bit_offset, 1) : 0; 214 | 215 | color_rgba endpoints[ENDPOINTS]; 216 | for (uint32_t c = 0; c < COMPS; c++) 217 | for (uint32_t e = 0; e < ENDPOINTS; e++) 218 | endpoints[e][c] = (uint8_t)read_bits32(pBuf, bit_offset, (c == 3) ? A_ENDPOINT_BITS : ENDPOINT_BITS); 219 | 220 | const uint32_t weight_bits[2] = { index_mode ? A_WEIGHT_BITS : WEIGHT_BITS, index_mode ? WEIGHT_BITS : A_WEIGHT_BITS }; 221 | 222 | uint32_t weights[16], a_weights[16]; 223 | 224 | for (uint32_t i = 0; i < 16; i++) 225 | (index_mode ? a_weights : weights)[i] = read_bits32(pBuf, bit_offset, weight_bits[index_mode] - ((!i) ? 1 : 0)); 226 | 227 | for (uint32_t i = 0; i < 16; i++) 228 | (index_mode ? weights : a_weights)[i] = read_bits32(pBuf, bit_offset, weight_bits[1 - index_mode] - ((!i) ? 1 : 0)); 229 | 230 | assert(bit_offset == 128); 231 | 232 | for (uint32_t e = 0; e < ENDPOINTS; e++) 233 | for (uint32_t c = 0; c < 4; c++) 234 | endpoints[e][c] = (uint8_t)bc7_dequant(endpoints[e][c], (c == 3) ? A_ENDPOINT_BITS : ENDPOINT_BITS); 235 | 236 | color_rgba block_colors[8]; 237 | for (uint32_t i = 0; i < (1U << weight_bits[0]); i++) 238 | for (uint32_t c = 0; c < 3; c++) 239 | block_colors[i][c] = (uint8_t)bc7_interp(endpoints[0][c], endpoints[1][c], i, weight_bits[0]); 240 | 241 | for (uint32_t i = 0; i < (1U << weight_bits[1]); i++) 242 | block_colors[i][3] = (uint8_t)bc7_interp(endpoints[0][3], endpoints[1][3], i, weight_bits[1]); 243 | 244 | for (uint32_t i = 0; i < 16; i++) 245 | { 246 | pPixels[i] = block_colors[weights[i]]; 247 | pPixels[i].a = block_colors[a_weights[i]].a; 248 | if (comp_rot >= 1) 249 | std::swap(pPixels[i].a, pPixels[i].m_comps[comp_rot - 1]); 250 | } 251 | 252 | return true; 253 | } 254 | 255 | struct bc7_mode_6 256 | { 257 | struct 258 | { 259 | uint64_t m_mode : 7; 260 | uint64_t m_r0 : 7; 261 | uint64_t m_r1 : 7; 262 | uint64_t m_g0 : 7; 263 | uint64_t m_g1 : 7; 264 | uint64_t m_b0 : 7; 265 | uint64_t m_b1 : 7; 266 | uint64_t m_a0 : 7; 267 | uint64_t m_a1 : 7; 268 | uint64_t m_p0 : 1; 269 | } m_lo; 270 | 271 | union 272 | { 273 | struct 274 | { 275 | uint64_t m_p1 : 1; 276 | uint64_t m_s00 : 3; 277 | uint64_t m_s10 : 4; 278 | uint64_t m_s20 : 4; 279 | uint64_t m_s30 : 4; 280 | 281 | uint64_t m_s01 : 4; 282 | uint64_t m_s11 : 4; 283 | uint64_t m_s21 : 4; 284 | uint64_t m_s31 : 4; 285 | 286 | uint64_t m_s02 : 4; 287 | uint64_t m_s12 : 4; 288 | uint64_t m_s22 : 4; 289 | uint64_t m_s32 : 4; 290 | 291 | uint64_t m_s03 : 4; 292 | uint64_t m_s13 : 4; 293 | uint64_t m_s23 : 4; 294 | uint64_t m_s33 : 4; 295 | 296 | } m_hi; 297 | 298 | uint64_t m_hi_bits; 299 | }; 300 | }; 301 | 302 | bool unpack_bc7_mode6(const void *pBlock_bits, color_rgba *pPixels) 303 | { 304 | static_assert(sizeof(bc7_mode_6) == 16, "sizeof(bc7_mode_6) == 16"); 305 | 306 | const bc7_mode_6 &block = *static_cast(pBlock_bits); 307 | 308 | if (block.m_lo.m_mode != (1 << 6)) 309 | return false; 310 | 311 | const uint32_t r0 = (uint32_t)((block.m_lo.m_r0 << 1) | block.m_lo.m_p0); 312 | const uint32_t g0 = (uint32_t)((block.m_lo.m_g0 << 1) | block.m_lo.m_p0); 313 | const uint32_t b0 = (uint32_t)((block.m_lo.m_b0 << 1) | block.m_lo.m_p0); 314 | const uint32_t a0 = (uint32_t)((block.m_lo.m_a0 << 1) | block.m_lo.m_p0); 315 | const uint32_t r1 = (uint32_t)((block.m_lo.m_r1 << 1) | block.m_hi.m_p1); 316 | const uint32_t g1 = (uint32_t)((block.m_lo.m_g1 << 1) | block.m_hi.m_p1); 317 | const uint32_t b1 = (uint32_t)((block.m_lo.m_b1 << 1) | block.m_hi.m_p1); 318 | const uint32_t a1 = (uint32_t)((block.m_lo.m_a1 << 1) | block.m_hi.m_p1); 319 | 320 | color_rgba vals[16]; 321 | for (uint32_t i = 0; i < 16; i++) 322 | { 323 | const uint32_t w = g_bc7_weights4[i]; 324 | const uint32_t iw = 64 - w; 325 | vals[i].set_noclamp_rgba( 326 | (r0 * iw + r1 * w + 32) >> 6, 327 | (g0 * iw + g1 * w + 32) >> 6, 328 | (b0 * iw + b1 * w + 32) >> 6, 329 | (a0 * iw + a1 * w + 32) >> 6); 330 | } 331 | 332 | pPixels[0] = vals[block.m_hi.m_s00]; 333 | pPixels[1] = vals[block.m_hi.m_s10]; 334 | pPixels[2] = vals[block.m_hi.m_s20]; 335 | pPixels[3] = vals[block.m_hi.m_s30]; 336 | 337 | pPixels[4] = vals[block.m_hi.m_s01]; 338 | pPixels[5] = vals[block.m_hi.m_s11]; 339 | pPixels[6] = vals[block.m_hi.m_s21]; 340 | pPixels[7] = vals[block.m_hi.m_s31]; 341 | 342 | pPixels[8] = vals[block.m_hi.m_s02]; 343 | pPixels[9] = vals[block.m_hi.m_s12]; 344 | pPixels[10] = vals[block.m_hi.m_s22]; 345 | pPixels[11] = vals[block.m_hi.m_s32]; 346 | 347 | pPixels[12] = vals[block.m_hi.m_s03]; 348 | pPixels[13] = vals[block.m_hi.m_s13]; 349 | pPixels[14] = vals[block.m_hi.m_s23]; 350 | pPixels[15] = vals[block.m_hi.m_s33]; 351 | 352 | return true; 353 | } 354 | 355 | bool unpack_bc7(const void *pBlock, color_rgba *pPixels) 356 | { 357 | const uint32_t first_byte = static_cast(pBlock)[0]; 358 | 359 | for (uint32_t mode = 0; mode <= 7; mode++) 360 | { 361 | if (first_byte & (1U << mode)) 362 | { 363 | switch (mode) 364 | { 365 | case 0: 366 | case 2: 367 | return unpack_bc7_mode0_2(mode, pBlock, pPixels); 368 | case 1: 369 | case 3: 370 | case 7: 371 | return unpack_bc7_mode1_3_7(mode, pBlock, pPixels); 372 | case 4: 373 | case 5: 374 | return unpack_bc7_mode4_5(mode, pBlock, pPixels); 375 | case 6: 376 | return unpack_bc7_mode6(pBlock, pPixels); 377 | default: 378 | break; 379 | } 380 | } 381 | } 382 | 383 | return false; 384 | } 385 | 386 | } // namespace bc7decomp 387 | 388 | /* 389 | ------------------------------------------------------------------------------ 390 | This software is available under 2 licenses -- choose whichever you prefer. 391 | ------------------------------------------------------------------------------ 392 | ALTERNATIVE A - MIT License 393 | Copyright(c) 2020 Richard Geldreich, Jr. 394 | Permission is hereby granted, free of charge, to any person obtaining a copy of 395 | this software and associated documentation files(the "Software"), to deal in 396 | the Software without restriction, including without limitation the rights to 397 | use, copy, modify, merge, publish, distribute, sublicense, and / or sell copies 398 | of the Software, and to permit persons to whom the Software is furnished to do 399 | so, subject to the following conditions : 400 | The above copyright notice and this permission notice shall be included in all 401 | copies or substantial portions of the Software. 402 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 403 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 404 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 405 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 406 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 407 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 408 | SOFTWARE. 409 | ------------------------------------------------------------------------------ 410 | ALTERNATIVE B - Public Domain(www.unlicense.org) 411 | This is free and unencumbered software released into the public domain. 412 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 413 | software, either in source code form or as a compiled binary, for any purpose, 414 | commercial or non - commercial, and by any means. 415 | In jurisdictions that recognize copyright laws, the author or authors of this 416 | software dedicate any and all copyright interest in the software to the public 417 | domain.We make this dedication for the benefit of the public at large and to 418 | the detriment of our heirs and successors.We intend this dedication to be an 419 | overt act of relinquishment in perpetuity of all present and future rights to 420 | this software under copyright law. 421 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 422 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 423 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 424 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 425 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 426 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 427 | ------------------------------------------------------------------------------ 428 | */ 429 | 430 | -------------------------------------------------------------------------------- /ThirdParty/bc7enc/bc7decomp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace bc7decomp 10 | { 11 | 12 | enum eNoClamp { cNoClamp }; 13 | 14 | template inline S clamp(S value, S low, S high) { return (value < low) ? low : ((value > high) ? high : value); } 15 | 16 | class color_rgba 17 | { 18 | public: 19 | union 20 | { 21 | uint8_t m_comps[4]; 22 | 23 | struct 24 | { 25 | uint8_t r; 26 | uint8_t g; 27 | uint8_t b; 28 | uint8_t a; 29 | }; 30 | }; 31 | 32 | inline color_rgba() 33 | { 34 | static_assert(sizeof(*this) == 4, "sizeof(*this) != 4"); 35 | } 36 | 37 | inline color_rgba(int y) 38 | { 39 | set(y); 40 | } 41 | 42 | inline color_rgba(int y, int na) 43 | { 44 | set(y, na); 45 | } 46 | 47 | inline color_rgba(int sr, int sg, int sb, int sa) 48 | { 49 | set(sr, sg, sb, sa); 50 | } 51 | 52 | inline color_rgba(eNoClamp, int sr, int sg, int sb, int sa) 53 | { 54 | set_noclamp_rgba((uint8_t)sr, (uint8_t)sg, (uint8_t)sb, (uint8_t)sa); 55 | } 56 | 57 | inline color_rgba& set_noclamp_y(int y) 58 | { 59 | m_comps[0] = (uint8_t)y; 60 | m_comps[1] = (uint8_t)y; 61 | m_comps[2] = (uint8_t)y; 62 | m_comps[3] = (uint8_t)255; 63 | return *this; 64 | } 65 | 66 | inline color_rgba &set_noclamp_rgba(int sr, int sg, int sb, int sa) 67 | { 68 | m_comps[0] = (uint8_t)sr; 69 | m_comps[1] = (uint8_t)sg; 70 | m_comps[2] = (uint8_t)sb; 71 | m_comps[3] = (uint8_t)sa; 72 | return *this; 73 | } 74 | 75 | inline color_rgba &set(int y) 76 | { 77 | m_comps[0] = static_cast(clamp(y, 0, 255)); 78 | m_comps[1] = m_comps[0]; 79 | m_comps[2] = m_comps[0]; 80 | m_comps[3] = 255; 81 | return *this; 82 | } 83 | 84 | inline color_rgba &set(int y, int na) 85 | { 86 | m_comps[0] = static_cast(clamp(y, 0, 255)); 87 | m_comps[1] = m_comps[0]; 88 | m_comps[2] = m_comps[0]; 89 | m_comps[3] = static_cast(clamp(na, 0, 255)); 90 | return *this; 91 | } 92 | 93 | inline color_rgba &set(int sr, int sg, int sb, int sa) 94 | { 95 | m_comps[0] = static_cast(clamp(sr, 0, 255)); 96 | m_comps[1] = static_cast(clamp(sg, 0, 255)); 97 | m_comps[2] = static_cast(clamp(sb, 0, 255)); 98 | m_comps[3] = static_cast(clamp(sa, 0, 255)); 99 | return *this; 100 | } 101 | 102 | inline color_rgba &set_rgb(int sr, int sg, int sb) 103 | { 104 | m_comps[0] = static_cast(clamp(sr, 0, 255)); 105 | m_comps[1] = static_cast(clamp(sg, 0, 255)); 106 | m_comps[2] = static_cast(clamp(sb, 0, 255)); 107 | return *this; 108 | } 109 | 110 | inline color_rgba &set_rgb(const color_rgba &other) 111 | { 112 | r = other.r; 113 | g = other.g; 114 | b = other.b; 115 | return *this; 116 | } 117 | 118 | inline const uint8_t &operator[] (uint32_t index) const { assert(index < 4); return m_comps[index]; } 119 | inline uint8_t &operator[] (uint32_t index) { assert(index < 4); return m_comps[index]; } 120 | 121 | inline void clear() 122 | { 123 | m_comps[0] = 0; 124 | m_comps[1] = 0; 125 | m_comps[2] = 0; 126 | m_comps[3] = 0; 127 | } 128 | 129 | inline bool operator== (const color_rgba &rhs) const 130 | { 131 | if (m_comps[0] != rhs.m_comps[0]) return false; 132 | if (m_comps[1] != rhs.m_comps[1]) return false; 133 | if (m_comps[2] != rhs.m_comps[2]) return false; 134 | if (m_comps[3] != rhs.m_comps[3]) return false; 135 | return true; 136 | } 137 | 138 | inline bool operator!= (const color_rgba &rhs) const 139 | { 140 | return !(*this == rhs); 141 | } 142 | 143 | inline bool operator<(const color_rgba &rhs) const 144 | { 145 | for (int i = 0; i < 4; i++) 146 | { 147 | if (m_comps[i] < rhs.m_comps[i]) 148 | return true; 149 | else if (m_comps[i] != rhs.m_comps[i]) 150 | return false; 151 | } 152 | return false; 153 | } 154 | 155 | inline int get_601_luma() const { return (19595U * m_comps[0] + 38470U * m_comps[1] + 7471U * m_comps[2] + 32768U) >> 16U; } 156 | inline int get_709_luma() const { return (13938U * m_comps[0] + 46869U * m_comps[1] + 4729U * m_comps[2] + 32768U) >> 16U; } 157 | inline int get_luma(bool luma_601) const { return luma_601 ? get_601_luma() : get_709_luma(); } 158 | 159 | static color_rgba comp_min(const color_rgba& a, const color_rgba& b) { return color_rgba(std::min(a[0], b[0]), std::min(a[1], b[1]), std::min(a[2], b[2]), std::min(a[3], b[3])); } 160 | static color_rgba comp_max(const color_rgba& a, const color_rgba& b) { return color_rgba(std::max(a[0], b[0]), std::max(a[1], b[1]), std::max(a[2], b[2]), std::max(a[3], b[3])); } 161 | }; 162 | 163 | bool unpack_bc7(const void *pBlock, color_rgba *pPixels); 164 | 165 | } // namespace bc7decomp 166 | -------------------------------------------------------------------------------- /ThirdParty/bc7enc/bc7enc.h: -------------------------------------------------------------------------------- 1 | // File: bc7enc.h - Richard Geldreich, Jr. - MIT license or public domain (see end of bc7enc.c) 2 | #include 3 | #include 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | #define BC7ENC_BLOCK_SIZE (16) 10 | #define BC7ENC_MAX_PARTITIONS1 (64) 11 | #define BC7ENC_MAX_UBER_LEVEL (4) 12 | 13 | typedef uint8_t bc7enc_bool; 14 | #define BC7ENC_TRUE (1) 15 | #define BC7ENC_FALSE (0) 16 | 17 | typedef struct 18 | { 19 | // m_max_partitions_mode may range from 0 (disables mode 1) to BC7ENC_MAX_PARTITIONS1. The higher this value, the slower the compressor, but the higher the quality. 20 | uint32_t m_max_partitions_mode; 21 | 22 | // Relative RGBA or YCbCrA weights. 23 | uint32_t m_weights[4]; 24 | 25 | // m_uber_level may range from 0 to BC7ENC_MAX_UBER_LEVEL. The higher this value, the slower the compressor, but the higher the quality. 26 | uint32_t m_uber_level; 27 | 28 | // If m_perceptual is true, colorspace error is computed in YCbCr space, otherwise RGB. 29 | bc7enc_bool m_perceptual; 30 | 31 | // Set m_try_least_squares to false for slightly faster/lower quality compression. 32 | bc7enc_bool m_try_least_squares; 33 | 34 | // When m_mode_partition_estimation_filterbank, the mode1 partition estimator skips lesser used partition patterns unless they are strongly predicted to be potentially useful. 35 | // There's a slight loss in quality with this enabled (around .08 dB RGB PSNR or .05 dB Y PSNR), but up to a 11% gain in speed depending on the other settings. 36 | bc7enc_bool m_mode_partition_estimation_filterbank; 37 | 38 | bc7enc_bool m_use_mode5_for_alpha; 39 | bc7enc_bool m_use_mode7_for_alpha; 40 | 41 | } bc7enc_compress_block_params; 42 | 43 | inline void bc7enc_compress_block_params_init_linear_weights(bc7enc_compress_block_params *p) 44 | { 45 | p->m_perceptual = BC7ENC_FALSE; 46 | p->m_weights[0] = 1; 47 | p->m_weights[1] = 1; 48 | p->m_weights[2] = 1; 49 | p->m_weights[3] = 1; 50 | } 51 | 52 | inline void bc7enc_compress_block_params_init_perceptual_weights(bc7enc_compress_block_params *p) 53 | { 54 | p->m_perceptual = BC7ENC_TRUE; 55 | p->m_weights[0] = 128; 56 | p->m_weights[1] = 64; 57 | p->m_weights[2] = 16; 58 | p->m_weights[3] = 32; 59 | } 60 | 61 | inline void bc7enc_compress_block_params_init(bc7enc_compress_block_params *p) 62 | { 63 | p->m_max_partitions_mode = BC7ENC_MAX_PARTITIONS1; 64 | p->m_try_least_squares = BC7ENC_TRUE; 65 | p->m_mode_partition_estimation_filterbank = BC7ENC_TRUE; 66 | p->m_uber_level = 0; 67 | p->m_use_mode5_for_alpha = BC7ENC_TRUE; 68 | p->m_use_mode7_for_alpha = BC7ENC_TRUE; 69 | bc7enc_compress_block_params_init_perceptual_weights(p); 70 | } 71 | 72 | // bc7enc_compress_block_init() MUST be called before calling bc7enc_compress_block() (or you'll get artifacts). 73 | void bc7enc_compress_block_init(); 74 | 75 | // Packs a single block of 16x16 RGBA pixels (R first in memory) to 128-bit BC7 block pBlock, using either mode 1 and/or 6. 76 | // Alpha blocks will always use mode 6, and by default opaque blocks will use either modes 1 or 6. 77 | // Returns BC7ENC_TRUE if the block had any pixels with alpha < 255, otherwise it return BC7ENC_FALSE. (This is not an error code - a block is always encoded.) 78 | bc7enc_bool bc7enc_compress_block(void *pBlock, const void *pPixelsRGBA, const bc7enc_compress_block_params *pComp_params); 79 | 80 | #ifdef __cplusplus 81 | } 82 | #endif 83 | -------------------------------------------------------------------------------- /ThirdParty/bc7enc/dds_defs.h: -------------------------------------------------------------------------------- 1 | // File: dds_defs.h 2 | // DX9/10 .DDS file header definitions. 3 | #pragma once 4 | 5 | #define PIXEL_FMT_FOURCC(a, b, c, d) ((a) | ((b) << 8U) | ((c) << 16U) | ((d) << 24U)) 6 | 7 | enum pixel_format 8 | { 9 | PIXEL_FMT_INVALID = 0, 10 | 11 | PIXEL_FMT_DXT1 = PIXEL_FMT_FOURCC('D', 'X', 'T', '1'), 12 | PIXEL_FMT_DXT2 = PIXEL_FMT_FOURCC('D', 'X', 'T', '2'), 13 | PIXEL_FMT_DXT3 = PIXEL_FMT_FOURCC('D', 'X', 'T', '3'), 14 | PIXEL_FMT_DXT4 = PIXEL_FMT_FOURCC('D', 'X', 'T', '4'), 15 | PIXEL_FMT_DXT5 = PIXEL_FMT_FOURCC('D', 'X', 'T', '5'), 16 | PIXEL_FMT_3DC = PIXEL_FMT_FOURCC('A', 'T', 'I', '2'), // DXN_YX 17 | PIXEL_FMT_DXN = PIXEL_FMT_FOURCC('A', '2', 'X', 'Y'), // DXN_XY 18 | PIXEL_FMT_DXT5A = PIXEL_FMT_FOURCC('A', 'T', 'I', '1'), // ATI1N, http://developer.amd.com/media/gpu_assets/Radeon_X1x00_Programming_Guide.pdf 19 | 20 | // Non-standard formats (some of these are supported by ATI's Compressonator) 21 | PIXEL_FMT_DXT5_CCxY = PIXEL_FMT_FOURCC('C', 'C', 'x', 'Y'), 22 | PIXEL_FMT_DXT5_xGxR = PIXEL_FMT_FOURCC('x', 'G', 'x', 'R'), 23 | PIXEL_FMT_DXT5_xGBR = PIXEL_FMT_FOURCC('x', 'G', 'B', 'R'), 24 | PIXEL_FMT_DXT5_AGBR = PIXEL_FMT_FOURCC('A', 'G', 'B', 'R'), 25 | 26 | PIXEL_FMT_DXT1A = PIXEL_FMT_FOURCC('D', 'X', '1', 'A'), 27 | PIXEL_FMT_ETC1 = PIXEL_FMT_FOURCC('E', 'T', 'C', '1'), 28 | 29 | PIXEL_FMT_R8G8B8 = PIXEL_FMT_FOURCC('R', 'G', 'B', 'x'), 30 | PIXEL_FMT_L8 = PIXEL_FMT_FOURCC('L', 'x', 'x', 'x'), 31 | PIXEL_FMT_A8 = PIXEL_FMT_FOURCC('x', 'x', 'x', 'A'), 32 | PIXEL_FMT_A8L8 = PIXEL_FMT_FOURCC('L', 'x', 'x', 'A'), 33 | PIXEL_FMT_A8R8G8B8 = PIXEL_FMT_FOURCC('R', 'G', 'B', 'A') 34 | }; 35 | 36 | const uint32_t cDDSMaxImageDimensions = 8192U; 37 | 38 | // Total size of header is sizeof(uint32)+cDDSSizeofDDSurfaceDesc2; 39 | const uint32_t cDDSSizeofDDSurfaceDesc2 = 124; 40 | 41 | // "DDS " 42 | const uint32_t cDDSFileSignature = 0x20534444; 43 | 44 | struct DDCOLORKEY 45 | { 46 | uint32_t dwUnused0; 47 | uint32_t dwUnused1; 48 | }; 49 | 50 | struct DDPIXELFORMAT 51 | { 52 | uint32_t dwSize; 53 | uint32_t dwFlags; 54 | uint32_t dwFourCC; 55 | uint32_t dwRGBBitCount; // ATI compressonator will place a FOURCC code here for swizzled/cooked DXTn formats 56 | uint32_t dwRBitMask; 57 | uint32_t dwGBitMask; 58 | uint32_t dwBBitMask; 59 | uint32_t dwRGBAlphaBitMask; 60 | }; 61 | 62 | struct DDSCAPS2 63 | { 64 | uint32_t dwCaps; 65 | uint32_t dwCaps2; 66 | uint32_t dwCaps3; 67 | uint32_t dwCaps4; 68 | }; 69 | 70 | struct DDSURFACEDESC2 71 | { 72 | uint32_t dwSize; 73 | uint32_t dwFlags; 74 | uint32_t dwHeight; 75 | uint32_t dwWidth; 76 | union 77 | { 78 | int32_t lPitch; 79 | uint32_t dwLinearSize; 80 | }; 81 | uint32_t dwBackBufferCount; 82 | uint32_t dwMipMapCount; 83 | uint32_t dwAlphaBitDepth; 84 | uint32_t dwUnused0; 85 | uint32_t lpSurface; 86 | DDCOLORKEY unused0; 87 | DDCOLORKEY unused1; 88 | DDCOLORKEY unused2; 89 | DDCOLORKEY unused3; 90 | DDPIXELFORMAT ddpfPixelFormat; 91 | DDSCAPS2 ddsCaps; 92 | uint32_t dwUnused1; 93 | }; 94 | 95 | const uint32_t DDSD_CAPS = 0x00000001; 96 | const uint32_t DDSD_HEIGHT = 0x00000002; 97 | const uint32_t DDSD_WIDTH = 0x00000004; 98 | const uint32_t DDSD_PITCH = 0x00000008; 99 | 100 | const uint32_t DDSD_BACKBUFFERCOUNT = 0x00000020; 101 | const uint32_t DDSD_ZBUFFERBITDEPTH = 0x00000040; 102 | const uint32_t DDSD_ALPHABITDEPTH = 0x00000080; 103 | 104 | const uint32_t DDSD_LPSURFACE = 0x00000800; 105 | 106 | const uint32_t DDSD_PIXELFORMAT = 0x00001000; 107 | const uint32_t DDSD_CKDESTOVERLAY = 0x00002000; 108 | const uint32_t DDSD_CKDESTBLT = 0x00004000; 109 | const uint32_t DDSD_CKSRCOVERLAY = 0x00008000; 110 | 111 | const uint32_t DDSD_CKSRCBLT = 0x00010000; 112 | const uint32_t DDSD_MIPMAPCOUNT = 0x00020000; 113 | const uint32_t DDSD_REFRESHRATE = 0x00040000; 114 | const uint32_t DDSD_LINEARSIZE = 0x00080000; 115 | 116 | const uint32_t DDSD_TEXTURESTAGE = 0x00100000; 117 | const uint32_t DDSD_FVF = 0x00200000; 118 | const uint32_t DDSD_SRCVBHANDLE = 0x00400000; 119 | const uint32_t DDSD_DEPTH = 0x00800000; 120 | 121 | const uint32_t DDSD_ALL = 0x00fff9ee; 122 | 123 | const uint32_t DDPF_ALPHAPIXELS = 0x00000001; 124 | const uint32_t DDPF_ALPHA = 0x00000002; 125 | const uint32_t DDPF_FOURCC = 0x00000004; 126 | const uint32_t DDPF_PALETTEINDEXED8 = 0x00000020; 127 | const uint32_t DDPF_RGB = 0x00000040; 128 | const uint32_t DDPF_LUMINANCE = 0x00020000; 129 | 130 | const uint32_t DDSCAPS_COMPLEX = 0x00000008; 131 | const uint32_t DDSCAPS_TEXTURE = 0x00001000; 132 | const uint32_t DDSCAPS_MIPMAP = 0x00400000; 133 | 134 | const uint32_t DDSCAPS2_CUBEMAP = 0x00000200; 135 | const uint32_t DDSCAPS2_CUBEMAP_POSITIVEX = 0x00000400; 136 | const uint32_t DDSCAPS2_CUBEMAP_NEGATIVEX = 0x00000800; 137 | 138 | const uint32_t DDSCAPS2_CUBEMAP_POSITIVEY = 0x00001000; 139 | const uint32_t DDSCAPS2_CUBEMAP_NEGATIVEY = 0x00002000; 140 | const uint32_t DDSCAPS2_CUBEMAP_POSITIVEZ = 0x00004000; 141 | const uint32_t DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x00008000; 142 | 143 | const uint32_t DDSCAPS2_VOLUME = 0x00200000; 144 | 145 | typedef enum DXGI_FORMAT 146 | { 147 | DXGI_FORMAT_UNKNOWN = 0, 148 | DXGI_FORMAT_R32G32B32A32_TYPELESS = 1, 149 | DXGI_FORMAT_R32G32B32A32_FLOAT = 2, 150 | DXGI_FORMAT_R32G32B32A32_UINT = 3, 151 | DXGI_FORMAT_R32G32B32A32_SINT = 4, 152 | DXGI_FORMAT_R32G32B32_TYPELESS = 5, 153 | DXGI_FORMAT_R32G32B32_FLOAT = 6, 154 | DXGI_FORMAT_R32G32B32_UINT = 7, 155 | DXGI_FORMAT_R32G32B32_SINT = 8, 156 | DXGI_FORMAT_R16G16B16A16_TYPELESS = 9, 157 | DXGI_FORMAT_R16G16B16A16_FLOAT = 10, 158 | DXGI_FORMAT_R16G16B16A16_UNORM = 11, 159 | DXGI_FORMAT_R16G16B16A16_UINT = 12, 160 | DXGI_FORMAT_R16G16B16A16_SNORM = 13, 161 | DXGI_FORMAT_R16G16B16A16_SINT = 14, 162 | DXGI_FORMAT_R32G32_TYPELESS = 15, 163 | DXGI_FORMAT_R32G32_FLOAT = 16, 164 | DXGI_FORMAT_R32G32_UINT = 17, 165 | DXGI_FORMAT_R32G32_SINT = 18, 166 | DXGI_FORMAT_R32G8X24_TYPELESS = 19, 167 | DXGI_FORMAT_D32_FLOAT_S8X24_UINT = 20, 168 | DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS = 21, 169 | DXGI_FORMAT_X32_TYPELESS_G8X24_UINT = 22, 170 | DXGI_FORMAT_R10G10B10A2_TYPELESS = 23, 171 | DXGI_FORMAT_R10G10B10A2_UNORM = 24, 172 | DXGI_FORMAT_R10G10B10A2_UINT = 25, 173 | DXGI_FORMAT_R11G11B10_FLOAT = 26, 174 | DXGI_FORMAT_R8G8B8A8_TYPELESS = 27, 175 | DXGI_FORMAT_R8G8B8A8_UNORM = 28, 176 | DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29, 177 | DXGI_FORMAT_R8G8B8A8_UINT = 30, 178 | DXGI_FORMAT_R8G8B8A8_SNORM = 31, 179 | DXGI_FORMAT_R8G8B8A8_SINT = 32, 180 | DXGI_FORMAT_R16G16_TYPELESS = 33, 181 | DXGI_FORMAT_R16G16_FLOAT = 34, 182 | DXGI_FORMAT_R16G16_UNORM = 35, 183 | DXGI_FORMAT_R16G16_UINT = 36, 184 | DXGI_FORMAT_R16G16_SNORM = 37, 185 | DXGI_FORMAT_R16G16_SINT = 38, 186 | DXGI_FORMAT_R32_TYPELESS = 39, 187 | DXGI_FORMAT_D32_FLOAT = 40, 188 | DXGI_FORMAT_R32_FLOAT = 41, 189 | DXGI_FORMAT_R32_UINT = 42, 190 | DXGI_FORMAT_R32_SINT = 43, 191 | DXGI_FORMAT_R24G8_TYPELESS = 44, 192 | DXGI_FORMAT_D24_UNORM_S8_UINT = 45, 193 | DXGI_FORMAT_R24_UNORM_X8_TYPELESS = 46, 194 | DXGI_FORMAT_X24_TYPELESS_G8_UINT = 47, 195 | DXGI_FORMAT_R8G8_TYPELESS = 48, 196 | DXGI_FORMAT_R8G8_UNORM = 49, 197 | DXGI_FORMAT_R8G8_UINT = 50, 198 | DXGI_FORMAT_R8G8_SNORM = 51, 199 | DXGI_FORMAT_R8G8_SINT = 52, 200 | DXGI_FORMAT_R16_TYPELESS = 53, 201 | DXGI_FORMAT_R16_FLOAT = 54, 202 | DXGI_FORMAT_D16_UNORM = 55, 203 | DXGI_FORMAT_R16_UNORM = 56, 204 | DXGI_FORMAT_R16_UINT = 57, 205 | DXGI_FORMAT_R16_SNORM = 58, 206 | DXGI_FORMAT_R16_SINT = 59, 207 | DXGI_FORMAT_R8_TYPELESS = 60, 208 | DXGI_FORMAT_R8_UNORM = 61, 209 | DXGI_FORMAT_R8_UINT = 62, 210 | DXGI_FORMAT_R8_SNORM = 63, 211 | DXGI_FORMAT_R8_SINT = 64, 212 | DXGI_FORMAT_A8_UNORM = 65, 213 | DXGI_FORMAT_R1_UNORM = 66, 214 | DXGI_FORMAT_R9G9B9E5_SHAREDEXP = 67, 215 | DXGI_FORMAT_R8G8_B8G8_UNORM = 68, 216 | DXGI_FORMAT_G8R8_G8B8_UNORM = 69, 217 | DXGI_FORMAT_BC1_TYPELESS = 70, 218 | DXGI_FORMAT_BC1_UNORM = 71, 219 | DXGI_FORMAT_BC1_UNORM_SRGB = 72, 220 | DXGI_FORMAT_BC2_TYPELESS = 73, 221 | DXGI_FORMAT_BC2_UNORM = 74, 222 | DXGI_FORMAT_BC2_UNORM_SRGB = 75, 223 | DXGI_FORMAT_BC3_TYPELESS = 76, 224 | DXGI_FORMAT_BC3_UNORM = 77, 225 | DXGI_FORMAT_BC3_UNORM_SRGB = 78, 226 | DXGI_FORMAT_BC4_TYPELESS = 79, 227 | DXGI_FORMAT_BC4_UNORM = 80, 228 | DXGI_FORMAT_BC4_SNORM = 81, 229 | DXGI_FORMAT_BC5_TYPELESS = 82, 230 | DXGI_FORMAT_BC5_UNORM = 83, 231 | DXGI_FORMAT_BC5_SNORM = 84, 232 | DXGI_FORMAT_B5G6R5_UNORM = 85, 233 | DXGI_FORMAT_B5G5R5A1_UNORM = 86, 234 | DXGI_FORMAT_B8G8R8A8_UNORM = 87, 235 | DXGI_FORMAT_B8G8R8X8_UNORM = 88, 236 | DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM = 89, 237 | DXGI_FORMAT_B8G8R8A8_TYPELESS = 90, 238 | DXGI_FORMAT_B8G8R8A8_UNORM_SRGB = 91, 239 | DXGI_FORMAT_B8G8R8X8_TYPELESS = 92, 240 | DXGI_FORMAT_B8G8R8X8_UNORM_SRGB = 93, 241 | DXGI_FORMAT_BC6H_TYPELESS = 94, 242 | DXGI_FORMAT_BC6H_UF16 = 95, 243 | DXGI_FORMAT_BC6H_SF16 = 96, 244 | DXGI_FORMAT_BC7_TYPELESS = 97, 245 | DXGI_FORMAT_BC7_UNORM = 98, 246 | DXGI_FORMAT_BC7_UNORM_SRGB = 99, 247 | DXGI_FORMAT_AYUV = 100, 248 | DXGI_FORMAT_Y410 = 101, 249 | DXGI_FORMAT_Y416 = 102, 250 | DXGI_FORMAT_NV12 = 103, 251 | DXGI_FORMAT_P010 = 104, 252 | DXGI_FORMAT_P016 = 105, 253 | DXGI_FORMAT_420_OPAQUE = 106, 254 | DXGI_FORMAT_YUY2 = 107, 255 | DXGI_FORMAT_Y210 = 108, 256 | DXGI_FORMAT_Y216 = 109, 257 | DXGI_FORMAT_NV11 = 110, 258 | DXGI_FORMAT_AI44 = 111, 259 | DXGI_FORMAT_IA44 = 112, 260 | DXGI_FORMAT_P8 = 113, 261 | DXGI_FORMAT_A8P8 = 114, 262 | DXGI_FORMAT_B4G4R4A4_UNORM = 115, 263 | DXGI_FORMAT_P208 = 130, 264 | DXGI_FORMAT_V208 = 131, 265 | DXGI_FORMAT_V408 = 132, 266 | DXGI_FORMAT_FORCE_UINT = 0xffffffff 267 | } DXGI_FORMAT; 268 | 269 | enum D3D10_RESOURCE_DIMENSION 270 | { 271 | D3D10_RESOURCE_DIMENSION_UNKNOWN = 0, 272 | D3D10_RESOURCE_DIMENSION_BUFFER = 1, 273 | D3D10_RESOURCE_DIMENSION_TEXTURE1D = 2, 274 | D3D10_RESOURCE_DIMENSION_TEXTURE2D = 3, 275 | D3D10_RESOURCE_DIMENSION_TEXTURE3D = 4 276 | }; 277 | 278 | struct DDS_HEADER_DXT10 279 | { 280 | DXGI_FORMAT dxgiFormat; 281 | D3D10_RESOURCE_DIMENSION resourceDimension; 282 | uint32_t miscFlag; 283 | uint32_t arraySize; 284 | uint32_t miscFlags2; 285 | }; 286 | 287 | -------------------------------------------------------------------------------- /ThirdParty/bc7enc/test.cpp: -------------------------------------------------------------------------------- 1 | // test.cpp - bc7enc17.c command line example/test app 2 | #ifdef _MSC_VER 3 | #define _CRT_SECURE_NO_WARNINGS 4 | #endif 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "bc7enc.h" 15 | #include "lodepng.h" 16 | #include "dds_defs.h" 17 | #include "bc7decomp.h" 18 | 19 | #define RGBCX_IMPLEMENTATION 20 | #include "rgbcx.h" 21 | 22 | const int MAX_UBER_LEVEL = 5; 23 | 24 | inline int iabs(int i) { if (i < 0) i = -i; return i; } 25 | inline uint8_t clamp255(int32_t i) { return (uint8_t)((i & 0xFFFFFF00U) ? (~(i >> 31)) : i); } 26 | template inline S clamp(S value, S low, S high) { return (value < low) ? low : ((value > high) ? high : value); } 27 | 28 | static int print_usage() 29 | { 30 | fprintf(stderr, "bc7enc\n"); 31 | fprintf(stderr, "Reads PNG files (with or without alpha channels) and packs them to BC1-5 or BC7/BPTC using\nmodes 1, 6 (opaque blocks) or modes 1, 5, 6, and 7 (alpha blocks).\n"); 32 | 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"); 33 | fprintf(stderr, "Usage: bc7enc [-apng_filename] [options] input_filename.png [compressed_output.dds] [unpacked_output.png]\n\n"); 34 | fprintf(stderr, "-apng_filename Load G channel of PNG file into alpha channel of source image\n"); 35 | fprintf(stderr, "-g Don't write unpacked output PNG files (this disables PSNR metrics too).\n"); 36 | fprintf(stderr, "-y Flip source image along Y axis before packing\n"); 37 | fprintf(stderr, "-o Write output files in same directory as source files\n"); 38 | fprintf(stderr, "-1 Encode to BC1. -u[0,5] controls quality vs. perf. tradeoff for RGB.\n"); 39 | fprintf(stderr, "-3 Encode to BC3. -u[0,5] controls quality vs. perf. tradeoff for RGB.\n"); 40 | fprintf(stderr, "-4 Encode to BC4\n"); 41 | fprintf(stderr, "-5 Encode to BC5\n"); 42 | fprintf(stderr, "\n"); 43 | fprintf(stderr, "-X# BC4/5: Set first color channel (defaults to 0 or red)\n"); 44 | fprintf(stderr, "-Y# BC4/5: Set second color channel (defaults to 1 or green)\n"); 45 | fprintf(stderr, "\n"); 46 | fprintf(stderr, "-l BC7: Use linear colorspace metrics instead of perceptual\n"); 47 | fprintf(stderr, "-uX BC7: Higher quality levels, X ranges from [0,4]\n"); 48 | fprintf(stderr, "-pX BC7: Scan X partitions in mode 1, X ranges from [0,64], use 0 to disable mode 1 entirely (faster)\n"); 49 | fprintf(stderr, "\n"); 50 | 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"); 51 | fprintf(stderr, "-c BC1: Disable 3-color mode for solid color blocks\n"); 52 | fprintf(stderr, "-n BC1: Enable balancing decoding error between ideal and NVidia hardware BC1 decoding (textures still usable on parts from other vendors)\n"); 53 | fprintf(stderr, "-LX BC1: Set encoding level, where 0=fastest and 10=slowest but highest quality\n"); 54 | 55 | return EXIT_FAILURE; 56 | } 57 | 58 | struct color_quad_u8 59 | { 60 | uint8_t m_c[4]; 61 | 62 | inline color_quad_u8(uint8_t r, uint8_t g, uint8_t b, uint8_t a) 63 | { 64 | set(r, g, b, a); 65 | } 66 | 67 | inline color_quad_u8(uint8_t y = 0, uint8_t a = 255) 68 | { 69 | set(y, a); 70 | } 71 | 72 | inline color_quad_u8 &set(uint8_t y, uint8_t a = 255) 73 | { 74 | m_c[0] = y; 75 | m_c[1] = y; 76 | m_c[2] = y; 77 | m_c[3] = a; 78 | return *this; 79 | } 80 | 81 | inline color_quad_u8 &set(uint8_t r, uint8_t g, uint8_t b, uint8_t a) 82 | { 83 | m_c[0] = r; 84 | m_c[1] = g; 85 | m_c[2] = b; 86 | m_c[3] = a; 87 | return *this; 88 | } 89 | 90 | inline uint8_t &operator[] (uint32_t i) { assert(i < 4); return m_c[i]; } 91 | inline uint8_t operator[] (uint32_t i) const { assert(i < 4); return m_c[i]; } 92 | 93 | inline int get_luma() const { return (13938U * m_c[0] + 46869U * m_c[1] + 4729U * m_c[2] + 32768U) >> 16U; } // REC709 weightings 94 | }; 95 | typedef std::vector color_quad_u8_vec; 96 | 97 | class image_u8 98 | { 99 | public: 100 | image_u8() : 101 | m_width(0), m_height(0) 102 | { 103 | } 104 | 105 | image_u8(uint32_t width, uint32_t height) : 106 | m_width(width), m_height(height) 107 | { 108 | m_pixels.resize(width * height); 109 | } 110 | 111 | inline const color_quad_u8_vec &get_pixels() const { return m_pixels; } 112 | inline color_quad_u8_vec &get_pixels() { return m_pixels; } 113 | 114 | inline uint32_t width() const { return m_width; } 115 | inline uint32_t height() const { return m_height; } 116 | inline uint32_t total_pixels() const { return m_width * m_height; } 117 | 118 | 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]; } 119 | 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]; } 120 | 121 | image_u8& clear() 122 | { 123 | m_width = m_height = 0; 124 | m_pixels.clear(); 125 | return *this; 126 | } 127 | 128 | image_u8& init(uint32_t width, uint32_t height) 129 | { 130 | clear(); 131 | 132 | m_width = width; 133 | m_height = height; 134 | m_pixels.resize(width * height); 135 | return *this; 136 | } 137 | 138 | image_u8& set_all(const color_quad_u8 &p) 139 | { 140 | for (uint32_t i = 0; i < m_pixels.size(); i++) 141 | m_pixels[i] = p; 142 | return *this; 143 | } 144 | 145 | image_u8& crop(uint32_t new_width, uint32_t new_height) 146 | { 147 | if ((m_width == new_width) && (m_height == new_height)) 148 | return *this; 149 | 150 | image_u8 new_image(new_width, new_height); 151 | 152 | const uint32_t w = std::min(m_width, new_width); 153 | const uint32_t h = std::min(m_height, new_height); 154 | 155 | for (uint32_t y = 0; y < h; y++) 156 | for (uint32_t x = 0; x < w; x++) 157 | new_image(x, y) = (*this)(x, y); 158 | 159 | return swap(new_image); 160 | } 161 | 162 | image_u8 &swap(image_u8 &other) 163 | { 164 | std::swap(m_width, other.m_width); 165 | std::swap(m_height, other.m_height); 166 | std::swap(m_pixels, other.m_pixels); 167 | return *this; 168 | } 169 | 170 | inline void get_block(uint32_t bx, uint32_t by, uint32_t width, uint32_t height, color_quad_u8 *pPixels) 171 | { 172 | assert((bx * width + width) <= m_width); 173 | assert((by * height + height) <= m_height); 174 | 175 | for (uint32_t y = 0; y < height; y++) 176 | memcpy(pPixels + y * width, &(*this)(bx * width, by * height + y), width * sizeof(color_quad_u8)); 177 | } 178 | 179 | inline void set_block(uint32_t bx, uint32_t by, uint32_t width, uint32_t height, const color_quad_u8 *pPixels) 180 | { 181 | assert((bx * width + width) <= m_width); 182 | assert((by * height + height) <= m_height); 183 | 184 | for (uint32_t y = 0; y < height; y++) 185 | memcpy(&(*this)(bx * width, by * height + y), pPixels + y * width, width * sizeof(color_quad_u8)); 186 | } 187 | 188 | image_u8 &swizzle(uint32_t r, uint32_t g, uint32_t b, uint32_t a) 189 | { 190 | assert((r | g | b | a) <= 3); 191 | for (uint32_t y = 0; y < m_height; y++) 192 | { 193 | for (uint32_t x = 0; x < m_width; x++) 194 | { 195 | color_quad_u8 tmp((*this)(x, y)); 196 | (*this)(x, y).set(tmp[r], tmp[g], tmp[b], tmp[a]); 197 | } 198 | } 199 | 200 | return *this; 201 | } 202 | 203 | private: 204 | color_quad_u8_vec m_pixels; 205 | uint32_t m_width, m_height; 206 | }; 207 | 208 | static bool load_png(const char *pFilename, image_u8 &img) 209 | { 210 | img.clear(); 211 | 212 | std::vector pixels; 213 | unsigned int w = 0, h = 0; 214 | unsigned int e = lodepng::decode(pixels, w, h, pFilename); 215 | if (e != 0) 216 | { 217 | fprintf(stderr, "Failed loading PNG file %s\n", pFilename); 218 | return false; 219 | } 220 | 221 | img.init(w, h); 222 | memcpy(&img.get_pixels()[0], &pixels[0], w * h * sizeof(uint32_t)); 223 | 224 | return true; 225 | } 226 | 227 | static bool save_png(const char *pFilename, const image_u8 &img, bool save_alpha) 228 | { 229 | const uint32_t w = img.width(); 230 | const uint32_t h = img.height(); 231 | 232 | std::vector pixels; 233 | if (save_alpha) 234 | { 235 | pixels.resize(w * h * sizeof(color_quad_u8)); 236 | memcpy(&pixels[0], &img.get_pixels()[0], w * h * sizeof(color_quad_u8)); 237 | } 238 | else 239 | { 240 | pixels.resize(w * h * 3); 241 | unsigned char *pDst = &pixels[0]; 242 | for (uint32_t y = 0; y < h; y++) 243 | for (uint32_t x = 0; x < w; x++, pDst += 3) 244 | pDst[0] = img(x, y)[0], pDst[1] = img(x, y)[1], pDst[2] = img(x, y)[2]; 245 | } 246 | 247 | return lodepng::encode(pFilename, pixels, w, h, save_alpha ? LCT_RGBA : LCT_RGB) == 0; 248 | } 249 | 250 | class image_metrics 251 | { 252 | public: 253 | double m_max, m_mean, m_mean_squared, m_root_mean_squared, m_peak_snr; 254 | 255 | image_metrics() 256 | { 257 | clear(); 258 | } 259 | 260 | void clear() 261 | { 262 | memset(this, 0, sizeof(*this)); 263 | } 264 | 265 | void compute(const image_u8 &a, const image_u8 &b, uint32_t first_channel, uint32_t num_channels) 266 | { 267 | const bool average_component_error = true; 268 | 269 | const uint32_t width = std::min(a.width(), b.width()); 270 | const uint32_t height = std::min(a.height(), b.height()); 271 | 272 | assert((first_channel < 4U) && (first_channel + num_channels <= 4U)); 273 | 274 | // Histogram approach originally due to Charles Bloom. 275 | double hist[256]; 276 | memset(hist, 0, sizeof(hist)); 277 | 278 | for (uint32_t y = 0; y < height; y++) 279 | { 280 | for (uint32_t x = 0; x < width; x++) 281 | { 282 | const color_quad_u8 &ca = a(x, y); 283 | const color_quad_u8 &cb = b(x, y); 284 | 285 | if (!num_channels) 286 | hist[iabs(ca.get_luma() - cb.get_luma())]++; 287 | else 288 | { 289 | for (uint32_t c = 0; c < num_channels; c++) 290 | hist[iabs(ca[first_channel + c] - cb[first_channel + c])]++; 291 | } 292 | } 293 | } 294 | 295 | m_max = 0; 296 | double sum = 0.0f, sum2 = 0.0f; 297 | for (uint32_t i = 0; i < 256; i++) 298 | { 299 | if (!hist[i]) 300 | continue; 301 | 302 | m_max = std::max(m_max, i); 303 | 304 | double x = i * hist[i]; 305 | 306 | sum += x; 307 | sum2 += i * x; 308 | } 309 | 310 | // See http://richg42.blogspot.com/2016/09/how-to-compute-psnr-from-old-berkeley.html 311 | double total_values = width * height; 312 | 313 | if (average_component_error) 314 | total_values *= clamp(num_channels, 1, 4); 315 | 316 | m_mean = clamp(sum / total_values, 0.0f, 255.0f); 317 | m_mean_squared = clamp(sum2 / total_values, 0.0f, 255.0f * 255.0f); 318 | 319 | m_root_mean_squared = sqrt(m_mean_squared); 320 | 321 | if (!m_root_mean_squared) 322 | m_peak_snr = 100.0f; 323 | else 324 | m_peak_snr = clamp(log10(255.0f / m_root_mean_squared) * 20.0f, 0.0f, 100.0f); 325 | } 326 | }; 327 | 328 | struct block8 329 | { 330 | uint64_t m_vals[1]; 331 | }; 332 | 333 | typedef std::vector block8_vec; 334 | 335 | struct block16 336 | { 337 | uint64_t m_vals[2]; 338 | }; 339 | 340 | typedef std::vector block16_vec; 341 | 342 | 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) 343 | { 344 | (void)srgb; 345 | 346 | FILE *pFile = NULL; 347 | pFile = fopen(pFilename, "wb"); 348 | if (!pFile) 349 | { 350 | fprintf(stderr, "Failed creating file %s!\n", pFilename); 351 | return false; 352 | } 353 | 354 | fwrite("DDS ", 4, 1, pFile); 355 | 356 | DDSURFACEDESC2 desc; 357 | memset(&desc, 0, sizeof(desc)); 358 | 359 | desc.dwSize = sizeof(desc); 360 | desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS; 361 | 362 | desc.dwWidth = width; 363 | desc.dwHeight = height; 364 | 365 | desc.ddsCaps.dwCaps = DDSCAPS_TEXTURE; 366 | desc.ddpfPixelFormat.dwSize = sizeof(desc.ddpfPixelFormat); 367 | 368 | desc.ddpfPixelFormat.dwFlags |= DDPF_FOURCC; 369 | 370 | desc.ddpfPixelFormat.dwFourCC = (uint32_t)PIXEL_FMT_FOURCC('D', 'X', '1', '0'); 371 | desc.ddpfPixelFormat.dwRGBBitCount = 0; 372 | 373 | desc.lPitch = (((desc.dwWidth + 3) & ~3) * ((desc.dwHeight + 3) & ~3) * pixel_format_bpp) >> 3; 374 | desc.dwFlags |= DDSD_LINEARSIZE; 375 | 376 | fwrite(&desc, sizeof(desc), 1, pFile); 377 | 378 | DDS_HEADER_DXT10 hdr10; 379 | memset(&hdr10, 0, sizeof(hdr10)); 380 | 381 | // 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. 382 | // For best compatibility just write DXGI_FORMAT_BC7_UNORM. 383 | //hdr10.dxgiFormat = srgb ? DXGI_FORMAT_BC7_UNORM_SRGB : DXGI_FORMAT_BC7_UNORM; 384 | hdr10.dxgiFormat = dxgi_format; // DXGI_FORMAT_BC7_UNORM; 385 | hdr10.resourceDimension = D3D10_RESOURCE_DIMENSION_TEXTURE2D; 386 | hdr10.arraySize = 1; 387 | 388 | fwrite(&hdr10, sizeof(hdr10), 1, pFile); 389 | 390 | fwrite(pBlocks, desc.lPitch, 1, pFile); 391 | 392 | if (fclose(pFile) == EOF) 393 | { 394 | fprintf(stderr, "Failed writing to DDS file %s!\n", pFilename); 395 | return false; 396 | } 397 | 398 | return true; 399 | } 400 | 401 | static void strip_extension(std::string &s) 402 | { 403 | for (int32_t i = (int32_t)s.size() - 1; i >= 0; i--) 404 | { 405 | if (s[i] == '.') 406 | { 407 | s.resize(i); 408 | break; 409 | } 410 | } 411 | } 412 | 413 | static void strip_path(std::string& s) 414 | { 415 | for (int32_t i = (int32_t)s.size() - 1; i >= 0; i--) 416 | { 417 | if ((s[i] == '/') || (s[i] == ':') || (s[i] == '\\')) 418 | { 419 | s.erase(0, i + 1); 420 | break; 421 | } 422 | } 423 | } 424 | 425 | int main(int argc, char *argv[]) 426 | { 427 | if (argc < 2) 428 | return print_usage(); 429 | 430 | std::string src_filename; 431 | std::string src_alpha_filename; 432 | std::string dds_output_filename; 433 | std::string png_output_filename; 434 | std::string png_alpha_output_filename; 435 | 436 | bool no_output_png = false; 437 | bool out_same_dir = false; 438 | 439 | int uber_level = 0; 440 | int max_partitions_to_scan = BC7ENC_MAX_PARTITIONS1; 441 | bool perceptual = true; 442 | bool y_flip = false; 443 | uint32_t bc45_channel0 = 0; 444 | uint32_t bc45_channel1 = 1; 445 | 446 | bool balance_nv_error = false; 447 | bool use_bc1_3color_mode = true; 448 | bool use_bc1_3color_mode_for_black = false; 449 | int bc1_quality_level = 2; 450 | 451 | DXGI_FORMAT dxgi_format = DXGI_FORMAT_BC7_UNORM; 452 | uint32_t pixel_format_bpp = 8; 453 | 454 | for (int i = 1; i < argc; i++) 455 | { 456 | const char *pArg = argv[i]; 457 | if (pArg[0] == '-') 458 | { 459 | switch (pArg[1]) 460 | { 461 | case '1': 462 | { 463 | dxgi_format = DXGI_FORMAT_BC1_UNORM; 464 | pixel_format_bpp = 4; 465 | printf("Compressing to BC1\n"); 466 | break; 467 | } 468 | case '3': 469 | { 470 | dxgi_format = DXGI_FORMAT_BC3_UNORM; 471 | pixel_format_bpp = 8; 472 | printf("Compressing to BC3\n"); 473 | break; 474 | } 475 | case '4': 476 | { 477 | dxgi_format = DXGI_FORMAT_BC4_UNORM; 478 | pixel_format_bpp = 4; 479 | printf("Compressing to BC4\n"); 480 | break; 481 | } 482 | case '5': 483 | { 484 | dxgi_format = DXGI_FORMAT_BC5_UNORM; 485 | pixel_format_bpp = 8; 486 | printf("Compressing to BC5\n"); 487 | break; 488 | } 489 | case 'y': 490 | { 491 | y_flip = true; 492 | break; 493 | } 494 | case 'a': 495 | { 496 | src_alpha_filename = pArg + 2; 497 | break; 498 | } 499 | case 'X': 500 | { 501 | bc45_channel0 = atoi(pArg + 2); 502 | if ((bc45_channel0 < 0) || (bc45_channel0 > 3)) 503 | { 504 | fprintf(stderr, "Invalid argument: %s\n", pArg); 505 | return EXIT_FAILURE; 506 | } 507 | break; 508 | } 509 | case 'Y': 510 | { 511 | bc45_channel1 = atoi(pArg + 2); 512 | if ((bc45_channel1 < 0) || (bc45_channel1 > 3)) 513 | { 514 | fprintf(stderr, "Invalid argument: %s\n", pArg); 515 | return EXIT_FAILURE; 516 | } 517 | break; 518 | } 519 | case 'u': 520 | { 521 | uber_level = atoi(pArg + 2); 522 | if ((uber_level < 0) || (uber_level > MAX_UBER_LEVEL)) 523 | { 524 | fprintf(stderr, "Invalid argument: %s\n", pArg); 525 | return EXIT_FAILURE; 526 | } 527 | break; 528 | 529 | } 530 | case 'L': 531 | { 532 | bc1_quality_level = atoi(pArg + 2); 533 | if (((int)bc1_quality_level < (int)rgbcx::MIN_LEVEL) || ((int)bc1_quality_level > (int)rgbcx::MAX_LEVEL)) 534 | { 535 | fprintf(stderr, "Invalid argument: %s\n", pArg); 536 | return EXIT_FAILURE; 537 | } 538 | break; 539 | 540 | } 541 | case 'g': 542 | { 543 | no_output_png = true; 544 | break; 545 | } 546 | case 'l': 547 | { 548 | perceptual = false; 549 | break; 550 | } 551 | case 'p': 552 | { 553 | max_partitions_to_scan = atoi(pArg + 2); 554 | if ((max_partitions_to_scan < 0) || (max_partitions_to_scan > BC7ENC_MAX_PARTITIONS1)) 555 | { 556 | fprintf(stderr, "Invalid argument: %s\n", pArg); 557 | return EXIT_FAILURE; 558 | } 559 | break; 560 | } 561 | case 'n': 562 | { 563 | balance_nv_error = true; 564 | break; 565 | } 566 | case 'o': 567 | { 568 | out_same_dir = true; 569 | break; 570 | } 571 | case 'b': 572 | { 573 | use_bc1_3color_mode_for_black = true; 574 | break; 575 | } 576 | case 'c': 577 | { 578 | use_bc1_3color_mode = false; 579 | break; 580 | } 581 | default: 582 | { 583 | fprintf(stderr, "Invalid argument: %s\n", pArg); 584 | return EXIT_FAILURE; 585 | } 586 | } 587 | } 588 | else 589 | { 590 | if (!src_filename.size()) 591 | src_filename = pArg; 592 | else if (!dds_output_filename.size()) 593 | dds_output_filename = pArg; 594 | else if (!png_output_filename.size()) 595 | png_output_filename = pArg; 596 | else 597 | { 598 | fprintf(stderr, "Invalid argument: %s\n", pArg); 599 | return EXIT_FAILURE; 600 | } 601 | } 602 | } 603 | 604 | const uint32_t bytes_per_block = (16 * pixel_format_bpp) / 8; 605 | assert(bytes_per_block == 8 || bytes_per_block == 16); 606 | 607 | if (!src_filename.size()) 608 | { 609 | fprintf(stderr, "No source filename specified!\n"); 610 | return EXIT_FAILURE; 611 | } 612 | 613 | if (!dds_output_filename.size()) 614 | { 615 | dds_output_filename = src_filename; 616 | strip_extension(dds_output_filename); 617 | if (!out_same_dir) 618 | strip_path(dds_output_filename); 619 | dds_output_filename += ".dds"; 620 | } 621 | 622 | if (!png_output_filename.size()) 623 | { 624 | png_output_filename = src_filename; 625 | strip_extension(png_output_filename); 626 | if (!out_same_dir) 627 | strip_path(png_output_filename); 628 | png_output_filename += "_unpacked.png"; 629 | } 630 | 631 | png_alpha_output_filename = png_output_filename; 632 | strip_extension(png_alpha_output_filename); 633 | png_alpha_output_filename += "_unpacked_alpha.png"; 634 | 635 | image_u8 source_image; 636 | if (!load_png(src_filename.c_str(), source_image)) 637 | return EXIT_FAILURE; 638 | 639 | printf("Source image: %s %ux%u\n", src_filename.c_str(), source_image.width(), source_image.height()); 640 | 641 | if (src_alpha_filename.size()) 642 | { 643 | image_u8 source_alpha_image; 644 | if (!load_png(src_alpha_filename.c_str(), source_alpha_image)) 645 | return EXIT_FAILURE; 646 | 647 | printf("Source alpha image: %s %ux%u\n", src_alpha_filename.c_str(), source_alpha_image.width(), source_alpha_image.height()); 648 | 649 | const uint32_t w = std::min(source_alpha_image.width(), source_image.width()); 650 | const uint32_t h = std::min(source_alpha_image.height(), source_image.height()); 651 | 652 | for (uint32_t y = 0; y < h; y++) 653 | for (uint32_t x = 0; x < w; x++) 654 | source_image(x, y)[3] = source_alpha_image(x, y)[1]; 655 | } 656 | 657 | #if 0 658 | // HACK HACK 659 | for (uint32_t y = 0; y < source_image.height(); y++) 660 | for (uint32_t x = 0; x < source_image.width(); x++) 661 | source_image(x, y)[3] = 254; 662 | #endif 663 | 664 | const uint32_t orig_width = source_image.width(); 665 | const uint32_t orig_height = source_image.height(); 666 | 667 | if (y_flip) 668 | { 669 | image_u8 temp; 670 | temp.init(orig_width, orig_height); 671 | 672 | for (uint32_t y = 0; y < orig_height; y++) 673 | for (uint32_t x = 0; x < orig_width; x++) 674 | temp(x, (orig_height - 1) - y) = source_image(x, y); 675 | 676 | temp.swap(source_image); 677 | } 678 | 679 | source_image.crop((source_image.width() + 3) & ~3, (source_image.height() + 3) & ~3); 680 | 681 | const uint32_t blocks_x = source_image.width() / 4; 682 | const uint32_t blocks_y = source_image.height() / 4; 683 | 684 | block16_vec packed_image16(blocks_x * blocks_y); 685 | block8_vec packed_image8(blocks_x * blocks_y); 686 | 687 | bc7enc_compress_block_params pack_params; 688 | bc7enc_compress_block_params_init(&pack_params); 689 | if (!perceptual) 690 | bc7enc_compress_block_params_init_linear_weights(&pack_params); 691 | pack_params.m_max_partitions_mode = max_partitions_to_scan; 692 | pack_params.m_uber_level = std::min(BC7ENC_MAX_UBER_LEVEL, uber_level); 693 | 694 | if (dxgi_format == DXGI_FORMAT_BC7_UNORM) 695 | { 696 | printf("Max mode 1 partitions: %u, uber level: %u, perceptual: %u\n", pack_params.m_max_partitions_mode, pack_params.m_uber_level, perceptual); 697 | } 698 | else 699 | { 700 | printf("Level: %u, use 3-color mode: %u, use 3-color mode for black: %u, balance NV error: %u\n", 701 | bc1_quality_level, use_bc1_3color_mode, use_bc1_3color_mode_for_black, balance_nv_error); 702 | } 703 | 704 | bc7enc_compress_block_init(); 705 | rgbcx::encode_bc1_init(balance_nv_error); 706 | 707 | bool has_alpha = false; 708 | 709 | clock_t start_t = clock(); 710 | uint32_t bc7_mode_hist[8]; 711 | memset(bc7_mode_hist, 0, sizeof(bc7_mode_hist)); 712 | 713 | for (uint32_t by = 0; by < blocks_y; by++) 714 | { 715 | for (uint32_t bx = 0; bx < blocks_x; bx++) 716 | { 717 | color_quad_u8 pixels[16]; 718 | 719 | source_image.get_block(bx, by, 4, 4, pixels); 720 | if (!has_alpha) 721 | { 722 | for (uint32_t i = 0; i < 16; i++) 723 | { 724 | if (pixels[i].m_c[3] < 255) 725 | { 726 | has_alpha = true; 727 | break; 728 | } 729 | } 730 | } 731 | 732 | switch (dxgi_format) 733 | { 734 | case DXGI_FORMAT_BC1_UNORM: 735 | { 736 | block8* pBlock = &packed_image8[bx + by * blocks_x]; 737 | 738 | rgbcx::encode_bc1(bc1_quality_level, pBlock, &pixels[0].m_c[0], use_bc1_3color_mode, use_bc1_3color_mode_for_black); 739 | break; 740 | } 741 | case DXGI_FORMAT_BC3_UNORM: 742 | { 743 | block16* pBlock = &packed_image16[bx + by * blocks_x]; 744 | 745 | rgbcx::encode_bc3(bc1_quality_level, pBlock, &pixels[0].m_c[0]); 746 | break; 747 | } 748 | case DXGI_FORMAT_BC4_UNORM: 749 | { 750 | block8* pBlock = &packed_image8[bx + by * blocks_x]; 751 | 752 | rgbcx::encode_bc4(pBlock, &pixels[0].m_c[bc45_channel0], 4); 753 | break; 754 | } 755 | case DXGI_FORMAT_BC5_UNORM: 756 | { 757 | block16* pBlock = &packed_image16[bx + by * blocks_x]; 758 | 759 | rgbcx::encode_bc5(pBlock, &pixels[0].m_c[0], bc45_channel0, bc45_channel1, 4); 760 | break; 761 | } 762 | case DXGI_FORMAT_BC7_UNORM: 763 | { 764 | block16* pBlock = &packed_image16[bx + by * blocks_x]; 765 | 766 | bc7enc_compress_block(pBlock, pixels, &pack_params); 767 | 768 | uint32_t mode = ((uint8_t *)pBlock)[0]; 769 | for (uint32_t m = 0; m <= 7; m++) 770 | { 771 | if (mode & (1 << m)) 772 | { 773 | bc7_mode_hist[m]++; 774 | break; 775 | } 776 | } 777 | break; 778 | } 779 | default: 780 | { 781 | assert(0); 782 | break; 783 | } 784 | } 785 | } 786 | 787 | if ((by & 127) == 0) 788 | printf("."); 789 | } 790 | 791 | clock_t end_t = clock(); 792 | 793 | printf("\nTotal time: %f secs\n", (double)(end_t - start_t) / CLOCKS_PER_SEC); 794 | 795 | if (dxgi_format == DXGI_FORMAT_BC7_UNORM) 796 | { 797 | printf("BC7 mode histogram:\n"); 798 | for (uint32_t i = 0; i < 8; i++) 799 | printf("%u: %u\n", i, bc7_mode_hist[i]); 800 | } 801 | 802 | if (has_alpha) 803 | printf("Source image had an alpha channel.\n"); 804 | 805 | bool failed = false; 806 | 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)) 807 | failed = true; 808 | else 809 | printf("Wrote DDS file %s\n", dds_output_filename.c_str()); 810 | 811 | if ((!no_output_png) && (png_output_filename.size())) 812 | { 813 | image_u8 unpacked_image(source_image.width(), source_image.height()); 814 | 815 | bool punchthrough_flag = false; 816 | for (uint32_t by = 0; by < blocks_y; by++) 817 | { 818 | for (uint32_t bx = 0; bx < blocks_x; bx++) 819 | { 820 | void* pBlock = (bytes_per_block == 16) ? (void *)&packed_image16[bx + by * blocks_x] : (void*)&packed_image8[bx + by * blocks_x]; 821 | 822 | color_quad_u8 unpacked_pixels[16]; 823 | for (uint32_t i = 0; i < 16; i++) 824 | unpacked_pixels[i].set(0, 0, 0, 255); 825 | 826 | switch (dxgi_format) 827 | { 828 | case DXGI_FORMAT_BC1_UNORM: 829 | rgbcx::unpack_bc1(pBlock, unpacked_pixels, true); 830 | break; 831 | case DXGI_FORMAT_BC3_UNORM: 832 | if (!rgbcx::unpack_bc3(pBlock, unpacked_pixels)) 833 | punchthrough_flag = true; 834 | break; 835 | case DXGI_FORMAT_BC4_UNORM: 836 | rgbcx::unpack_bc4(pBlock, &unpacked_pixels[0][0], 4); 837 | break; 838 | case DXGI_FORMAT_BC5_UNORM: 839 | rgbcx::unpack_bc5(pBlock, &unpacked_pixels[0][0], 0, 1, 4); 840 | break; 841 | case DXGI_FORMAT_BC7_UNORM: 842 | bc7decomp::unpack_bc7((const uint8_t*)pBlock, (bc7decomp::color_rgba*)unpacked_pixels); 843 | break; 844 | default: 845 | assert(0); 846 | break; 847 | } 848 | 849 | unpacked_image.set_block(bx, by, 4, 4, unpacked_pixels); 850 | } // bx 851 | } // by 852 | 853 | if ((punchthrough_flag) && (dxgi_format == DXGI_FORMAT_BC3_UNORM)) 854 | fprintf(stderr, "Warning: BC3 mode selected, but rgbcx::unpack_bc3() returned one or more blocks using 3-color mode!\n"); 855 | 856 | if ((dxgi_format != DXGI_FORMAT_BC4_UNORM) && (dxgi_format != DXGI_FORMAT_BC5_UNORM)) 857 | { 858 | image_metrics y_metrics; 859 | y_metrics.compute(source_image, unpacked_image, 0, 0); 860 | 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); 861 | 862 | image_metrics rgb_metrics; 863 | rgb_metrics.compute(source_image, unpacked_image, 0, 3); 864 | 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); 865 | 866 | image_metrics rgba_metrics; 867 | rgba_metrics.compute(source_image, unpacked_image, 0, 4); 868 | 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); 869 | } 870 | 871 | for (uint32_t chan = 0; chan < 4; chan++) 872 | { 873 | if (dxgi_format == DXGI_FORMAT_BC4_UNORM) 874 | { 875 | if (chan != bc45_channel0) 876 | continue; 877 | } 878 | else if (dxgi_format == DXGI_FORMAT_BC5_UNORM) 879 | { 880 | if ((chan != bc45_channel0) && (chan != bc45_channel1)) 881 | continue; 882 | } 883 | 884 | image_metrics c_metrics; 885 | c_metrics.compute(source_image, unpacked_image, chan, 1); 886 | static const char *s_chan_names[4] = { "Red ", "Green", "Blue ", "Alpha" }; 887 | 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); 888 | } 889 | 890 | if (!save_png(png_output_filename.c_str(), unpacked_image, false)) 891 | failed = true; 892 | else 893 | printf("Wrote PNG file %s\n", png_output_filename.c_str()); 894 | 895 | if (png_alpha_output_filename.size()) 896 | { 897 | image_u8 unpacked_image_alpha(unpacked_image); 898 | for (uint32_t y = 0; y < unpacked_image_alpha.height(); y++) 899 | for (uint32_t x = 0; x < unpacked_image_alpha.width(); x++) 900 | unpacked_image_alpha(x, y).set(unpacked_image_alpha(x, y)[3], 255); 901 | 902 | if (!save_png(png_alpha_output_filename.c_str(), unpacked_image_alpha, false)) 903 | failed = true; 904 | else 905 | printf("Wrote PNG file %s\n", png_alpha_output_filename.c_str()); 906 | } 907 | } 908 | 909 | return failed ? EXIT_FAILURE : EXIT_SUCCESS; 910 | } 911 | -------------------------------------------------------------------------------- /ThirdParty/icbc/README.md: -------------------------------------------------------------------------------- 1 | # ICBC - A High Quality BC1 Encoder 2 | This is the BC1 texture block encoder used by [NVTT](https://github.com/castano/nvidia-texture-tools), extracted as an easy to use single header file library. 3 | 4 | It implements several algorithms: 5 | 6 | - Fast DXT encoding using box fitting as described in: [Real-Time-YCoCg-DXT-Compression](https://developer.download.nvidia.com/whitepapers/2007/Real-Time-YCoCg-DXT-Compression/Real-Time%20YCoCg-DXT%20Compression.pdf) 7 | - Least squares endpoint optimization as in stb_dxt: https://github.com/nothings/stb/blob/master/stb_dxt.h 8 | - Exhaustive cluster fit evaluation as in squish: http://sjbrown.co.uk/2006/01/19/dxt-compression-techniques/ 9 | - Iterative end-point refinement along the lines of the algorithm described by clooom: http://cbloomrants.blogspot.com/2008/12/12-08-08-dxtc-summary.html 10 | 11 | There are also some experimental algorithms that are not enabled: 12 | 13 | - Exhaustive cluster fit with pre-computed factors for every cluster configuration. As in [High Quality DXT Compression using CUDA](https://developer.download.nvidia.com/compute/cuda/1.1-Beta/x86_website/projects/dxtc/doc/cuda_dxtc.pdf) 14 | - K-means cluster fit. 15 | 16 | Even though NVTT contained some fast compressors, these were not particularly optimized. The focus was on producing very high quality textures, and for a long time this was the highest quality open-source compressor available. 17 | 18 | Today it has been superseded by Rich Geldreich's [RGBCX](https://github.com/richgel999/bc7enc/blob/master/rgbcx.h) library, which I hope becomes a suitable replacement for this code in almost all scenarios. 19 | 20 | -------------------------------------------------------------------------------- /ThirdParty/lodepng/LICENSE: -------------------------------------------------------------------------------- 1 | LodePNG version 20160124 2 | 3 | Copyright (c) 2005-2016 Lode Vandevenne 4 | 5 | This software is provided 'as-is', without any express or implied 6 | warranty. In no event will the authors be held liable for any damages 7 | arising from the use of this software. 8 | 9 | Permission is granted to anyone to use this software for any purpose, 10 | including commercial applications, and to alter it and redistribute it 11 | freely, subject to the following restrictions: 12 | 13 | 1. The origin of this software must not be misrepresented; you must not 14 | claim that you wrote the original software. If you use this software 15 | in a product, an acknowledgment in the product documentation would be 16 | appreciated but is not required. 17 | 18 | 2. Altered source versions must be plainly marked as such, and must not be 19 | misrepresented as being the original software. 20 | 21 | 3. This notice may not be removed or altered from any source 22 | distribution. 23 | 24 | The manual and changelog are in the header file "lodepng.h" 25 | Rename this file to lodepng.cpp to use it for C++, or to lodepng.c to use it for C. 26 | -------------------------------------------------------------------------------- /ThirdParty/rgetc/rg_etc1.h: -------------------------------------------------------------------------------- 1 | // File: rg_etc1.h - Fast, high quality ETC1 block packer/unpacker - Rich Geldreich 2 | // Please see ZLIB license at the end of this file. 3 | #pragma once 4 | 5 | namespace rg_etc1 6 | { 7 | // Unpacks an 8-byte ETC1 compressed block to a block of 4x4 32bpp RGBA pixels. 8 | // Returns false if the block is invalid. Invalid blocks will still be unpacked with clamping. 9 | // This function is thread safe, and does not dynamically allocate any memory. 10 | // If preserve_alpha is true, the alpha channel of the destination pixels will not be overwritten. Otherwise, alpha will be set to 255. 11 | bool unpack_etc1_block(const void *pETC1_block, unsigned int* pDst_pixels_rgba, bool preserve_alpha = false); 12 | 13 | // Quality setting = the higher the quality, the slower. 14 | // To pack large textures, it is highly recommended to call pack_etc1_block() in parallel, on different blocks, from multiple threads (particularly when using cHighQuality). 15 | enum etc1_quality 16 | { 17 | cLowQuality, 18 | cMediumQuality, 19 | cHighQuality, 20 | }; 21 | 22 | struct etc1_pack_params 23 | { 24 | etc1_quality m_quality; 25 | bool m_dithering; 26 | 27 | inline etc1_pack_params() 28 | { 29 | clear(); 30 | } 31 | 32 | void clear() 33 | { 34 | m_quality = cHighQuality; 35 | m_dithering = false; 36 | } 37 | }; 38 | 39 | // Important: pack_etc1_block_init() must be called before calling pack_etc1_block(). 40 | void pack_etc1_block_init(); 41 | 42 | // Packs a 4x4 block of 32bpp RGBA pixels to an 8-byte ETC1 block. 43 | // 32-bit RGBA pixels must always be arranged as (R,G,B,A) (R first, A last) in memory, independent of platform endianness. A should always be 255. 44 | // Returns squared error of result. 45 | // This function is thread safe, and does not dynamically allocate any memory. 46 | // pack_etc1_block() does not currently support "perceptual" colorspace metrics - it primarily optimizes for RGB RMSE. 47 | unsigned int pack_etc1_block(void* pETC1_block, const unsigned int* pSrc_pixels_rgba, etc1_pack_params& pack_params); 48 | 49 | } // namespace rg_etc1 50 | 51 | //------------------------------------------------------------------------------ 52 | // 53 | // rg_etc1 uses the ZLIB license: 54 | // http://opensource.org/licenses/Zlib 55 | // 56 | // Copyright (c) 2012 Rich Geldreich 57 | // 58 | // This software is provided 'as-is', without any express or implied 59 | // warranty. In no event will the authors be held liable for any damages 60 | // arising from the use of this software. 61 | // 62 | // Permission is granted to anyone to use this software for any purpose, 63 | // including commercial applications, and to alter it and redistribute it 64 | // freely, subject to the following restrictions: 65 | // 66 | // 1. The origin of this software must not be misrepresented; you must not 67 | // claim that you wrote the original software. If you use this software 68 | // in a product, an acknowledgment in the product documentation would be 69 | // appreciated but is not required. 70 | // 71 | // 2. Altered source versions must be plainly marked as such, and must not be 72 | // misrepresented as being the original software. 73 | // 74 | // 3. This notice may not be removed or altered from any source distribution. 75 | // 76 | //------------------------------------------------------------------------------ 77 | -------------------------------------------------------------------------------- /ThirdParty/ryg/stb_dxt.h: -------------------------------------------------------------------------------- 1 | // stb_dxt.h - v1.09 - DXT1/DXT5 compressor - public domain 2 | // original by fabian "ryg" giesen - ported to C by stb 3 | // use '#define STB_DXT_IMPLEMENTATION' before including to create the implementation 4 | // 5 | // USAGE: 6 | // call stb_compress_dxt_block() for every block (you must pad) 7 | // source should be a 4x4 block of RGBA data in row-major order; 8 | // Alpha channel is not stored if you specify alpha=0 (but you 9 | // must supply some constant alpha in the alpha channel). 10 | // You can turn on dithering and "high quality" using mode. 11 | // 12 | // version history: 13 | // v1.09 - (stb) update documentation re: surprising alpha channel requirement 14 | // v1.08 - (stb) fix bug in dxt-with-alpha block 15 | // v1.07 - (stb) bc4; allow not using libc; add STB_DXT_STATIC 16 | // v1.06 - (stb) fix to known-broken 1.05 17 | // v1.05 - (stb) support bc5/3dc (Arvids Kokins), use extern "C" in C++ (Pavel Krajcevski) 18 | // v1.04 - (ryg) default to no rounding bias for lerped colors (as per S3TC/DX10 spec); 19 | // single color match fix (allow for inexact color interpolation); 20 | // optimal DXT5 index finder; "high quality" mode that runs multiple refinement steps. 21 | // v1.03 - (stb) endianness support 22 | // v1.02 - (stb) fix alpha encoding bug 23 | // v1.01 - (stb) fix bug converting to RGB that messed up quality, thanks ryg & cbloom 24 | // v1.00 - (stb) first release 25 | // 26 | // contributors: 27 | // Kevin Schmidt (#defines for "freestanding" compilation) 28 | // github:ppiastucki (BC4 support) 29 | // 30 | // LICENSE 31 | // 32 | // See end of file for license information. 33 | 34 | #ifndef STB_INCLUDE_STB_DXT_H 35 | #define STB_INCLUDE_STB_DXT_H 36 | 37 | #ifdef __cplusplus 38 | extern "C" { 39 | #endif 40 | 41 | #ifdef STB_DXT_STATIC 42 | #define STBDDEF static 43 | #else 44 | #define STBDDEF extern 45 | #endif 46 | 47 | // compression mode (bitflags) 48 | #define STB_DXT_NORMAL 0 49 | #define STB_DXT_DITHER 1 // use dithering. dubious win. never use for normal maps and the like! 50 | #define STB_DXT_HIGHQUAL 2 // high quality mode, does two refinement steps instead of 1. ~30-40% slower. 51 | 52 | STBDDEF void stb_compress_dxt_block(unsigned char *dest, const unsigned char *src_rgba_four_bytes_per_pixel, int alpha, int mode); 53 | STBDDEF void stb_compress_bc4_block(unsigned char *dest, const unsigned char *src_r_one_byte_per_pixel); 54 | STBDDEF void stb_compress_bc5_block(unsigned char *dest, const unsigned char *src_rg_two_byte_per_pixel); 55 | 56 | #define STB_COMPRESS_DXT_BLOCK 57 | 58 | #ifdef __cplusplus 59 | } 60 | #endif 61 | #endif // STB_INCLUDE_STB_DXT_H 62 | 63 | #ifdef STB_DXT_IMPLEMENTATION 64 | 65 | // configuration options for DXT encoder. set them in the project/makefile or just define 66 | // them at the top. 67 | 68 | // STB_DXT_USE_ROUNDING_BIAS 69 | // use a rounding bias during color interpolation. this is closer to what "ideal" 70 | // interpolation would do but doesn't match the S3TC/DX10 spec. old versions (pre-1.03) 71 | // implicitly had this turned on. 72 | // 73 | // in case you're targeting a specific type of hardware (e.g. console programmers): 74 | // NVidia and Intel GPUs (as of 2010) as well as DX9 ref use DXT decoders that are closer 75 | // to STB_DXT_USE_ROUNDING_BIAS. AMD/ATI, S3 and DX10 ref are closer to rounding with no bias. 76 | // you also see "(a*5 + b*3) / 8" on some old GPU designs. 77 | // #define STB_DXT_USE_ROUNDING_BIAS 78 | 79 | #include 80 | 81 | #if !defined(STBD_ABS) || !defined(STBI_FABS) 82 | #include 83 | #endif 84 | 85 | #ifndef STBD_ABS 86 | #define STBD_ABS(i) abs(i) 87 | #endif 88 | 89 | #ifndef STBD_FABS 90 | #define STBD_FABS(x) fabs(x) 91 | #endif 92 | 93 | #ifndef STBD_MEMSET 94 | #include 95 | #define STBD_MEMSET memset 96 | #endif 97 | 98 | static unsigned char stb__Expand5[32]; 99 | static unsigned char stb__Expand6[64]; 100 | static unsigned char stb__OMatch5[256][2]; 101 | static unsigned char stb__OMatch6[256][2]; 102 | static unsigned char stb__QuantRBTab[256+16]; 103 | static unsigned char stb__QuantGTab[256+16]; 104 | 105 | static int stb__Mul8Bit(int a, int b) 106 | { 107 | int t = a*b + 128; 108 | return (t + (t >> 8)) >> 8; 109 | } 110 | 111 | static void stb__From16Bit(unsigned char *out, unsigned short v) 112 | { 113 | int rv = (v & 0xf800) >> 11; 114 | int gv = (v & 0x07e0) >> 5; 115 | int bv = (v & 0x001f) >> 0; 116 | 117 | out[0] = stb__Expand5[rv]; 118 | out[1] = stb__Expand6[gv]; 119 | out[2] = stb__Expand5[bv]; 120 | out[3] = 0; 121 | } 122 | 123 | static unsigned short stb__As16Bit(int r, int g, int b) 124 | { 125 | return (unsigned short)((stb__Mul8Bit(r,31) << 11) + (stb__Mul8Bit(g,63) << 5) + stb__Mul8Bit(b,31)); 126 | } 127 | 128 | // linear interpolation at 1/3 point between a and b, using desired rounding type 129 | static int stb__Lerp13(int a, int b) 130 | { 131 | #ifdef STB_DXT_USE_ROUNDING_BIAS 132 | // with rounding bias 133 | return a + stb__Mul8Bit(b-a, 0x55); 134 | #else 135 | // without rounding bias 136 | // replace "/ 3" by "* 0xaaab) >> 17" if your compiler sucks or you really need every ounce of speed. 137 | return (2*a + b) / 3; 138 | #endif 139 | } 140 | 141 | // lerp RGB color 142 | static void stb__Lerp13RGB(unsigned char *out, unsigned char *p1, unsigned char *p2) 143 | { 144 | out[0] = (unsigned char)stb__Lerp13(p1[0], p2[0]); 145 | out[1] = (unsigned char)stb__Lerp13(p1[1], p2[1]); 146 | out[2] = (unsigned char)stb__Lerp13(p1[2], p2[2]); 147 | } 148 | 149 | /****************************************************************************/ 150 | 151 | // compute table to reproduce constant colors as accurately as possible 152 | static void stb__PrepareOptTable(unsigned char *Table,const unsigned char *expand,int size) 153 | { 154 | int i,mn,mx; 155 | for (i=0;i<256;i++) { 156 | int bestErr = 256; 157 | for (mn=0;mn> 4)]; 202 | ep1[0] = bp[ 0] - dp[ 0]; 203 | dp[ 4] = quant[bp[ 4] + ((7*ep1[0] + 3*ep2[2] + 5*ep2[1] + ep2[0]) >> 4)]; 204 | ep1[1] = bp[ 4] - dp[ 4]; 205 | dp[ 8] = quant[bp[ 8] + ((7*ep1[1] + 3*ep2[3] + 5*ep2[2] + ep2[1]) >> 4)]; 206 | ep1[2] = bp[ 8] - dp[ 8]; 207 | dp[12] = quant[bp[12] + ((7*ep1[2] + 5*ep2[3] + ep2[2]) >> 4)]; 208 | ep1[3] = bp[12] - dp[12]; 209 | bp += 16; 210 | dp += 16; 211 | et = ep1, ep1 = ep2, ep2 = et; // swap 212 | } 213 | } 214 | } 215 | 216 | // The color matching function 217 | static unsigned int stb__MatchColorsBlock(unsigned char *block, unsigned char *color,int dither) 218 | { 219 | unsigned int mask = 0; 220 | int dirr = color[0*4+0] - color[1*4+0]; 221 | int dirg = color[0*4+1] - color[1*4+1]; 222 | int dirb = color[0*4+2] - color[1*4+2]; 223 | int dots[16]; 224 | int stops[4]; 225 | int i; 226 | int c0Point, halfPoint, c3Point; 227 | 228 | for(i=0;i<16;i++) 229 | dots[i] = block[i*4+0]*dirr + block[i*4+1]*dirg + block[i*4+2]*dirb; 230 | 231 | for(i=0;i<4;i++) 232 | stops[i] = color[i*4+0]*dirr + color[i*4+1]*dirg + color[i*4+2]*dirb; 233 | 234 | // think of the colors as arranged on a line; project point onto that line, then choose 235 | // next color out of available ones. we compute the crossover points for "best color in top 236 | // half"/"best in bottom half" and then the same inside that subinterval. 237 | // 238 | // relying on this 1d approximation isn't always optimal in terms of euclidean distance, 239 | // but it's very close and a lot faster. 240 | // http://cbloomrants.blogspot.com/2008/12/12-08-08-dxtc-summary.html 241 | 242 | c0Point = (stops[1] + stops[3]) >> 1; 243 | halfPoint = (stops[3] + stops[2]) >> 1; 244 | c3Point = (stops[2] + stops[0]) >> 1; 245 | 246 | if(!dither) { 247 | // the version without dithering is straightforward 248 | for (i=15;i>=0;i--) { 249 | int dot = dots[i]; 250 | mask <<= 2; 251 | 252 | if(dot < halfPoint) 253 | mask |= (dot < c0Point) ? 1 : 3; 254 | else 255 | mask |= (dot < c3Point) ? 2 : 0; 256 | } 257 | } else { 258 | // with floyd-steinberg dithering 259 | int err[8],*ep1 = err,*ep2 = err+4; 260 | int *dp = dots, y; 261 | 262 | c0Point <<= 4; 263 | halfPoint <<= 4; 264 | c3Point <<= 4; 265 | for(i=0;i<8;i++) 266 | err[i] = 0; 267 | 268 | for(y=0;y<4;y++) 269 | { 270 | int dot,lmask,step; 271 | 272 | dot = (dp[0] << 4) + (3*ep2[1] + 5*ep2[0]); 273 | if(dot < halfPoint) 274 | step = (dot < c0Point) ? 1 : 3; 275 | else 276 | step = (dot < c3Point) ? 2 : 0; 277 | ep1[0] = dp[0] - stops[step]; 278 | lmask = step; 279 | 280 | dot = (dp[1] << 4) + (7*ep1[0] + 3*ep2[2] + 5*ep2[1] + ep2[0]); 281 | if(dot < halfPoint) 282 | step = (dot < c0Point) ? 1 : 3; 283 | else 284 | step = (dot < c3Point) ? 2 : 0; 285 | ep1[1] = dp[1] - stops[step]; 286 | lmask |= step<<2; 287 | 288 | dot = (dp[2] << 4) + (7*ep1[1] + 3*ep2[3] + 5*ep2[2] + ep2[1]); 289 | if(dot < halfPoint) 290 | step = (dot < c0Point) ? 1 : 3; 291 | else 292 | step = (dot < c3Point) ? 2 : 0; 293 | ep1[2] = dp[2] - stops[step]; 294 | lmask |= step<<4; 295 | 296 | dot = (dp[3] << 4) + (7*ep1[2] + 5*ep2[3] + ep2[2]); 297 | if(dot < halfPoint) 298 | step = (dot < c0Point) ? 1 : 3; 299 | else 300 | step = (dot < c3Point) ? 2 : 0; 301 | ep1[3] = dp[3] - stops[step]; 302 | lmask |= step<<6; 303 | 304 | dp += 4; 305 | mask |= lmask << (y*8); 306 | { int *et = ep1; ep1 = ep2; ep2 = et; } // swap 307 | } 308 | } 309 | 310 | return mask; 311 | } 312 | 313 | // The color optimization function. (Clever code, part 1) 314 | static void stb__OptimizeColorsBlock(unsigned char *block, unsigned short *pmax16, unsigned short *pmin16) 315 | { 316 | int mind = 0x7fffffff,maxd = -0x7fffffff; 317 | unsigned char *minp, *maxp; 318 | double magn; 319 | int v_r,v_g,v_b; 320 | static const int nIterPower = 4; 321 | float covf[6],vfr,vfg,vfb; 322 | 323 | // determine color distribution 324 | int cov[6]; 325 | int mu[3],min[3],max[3]; 326 | int ch,i,iter; 327 | 328 | for(ch=0;ch<3;ch++) 329 | { 330 | const unsigned char *bp = ((const unsigned char *) block) + ch; 331 | int muv,minv,maxv; 332 | 333 | muv = minv = maxv = bp[0]; 334 | for(i=4;i<64;i+=4) 335 | { 336 | muv += bp[i]; 337 | if (bp[i] < minv) minv = bp[i]; 338 | else if (bp[i] > maxv) maxv = bp[i]; 339 | } 340 | 341 | mu[ch] = (muv + 8) >> 4; 342 | min[ch] = minv; 343 | max[ch] = maxv; 344 | } 345 | 346 | // determine covariance matrix 347 | for (i=0;i<6;i++) 348 | cov[i] = 0; 349 | 350 | for (i=0;i<16;i++) 351 | { 352 | int r = block[i*4+0] - mu[0]; 353 | int g = block[i*4+1] - mu[1]; 354 | int b = block[i*4+2] - mu[2]; 355 | 356 | cov[0] += r*r; 357 | cov[1] += r*g; 358 | cov[2] += r*b; 359 | cov[3] += g*g; 360 | cov[4] += g*b; 361 | cov[5] += b*b; 362 | } 363 | 364 | // convert covariance matrix to float, find principal axis via power iter 365 | for(i=0;i<6;i++) 366 | covf[i] = cov[i] / 255.0f; 367 | 368 | vfr = (float) (max[0] - min[0]); 369 | vfg = (float) (max[1] - min[1]); 370 | vfb = (float) (max[2] - min[2]); 371 | 372 | for(iter=0;iter magn) magn = STBD_FABS(vfg); 385 | if (STBD_FABS(vfb) > magn) magn = STBD_FABS(vfb); 386 | 387 | if(magn < 4.0f) { // too small, default to luminance 388 | v_r = 299; // JPEG YCbCr luma coefs, scaled by 1000. 389 | v_g = 587; 390 | v_b = 114; 391 | } else { 392 | magn = 512.0 / magn; 393 | v_r = (int) (vfr * magn); 394 | v_g = (int) (vfg * magn); 395 | v_b = (int) (vfb * magn); 396 | } 397 | 398 | // Pick colors at extreme points 399 | for(i=0;i<16;i++) 400 | { 401 | int dot = block[i*4+0]*v_r + block[i*4+1]*v_g + block[i*4+2]*v_b; 402 | 403 | if (dot < mind) { 404 | mind = dot; 405 | minp = block+i*4; 406 | } 407 | 408 | if (dot > maxd) { 409 | maxd = dot; 410 | maxp = block+i*4; 411 | } 412 | } 413 | 414 | *pmax16 = stb__As16Bit(maxp[0],maxp[1],maxp[2]); 415 | *pmin16 = stb__As16Bit(minp[0],minp[1],minp[2]); 416 | } 417 | 418 | static int stb__sclamp(float y, int p0, int p1) 419 | { 420 | int x = (int) y; 421 | if (x < p0) return p0; 422 | if (x > p1) return p1; 423 | return x; 424 | } 425 | 426 | // The refinement function. (Clever code, part 2) 427 | // Tries to optimize colors to suit block contents better. 428 | // (By solving a least squares system via normal equations+Cramer's rule) 429 | static int stb__RefineBlock(unsigned char *block, unsigned short *pmax16, unsigned short *pmin16, unsigned int mask) 430 | { 431 | static const int w1Tab[4] = { 3,0,2,1 }; 432 | static const int prods[4] = { 0x090000,0x000900,0x040102,0x010402 }; 433 | // ^some magic to save a lot of multiplies in the accumulating loop... 434 | // (precomputed products of weights for least squares system, accumulated inside one 32-bit register) 435 | 436 | float frb,fg; 437 | unsigned short oldMin, oldMax, min16, max16; 438 | int i, akku = 0, xx,xy,yy; 439 | int At1_r,At1_g,At1_b; 440 | int At2_r,At2_g,At2_b; 441 | unsigned int cm = mask; 442 | 443 | oldMin = *pmin16; 444 | oldMax = *pmax16; 445 | 446 | if((mask ^ (mask<<2)) < 4) // all pixels have the same index? 447 | { 448 | // yes, linear system would be singular; solve using optimal 449 | // single-color match on average color 450 | int r = 8, g = 8, b = 8; 451 | for (i=0;i<16;++i) { 452 | r += block[i*4+0]; 453 | g += block[i*4+1]; 454 | b += block[i*4+2]; 455 | } 456 | 457 | r >>= 4; g >>= 4; b >>= 4; 458 | 459 | max16 = (stb__OMatch5[r][0]<<11) | (stb__OMatch6[g][0]<<5) | stb__OMatch5[b][0]; 460 | min16 = (stb__OMatch5[r][1]<<11) | (stb__OMatch6[g][1]<<5) | stb__OMatch5[b][1]; 461 | } else { 462 | At1_r = At1_g = At1_b = 0; 463 | At2_r = At2_g = At2_b = 0; 464 | for (i=0;i<16;++i,cm>>=2) { 465 | int step = cm&3; 466 | int w1 = w1Tab[step]; 467 | int r = block[i*4+0]; 468 | int g = block[i*4+1]; 469 | int b = block[i*4+2]; 470 | 471 | akku += prods[step]; 472 | At1_r += w1*r; 473 | At1_g += w1*g; 474 | At1_b += w1*b; 475 | At2_r += r; 476 | At2_g += g; 477 | At2_b += b; 478 | } 479 | 480 | At2_r = 3*At2_r - At1_r; 481 | At2_g = 3*At2_g - At1_g; 482 | At2_b = 3*At2_b - At1_b; 483 | 484 | // extract solutions and decide solvability 485 | xx = akku >> 16; 486 | yy = (akku >> 8) & 0xff; 487 | xy = (akku >> 0) & 0xff; 488 | 489 | frb = 3.0f * 31.0f / 255.0f / (xx*yy - xy*xy); 490 | fg = frb * 63.0f / 31.0f; 491 | 492 | // solve. 493 | max16 = (unsigned short)(stb__sclamp((At1_r*yy - At2_r*xy)*frb+0.5f,0,31) << 11); 494 | max16 |= (unsigned short)(stb__sclamp((At1_g*yy - At2_g*xy)*fg +0.5f,0,63) << 5); 495 | max16 |= (unsigned short)(stb__sclamp((At1_b*yy - At2_b*xy)*frb+0.5f,0,31) << 0); 496 | 497 | min16 = (unsigned short)(stb__sclamp((At2_r*xx - At1_r*xy)*frb+0.5f,0,31) << 11); 498 | min16 |= (unsigned short)(stb__sclamp((At2_g*xx - At1_g*xy)*fg +0.5f,0,63) << 5); 499 | min16 |= (unsigned short)(stb__sclamp((At2_b*xx - At1_b*xy)*frb+0.5f,0,31) << 0); 500 | } 501 | 502 | *pmin16 = min16; 503 | *pmax16 = max16; 504 | return oldMin != min16 || oldMax != max16; 505 | } 506 | 507 | // Color block compression 508 | static void stb__CompressColorBlock(unsigned char *dest, unsigned char *block, int mode) 509 | { 510 | unsigned int mask; 511 | int i; 512 | int dither; 513 | int refinecount; 514 | unsigned short max16, min16; 515 | unsigned char dblock[16*4],color[4*4]; 516 | 517 | dither = mode & STB_DXT_DITHER; 518 | refinecount = (mode & STB_DXT_HIGHQUAL) ? 2 : 1; 519 | 520 | // check if block is constant 521 | for (i=1;i<16;i++) 522 | if (((unsigned int *) block)[i] != ((unsigned int *) block)[0]) 523 | break; 524 | 525 | if(i == 16) { // constant color 526 | int r = block[0], g = block[1], b = block[2]; 527 | mask = 0xaaaaaaaa; 528 | max16 = (stb__OMatch5[r][0]<<11) | (stb__OMatch6[g][0]<<5) | stb__OMatch5[b][0]; 529 | min16 = (stb__OMatch5[r][1]<<11) | (stb__OMatch6[g][1]<<5) | stb__OMatch5[b][1]; 530 | } else { 531 | // first step: compute dithered version for PCA if desired 532 | if(dither) 533 | stb__DitherBlock(dblock,block); 534 | 535 | // second step: pca+map along principal axis 536 | stb__OptimizeColorsBlock(dither ? dblock : block,&max16,&min16); 537 | if (max16 != min16) { 538 | stb__EvalColors(color,max16,min16); 539 | mask = stb__MatchColorsBlock(block,color,dither); 540 | } else 541 | mask = 0; 542 | 543 | // third step: refine (multiple times if requested) 544 | for (i=0;i> 8); 573 | dest[2] = (unsigned char) (min16); 574 | dest[3] = (unsigned char) (min16 >> 8); 575 | dest[4] = (unsigned char) (mask); 576 | dest[5] = (unsigned char) (mask >> 8); 577 | dest[6] = (unsigned char) (mask >> 16); 578 | dest[7] = (unsigned char) (mask >> 24); 579 | } 580 | 581 | // Alpha block compression (this is easy for a change) 582 | static void stb__CompressAlphaBlock(unsigned char *dest,unsigned char *src, int stride) 583 | { 584 | int i,dist,bias,dist4,dist2,bits,mask; 585 | 586 | // find min/max color 587 | int mn,mx; 588 | mn = mx = src[0]; 589 | 590 | for (i=1;i<16;i++) 591 | { 592 | if (src[i*stride] < mn) mn = src[i*stride]; 593 | else if (src[i*stride] > mx) mx = src[i*stride]; 594 | } 595 | 596 | // encode them 597 | dest[0] = (unsigned char)mx; 598 | dest[1] = (unsigned char)mn; 599 | dest += 2; 600 | 601 | // determine bias and emit color indices 602 | // given the choice of mx/mn, these indices are optimal: 603 | // http://fgiesen.wordpress.com/2009/12/15/dxt5-alpha-block-index-determination/ 604 | dist = mx-mn; 605 | dist4 = dist*4; 606 | dist2 = dist*2; 607 | bias = (dist < 8) ? (dist - 1) : (dist/2 + 2); 608 | bias -= mn * 7; 609 | bits = 0,mask=0; 610 | 611 | for (i=0;i<16;i++) { 612 | int a = src[i*stride]*7 + bias; 613 | int ind,t; 614 | 615 | // select index. this is a "linear scale" lerp factor between 0 (val=min) and 7 (val=max). 616 | t = (a >= dist4) ? -1 : 0; ind = t & 4; a -= dist4 & t; 617 | t = (a >= dist2) ? -1 : 0; ind += t & 2; a -= dist2 & t; 618 | ind += (a >= dist); 619 | 620 | // turn linear scale into DXT index (0/1 are extremal pts) 621 | ind = -ind & 7; 622 | ind ^= (2 > ind); 623 | 624 | // write index 625 | mask |= ind << bits; 626 | if((bits += 3) >= 8) { 627 | *dest++ = (unsigned char)mask; 628 | mask >>= 8; 629 | bits -= 8; 630 | } 631 | } 632 | } 633 | 634 | static void stb__InitDXT() 635 | { 636 | int i; 637 | for(i=0;i<32;i++) 638 | stb__Expand5[i] = (unsigned char)((i<<3)|(i>>2)); 639 | 640 | for(i=0;i<64;i++) 641 | stb__Expand6[i] = (unsigned char)((i<<2)|(i>>4)); 642 | 643 | for(i=0;i<256+16;i++) 644 | { 645 | int v = i-8 < 0 ? 0 : i-8 > 255 ? 255 : i-8; 646 | stb__QuantRBTab[i] = stb__Expand5[stb__Mul8Bit(v,31)]; 647 | stb__QuantGTab[i] = stb__Expand6[stb__Mul8Bit(v,63)]; 648 | } 649 | 650 | stb__PrepareOptTable(&stb__OMatch5[0][0],stb__Expand5,32); 651 | stb__PrepareOptTable(&stb__OMatch6[0][0],stb__Expand6,64); 652 | } 653 | 654 | void stb_compress_dxt_block(unsigned char *dest, const unsigned char *src, int alpha, int mode) 655 | { 656 | unsigned char data[16][4]; 657 | static int init=1; 658 | if (init) { 659 | stb__InitDXT(); 660 | init=0; 661 | } 662 | 663 | if (alpha) { 664 | int i; 665 | stb__CompressAlphaBlock(dest,(unsigned char*) src+3, 4); 666 | dest += 8; 667 | // make a new copy of the data in which alpha is opaque, 668 | // because code uses a fast test for color constancy 669 | memcpy(data, src, 4*16); 670 | for (i=0; i < 16; ++i) 671 | data[i][3] = 255; 672 | src = &data[0][0]; 673 | } 674 | 675 | stb__CompressColorBlock(dest,(unsigned char*) src,mode); 676 | } 677 | 678 | void stb_compress_bc4_block(unsigned char *dest, const unsigned char *src) 679 | { 680 | stb__CompressAlphaBlock(dest,(unsigned char*) src, 1); 681 | } 682 | 683 | void stb_compress_bc5_block(unsigned char *dest, const unsigned char *src) 684 | { 685 | stb__CompressAlphaBlock(dest,(unsigned char*) src,2); 686 | stb__CompressAlphaBlock(dest + 8,(unsigned char*) src+1,2); 687 | } 688 | #endif // STB_DXT_IMPLEMENTATION 689 | 690 | /* 691 | ------------------------------------------------------------------------------ 692 | This software is available under 2 licenses -- choose whichever you prefer. 693 | ------------------------------------------------------------------------------ 694 | ALTERNATIVE A - MIT License 695 | Copyright (c) 2017 Sean Barrett 696 | Permission is hereby granted, free of charge, to any person obtaining a copy of 697 | this software and associated documentation files (the "Software"), to deal in 698 | the Software without restriction, including without limitation the rights to 699 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 700 | of the Software, and to permit persons to whom the Software is furnished to do 701 | so, subject to the following conditions: 702 | The above copyright notice and this permission notice shall be included in all 703 | copies or substantial portions of the Software. 704 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 705 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 706 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 707 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 708 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 709 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 710 | SOFTWARE. 711 | ------------------------------------------------------------------------------ 712 | ALTERNATIVE B - Public Domain (www.unlicense.org) 713 | This is free and unencumbered software released into the public domain. 714 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 715 | software, either in source code form or as a compiled binary, for any purpose, 716 | commercial or non-commercial, and by any means. 717 | In jurisdictions that recognize copyright laws, the author or authors of this 718 | software dedicate any and all copyright interest in the software to the public 719 | domain. We make this dedication for the benefit of the public at large and to 720 | the detriment of our heirs and successors. We intend this dedication to be an 721 | overt act of relinquishment in perpetuity of all present and future rights to 722 | this software under copyright law. 723 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 724 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 725 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 726 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 727 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 728 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 729 | ------------------------------------------------------------------------------ 730 | */ 731 | -------------------------------------------------------------------------------- /VisualStudio/generate17.cmd: -------------------------------------------------------------------------------- 1 | set builddir="vs2017" 2 | del %builddir% /S /Q 3 | mkdir %builddir% 4 | cd %builddir% 5 | cmake -G "Visual Studio 15 2017 Win64" ../../ 6 | cd .. -------------------------------------------------------------------------------- /cmake/alp_add_git_repository.cmake: -------------------------------------------------------------------------------- 1 | ############################################################################# 2 | # Alpine Terrain Renderer 3 | # Copyright (C) 2023 Adam Celarek 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | ############################################################################# 18 | 19 | find_package(Git 2.22 REQUIRED) 20 | 21 | # CMake's FetchContent caches information about the downloads / clones in the build dir. 22 | # Therefore it walks over the clones every time we switch the build type (release, debug, webassembly, android etc), 23 | # which takes forever. Moreover, it messes up changes to subprojects. This function, on the other hand, checks whether 24 | # we are on a branch and in that case only issues a warning. Use origin/main or similar, if you want to stay up-to-date 25 | # with upstream. 26 | 27 | function(alp_add_git_repository name) 28 | set(options DO_NOT_ADD_SUBPROJECT) 29 | set(oneValueArgs URL COMMITISH DESTINATION_PATH) 30 | set(multiValueArgs ) 31 | cmake_parse_arguments(PARSE_ARGV 1 PARAM "${options}" "${oneValueArgs}" "${multiValueArgs}") 32 | 33 | file(MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/ThirdParty/extern ) 34 | set(repo_dir ${CMAKE_SOURCE_DIR}/ThirdParty/extern/${name}) 35 | set(short_repo_dir ThirdParty/extern/${name}) 36 | if (DEFINED PARAM_DESTINATION_PATH AND NOT PARAM_DESTINATION_PATH STREQUAL "") 37 | set(repo_dir ${CMAKE_SOURCE_DIR}/${PARAM_DESTINATION_PATH}) 38 | set(short_repo_dir ${PARAM_DESTINATION_PATH}) 39 | endif() 40 | 41 | set(${name}_SOURCE_DIR "${repo_dir}" PARENT_SCOPE) 42 | 43 | if(EXISTS "${repo_dir}/.git") 44 | message(STATUS "Updating git repo in ${short_repo_dir}") 45 | execute_process(COMMAND ${GIT_EXECUTABLE} fetch 46 | WORKING_DIRECTORY ${repo_dir} 47 | RESULT_VARIABLE GIT_FETCH_RESULT) 48 | if (NOT ${GIT_FETCH_RESULT}) 49 | message(STATUS "Fetching ${name} was successfull.") 50 | 51 | execute_process(COMMAND ${GIT_EXECUTABLE} branch --show-current 52 | WORKING_DIRECTORY ${repo_dir} 53 | RESULT_VARIABLE GIT_BRANCH_RESULT 54 | OUTPUT_STRIP_TRAILING_WHITESPACE 55 | OUTPUT_VARIABLE GIT_BRANCH_OUTPUT) 56 | if (${GIT_BRANCH_RESULT}) 57 | message(FATAL_ERROR "${repo_dir}: git branch --show-current not successfull") 58 | endif() 59 | if (GIT_BRANCH_OUTPUT STREQUAL "") 60 | execute_process(COMMAND ${GIT_EXECUTABLE} checkout --quiet ${PARAM_COMMITISH} 61 | WORKING_DIRECTORY ${repo_dir} 62 | RESULT_VARIABLE GIT_CHECKOUT_RESULT) 63 | if (NOT ${GIT_CHECKOUT_RESULT}) 64 | message(STATUS "In ${name}, checking out ${PARAM_COMMITISH} was successfull.") 65 | else() 66 | message(FATAL_ERROR "In ${name}, checking out ${PARAM_COMMITISH} was NOT successfull!") 67 | endif() 68 | else() 69 | message(WARNING "${short_repo_dir} is on branch ${GIT_BRANCH_OUTPUT}, leaving it there. " 70 | "NOT checking out ${PARAM_COMMITISH}! Use origin/main or similar if you want to stay up-to-date with upstream.") 71 | endif() 72 | else() 73 | message(WARNING "Fetching ${name} was NOT successfull!") 74 | endif() 75 | else() 76 | message(STATUS "Clonging ${PARAM_URL} to ${short_repo_dir}.") 77 | execute_process(COMMAND ${GIT_EXECUTABLE} clone --recurse-submodules ${PARAM_URL} ${repo_dir} 78 | RESULT_VARIABLE GIT_CLONE_RESULT) 79 | if (NOT ${GIT_CLONE_RESULT}) 80 | execute_process(COMMAND ${GIT_EXECUTABLE} checkout --quiet ${PARAM_COMMITISH} 81 | WORKING_DIRECTORY ${repo_dir} 82 | RESULT_VARIABLE GIT_CHECKOUT_RESULT) 83 | if (NOT ${GIT_CHECKOUT_RESULT}) 84 | message(STATUS "Checking out ${PARAM_COMMITISH} was successfull.") 85 | else() 86 | message(FATAL_ERROR "In ${name}, checking out ${PARAM_COMMITISH} was NOT successfull!") 87 | endif() 88 | else() 89 | message(FATAL_ERROR "Clonging ${name} was NOT successfull!") 90 | endif() 91 | endif() 92 | 93 | if (NOT ${PARAM_DO_NOT_ADD_SUBPROJECT}) 94 | add_subdirectory(${repo_dir} ${CMAKE_BINARY_DIR}/alp_external/${name}) 95 | endif() 96 | endfunction() 97 | -------------------------------------------------------------------------------- /test-data/baboon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/baboon.png -------------------------------------------------------------------------------- /test-data/basis/README.md: -------------------------------------------------------------------------------- 1 | Put basisu executable into this folder to generate test data set. 2 | -------------------------------------------------------------------------------- /test-data/basis/basis_file_list.txt: -------------------------------------------------------------------------------- 1 | ./temp/baboon.basis 2 | ./temp/frymire.basis 3 | ./temp/kodim01.basis 4 | ./temp/kodim02.basis 5 | ./temp/kodim03.basis 6 | ./temp/kodim04.basis 7 | ./temp/kodim05.basis 8 | ./temp/kodim06.basis 9 | ./temp/kodim07.basis 10 | ./temp/kodim08.basis 11 | ./temp/kodim09.basis 12 | ./temp/kodim10.basis 13 | ./temp/kodim11.basis 14 | ./temp/kodim12.basis 15 | ./temp/kodim13.basis 16 | ./temp/kodim14.basis 17 | ./temp/kodim15.basis 18 | ./temp/kodim16.basis 19 | ./temp/kodim17.basis 20 | ./temp/kodim18.basis 21 | ./temp/kodim19.basis 22 | ./temp/kodim20.basis 23 | ./temp/kodim21.basis 24 | ./temp/kodim22.basis 25 | ./temp/kodim23.basis 26 | ./temp/kodim24.basis 27 | ./temp/lena.basis 28 | ./temp/monarch.basis 29 | ./temp/patterns.basis 30 | ./temp/peppers.basis 31 | ./temp/roblox01.basis 32 | ./temp/roblox02.basis 33 | ./temp/roblox03.basis 34 | ./temp/roblox04.basis 35 | ./temp/roblox05.basis 36 | ./temp/roblox06.basis 37 | ./temp/sail.basis 38 | ./temp/serrano.basis 39 | ./temp/tulips.basis 40 | ./temp/pbr_bricks_albedo.basis 41 | ./temp/pbr_ground_albedo.basis 42 | ./temp/pbr_stones_albedo.basis 43 | ./temp/pbr_stones_normal.basis 44 | ./temp/pbr_head_albedo.basis 45 | ./temp/parrot_red.basis 46 | -------------------------------------------------------------------------------- /test-data/basis/etc1/baboon_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/baboon_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/frymire_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/frymire_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/kodim01_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/kodim01_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/kodim02_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/kodim02_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/kodim03_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/kodim03_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/kodim04_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/kodim04_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/kodim05_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/kodim05_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/kodim06_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/kodim06_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/kodim07_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/kodim07_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/kodim08_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/kodim08_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/kodim09_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/kodim09_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/kodim10_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/kodim10_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/kodim11_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/kodim11_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/kodim12_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/kodim12_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/kodim13_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/kodim13_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/kodim14_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/kodim14_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/kodim15_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/kodim15_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/kodim16_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/kodim16_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/kodim17_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/kodim17_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/kodim18_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/kodim18_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/kodim19_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/kodim19_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/kodim20_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/kodim20_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/kodim21_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/kodim21_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/kodim22_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/kodim22_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/kodim23_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/kodim23_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/kodim24_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/kodim24_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/lena_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/lena_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/monarch_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/monarch_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/parrot_red_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/parrot_red_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/patterns_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/patterns_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/pbr_bricks_albedo_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/pbr_bricks_albedo_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/pbr_ground_albedo_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/pbr_ground_albedo_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/pbr_head_albedo_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/pbr_head_albedo_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/pbr_stones_albedo_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/pbr_stones_albedo_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/pbr_stones_normal_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/pbr_stones_normal_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/peppers_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/peppers_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/roblox01_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/roblox01_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/roblox02_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/roblox02_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/roblox03_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/roblox03_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/roblox04_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/roblox04_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/roblox05_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/roblox05_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/roblox06_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/roblox06_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/sail_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/sail_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/serrano_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/serrano_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/etc1/tulips_unpacked_rgb_ETC1_RGB_0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/basis/etc1/tulips_unpacked_rgb_ETC1_RGB_0000.png -------------------------------------------------------------------------------- /test-data/basis/pack.cmd: -------------------------------------------------------------------------------- 1 | basisu @png_file_list.txt -individual -output_path ./temp/ -------------------------------------------------------------------------------- /test-data/basis/png_file_list.txt: -------------------------------------------------------------------------------- 1 | ../baboon.png 2 | ../frymire.png 3 | ../kodim01.png 4 | ../kodim02.png 5 | ../kodim03.png 6 | ../kodim04.png 7 | ../kodim05.png 8 | ../kodim06.png 9 | ../kodim07.png 10 | ../kodim08.png 11 | ../kodim09.png 12 | ../kodim10.png 13 | ../kodim11.png 14 | ../kodim12.png 15 | ../kodim13.png 16 | ../kodim14.png 17 | ../kodim15.png 18 | ../kodim16.png 19 | ../kodim17.png 20 | ../kodim18.png 21 | ../kodim19.png 22 | ../kodim20.png 23 | ../kodim21.png 24 | ../kodim22.png 25 | ../kodim23.png 26 | ../kodim24.png 27 | ../lena.png 28 | ../monarch.png 29 | ../patterns.png 30 | ../peppers.png 31 | ../roblox01.png 32 | ../roblox02.png 33 | ../roblox03.png 34 | ../roblox04.png 35 | ../roblox05.png 36 | ../roblox06.png 37 | ../sail.png 38 | ../serrano.png 39 | ../tulips.png 40 | ../pbr_bricks_albedo.png 41 | ../pbr_ground_albedo.png 42 | ../pbr_stones_albedo.png 43 | ../pbr_stones_normal.png 44 | ../pbr_head_albedo.png 45 | ../parrot_red.png 46 | -------------------------------------------------------------------------------- /test-data/basis/temp/keep_me.txt: -------------------------------------------------------------------------------- 1 | These files take a long time to generate!!! -------------------------------------------------------------------------------- /test-data/basis/unpack.cmd: -------------------------------------------------------------------------------- 1 | basisu @basis_file_list.txt -unpack -no_ktx -etc1_only -output_path ./png 2 | copy /B /Y *etc1*.png etc1 3 | del /Q *.png -------------------------------------------------------------------------------- /test-data/frymire.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/frymire.png -------------------------------------------------------------------------------- /test-data/kodim01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/kodim01.png -------------------------------------------------------------------------------- /test-data/kodim02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/kodim02.png -------------------------------------------------------------------------------- /test-data/kodim03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/kodim03.png -------------------------------------------------------------------------------- /test-data/kodim04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/kodim04.png -------------------------------------------------------------------------------- /test-data/kodim05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/kodim05.png -------------------------------------------------------------------------------- /test-data/kodim06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/kodim06.png -------------------------------------------------------------------------------- /test-data/kodim07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/kodim07.png -------------------------------------------------------------------------------- /test-data/kodim08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/kodim08.png -------------------------------------------------------------------------------- /test-data/kodim09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/kodim09.png -------------------------------------------------------------------------------- /test-data/kodim10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/kodim10.png -------------------------------------------------------------------------------- /test-data/kodim11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/kodim11.png -------------------------------------------------------------------------------- /test-data/kodim12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/kodim12.png -------------------------------------------------------------------------------- /test-data/kodim13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/kodim13.png -------------------------------------------------------------------------------- /test-data/kodim14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/kodim14.png -------------------------------------------------------------------------------- /test-data/kodim15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/kodim15.png -------------------------------------------------------------------------------- /test-data/kodim16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/kodim16.png -------------------------------------------------------------------------------- /test-data/kodim17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/kodim17.png -------------------------------------------------------------------------------- /test-data/kodim18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/kodim18.png -------------------------------------------------------------------------------- /test-data/kodim19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/kodim19.png -------------------------------------------------------------------------------- /test-data/kodim20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/kodim20.png -------------------------------------------------------------------------------- /test-data/kodim21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/kodim21.png -------------------------------------------------------------------------------- /test-data/kodim22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/kodim22.png -------------------------------------------------------------------------------- /test-data/kodim23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/kodim23.png -------------------------------------------------------------------------------- /test-data/kodim24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/kodim24.png -------------------------------------------------------------------------------- /test-data/lena.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/lena.png -------------------------------------------------------------------------------- /test-data/monarch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/monarch.png -------------------------------------------------------------------------------- /test-data/parrot_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/parrot_red.png -------------------------------------------------------------------------------- /test-data/patterns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/patterns.png -------------------------------------------------------------------------------- /test-data/pbr_bricks_albedo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/pbr_bricks_albedo.png -------------------------------------------------------------------------------- /test-data/pbr_ground_albedo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/pbr_ground_albedo.png -------------------------------------------------------------------------------- /test-data/pbr_head_albedo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/pbr_head_albedo.png -------------------------------------------------------------------------------- /test-data/pbr_stones_albedo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/pbr_stones_albedo.png -------------------------------------------------------------------------------- /test-data/pbr_stones_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/pbr_stones_normal.png -------------------------------------------------------------------------------- /test-data/peppers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/peppers.png -------------------------------------------------------------------------------- /test-data/roblox01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/roblox01.png -------------------------------------------------------------------------------- /test-data/roblox02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/roblox02.png -------------------------------------------------------------------------------- /test-data/roblox03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/roblox03.png -------------------------------------------------------------------------------- /test-data/roblox04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/roblox04.png -------------------------------------------------------------------------------- /test-data/roblox05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/roblox05.png -------------------------------------------------------------------------------- /test-data/roblox06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/roblox06.png -------------------------------------------------------------------------------- /test-data/sail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/sail.png -------------------------------------------------------------------------------- /test-data/serrano.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/serrano.png -------------------------------------------------------------------------------- /test-data/tulips.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-data/tulips.png -------------------------------------------------------------------------------- /test-results/keep_me.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergeyMakeev/Goofy/25f1ac9f869df8ba6e0f3828d8a590e73dd6b966/test-results/keep_me.txt --------------------------------------------------------------------------------