├── .github └── workflows │ └── main.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE.txt ├── README.md ├── cmake ├── FindFreeImage.cmake ├── FindPVRTexLib.cmake ├── config.cmake ├── install.cmake └── templates │ ├── NoExport.h │ ├── UnixExport.h │ └── WindowsExport.h ├── doc ├── CMakeLists.txt └── Doxyfile.in ├── lib ├── AstcSources.cmake ├── Bc7encRdoSources.cmake ├── CMakeLists.txt ├── CompressonatorSources.cmake ├── Etc2CompSources.cmake ├── FreeImageSources.cmake ├── IspcTexcompSources.cmake ├── README.md ├── SquishSources.cmake ├── include │ └── cuttlefish │ │ ├── Color.h │ │ ├── Config.h │ │ ├── Image.h │ │ └── Texture.h ├── src │ ├── AstcConverter.cpp │ ├── AstcConverter.h │ ├── Converter.cpp │ ├── Converter.h │ ├── EtcConverter.cpp │ ├── EtcConverter.h │ ├── HalfFloat.cpp │ ├── HalfFloat.h │ ├── Image.cpp │ ├── PvrtcConverter.cpp │ ├── PvrtcConverter.h │ ├── S3tcConverter.cpp │ ├── S3tcConverter.h │ ├── SaveDds.cpp │ ├── SaveDds.h │ ├── SaveKtx.cpp │ ├── SaveKtx.h │ ├── SavePvr.cpp │ ├── SavePvr.h │ ├── Shared.cpp │ ├── Shared.h │ ├── StandardConverter.cpp │ ├── StandardConverter.h │ └── Texture.cpp └── test │ ├── CMakeLists.txt │ ├── HalfFloatTest.cpp │ ├── ImageTest.cpp │ ├── TextureSaveTest.cpp │ └── TextureTest.cpp └── tool ├── CMakeLists.txt ├── CommandLine.cpp ├── CommandLine.h ├── README.md ├── main.cpp └── test ├── array.txt ├── array0.png ├── array1.png ├── array2.png ├── cube-array.txt ├── cube.txt ├── image.txt ├── negx.png ├── negy.png ├── negz.png ├── posx.png ├── posy.png ├── posz.png ├── run-test.bat ├── run-test.sh └── texture.png /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Cuttlefish 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | branches: 8 | - master 9 | env: 10 | CTEST_OUTPUT_ON_FAILURE: '1' 11 | GTEST_OUTPUT: xml:${{ github.workspace }}/test-results/ 12 | cmake_common_args: >- 13 | -DCUTTLEFISH_FORCE_INTERNAL_FREEIMAGE=ON 14 | -DCMAKE_FIND_ROOT_PATH=${{ github.workspace }}/dependencies 15 | -DCMAKE_PREFIX_PATH=${{ github.workspace }}/dependencies 16 | -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/build/cuttlefish 17 | cores_count: '4' 18 | cores_mac_count: '3' 19 | dependency_location: "${{ github.workspace }}/dependencies" 20 | gtest_version: v1.17.0 21 | ispc_version: 1.27.0 22 | test_results_location: "${{ github.workspace }}/test-results" 23 | jobs: 24 | Linux: 25 | runs-on: ubuntu-22.04 26 | strategy: 27 | matrix: 28 | include: 29 | - lib_type: Static 30 | cmake_args: "-DCUTTLEFISH_SHARED=OFF" 31 | ispc: 0 32 | - lib_type: Shared 33 | cmake_args: "-DCUTTLEFISH_SHARED=ON" 34 | ispc: 0 35 | - lib_type: Static 36 | cmake_args: "-DCUTTLEFISH_SHARED=OFF -DCUTTLEFISH_ISPC_PATH=/tmp/ispc/bin/ispc" 37 | ispc: 1 38 | - lib_type: Shared 39 | cmake_args: "-DCUTTLEFISH_SHARED=ON -DCUTTLEFISH_ISPC_PATH=/tmp/ispc/bin/ispc" 40 | ispc: 1 41 | artifact: 1 42 | steps: 43 | - name: checkout 44 | uses: actions/checkout@v4 45 | - name: Download submodules 46 | run: |- 47 | git submodule update --init 48 | sudo apt-get update 49 | sudo apt-get -y install doxygen 50 | if [ ${{ matrix.ispc }} -eq 1 ]; then 51 | curl -L https://github.com/ispc/ispc/releases/download/v${{ env.ispc_version }}/ispc-v${{ env.ispc_version }}-linux.tar.gz -o ispc.tar.gz 52 | tar xzf ispc.tar.gz 53 | mv ispc-v${{ env.ispc_version }}-linux /tmp/ispc 54 | fi 55 | working-directory: "${{ github.workspace }}" 56 | - name: Build gtest 57 | run: |- 58 | git clone https://github.com/google/googletest.git googletest-code 59 | cd googletest-code 60 | git checkout ${{ env.gtest_version }} 61 | mkdir build 62 | cd build 63 | cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=${{ env.dependency_location }} 64 | cmake --build . -j ${{ env.cores_count }} 65 | cmake --build . --target install 66 | working-directory: "${{ github.workspace }}" 67 | - name: Build debug 68 | run: |- 69 | mkdir -p build/Debug 70 | cd build/Debug 71 | cmake -DCMAKE_BUILD_TYPE=Debug ${{ env.cmake_common_args }} ${{ matrix.cmake_args }} \ 72 | ${{ github.workspace }} 73 | cmake --build . -j ${{ env.cores_count }} 74 | working-directory: "${{ github.workspace }}" 75 | - name: Run tests debug 76 | continue-on-error: true 77 | timeout-minutes: 5 78 | run: ctest 79 | working-directory: "${{ github.workspace }}/build/Debug" 80 | - name: Publish test results 81 | uses: EnricoMi/publish-unit-test-result-action@v2 82 | with: 83 | check_name: Tests (Linux ${{ matrix.lib_type }} ISPC:${{ matrix.ispc }} Debug) 84 | junit_files: "${{ env.test_results_location }}/*.xml" 85 | - name: Clear test results 86 | run: rm *.xml 87 | working-directory: "${{ env.test_results_location }}" 88 | - name: Build release 89 | run: |- 90 | mkdir -p build/Release 91 | cd build/Release 92 | cmake -DCMAKE_BUILD_TYPE=Release ${{ env.cmake_common_args }} ${{ matrix.cmake_args }} \ 93 | ${{ github.workspace }} 94 | cmake --build . -j ${{ env.cores_count }} 95 | working-directory: "${{ github.workspace }}" 96 | - name: Run tests release 97 | continue-on-error: true 98 | timeout-minutes: 5 99 | run: ctest 100 | working-directory: "${{ github.workspace }}/build/Release" 101 | - name: Publish test results 102 | uses: EnricoMi/publish-unit-test-result-action@v2 103 | with: 104 | check_name: Tests (Linux ${{ matrix.lib_type }} ISPC:${{ matrix.ispc }} Release) 105 | junit_files: "${{ env.test_results_location }}/*.xml" 106 | - name: Package artifact 107 | if: matrix.artifact == 1 108 | run: |- 109 | cmake --build Release --target install 110 | tar czf cuttlefish-linux.tar.gz cuttlefish 111 | working-directory: "${{ github.workspace }}/build" 112 | - name: Publish artifact 113 | if: matrix.artifact == 1 114 | uses: actions/upload-artifact@v4 115 | with: 116 | name: Linux 117 | path: "${{ github.workspace }}/build/cuttlefish-linux.tar.gz" 118 | Mac: 119 | runs-on: macos-latest 120 | strategy: 121 | matrix: 122 | include: 123 | - lib_type: Static 124 | cmake_args: >- 125 | -GXcode -DCUTTLEFISH_SHARED=OFF -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" 126 | -DCMAKE_OSX_DEPLOYMENT_TARGET=10.14 127 | ispc: 0 128 | - lib_type: Shared 129 | cmake_args: >- 130 | -GXcode -DCUTTLEFISH_SHARED=ON -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" 131 | -DCMAKE_OSX_DEPLOYMENT_TARGET=10.14 132 | ispc: 0 133 | - lib_type: Static 134 | cmake_args: >- 135 | -GXcode -DCUTTLEFISH_SHARED=OFF -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" 136 | -DCMAKE_OSX_DEPLOYMENT_TARGET=10.14 -DCUTTLEFISH_ISPC_PATH=/tmp/ispc/bin/ispc 137 | ispc: 1 138 | - lib_type: Shared 139 | cmake_args: >- 140 | -GXcode -DCUTTLEFISH_SHARED=ON -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" 141 | -DCMAKE_OSX_DEPLOYMENT_TARGET=10.14 -DCUTTLEFISH_ISPC_PATH=/tmp/ispc/bin/ispc 142 | ispc: 1 143 | artifact: 1 144 | steps: 145 | - name: checkout 146 | uses: actions/checkout@v4 147 | - name: Download submodules 148 | run: |- 149 | git submodule update --init 150 | brew install doxygen 151 | if [ ${{ matrix.ispc }} -eq 1 ]; then 152 | curl -L https://github.com/ispc/ispc/releases/download/v${{ env.ispc_version }}/ispc-v${{ env.ispc_version }}-macOS.universal.tar.gz -o ispc.tar.gz 153 | tar xzf ispc.tar.gz 154 | mv ispc-v${{ env.ispc_version }}-macOS.universal /tmp/ispc 155 | fi 156 | working-directory: "${{ github.workspace }}" 157 | - name: Build gtest 158 | run: |- 159 | git clone https://github.com/google/googletest.git googletest-code 160 | cd googletest-code 161 | git checkout ${{ env.gtest_version }} 162 | mkdir build 163 | cd build 164 | cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ 165 | -DCMAKE_INSTALL_PREFIX=${{ env.dependency_location }} 166 | cmake --build . -j ${{ env.cores_mac_count }} 167 | cmake --build . --target install 168 | working-directory: "${{ github.workspace }}" 169 | - name: Run CMake 170 | run: |- 171 | mkdir build 172 | cd build 173 | cmake ${{ env.cmake_common_args }} ${{ matrix.cmake_args }} ${{ github.workspace }} 174 | working-directory: "${{ github.workspace }}" 175 | - name: Build debug 176 | run: cmake --build . --config Debug 177 | working-directory: "${{ github.workspace }}/build" 178 | - name: Run tests debug 179 | continue-on-error: true 180 | timeout-minutes: 5 181 | run: ctest -C Debug 182 | working-directory: "${{ github.workspace }}/build" 183 | - name: Publish test results 184 | uses: EnricoMi/publish-unit-test-result-action/macos@v2 185 | with: 186 | check_name: Tests (Mac ${{ matrix.lib_type }} ISPC:${{ matrix.ispc }} Debug) 187 | junit_files: "${{ env.test_results_location }}/*.xml" 188 | - name: Clear test results 189 | run: rm *.xml 190 | working-directory: "${{ env.test_results_location }}" 191 | - name: Build release 192 | run: cmake --build . --config Release 193 | working-directory: "${{ github.workspace }}/build" 194 | - name: Run tests release 195 | continue-on-error: true 196 | timeout-minutes: 5 197 | run: ctest -C Release 198 | working-directory: "${{ github.workspace }}/build" 199 | - name: Publish test results 200 | uses: EnricoMi/publish-unit-test-result-action/macos@v2 201 | with: 202 | check_name: Tests (Mac ${{ matrix.lib_type }} ISPC:${{ matrix.ispc }} Release) 203 | junit_files: "${{ env.test_results_location }}/*.xml" 204 | - name: Fixup install path 205 | run: cmake -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON ${{ github.workspace }} 206 | working-directory: "${{ github.workspace }}/build" 207 | - name: Package artifact 208 | if: matrix.artifact == 1 209 | run: |- 210 | cmake --build . --config Release --target install 211 | tar czf cuttlefish-mac.tar.gz cuttlefish 212 | working-directory: "${{ github.workspace }}/build" 213 | - name: Publish artifact 214 | if: matrix.artifact == 1 215 | uses: actions/upload-artifact@v4 216 | with: 217 | name: Mac 218 | path: "${{ github.workspace }}/build/cuttlefish-mac.tar.gz" 219 | Windows: 220 | runs-on: windows-2019 221 | strategy: 222 | matrix: 223 | include: 224 | - arch: Win32 225 | lib_type: Static 226 | gtest_cmake_args: "-Dgtest_force_shared_crt=ON" 227 | cmake_args: "-DCUTTLEFISH_SHARED=OFF" 228 | ispc: 0 229 | - arch: Win32 230 | lib_type: Shared 231 | gtest_cmake_args: "-Dgtest_force_shared_crt=ON" 232 | cmake_args: "-DCUTTLEFISH_SHARED=ON" 233 | ispc: 0 234 | - arch: Win32 235 | lib_type: Static 236 | gtest_cmake_args: "-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded$<$:Debug>" 237 | cmake_args: "-DCUTTLEFISH_SHARED=OFF -DCUTTLEFISH_STATIC_RUNTIME=ON -DCUTTLEFISH_ISPC_PATH=D:/ispc/bin/ispc.exe" 238 | ispc: 1 239 | artifact: win32-tool 240 | - arch: Win32 241 | lib_type: Shared 242 | gtest_cmake_args: "-Dgtest_force_shared_crt=ON" 243 | cmake_args: "-DCUTTLEFISH_SHARED=ON -DCUTTLEFISH_ISPC_PATH=D:/ispc/bin/ispc.exe" 244 | ispc: 1 245 | artifact: win32-full 246 | - arch: x64 247 | lib_type: Static 248 | gtest_cmake_args: "-Dgtest_force_shared_crt=ON" 249 | cmake_args: "-DCUTTLEFISH_SHARED=OFF" 250 | ispc: 0 251 | - arch: x64 252 | lib_type: Shared 253 | gtest_cmake_args: "-Dgtest_force_shared_crt=ON" 254 | cmake_args: "-DCUTTLEFISH_SHARED=ON" 255 | ispc: 0 256 | - arch: x64 257 | lib_type: Static 258 | gtest_cmake_args: "-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded$<$:Debug>" 259 | cmake_args: "-DCUTTLEFISH_SHARED=OFF -DCUTTLEFISH_STATIC_RUNTIME=ON -DCUTTLEFISH_ISPC_PATH=D:/ispc/bin/ispc.exe" 260 | ispc: 1 261 | artifact: win64-tool 262 | - arch: x64 263 | lib_type: Shared 264 | gtest_cmake_args: "-Dgtest_force_shared_crt=ON" 265 | cmake_args: "-DCUTTLEFISH_SHARED=ON -DCUTTLEFISH_ISPC_PATH=D:/ispc/bin/ispc.exe" 266 | ispc: 1 267 | artifact: win64-full 268 | steps: 269 | - name: checkout 270 | uses: actions/checkout@v4 271 | - name: Download submodules 272 | run: |- 273 | git submodule update --init 274 | if [ ${{ matrix.ispc }} -eq 1 ]; then 275 | curl -L https://github.com/ispc/ispc/releases/download/v${{ env.ispc_version }}/ispc-v${{ env.ispc_version }}-windows.zip -o ispc.zip 276 | unzip ispc.zip 277 | mv ispc-v${{ env.ispc_version }}-windows /d/ispc 278 | fi 279 | shell: bash 280 | working-directory: "${{ github.workspace }}" 281 | - name: Checkout gtest 282 | run: |- 283 | git clone https://github.com/google/googletest.git googletest-code 284 | cd googletest-code 285 | git checkout ${{ env.gtest_version }} 286 | mkdir build 287 | shell: bash 288 | working-directory: "${{ github.workspace }}" 289 | - name: Build gtest 290 | run: |- 291 | cmake .. -DCMAKE_INSTALL_PREFIX=${{ env.dependency_location }} ` 292 | ${{ matrix.gtest_cmake_args }} -A ${{ matrix.arch }} -T v141 -DCMAKE_DEBUG_POSTFIX=d 293 | cmake --build . --config Debug 294 | cmake --build . --config Debug --target install 295 | cmake --build . --config Release 296 | cmake --build . --config Release --target install 297 | working-directory: "${{ github.workspace }}/googletest-code/build" 298 | - name: Run CMake 299 | run: |- 300 | mkdir build 301 | cd build 302 | cmake ${{ env.cmake_common_args }} ${{ matrix.cmake_args }} -A ${{ matrix.arch }} -T v141 ` 303 | ${{ github.workspace }} 304 | working-directory: "${{ github.workspace }}" 305 | - name: Build debug 306 | run: cmake --build . --config Debug 307 | working-directory: "${{ github.workspace }}/build" 308 | - name: Run tests debug 309 | continue-on-error: true 310 | timeout-minutes: 5 311 | run: ctest -C Debug 312 | working-directory: "${{ github.workspace }}/build" 313 | - name: Publish test results 314 | uses: EnricoMi/publish-unit-test-result-action/windows@v2 315 | with: 316 | check_name: >- 317 | Tests (Windows ${{ matrix.arch }} ${{ matrix.lib_type }} ISPC:${{ matrix.ispc }} Debug) 318 | junit_files: "${{ env.test_results_location }}/*.xml" 319 | - name: Clear test results 320 | run: rm *.xml 321 | shell: bash 322 | working-directory: "${{ env.test_results_location }}" 323 | - name: Build release 324 | run: cmake --build . --config Release 325 | working-directory: "${{ github.workspace }}/build" 326 | - name: Run tests release 327 | continue-on-error: true 328 | timeout-minutes: 5 329 | run: ctest -C Release 330 | working-directory: "${{ github.workspace }}/build" 331 | - name: Publish test results 332 | uses: EnricoMi/publish-unit-test-result-action/windows@v2 333 | with: 334 | check_name: >- 335 | Tests (Windows ${{ matrix.arch }} ${{ matrix.lib_type }} ISPC:${{ matrix.ispc }} Release) 336 | junit_files: "${{ env.test_results_location }}/*.xml" 337 | - name: Package artifact 338 | if: endsWith(matrix.artifact, '-full') 339 | # Full package with debug and release. 340 | run: |- 341 | cmake --build . --config Debug --target install 342 | cmake --build . --config Release --target install 343 | 7z a -tzip cuttlefish-${{ matrix.artifact }}.zip cuttlefish 344 | working-directory: "${{ github.workspace }}/build" 345 | - name: Package artifact 346 | if: endsWith(matrix.artifact, '-tool') 347 | # Only tool and supplemental DLLs. 348 | run: |- 349 | cmake --build . --config Release --target install 350 | mv cuttlefish cuttlefish-full 351 | mv cuttlefish-full\bin cuttlefish 352 | 7z a -tzip cuttlefish-${{ matrix.artifact }}.zip cuttlefish 353 | working-directory: "${{ github.workspace }}/build" 354 | - name: Publish artifact 355 | if: matrix.artifact != '' 356 | uses: actions/upload-artifact@v4 357 | with: 358 | name: "${{ matrix.artifact }}" 359 | path: "${{ github.workspace }}/build/cuttlefish-${{ matrix.artifact }}.zip" 360 | 361 | # vim: ts=2 sts=2 sw=2 et 362 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | .DS_Store 3 | .cmake 4 | .idea 5 | .vscode 6 | *.swp 7 | *.user 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/FreeImage"] 2 | path = lib/FreeImage 3 | url = https://github.com/akb825/freeimage.git 4 | shallow = true 5 | [submodule "lib/etc2comp"] 6 | path = lib/etc2comp 7 | url = https://github.com/akb825/etc2comp.git 8 | shallow = true 9 | [submodule "lib/astc-encoder"] 10 | path = lib/astc-encoder 11 | url = https://github.com/ARM-software/astc-encoder.git 12 | shallow = true 13 | [submodule "lib/glm"] 14 | path = lib/glm 15 | url = https://github.com/g-truc/glm.git 16 | shallow = true 17 | [submodule "lib/PVRTexToolLib"] 18 | path = lib/PVRTexToolLib 19 | url = https://github.com/akb825/PVRTexToolLib.git 20 | shallow = true 21 | [submodule "lib/bc7enc_rdo"] 22 | path = lib/bc7enc_rdo 23 | url = https://github.com/richgel999/bc7enc_rdo.git 24 | shallow = true 25 | [submodule "lib/compressonator"] 26 | path = lib/compressonator 27 | url = https://github.com/GPUOpen-Tools/compressonator.git 28 | shallow = true 29 | [submodule "lib/ISPCTextureCompressor"] 30 | path = lib/ISPCTextureCompressor 31 | url = https://github.com/GameTechDev/ISPCTextureCompressor.git 32 | shallow = true 33 | [submodule "lib/libsquish"] 34 | path = lib/libsquish 35 | url = https://github.com/akb825/libsquish.git 36 | shallow = true 37 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (WIN32) 2 | # Needs 3.15 for CMAKE_MSVC_RUNTIME_LIBRARY. 3 | cmake_minimum_required(VERSION 3.15) 4 | else() 5 | cmake_minimum_required(VERSION 3.10) 6 | endif() 7 | if (POLICY CMP0144) 8 | cmake_policy(SET CMP0144 NEW) 9 | endif() 10 | project(Cuttlefish) 11 | 12 | # Build options 13 | # Use if since BUILD_SHARED_LIBS defaults to unset. 14 | if (BUILD_SHARED_LIBS) 15 | set(CUTTLEFISH_SHARED_DEFAULT ON) 16 | else() 17 | set(CUTTLEFISH_SHARED_DEFAULT OFF) 18 | endif() 19 | set(CUTTLEFISH_SHARED ${CUTTLEFISH_SHARED_DEFAULT} CACHE BOOL 20 | "Build Cuttlefish using shared libraries.") 21 | set(CUTTLEFISH_STATIC_RUNTIME OFF CACHE BOOL "Use static runtime library on Windows.") 22 | 23 | # Options for disabling portions of the build. 24 | set(CUTTLEFISH_BUILD_TESTS ON CACHE BOOL "Build unit tests.") 25 | set(CUTTLEFISH_BUILD_DOCS ON CACHE BOOL "Build documentation.") 26 | set(CUTTLEFISH_BUILD_TOOL ON CACHE BOOL "Build the tool.") 27 | set(CUTTLEFISH_FORCE_INTERNAL_FREEIMAGE OFF CACHE BOOL "Force building FreeImage internally.") 28 | set(CUTTLEFISH_BUILD_S3TC ON CACHE BOOL "Build support for S3TC texture compression.") 29 | set(CUTTLEFISH_BUILD_ETC ON CACHE BOOL "Build support for ETC texture compression.") 30 | set(CUTTLEFISH_BUILD_ASTC ON CACHE BOOL "Build support for ASTC texture compression.") 31 | set(CUTTLEFISH_BUILD_PVRTC ON CACHE BOOL "Build support for PVRTC texture compression.") 32 | 33 | # Misc options. 34 | set(CUTTLEFISH_ISPC_PATH "" CACHE PATH "Path to the ISPC tool.") 35 | set(CUTTLEFISH_OUTPUT_DIR ${CMAKE_BINARY_DIR}/output CACHE PATH 36 | "Folder for placing the build output.") 37 | set(CUTTLEFISH_EXPORTS_DIR ${CMAKE_BINARY_DIR}/cmake CACHE PATH 38 | "Folder for placing the cmake exports while building. Useful when embedding in other projects.") 39 | set(CUTTLEFISH_ROOT_FOLDER Cuttlefish CACHE STRING 40 | "Root folder for the Cuttlefish projects. Usefull when embedding in other projects.") 41 | set(CUTTLEFISH_INSTALL ON CACHE BOOL "Allow installation for Cuttlefish components.") 42 | set(CUTTLEFISH_INSTALL_PVRTEXLIB ON CACHE BOOL 43 | "Include the PVRTexTool library with the installation.") 44 | set(CUTTLEFISH_INSTALL_SET_RPATH ON CACHE BOOL "Set rpath for library and tool on installation.") 45 | 46 | if (APPLE AND NOT CMAKE_OSX_DEPLOYMENT_TARGET) 47 | # PVRTexTool library requires 10.14 or higher. 48 | set(CMAKE_OSX_DEPLOYMENT_TARGET 10.14 CACHE STRING "Minimum macOS deployment version." FORCE) 49 | endif() 50 | 51 | if (CUTTLEFISH_BUILD_TESTS) 52 | find_package(GTest QUIET) 53 | if (NOT GTEST_FOUND) 54 | message("GTest not installed. Skipping tests.") 55 | endif() 56 | endif() 57 | 58 | if (CUTTLEFISH_BUILD_DOCS) 59 | find_package(Doxygen QUIET) 60 | if (NOT DOXYGEN_FOUND) 61 | message("Doxygon not installed. Skipping documentation.") 62 | endif() 63 | endif() 64 | 65 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 66 | 67 | if (CUTTLEFISH_SHARED) 68 | set(CUTTLEFISH_LIB SHARED) 69 | else() 70 | set(CUTTLEFISH_LIB STATIC) 71 | endif() 72 | 73 | if (CUTTLEFISH_OUTPUT_DIR) 74 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CUTTLEFISH_OUTPUT_DIR}) 75 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CUTTLEFISH_OUTPUT_DIR}) 76 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CUTTLEFISH_OUTPUT_DIR}) 77 | endif() 78 | 79 | set(CUTTLEFISH_MAJOR_VERSION 2) 80 | set(CUTTLEFISH_MINOR_VERSION 9) 81 | set(CUTTLEFISH_PATCH_VERSION 0) 82 | set(CUTTLEFISH_VERSION ${CUTTLEFISH_MAJOR_VERSION}.${CUTTLEFISH_MINOR_VERSION}.${CUTTLEFISH_PATCH_VERSION}) 83 | 84 | set(CUTTLEFISH_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) 85 | 86 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) 87 | include(cmake/config.cmake) 88 | include(cmake/install.cmake) 89 | 90 | add_subdirectory(lib) 91 | if (CUTTLEFISH_BUILD_TOOL) 92 | add_subdirectory(tool) 93 | endif() 94 | 95 | if (CUTTLEFISH_INSTALL) 96 | include(CMakePackageConfigHelpers) 97 | set(versionPath ${CUTTLEFISH_EXPORTS_DIR}/CuttlefishConfigVersion.cmake) 98 | write_basic_package_version_file(${versionPath} 99 | VERSION ${CUTTLEFISH_VERSION} 100 | COMPATIBILITY SameMajorVersion) 101 | 102 | set(configPath ${CUTTLEFISH_EXPORTS_DIR}/CuttlefishConfig.cmake) 103 | set(configLines 104 | "include(\${CMAKE_CURRENT_LIST_DIR}/cuttlefish_lib-targets.cmake)\n" 105 | "set(Cuttlefish_LIBRARIES Cuttlefish::lib)\n" 106 | "get_target_property(Cuttlefish_INCLUDE_DIRS Cuttlefish::lib INTERFACE_INCLUDE_DIRECTORIES)\n") 107 | if (CUTTLEFISH_BUILD_TOOL) 108 | list(APPEND configLines "include(\${CMAKE_CURRENT_LIST_DIR}/cuttlefish-targets.cmake)\n") 109 | endif() 110 | file(WRITE ${configPath} ${configLines}) 111 | 112 | install(FILES ${configPath} ${versionPath} DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Cuttlefish 113 | COMPONENT dev) 114 | endif() 115 | 116 | # Documentation. (populated by built libraries) 117 | add_subdirectory(doc) 118 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | [![Cuttlefish](https://github.com/akb825/Cuttlefish/actions/workflows/main.yml/badge.svg)](https://github.com/akb825/Cuttlefish/actions/workflows/main.yml) 4 | 5 | Cuttlefish is a texture conversion library and tool. A command line tool is provided for most texture conversion needs, such as running manually or part of an asset conversion pipeline. The library may be used to integrate more advanced texture generation within other software. 6 | 7 | Features include: 8 | 9 | * Load almost any image format. ([all supported by FreeImage](http://freeimage.sourceforge.net/features.html)) 10 | * Perform simple operations on input images such as resizing, flipping and rotating, and generating normalmaps. 11 | * Create 1D, 2D, 3D, and cube textures, as well as texture arrays. 12 | * Generate mipmaps. 13 | * Convert to most formats supported by the GPU, including: 14 | * Most standard uncompressed formats. (normalized integers, non-normalized integers, floating point, etc.) 15 | * S3TC formats (BC/DXT) 16 | * ETC 17 | * ASTC 18 | * PVRTC 19 | * Save the output texture in DDS, KTX, or PVR format. 20 | 21 | # Dependencies 22 | 23 | The following software is required to build Cuttlefish: 24 | 25 | * [CMake](https://cmake.org/) 3.10 or later 26 | * [FreeImage](http://freeimage.sourceforge.net/) (required, included as a submodule) 27 | * [GLM](https://glm.g-truc.net/0.9.9/index.html) (required, included as a submodule) 28 | * [squish](https://sourceforge.net/projects/libsquish/) (optional, included as a submodule) 29 | * [Compressonator](https://github.com/GPUOpen-Tools/compressonator) (optional, included as a submodule) 30 | * [bc7enc_rdo](https://github.com/richgel999/bc7enc_rdo) (optional, included as a submodule) 31 | * [ispc_texcomp](https://github.com/GameTechDev/ISPCTextureCompressor) (optional, included as a submodule) 32 | * [etc2comp](https://github.com/google/etc2comp) (optional, included as a submodule) 33 | * [astc-encoder](https://github.com/ARM-software/astc-encoder) (optional, included as a submodule) 34 | * [PVRTexTool](https://developer.imaginationtech.com/pvrtextool/) (optional, included as a submodule) 35 | * [doxygen](https://doxygen.nl/) (optional) 36 | * [gtest](https://github.com/google/googletest) (optional) 37 | 38 | The submodules can be downloaded by running the command 39 | 40 | Cuttlefish$ git submodule update --init 41 | 42 | When using the BC6H and BC7 encoders, it's highly recommended to install the [ISPC](https://ispc.github.io) compiler. This will use higher quality encoders that are also faster compared to the fallback used when ISP isn't available. 43 | 44 | > **Note:** Use the `CUTTLEFISH_ISPC_PATH` CMake variable when ISPC isn't visible on the system `PATH`. 45 | 46 | > **Note:** As of this writing, the last release of PVRTexTool that supports macOS is the 2023r2 release, with both releases in 2024 not having downloads available for macOS. For now, the 2023r2 release will continue to be used to support PVRTC formats across all major platforms until either it breaks or releases resume on macOS. 47 | 48 | # Platforms 49 | 50 | Cuttlefish has been built for and tested on the following platforms: 51 | 52 | * Linux (GCC and LLVM clang) 53 | * Windows (requires Visual Studio 2015 or later) 54 | * macOS 55 | 56 | # Building and Installing 57 | 58 | [CMake](https://cmake.org/) is used as the build system. The way to invoke CMake differs for different platforms. 59 | 60 | ## Linux/macOS 61 | 62 | To create a release build, execute the following commands: 63 | 64 | Cuttlefish$ mkdir build 65 | Cuttlefish$ cd build 66 | Cuttlefish/build$ cmake .. -DCMAKE_BUILD_TYPE=Release 67 | Cuttlefish/build$ make 68 | 69 | The tests can be run by running the command: 70 | 71 | Cuttlefish/build$ ctest 72 | 73 | The library and tool may then be installed by running the command: 74 | 75 | Cuttlefish/build$ sudo make install 76 | 77 | ### macOS Universal Binary 78 | 79 | To create a universal binary on macOS, set the `CMAKE_OSX_ARCHITECTURES` variable. For example, for an x86_64/arm64 universal binary, add the argument `-DCMAKE_OSX_ARCHITECTURES=arm64;x86_64`. Building a universal binary requires creating an Xcode project with the `-GXcode` argument to CMake. 80 | 81 | > **Note:** when installing ISPC through homebrew not all targets are supported. For example, when installing on an ARM CPU (e.g. M1), only ARM targets are supported. Download from the [ISPC website](https://ispc.github.io) to get a version that supports both x86 and ARM targets. 82 | 83 | > **Note:** when creating an install package using shared libraries with Xcode, add the `-DCMAKE_BUILD_WITH_INSTALL_RPATH=ON` option when running CMake. This avoids CMake breaking the code signature, which will make the executable unrunnable. The executables won't run in the build directory, however. 84 | 85 | ## Windows 86 | 87 | Building is generally performed through Visual Studio. This can either be done through the CMake GUI tool or on the command line. To generate Visual Studio 2017 projects from the command line, you can run the commands: 88 | 89 | Cuttlefish$ mkdir build 90 | Cuttlefish$ cd build 91 | Cuttlefish\build$ cmake .. -G "Visual Studio 15 2017 Win64" 92 | 93 | Once generated, the project may be opened through Visual Studio and built as normal. The `RUN_TESTS` project may be built in order to run the tests. 94 | 95 | In order to install the libraries and tool, run Visual Studio as administrator, perform a release build, and run the `INSTALL` project. The default installation location is `C:\Program Files\Cuttlefish`. After installation, it's recommended to place the `C:\Program Files\Cuttlefish\bin` folder on your `PATH` environment variable to run the `cuttlefish` tool from the command line. 96 | 97 | ## Options 98 | 99 | The following options may be used when running cmake: 100 | 101 | ### Compile Options: 102 | 103 | * `-DCMAKE_BUILD_TYPE=Debug|Release`: Building in `Debug` or `Release`. This should always be specified. 104 | * `-DCMAKE_INSTALL_PREFIX=path`: Sets the path to install to when running `make install`. 105 | * `-DCUTTLEFISH_SHARED=ON|OFF`: Set to `ON` to build with shared libraries, `OFF` to build with static libraries. TDefault is the value of `BUILD_SHARED_LIBS` or `OFF`. 106 | * `-DCUTTLEFISH_ISPC_PATH=path`: The path to the ISPC compiler. If unset, ispc will be searched in the `PATH` or default instal location. 107 | * `-DCUTTLEFISH_STATIC_RUNTIME=ON|OFF`: Set to `ON` to use the static runtime library on Windows. When `OFF`, it will respect the existing value of `CMAKE_MSVC_RUNTIME_LIBRARY`, or use dynamic runtime if otherwise unset. It is not recommended to set this to `ON` when `CUTTLEFISH_SHARED` is also `ON`. Default is `OFF`. 108 | 109 | ### Enabled Builds 110 | 111 | * `-DCUTTLEFISH_BUILD_TESTS=ON|OFF`: Set to `ON` to build the unit tests. `gtest` must also be found in order to build the unit tests. Defaults to `ON`. 112 | * `-DCUTTLEFISH_BUILD_DOCS=ON|OFF`: Set to `ON` to build the documentation. `doxygen` must also be found in order to build the documentation. Defaults to `ON`. 113 | * `-DCUTTLEFISH_BUILD_TOOL=ON|OFF`: Set to `ON` to build the tool. Defaults to `ON`. 114 | * `-DCUTTLEFISH_FORCE_INTERNAL_FREEIMAGE=ON|OFF`: Set to `ON` to force building FreeImage internally even if a system version is found. Defaults to `OFF`. 115 | * `-DCUTTLEFISH_BUILD_S3TC=ON|OFF`: Set to `ON` to build S3TC texture compression support. Defaults to `ON`. 116 | * `-DCUTTLEFISH_BUILD_ETC=ON|OFF`: Set to `ON` to build ETC texture compression support. Defaults to `ON`. 117 | * `-DCUTTLEFISH_BUILD_ASTC=ON|OFF`: Set to `ON` to build ASTC texture compression support. Defaults to `ON`. 118 | * `-DCUTTLEFISH_BUILD_PVRTC=ON|OFF`: Set to `ON` to build PVRTC texture compression support. Defaults to `ON`. If the PVRTexTool library isn't found, support will be disabled. 119 | 120 | ### Miscellaneous Options: 121 | 122 | * `-DCUTTLEFISH_OUTPUT_DIR=directory`: The folder to place the output files. This may be explicitly left empty in order to output to the defaults used by cmake, but this may prevent tests and executables from running properly when `CUTTLEFISH_SHARED` is set to `ON`. Defaults to `${CMAKE_BINARY_DIR}/output`. 123 | * `-DCUTTLEFISH_EXPORTS_DIR=directory`: The folder to place the cmake exports when building. This directory can be added to the module path when embedding in other projects to be able to use the `library_find()` cmake function. Defaults to `${CMAKE_BINARY_DIR}/cmake`. 124 | * `-DCUTTLEFISH_ROOT_FOLDER=folder`: The root folder for the projects in IDEs that support them. (e.g. Visual Studio or XCode) This is useful if embedding Cuttlefish in another project. Defaults to Cuttlefish. 125 | * `-DCUTTLEFISH_INSTALL=ON|OFF`: Allow installation for Cuttlefish components. This can be useful when embedding in other projects to prevent installations from including Cuttlefish. For example, when statically linking into a shared library. Default is `ON`. 126 | * `-DCUTTLEFISH_INSTALL_PVRTEXLIB=ON|OFF`: Include the PVRTextTool library with the installation. This allows the installation to be used for machines that don't have PVRTexTool installed, and can avoid adjusting the `PATH` environment variable on some platforms. Default is `ON`. 127 | * `-DCUTTLEFISH_INSTALL_SET_RPATH=ON|OFF`: Set rpath during install for the library and tool on installation. Set to `OFF` if including in another project that wants to control the rpath. Default is `ON`. 128 | * `-DPVRTEXLIB_ROOT=directory`: The location of the PVRTexTool library platform subdirectories. If the PVRTexTool library is not installed to the standard location on this machine, this variable can be set to tell CMake where to look for the library. The given folder must contain a subdirectory for the current platform (one of `OSX_x86`, `Linux_x86_64`, `Linux_x86_32`, `Windows_x86_64`, or `Windows_x86_32`) that contains the library files. 129 | * `-DCMAKE_OSX_DEPLOYMENT_TARGET=version`: Minimum version of macOS to target when building for Mac. Defaults to 10.14, which is the minimum required for the PVRTexTool library. 130 | 131 | Once you have built and installed Cuttlefish, you can find the library by calling `find_package(Cuttlefish)` within your CMake files. You can either link to the `Cuttlefish::lib` target or use the `Cuttlefish_LIBRARIES` and `Cuttlefish_INCLUDE_DIRS` CMake variables. The `Cuttlefish::tool` target may also be used for the tool executable. 132 | 133 | > **Note:** In order for `find_package()` to succeed, you will need to add the base installation path to `CMAKE_PREFIX_PATH`. 134 | 135 | # Limitations 136 | 137 | ## Texture file format limitations 138 | 139 | Some texture file formats have limitations for what texture formats are used. The following formats are *not* supported: 140 | 141 | * DDS 142 | * R4G4B4A4 143 | * B4G4R4A4 144 | * B5G6R5 145 | * R5G5B5A1 146 | * B5G5R5A1 147 | * R8G8B8 148 | * B8G8R8 149 | * A8B8G8R8 150 | * A2R10G10B10 151 | * R16G16B16 152 | * ETC/EAC compressed formats 153 | * ASTC compressed formats 154 | * PVRTC compressed formats 155 | * KTX 156 | * R4G4 157 | * A4R4G4B4 158 | * B8G8R8 159 | * PVR 160 | * All formats supported 161 | 162 | ## Custom PVR metadata 163 | 164 | Metadata is used to enhance the PVR file format to provide information beyond what is supported by the base format. The metadata values are: 165 | 166 | * **FourCC:** FOURCC('C', 'T', 'F', 'S') 167 | * **Key:** one of the following: 168 | * FOURCC('B', 'C', '1', 'A'): set for BC1_RGBA format. (i.e. BC1 with 1-bit alpha) 169 | * FOURCC('B', 'C', '1', 0): set for BC1_RGB format. (i.e. BC1 with no alpha) 170 | * FOURCC('A', 'R', 'R', 'Y'): set for texture arrays. This can be used to differentiate between a non-array texture and an array with 1 element. 171 | * FOURCC('D', 'I', 'M', '1'): set for 1D textures. 172 | * **Data Size:**: 4 173 | * **Data:** `(uint32_t)0` 174 | 175 | A dummy data field of 4 bytes is used because the PVRTexTool GUI tool will crash with a data size of 0. 176 | 177 | # Further Documentation 178 | 179 | * [Library](lib/README.md) 180 | * [Tool](tool/README.md) 181 | -------------------------------------------------------------------------------- /cmake/FindFreeImage.cmake: -------------------------------------------------------------------------------- 1 | # Find FreeImage includes and library 2 | # 3 | # This module defines 4 | # FreeImage_INCLUDE_DIRS 5 | # FreeImage_LIBRARIES, the libraries to link against to use FreeImage. 6 | # FreeImage_LIBRARY_DIRS, the location of the libraries 7 | # FreeImage_FOUND, If false, do not try to use FreeImage 8 | # 9 | # Copyright © 2007, Matt Williams 10 | # 11 | # Redistribution and use is allowed according to the terms of the BSD license. 12 | # For details see the accompanying COPYING-CMAKE-SCRIPTS file. 13 | 14 | IF (FreeImage_LIBRARIES AND FreeImage_INCLUDE_DIRS) 15 | SET(FreeImage_FIND_QUIETLY TRUE) # Already in cache, be silent 16 | ELSE(FreeImage_LIBRARIES AND FreeImage_INCLUDE_DIRS) 17 | MESSAGE(STATUS "Looking for FreeImage") 18 | ENDIF (FreeImage_LIBRARIES AND FreeImage_INCLUDE_DIRS) 19 | 20 | SET(FreeImage_INCLUDE_SEARCH_DIRS 21 | ${FreeImage_INCLUDE_SEARCH_DIRS} 22 | ${CMAKE_INCLUDE_PATH} 23 | /usr/include 24 | /usr/local/include 25 | /opt/include 26 | /opt/freeimage/include 27 | ) 28 | 29 | SET(FreeImage_LIBRARY_SEARCH_DIRS 30 | ${FreeImage_LIBRARY_SEARCH_DIRS} 31 | ${CMAKE_LIBRARY_PATH} 32 | /usr/lib 33 | /usr/local/lib 34 | /opt/lib 35 | /opt/freeimage/lib 36 | ) 37 | 38 | FIND_PATH(FreeImage_INCLUDE_DIRS FreeImage.h ${FreeImage_INCLUDE_SEARCH_DIRS}) 39 | FIND_LIBRARY(FreeImage_LIBRARIES freeimage PATHS ${FreeImage_LIBRARY_SEARCH_DIRS}) 40 | 41 | #Do some preparation 42 | SEPARATE_ARGUMENTS(FreeImage_INCLUDE_DIRS) 43 | SEPARATE_ARGUMENTS(FreeImage_LIBRARIES) 44 | 45 | MARK_AS_ADVANCED(FreeImage_INCLUDE_DIRS FreeImage_LIBRARIES FreeImage_LIBRARY_DIRS) 46 | 47 | IF (FreeImage_INCLUDE_DIRS AND FreeImage_LIBRARIES) 48 | SET(FreeImage_FOUND TRUE) 49 | ENDIF (FreeImage_INCLUDE_DIRS AND FreeImage_LIBRARIES) 50 | 51 | IF (FreeImage_FOUND) 52 | IF (NOT FreeImage_FIND_QUIETLY) 53 | MESSAGE(STATUS " libraries : ${FreeImage_LIBRARIES} from ${FreeImage_LIBRARY_DIRS}") 54 | MESSAGE(STATUS " includes : ${FreeImage_INCLUDE_DIRS}") 55 | ENDIF (NOT FreeImage_FIND_QUIETLY) 56 | ELSE (FreeImage_FOUND) 57 | IF (FreeImage_FIND_REQUIRED) 58 | MESSAGE(FATAL_ERROR "Could not find FreeImage") 59 | ENDIF (FreeImage_FIND_REQUIRED) 60 | ENDIF (FreeImage_FOUND) 61 | 62 | -------------------------------------------------------------------------------- /cmake/FindPVRTexLib.cmake: -------------------------------------------------------------------------------- 1 | # Copyright 2016 The University of North Carolina at Chapel Hill 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | # Please send all BUG REPORTS to . 16 | # 17 | 18 | # - Try to find libPVRTexLib 19 | # Once done this will define 20 | # PVRTEXLIB_FOUND - System has PVRTexLib 21 | # PVRTEXLIB_INCLUDE_DIRS - The PVRTexLib include directories 22 | # PVRTEXLIB_LIBRARIES - The libraries needed to use PVRTexLib 23 | 24 | SET( PVRTEXLIB_ROOT "${CMAKE_CURRENT_LIST_DIR}/../lib/PVRTexToolLib" 25 | CACHE PATH "Location of the PVRTexTool library platform subdirectories" ) 26 | find_path( 27 | PVRTEXLIB_INCLUDE_DIR PVRTexLib.h 28 | PATHS ${PVRTEXLIB_ROOT}/Include 29 | ) 30 | 31 | IF (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 32 | find_library(PVRTEXLIB_LIB PVRTexLib 33 | PATHS ${PVRTEXLIB_ROOT}/macOS 34 | NO_DEFAULT_PATH 35 | ) 36 | 37 | SET( USE_PTHREAD TRUE ) 38 | 39 | ELSEIF (${CMAKE_SYSTEM_NAME} MATCHES "Linux") 40 | IF (${CUTTLEFISH_ARCH} STREQUAL "x86_64") 41 | find_library(PVRTEXLIB_LIB PVRTexLib 42 | PATHS ${PVRTEXLIB_ROOT}/Linux_x86_64 43 | NO_DEFAULT_PATH 44 | ) 45 | ELSEIF (${CUTTLEFISH_ARCH} STREQUAL "x86") 46 | find_library(PVRTEXLIB_LIB PVRTexLib 47 | PATHS ${PVRTEXLIB_ROOT}/Linux_x86_32 48 | NO_DEFAULT_PATH 49 | ) 50 | ELSEIF (${CUTTLEFISH_ARCH} STREQUAL "arm") 51 | # Assume anybody running this will have hardware float support. 52 | find_library(PVRTEXLIB_LIB PVRTexLib 53 | PATHS ${PVRTEXLIB_ROOT}/Linux_armv7hf 54 | NO_DEFAULT_PATH 55 | ) 56 | ELSEIF (${CUTTLEFISH_ARCH} STREQUAL "arm64") 57 | find_library(PVRTEXLIB_LIB PVRTexLib 58 | PATHS ${PVRTEXLIB_ROOT}/Linux_armv8_64 59 | NO_DEFAULT_PATH 60 | ) 61 | ELSEIF (${CUTTLEFISH_ARCH} STREQUAL "riscv64") 62 | find_library(PVRTEXLIB_LIB PVRTexLib 63 | PATHS ${PVRTEXLIB_ROOT}/Linux_riscv64 64 | NO_DEFAULT_PATH 65 | ) 66 | ENDIF() 67 | 68 | SET( USE_PTHREAD TRUE ) 69 | 70 | ELSEIF(MSVC) 71 | IF (${CUTTLEFISH_ARCH} STREQUAL "x86_64") 72 | find_library(PVRTEXLIB_LIB PVRTexLib 73 | PATHS ${PVRTEXLIB_ROOT}/Windows_x86_64 74 | NO_DEFAULT_PATH 75 | ) 76 | ELSEIF (${CUTTLEFISH_ARCH} STREQUAL "x86") 77 | find_library(PVRTEXLIB_LIB PVRTexLib 78 | PATHS ${PVRTEXLIB_ROOT}/Windows_x86_32 79 | NO_DEFAULT_PATH 80 | ) 81 | ENDIF() 82 | ENDIF() 83 | 84 | IF( USE_PTHREAD ) 85 | FIND_PACKAGE( Threads REQUIRED ) 86 | set(PVRTEXLIB_LIBRARIES 87 | ${PVRTEXLIB_LIB} 88 | ${CMAKE_THREAD_LIBS_INIT} 89 | ) 90 | ELSE() 91 | set(PVRTEXLIB_LIBRARIES ${PVRTEXLIB_LIB}) 92 | ENDIF() 93 | set(PVRTEXLIB_INCLUDE_DIRS ${PVRTEXLIB_INCLUDE_DIR} ) 94 | 95 | include(FindPackageHandleStandardArgs) 96 | # handle the QUIETLY and REQUIRED arguments and set LIBXML2_FOUND to TRUE 97 | # if all listed variables are TRUE 98 | find_package_handle_standard_args(PVRTexLib DEFAULT_MSG 99 | PVRTEXLIB_LIB PVRTEXLIB_INCLUDE_DIR) 100 | 101 | mark_as_advanced( PVRTEXLIB_ROOT PVRTEXLIB_INCLUDE_DIR PVRTEXLIB_LIB ) 102 | -------------------------------------------------------------------------------- /cmake/config.cmake: -------------------------------------------------------------------------------- 1 | # Copyright 2017-2024 Aaron Barany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | include(GNUInstallDirs) 16 | 17 | # Code should compile with C++11, but set to 14 for dependencies. Compiling on older targets will 18 | # fall back to the latest version. 19 | set(CMAKE_CXX_STANDARD 14) 20 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 21 | 22 | if (APPLE AND CMAKE_OSX_ARCHITECTURES) 23 | list(LENGTH CMAKE_OSX_ARCHITECTURES architectureCount) 24 | if (architectureCount EQUAL 1) 25 | set(CUTTLEFISH_ARCH ${CMAKE_OSX_ARCHITECTURES}) 26 | else() 27 | set(CUTTLEFISH_ARCH multi) 28 | endif() 29 | else() 30 | set(CUTTLEFISH_ARCH ${CMAKE_SYSTEM_PROCESSOR}) 31 | endif() 32 | 33 | if (CUTTLEFISH_ARCH MATCHES "^x86" OR CUTTLEFISH_ARCH STREQUAL "amd64" OR 34 | CUTTLEFISH_ARCH STREQUAL "AMD64" OR CUTTLEFISH_ARCH STREQUAL "i386" OR 35 | CUTTLEFISH_ARCH STREQUAL "i686") 36 | if (CMAKE_SIZEOF_VOID_P EQUAL 8) 37 | set(CUTTLEFISH_ARCH x86_64) 38 | else() 39 | set(CUTTLEFISH_ARCH x86) 40 | endif() 41 | elseif (CUTTLEFISH_ARCH MATCHES "^arm" OR CUTTLEFISH_ARCH STREQUAL "aarch64") 42 | if (CMAKE_SIZEOF_VOID_P EQUAL 8) 43 | set(CUTTLEFISH_ARCH arm64) 44 | else() 45 | set(CUTTLEFISH_ARCH arm) 46 | endif() 47 | endif() 48 | 49 | if (MSVC) 50 | if (CUTTLEFISH_STATIC_RUNTIME) 51 | set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") 52 | if (CUTTLEFISH_SHARED) 53 | message(WARNING 54 | "It is not recommended to have CUTTLEFISH_SHARED and CUTTLEFISH_STATIC_RUNTIME both set to ON.") 55 | endif() 56 | endif() 57 | add_compile_options(/W3 /WX /wd4200 /MP) 58 | if (CUTTLEFISH_ARCH STREQUAL "x86") 59 | add_compile_options(/arch:SSE2) 60 | endif() 61 | add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_WARNINGS) 62 | else() 63 | add_compile_options(-Wall -Werror -Wconversion -Wno-sign-conversion -fno-strict-aliasing) 64 | if (CUTTLEFISH_ARCH STREQUAL "x86") 65 | add_compile_options(-msse2) 66 | endif() 67 | if (CMAKE_C_COMPILER_ID MATCHES "GNU") 68 | add_compile_options(-Wno-comment) 69 | if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 7.0) 70 | add_compile_options(-faligned-new) 71 | endif() 72 | elseif (CMAKE_C_COMPILER_ID MATCHES "Clang" AND 73 | CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 5.0) 74 | add_compile_options(-faligned-new) 75 | endif() 76 | # Behavior for VISIBILITY_PRESET variables are inconsistent between CMake versions. 77 | if (CUTTLEFISH_SHARED) 78 | add_compile_options(-fvisibility=hidden) 79 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility-inlines-hidden") 80 | endif() 81 | endif() 82 | 83 | enable_testing() 84 | 85 | if (CUTTLEFISH_INSTALL AND CUTTLEFISH_INSTALL_SET_RPATH) 86 | if (APPLE) 87 | set(CMAKE_INSTALL_RPATH "@executable_path;@executable_path/../${CMAKE_INSTALL_LIBDIR}") 88 | else() 89 | set(CMAKE_INSTALL_RPATH "$ORIGIN;$ORIGIN/../${CMAKE_INSTALL_LIBDIR}") 90 | endif() 91 | endif() 92 | 93 | function(cfs_set_folder target) 94 | set_property(TARGET ${target} PROPERTY FOLDER ${CUTTLEFISH_ROOT_FOLDER}) 95 | endfunction() 96 | 97 | function(cfs_setup_filters) 98 | set(options) 99 | set(oneValueArgs SRC_DIR INCLUDE_DIR) 100 | set(multiValueArgs FILES) 101 | cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 102 | 103 | foreach (fileName ${ARGS_FILES}) 104 | # Get the directory name. Make sure there's a trailing /. 105 | get_filename_component(directoryName ${fileName} DIRECTORY) 106 | set(directoryName ${directoryName}/) 107 | 108 | set(filterName) 109 | string(REGEX MATCH ${ARGS_SRC_DIR}/.* matchSrc ${directoryName}) 110 | string(REGEX MATCH ${ARGS_INCLUDE_DIR}/.* matchInclude ${directoryName}) 111 | 112 | if (matchSrc) 113 | string(REPLACE ${ARGS_SRC_DIR}/ "" filterName ${directoryName}) 114 | set(filterName src/${filterName}) 115 | elseif (matchInclude) 116 | string(REPLACE ${ARGS_INCLUDE_DIR}/ "" filterName ${directoryName}) 117 | set(filterName include/${filterName}) 118 | endif() 119 | 120 | if (filterName) 121 | string(REPLACE "/" "\\" filterName ${filterName}) 122 | source_group(${filterName} FILES ${fileName}) 123 | endif() 124 | endforeach() 125 | endfunction() 126 | -------------------------------------------------------------------------------- /cmake/install.cmake: -------------------------------------------------------------------------------- 1 | # Copyright 2017-2022 Aaron Barany 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | function(cfs_install_library target name) 16 | set_target_properties(${target} PROPERTIES 17 | VERSION ${CUTTLEFISH_VERSION} 18 | DEBUG_POSTFIX d 19 | EXPORT_NAME ${name} 20 | SOVERSION ${CUTTLEFISH_MAJOR_VERSION}.${CUTTLEFISH_MINOR_VERSION}) 21 | add_library(Cuttlefish::${name} ALIAS ${target}) 22 | 23 | set(interfaceIncludes 24 | $ 25 | $) 26 | set_property(TARGET ${target} APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES 27 | ${interfaceIncludes}) 28 | 29 | set(exportPath ${CMAKE_CURRENT_BINARY_DIR}/include/cuttlefish/Export.h) 30 | set_property(TARGET ${target} APPEND PROPERTY INCLUDE_DIRECTORIES 31 | ${CMAKE_CURRENT_BINARY_DIR}/include ${interfaceIncludes}) 32 | if (CUTTLEFISH_SHARED) 33 | if (MSVC) 34 | set_property(TARGET ${target} APPEND PROPERTY COMPILE_DEFINITIONS 35 | CUTTLEFISH_BUILD) 36 | configure_file(${CUTTLEFISH_SOURCE_DIR}/cmake/templates/WindowsExport.h ${exportPath} 37 | COPYONLY) 38 | else() 39 | configure_file(${CUTTLEFISH_SOURCE_DIR}/cmake/templates/UnixExport.h ${exportPath} 40 | COPYONLY) 41 | endif() 42 | else() 43 | configure_file(${CUTTLEFISH_SOURCE_DIR}/cmake/templates/NoExport.h ${exportPath} COPYONLY) 44 | endif() 45 | 46 | if (NOT CUTTLEFISH_INSTALL) 47 | return() 48 | endif() 49 | 50 | install(TARGETS ${target} EXPORT ${target}Targets 51 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 52 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 53 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 54 | INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 55 | install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 56 | COMPONENT dev) 57 | install(FILES ${exportPath} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/cuttlefish COMPONENT dev) 58 | 59 | export(EXPORT ${target}Targets FILE ${CUTTLEFISH_EXPORTS_DIR}/${target}-targets.cmake) 60 | install(EXPORT ${target}Targets NAMESPACE Cuttlefish:: FILE ${target}-targets.cmake 61 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Cuttlefish) 62 | endfunction() 63 | 64 | function(cfs_install_executable target name) 65 | set_target_properties(${target} PROPERTIES EXPORT_NAME ${name}) 66 | add_executable(Cuttlefish::${name} ALIAS ${target}) 67 | 68 | if (NOT CUTTLEFISH_INSTALL) 69 | return() 70 | endif() 71 | 72 | install(TARGETS ${target} EXPORT ${target}Targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) 73 | export(EXPORT ${target}Targets FILE ${CUTTLEFISH_EXPORTS_DIR}/${target}-targets.cmake) 74 | install(EXPORT ${target}Targets NAMESPACE Cuttlefish:: FILE ${target}-targets.cmake 75 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Cuttlefish) 76 | endfunction() 77 | -------------------------------------------------------------------------------- /cmake/templates/NoExport.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define CUTTLEFISH_EXPORT 3 | -------------------------------------------------------------------------------- /cmake/templates/UnixExport.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define CUTTLEFISH_EXPORT __attribute__((visibility("default"))) 3 | -------------------------------------------------------------------------------- /cmake/templates/WindowsExport.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifdef CUTTLEFISH_BUILD 3 | #define CUTTLEFISH_EXPORT __declspec(dllexport) 4 | #else 5 | #define CUTTLEFISH_EXPORT __declspec(dllimport) 6 | #endif 7 | -------------------------------------------------------------------------------- /doc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (NOT DOXYGEN_FOUND OR NOT CUTTLEFISH_BUILD_DOCS) 2 | return() 3 | endif() 4 | 5 | set(rootDir ${CMAKE_CURRENT_SOURCE_DIR}/..) 6 | 7 | set(docSource "\"${rootDir}/README.md\" \"${rootDir}/tool/README.md\"") 8 | set(includeDirs) 9 | set(dependencies ${rootDir}/README.md ${rootDir}/tool/README.md) 10 | foreach (docProject ${CUTTLEFISH_DOC_PROJECTS}) 11 | set(thisDir ${rootDir}/${docProject}) 12 | set(docSource "${docSource} \"${thisDir}/include\" \"${thisDir}/README.md\"") 13 | set(includeDirs "${includeDirs} \"${thisDir}/include\"") 14 | file(GLOB_RECURSE theseDependencies ${thisDir}/include/*.h) 15 | set(dependencies ${dependencies} ${theseDependencies} ${thisDir}/README.md) 16 | endforeach() 17 | 18 | configure_file(Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY) 19 | set(outputFile ${CMAKE_CURRENT_BINARY_DIR}/html/index.html) 20 | add_custom_command(OUTPUT ${outputFile} 21 | COMMAND ${DOXYGEN_EXECUTABLE} ARGS ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile 22 | WORKING_DIRECTORY ${rootDir} 23 | DEPENDS ${dependencies} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile ${rootDir}/README.md 24 | COMMENT "Generating API documentation with Doxygen" VERBATIM) 25 | add_custom_target(cuttlefish_doc ALL DEPENDS ${outputFile}) 26 | 27 | cfs_set_folder(cuttlefish_doc "") 28 | 29 | if (CUTTLEFISH_INSTALL) 30 | install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html/ DESTINATION ${CMAKE_INSTALL_DOCDIR}) 31 | endif() 32 | -------------------------------------------------------------------------------- /lib/AstcSources.cmake: -------------------------------------------------------------------------------- 1 | set(astcSources 2 | ${ASTC_DIR}/Source/astcenc.h 3 | ${ASTC_DIR}/Source/astcenc_averages_and_directions.cpp 4 | ${ASTC_DIR}/Source/astcenc_block_sizes.cpp 5 | ${ASTC_DIR}/Source/astcenc_color_quantize.cpp 6 | ${ASTC_DIR}/Source/astcenc_color_unquantize.cpp 7 | ${ASTC_DIR}/Source/astcenc_compress_symbolic.cpp 8 | ${ASTC_DIR}/Source/astcenc_compute_variance.cpp 9 | ${ASTC_DIR}/Source/astcenc_decompress_symbolic.cpp 10 | ${ASTC_DIR}/Source/astcenc_entry.cpp 11 | ${ASTC_DIR}/Source/astcenc_find_best_partitioning.cpp 12 | ${ASTC_DIR}/Source/astcenc_ideal_endpoints_and_weights.cpp 13 | ${ASTC_DIR}/Source/astcenc_image.cpp 14 | ${ASTC_DIR}/Source/astcenc_integer_sequence.cpp 15 | ${ASTC_DIR}/Source/astcenc_internal.h 16 | ${ASTC_DIR}/Source/astcenc_mathlib_softfloat.cpp 17 | ${ASTC_DIR}/Source/astcenc_mathlib.cpp 18 | ${ASTC_DIR}/Source/astcenc_mathlib.h 19 | ${ASTC_DIR}/Source/astcenc_partition_tables.cpp 20 | ${ASTC_DIR}/Source/astcenc_percentile_tables.cpp 21 | ${ASTC_DIR}/Source/astcenc_pick_best_endpoint_format.cpp 22 | ${ASTC_DIR}/Source/astcenc_quantization.cpp 23 | ${ASTC_DIR}/Source/astcenc_symbolic_physical.cpp 24 | ${ASTC_DIR}/Source/astcenc_weight_align.cpp 25 | ${ASTC_DIR}/Source/astcenc_weight_quant_xfer_tables.cpp 26 | ) 27 | 28 | set(ASTC_INCLUDE_DIRS ${ASTC_DIR}/Source) 29 | -------------------------------------------------------------------------------- /lib/Bc7encRdoSources.cmake: -------------------------------------------------------------------------------- 1 | set(bc7encRdoSources 2 | ${BC7ENC_RDO_DIR}/bc7enc.cpp 3 | ${BC7ENC_RDO_DIR}/bc7enc.h 4 | ${BC7ENC_RDO_DIR}/rgbcx.cpp 5 | ${BC7ENC_RDO_DIR}/rgbcx.h 6 | ${BC7ENC_RDO_DIR}/rgbcx_table4.h 7 | ) 8 | 9 | set(BC7ENC_RDO_INCLUDE_DIRS ${BC7ENC_RDO_DIR}) 10 | -------------------------------------------------------------------------------- /lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(Threads) 2 | 3 | set(FREEIMAGE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/FreeImage) 4 | set(SQUISH_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libsquish) 5 | set(COMPRESSONATOR_DIR ${CMAKE_CURRENT_SOURCE_DIR}/compressonator) 6 | set(BC7ENC_RDO_DIR ${CMAKE_CURRENT_SOURCE_DIR}/bc7enc_rdo) 7 | set(ISPC_TEXCOMP_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ISPCTextureCompressor) 8 | set(ETC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/etc2comp) 9 | set(ASTC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/astc-encoder) 10 | set(ispcSources) 11 | set(defines) 12 | set(privateDefines) 13 | set(externalSources) 14 | set(extraLibraries) 15 | set(extraIncludeDirs) 16 | 17 | if (CUTTLEFISH_ISPC_PATH) 18 | set(ISPC ${CUTTLEFISH_ISPC_PATH} CACHE FILEPATH "Path to the ISPC compiler." FORCE) 19 | mark_as_advanced(ISPC) 20 | else() 21 | if (WIN32) 22 | file(GLOB ispcInstallDir "C:/Program Files/ISPC/ispc-*-windows") 23 | set(ispcVersion) 24 | foreach (ispcDir ${ispcInstallDir}) 25 | string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" curVersion ${ispcDir}) 26 | if (curVersion VERSION_GREATER ispcVersion) 27 | set(ispcInstallPath ${ispcDir}/bin) 28 | set(ispcVersion ${curVersion}) 29 | endif() 30 | endforeach() 31 | endif() 32 | find_program(ISPC ispc PATHS ${ispcInstallPath}) 33 | endif() 34 | 35 | function(get_ispc_arch outArch outTargets outTargetNames arch singleTarget) 36 | # Supports both CUTTLEFISH_ARCH and CMAKE_OSX_ARCHITECTURES values. 37 | if (arch STREQUAL "x86" OR arch STREQUAL "i366") 38 | set(${outArch} x86 PARENT_SCOPE) 39 | if (singleTarget) 40 | set(${outTargets} sse4-i32x4 PARENT_SCOPE) 41 | set(${outTargetNames} sse4 PARENT_SCOPE) 42 | else() 43 | set(${outTargets} sse2-i32x4 sse4-i32x4 avx2-i32x4 PARENT_SCOPE) 44 | set(${outTargetNames} sse2 sse4 avx2 PARENT_SCOPE) 45 | endif() 46 | elseif (arch STREQUAL "x86_64") 47 | set(${outArch} x86-64 PARENT_SCOPE) 48 | if (singleTarget) 49 | set(${outTargets} sse4-i32x4 PARENT_SCOPE) 50 | set(${outTargetNames} sse4 PARENT_SCOPE) 51 | else() 52 | set(${outTargets} sse2-i32x4 sse4-i32x4 avx2-i32x4 PARENT_SCOPE) 53 | set(${outTargetNames} sse2 sse4 avx2 PARENT_SCOPE) 54 | endif() 55 | elseif (arch STREQUAL "arm") 56 | set(${outArch} arm PARENT_SCOPE) 57 | set(${outTargets} neon-i32x4 PARENT_SCOPE) 58 | set(${outTargetNames} neon PARENT_SCOPE) 59 | elseif (arch STREQUAL "arm64") 60 | set(${outArch} aarch64 PARENT_SCOPE) 61 | set(${outTargets} neon-i32x4 PARENT_SCOPE) 62 | set(${outTargetNames} neon PARENT_SCOPE) 63 | else() 64 | message(FATAL_ERROR "Unsupported CPU architecture ${arch}.") 65 | endif() 66 | endfunction() 67 | 68 | if (NOT CUTTLEFISH_FORCE_INTERNAL_FREEIMAGE) 69 | find_package(FreeImage QUIET) 70 | endif() 71 | if (CUTTLEFISH_FORCE_INTERNAL_FREEIMAGE OR NOT FreeImage_FOUND) 72 | message("Building FreeImage from submodule.") 73 | if (NOT EXISTS ${FREEIMAGE_DIR}) 74 | message(FATAL_ERROR 75 | "FreeImage not found. Run 'git submodule update --init' to pull the submodules.") 76 | endif() 77 | include(${CMAKE_CURRENT_LIST_DIR}/FreeImageSources.cmake) 78 | list(APPEND externalSources ${freeImageSources}) 79 | # NOTE: FreeImage doesn't include the libPNG Neon functions. 80 | list(APPEND privateDefines DISABLE_PERF_MEASUREMENT=1 PNG_ARM_NEON_OPT=0 FREEIMAGE_LIB 81 | _LARGEFILE64_SOURCE=1) 82 | if (WIN32) 83 | list(APPEND privateDefines OPJ_STATIC LIBRAW_NODLL) 84 | else() 85 | list(APPEND privateDefines __ANSI__) 86 | endif() 87 | list(APPEND extraIncludeDirs ${freeImageIncludeDirs}) 88 | else() 89 | list(APPEND extraLibraries ${FreeImage_LIBRARIES}) 90 | list(APPEND extraIncludeDirs ${FreeImage_INCLUDE_DIRS}) 91 | endif() 92 | 93 | if (CUTTLEFISH_BUILD_S3TC) 94 | if (NOT EXISTS ${SQUISH_DIR}) 95 | message(FATAL_ERROR 96 | "squish not found. Run 'git submodule update --init' to pull the submodules.") 97 | endif() 98 | include(${CMAKE_CURRENT_LIST_DIR}/SquishSources.cmake) 99 | list(APPEND externalSources ${squishSources}) 100 | list(APPEND extraIncludeDirs ${SQUISH_INCLUDE_DIRS}) 101 | 102 | if (NOT EXISTS ${COMPRESSONATOR_DIR}) 103 | message(FATAL_ERROR 104 | "compressonator not found. Run 'git submodule update --init' to pull the submodules.") 105 | endif() 106 | include(${CMAKE_CURRENT_LIST_DIR}/CompressonatorSources.cmake) 107 | list(APPEND externalSources ${compressonatorSources}) 108 | list(APPEND extraIncludeDirs ${COMPRESSONATOR_INCLUDE_DIRS}) 109 | 110 | if (NOT EXISTS ${BC7ENC_RDO_DIR}) 111 | message(FATAL_ERROR 112 | "bc7enc_rdo not found. Run 'git submodule update --init' to pull the submodules.") 113 | endif() 114 | include(${CMAKE_CURRENT_LIST_DIR}/Bc7encRdoSources.cmake) 115 | list(APPEND externalSources ${bc7encRdoSources}) 116 | list(APPEND extraIncludeDirs ${BC7ENC_RDO_INCLUDE_DIRS}) 117 | 118 | if (ISPC) 119 | if (NOT EXISTS ${ISPC_TEXCOMP_DIR}) 120 | message(FATAL_ERROR 121 | "ispc_texcomp not found. Run 'git submodule update --init' to pull the submodules.") 122 | endif() 123 | include(${CMAKE_CURRENT_LIST_DIR}/IspcTexcompSources.cmake) 124 | list(APPEND externalSources ${ispcTexcompSources}) 125 | list(APPEND extraIncludeDirs ${ISPC_TEXCOMP_INCLUDE_DIRS}) 126 | 127 | list(APPEND ispcSources ${BC7ENC_RDO_DIR}/bc7e.ispc 128 | ${ISPC_TEXCOMP_DIR}/ispc_texcomp/kernel.ispc) 129 | else() 130 | message(WARNING "ISPC not found, falling back to lower quality BC6H and BC7 encoders.") 131 | endif() 132 | 133 | list(APPEND defines CUTTLEFISH_HAS_S3TC=1) 134 | endif() 135 | 136 | if (CUTTLEFISH_BUILD_ETC) 137 | if (NOT EXISTS ${ETC_DIR}) 138 | message(FATAL_ERROR 139 | "etc2comp not found. Run 'git submodule update --init' to pull the submodules.") 140 | endif() 141 | include(${CMAKE_CURRENT_LIST_DIR}/Etc2CompSources.cmake) 142 | list(APPEND externalSources ${etcSources}) 143 | list(APPEND extraIncludeDirs ${ETC_INCLUDE_DIRS}) 144 | list(APPEND defines CUTTLEFISH_HAS_ETC=1) 145 | endif() 146 | 147 | if (CUTTLEFISH_BUILD_ASTC) 148 | if (NOT EXISTS ${ASTC_DIR}) 149 | message(FATAL_ERROR 150 | "astc-encoder not found. Run 'git submodule update --init' to pull the submodules.") 151 | endif() 152 | include(${CMAKE_CURRENT_LIST_DIR}/AstcSources.cmake) 153 | list(APPEND externalSources ${astcSources}) 154 | list(APPEND extraIncludeDirs ${ASTC_INCLUDE_DIRS}) 155 | list(APPEND defines CUTTLEFISH_HAS_ASTC=1) 156 | endif() 157 | 158 | if (CUTTLEFISH_BUILD_PVRTC) 159 | find_package(PVRTexLib QUIET) 160 | if (PVRTEXLIB_FOUND) 161 | list(APPEND extraIncludeDirs ${PVRTEXLIB_INCLUDE_DIRS}) 162 | list(APPEND extraLibraries ${PVRTEXLIB_LIBRARIES}) 163 | list(APPEND defines CUTTLEFISH_HAS_PVRTC=1) 164 | else() 165 | message("PVRTexLib not found.") 166 | endif() 167 | endif() 168 | 169 | # Setup ISPC compilations. 170 | if (ispcSources) 171 | set(ispcObjFiles) 172 | set(ispcTargetObjFiles) 173 | foreach (source ${ispcSources}) 174 | get_filename_component(baseName ${source} NAME_WLE) 175 | set(headerPath ${CMAKE_CURRENT_BINARY_DIR}/${baseName}_ispc.h) 176 | if (APPLE AND CUTTLEFISH_ARCH STREQUAL "multi") 177 | if (CMAKE_VERSION VERSION_LESS "3.20.0") 178 | message(FATAL_ERROR 179 | "CMake 3.20 or higher required for universal binaries with BTPC support.") 180 | endif() 181 | if (NOT CMAKE_GENERATOR STREQUAL "Xcode") 182 | message(FATAL_ERROR "Universal binaries can only be created with Xcode generator.") 183 | endif() 184 | 185 | list(APPEND ispcTargetObjFiles 186 | "${CMAKE_CURRENT_BINARY_DIR}/$(CURRENT_ARCH)/${baseName}.o") 187 | set(first ON) 188 | foreach (arch ${CMAKE_OSX_ARCHITECTURES}) 189 | set(outputDir ${CMAKE_CURRENT_BINARY_DIR}/${arch}) 190 | set(objPath ${outputDir}/${baseName}.o) 191 | 192 | # Only output the header for the first architecture. 193 | if (first) 194 | set(headerArgs -h ${headerPath}) 195 | set(first OFF) 196 | else() 197 | set(headerPath) 198 | set(headerArgs) 199 | endif() 200 | 201 | get_ispc_arch(ispcArch ispcTarget ispcTargetName ${arch} ON) 202 | add_custom_command(OUTPUT ${objPath} ${headerPath} 203 | COMMAND ${CMAKE_COMMAND} -E make_directory ${outputDir} 204 | COMMAND ${ISPC} -O3 ${source} -o ${objPath} ${headerArgs} --arch=${ispcArch} 205 | --target=${ispcTarget} --opt=disable-assertions --pic -woff 206 | MAIN_DEPENDENCY ${source}) 207 | list(APPEND ispcObjFiles ${objPath}) 208 | endforeach() 209 | else() 210 | get_ispc_arch(ispcArch ispcTargets ispcTargetNames ${CUTTLEFISH_ARCH} OFF) 211 | string(REPLACE ";" "," ispcCommandTargets "${ispcTargets}") 212 | if (MSVC) 213 | set(objPath ${CMAKE_CURRENT_BINARY_DIR}/${baseName}.obj) 214 | else() 215 | set(objPath ${CMAKE_CURRENT_BINARY_DIR}/${baseName}.o) 216 | endif() 217 | set(objPaths ${objPath}) 218 | list(LENGTH ispcTargetNames targetCount) 219 | if (targetCount GREATER 1) 220 | foreach (target ${ispcTargetNames}) 221 | if (MSVC) 222 | list(APPEND objPaths ${CMAKE_CURRENT_BINARY_DIR}/${baseName}_${target}.obj) 223 | else() 224 | list(APPEND objPaths ${CMAKE_CURRENT_BINARY_DIR}/${baseName}_${target}.o) 225 | endif() 226 | endforeach() 227 | endif() 228 | add_custom_command(OUTPUT ${objPaths} ${headerPath} 229 | COMMAND ${ISPC} -O3 ${source} -o ${objPath} -h ${headerPath} --arch=${ispcArch} 230 | --target=${ispcCommandTargets} --opt=disable-assertions --pic -woff 231 | MAIN_DEPENDENCY ${source}) 232 | list(APPEND ispcObjFiles ${objPaths}) 233 | endif() 234 | endforeach() 235 | 236 | if (APPLE AND CUTTLEFISH_ARCH STREQUAL "multi") 237 | add_custom_target(cuttlefish_ispc_build DEPENDS ${ispcObjFiles}) 238 | add_library(cuttlefish_ispc OBJECT IMPORTED) 239 | set_property(TARGET cuttlefish_ispc PROPERTY IMPORTED_OBJECTS ${ispcTargetObjFiles}) 240 | add_dependencies(cuttlefish_ispc cuttlefish_ispc_build) 241 | list(APPEND extraLibraries cuttlefish_ispc) 242 | 243 | cfs_set_folder(cuttlefish_ispc_build libs/internal) 244 | cfs_set_folder(cuttlefish_ispc libs/internal) 245 | else() 246 | # Add sources directly to support older versions of CMake. 247 | list(APPEND externalSources ${ispcObjFiles}) 248 | endif() 249 | 250 | list(APPEND privateDefines CUTTLEFISH_ISPC=1) 251 | else() 252 | list(APPEND privateDefines CUTTLEFISH_ISPC=0) 253 | endif() 254 | 255 | file(GLOB_RECURSE sources src/*.cpp src/*.h include/*.h) 256 | 257 | # Don't care about warnings for external files. 258 | if (MSVC) 259 | set_source_files_properties(${externalSources} PROPERTIES COMPILE_FLAGS /w) 260 | else() 261 | set_source_files_properties(${externalSources} PROPERTIES COMPILE_FLAGS "-w -Wno-c++11-narrowing") 262 | endif() 263 | 264 | add_library(cuttlefish_lib ${CUTTLEFISH_LIB} ${sources} ${externalSources} ${ispcSources}) 265 | set_target_properties(cuttlefish_lib PROPERTIES OUTPUT_NAME cuttlefish) 266 | target_include_directories(cuttlefish_lib 267 | PRIVATE ${CMAKE_CURRENT_BINARY_DIR} 268 | glm 269 | src 270 | ${extraIncludeDirs}) 271 | target_link_libraries(cuttlefish_lib PRIVATE ${CMAKE_THREAD_LIBS_INIT} ${extraLibraries}) 272 | target_compile_definitions(cuttlefish_lib PUBLIC ${defines} PRIVATE ${privateDefines}) 273 | 274 | if (CUTTLEFISH_BUILD_PVRTC AND PVRTEXLIB_FOUND) 275 | # Make sure the PVRTexLib library is available for running in the build directory. 276 | if (WIN32) 277 | get_filename_component(pvrTexLibDir ${PVRTEXLIB_LIB} DIRECTORY) 278 | set(pvrTexLibDll ${pvrTexLibDir}/PVRTexLib.dll) 279 | add_custom_command(TARGET cuttlefish_lib POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy 280 | ${pvrTexLibDll} $) 281 | elseif (APPLE) 282 | add_custom_command(TARGET cuttlefish_lib POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy 283 | ${PVRTEXLIB_LIB} $) 284 | endif() 285 | 286 | # Install the PVRTexLib library when needed. 287 | if (CUTTLEFISH_INSTALL AND CUTTLEFISH_INSTALL_PVRTEXLIB) 288 | if (WIN32) 289 | install(PROGRAMS ${pvrTexLibDll} DESTINATION ${CMAKE_INSTALL_BINDIR}) 290 | else() 291 | install(PROGRAMS ${PVRTEXLIB_LIB} DESTINATION ${CMAKE_INSTALL_LIBDIR}) 292 | endif() 293 | endif() 294 | endif() 295 | 296 | cfs_set_folder(cuttlefish_lib) 297 | cfs_setup_filters(SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src 298 | INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include/cuttlefish 299 | FILES ${sources}) 300 | cfs_setup_filters(SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR} FILES ${externalSources}) 301 | 302 | cfs_install_library(cuttlefish_lib lib) 303 | set(CUTTLEFISH_DOC_PROJECTS ${CUTTLEFISH_DOC_PROJECTS} lib PARENT_SCOPE) 304 | 305 | add_subdirectory(test) 306 | -------------------------------------------------------------------------------- /lib/CompressonatorSources.cmake: -------------------------------------------------------------------------------- 1 | set(compressonatorSources 2 | ${COMPRESSONATOR_DIR}/cmp_core/shaders/bc4_encode_kernel.cpp 3 | ${COMPRESSONATOR_DIR}/cmp_core/shaders/bc4_encode_kernel.h 4 | ${COMPRESSONATOR_DIR}/cmp_core/shaders/bc5_encode_kernel.cpp 5 | ${COMPRESSONATOR_DIR}/cmp_core/shaders/bc5_encode_kernel.h 6 | ${COMPRESSONATOR_DIR}/cmp_core/shaders/bc6_encode_kernel.cpp 7 | ${COMPRESSONATOR_DIR}/cmp_core/shaders/bc6_encode_kernel.h 8 | ) 9 | 10 | set(COMPRESSONATOR_INCLUDE_DIRS ${COMPRESSONATOR_DIR}/cmp_core/source 11 | ${COMPRESSONATOR_DIR}/cmp_core/shaders) 12 | -------------------------------------------------------------------------------- /lib/Etc2CompSources.cmake: -------------------------------------------------------------------------------- 1 | file(GLOB_RECURSE etcSources ${ETC_DIR}/EtcLib/EtcCodec/*.cpp ${ETC_DIR}/EtcLib/EtcCodec/*.h) 2 | set(etcSources ${etcSources} 3 | ${ETC_DIR}/EtcLib/Etc/EtcImage.cpp 4 | ${ETC_DIR}/EtcLib/Etc/EtcImage.h 5 | ${ETC_DIR}/EtcLib/Etc/EtcMath.cpp 6 | ${ETC_DIR}/EtcLib/Etc/EtcMath.h 7 | ${ETC_DIR}/EtcLib/Etc/EtcConfig.h 8 | ) 9 | 10 | set(ETC_INCLUDE_DIRS ${ETC_DIR}/EtcLib/Etc ${ETC_DIR}/EtcLib/EtcCodec) 11 | -------------------------------------------------------------------------------- /lib/IspcTexcompSources.cmake: -------------------------------------------------------------------------------- 1 | set(ispcTexcompSources 2 | ${ISPC_TEXCOMP_DIR}/ispc_texcomp/ispc_texcomp.cpp 3 | ${ISPC_TEXCOMP_DIR}/ispc_texcomp/ispc_texcomp.h 4 | ) 5 | 6 | set(ISPC_TEXCOMP_INCLUDE_DIRS ${ISPC_TEXCOMP_DIR}/ispc_texcomp) 7 | -------------------------------------------------------------------------------- /lib/README.md: -------------------------------------------------------------------------------- 1 | # Library 2 | 3 | The Cuttlefish library provides utilities to load images, perform operations on the images, convert to a texture, and save the final result. The texture may be saved in a common format (DDS, KTX, or PVR) or the raw data may be extracted to be used in another manner. 4 | 5 | # Image 6 | 7 | The `Image` class represents an input image. It may either be loaded from a file, a data buffer, or generated programatically. The data may be accessed directly to be manipulated by the program. Some common operations are provided by the `Image` class. See the `Image` class for a full list of operations that are supported. 8 | 9 | # Texture 10 | 11 | Once the source images have been created, the `Texture` class is used to create the final texture. After initialization, the images are set through `Texture::setImage()`. This must be called for all surfaces, such as cube faces, depth slices, and array indices. 12 | 13 | When generating a mipmapped texture, you can avoid having to set each mip level manually by calling `Texture::generateMipmaps()`. When calling this, you should initialize the texture with one mip level and set the images for mip level 0 before calling `Texture::generateMipmaps()`. 14 | 15 | Once all images have been set on the texture, `Texture::convert()` may be called to convert to a texture usable by the GPU. The parameters include: 16 | 17 | * The format. This is either the bit depth per channel a compressed format. 18 | * The type of data stored for each channel. 19 | * The quality used for texture compression, which also affects conversion speed. 20 | * The color space of the image. This may be used to flag the GPU to convert from sRGB to linear during texture sampling. 21 | * The alpha type and color mask. This may be used for compressed formats to adjust pixel weights. 22 | * The number of threads to use during conversion. If 1 is provided, no threads will be spawned. 23 | 24 | After conversion, `Texture::save()` may be called to save to a DDS, KTX, or PVR texture. Alternatively, the `Texture::data()` and `Texture::dataSize()` accessors may be used to access the raw data for each surface. 25 | -------------------------------------------------------------------------------- /lib/SquishSources.cmake: -------------------------------------------------------------------------------- 1 | set(squishSources 2 | ${SQUISH_DIR}/alpha.cpp 3 | ${SQUISH_DIR}/alpha.h 4 | ${SQUISH_DIR}/clusterfit.cpp 5 | ${SQUISH_DIR}/clusterfit.h 6 | ${SQUISH_DIR}/colourblock.cpp 7 | ${SQUISH_DIR}/colourblock.h 8 | ${SQUISH_DIR}/colourfit.cpp 9 | ${SQUISH_DIR}/colourfit.h 10 | ${SQUISH_DIR}/colourset.cpp 11 | ${SQUISH_DIR}/colourset.h 12 | ${SQUISH_DIR}/config.h 13 | ${SQUISH_DIR}/maths.cpp 14 | ${SQUISH_DIR}/maths.h 15 | ${SQUISH_DIR}/rangefit.cpp 16 | ${SQUISH_DIR}/rangefit.h 17 | ${SQUISH_DIR}/simd.h 18 | ${SQUISH_DIR}/simd_float.h 19 | ${SQUISH_DIR}/simd_sse.h 20 | ${SQUISH_DIR}/simd_ve.h 21 | ${SQUISH_DIR}/singlecolourfit.cpp 22 | ${SQUISH_DIR}/singlecolourfit.h 23 | ${SQUISH_DIR}/singlecolourlookup.inl 24 | ${SQUISH_DIR}/squish.cpp 25 | ${SQUISH_DIR}/squish.h 26 | ) 27 | 28 | set(SQUISH_INCLUDE_DIRS ${SQUISH_DIR}) 29 | -------------------------------------------------------------------------------- /lib/include/cuttlefish/Color.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Aaron Barany 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | /** 20 | * @file 21 | * @brief File containing structures for various color fromats. 22 | * 23 | * Colors that have 8-bit per channel or less may have different storage orders based on the 24 | * platform and source image. Use the shift and mask accessors in Image to access the color 25 | * channels. For example, for a 16 BPP image you can use the shift and mask on a 16-bit integer. 26 | * For a 24 or 32 BPP image, you can divide the shift by 8 to use as an index or use the shift and 27 | * mask on a 32-bit integer. 28 | */ 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | namespace cuttlefish 35 | { 36 | 37 | /** 38 | * @brief Enum for the color space. 39 | */ 40 | enum class ColorSpace 41 | { 42 | Linear, ///< Linear color space. 43 | sRGB ///< sRGB color space. 44 | }; 45 | 46 | /** 47 | * @brief Structure containing a 3 channel color with 16 bits per channel. 48 | */ 49 | struct ColorRGB16 50 | { 51 | std::uint16_t r; ///< @brief The red channel. 52 | std::uint16_t g; ///< @brief The green channel. 53 | std::uint16_t b; ///< @brief The blue channel. 54 | }; 55 | 56 | /** 57 | * @brief Structure containing a 4 channel color with 16 bits per channel. 58 | */ 59 | struct ColorRGBA16 60 | { 61 | std::uint16_t r; ///< @brief The red channel. 62 | std::uint16_t g; ///< @brief The green channel. 63 | std::uint16_t b; ///< @brief The blue channel. 64 | std::uint16_t a; ///< @brief The alpha channel. 65 | }; 66 | 67 | /** 68 | * @brief Structure containing a 3 channel floating point color. 69 | */ 70 | struct ColorRGBf 71 | { 72 | float r; ///< @brief The red channel. 73 | float g; ///< @brief The green channel. 74 | float b; ///< @brief The blue channel. 75 | }; 76 | 77 | /** 78 | * @brief Structure containing a 4 channel floating point color. 79 | */ 80 | struct ColorRGBAf 81 | { 82 | float r; ///< @brief The red channel. 83 | float g; ///< @brief The green channel. 84 | float b; ///< @brief The blue channel. 85 | float a; ///< @brief The alpha channel. 86 | }; 87 | 88 | /** 89 | * @brief Structure containing a 4 channel double-precision floating point color. 90 | */ 91 | struct ColorRGBAd 92 | { 93 | double r; ///< @brief The red channel. 94 | double g; ///< @brief The green channel. 95 | double b; ///< @brief The blue channel. 96 | double a; ///< @brief The alpha channel. 97 | }; 98 | 99 | /** 100 | * @brief Structure containing a complex number. 101 | */ 102 | struct Complex 103 | { 104 | double r; ///< @brief The real component. 105 | double i; ///< @brief The imaginary component. 106 | }; 107 | 108 | /** 109 | * @brief Converts a color to grayscale. 110 | * @param r The red channel. 111 | * @param g The green channel. 112 | * @param b The blue channel. 113 | * @return The grayscale value. 114 | */ 115 | inline double toGrayscale(double r, double g, double b) 116 | { 117 | // Rec. 709 118 | return r*0.2126 + g*0.7152 + b*0.0722; 119 | } 120 | 121 | /** 122 | * @brief Converts a color channel from sRGB to linear color space. 123 | * @param c The color channel in sRGB space. 124 | * @return The color channel in linear space. 125 | */ 126 | inline double sRGBToLinear(double c) 127 | { 128 | if (c <= 0.04045) 129 | return c/12.92; 130 | return std::pow((c + 0.055)/1.055, 2.4); 131 | } 132 | 133 | /** 134 | * @brief Converts a color channel from linear to sRGB color space. 135 | * @param c The color channel in linear space. 136 | * @return The color channel in sRGB space. 137 | */ 138 | inline double linearToSRGB(double c) 139 | { 140 | // linear to sRGB 141 | if (c <= 0.0031308) 142 | return c*12.92; 143 | return 1.055*std::pow(c, 1.0/2.4) - 0.055; 144 | } 145 | 146 | } // namespace cuttlefish 147 | -------------------------------------------------------------------------------- /lib/include/cuttlefish/Config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2023 Aaron Barany 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | /** 20 | * @file 21 | * @brief Configuration macros for the project. 22 | */ 23 | 24 | #if defined(_WIN32) 25 | # define CUTTLEFISH_WINDOWS 1 26 | #elif defined(linux) 27 | # define CUTTLEFISH_LINUX 1 28 | #elif defined(__APPLE__) 29 | # define CUTTLEFISH_APPLE 30 | #endif 31 | 32 | #if defined(_MSC_VER) 33 | # define CUTTLEFISH_MSC 1 34 | #elif defined(__clang__) 35 | # define CUTTLEFISH_CLANG 1 36 | #elif defined(__GNUC__) 37 | # define CUTTLEFISH_GCC 1 38 | #else 39 | #error Unknown compiler. 40 | #endif 41 | 42 | /** 43 | * @brief Define for whether the platform is Windows. 44 | */ 45 | #ifndef CUTTLEFISH_WINDOWS 46 | # define CUTTLEFISH_WINDOWS 0 47 | #endif 48 | 49 | /** 50 | * @brief Define for whether the platform is Linux. 51 | */ 52 | #ifndef CUTTLEFISH_LINUX 53 | # define CUTTLEFISH_LINUX 0 54 | #endif 55 | 56 | /** 57 | * @brief Define for whether the platform is Apple. 58 | */ 59 | #ifndef CUTTLEFISH_APPLE 60 | # define CUTTLEFISH_APPLE 0 61 | #endif 62 | 63 | /** 64 | * @brief Define for whether the compler is Microsoft's C compiler. 65 | */ 66 | #ifndef CUTTLEFISH_MSC 67 | # define CUTTLEFISH_MSC 0 68 | #endif 69 | 70 | /** 71 | * @brief Define for whether the compiler is LLVM clang. 72 | */ 73 | #ifndef CUTTLEFISH_CLANG 74 | # define CUTTLEFISH_CLANG 0 75 | #endif 76 | 77 | /** 78 | * @def CUTTLEFISH_GCC 79 | * @brief Define for whether the compiler is GCC. 80 | */ 81 | #ifndef CUTTLEFISH_GCC 82 | # define CUTTLEFISH_GCC 0 83 | #endif 84 | 85 | /** 86 | * @brief Macro defined to whether or not the system is 64-bit. 87 | */ 88 | #if defined(__LP64__) || defined(_WIN64) || defined(__x86_64__) || defined(__ppc64__) || defined(__arm64__) 89 | #define CUTTLEFISH_64BIT 1 90 | #else 91 | #define CUTTLEFISH_64BIT 0 92 | #endif 93 | 94 | /** 95 | * @brief Macro defined to whether or not the system is 64-bit x86. 96 | */ 97 | #if defined(__x86_64__) || defined(_M_AMD64) 98 | #define CUTTLEFISH_X86_64 1 99 | #else 100 | #define CUTTLEFISH_X86_64 0 101 | #endif 102 | 103 | /** 104 | * @brief Macro defined to whether or not the system is 32-bit x86. 105 | */ 106 | #if defined(__i386__) || defined(_M_IX86) 107 | #define CUTTLEFISH_X86_32 1 108 | #else 109 | #define CUTTLEFISH_X86_32 0 110 | #endif 111 | 112 | /** 113 | * @brief Macro defined to whether or not the system is 64-bit ARM. 114 | */ 115 | #if defined(__arm64__) || defined(__aarch64__) 116 | #define CUTTLEFISH_ARM_64 1 117 | #else 118 | #define CUTTLEFISH_ARM_64 0 119 | #endif 120 | 121 | /** 122 | * @brief Macro defined to whether or not the system is 32-bit ARM. 123 | */ 124 | #if defined(__arm__) || defined(_M_ARM) 125 | #define CUTTLEFISH_ARM_32 1 126 | #else 127 | #define CUTTLEFISH_ARM_32 0 128 | #endif 129 | 130 | /** 131 | * @brief Define for whether or not this is a debug build. 132 | */ 133 | #ifdef NDEBUG 134 | #define CUTTLEFISH_DEBUG 0 135 | #else 136 | #define CUTTLEFISH_DEBUG 1 137 | #endif 138 | 139 | /** 140 | * @brief Macro for an unused variable. 141 | * 142 | * This can be used to work around compiler warnings. 143 | * @param x The unused variable. 144 | */ 145 | #define CUTTLEFISH_UNUSED(x) (void)(&x) 146 | 147 | #if CUTTLEFISH_MSC 148 | #pragma warning(disable: 4251) // 'x' needs to have dll-interface to be used by clients of class 'y' 149 | #endif 150 | -------------------------------------------------------------------------------- /lib/include/cuttlefish/Image.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2024 Aaron Barany 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | /** 20 | * @file 21 | * @brief File containing the Image class to load and manipulate images. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | namespace cuttlefish 34 | { 35 | 36 | struct ColorRGBAd; 37 | 38 | /** 39 | * @brief Class to store the data for and manipulate source images. 40 | * 41 | * Loading an image is the first step for creating a texture. It is loaded from a source image file 42 | * (such as a PNG) then transformed as desired, such as resizing or flipping the image. Afterward, 43 | * one or more images will be added to a Texture. 44 | * 45 | * @remark The coordinate (0, 0) is the upper-left of the image. 46 | */ 47 | class CUTTLEFISH_EXPORT Image 48 | { 49 | public: 50 | 51 | /** 52 | * @brief Enum for the pixel format of the image. 53 | */ 54 | enum class Format 55 | { 56 | Invalid, ///< Invalid format, used for errors. 57 | Gray8, ///< 8-bit grayscale. 58 | Gray16, ///< 16-bit grayscale. 59 | RGB5, ///< 6-bit per channel RGB. 60 | RGB565, ///< 5, 6, and 5 bits per channel RGB. 61 | RGB8, ///< 8-bit per channel RGB. 62 | RGB16, ///< 16-bit per channel RGB. 63 | RGBF, ///< Floating point RGB. 64 | RGBA8, ///< 8-bit per channel RGBA. 65 | RGBA16, ///< 16-bit per channel RGBA. 66 | RGBAF, ///< Floating point RGBA. 67 | Int16, ///< Signed 16-bit integer. 68 | UInt16, ///< Unsigned 16-bit integer. 69 | Int32, ///< Signed 32-bit integer. 70 | UInt32, ///< Unsigned 32-bit integer. 71 | Float, ///< Float. 72 | Double, ///< Double. 73 | Complex ///< Two doubles as a complex number. 74 | }; 75 | 76 | /** 77 | * @brief Enum for the filter to use when resizing. 78 | */ 79 | enum class ResizeFilter 80 | { 81 | Box, ///< Averages all pixels in the box. 82 | Linear, ///< Linear sampling. 83 | Cubic, ///< Cubic curve sampling. 84 | CatmullRom, ///< Catmull-Rom curve fitting. 85 | BSpline ///< B-Spline curve fitting. 86 | }; 87 | 88 | /** 89 | * @brief Enum for the angle to rotate by. 90 | */ 91 | enum class RotateAngle 92 | { 93 | CW90, ///< 90 degrees clockwise. 94 | CW180, ///< 180 degrees clockwise. 95 | CW270, ///< 270 degrees clockwise. 96 | CCW90, ///< 90 degrees counter-clockwise. 97 | CCW180, ///< 180 degrees counter-clockwise. 98 | CCW270 ///< 270 degrees counter-clockwise. 99 | }; 100 | 101 | /** 102 | * @brief Enum for a color channel. 103 | */ 104 | enum class Channel 105 | { 106 | Red, ///< Red channel 107 | Green, ///< Green channel 108 | Blue, ///< Blue channel 109 | Alpha, ///< Alpha channel 110 | None ///< Unused. Will be set to 1 for alpha, 0 otherwise. 111 | }; 112 | 113 | /** 114 | * @brief Bitmask enum for options when creating normal maps. 115 | */ 116 | enum class NormalOptions 117 | { 118 | Default = 0x0, ///< Default options. 119 | KeepSign = 0x1, ///< Keep the range in [-1, 1] rather than [0, 1]. 120 | WrapX = 0x2, ///< Wrap along the X axis. 121 | WrapY = 0x4 ///< Wrap along the Y axis. 122 | }; 123 | 124 | Image(); 125 | 126 | /** 127 | * @brief Loads an image from file. 128 | * @param fileName The name of the file to load. 129 | * @param colorSpace The color space of the image. 130 | * @remark The image will be invalid if it failed to load. 131 | */ 132 | explicit Image(const char* fileName, ColorSpace colorSpace = ColorSpace::Linear); 133 | 134 | /** 135 | * @brief Loads an image from a stream. 136 | * @param stream The stream to read from. This must be opened in binary mode! 137 | * @param colorSpace The color space of the image. 138 | * @remark The image will be invalid if it failed to load. 139 | */ 140 | explicit Image(std::istream& stream, ColorSpace colorSpace = ColorSpace::Linear); 141 | 142 | /** 143 | * @brief Loads an image from data. 144 | * @param data The data to load from. 145 | * @param size The size of the data. 146 | * @param colorSpace The color space of the image. 147 | * @remark The image will be invalid if it failed to load. 148 | */ 149 | Image(const void* data, std::size_t size, ColorSpace colorSpace = ColorSpace::Linear); 150 | 151 | /** 152 | * @brief Initializes an empty image. 153 | * @param format The pixel format. 154 | * @param width The width of the image. 155 | * @param height The height of the image. 156 | * @param colorSpace The color space of the image. 157 | * @remark The image will be invalid if it failed to initialize. 158 | */ 159 | Image(Format format, unsigned int width, unsigned int height, 160 | ColorSpace colorSpace = ColorSpace::Linear); 161 | 162 | ~Image(); 163 | 164 | /// @cond 165 | Image(const Image& other); 166 | Image(Image&& other) noexcept; 167 | 168 | Image& operator=(const Image& other); 169 | Image& operator=(Image&& other) noexcept; 170 | /// @endcond 171 | 172 | /** 173 | * @brief Returns whether or not an image is valid. 174 | * @return True if valid. 175 | */ 176 | bool isValid() const; 177 | 178 | /** @copydoc isValid() */ 179 | explicit operator bool() const; 180 | 181 | /** 182 | * @brief Loads an image from file. 183 | * @param fileName The name of the file to load. 184 | * @param colorSpace The color space of the image. 185 | * @return False if the image couldn't be loaded. 186 | */ 187 | bool load(const char* fileName, ColorSpace colorSpace = ColorSpace::Linear); 188 | 189 | /** 190 | * @brief Loads an image from a stream. 191 | * @param stream The stream to load from. This must be opened in binary mode! 192 | * @param colorSpace The color space of the image. 193 | * @return False if the image couldn't be loaded. 194 | */ 195 | bool load(std::istream& stream, ColorSpace colorSpace = ColorSpace::Linear); 196 | 197 | /** 198 | * @brief Loads an image from data. 199 | * @param data The data to load from. 200 | * @param size The size of the data. 201 | * @param colorSpace The color space of the image. 202 | * @return False if the image couldn't be loaded. 203 | */ 204 | bool load(const void* data, std::size_t size, ColorSpace colorSpace = ColorSpace::Linear); 205 | 206 | /** 207 | * @brief Saves an image to a file. 208 | * 209 | * Nearly all image file formats, such as png, jpeg, etc., are supported. This will use the 210 | * default parameters for settings like compression, and is intended for the most common use 211 | * cases. For more intricate use cases where finer levels of control are needed, you will need 212 | * to extract the raw image data and use a dedicated image library. 213 | * 214 | * @remark You should ensure that the image's pixel format is supported by the file format. 215 | * @param fileName The name of the file to save. 216 | * @return False if the image couldn't be saved. 217 | */ 218 | bool save(const char* fileName); 219 | 220 | /** 221 | * @brief Saves an image to byte vector. 222 | * 223 | * Nearly all image file formats, such as png, jpeg, etc., are supported. This will use the 224 | * default parameters for settings like compression, and is intended for the most common use 225 | * cases. For more intricate use cases where finer levels of control are needed, you will need 226 | * to extract the raw image data and use a dedicated image library. 227 | * 228 | * @remark You should ensure that the image's pixel format is supported by the file format. 229 | * @param stream The stream to save the image to. This must be opened in binary mode! 230 | * @param extension The file extension used to determine the file format. 231 | * @return False if the image couldn't be saved. 232 | */ 233 | bool save(std::ostream& stream, const char* extension); 234 | 235 | /** 236 | * @brief Saves an image to a stream. 237 | * 238 | * Nearly all image file formats, such as png, jpeg, etc., are supported. This will use the 239 | * default parameters for settings like compression, and is intended for the most common use 240 | * cases. For more intricate use cases where finer levels of control are needed, you will need 241 | * to extract the raw image data and use a dedicated image library. 242 | * 243 | * @remark You should ensure that the image's pixel format is supported by the file format. 244 | * @param[out] outData The byte vector to save the image to. The contents will be overwritten. 245 | * @param extension The file extension used to determine the file format. 246 | * @return False if the image couldn't be saved. 247 | */ 248 | bool save(std::vector& outData, const char* extension); 249 | 250 | /** 251 | * @brief Initializes an empty image. 252 | * @param format The pixel format. 253 | * @param width The width of the image. 254 | * @param height The height of the image. 255 | * @param colorSpace The color space of the image. 256 | * @return False if the image couldn't be initialized. 257 | */ 258 | bool initialize(Format format, unsigned int width, unsigned int height, 259 | ColorSpace colorSpace = ColorSpace::Linear); 260 | 261 | /** 262 | * @brief Resets the image to an unitialized state. 263 | */ 264 | void reset(); 265 | 266 | /** 267 | * @brief Gets the pixel format of the image. 268 | * @return The pixel format. 269 | */ 270 | Format format() const; 271 | 272 | /** 273 | * @brief Gets the color space of the image. 274 | * @return The color space. 275 | */ 276 | ColorSpace colorSpace() const; 277 | 278 | /** 279 | * @brief Gets the number of bits per pixel in the image. 280 | * @return The bits per pixel. 281 | */ 282 | unsigned int bitsPerPixel() const; 283 | 284 | /** 285 | * @brief Gets the width of the image. 286 | * @return The width in pixels. 287 | */ 288 | unsigned int width() const; 289 | 290 | /** 291 | * @brief Gets the height of the image. 292 | * @return The height in pixels. 293 | */ 294 | unsigned int height() const; 295 | 296 | /** 297 | * @brief Gets the mask for the red channel for a 16 or 32 BPP RGB or RGBA format. 298 | * @return The red channel mask. 299 | */ 300 | std::uint32_t redMask() const; 301 | 302 | /** 303 | * @brief Gets the bit shift for the red channel for a 16 or 32 BPP RGB or RGBA format. 304 | * @return The red channel shift. 305 | */ 306 | unsigned int redShift() const; 307 | 308 | /** 309 | * @brief Gets the mask for the green channel for a 16 or 32 BPP RGB or RGBA format. 310 | * @return The green channel mask. 311 | */ 312 | std::uint32_t greenMask() const; 313 | 314 | /** 315 | * @brief Gets the bit shift for the green channel for a 16 or 32 BPP RGB or RGBA format. 316 | * @return The green channel shift. 317 | */ 318 | unsigned int greenShift() const; 319 | 320 | /** 321 | * @brief Gets the mask for the blue channel for a 16 or 32 BPP RGB or RGBA format. 322 | * @return The blue channel mask. 323 | */ 324 | std::uint32_t blueMask() const; 325 | 326 | /** 327 | * @brief Gets the bit shift for the blue channel for a 16 or 32 BPP RGB or RGBA format. 328 | * @return The blue channel shift. 329 | */ 330 | unsigned int blueShift() const; 331 | 332 | /** 333 | * @brief Gets the mask for the alpha channel for a 16 or 32 BPP RGB or RGBA format. 334 | * @return The alpha channel mask. 335 | */ 336 | std::uint32_t alphaMask() const; 337 | 338 | /** 339 | * @brief Gets the bit shift for the alpha channel for a 16 or 32 BPP RGB or RGBA format. 340 | * @return The alpha channel shift. 341 | */ 342 | unsigned int alphaShift() const; 343 | 344 | /** 345 | * @brief Gets a scanline of the image. 346 | * @param y The Y coordinate of the scanline. 347 | * @return The data for the scanline. 348 | */ 349 | void* scanline(unsigned int y); 350 | 351 | /** @copydoc scanline() */ 352 | const void* scanline(unsigned int y) const; 353 | 354 | /** 355 | * @brief Gets a pixel as a floating point value. 356 | * @remark This will work with any image format. 357 | * @param[out] outColor The color of the pixel. 358 | * @param x The X coordinate of the image. 359 | * @param y The Y coordinate of the image. 360 | * @return False if the pixel is out of range. 361 | */ 362 | bool getPixel(ColorRGBAd& outColor, unsigned int x, unsigned int y) const; 363 | 364 | /** 365 | * @brief Sets a pixel as a floating point value. 366 | * @remark This will work with any image format. 367 | * @param x The X coordinate of the image. 368 | * @param y The Y coordinate of the image. 369 | * @param color The color of the pixel. 370 | * @param convertGrayscale True to convert to grayscale for grayscale image types 371 | * (Gray8, Gray16, Float, Double), false to take the red channel as-is. 372 | * @return False if the pixel is out of range. 373 | */ 374 | bool setPixel(unsigned int x, unsigned int y, const ColorRGBAd& color, 375 | bool convertGrayscale = true); 376 | 377 | /** 378 | * @brief Converts the image to another pixel format. 379 | * @param dstFormat The new pixel format. 380 | * @param convertGrayscale True to convert to grayscale for grayscale image types 381 | * (Gray8, Gray16, Float, Double), false to take the red channel as-is. 382 | * @return The converted image. 383 | */ 384 | Image convert(Format dstFormat, bool convertGrayscale = true) const; 385 | 386 | /** 387 | * @brief Resizes an image. 388 | * @remark In some situations the format may change. 389 | * @remark Int*, UInt32, Double, and Complex types may only use Nearest and Linear filters. 390 | * @param width The new width of the image. 391 | * @param height The new height of the image. 392 | * @param filter The filter to use for resizing. 393 | * @return The resized image, or an invalid image if the format cannot be resized. 394 | */ 395 | Image resize(unsigned int width, unsigned int height, ResizeFilter filter) const; 396 | 397 | /** 398 | * @brief Rotates an image. 399 | * @param angle The angle to rotate by. 400 | * @return The rotated image. 401 | * @return The rotated image, or an invalid image if the format cannot be rotated. 402 | */ 403 | Image rotate(RotateAngle angle) const; 404 | 405 | /** 406 | * @brief Flips the image horizontally in place. 407 | * @return False if the image was invalid. 408 | */ 409 | bool flipHorizontal(); 410 | 411 | /** 412 | * @brief Flips the image vertically in place. 413 | * @return False if the image was invalid. 414 | */ 415 | bool flipVertical(); 416 | 417 | /** 418 | * @brief Pre-multiplies the alpha values with the color values in place. 419 | * @return False if the image was invalid. 420 | */ 421 | bool preMultiplyAlpha(); 422 | 423 | /** 424 | * @brief Converts the image to a different color space. 425 | * 426 | * This will affect every channel except the alpha channel. 427 | * 428 | * @remark Converting from sRGB to linear will reduce the precision for percieved color values, 429 | * so it's not recommended for 8 bits per channel or less. 430 | * @return False if the image was invalid. 431 | */ 432 | bool changeColorSpace(ColorSpace colorSpace); 433 | 434 | /** 435 | * @brief Converts to grayscale. 436 | * @return False if the image was invalid. 437 | */ 438 | bool grayscale(); 439 | 440 | /** 441 | * @brief Swizzles the texture. 442 | * @param red The channel to place in red. 443 | * @param green The channel to place in green. 444 | * @param blue The channel to place in blue. 445 | * @param alpha The channel to place in alpha. 446 | * @return False if the image was invalid. 447 | */ 448 | bool swizzle(Channel red, Channel green, Channel blue, Channel alpha); 449 | 450 | /** 451 | * @brief Creates a normal map from the R channel of the image. 452 | * @param options The options to use for computing the normal map. 453 | * @param height The height for the image. 454 | * @param dstFormat The format of the final image. 455 | * @return The normal map image. 456 | */ 457 | Image createNormalMap(NormalOptions options = NormalOptions::Default, double height = 1.0, 458 | Format dstFormat = Format::RGBF); 459 | 460 | private: 461 | struct Impl; 462 | std::unique_ptr m_impl; 463 | }; 464 | 465 | /** 466 | * @brief Combines two normal options. 467 | * @param left The left options. 468 | * @param right The right options. 469 | * @return The combination of the left and right options. 470 | */ 471 | inline Image::NormalOptions operator|(Image::NormalOptions left, Image::NormalOptions right) 472 | { 473 | return static_cast(static_cast(left) | 474 | static_cast(right)); 475 | } 476 | 477 | /** 478 | * @brief Combines two normal options. 479 | * @param left The left options to add right options to. 480 | * @param right The right options. 481 | * @return The combination of the left and right options. 482 | */ 483 | inline Image::NormalOptions& operator|=(Image::NormalOptions& left, Image::NormalOptions right) 484 | { 485 | left = static_cast(static_cast(left) | 486 | static_cast(right)); 487 | return left; 488 | } 489 | 490 | /** 491 | * @brief Checks if there are shared options between two normal options. 492 | * @param left The left options. 493 | * @param right The right options. 494 | * @return True if there are shared options between left and right. 495 | */ 496 | inline bool operator&(Image::NormalOptions left, Image::NormalOptions right) 497 | { 498 | return (static_cast(left) & static_cast(right)) != 0; 499 | } 500 | 501 | } // namespace cuttlefish 502 | -------------------------------------------------------------------------------- /lib/src/AstcConverter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2021 Aaron Barany 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #if CUTTLEFISH_HAS_ASTC 18 | 19 | #include "AstcConverter.h" 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "astcenc.h" 30 | 31 | namespace cuttlefish 32 | { 33 | 34 | namespace 35 | { 36 | 37 | const unsigned int blockSize = 16; 38 | const unsigned int maxBlockDim = 12; 39 | 40 | class AstcContextManager 41 | { 42 | public: 43 | static const unsigned int cacheSize; 44 | 45 | astcenc_context* createContext(const astcenc_config& config) 46 | { 47 | std::lock_guard lock(m_mutex); 48 | 49 | for (auto it = m_cache.begin(); it != m_cache.end(); ++it) 50 | { 51 | if (std::memcmp(&config, &it->config, sizeof(astcenc_config)) != 0) 52 | continue; 53 | 54 | astcenc_context* context = it->context; 55 | it->context = nullptr; 56 | m_cache.erase(it); 57 | return context; 58 | } 59 | 60 | astcenc_context* context = nullptr; 61 | astcenc_context_alloc(&config, 1, &context); 62 | assert(context); 63 | return context; 64 | } 65 | 66 | void destroyContext(astcenc_context* context, const astcenc_config& config) 67 | { 68 | std::lock_guard lock(m_mutex); 69 | 70 | while (m_cache.size() >= cacheSize) 71 | m_cache.pop_back(); 72 | 73 | m_cache.emplace_front(context, config); 74 | } 75 | 76 | private: 77 | struct ContextInfo 78 | { 79 | ContextInfo(astcenc_context* _context, const astcenc_config& _config) 80 | : context(_context), config(_config) 81 | { 82 | } 83 | 84 | ContextInfo(const ContextInfo&) = delete; 85 | ContextInfo& operator=(const ContextInfo&) = delete; 86 | 87 | ~ContextInfo() 88 | { 89 | astcenc_context_free(context); 90 | } 91 | 92 | astcenc_context* context; 93 | astcenc_config config; 94 | }; 95 | 96 | std::list m_cache; 97 | std::mutex m_mutex; 98 | }; 99 | 100 | const unsigned int AstcContextManager::cacheSize = 3*std::thread::hardware_concurrency(); 101 | 102 | AstcContextManager g_contextManager; 103 | 104 | } // namespace 105 | 106 | struct AstcConverter::AstcData 107 | { 108 | astcenc_swizzle swizzle; 109 | astcenc_config config; 110 | }; 111 | 112 | class AstcConverter::AstcThreadData : public Converter::ThreadData 113 | { 114 | public: 115 | AstcThreadData(unsigned int blockX, unsigned int blockY, const astcenc_config& _config) 116 | : config(&_config), context(g_contextManager.createContext(_config)) 117 | { 118 | dummyImage.dim_x = blockX; 119 | dummyImage.dim_y = blockY; 120 | dummyImage.dim_z = 1; 121 | dummyImage.data_type = ASTCENC_TYPE_F32; 122 | dummyImage.data = nullptr; 123 | } 124 | 125 | ~AstcThreadData() 126 | { 127 | g_contextManager.destroyContext(context, *config); 128 | } 129 | 130 | astcenc_image dummyImage; 131 | const astcenc_config* config; 132 | astcenc_context* context; 133 | }; 134 | 135 | AstcConverter::AstcConverter(const Texture& texture, const Image& image, unsigned int blockX, 136 | unsigned int blockY, Texture::Quality quality) 137 | : Converter(image), m_blockX(blockX), m_blockY(blockY), 138 | m_jobsX((image.width() + blockX - 1)/blockX), m_jobsY((image.height() + blockY - 1)/blockY), 139 | m_astcData(new AstcData) 140 | { 141 | m_astcData->swizzle.r = texture.colorMask().r ? ASTCENC_SWZ_R : ASTCENC_SWZ_0; 142 | m_astcData->swizzle.g = texture.colorMask().g ? ASTCENC_SWZ_G : ASTCENC_SWZ_0; 143 | m_astcData->swizzle.b = texture.colorMask().b ? ASTCENC_SWZ_B : ASTCENC_SWZ_0; 144 | if (texture.colorMask().a) 145 | { 146 | m_astcData->swizzle.a = texture.alphaType() == Texture::Alpha::None ? 147 | ASTCENC_SWZ_1 : ASTCENC_SWZ_A; 148 | } 149 | else 150 | m_astcData->swizzle.a = ASTCENC_SWZ_0; 151 | 152 | astcenc_profile profile; 153 | if (texture.type() == Texture::Type::UFloat) 154 | { 155 | if (texture.alphaType() == Texture::Alpha::None || 156 | texture.alphaType() == Texture::Alpha::PreMultiplied) 157 | { 158 | profile = ASTCENC_PRF_HDR_RGB_LDR_A; 159 | } 160 | else 161 | profile = ASTCENC_PRF_HDR; 162 | } 163 | else 164 | profile = ASTCENC_PRF_LDR; 165 | 166 | unsigned int flags = 0; 167 | if (texture.alphaType() == Texture::Alpha::Standard || 168 | texture.alphaType() == Texture::Alpha::PreMultiplied) 169 | { 170 | flags |= ASTCENC_FLG_USE_ALPHA_WEIGHT; 171 | } 172 | if (image.colorSpace() == ColorSpace::sRGB) 173 | flags |= ASTCENC_FLG_USE_PERCEPTUAL; 174 | 175 | float preset; 176 | switch (quality) 177 | { 178 | case Texture::Quality::Lowest: 179 | preset = ASTCENC_PRE_FASTEST; 180 | break; 181 | case Texture::Quality::Low: 182 | preset = ASTCENC_PRE_FAST; 183 | break; 184 | case Texture::Quality::Normal: 185 | preset = ASTCENC_PRE_MEDIUM; 186 | break; 187 | case Texture::Quality::High: 188 | preset = ASTCENC_PRE_THOROUGH; 189 | break; 190 | case Texture::Quality::Highest: 191 | preset = ASTCENC_PRE_EXHAUSTIVE; 192 | break; 193 | default: 194 | assert(false); 195 | return; 196 | } 197 | 198 | astcenc_config_init(profile, blockX, blockY, 1, preset, flags, &m_astcData->config); 199 | 200 | assert(texture.type() == Texture::Type::UNorm || texture.type() == Texture::Type::UFloat); 201 | data().resize(m_jobsX*m_jobsY*blockSize); 202 | } 203 | 204 | AstcConverter::~AstcConverter() 205 | { 206 | delete m_astcData; 207 | } 208 | 209 | void AstcConverter::process(unsigned int x, unsigned int y, ThreadData* threadData) 210 | { 211 | ColorRGBAf imageData[maxBlockDim*maxBlockDim]; 212 | void* imageRows[maxBlockDim]; 213 | for (unsigned int j = 0, index = 0; j < m_blockY; ++j) 214 | { 215 | imageRows[j] = imageData + index; 216 | auto scanline = reinterpret_cast(image().scanline( 217 | std::min(y*m_blockY + j, image().height() - 1))); 218 | for (unsigned int i = 0; i < m_blockX; ++i, ++index) 219 | { 220 | unsigned int scanlineIdx = std::min(x*m_blockX + i, image().width() - 1); 221 | imageData[index] = scanline[scanlineIdx]; 222 | } 223 | } 224 | 225 | auto block = data().data() + (y*m_jobsX + x)*blockSize; 226 | auto astcThreadData = static_cast(threadData); 227 | astcThreadData->dummyImage.data = imageRows; 228 | astcenc_compress_image(astcThreadData->context, &astcThreadData->dummyImage, 229 | &m_astcData->swizzle, block, blockSize, 0); 230 | astcenc_compress_reset(astcThreadData->context); 231 | } 232 | 233 | std::unique_ptr AstcConverter::createThreadData() 234 | { 235 | return std::unique_ptr(new AstcThreadData(m_blockX, m_blockY, m_astcData->config)); 236 | } 237 | 238 | } // namespace cuttlefish 239 | 240 | #endif // CUTTLEFISH_HAS_ASTC 241 | -------------------------------------------------------------------------------- /lib/src/AstcConverter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2020 Aaron Barany 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include "Converter.h" 21 | #include 22 | 23 | #if CUTTLEFISH_HAS_ASTC 24 | 25 | namespace cuttlefish 26 | { 27 | 28 | class AstcConverter : public Converter 29 | { 30 | public: 31 | AstcConverter(const Texture& texture, const Image& image, unsigned int blockX, 32 | unsigned int blockY, Texture::Quality quality); 33 | ~AstcConverter(); 34 | 35 | unsigned int jobsX() const override {return m_jobsX;} 36 | unsigned int jobsY() const override {return m_jobsY;} 37 | void process(unsigned int x, unsigned int y, ThreadData* threadData) override; 38 | std::unique_ptr createThreadData() override; 39 | 40 | private: 41 | struct AstcData; 42 | class AstcThreadData; 43 | 44 | unsigned int m_blockX; 45 | unsigned int m_blockY; 46 | unsigned int m_jobsX; 47 | unsigned int m_jobsY; 48 | AstcData* m_astcData; 49 | }; 50 | 51 | } // namespace cuttlefish 52 | 53 | #endif // CUTTLEFISH_HAS_ASTC 54 | -------------------------------------------------------------------------------- /lib/src/Converter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Aaron Barany 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | namespace cuttlefish 27 | { 28 | 29 | class Texture; 30 | 31 | class Converter 32 | { 33 | public: 34 | using FaceImageList = std::vector; 35 | using DepthImageList = std::vector; 36 | using MipImageList = std::vector; 37 | 38 | using TextureData = std::vector; 39 | using FaceTextureList = std::vector; 40 | using DepthTextureList = std::vector; 41 | using MipTextureList = std::vector; 42 | 43 | class ThreadData 44 | { 45 | public: 46 | virtual ~ThreadData() = default; 47 | }; 48 | 49 | static bool convert(const Texture& texture, MipImageList& images, MipTextureList& textureData, 50 | Texture::Quality quality, unsigned int threadCount); 51 | 52 | explicit Converter(const Image& image) 53 | : m_image(&image) 54 | { 55 | assert(m_image->format() == Image::Format::RGBAF); 56 | } 57 | 58 | Converter(const Converter&) = delete; 59 | Converter& operator=(const Converter&) = delete; 60 | 61 | virtual ~Converter() = default; 62 | 63 | const Image& image() const {return *m_image;} 64 | 65 | std::vector& data() {return m_data;} 66 | const std::vector& data() const {return m_data;} 67 | 68 | virtual unsigned int jobsX() const = 0; 69 | virtual unsigned int jobsY() const = 0; 70 | virtual void process(unsigned int x, unsigned int y, ThreadData* threadData) = 0; 71 | virtual std::unique_ptr createThreadData(); 72 | 73 | private: 74 | const Image* m_image; 75 | std::vector m_data; 76 | }; 77 | 78 | } // namespace cuttlefish 79 | -------------------------------------------------------------------------------- /lib/src/EtcConverter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2025 Aaron Barany 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #if CUTTLEFISH_HAS_ETC 18 | 19 | #include "EtcConverter.h" 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | 27 | namespace cuttlefish 28 | { 29 | 30 | EtcConverter::EtcConverter(const Texture& texture, const Image& image, Texture::Quality quality) 31 | : Converter(image), m_jobsX((image.width() + blockDim - 1)/blockDim), 32 | m_jobsY((image.height() + blockDim - 1)/blockDim) 33 | { 34 | switch (quality) 35 | { 36 | case Texture::Quality::Lowest: 37 | m_effort = ETCCOMP_MIN_EFFORT_LEVEL; 38 | break; 39 | case Texture::Quality::Low: 40 | m_effort = (ETCCOMP_MIN_EFFORT_LEVEL + ETCCOMP_DEFAULT_EFFORT_LEVEL)/2; 41 | break; 42 | case Texture::Quality::Normal: 43 | m_effort = ETCCOMP_DEFAULT_EFFORT_LEVEL; 44 | break; 45 | case Texture::Quality::High: 46 | m_effort = (ETCCOMP_DEFAULT_EFFORT_LEVEL + ETCCOMP_MAX_EFFORT_LEVEL)/2; 47 | break; 48 | case Texture::Quality::Highest: 49 | m_effort = ETCCOMP_MAX_EFFORT_LEVEL; 50 | break; 51 | default: 52 | assert(false); 53 | break; 54 | } 55 | 56 | switch (texture.format()) 57 | { 58 | case Texture::Format::ETC1: 59 | m_blockSize = 8; 60 | m_format = Etc::Image::Format::ETC1; 61 | if (texture.colorSpace() == ColorSpace::Linear) 62 | m_metric = Etc::RGBX; 63 | else 64 | m_metric = Etc::REC709; 65 | break; 66 | case Texture::Format::ETC2_R8G8B8: 67 | m_blockSize = 8; 68 | m_format = Etc::Image::Format::RGB8; 69 | if (texture.colorSpace() == ColorSpace::Linear) 70 | m_metric = Etc::RGBX; 71 | else 72 | m_metric = Etc::REC709; 73 | break; 74 | case Texture::Format::ETC2_R8G8B8A1: 75 | m_blockSize = 8; 76 | m_format = Etc::Image::Format::RGB8A1; 77 | if (texture.colorSpace() == ColorSpace::Linear) 78 | m_metric = Etc::RGBA; 79 | else 80 | m_metric = Etc::REC709; 81 | break; 82 | case Texture::Format::ETC2_R8G8B8A8: 83 | m_blockSize = 16; 84 | m_format = Etc::Image::Format::RGBA8; 85 | if (texture.colorSpace() == ColorSpace::Linear) 86 | m_metric = Etc::RGBA; 87 | else 88 | m_metric = Etc::REC709; 89 | break; 90 | case Texture::Format::EAC_R11: 91 | m_blockSize = 8; 92 | if (texture.type() == Texture::Type::UNorm) 93 | m_format = Etc::Image::Format::R11; 94 | else 95 | { 96 | assert(texture.type() == Texture::Type::SNorm); 97 | m_format = Etc::Image::Format::SIGNED_R11; 98 | } 99 | m_metric = Etc::NUMERIC; 100 | break; 101 | case Texture::Format::EAC_R11G11: 102 | m_blockSize = 16; 103 | if (texture.type() == Texture::Type::UNorm) 104 | m_format = Etc::Image::Format::RG11; 105 | else 106 | { 107 | assert(texture.type() == Texture::Type::SNorm); 108 | m_format = Etc::Image::Format::SIGNED_RG11; 109 | } 110 | m_metric = Etc::NUMERIC; 111 | break; 112 | default: 113 | assert(false); 114 | break; 115 | } 116 | 117 | data().resize(m_jobsX*m_jobsY*m_blockSize); 118 | } 119 | 120 | void EtcConverter::process(unsigned int x, unsigned int y, ThreadData*) 121 | { 122 | ColorRGBAf pixels[blockDim*blockDim]; 123 | unsigned int limitX = std::min((x + 1)*blockDim, image().width()); 124 | unsigned int limitY = std::min((y + 1)*blockDim, image().height()); 125 | for (unsigned int j = y*blockDim, index = 0; j < limitY; ++j) 126 | { 127 | auto scanline = reinterpret_cast(image().scanline(j)); 128 | for (unsigned int i = x*blockDim; i < limitX; ++i, ++index) 129 | pixels[index] = scanline[i]; 130 | } 131 | 132 | // Signed formats expect inputs in the range [0, 1]. 133 | if (m_format == Etc::Image::Format::SIGNED_R11 || m_format == Etc::Image::Format::SIGNED_RG11) 134 | { 135 | for (unsigned int j = 0, index = 0; j < blockDim; ++j) 136 | { 137 | for (unsigned int i = 0; i < blockDim; ++i, ++index) 138 | { 139 | pixels[index].r = pixels[index].r*0.5f + 0.5f; 140 | pixels[index].g = pixels[index].g*0.5f + 0.5f; 141 | } 142 | } 143 | } 144 | 145 | Etc::Image etcImage(reinterpret_cast(pixels), limitX - x*blockDim, limitY - y*blockDim, 146 | m_metric); 147 | etcImage.Encode(m_format, m_metric, m_effort, 1, 1); 148 | 149 | assert(etcImage.GetEncodingBitsBytes() == m_blockSize); 150 | void* block = data().data() + (y*m_jobsX + x)*m_blockSize; 151 | std::memcpy(block, etcImage.GetEncodingBits(), m_blockSize); 152 | } 153 | 154 | } // namespace cuttlefish 155 | 156 | #endif // CUTTLEFISH_HAS_ETC 157 | -------------------------------------------------------------------------------- /lib/src/EtcConverter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Aaron Barany 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include "Converter.h" 21 | 22 | #if CUTTLEFISH_HAS_ETC 23 | 24 | #if CUTTLEFISH_MSC 25 | #pragma warning(push) 26 | #pragma warning(disable: 4005) 27 | #endif 28 | 29 | #include 30 | 31 | #if CUTTLEFISH_MSC 32 | #pragma warning(pop) 33 | #endif 34 | 35 | namespace Etc { class Image; } 36 | 37 | namespace cuttlefish 38 | { 39 | 40 | class EtcConverter : public Converter 41 | { 42 | public: 43 | static const unsigned int blockDim = 4; 44 | 45 | EtcConverter(const Texture& texture, const Image& image, Texture::Quality quality); 46 | 47 | unsigned int jobsX() const override {return m_jobsX;} 48 | unsigned int jobsY() const override {return m_jobsY;} 49 | void process(unsigned int x, unsigned int y, ThreadData* threadData) override; 50 | 51 | private: 52 | unsigned int m_blockSize; 53 | unsigned int m_jobsX; 54 | unsigned int m_jobsY; 55 | Etc::Image::Format m_format; 56 | Etc::ErrorMetric m_metric; 57 | float m_effort; 58 | }; 59 | 60 | } // namespace cuttlefish 61 | 62 | #endif // CUTTLEFISH_HAS_ETC 63 | -------------------------------------------------------------------------------- /lib/src/HalfFloat.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Aaron Barany 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "HalfFloat.h" 18 | 19 | #if CUTTLEFISH_WINDOWS 20 | #include 21 | #elif CUTTLEFISH_SSE 22 | #include 23 | #endif 24 | 25 | namespace cuttlefish 26 | { 27 | 28 | #if CUTTLEFISH_WINDOWS 29 | static void __get_cpuid(unsigned int level, unsigned int* eax, unsigned int* ebx, unsigned int* ecx, 30 | unsigned int* edx) 31 | { 32 | int cpuInfo[4]; 33 | __cpuid(cpuInfo, level); 34 | *eax = cpuInfo[0]; 35 | *ebx = cpuInfo[1]; 36 | *ecx = cpuInfo[2]; 37 | *edx = cpuInfo[3]; 38 | } 39 | #endif 40 | 41 | bool checkHasHardwareHalfFloat() 42 | { 43 | #if CUTTLEFISH_SSE 44 | const int f16cBit = 1 << 29; 45 | 46 | unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0; 47 | __get_cpuid(1, &eax, &ebx, &ecx, &edx); 48 | return (ecx & f16cBit) != 0; 49 | #elif CUTTLEFISH_NEON 50 | return true; 51 | #else 52 | return false; 53 | #endif 54 | } 55 | 56 | const bool hasHardwareHalfFloat = checkHasHardwareHalfFloat(); 57 | 58 | } // cuttlefish 59 | -------------------------------------------------------------------------------- /lib/src/HalfFloat.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Aaron Barany 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | 25 | #if CUTTLEFISH_X86_32 || CUTTLEFISH_X86_64 26 | #include 27 | #define CUTTLEFISH_SSE 1 28 | #define CUTTLEFISH_NEON 0 29 | #elif CUTTLEFISH_ARM_32 || CUTTLEFISH_ARM_64 30 | #include 31 | #define CUTTLEFISH_SSE 0 32 | #define CUTTLEFISH_NEON 1 33 | #else 34 | #define CUTTLEFISH_SSE 0 35 | #define CUTTLEFISH_NEON 0 36 | #endif 37 | 38 | #if CUTTLEFISH_SSE && CUTTLEFISH_CLANG 39 | #define CUTTLEFISH_START_HALF_FLOAT() \ 40 | _Pragma("clang attribute push(__attribute__((target(\"sse,sse2,f16c\"))), apply_to = function)") 41 | #define CUTTLEFISH_END_HALF_FLOAT() _Pragma("clang attribute pop") 42 | #elif CUTTLEFISH_SSE && CUTTLEFISH_GCC 43 | #define CUTTLEFISH_START_HALF_FLOAT() \ 44 | _Pragma("GCC push_options") \ 45 | _Pragma("GCC target(\"sse,sse2,f16c\")") 46 | #define CUTTLEFISH_END_HALF_FLOAT() _Pragma("GCC pop_options") 47 | #else 48 | #define CUTTLEFISH_START_HALF_FLOAT() 49 | #define CUTTLEFISH_END_HALF_FLOAT() 50 | #endif 51 | 52 | namespace cuttlefish 53 | { 54 | 55 | // Export for unit tests. 56 | CUTTLEFISH_EXPORT extern const bool hasHardwareHalfFloat; 57 | 58 | CUTTLEFISH_START_HALF_FLOAT() 59 | 60 | // NOTE: Always assume input has 4 floats, though output may be different. 61 | inline void packHardwareHalfFloat1(std::uint16_t* result, const float* value) 62 | { 63 | #if CUTTLEFISH_SSE 64 | __m128 f = _mm_loadu_ps(value); 65 | __m128i h = _mm_cvtps_ph(f, 0); 66 | *result = static_cast(_mm_cvtsi128_si32(h)); 67 | #elif CUTTLEFISH_NEON 68 | float32x4_t f = vld1q_f32(value); 69 | float16x4_t h = vcvt_f16_f32(f); 70 | vst1_lane_f16(reinterpret_cast(result), h, 0); 71 | #else 72 | CUTTLEFISH_UNUSED(result); 73 | CUTTLEFISH_UNUSED(value); 74 | assert(false); 75 | #endif 76 | } 77 | 78 | inline void packHardwareHalfFloat2(std::uint16_t* result, const float* value) 79 | { 80 | #if CUTTLEFISH_SSE 81 | __m128 f = _mm_loadu_ps(value); 82 | __m128i h = _mm_cvtps_ph(f, 0); 83 | *reinterpret_cast(result) = _mm_cvtsi128_si32(h); 84 | #elif CUTTLEFISH_NEON 85 | float32x4_t f = vld1q_f32(value); 86 | float16x4_t h = vcvt_f16_f32(f); 87 | vst1_lane_f16(reinterpret_cast(result), h, 0); 88 | vst1_lane_f16(reinterpret_cast(result) + 1, h, 1); 89 | #else 90 | CUTTLEFISH_UNUSED(result); 91 | CUTTLEFISH_UNUSED(value); 92 | assert(false); 93 | #endif 94 | } 95 | 96 | inline void packHardwareHalfFloat3(std::uint16_t* result, const float* value) 97 | { 98 | #if CUTTLEFISH_SSE 99 | __m128 f = _mm_loadu_ps(value); 100 | __m128i h = _mm_cvtps_ph(f, 0); 101 | std::uint64_t temp; 102 | _mm_storeu_si64(&temp, h); 103 | result[0] = reinterpret_cast(&temp)[0]; 104 | result[1] = reinterpret_cast(&temp)[1]; 105 | result[2] = reinterpret_cast(&temp)[2]; 106 | #elif CUTTLEFISH_NEON 107 | float32x4_t f = vld1q_f32(value); 108 | float16x4_t h = vcvt_f16_f32(f); 109 | vst1_lane_f16(reinterpret_cast(result), h, 0); 110 | vst1_lane_f16(reinterpret_cast(result) + 1, h, 1); 111 | vst1_lane_f16(reinterpret_cast(result) + 2, h, 2); 112 | #else 113 | CUTTLEFISH_UNUSED(result); 114 | CUTTLEFISH_UNUSED(value); 115 | assert(false); 116 | #endif 117 | } 118 | 119 | inline void packHardwareHalfFloat4(std::uint16_t* result, const float* value) 120 | { 121 | #if CUTTLEFISH_SSE 122 | __m128 f = _mm_loadu_ps(value); 123 | __m128i h = _mm_cvtps_ph(f, 0); 124 | _mm_storeu_si64(result, h); 125 | #elif CUTTLEFISH_NEON 126 | float32x4_t f = vld1q_f32(value); 127 | float16x4_t h = vcvt_f16_f32(f); 128 | vst1_f16(reinterpret_cast(result), h); 129 | #else 130 | CUTTLEFISH_UNUSED(result); 131 | CUTTLEFISH_UNUSED(value); 132 | assert(false); 133 | #endif 134 | } 135 | 136 | CUTTLEFISH_END_HALF_FLOAT() 137 | 138 | } // namespace cuttlefish 139 | -------------------------------------------------------------------------------- /lib/src/PvrtcConverter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2022 Aaron Barany 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #if CUTTLEFISH_HAS_PVRTC 18 | 19 | #include "PvrtcConverter.h" 20 | #include "Shared.h" 21 | #include 22 | #include 23 | #include 24 | 25 | #if CUTTLEFISH_GCC || CUTTLEFISH_CLANG 26 | #pragma GCC diagnostic push 27 | #pragma GCC diagnostic ignored "-Wconversion" 28 | #pragma GCC diagnostic ignored "-Wmissing-braces" 29 | #endif 30 | 31 | #include 32 | 33 | #if CUTTLEFISH_GCC || CUTTLEFISH_CLANG 34 | #pragma GCC diagnostic pop 35 | #endif 36 | 37 | namespace cuttlefish 38 | { 39 | 40 | PvrtcConverter::PvrtcConverter(const Texture& texture, const Image& image, Texture::Quality quality, 41 | unsigned int threadCount) 42 | : Converter(image) 43 | , m_format(texture.format()) 44 | , m_quality(quality) 45 | , m_threadCount(threadCount) 46 | , m_premultipliedAlpha(texture.alphaType() == Texture::Alpha::PreMultiplied) 47 | { 48 | } 49 | 50 | void PvrtcConverter::process(unsigned int, unsigned int, ThreadData*) 51 | { 52 | unsigned int width = image().width(), height = image().height(); 53 | const PVRTuint64 inPixelType = PVRTGENPIXELID4('r', 'g', 'b', 'a', 8, 8, 8, 8); 54 | pvrtexlib::PVRTexture pvrTexture(pvrtexlib::PVRTextureHeader(inPixelType, width, height, 1, 1, 55 | 1, 1, PVRTLCS_Linear, PVRTLVT_UnsignedByteNorm, m_premultipliedAlpha), nullptr); 56 | 57 | auto dstData = reinterpret_cast(pvrTexture.GetTextureDataPointer()); 58 | for (unsigned int y = 0; y < height; ++y) 59 | { 60 | const ColorRGBAf* scanline = reinterpret_cast(image().scanline(y)); 61 | for (unsigned int x = 0; x < width; ++x) 62 | { 63 | unsigned int index = (y*width + x)*4; 64 | dstData[index] = static_cast( 65 | std::round(clamp(scanline[x].r, 0.0f, 1.0f)*0xFF)); 66 | dstData[index + 1] = static_cast( 67 | std::round(clamp(scanline[x].g, 0.0f, 1.0f)*0xFF)); 68 | dstData[index + 2] = static_cast( 69 | std::round(clamp(scanline[x].b, 0.0f, 1.0f)*0xFF)); 70 | dstData[index + 3] = static_cast( 71 | std::round(clamp(scanline[x].a, 0.0f, 1.0f)*0xFF)); 72 | } 73 | } 74 | 75 | std::uint64_t pixelType; 76 | switch (m_format) 77 | { 78 | case Texture::Format::PVRTC1_RGB_2BPP: 79 | pixelType = PVRTLPF_PVRTCI_2bpp_RGB; 80 | break; 81 | case Texture::Format::PVRTC1_RGBA_2BPP: 82 | pixelType = PVRTLPF_PVRTCI_2bpp_RGBA; 83 | break; 84 | case Texture::Format::PVRTC1_RGB_4BPP: 85 | pixelType = PVRTLPF_PVRTCI_4bpp_RGB; 86 | break; 87 | case Texture::Format::PVRTC1_RGBA_4BPP: 88 | pixelType = PVRTLPF_PVRTCI_4bpp_RGBA; 89 | break; 90 | case Texture::Format::PVRTC2_RGBA_2BPP: 91 | pixelType = PVRTLPF_PVRTCII_2bpp; 92 | break; 93 | case Texture::Format::PVRTC2_RGBA_4BPP: 94 | pixelType = PVRTLPF_PVRTCII_4bpp; 95 | break; 96 | default: 97 | assert(false); 98 | return; 99 | } 100 | 101 | PVRTexLibCompressorQuality quality; 102 | switch (m_quality) 103 | { 104 | case Texture::Quality::Lowest: 105 | quality = PVRTLCQ_PVRTCFastest; 106 | break; 107 | case Texture::Quality::Low: 108 | quality = PVRTLCQ_PVRTCLow; 109 | break; 110 | case Texture::Quality::Normal: 111 | quality = PVRTLCQ_PVRTCNormal; 112 | break; 113 | case Texture::Quality::High: 114 | quality = PVRTLCQ_PVRTCHigh; 115 | break; 116 | case Texture::Quality::Highest: 117 | quality = PVRTLCQ_PVRTCBest; 118 | break; 119 | default: 120 | assert(false); 121 | return; 122 | } 123 | 124 | pvrTexture.Transcode(pixelType, PVRTLVT_UnsignedByteNorm, PVRTLCS_Linear, quality, 125 | m_threadCount); 126 | 127 | auto textureData = reinterpret_cast(pvrTexture.GetTextureDataPointer()); 128 | data().assign(textureData, textureData + pvrTexture.GetTextureDataSize()); 129 | } 130 | 131 | } // namespace cuttlefish 132 | 133 | #endif // CUTTLEFISH_HAS_PVRTC 134 | -------------------------------------------------------------------------------- /lib/src/PvrtcConverter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2022 Aaron Barany 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include "Converter.h" 21 | 22 | #if CUTTLEFISH_HAS_PVRTC 23 | 24 | namespace pvrtexture { class CPVRTexture; } 25 | 26 | namespace cuttlefish 27 | { 28 | 29 | class PvrtcConverter : public Converter 30 | { 31 | public: 32 | static const unsigned int blockDim = 4; 33 | 34 | PvrtcConverter(const Texture& texture, const Image& image, Texture::Quality quality, 35 | unsigned int threadCount); 36 | 37 | unsigned int jobsX() const override {return 1;} 38 | unsigned int jobsY() const override {return 1;} 39 | void process(unsigned int x, unsigned int y, ThreadData* threadData) override; 40 | 41 | private: 42 | Texture::Format m_format; 43 | Texture::Quality m_quality; 44 | unsigned int m_threadCount; 45 | bool m_premultipliedAlpha; 46 | }; 47 | 48 | } // namespace cuttlefish 49 | 50 | #endif // CUTTLEFISH_HAS_PVRTC 51 | -------------------------------------------------------------------------------- /lib/src/S3tcConverter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2021 Aaron Barany 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include "Converter.h" 21 | 22 | #if CUTTLEFISH_HAS_S3TC 23 | 24 | struct bc6h_enc_settings; 25 | struct bc7enc_compress_block_params; 26 | 27 | namespace ispc 28 | { 29 | struct bc7e_compress_block_params; 30 | } 31 | 32 | namespace cuttlefish 33 | { 34 | 35 | class S3tcConverter : public Converter 36 | { 37 | public: 38 | S3tcConverter(const Texture& texture, const Image& image, unsigned int blockSize, 39 | Texture::Quality quality); 40 | 41 | unsigned int jobsX() const override {return m_jobsX;} 42 | unsigned int jobsY() const override {return m_jobsY;} 43 | void process(unsigned int x, unsigned int y, ThreadData* threadData) override; 44 | 45 | Texture::Quality quality() const {return m_quality;} 46 | ColorSpace colorSpace() const {return m_colorSpace;} 47 | Texture::ColorMask colorMask() const {return m_colorMask;} 48 | bool weightAlpha() const {return m_weightAlpha;} 49 | virtual void compressBlock(void* block, ColorRGBAf* blockColors) = 0; 50 | 51 | private: 52 | unsigned int m_blockSize; 53 | unsigned int m_jobsX; 54 | unsigned int m_jobsY; 55 | ColorSpace m_colorSpace; 56 | Texture::Quality m_quality; 57 | Texture::ColorMask m_colorMask; 58 | bool m_weightAlpha; 59 | }; 60 | 61 | class Bc1Converter : public S3tcConverter 62 | { 63 | public: 64 | Bc1Converter(const Texture& texture, const Image& image, Texture::Quality quality); 65 | void compressBlock(void* block, ColorRGBAf* blockColors) override; 66 | 67 | private: 68 | std::uint32_t m_qualityLevel; 69 | }; 70 | 71 | class Bc1AConverter : public S3tcConverter 72 | { 73 | public: 74 | Bc1AConverter(const Texture& texture, const Image& image, Texture::Quality quality); 75 | void compressBlock(void* block, ColorRGBAf* blockColors) override; 76 | 77 | private: 78 | int m_squishFlags; 79 | std::uint32_t m_qualityLevel; 80 | }; 81 | 82 | class Bc2Converter : public S3tcConverter 83 | { 84 | public: 85 | Bc2Converter(const Texture& texture, const Image& image, Texture::Quality quality); 86 | void compressBlock(void* block, ColorRGBAf* blockColors) override; 87 | 88 | private: 89 | std::uint32_t m_qualityLevel; 90 | }; 91 | 92 | class Bc3Converter : public S3tcConverter 93 | { 94 | public: 95 | Bc3Converter(const Texture& texture, const Image& image, Texture::Quality quality); 96 | void compressBlock(void* block, ColorRGBAf* blockColors) override; 97 | 98 | private: 99 | std::uint32_t m_qualityLevel; 100 | std::uint32_t m_searchRadius; 101 | }; 102 | 103 | class Bc4Converter : public S3tcConverter 104 | { 105 | public: 106 | Bc4Converter(const Texture& texture, const Image& image, Texture::Quality quality, 107 | bool keepSign); 108 | ~Bc4Converter(); 109 | void compressBlock(void* block, ColorRGBAf* blockColors) override; 110 | 111 | private: 112 | bool m_signed; 113 | std::uint32_t m_searchRadius; 114 | void* m_compressonatorOptions; 115 | }; 116 | 117 | class Bc5Converter : public S3tcConverter 118 | { 119 | public: 120 | Bc5Converter(const Texture& texture, const Image& image, Texture::Quality quality, 121 | bool keepSign); 122 | ~Bc5Converter(); 123 | void compressBlock(void* block, ColorRGBAf* blockColors) override; 124 | 125 | private: 126 | bool m_signed; 127 | std::uint32_t m_searchRadius; 128 | void* m_compressonatorOptions; 129 | }; 130 | 131 | class Bc6HConverter : public S3tcConverter 132 | { 133 | public: 134 | Bc6HConverter(const Texture& texture, const Image& image, Texture::Quality quality, 135 | bool keepSign); 136 | ~Bc6HConverter(); 137 | void compressBlock(void* block, ColorRGBAf* blockColors) override; 138 | 139 | private: 140 | #if CUTTLEFISH_ISPC 141 | bc6h_enc_settings* m_ispcTexcompSettings; 142 | #endif 143 | void* m_compressonatorOptions; 144 | }; 145 | 146 | class Bc7Converter : public S3tcConverter 147 | { 148 | public: 149 | Bc7Converter(const Texture& texture, const Image& image, Texture::Quality quality); 150 | ~Bc7Converter(); 151 | void compressBlock(void* block, ColorRGBAf* blockColors) override; 152 | 153 | private: 154 | #if CUTTLEFISH_ISPC 155 | ispc::bc7e_compress_block_params* m_params; 156 | #else 157 | bc7enc_compress_block_params* m_params; 158 | #endif 159 | }; 160 | 161 | } // namespace cuttlefish 162 | 163 | #endif // CUTTLEFISH_HAS_S3TC 164 | -------------------------------------------------------------------------------- /lib/src/SaveDds.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Aaron Barany 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include 21 | 22 | namespace cuttlefish 23 | { 24 | 25 | bool isValidForDds(Texture::Format format, Texture::Type type); 26 | Texture::SaveResult saveDds(const Texture& texture, std::ostream& stream); 27 | 28 | } // namespace cuttlefish 29 | -------------------------------------------------------------------------------- /lib/src/SaveKtx.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Aaron Barany 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include 21 | 22 | namespace cuttlefish 23 | { 24 | 25 | bool isValidForKtx(Texture::Format format, Texture::Type type); 26 | Texture::SaveResult saveKtx(const Texture& texture, std::ostream& stream); 27 | 28 | } // namespace cuttlefish 29 | -------------------------------------------------------------------------------- /lib/src/SavePvr.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Aaron Barany 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include 21 | 22 | namespace cuttlefish 23 | { 24 | 25 | bool isValidForPvr(Texture::Format format, Texture::Type type); 26 | Texture::SaveResult savePvr(const Texture& texture, std::ostream& stream); 27 | 28 | } // namespace cuttlefish 29 | -------------------------------------------------------------------------------- /lib/src/Shared.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Aaron Barany 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "Shared.h" 18 | #include 19 | 20 | namespace cuttlefish 21 | { 22 | 23 | void readStreamData(std::vector& outData, std::istream& stream) 24 | { 25 | // Try to get the full size of the data by seeking. 26 | stream.seekg(0, std::ios_base::end); 27 | std::istream::pos_type pos = stream.tellg(); 28 | if (pos < 0) 29 | { 30 | // Fall back to reading bytes if we can't seek. Clear the stream bits in case the seek 31 | // set a fail bit. 32 | stream.clear(); 33 | 34 | constexpr std::size_t bufferSize = 4096; 35 | char buffer[bufferSize]; 36 | outData.clear(); 37 | do 38 | { 39 | stream.read(buffer, bufferSize); 40 | auto readSize = static_cast(stream.gcount()); 41 | if (readSize == 0) 42 | return; 43 | 44 | outData.insert(outData.end(), buffer, buffer + readSize); 45 | } while (true); 46 | return; 47 | } 48 | stream.seekg(0, std::ios_base::beg); 49 | 50 | // Resize will zero-initialize the vector. Theoretically calling reserve() and using 51 | // std::istreambuf_iterator to populate the data would have the fewest data writes, but is 52 | // more function call overhead and slower in practice. 53 | auto size = static_cast(pos); 54 | outData.resize(size); 55 | stream.read(reinterpret_cast(outData.data()), size); 56 | } 57 | 58 | } // namespace cuttlefish 59 | -------------------------------------------------------------------------------- /lib/src/Shared.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2022 Aaron Barany 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #define FOURCC(a, b, c, d) ((std::uint32_t)(a) | ((std::uint32_t)(b) << 8) | \ 25 | ((std::uint32_t)(c) << 16) | ((std::uint32_t)(d) << 24 )) 26 | 27 | namespace cuttlefish 28 | { 29 | 30 | inline float clamp(float v, float minVal, float maxVal) 31 | { 32 | if (v < minVal) 33 | return minVal; 34 | else if (v > maxVal) 35 | return maxVal; 36 | return v; 37 | } 38 | 39 | template 40 | bool write(std::ostream& stream, const T& value) 41 | { 42 | stream.write(reinterpret_cast(&value), sizeof(T)); 43 | return stream.good(); 44 | } 45 | 46 | void readStreamData(std::vector& outData, std::istream& stream); 47 | 48 | } // namespace cuttlefish 49 | -------------------------------------------------------------------------------- /lib/src/StandardConverter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Aaron Barany 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "StandardConverter.h" 18 | 19 | namespace cuttlefish 20 | { 21 | 22 | void R4G4Converter::process(unsigned int x, unsigned int, ThreadData*) 23 | { 24 | std::uint8_t* curData = reinterpret_cast(data().data()) + x*batchSize; 25 | unsigned int row = x*batchSize/image().width(); 26 | const ColorRGBAf* scanline = reinterpret_cast(image().scanline(row)); 27 | for (unsigned int i = 0; i < batchSize; ++i) 28 | { 29 | unsigned int curRow = (x*batchSize + i)/image().width(); 30 | if (curRow != row) 31 | { 32 | if (curRow >= image().height()) 33 | break; 34 | row = curRow; 35 | scanline = reinterpret_cast(image().scanline(row)); 36 | } 37 | 38 | unsigned int col = (x*batchSize + i) % image().width(); 39 | auto r = static_cast(std::round(clamp(scanline[col].r, 0.0f, 1.0f)*0xF)) & 0xF; 40 | auto g = static_cast(std::round(clamp(scanline[col].g, 0.0f, 1.0f)*0xF)) & 0xF; 41 | curData[i] = static_cast(g | (r << 4)); 42 | } 43 | } 44 | 45 | void R4G4B4A4Converter::process(unsigned int x, unsigned int, ThreadData*) 46 | { 47 | std::uint16_t* curData = reinterpret_cast(data().data()) + x*batchSize; 48 | unsigned int row = x*batchSize/image().width(); 49 | const ColorRGBAf* scanline = reinterpret_cast(image().scanline(row)); 50 | for (unsigned int i = 0; i < batchSize; ++i) 51 | { 52 | unsigned int curRow = (x*batchSize + i)/image().width(); 53 | if (curRow != row) 54 | { 55 | if (curRow >= image().height()) 56 | break; 57 | row = curRow; 58 | scanline = reinterpret_cast(image().scanline(row)); 59 | } 60 | 61 | unsigned int col = (x*batchSize + i) % image().width(); 62 | auto r = static_cast(std::round(clamp(scanline[col].r, 0, 1)*0xF)) & 0xF; 63 | auto g = static_cast(std::round(clamp(scanline[col].g, 0, 1)*0xF)) & 0xF; 64 | auto b = static_cast(std::round(clamp(scanline[col].b, 0, 1)*0xF)) & 0xF; 65 | auto a = static_cast(std::round(clamp(scanline[col].a, 0, 1)*0xF)) & 0xF; 66 | curData[i] = static_cast(a | (b << 4) | (g << 8) | (r << 12)); 67 | } 68 | } 69 | 70 | void B4G4R4A4Converter::process(unsigned int x, unsigned int, ThreadData*) 71 | { 72 | std::uint16_t* curData = reinterpret_cast(data().data()) + x*batchSize; 73 | unsigned int row = x*batchSize/image().width(); 74 | const ColorRGBAf* scanline = reinterpret_cast(image().scanline(row)); 75 | for (unsigned int i = 0; i < batchSize; ++i) 76 | { 77 | unsigned int curRow = (x*batchSize + i)/image().width(); 78 | if (curRow != row) 79 | { 80 | if (curRow >= image().height()) 81 | break; 82 | row = curRow; 83 | scanline = reinterpret_cast(image().scanline(row)); 84 | } 85 | 86 | unsigned int col = (x*batchSize + i) % image().width(); 87 | auto r = static_cast(std::round(clamp(scanline[col].r, 0, 1)*0xF)) & 0xF; 88 | auto g = static_cast(std::round(clamp(scanline[col].g, 0, 1)*0xF)) & 0xF; 89 | auto b = static_cast(std::round(clamp(scanline[col].b, 0, 1)*0xF)) & 0xF; 90 | auto a = static_cast(std::round(clamp(scanline[col].a, 0, 1)*0xF)) & 0xF; 91 | curData[i] = static_cast(a | (r << 4) | (g << 8) | (b << 12)); 92 | } 93 | } 94 | 95 | void A4R4G4B4Converter::process(unsigned int x, unsigned int, ThreadData*) 96 | { 97 | std::uint16_t* curData = reinterpret_cast(data().data()) + x*batchSize; 98 | unsigned int row = x*batchSize/image().width(); 99 | const ColorRGBAf* scanline = reinterpret_cast(image().scanline(row)); 100 | for (unsigned int i = 0; i < batchSize; ++i) 101 | { 102 | unsigned int curRow = (x*batchSize + i)/image().width(); 103 | if (curRow != row) 104 | { 105 | if (curRow >= image().height()) 106 | break; 107 | row = curRow; 108 | scanline = reinterpret_cast(image().scanline(row)); 109 | } 110 | 111 | unsigned int col = (x*batchSize + i) % image().width(); 112 | auto r = static_cast(std::round(clamp(scanline[col].r, 0, 1)*0xF)) & 0xF; 113 | auto g = static_cast(std::round(clamp(scanline[col].g, 0, 1)*0xF)) & 0xF; 114 | auto b = static_cast(std::round(clamp(scanline[col].b, 0, 1)*0xF)) & 0xF; 115 | auto a = static_cast(std::round(clamp(scanline[col].a, 0, 1)*0xF)) & 0xF; 116 | curData[i] = static_cast(b | (g << 4) | (r << 8) | (a << 12)); 117 | } 118 | } 119 | 120 | void R5G6B5Converter::process(unsigned int x, unsigned int, ThreadData*) 121 | { 122 | std::uint16_t* curData = reinterpret_cast(data().data()) + x*batchSize; 123 | unsigned int row = x*batchSize/image().width(); 124 | const ColorRGBAf* scanline = reinterpret_cast(image().scanline(row)); 125 | for (unsigned int i = 0; i < batchSize; ++i) 126 | { 127 | unsigned int curRow = (x*batchSize + i)/image().width(); 128 | if (curRow != row) 129 | { 130 | if (curRow >= image().height()) 131 | break; 132 | row = curRow; 133 | scanline = reinterpret_cast(image().scanline(row)); 134 | } 135 | 136 | unsigned int col = (x*batchSize + i) % image().width(); 137 | auto r = static_cast(std::round(clamp(scanline[col].r, 0.0f, 1.0f)*0x1F)) & 0x1F; 138 | auto g = static_cast(std::round(clamp(scanline[col].g, 0.0f, 1.0f)*0x3F)) & 0x3F; 139 | auto b = static_cast(std::round(clamp(scanline[col].b, 0.0f, 1.0f)*0x1F)) & 0x1F; 140 | curData[i] = static_cast(b | (g << 5) | (r << 11)); 141 | } 142 | } 143 | 144 | void B5G6R5Converter::process(unsigned int x, unsigned int, ThreadData*) 145 | { 146 | std::uint16_t* curData = reinterpret_cast(data().data()) + x*batchSize; 147 | unsigned int row = x*batchSize/image().width(); 148 | const ColorRGBAf* scanline = reinterpret_cast(image().scanline(row)); 149 | for (unsigned int i = 0; i < batchSize; ++i) 150 | { 151 | unsigned int curRow = (x*batchSize + i)/image().width(); 152 | if (curRow != row) 153 | { 154 | if (curRow >= image().height()) 155 | break; 156 | row = curRow; 157 | scanline = reinterpret_cast(image().scanline(row)); 158 | } 159 | 160 | unsigned int col = (x*batchSize + i) % image().width(); 161 | auto r = static_cast(std::round(clamp(scanline[col].r, 0.0f, 1.0f)*0x1F)) & 0x1F; 162 | auto g = static_cast(std::round(clamp(scanline[col].g, 0.0f, 1.0f)*0x3F)) & 0x3F; 163 | auto b = static_cast(std::round(clamp(scanline[col].b, 0.0f, 1.0f)*0x1F)) & 0x1F; 164 | curData[i] = static_cast(r | (g << 5) | (b << 11)); 165 | } 166 | } 167 | 168 | void R5G5B5A1Converter::process(unsigned int x, unsigned int, ThreadData*) 169 | { 170 | std::uint16_t* curData = reinterpret_cast(data().data()) + x*batchSize; 171 | unsigned int row = x*batchSize/image().width(); 172 | const ColorRGBAf* scanline = reinterpret_cast(image().scanline(row)); 173 | for (unsigned int i = 0; i < batchSize; ++i) 174 | { 175 | unsigned int curRow = (x*batchSize + i)/image().width(); 176 | if (curRow != row) 177 | { 178 | if (curRow >= image().height()) 179 | break; 180 | row = curRow; 181 | scanline = reinterpret_cast(image().scanline(row)); 182 | } 183 | 184 | unsigned int col = (x*batchSize + i) % image().width(); 185 | auto r = static_cast(std::round(clamp(scanline[col].r, 0.0f, 1.0f)*0x1F)) & 0x1F; 186 | auto g = static_cast(std::round(clamp(scanline[col].g, 0.0f, 1.0f)*0x1F)) & 0x1F; 187 | auto b = static_cast(std::round(clamp(scanline[col].b, 0.0f, 1.0f)*0x1F)) & 0x1F; 188 | auto a = static_cast(std::round(clamp(scanline[col].a, 0.0f, 1.0f))); 189 | curData[i] = static_cast(a | (b << 1) | (g << 6) | (r << 11)); 190 | } 191 | } 192 | 193 | void B5G5R5A1Converter::process(unsigned int x, unsigned int, ThreadData*) 194 | { 195 | std::uint16_t* curData = reinterpret_cast(data().data()) + x*batchSize; 196 | unsigned int row = x*batchSize/image().width(); 197 | const ColorRGBAf* scanline = reinterpret_cast(image().scanline(row)); 198 | for (unsigned int i = 0; i < batchSize; ++i) 199 | { 200 | unsigned int curRow = (x*batchSize + i)/image().width(); 201 | if (curRow != row) 202 | { 203 | if (curRow >= image().height()) 204 | break; 205 | row = curRow; 206 | scanline = reinterpret_cast(image().scanline(row)); 207 | } 208 | 209 | unsigned int col = (x*batchSize + i) % image().width(); 210 | auto r = static_cast(std::round(clamp(scanline[col].r, 0.0f, 1.0f)*0x1F)) & 0x1F; 211 | auto g = static_cast(std::round(clamp(scanline[col].g, 0.0f, 1.0f)*0x1F)) & 0x1F; 212 | auto b = static_cast(std::round(clamp(scanline[col].b, 0.0f, 1.0f)*0x1F)) & 0x1F; 213 | auto a = static_cast(std::round(clamp(scanline[col].a, 0.0f, 1.0f))); 214 | curData[i] = static_cast(a | (r << 1) | (g << 6) | (b << 11)); 215 | } 216 | } 217 | 218 | void A1R5G5B5Converter::process(unsigned int x, unsigned int, ThreadData*) 219 | { 220 | std::uint16_t* curData = reinterpret_cast(data().data()) + x*batchSize; 221 | unsigned int row = x*batchSize/image().width(); 222 | const ColorRGBAf* scanline = reinterpret_cast(image().scanline(row)); 223 | for (unsigned int i = 0; i < batchSize; ++i) 224 | { 225 | unsigned int curRow = (x*batchSize + i)/image().width(); 226 | if (curRow != row) 227 | { 228 | if (curRow >= image().height()) 229 | break; 230 | row = curRow; 231 | scanline = reinterpret_cast(image().scanline(row)); 232 | } 233 | 234 | unsigned int col = (x*batchSize + i) % image().width(); 235 | auto r = static_cast(std::round(clamp(scanline[col].r, 0.0f, 1.0f)*0x1F)) & 0x1F; 236 | auto g = static_cast(std::round(clamp(scanline[col].g, 0.0f, 1.0f)*0x1F)) & 0x1F; 237 | auto b = static_cast(std::round(clamp(scanline[col].b, 0.0f, 1.0f)*0x1F)) & 0x1F; 238 | auto a = static_cast(std::round(clamp(scanline[col].a, 0.0f, 1.0f))); 239 | curData[i] = static_cast(b | (g << 5) | (r << 10) | (a << 15)); 240 | } 241 | } 242 | 243 | void B8G8R8Converter::process(unsigned int x, unsigned int, ThreadData*) 244 | { 245 | std::uint8_t* curData = reinterpret_cast(data().data()) + x*batchSize*3; 246 | unsigned int row = x*batchSize/image().width(); 247 | const ColorRGBAf* scanline = reinterpret_cast(image().scanline(row)); 248 | for (unsigned int i = 0; i < batchSize; ++i) 249 | { 250 | unsigned int curRow = (x*batchSize + i)/image().width(); 251 | if (curRow != row) 252 | { 253 | if (curRow >= image().height()) 254 | break; 255 | row = curRow; 256 | scanline = reinterpret_cast(image().scanline(row)); 257 | } 258 | 259 | unsigned int col = (x*batchSize + i) % image().width(); 260 | curData[i*3] = static_cast( 261 | std::round(clamp(scanline[col].b, 0.0f, 1.0f)*0xFF)); 262 | curData[i*3 + 1] = static_cast( 263 | std::round(clamp(scanline[col].g, 0.0f, 1.0f)*0xFF)); 264 | curData[i*3 + 2] = static_cast( 265 | std::round(clamp(scanline[col].r, 0.0f, 1.0f)*0xFF)); 266 | } 267 | } 268 | 269 | void B8G8R8A8Converter::process(unsigned int x, unsigned int, ThreadData*) 270 | { 271 | std::uint8_t* curData = reinterpret_cast(data().data()) + x*batchSize*4; 272 | unsigned int row = x*batchSize/image().width(); 273 | const ColorRGBAf* scanline = reinterpret_cast(image().scanline(row)); 274 | for (unsigned int i = 0; i < batchSize; ++i) 275 | { 276 | unsigned int curRow = (x*batchSize + i)/image().width(); 277 | if (curRow != row) 278 | { 279 | if (curRow >= image().height()) 280 | break; 281 | row = curRow; 282 | scanline = reinterpret_cast(image().scanline(row)); 283 | } 284 | 285 | unsigned int col = (x*batchSize + i) % image().width(); 286 | curData[i*4] = static_cast( 287 | std::round(clamp(scanline[col].b, 0.0f, 1.0f)*0xFF)); 288 | curData[i*4 + 1] = static_cast( 289 | std::round(clamp(scanline[col].g, 0.0f, 1.0f)*0xFF)); 290 | curData[i*4 + 2] = static_cast( 291 | std::round(clamp(scanline[col].r, 0.0f, 1.0f)*0xFF)); 292 | curData[i*4 + 3] = static_cast( 293 | std::round(clamp(scanline[col].a, 0.0f, 1.0f)*0xFF)); 294 | } 295 | } 296 | 297 | void A8B8G8R8Converter::process(unsigned int x, unsigned int, ThreadData*) 298 | { 299 | std::uint8_t* curData = reinterpret_cast(data().data()) + x*batchSize*4; 300 | unsigned int row = x*batchSize/image().width(); 301 | const ColorRGBAf* scanline = reinterpret_cast(image().scanline(row)); 302 | for (unsigned int i = 0; i < batchSize; ++i) 303 | { 304 | unsigned int curRow = (x*batchSize + i)/image().width(); 305 | if (curRow != row) 306 | { 307 | if (curRow >= image().height()) 308 | break; 309 | row = curRow; 310 | scanline = reinterpret_cast(image().scanline(row)); 311 | } 312 | 313 | unsigned int col = (x*batchSize + i) % image().width(); 314 | curData[i*4] = static_cast( 315 | std::round(clamp(scanline[col].a, 0.0f, 1.0f)*0xFF)); 316 | curData[i*4 + 1] = static_cast( 317 | std::round(clamp(scanline[col].b, 0.0f, 1.0f)*0xFF)); 318 | curData[i*4 + 2] = static_cast( 319 | std::round(clamp(scanline[col].g, 0.0f, 1.0f)*0xFF)); 320 | curData[i*4 + 3] = static_cast( 321 | std::round(clamp(scanline[col].r, 0.0f, 1.0f)*0xFF)); 322 | } 323 | } 324 | 325 | void A2R10G10B10UNormConverter::process(unsigned int x, unsigned int, ThreadData*) 326 | { 327 | std::uint32_t* curData = reinterpret_cast(data().data()) + x*batchSize; 328 | unsigned int row = x*batchSize/image().width(); 329 | const ColorRGBAf* scanline = reinterpret_cast(image().scanline(row)); 330 | for (unsigned int i = 0; i < batchSize; ++i) 331 | { 332 | unsigned int curRow = (x*batchSize + i)/image().width(); 333 | if (curRow != row) 334 | { 335 | if (curRow >= image().height()) 336 | break; 337 | row = curRow; 338 | scanline = reinterpret_cast(image().scanline(row)); 339 | } 340 | 341 | unsigned int col = (x*batchSize + i) % image().width(); 342 | auto r = static_cast(std::round(clamp(scanline[col].r, 0.0f, 1.0f)*0x3FF)) & 0x3FF; 343 | auto g = static_cast(std::round(clamp(scanline[col].g, 0.0f, 1.0f)*0x3FF)) & 0x3FF; 344 | auto b = static_cast(std::round(clamp(scanline[col].b, 0.0f, 1.0f)*0x3FF)) & 0x3FF; 345 | auto a = static_cast(std::round(clamp(scanline[col].a, 0.0f, 1.0f)*0x3)) & 0x3; 346 | curData[i] = static_cast(b | (g << 10) | (r << 20) | (a << 30)); 347 | } 348 | } 349 | 350 | void A2R10G10B10UIntConverter::process(unsigned int x, unsigned int, ThreadData*) 351 | { 352 | std::uint32_t* curData = reinterpret_cast(data().data()) + x*batchSize; 353 | unsigned int row = x*batchSize/image().width(); 354 | const ColorRGBAf* scanline = reinterpret_cast(image().scanline(row)); 355 | for (unsigned int i = 0; i < batchSize; ++i) 356 | { 357 | unsigned int curRow = (x*batchSize + i)/image().width(); 358 | if (curRow != row) 359 | { 360 | if (curRow >= image().height()) 361 | break; 362 | row = curRow; 363 | scanline = reinterpret_cast(image().scanline(row)); 364 | } 365 | 366 | unsigned int col = (x*batchSize + i) % image().width(); 367 | auto r = static_cast(std::round(clamp(scanline[col].r, 0, 0x3FF))); 368 | auto g = static_cast(std::round(clamp(scanline[col].g, 0, 0x3FF))); 369 | auto b = static_cast(std::round(clamp(scanline[col].b, 0, 0x3FF))); 370 | auto a = static_cast(std::round(clamp(scanline[col].a, 0, 0x3))); 371 | curData[i] = static_cast(b | (g << 10) | (r << 20) | (a << 30)); 372 | } 373 | } 374 | 375 | void A2B10G10R10UNormConverter::process(unsigned int x, unsigned int, ThreadData*) 376 | { 377 | std::uint32_t* curData = reinterpret_cast(data().data()) + x*batchSize; 378 | unsigned int row = x*batchSize/image().width(); 379 | const ColorRGBAf* scanline = reinterpret_cast(image().scanline(row)); 380 | for (unsigned int i = 0; i < batchSize; ++i) 381 | { 382 | unsigned int curRow = (x*batchSize + i)/image().width(); 383 | if (curRow != row) 384 | { 385 | if (curRow >= image().height()) 386 | break; 387 | row = curRow; 388 | scanline = reinterpret_cast(image().scanline(row)); 389 | } 390 | 391 | unsigned int col = (x*batchSize + i) % image().width(); 392 | auto r = static_cast(std::round(clamp(scanline[col].r, 0.0f, 1.0f)*0x3FF)) & 0x3FF; 393 | auto g = static_cast(std::round(clamp(scanline[col].g, 0.0f, 1.0f)*0x3FF)) & 0x3FF; 394 | auto b = static_cast(std::round(clamp(scanline[col].b, 0.0f, 1.0f)*0x3FF)) & 0x3FF; 395 | auto a = static_cast(std::round(clamp(scanline[col].a, 0.0f, 1.0f)*0x3)) & 0x3; 396 | curData[i] = static_cast(r | (g << 10) | (b << 20) | (a << 30)); 397 | } 398 | } 399 | 400 | void A2B10G10R10UIntConverter::process(unsigned int x, unsigned int, ThreadData*) 401 | { 402 | std::uint32_t* curData = reinterpret_cast(data().data()) + x*batchSize; 403 | unsigned int row = x*batchSize/image().width(); 404 | const ColorRGBAf* scanline = reinterpret_cast(image().scanline(row)); 405 | for (unsigned int i = 0; i < batchSize; ++i) 406 | { 407 | unsigned int curRow = (x*batchSize + i)/image().width(); 408 | if (curRow != row) 409 | { 410 | if (curRow >= image().height()) 411 | break; 412 | row = curRow; 413 | scanline = reinterpret_cast(image().scanline(row)); 414 | } 415 | 416 | unsigned int col = (x*batchSize + i) % image().width(); 417 | auto r = static_cast(std::round(clamp(scanline[col].r, 0, 0x3FF))); 418 | auto g = static_cast(std::round(clamp(scanline[col].g, 0, 0x3FF))); 419 | auto b = static_cast(std::round(clamp(scanline[col].b, 0, 0x3FF))); 420 | auto a = static_cast(std::round(clamp(scanline[col].a, 0, 0x3))); 421 | curData[i] = static_cast(r | (g << 10) | (b << 20) | (a << 30)); 422 | } 423 | } 424 | 425 | void B10R11R11UFloatConverter::process(unsigned int x, unsigned int, ThreadData*) 426 | { 427 | std::uint32_t* curData = reinterpret_cast(data().data()) + x*batchSize; 428 | unsigned int row = x*batchSize/image().width(); 429 | const ColorRGBAf* scanline = reinterpret_cast(image().scanline(row)); 430 | for (unsigned int i = 0; i < batchSize; ++i) 431 | { 432 | unsigned int curRow = (x*batchSize + i)/image().width(); 433 | if (curRow != row) 434 | { 435 | if (curRow >= image().height()) 436 | break; 437 | row = curRow; 438 | scanline = reinterpret_cast(image().scanline(row)); 439 | } 440 | 441 | unsigned int col = (x*batchSize + i) % image().width(); 442 | curData[i] = glm::packF2x11_1x10(*reinterpret_cast(scanline + col)); 443 | } 444 | } 445 | 446 | void E5B9G9R9UFloatConverter::process(unsigned int x, unsigned int, ThreadData*) 447 | { 448 | std::uint32_t* curData = reinterpret_cast(data().data()) + x*batchSize; 449 | unsigned int row = x*batchSize/image().width(); 450 | const ColorRGBAf* scanline = reinterpret_cast(image().scanline(row)); 451 | for (unsigned int i = 0; i < batchSize; ++i) 452 | { 453 | unsigned int curRow = (x*batchSize + i)/image().width(); 454 | if (curRow != row) 455 | { 456 | if (curRow >= image().height()) 457 | break; 458 | row = curRow; 459 | scanline = reinterpret_cast(image().scanline(row)); 460 | } 461 | 462 | unsigned int col = (x*batchSize + i) % image().width(); 463 | curData[i] = glm::packF3x9_E1x5(*reinterpret_cast(scanline + col)); 464 | } 465 | } 466 | 467 | } // namespace cuttlefish 468 | -------------------------------------------------------------------------------- /lib/src/StandardConverter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2023 Aaron Barany 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | 21 | #include "Converter.h" 22 | #include "HalfFloat.h" 23 | #include "Shared.h" 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #if CUTTLEFISH_GCC 31 | #pragma GCC diagnostic push 32 | #pragma GCC diagnostic ignored "-Wconversion" 33 | #endif 34 | 35 | #include 36 | 37 | #if CUTTLEFISH_GCC 38 | #pragma GCC diagnostic pop 39 | #endif 40 | 41 | namespace cuttlefish 42 | { 43 | 44 | template 45 | class StandardConverter : public Converter 46 | { 47 | public: 48 | static const unsigned int batchSize = 32; 49 | 50 | explicit StandardConverter(const Image& image) 51 | : Converter(image) 52 | { 53 | data().resize(image.width()*image.height()*sizeof(T)*C); 54 | } 55 | 56 | unsigned int jobsX() const override 57 | { 58 | // Only give jobs in 1 dimension to guarantee minimum alignment for multithreading. 59 | return (image().width()*image().height() + batchSize - 1)/batchSize; 60 | } 61 | 62 | unsigned int jobsY() const override 63 | { 64 | return 1; 65 | } 66 | }; 67 | 68 | template 69 | class UNormConverter : public StandardConverter 70 | { 71 | public: 72 | using StandardConverter::batchSize; 73 | using typename Converter::ThreadData; 74 | using Converter::data; 75 | using Converter::image; 76 | 77 | explicit UNormConverter(const Image& image) 78 | : StandardConverter(image) 79 | { 80 | } 81 | 82 | void process(unsigned int x, unsigned int, ThreadData*) override 83 | { 84 | const T maxVal = std::numeric_limits::max(); 85 | T* curData = reinterpret_cast(data().data()) + x*batchSize*C; 86 | unsigned int row = x*batchSize/image().width(); 87 | const float* scanline = reinterpret_cast(image().scanline(row)); 88 | for (unsigned int i = 0; i < batchSize; ++i) 89 | { 90 | unsigned int curRow = (x*batchSize + i)/image().width(); 91 | if (curRow != row) 92 | { 93 | if (curRow >= image().height()) 94 | break; 95 | row = curRow; 96 | scanline = reinterpret_cast(image().scanline(row)); 97 | } 98 | 99 | unsigned int col = (x*batchSize + i) % image().width(); 100 | for (unsigned int c = 0; c < C; ++c) 101 | { 102 | curData[i*C + c] = static_cast( 103 | std::round(clamp(scanline[col*4 + c], 0.0f, 1.0f)*maxVal)); 104 | } 105 | } 106 | } 107 | }; 108 | 109 | template 110 | class SNormConverter : public StandardConverter 111 | { 112 | public: 113 | using StandardConverter::batchSize; 114 | using typename Converter::ThreadData; 115 | using Converter::data; 116 | using Converter::image; 117 | 118 | explicit SNormConverter(const Image& image) 119 | : StandardConverter(image) 120 | { 121 | } 122 | 123 | void process(unsigned int x, unsigned int, ThreadData*) override 124 | { 125 | const T maxVal = std::numeric_limits::max(); 126 | T* curData = reinterpret_cast(data().data()) + x*batchSize*C; 127 | unsigned int row = x*batchSize/image().width(); 128 | const float* scanline = reinterpret_cast(image().scanline(row)); 129 | for (unsigned int i = 0; i < batchSize; ++i) 130 | { 131 | unsigned int curRow = (x*batchSize + i)/image().width(); 132 | if (curRow != row) 133 | { 134 | if (curRow >= image().height()) 135 | break; 136 | row = curRow; 137 | scanline = reinterpret_cast(image().scanline(row)); 138 | } 139 | 140 | unsigned int col = (x*batchSize + i) % image().width(); 141 | for (unsigned int c = 0; c < C; ++c) 142 | { 143 | curData[i*C + c] = static_cast( 144 | std::round(clamp(scanline[col*4 + c], -1.0f, 1.0f)*maxVal)); 145 | } 146 | } 147 | } 148 | }; 149 | 150 | template 151 | class IntConverter : public StandardConverter 152 | { 153 | public: 154 | using StandardConverter::batchSize; 155 | using typename Converter::ThreadData; 156 | using Converter::data; 157 | using Converter::image; 158 | 159 | explicit IntConverter(const Image& image) 160 | : StandardConverter(image) 161 | { 162 | } 163 | 164 | void process(unsigned int x, unsigned int, ThreadData*) override 165 | { 166 | const float minVal = static_cast(std::numeric_limits::min()); 167 | const float maxVal = static_cast(std::numeric_limits::max()); 168 | 169 | T* curData = reinterpret_cast(data().data()) + x*batchSize*C; 170 | unsigned int row = x*batchSize/image().width(); 171 | const float* scanline = reinterpret_cast(image().scanline(row)); 172 | for (unsigned int i = 0; i < batchSize; ++i) 173 | { 174 | unsigned int curRow = (x*batchSize + i)/image().width(); 175 | if (curRow != row) 176 | { 177 | if (curRow >= image().height()) 178 | break; 179 | row = curRow; 180 | scanline = reinterpret_cast(image().scanline(row)); 181 | } 182 | 183 | unsigned int col = (x*batchSize + i) % image().width(); 184 | for (unsigned int c = 0; c < C; ++c) 185 | { 186 | curData[i*C + c] = static_cast( 187 | std::round(clamp(scanline[col*4 + c], minVal, maxVal))); 188 | } 189 | } 190 | } 191 | }; 192 | 193 | template 194 | class FloatConverter : public StandardConverter 195 | { 196 | public: 197 | using StandardConverter::batchSize; 198 | using typename Converter::ThreadData; 199 | using Converter::data; 200 | using Converter::image; 201 | 202 | explicit FloatConverter(const Image& image) 203 | : StandardConverter(image) 204 | { 205 | } 206 | 207 | void process(unsigned int x, unsigned int, ThreadData*) override 208 | { 209 | float* curData = reinterpret_cast(data().data()) + x*batchSize*C; 210 | unsigned int row = x*batchSize/image().width(); 211 | const float* scanline = reinterpret_cast(image().scanline(row)); 212 | for (unsigned int i = 0; i < batchSize; ++i) 213 | { 214 | unsigned int curRow = (x*batchSize + i)/image().width(); 215 | if (curRow != row) 216 | { 217 | if (curRow >= image().height()) 218 | break; 219 | row = curRow; 220 | scanline = reinterpret_cast(image().scanline(row)); 221 | } 222 | 223 | unsigned int col = (x*batchSize + i) % image().width(); 224 | for (unsigned int c = 0; c < C; ++c) 225 | curData[i*C + c] = scanline[col*4 + c]; 226 | } 227 | } 228 | }; 229 | 230 | template 231 | class HalfConverter : public StandardConverter 232 | { 233 | public: 234 | using StandardConverter::batchSize; 235 | using typename Converter::ThreadData; 236 | using Converter::data; 237 | using Converter::image; 238 | 239 | explicit HalfConverter(const Image& image) 240 | : StandardConverter(image) 241 | { 242 | } 243 | 244 | void process(unsigned int x, unsigned int, ThreadData*) override 245 | { 246 | std::uint16_t* curData = reinterpret_cast(data().data()) + x*batchSize*C; 247 | unsigned int row = x*batchSize/image().width(); 248 | const float* scanline = reinterpret_cast(image().scanline(row)); 249 | 250 | if (hasHardwareHalfFloat) 251 | processHardware(x, curData, row, scanline); 252 | else 253 | { 254 | for (unsigned int i = 0; i < batchSize; ++i) 255 | { 256 | unsigned int curRow = (x*batchSize + i)/image().width(); 257 | if (curRow != row) 258 | { 259 | if (curRow >= image().height()) 260 | break; 261 | row = curRow; 262 | scanline = reinterpret_cast(image().scanline(row)); 263 | } 264 | 265 | unsigned int col = (x*batchSize + i) % image().width(); 266 | for (unsigned int c = 0; c < C; ++c) 267 | curData[i*C + c] = glm::packHalf(glm::vec1(scanline[col*4 + c])).x; 268 | } 269 | } 270 | } 271 | 272 | private: 273 | void processHardware(unsigned int x, std::uint16_t* curData, unsigned int row, 274 | const float* scanline); 275 | }; 276 | 277 | CUTTLEFISH_START_HALF_FLOAT() 278 | 279 | template 280 | void HalfConverter::processHardware(unsigned int x, std::uint16_t* curData, unsigned int row, 281 | const float* scanline) 282 | { 283 | for (unsigned int i = 0; i < batchSize; ++i) 284 | { 285 | unsigned int curRow = (x*batchSize + i)/image().width(); 286 | if (curRow != row) 287 | { 288 | if (curRow >= image().height()) 289 | break; 290 | row = curRow; 291 | scanline = reinterpret_cast(image().scanline(row)); 292 | } 293 | 294 | unsigned int col = (x*batchSize + i) % image().width(); 295 | switch (C) 296 | { 297 | case 1: 298 | packHardwareHalfFloat1(curData + i*C, scanline + col*4); 299 | break; 300 | case 2: 301 | packHardwareHalfFloat2(curData + i*C, scanline + col*4); 302 | break; 303 | case 3: 304 | packHardwareHalfFloat3(curData + i*C, scanline + col*4); 305 | break; 306 | case 4: 307 | packHardwareHalfFloat4(curData + i*C, scanline + col*4); 308 | break; 309 | default: 310 | assert(false); 311 | } 312 | } 313 | } 314 | 315 | CUTTLEFISH_END_HALF_FLOAT() 316 | 317 | class R4G4Converter : public StandardConverter 318 | { 319 | public: 320 | explicit R4G4Converter(const Image& image) 321 | : StandardConverter(image) 322 | { 323 | } 324 | 325 | void process(unsigned int x, unsigned int, ThreadData*) override; 326 | }; 327 | 328 | class R4G4B4A4Converter : public StandardConverter 329 | { 330 | public: 331 | explicit R4G4B4A4Converter(const Image& image) 332 | : StandardConverter(image) 333 | { 334 | } 335 | 336 | void process(unsigned int x, unsigned int, ThreadData*) override; 337 | }; 338 | 339 | class B4G4R4A4Converter : public StandardConverter 340 | { 341 | public: 342 | explicit B4G4R4A4Converter(const Image& image) 343 | : StandardConverter(image) 344 | { 345 | } 346 | 347 | void process(unsigned int x, unsigned int, ThreadData*) override; 348 | }; 349 | 350 | class A4R4G4B4Converter : public StandardConverter 351 | { 352 | public: 353 | explicit A4R4G4B4Converter(const Image& image) 354 | : StandardConverter(image) 355 | { 356 | } 357 | 358 | void process(unsigned int x, unsigned int, ThreadData*) override; 359 | }; 360 | 361 | class R5G6B5Converter : public StandardConverter 362 | { 363 | public: 364 | explicit R5G6B5Converter(const Image& image) 365 | : StandardConverter(image) 366 | { 367 | } 368 | 369 | void process(unsigned int x, unsigned int, ThreadData*) override; 370 | }; 371 | 372 | class B5G6R5Converter : public StandardConverter 373 | { 374 | public: 375 | explicit B5G6R5Converter(const Image& image) 376 | : StandardConverter(image) 377 | { 378 | } 379 | 380 | void process(unsigned int x, unsigned int, ThreadData*) override; 381 | }; 382 | 383 | class R5G5B5A1Converter : public StandardConverter 384 | { 385 | public: 386 | explicit R5G5B5A1Converter(const Image& image) 387 | : StandardConverter(image) 388 | { 389 | } 390 | 391 | void process(unsigned int x, unsigned int, ThreadData*) override; 392 | }; 393 | 394 | class B5G5R5A1Converter : public StandardConverter 395 | { 396 | public: 397 | explicit B5G5R5A1Converter(const Image& image) 398 | : StandardConverter(image) 399 | { 400 | } 401 | 402 | void process(unsigned int x, unsigned int, ThreadData*) override; 403 | }; 404 | 405 | class A1R5G5B5Converter : public StandardConverter 406 | { 407 | public: 408 | explicit A1R5G5B5Converter(const Image& image) 409 | : StandardConverter(image) 410 | { 411 | } 412 | 413 | void process(unsigned int x, unsigned int, ThreadData*) override; 414 | }; 415 | 416 | class B8G8R8Converter : public StandardConverter 417 | { 418 | public: 419 | explicit B8G8R8Converter(const Image& image) 420 | : StandardConverter(image) 421 | { 422 | } 423 | 424 | void process(unsigned int x, unsigned int, ThreadData*) override; 425 | }; 426 | 427 | class B8G8R8A8Converter : public StandardConverter 428 | { 429 | public: 430 | explicit B8G8R8A8Converter(const Image& image) 431 | : StandardConverter(image) 432 | { 433 | } 434 | 435 | void process(unsigned int x, unsigned int, ThreadData*) override; 436 | }; 437 | 438 | class A8B8G8R8Converter : public StandardConverter 439 | { 440 | public: 441 | explicit A8B8G8R8Converter(const Image& image) 442 | : StandardConverter(image) 443 | { 444 | } 445 | 446 | void process(unsigned int x, unsigned int, ThreadData*) override; 447 | }; 448 | 449 | class A2R10G10B10UNormConverter : public StandardConverter 450 | { 451 | public: 452 | explicit A2R10G10B10UNormConverter(const Image& image) 453 | : StandardConverter(image) 454 | { 455 | } 456 | 457 | void process(unsigned int x, unsigned int, ThreadData*) override; 458 | }; 459 | 460 | class A2R10G10B10UIntConverter : public StandardConverter 461 | { 462 | public: 463 | explicit A2R10G10B10UIntConverter(const Image& image) 464 | : StandardConverter(image) 465 | { 466 | } 467 | 468 | void process(unsigned int x, unsigned int, ThreadData*) override; 469 | }; 470 | 471 | class A2B10G10R10UNormConverter : public StandardConverter 472 | { 473 | public: 474 | explicit A2B10G10R10UNormConverter(const Image& image) 475 | : StandardConverter(image) 476 | { 477 | } 478 | 479 | void process(unsigned int x, unsigned int, ThreadData*) override; 480 | }; 481 | 482 | class A2B10G10R10UIntConverter : public StandardConverter 483 | { 484 | public: 485 | explicit A2B10G10R10UIntConverter(const Image& image) 486 | : StandardConverter(image) 487 | { 488 | } 489 | 490 | void process(unsigned int x, unsigned int, ThreadData*) override; 491 | }; 492 | 493 | class B10R11R11UFloatConverter : public StandardConverter 494 | { 495 | public: 496 | explicit B10R11R11UFloatConverter(const Image& image) 497 | : StandardConverter(image) 498 | { 499 | } 500 | 501 | void process(unsigned int x, unsigned int, ThreadData*) override; 502 | }; 503 | 504 | class E5B9G9R9UFloatConverter : public StandardConverter 505 | { 506 | public: 507 | explicit E5B9G9R9UFloatConverter(const Image& image) 508 | : StandardConverter(image) 509 | { 510 | } 511 | 512 | void process(unsigned int x, unsigned int, ThreadData*) override; 513 | }; 514 | 515 | } // namespace cuttlefish 516 | -------------------------------------------------------------------------------- /lib/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (NOT GTEST_FOUND OR NOT CUTTLEFISH_BUILD_TESTS) 2 | return() 3 | endif() 4 | 5 | find_package(Threads) 6 | 7 | file(GLOB_RECURSE sources *.cpp *.h) 8 | add_executable(cuttlefish_lib_test ${sources}) 9 | 10 | target_include_directories(cuttlefish_lib_test PRIVATE 11 | ${GTEST_INCLUDE_DIRS} 12 | ${CMAKE_CURRENT_SOURCE_DIR}/../src 13 | ${CMAKE_CURRENT_SOURCE_DIR}/../glm) 14 | target_link_libraries(cuttlefish_lib_test PRIVATE 15 | Cuttlefish::lib 16 | ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) 17 | 18 | cfs_set_folder(cuttlefish_lib_test) 19 | add_test(NAME CuttlefishLibTest COMMAND cuttlefish_lib_test) 20 | -------------------------------------------------------------------------------- /lib/test/HalfFloatTest.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Aaron Barany 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "HalfFloat.h" 18 | #include 19 | 20 | #if CUTTLEFISH_GCC 21 | #pragma GCC diagnostic push 22 | #pragma GCC diagnostic ignored "-Wconversion" 23 | #endif 24 | 25 | #include 26 | 27 | #if CUTTLEFISH_GCC 28 | #pragma GCC diagnostic pop 29 | #endif 30 | 31 | namespace cuttlefish 32 | { 33 | 34 | TEST(HalfFloatTest, PackHardwareHalfFloat) 35 | { 36 | if (!hasHardwareHalfFloat) 37 | return; 38 | 39 | float floatValues[4] = {1.2f, -3.4f, 5.6f, -7.8f}; 40 | std::uint16_t halfFloatValues[4]; 41 | *reinterpret_cast(halfFloatValues) = 42 | glm::packHalf4x16(*reinterpret_cast(floatValues)); 43 | 44 | std::uint16_t convertedValues[4] = {0, 0, 0, 0}; 45 | 46 | packHardwareHalfFloat1(convertedValues, floatValues); 47 | EXPECT_EQ(halfFloatValues[0], convertedValues[0]); 48 | EXPECT_EQ(0, convertedValues[1]); 49 | EXPECT_EQ(0, convertedValues[2]); 50 | EXPECT_EQ(0, convertedValues[3]); 51 | 52 | packHardwareHalfFloat2(convertedValues, floatValues); 53 | EXPECT_EQ(halfFloatValues[0], convertedValues[0]); 54 | EXPECT_EQ(halfFloatValues[1], convertedValues[1]); 55 | EXPECT_EQ(0, convertedValues[2]); 56 | EXPECT_EQ(0, convertedValues[3]); 57 | 58 | packHardwareHalfFloat3(convertedValues, floatValues); 59 | EXPECT_EQ(halfFloatValues[0], convertedValues[0]); 60 | EXPECT_EQ(halfFloatValues[1], convertedValues[1]); 61 | EXPECT_EQ(halfFloatValues[2], convertedValues[2]); 62 | EXPECT_EQ(0, convertedValues[3]); 63 | 64 | packHardwareHalfFloat4(convertedValues, floatValues); 65 | EXPECT_EQ(halfFloatValues[0], convertedValues[0]); 66 | EXPECT_EQ(halfFloatValues[1], convertedValues[1]); 67 | EXPECT_EQ(halfFloatValues[2], convertedValues[2]); 68 | EXPECT_EQ(halfFloatValues[3], convertedValues[3]); 69 | } 70 | 71 | } // cuttlefish 72 | -------------------------------------------------------------------------------- /tool/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file(GLOB_RECURSE sources *.cpp *.h) 2 | add_executable(cuttlefish ${sources}) 3 | 4 | target_link_libraries(cuttlefish PRIVATE Cuttlefish::lib) 5 | 6 | cfs_set_folder(cuttlefish) 7 | cfs_install_executable(cuttlefish tool) 8 | 9 | set(testPath ${CMAKE_CURRENT_SOURCE_DIR}/test) 10 | set(cuttlefishPath $) 11 | if (WIN32) 12 | set(commandPath ${testPath}/run-test.bat) 13 | set(nullFile NUL) 14 | else() 15 | set(commandPath ${testPath}/run-test.sh) 16 | set(nullFile /dev/null) 17 | endif() 18 | 19 | # NOTE: There will be some weird capiltalization in these tests. This is intentional to make sure 20 | # some of the options (ones not prefixed with - or --) are case-insensitive. 21 | add_test(NAME CuttlefishNoArgs 22 | WORKING_DIRECTORY ${testPath} 23 | COMMAND ${commandPath} ${cuttlefishPath} "" 1) 24 | add_test(NAME CuttlefishHelp 25 | WORKING_DIRECTORY ${testPath} 26 | COMMAND ${commandPath} ${cuttlefishPath} "-h" 1) 27 | add_test(NAME CuttlefishSaveDds 28 | WORKING_DIRECTORY ${testPath} 29 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 -o ${nullFile} --file-format dds" 0) 30 | add_test(NAME CuttlefishSaveKtx 31 | WORKING_DIRECTORY ${testPath} 32 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f r8g8b8a8 -o ${nullFile} --file-format ktx" 0) 33 | add_test(NAME CuttlefishSavePvr 34 | WORKING_DIRECTORY ${testPath} 35 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 -o ${nullFile} --file-format pvr" 0) 36 | add_test(NAME CuttlefishSaveUnknownFileType 37 | WORKING_DIRECTORY ${testPath} 38 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 -o ${nullFile} --file-format asdf" 1) 39 | add_test(NAME CuttlefishSaveMissingFileTypeParam 40 | WORKING_DIRECTORY ${testPath} 41 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 -o ${nullFile} --file-format" 1) 42 | add_test(NAME CuttlefishMissingOutput 43 | WORKING_DIRECTORY ${testPath} 44 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 --file-format pvr" 1) 45 | add_test(NAME CuttlefishMultipleOutput 46 | WORKING_DIRECTORY ${testPath} 47 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 -o ${nullFile} -o ${nullFile} --file-format pvr" 1) 48 | 49 | if (CUTTLEFISH_BUILD_S3TC) 50 | add_test(NAME CuttlefishSaveBC3 51 | WORKING_DIRECTORY ${testPath} 52 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f BC3 -o ${nullFile} --file-format PVR" 0) 53 | add_test(NAME CuttlefishSaveBC3sRGB 54 | WORKING_DIRECTORY ${testPath} 55 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f BC3 --srgb -o ${nullFile} --file-format pvr" 0) 56 | add_test(NAME CuttlefishSaveBC6H 57 | WORKING_DIRECTORY ${testPath} 58 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f BC6H -o ${nullFile} --file-format pvr" 0) 59 | add_test(NAME CuttlefishSaveBC6HUNorm 60 | WORKING_DIRECTORY ${testPath} 61 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f BC6H -t UNorm -o ${nullFile} --file-format pvr" 1) 62 | add_test(NAME CuttlefishSaveBC6HUFloat 63 | WORKING_DIRECTORY ${testPath} 64 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f BC6H -t UFloat -o ${nullFile} --file-format pvr" 0) 65 | add_test(NAME CuttlefishSaveBC6HFloat 66 | WORKING_DIRECTORY ${testPath} 67 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f BC6H -t Float -o ${nullFile} --file-format pvr" 0) 68 | endif() 69 | 70 | if (CUTTLEFISH_BUILD_ETC) 71 | add_test(NAME CuttlefishSaveETC2 72 | WORKING_DIRECTORY ${testPath} 73 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f etc2_R8G8B8A8 -o ${nullFile} --file-format pvr" 0) 74 | add_test(NAME CuttlefishSaveETC2sRGB 75 | WORKING_DIRECTORY ${testPath} 76 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f ETC2_r8g8b8a8 --srgb -o ${nullFile} --file-format pvr" 0) 77 | endif() 78 | 79 | if (CUTTLEFISH_BUILD_ASTC) 80 | add_test(NAME CuttlefishSaveASTC 81 | WORKING_DIRECTORY ${testPath} 82 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f ASTC_5x5 -o ${nullFile} --file-format pvr" 0) 83 | add_test(NAME CuttlefishSaveASTCHDR 84 | WORKING_DIRECTORY ${testPath} 85 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f ASTC_5x5 -t UFLOAT -o ${nullFile} --file-format pvr" 0) 86 | add_test(NAME CuttlefishSaveASTCsRGB 87 | WORKING_DIRECTORY ${testPath} 88 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f ASTC_5x5 --srgb -o ${nullFile} --file-format pvr" 0) 89 | add_test(NAME CuttlefishSaveASTCLowest 90 | WORKING_DIRECTORY ${testPath} 91 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f ASTC_5x5 -o ${nullFile} -Q lowest --file-format pvr" 0) 92 | add_test(NAME CuttlefishSaveASTCLow 93 | WORKING_DIRECTORY ${testPath} 94 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f ASTC_5x5 -o ${nullFile} -Q low --file-format pvr" 0) 95 | add_test(NAME CuttlefishSaveASTCHigh 96 | WORKING_DIRECTORY ${testPath} 97 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f ASTC_5x5 -o ${nullFile} -Q high --file-format pvr" 0) 98 | add_test(NAME CuttlefishSaveASTCHightest 99 | WORKING_DIRECTORY ${testPath} 100 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f ASTC_5x5 -o ${nullFile} -Q highest --file-format pvr" 0) 101 | endif() 102 | 103 | if (CUTTLEFISH_BUILD_PVRTC) 104 | if (PVRTEXLIB_FOUND) 105 | add_test(NAME CuttlefishSavePVRTC 106 | WORKING_DIRECTORY ${testPath} 107 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f PVRTC1_RGB_4BPP -o ${nullFile} --file-format pvr" 0) 108 | add_test(NAME CuttlefishSavePVRTCsRGB 109 | WORKING_DIRECTORY ${testPath} 110 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f PVRTC1_RGB_4BPP --srgb -o ${nullFile} --file-format pvr" 0) 111 | endif() 112 | endif() 113 | 114 | add_test(NAME CuttlefishSaveB10G11R11_UFloat 115 | WORKING_DIRECTORY ${testPath} 116 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f B10G11R11_UFloat -o ${nullFile} --file-format pvr" 0) 117 | add_test(NAME CuttlefishSaveE5B9G9R9_UFloat 118 | WORKING_DIRECTORY ${testPath} 119 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f E5B9G9R9_UFloat -o ${nullFile} --file-format pvr" 0) 120 | 121 | add_test(NAME CuttlefishSavesRGB 122 | WORKING_DIRECTORY ${testPath} 123 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 --srgb -o ${nullFile} --file-format pvr" 0) 124 | add_test(NAME CuttlefishSaveUNorm 125 | WORKING_DIRECTORY ${testPath} 126 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R16G16B16A16 -t unorm -o ${nullFile} --file-format pvr" 0) 127 | add_test(NAME CuttlefishSaveSNorm 128 | WORKING_DIRECTORY ${testPath} 129 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R16G16B16A16 -t snorm -o ${nullFile} --file-format pvr" 0) 130 | add_test(NAME CuttlefishSaveUInt 131 | WORKING_DIRECTORY ${testPath} 132 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R16G16B16A16 -t uint -o ${nullFile} --file-format pvr" 0) 133 | add_test(NAME CuttlefishSaveInt 134 | WORKING_DIRECTORY ${testPath} 135 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R16G16B16A16 -t int -o ${nullFile} --file-format pvr" 0) 136 | add_test(NAME CuttlefishSaveUFloat 137 | WORKING_DIRECTORY ${testPath} 138 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R16G16B16A16 -t ufloat -o ${nullFile} --file-format pvr" 1) 139 | add_test(NAME CuttlefishSaveFloat 140 | WORKING_DIRECTORY ${testPath} 141 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R16G16B16A16 -t float -o ${nullFile} --file-format pvr" 0) 142 | add_test(NAME CuttlefishSaveUnknownType 143 | WORKING_DIRECTORY ${testPath} 144 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R16G16B16A16 -t asdf -o ${nullFile} --file-format pvr" 1) 145 | 146 | add_test(NAME CuttlefishSaveUnknownFormat 147 | WORKING_DIRECTORY ${testPath} 148 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f asdf -o ${nullFile} --file-format pvr" 1) 149 | add_test(NAME CuttlefishSaveMissingFormat 150 | WORKING_DIRECTORY ${testPath} 151 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -o ${nullFile} --file-format pvr" 1) 152 | 153 | add_test(NAME CuttlefishSaveMissingInput 154 | WORKING_DIRECTORY ${testPath} 155 | COMMAND ${commandPath} ${cuttlefishPath} "-f R8G8B8A8 -o ${nullFile} --file-format pvr" 1) 156 | add_test(NAME CuttlefishSaveInputNotFound 157 | WORKING_DIRECTORY ${testPath} 158 | COMMAND ${commandPath} ${cuttlefishPath} "-i asdf.png -f R8G8B8A8 -o ${nullFile} --file-format pvr" 2) 159 | 160 | add_test(NAME CuttlefishSaveArray 161 | WORKING_DIRECTORY ${testPath} 162 | COMMAND ${commandPath} ${cuttlefishPath} "-a array0.png -a 1 array1.png --array array2.png -f R8G8B8A8 -o ${nullFile} --file-format pvr" 0) 163 | add_test(NAME CuttlefishSaveArrayDuplicateIndex 164 | WORKING_DIRECTORY ${testPath} 165 | COMMAND ${commandPath} ${cuttlefishPath} "-a array0.png -a 0 array1.png --array array2.png -f R8G8B8A8 -o ${nullFile} --file-format pvr" 1) 166 | add_test(NAME CuttlefishSaveArrayMissingIndex 167 | WORKING_DIRECTORY ${testPath} 168 | COMMAND ${commandPath} ${cuttlefishPath} "-a array0.png -a array1.png --array 3 array2.png -f R8G8B8A8 -o ${nullFile} --file-format pvr" 1) 169 | 170 | add_test(NAME CuttlefishSave3D 171 | WORKING_DIRECTORY ${testPath} 172 | COMMAND ${commandPath} ${cuttlefishPath} "-a array0.png -a 1 array1.png --array array2.png -d 3 -f R8G8B8A8 -o ${nullFile} --file-format pvr" 0) 173 | 174 | add_test(NAME CuttlefishCube 175 | WORKING_DIRECTORY ${testPath} 176 | COMMAND ${commandPath} ${cuttlefishPath} "-c +X posx.png -c +Y posy.png -c +Z posz.png -c -x negx.png -c -y negy.png --cube -z negz.png -f R8G8B8A8 -o ${nullFile} --file-format pvr" 0) 177 | add_test(NAME CuttlefishCubeMissingFace 178 | WORKING_DIRECTORY ${testPath} 179 | COMMAND ${commandPath} ${cuttlefishPath} "-c +y posy.png -c +z posz.png -c -x negx.png -c -y negy.png --cube -z negz.png -f R8G8B8A8 -o ${nullFile} --file-format pvr" 1) 180 | add_test(NAME CuttlefishCubeDuplicateFace 181 | WORKING_DIRECTORY ${testPath} 182 | COMMAND ${commandPath} ${cuttlefishPath} "-c +x posx.png -c +x posx.png -c +y posy.png -c +z posz.png -c -x negx.png -c -y negy.png --cube -z negz.png -f R8G8B8A8 -o ${nullFile} --file-format pvr" 1) 183 | 184 | add_test(NAME CuttlefishCubeArray 185 | WORKING_DIRECTORY ${testPath} 186 | COMMAND ${commandPath} ${cuttlefishPath} "-C 0 +x posx.png -C 0 +y posy.png -C 0 +z posz.png -C 0 -X negx.png -C 0 -Y negy.png --cube-array 0 -Z negz.png -C 1 +x posx.png -C 1 +y posy.png -C 1 +z posz.png -C 1 -x negx.png -C 1 -y negy.png --cube-array 1 -z negz.png -f R8G8B8A8 -o ${nullFile} --file-format pvr" 0) 187 | add_test(NAME CuttlefishCubeArrayMissingFace 188 | WORKING_DIRECTORY ${testPath} 189 | COMMAND ${commandPath} ${cuttlefishPath} "-C 0 +y posy.png -C 0 +z posz.png -C 0 -x negx.png -C 0 -y negy.png --cube-array 0 -z negz.png -C 1 +x posx.png -C 1 +y posy.png -C 1 +z posz.png -C 1 -x negx.png -C 1 -y negy.png --cube-array 1 -z negz.png -f R8G8B8A8 -o ${nullFile} --file-format pvr" 1) 190 | add_test(NAME CuttlefishCubeArrayDuplicateFace 191 | WORKING_DIRECTORY ${testPath} 192 | COMMAND ${commandPath} ${cuttlefishPath} "-C 0 +x posx.png -C 0 +x posx.png -C 0 +y posy.png -C 0 +z posz.png -C 0 -x negx.png -C 0 -y negy.png --cube-array 0 -z negz.png -C 1 +x posx.png -C 1 +y posy.png -C 1 +z posz.png -C 1 -x negx.png -C 1 -y negy.png --cube-array 1 -z negz.png -f R8G8B8A8 -o ${nullFile} --file-format pvr" 1) 193 | 194 | add_test(NAME CuttlefishImageList 195 | WORKING_DIRECTORY ${testPath} 196 | COMMAND ${commandPath} ${cuttlefishPath} "-I image image.txt -f R8G8B8A8 -o ${nullFile} --file-format pvr" 0) 197 | add_test(NAME CuttlefishImageListNotFound 198 | WORKING_DIRECTORY ${testPath} 199 | COMMAND ${commandPath} ${cuttlefishPath} "-I image asdf.txt -f R8G8B8A8 -o ${nullFile} --file-format pvr" 1) 200 | add_test(NAME CuttlefishImageListMultipleImages 201 | WORKING_DIRECTORY ${testPath} 202 | COMMAND ${commandPath} ${cuttlefishPath} "-I image array.txt -f R8G8B8A8 -o ${nullFile} --file-format pvr" 1) 203 | 204 | add_test(NAME CuttlefishArrayList 205 | WORKING_DIRECTORY ${testPath} 206 | COMMAND ${commandPath} ${cuttlefishPath} "--input-list array array.txt -f R8G8B8A8 -o ${nullFile} --file-format pvr" 0) 207 | 208 | add_test(NAME CuttlefishCubeList 209 | WORKING_DIRECTORY ${testPath} 210 | COMMAND ${commandPath} ${cuttlefishPath} "-I cube cube.txt -f R8G8B8A8 -o ${nullFile} --file-format pvr" 0) 211 | add_test(NAME CuttlefishInvalidCubeList 212 | WORKING_DIRECTORY ${testPath} 213 | COMMAND ${commandPath} ${cuttlefishPath} "-I cube array.txt -f R8G8B8A8 -o ${nullFile} --file-format pvr" 1) 214 | 215 | add_test(NAME CuttlefishCubeArrayList 216 | WORKING_DIRECTORY ${testPath} 217 | COMMAND ${commandPath} ${cuttlefishPath} "-I cube-array cube-array.txt -f R8G8B8A8 -o ${nullFile} --file-format pvr" 0) 218 | add_test(NAME CuttlefishInvalidCubeArrayList 219 | WORKING_DIRECTORY ${testPath} 220 | COMMAND ${commandPath} ${cuttlefishPath} "-I cube-array array.txt -f R8G8B8A8 -o ${nullFile} --file-format pvr" 1) 221 | 222 | add_test(NAME CuttlefishUnknownList 223 | WORKING_DIRECTORY ${testPath} 224 | COMMAND ${commandPath} ${cuttlefishPath} "-I asdf array.txt -f R8G8B8A8 -o ${nullFile} --file-format pvr" 1) 225 | 226 | add_test(NAME CuttlefishResize 227 | WORKING_DIRECTORY ${testPath} 228 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 --resize 31 42 -o ${nullFile} --file-format pvr" 0) 229 | add_test(NAME CuttlefishResizeBox 230 | WORKING_DIRECTORY ${testPath} 231 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 -r 31 42 Box -o ${nullFile} --file-format pvr" 0) 232 | add_test(NAME CuttlefishResizeLinear 233 | WORKING_DIRECTORY ${testPath} 234 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 -r 31 42 lInear -o ${nullFile} --file-format pvr" 0) 235 | add_test(NAME CuttlefishResizeCubic 236 | WORKING_DIRECTORY ${testPath} 237 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 -r 31 42 cuBic -o ${nullFile} --file-format pvr" 0) 238 | add_test(NAME CuttlefishResizeCatmullRom 239 | WORKING_DIRECTORY ${testPath} 240 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 -r 31 42 catmull-Rom -o ${nullFile} --file-format pvr" 0) 241 | add_test(NAME CuttlefishResizePO2 242 | WORKING_DIRECTORY ${testPath} 243 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 -r nextPO2 Nearestpo2 -o ${nullFile} --file-format pvr" 0) 244 | add_test(NAME CuttlefishResizeInvalidX 245 | WORKING_DIRECTORY ${testPath} 246 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 -r asdf nearestPO2 -o ${nullFile} --file-format pvr" 1) 247 | add_test(NAME CuttlefishResizeInvalidY 248 | WORKING_DIRECTORY ${testPath} 249 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 -r nextpo2 asdf -o ${nullFile} --file-format pvr" 1) 250 | 251 | add_test(NAME CuttlefishMipmap 252 | WORKING_DIRECTORY ${testPath} 253 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 -m -o ${nullFile} --file-format pvr" 0) 254 | add_test(NAME CuttlefishMipmapLevels 255 | WORKING_DIRECTORY ${testPath} 256 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 --mipmap 1 -o ${nullFile} --file-format pvr" 0) 257 | add_test(NAME CuttlefishMipmapFilter 258 | WORKING_DIRECTORY ${testPath} 259 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 -m Box -o ${nullFile} --file-format pvr" 0) 260 | add_test(NAME CuttlefishMipmapLevelsFilter 261 | WORKING_DIRECTORY ${testPath} 262 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 -m 1 box -o ${nullFile} --file-format pvr" 0) 263 | 264 | add_test(NAME CuttlefishNormalimap 265 | WORKING_DIRECTORY ${testPath} 266 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 -n -o ${nullFile} --file-format pvr" 0) 267 | add_test(NAME CuttlefishNormalimapHeight 268 | WORKING_DIRECTORY ${testPath} 269 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 --normalmap 2.3 -o ${nullFile} --file-format pvr" 0) 270 | add_test(NAME CuttlefishNormalimapWrap 271 | WORKING_DIRECTORY ${testPath} 272 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 --normalmap wrap -o ${nullFile} --file-format pvr" 0) 273 | add_test(NAME CuttlefishNormalimapWrapX 274 | WORKING_DIRECTORY ${testPath} 275 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 --normalmap wrapx -o ${nullFile} --file-format pvr" 0) 276 | add_test(NAME CuttlefishNormalimapWrapY 277 | WORKING_DIRECTORY ${testPath} 278 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 --normalmap wrapy -o ${nullFile} --file-format pvr" 0) 279 | add_test(NAME CuttlefishNormalimapWrapWithHeight 280 | WORKING_DIRECTORY ${testPath} 281 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 --normalmap wrap 2.3 -o ${nullFile} --file-format pvr" 0) 282 | 283 | add_test(NAME CuttlefishAlphaNone 284 | WORKING_DIRECTORY ${testPath} 285 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 --alpha nonE -o ${nullFile} --file-format pvr" 0) 286 | add_test(NAME CuttlefishAlphaStandard 287 | WORKING_DIRECTORY ${testPath} 288 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 --alpha Standard -o ${nullFile} --file-format pvr" 0) 289 | add_test(NAME CuttlefishAlphaPreMultiplied 290 | WORKING_DIRECTORY ${testPath} 291 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 --alpha pre-muLtiplied -o ${nullFile} --file-format pvr" 0) 292 | add_test(NAME CuttlefishEncoded 293 | WORKING_DIRECTORY ${testPath} 294 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 --alpha encodeD -o ${nullFile} --file-format pvr" 0) 295 | 296 | add_test(NAME CuttlefishSwizzle 297 | WORKING_DIRECTORY ${testPath} 298 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 -s abgr -o ${nullFile} --file-format pvr" 0) 299 | add_test(NAME CuttlefishSwizzleAlternate 300 | WORKING_DIRECTORY ${testPath} 301 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 -s RgXb -o ${nullFile} --file-format pvr" 0) 302 | add_test(NAME CuttlefishSwizzleInvalidNumber 303 | WORKING_DIRECTORY ${testPath} 304 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 -s rgb -o ${nullFile} --file-format pvr" 1) 305 | add_test(NAME CuttlefishSwizzleInvalidChannel 306 | WORKING_DIRECTORY ${testPath} 307 | COMMAND ${commandPath} ${cuttlefishPath} "-i texture.png -f R8G8B8A8 -s abcd -o ${nullFile} --file-format pvr" 1) 308 | -------------------------------------------------------------------------------- /tool/CommandLine.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Aaron Barany 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | struct CommandLine 25 | { 26 | enum class ImageType 27 | { 28 | Image, 29 | Array, 30 | Cube, 31 | CubeArray 32 | }; 33 | 34 | enum class Log 35 | { 36 | Normal, 37 | Quiet, 38 | Verbose 39 | }; 40 | 41 | enum Size 42 | { 43 | OriginalSize = -1, 44 | NextPO2 = -2, 45 | NearestPO2 = -3, 46 | Width = -4, 47 | WidthNextPO2 = -5, 48 | WidthNearestPO2 = -6, 49 | Height = -7, 50 | HeightNextPO2 = -8, 51 | HeightNearestPO2 = -9, 52 | Min = -10, 53 | MinNextPO2 = -11, 54 | MinNearestPO2 = -12, 55 | Max = -13, 56 | MaxNextPO2 = -14, 57 | MaxNearestPO2 = -15 58 | }; 59 | 60 | bool parse(int argc, const char** argv); 61 | 62 | unsigned int jobs = 1; 63 | Log log = Log::Normal; 64 | ImageType imageType = ImageType::Image; 65 | std::vector images; 66 | int width = OriginalSize; 67 | int height = OriginalSize; 68 | cuttlefish::Image::ResizeFilter resizeFilter = cuttlefish::Image::ResizeFilter::CatmullRom; 69 | unsigned int mipLevels = 1; 70 | cuttlefish::Image::ResizeFilter mipFilter = cuttlefish::Image::ResizeFilter::CatmullRom; 71 | bool flipX = false; 72 | bool flipY = false; 73 | bool rotate = false; 74 | cuttlefish::Image::RotateAngle rotateAngle = cuttlefish::Image::RotateAngle::CW90; 75 | bool normalMap = false; 76 | cuttlefish::Image::NormalOptions normalOptions = cuttlefish::Image::NormalOptions::Default; 77 | double normalHeight = 1.0; 78 | bool grayscale = false; 79 | bool swizzle = false; 80 | cuttlefish::Image::Channel redSwzl = cuttlefish::Image::Channel::Red; 81 | cuttlefish::Image::Channel greenSwzl = cuttlefish::Image::Channel::Green; 82 | cuttlefish::Image::Channel blueSwzl = cuttlefish::Image::Channel::Blue; 83 | cuttlefish::Image::Channel alphaSwzl = cuttlefish::Image::Channel::Alpha; 84 | cuttlefish::Texture::ColorMask colorMask; 85 | cuttlefish::ColorSpace imageColorSpace = cuttlefish::ColorSpace::Linear; 86 | cuttlefish::ColorSpace textureColorSpace = cuttlefish::ColorSpace::Linear; 87 | bool preMultiply = false; 88 | cuttlefish::Texture::Dimension dimension = cuttlefish::Texture::Dimension::Dim2D; 89 | cuttlefish::Texture::Format format = cuttlefish::Texture::Format::Unknown; 90 | cuttlefish::Texture::Type type = cuttlefish::Texture::Type::UNorm; 91 | cuttlefish::Texture::Alpha alpha = cuttlefish::Texture::Alpha::Standard; 92 | cuttlefish::Texture::Quality quality = cuttlefish::Texture::Quality::Normal; 93 | const char* output = nullptr; 94 | cuttlefish::Texture::FileType fileType = cuttlefish::Texture::FileType::Auto; 95 | bool createOutputDir = false; 96 | }; 97 | -------------------------------------------------------------------------------- /tool/README.md: -------------------------------------------------------------------------------- 1 | # Tool 2 | 3 | The `cuttlefish` tool can be used to convert textures on the command line. This provides the functionality required to create most textures. 4 | 5 | When running the `cuttlefish` tool, one or more input images are provided on the command line. Different arguments are provided for single images, multiple images for an array or 3D texture, or a cube map. The images may optionally be provided from a text file, with one image path per line. 6 | 7 | Options are available for common operations on the images. The following operations are provided, and are executed in this order: 8 | 9 | * Convert from sRGB to linear. This is performed for formats that cannot natively be sampled as sRGB from the GPU. (note that this may cause a noticeable loss of quality for lower precision formats) 10 | * Resize. Resizing may be an explicit size, next power of 2, or nearest power of 2. 11 | * Rotate. 12 | * Convert to grayscale. 13 | * Convert from a bump map to a normal map. 14 | * Flip along the X axis. 15 | * Flip along the Y axis. 16 | * Swizzle. 17 | * Pre-multiply alpha with color channels. 18 | 19 | Even when sRGB sampling is supported by the texture format, resizing, conversion to grayscale, and pre-multiplying alpha is always performed in linear space. 20 | 21 | When converting a texture, the format is provided, which is either the color bits for each channel (e.g. R8G8B8A8, R5G6B5, B10G11R11\_UFloat) or the name of a compressed format. (e.g. BC3, ETC2\_R8G8B8) Some formats allow the type used for the channel to be provided. For example, R16G16B16A16 may be unorm, snorm, uint, int, or float. The final image may be saved as a DDS, KTX, or PVR file. 22 | 23 | When running the tool, you may provide the `-j` parameter to use multiple threaded jobs. The number of jobs may be provided, otherwise it will use all available cores. This is recommended when a single instance of `cuttlefish` is run, but shouldn't be used if integrated into a build system that will run multiple instances in parallel. (e.g. `make` with `-j` provided) 24 | 25 | For more detailed information about the command line arguments, run `cuttlefish -h`. 26 | -------------------------------------------------------------------------------- /tool/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2025 Aaron Barany 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "CommandLine.h" 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #if CUTTLEFISH_WINDOWS 28 | #include 29 | #define mkdir(path, mode) _mkdir(path) 30 | #else 31 | #include 32 | #endif 33 | 34 | using namespace cuttlefish; 35 | 36 | static unsigned int nextPO2(unsigned int size) 37 | { 38 | // https://stackoverflow.com/questions/466204/rounding-up-to-next-power-of-2 39 | --size; 40 | size |= size >> 1; 41 | size |= size >> 2; 42 | size |= size >> 4; 43 | size |= size >> 8; 44 | size |= size >> 16; 45 | return ++size; 46 | } 47 | 48 | static unsigned int nearestPO2(unsigned int size) 49 | { 50 | unsigned int next = nextPO2(size); 51 | unsigned int prev = next >> 1; 52 | if (prev == 0) 53 | return next; 54 | 55 | return next - size < size - prev ? next : prev; 56 | } 57 | 58 | static bool isSigned(Texture::Type type) 59 | { 60 | switch (type) 61 | { 62 | case Texture::Type::UNorm: 63 | case Texture::Type::UInt: 64 | case Texture::Type::UFloat: 65 | return false; 66 | case Texture::Type::SNorm: 67 | case Texture::Type::Int: 68 | case Texture::Type::Float: 69 | return true; 70 | } 71 | 72 | assert(false); 73 | return false; 74 | } 75 | 76 | // Intentionally pass by value since the path will be modified. 77 | static bool createParentDir(std::string path) 78 | { 79 | #if CUTTLEFISH_WINDOWS 80 | const char* pathSep = "\\/"; 81 | #else 82 | const char* pathSep = "/"; 83 | #endif 84 | 85 | std::size_t prevPos = 0; 86 | do 87 | { 88 | std::size_t nextPos = path.find_first_of(pathSep, prevPos + 1); 89 | if (nextPos == std::string::npos) 90 | return true; 91 | 92 | char prevChar = path[nextPos]; 93 | path[nextPos] = 0; 94 | int result = mkdir(path.c_str(), 0755); 95 | path[nextPos] = prevChar; 96 | if (result < 0 && errno != EEXIST) 97 | return false; 98 | 99 | prevPos = nextPos; 100 | } while (true); 101 | } 102 | 103 | static unsigned int getDimension(unsigned int curDim, unsigned int width, unsigned int height, 104 | int size) 105 | { 106 | switch (size) 107 | { 108 | case CommandLine::OriginalSize: 109 | return curDim; 110 | case CommandLine::NextPO2: 111 | return nextPO2(curDim); 112 | case CommandLine::NearestPO2: 113 | return nearestPO2(curDim); 114 | case CommandLine::Width: 115 | return width; 116 | case CommandLine::WidthNextPO2: 117 | return nextPO2(width); 118 | case CommandLine::WidthNearestPO2: 119 | return nearestPO2(width); 120 | case CommandLine::Height: 121 | return height; 122 | case CommandLine::HeightNextPO2: 123 | return nextPO2(height); 124 | case CommandLine::HeightNearestPO2: 125 | return nearestPO2(height); 126 | case CommandLine::Min: 127 | return std::min(width, height); 128 | case CommandLine::MinNextPO2: 129 | return nextPO2(std::min(width, height)); 130 | case CommandLine::MinNearestPO2: 131 | return nearestPO2(std::min(width, height)); 132 | case CommandLine::Max: 133 | return std::max(width, height); 134 | case CommandLine::MaxNextPO2: 135 | return nextPO2(std::max(width, height)); 136 | case CommandLine::MaxNearestPO2: 137 | return nearestPO2(std::max(width, height)); 138 | default: 139 | return size; 140 | } 141 | } 142 | 143 | static bool loadImages(std::vector& images, CommandLine& args) 144 | { 145 | images.resize(args.images.size()); 146 | for (std::size_t i = 0; i < args.images.size(); ++i) 147 | { 148 | if (args.log == CommandLine::Log::Verbose) 149 | std::cout << "loading image '" << args.images[i] << "'" << std::endl; 150 | if (!images[i].load(args.images[i].c_str(), args.imageColorSpace)) 151 | { 152 | std::cerr << "error: couldn't load image '" << args.images[i] << "'" << std::endl; 153 | return false; 154 | } 155 | 156 | Image::Format origImageFormat = images[i].format(); 157 | if (origImageFormat != Image::Format::RGBAF) 158 | { 159 | if (args.log == CommandLine::Log::Verbose) 160 | std::cout << "converting image '" << args.images[i] << "' to RGBAF" << std::endl; 161 | images[i] = images[i].convert(Image::Format::RGBAF); 162 | } 163 | 164 | if (args.textureColorSpace != args.imageColorSpace) 165 | { 166 | if (args.log == CommandLine::Log::Verbose) 167 | { 168 | std::cout << "converting image '" << args.images[i] << "' from sRGB to linear" << 169 | std::endl; 170 | } 171 | images[i].changeColorSpace(args.textureColorSpace); 172 | } 173 | 174 | unsigned int width = 175 | getDimension(images[0].width(), images[0].width(), images[0].height(), args.width); 176 | unsigned int height = 177 | getDimension(images[0].height(), images[0].width(), images[0].height(), args.height); 178 | 179 | if (width != images[i].width() || height != images[i].height()) 180 | { 181 | if (args.log == CommandLine::Log::Verbose) 182 | { 183 | std::cout << "resizing image '" << args.images[i] << "' to " << width << " x " << 184 | height << std::endl; 185 | } 186 | images[i] = images[i].resize(width, height, args.resizeFilter); 187 | } 188 | 189 | if (args.rotate) 190 | { 191 | if (args.log == CommandLine::Log::Verbose) 192 | std::cout << "rotating image '" << args.images[i] << "'" << std::endl; 193 | images[i] = images[i].rotate(args.rotateAngle); 194 | } 195 | 196 | if (args.grayscale) 197 | { 198 | if (args.log == CommandLine::Log::Verbose) 199 | { 200 | std::cout << "converting image '" << args.images[i] << "' to grayscale" << 201 | std::endl; 202 | } 203 | images[i].grayscale(); 204 | } 205 | 206 | if (args.normalMap) 207 | { 208 | if (args.log == CommandLine::Log::Verbose) 209 | { 210 | std::cout << "generating normalmap for image '" << args.images[i] << "'" << 211 | std::endl; 212 | } 213 | Image::NormalOptions options = args.normalOptions; 214 | if (isSigned(args.type)) 215 | options |= Image::NormalOptions::KeepSign; 216 | images[i] = images[i].createNormalMap(options, args.normalHeight); 217 | 218 | // Image no longer matches the original input. 219 | origImageFormat = images[i].format(); 220 | } 221 | 222 | if (args.flipX) 223 | { 224 | if (args.log == CommandLine::Log::Verbose) 225 | { 226 | std::cout << "flipping image '" << args.images[i] << "' along the X axis" << 227 | std::endl; 228 | } 229 | images[i].flipHorizontal(); 230 | } 231 | 232 | if (args.flipY) 233 | { 234 | if (args.log == CommandLine::Log::Verbose) 235 | { 236 | std::cout << "flipping image '" << args.images[i] << "' along the Y axis" << 237 | std::endl; 238 | } 239 | images[i].flipVertical(); 240 | } 241 | 242 | if (args.swizzle) 243 | { 244 | if (args.log == CommandLine::Log::Verbose) 245 | std::cout << "swizzling image '" << args.images[i] << "'" << std::endl; 246 | images[i].swizzle(args.redSwzl, args.greenSwzl, args.blueSwzl, args.alphaSwzl); 247 | } 248 | 249 | if (args.preMultiply) 250 | { 251 | if (args.log == CommandLine::Log::Verbose) 252 | { 253 | std::cout << "pre-multiplying alpha for image '" << args.images[i] << "'" << 254 | std::endl; 255 | } 256 | images[i].preMultiplyAlpha(); 257 | } 258 | 259 | Texture::adjustImageValueRange(images[i], args.type, origImageFormat); 260 | } 261 | 262 | return true; 263 | } 264 | 265 | static bool saveTexture(std::vector& images, const CommandLine& args) 266 | { 267 | unsigned int depth = 0; 268 | switch (args.imageType) 269 | { 270 | case CommandLine::ImageType::Array: 271 | depth = static_cast(images.size()); 272 | break; 273 | case CommandLine::ImageType::CubeArray: 274 | depth = static_cast(images.size()/6); 275 | break; 276 | default: 277 | break; 278 | } 279 | 280 | Texture texture(args.dimension, images[0].width(), images[0].height(), depth, 1, 281 | args.textureColorSpace); 282 | switch (args.imageType) 283 | { 284 | case CommandLine::ImageType::Image: 285 | assert(images.size() == 1); 286 | texture.setImage(std::move(images[0])); 287 | break; 288 | case CommandLine::ImageType::Array: 289 | for (unsigned int i = 0; i < images.size(); ++i) 290 | texture.setImage(std::move(images[i]), 0, i); 291 | break; 292 | case CommandLine::ImageType::Cube: 293 | assert(images.size() == 6); 294 | for (unsigned int i = 0; i < images.size(); ++i) 295 | texture.setImage(std::move(images[i]), static_cast(i)); 296 | break; 297 | case CommandLine::ImageType::CubeArray: 298 | assert(images.size() % 6 == 0); 299 | for (unsigned int i = 0; i < images.size(); ++i) 300 | { 301 | texture.setImage(std::move(images[i]), static_cast(i % 6), 0, 302 | i/6); 303 | } 304 | break; 305 | default: 306 | break; 307 | } 308 | 309 | if (args.mipLevels > 1) 310 | { 311 | if (args.log == CommandLine::Log::Verbose) 312 | std::cout << "generating mipmaps" << std::endl; 313 | texture.generateMipmaps(args.mipFilter, args.mipLevels); 314 | } 315 | 316 | if (args.log == CommandLine::Log::Verbose) 317 | std::cout << "converting texture" << std::endl; 318 | if (!texture.convert(args.format, args.type, args.quality, args.alpha, args.colorMask, 319 | args.jobs)) 320 | { 321 | std::cerr << "error: failed to convert texture" << std::endl; 322 | return false; 323 | } 324 | 325 | if (args.log != CommandLine::Log::Quiet) 326 | std::cout << "saving texture '" << args.output << "'" << std::endl; 327 | switch (texture.save(args.output, args.fileType)) 328 | { 329 | case Texture::SaveResult::Success: 330 | return true; 331 | case Texture::SaveResult::Invalid: 332 | std::cerr << "error: texture parameters were invalid" << std::endl; 333 | return false; 334 | case Texture::SaveResult::UnknownFormat: 335 | std::cerr << "error: unknown texture file format" << std::endl; 336 | return false; 337 | case Texture::SaveResult::Unsupported: 338 | std::cerr << "error: texture format unsupported by target file format" << std::endl; 339 | return false; 340 | case Texture::SaveResult::WriteError: 341 | { 342 | if (args.createOutputDir) 343 | { 344 | // Try to create the directory and save again. We do this off of the initial save 345 | // so another error like an invalid format won't leave directories behind. 346 | if (!createParentDir(args.output)) 347 | { 348 | std::cerr << "error: couldn't create parent directory for '" << 349 | args.output << "'" << std::endl; 350 | return false; 351 | } 352 | 353 | if (texture.save(args.output, args.fileType) == Texture::SaveResult::Success) 354 | return true; 355 | } 356 | std::cerr << "error: couldn't write file '" << args.output << "'" << std::endl; 357 | return false; 358 | } 359 | } 360 | assert(false); 361 | return false; 362 | } 363 | 364 | int main(int argc, const char** argv) 365 | { 366 | CommandLine commandLine; 367 | if (!commandLine.parse(argc, argv)) 368 | return 1; 369 | 370 | std::vector images; 371 | if (!loadImages(images, commandLine)) 372 | return 2; 373 | 374 | if (!saveTexture(images, commandLine)) 375 | return 3; 376 | 377 | return 0; 378 | } 379 | -------------------------------------------------------------------------------- /tool/test/array.txt: -------------------------------------------------------------------------------- 1 | array0.png 2 | array1.png 3 | array2.png 4 | -------------------------------------------------------------------------------- /tool/test/array0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akb825/Cuttlefish/0ca4274583bfd5ebad84a278955d02bdc37b2493/tool/test/array0.png -------------------------------------------------------------------------------- /tool/test/array1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akb825/Cuttlefish/0ca4274583bfd5ebad84a278955d02bdc37b2493/tool/test/array1.png -------------------------------------------------------------------------------- /tool/test/array2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akb825/Cuttlefish/0ca4274583bfd5ebad84a278955d02bdc37b2493/tool/test/array2.png -------------------------------------------------------------------------------- /tool/test/cube-array.txt: -------------------------------------------------------------------------------- 1 | negx.png 2 | posx.png 3 | negy.png 4 | posy.png 5 | negz.png 6 | posz.png 7 | negx.png 8 | posx.png 9 | negy.png 10 | posy.png 11 | negz.png 12 | posz.png 13 | -------------------------------------------------------------------------------- /tool/test/cube.txt: -------------------------------------------------------------------------------- 1 | negx.png 2 | posx.png 3 | negy.png 4 | posy.png 5 | negz.png 6 | posz.png 7 | -------------------------------------------------------------------------------- /tool/test/image.txt: -------------------------------------------------------------------------------- 1 | texture.png 2 | -------------------------------------------------------------------------------- /tool/test/negx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akb825/Cuttlefish/0ca4274583bfd5ebad84a278955d02bdc37b2493/tool/test/negx.png -------------------------------------------------------------------------------- /tool/test/negy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akb825/Cuttlefish/0ca4274583bfd5ebad84a278955d02bdc37b2493/tool/test/negy.png -------------------------------------------------------------------------------- /tool/test/negz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akb825/Cuttlefish/0ca4274583bfd5ebad84a278955d02bdc37b2493/tool/test/negz.png -------------------------------------------------------------------------------- /tool/test/posx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akb825/Cuttlefish/0ca4274583bfd5ebad84a278955d02bdc37b2493/tool/test/posx.png -------------------------------------------------------------------------------- /tool/test/posy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akb825/Cuttlefish/0ca4274583bfd5ebad84a278955d02bdc37b2493/tool/test/posy.png -------------------------------------------------------------------------------- /tool/test/posz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akb825/Cuttlefish/0ca4274583bfd5ebad84a278955d02bdc37b2493/tool/test/posz.png -------------------------------------------------------------------------------- /tool/test/run-test.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | set command=%1 3 | set command=%command:/=\% 4 | set args=%~2 5 | set exitCode=%3 6 | 7 | "%command%" %args% 8 | set realExitCode=%ERRORLEVEL% 9 | if %exitCode% NEQ %realExitCode% ( 10 | echo got exit code %realExitCode%; expected %exitCode% 11 | exit /b -1 12 | ) 13 | -------------------------------------------------------------------------------- /tool/test/run-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | command=$1 4 | args=$2 5 | exitCode=$3 6 | 7 | "$command" $args 8 | realExitCode=$? 9 | if [ $exitCode != $realExitCode ]; then 10 | echo "got exit code $realExitCode; expected $exitCode" 11 | exit 1 12 | fi 13 | -------------------------------------------------------------------------------- /tool/test/texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akb825/Cuttlefish/0ca4274583bfd5ebad84a278955d02bdc37b2493/tool/test/texture.png --------------------------------------------------------------------------------