From 9fcfd7c298696cd139cf4a4c897f694a5916da9e Mon Sep 17 00:00:00 2001 From: Navin Patel Date: Mon, 23 Nov 2020 15:45:28 -0500 Subject: [PATCH] v4.1 updates --- applications/_libs/cmakelists.txt | 2 + applications/_libs/cmp_fileio/cmakelists.txt | 4 +- applications/_libs/cmp_math/cmakelists.txt | 19 +- .../_libs/cmp_math/cmp_math_common.cpp | 48 +- applications/_libs/cmp_math/cmp_math_common.h | 6 +- .../_libs/cmp_math/cmp_math_cpuid.cpp | 8 +- applications/_libs/cmp_math/jml.h | 2 +- applications/_libs/cmp_math/jmlfuncs.h | 17 +- applications/_libs/cmp_math/jmlscalar.h | 30 +- applications/_libs/cmp_math/jmlssevec.h | 257 +- applications/_libs/cmp_math/tootlepch.h | 8 +- applications/_libs/cmp_mesh/cmakelists.txt | 22 +- applications/_libs/cmp_mesh/error.h | 4 +- applications/_libs/cmp_mesh/jrt/jrtcommon.h | 2 +- applications/_libs/cmp_mesh/jrt/jrtkdtree.h | 2 +- .../cmp_mesh/jrt/jrttriangleintersection.cpp | 4 +- applications/_libs/cmp_mesh/tootlelib.h | 2 +- applications/_libs/cmp_mesh/tootlepch.h | 4 +- .../_libs/cmp_meshcompressor/cmakelists.txt | 41 +- .../_libs/cmp_meshoptimizer/cmakelists.txt | 35 +- applications/_libs/gpu_decode/cmakelists.txt | 33 +- .../_libs/gpu_decode/gpu_decodebase.cpp | 4 +- .../_libs/gpu_decode/gpu_decodebase.h | 3 +- .../c3dmodel_loaders/drc/cmakelists.txt | 29 +- .../c3dmodel_loaders/drc/modelloader_drc.cpp | 1 - .../c3dmodel_loaders/gltf_20/cmakelists.txt | 36 +- .../c3dmodel_loaders/obj/cmakelists.txt | 32 +- .../gltf_dx12_ex/cmakelists.txt | 75 +- .../gltf_dx12_ex/gltf_dx12deviceex.cpp | 1 + .../gltf_dx12_ex/gltf_dx12rendererex.cpp | 208 +- .../gltf_opengl/cmakelists.txt | 46 +- .../c3dmodel_viewers/vulkan/cmakelists.txt | 69 +- .../c3dmodel_viewers/vulkan/vulkan_device.cpp | 6 +- .../_plugins/canalysis/analysis/canalysis.cpp | 134 +- .../_plugins/canalysis/analysis/canalysis.h | 3 +- .../_plugins/canalysis/cmakelists.txt | 129 +- applications/_plugins/ccmp_sdk/bc4/bc4.cpp | 8 +- applications/_plugins/ccmp_sdk/bc5/bc5.cpp | 2 + applications/_plugins/cfilter/boxfilter.cpp | 671 +++- applications/_plugins/cfilter/boxfilter.h | 36 +- applications/_plugins/cfilter/cmakelists.txt | 27 +- .../_plugins/cfilter_fx/CMakeLists.txt | 24 +- .../cgpudecode/directx/cmakelists.txt | 34 +- .../cgpudecode/directx/gpu_directx.cpp | 13 + .../_plugins/cgpudecode/opengl/cmakelists.txt | 29 +- .../_plugins/cgpudecode/opengl/gpu_opengl.cpp | 13 +- .../_plugins/cgpudecode/vulkan/cmakelists.txt | 60 +- .../_plugins/cgpudecode/vulkan/gpu_vulkan.cpp | 1535 ++++----- .../_plugins/cgpudecode/vulkan/gpu_vulkan.h | 3 +- .../cgpudecode/vulkan/vulkanswapchain.hpp | 3 +- .../cgpudecode/vulkan/vulkantools.cpp | 6 +- .../_plugins/cimage/astc/cmakelists.txt | 35 +- .../_plugins/cimage/dds/cmakelists.txt | 36 +- applications/_plugins/cimage/dds/dds.cpp | 219 +- applications/_plugins/cimage/dds/dds_dx10.cpp | 4 +- applications/_plugins/cimage/dds/dds_file.cpp | 570 ++-- applications/_plugins/cimage/dds/dds_file.h | 894 +++--- .../_plugins/cimage/dds/dds_helpers.cpp | 109 +- .../_plugins/cimage/dds/dds_helpers.h | 12 +- .../_plugins/cimage/exr/cmakelists.txt | 63 +- applications/_plugins/cimage/exr/exr.cpp | 2 +- .../_plugins/cimage/ktx/cmakelists.txt | 87 +- applications/_plugins/cimage/ktx/ktx.cpp | 961 ------ applications/_plugins/cimage/ktx/ktx1.cpp | 1346 ++++++++ .../_plugins/cimage/ktx/{ktx.def => ktx1.def} | 0 .../_plugins/cimage/ktx/{cktx.h => ktx1.h} | 151 +- .../_plugins/cimage/ktx/ktxcommon.cpp | 26 + applications/_plugins/cimage/ktx/ktxcommon.h | 30 + .../_plugins/cimage/ktx/lib/checkheader.c | 169 + applications/_plugins/cimage/ktx/lib/errstr.c | 79 + .../_plugins/cimage/ktx/lib/etcdec.cxx | 1845 +++++++++++ .../_plugins/cimage/ktx/lib/etcunpack.cxx | 296 ++ .../_plugins/cimage/ktx/lib/gl_funcptrs.h | 82 + .../_plugins/cimage/ktx/lib/gles1_funcptrs.h | 81 + .../_plugins/cimage/ktx/lib/gles2_funcptrs.h | 80 + .../_plugins/cimage/ktx/lib/gles3_funcptrs.h | 85 + .../_plugins/cimage/ktx/lib/hashtable.c | 333 ++ applications/_plugins/cimage/ktx/lib/ktx.h | 512 +++ .../_plugins/cimage/ktx/lib/ktxfilestream.c | 154 + .../_plugins/cimage/ktx/lib/ktxfilestream.h | 49 + applications/_plugins/cimage/ktx/lib/ktxint.h | 336 ++ .../_plugins/cimage/ktx/lib/ktxmemstream.c | 225 ++ .../_plugins/cimage/ktx/lib/ktxmemstream.h | 62 + .../_plugins/cimage/ktx/lib/ktxstream.h | 75 + applications/_plugins/cimage/ktx/lib/loader.c | 970 ++++++ applications/_plugins/cimage/ktx/lib/swap.c | 65 + applications/_plugins/cimage/ktx/lib/uthash.h | 960 ++++++ applications/_plugins/cimage/ktx/lib/writer.c | 748 +++++ .../_plugins/cimage/ktx2/CMakeLists.txt | 59 + applications/_plugins/cimage/ktx2/ktx2.cpp | 1119 +++++++ applications/_plugins/cimage/ktx2/ktx2.def | 17 + applications/_plugins/cimage/ktx2/ktx2.h | 150 + .../_plugins/cimage/tga/cmakelists.txt | 34 +- .../cmesh/mesh_compressor/cmakelists.txt | 34 +- .../cmesh/mesh_optimizer/cmakelists.txt | 23 +- .../_plugins/cmesh/tootle/cmakelists.txt | 50 +- .../_plugins/cmesh/tootle/mesh_tootle.cpp | 12 +- .../_plugins/cmp_gpu/directx/cmakelists.txt | 33 +- .../directx/vs2017/compute_directx.vcxproj | 2 +- .../_plugins/cmp_gpu/gpuhw/cmakelists.txt | 28 +- .../_plugins/cmp_gpu/gpuhw/compute_gpuhw.cpp | 13 +- .../_plugins/cmp_gpu/opencl/cmakelists.txt | 28 +- .../cmp_gpu/opencl/compute_opencl.cpp | 2 +- .../opencl/vs2017/compute_opencl.vcxproj | 2 +- applications/_plugins/common/atiformats.cpp | 283 +- applications/_plugins/common/atiformats.h | 3 +- applications/_plugins/common/cexr.h | 2 +- applications/_plugins/common/cmakelists.txt | 86 +- applications/_plugins/common/cmdline.cpp | 1991 ++++++++---- applications/_plugins/common/cmdline.h | 1 + applications/_plugins/common/cmp_fileio.cpp | 211 +- applications/_plugins/common/cmp_fileio.h | 3 +- .../_plugins/common/gltf/cmakelists.txt | 52 +- applications/_plugins/common/gltf/misc.h | 2 +- applications/_plugins/common/mipstoqimage.cpp | 561 +++- applications/_plugins/common/mipstoqimage.h | 2 +- applications/_plugins/common/misc.cpp | 2 +- .../_plugins/common/pluginmanager.cpp | 28 +- .../_plugins/common/qtimgui/cmakelists.txt | 58 +- .../common/qtimgui/imgui_dx12renderer.h | 3 +- applications/_plugins/common/texture.h | 5 + applications/_plugins/common/textureio.cpp | 178 +- applications/_plugins/common/utilfuncs.cpp | 9 +- applications/compressonatorcli/cmakelists.txt | 284 +- .../compressonatorcli/copyfiles.cmake | 28 +- .../source/compressonatorcli.cpp | 280 +- applications/compressonatorgui/cmakelists.txt | 272 +- .../compressonatorgui/common/cmakelists.txt | 41 +- .../components/ac3dmeshanalysis.cpp | 2 +- .../components/ac3dmeshanalysis.h | 2 +- .../components/acaboutdlg.cpp | 2 +- .../compressonatorgui/components/acaboutdlg.h | 4 +- .../components/accustomgraphics.cpp | 2 +- .../components/acdiffimage.cpp | 2 +- .../components/acdockwidgettitlebar.cpp | 4 +- .../components/acimageview.cpp | 1331 +++++--- .../components/cmakelists.txt | 171 +- .../components/cp3dmodelcompare.cpp | 4 +- .../components/cp3dmodelview.cpp | 439 ++- .../components/cpcompressstatus.cpp | 2 +- .../components/cpgenmips.cpp | 55 +- .../compressonatorgui/components/cpgenmips.h | 17 +- .../components/cpimageanalysis.cpp | 30 +- .../components/cpimagecompare.cpp | 28 +- .../components/cpimageloader.cpp | 10 +- .../components/cpimagepropertyview.cpp | 10 +- .../components/cpimageview.cpp | 846 +++-- .../components/cpprojectdata.h | 7 +- .../components/cpprojectview.cpp | 2793 +++++++++++------ .../components/cpsetcompressoptions.cpp | 42 +- .../components/cpsetmeshoptions.cpp | 32 +- .../components/cpstartuppage.cpp | 390 ++- .../components/cpstartuppage.h | 93 +- .../components/cpwelcomepage.cpp | 2 + .../components/cpwelcomepage.h | 45 +- .../compressonatorgui/compressonatorgui.qrc | 110 +- .../compressonatorgui/copyfiles.cmake | 8 +- .../qpropertypages/cmakelists.txt | 14 +- .../source/cpmaincomponents.cpp | 1480 ++++++--- .../compressonatorgui/source/main.cpp | 107 +- clang-tidy | 52 + cmakelists.txt | 260 +- cmp_compressonatorlib/astc/codec_astc.cpp | 8 +- cmp_compressonatorlib/ati/codec_ati1n.cpp | 102 +- cmp_compressonatorlib/ati/codec_ati1n.h | 10 +- cmp_compressonatorlib/ati/codec_ati2n.cpp | 186 +- cmp_compressonatorlib/ati/codec_ati2n.h | 23 + .../ati/compressonatorxcodec.cpp | 56 +- .../ati/compressonatorxcodec.h | 23 + cmp_compressonatorlib/bc6h/bc6h_encode.cpp | 14 +- cmp_compressonatorlib/buffer/codecbuffer.cpp | 597 +++- cmp_compressonatorlib/buffer/codecbuffer.h | 41 + .../buffer/codecbuffer_r8s.cpp | 248 ++ .../buffer/codecbuffer_r8s.h | 74 + .../buffer/codecbuffer_rg8s.cpp | 244 ++ .../buffer/codecbuffer_rg8s.h | 74 + .../buffer/codecbuffer_rgb888s.cpp | 217 ++ .../buffer/codecbuffer_rgb888s.h | 74 + .../buffer/codecbuffer_rgba2101010.cpp | 2 +- .../buffer/codecbuffer_rgba8888s.cpp | 263 ++ .../buffer/codecbuffer_rgba8888s.h | 74 + cmp_compressonatorlib/cmakelists.txt | 127 +- cmp_compressonatorlib/common.h | 145 +- cmp_compressonatorlib/common/codec.cpp | 5 +- cmp_compressonatorlib/common/commontypes.h | 157 +- cmp_compressonatorlib/compress.cpp | 203 +- cmp_compressonatorlib/compressonator.cpp | 654 ++-- cmp_compressonatorlib/compressonator.h | 60 +- cmp_compressonatorlib/dxtc/codec_dxtc.h | 15 +- .../dxtc/codec_dxtc_alpha.cpp | 668 +++- .../dxtc/dxtc_v11_compress.h | 4 - cmp_core/cmakelists.txt | 63 +- cmp_core/cmp_core.def | 4 + cmp_core/shaders/bc2_encode_kernel.cpp | 2 +- cmp_core/shaders/bc3_encode_kernel.cpp | 3 +- cmp_core/shaders/bc4_encode_kernel.cpp | 265 +- cmp_core/shaders/bc5_encode_kernel.cpp | 121 +- cmp_core/shaders/bc6_encode_kernel.cpp | 21 +- cmp_core/shaders/bc6_encode_kernel.h | 1 + cmp_core/shaders/bc7_encode_kernel.cpp | 10 +- cmp_core/shaders/bcn_common_kernel.h | 1090 +++++-- cmp_core/shaders/common_def.h | 483 ++- cmp_core/source/cmp_core.h | 139 +- cmp_core/source/cmp_math_vec4.h | 12 +- cmp_core/test/cmakelists.txt | 48 +- cmp_framework/cmakelists.txt | 215 +- cmp_framework/cmp_framework.h | 535 ++-- cmp_framework/common/cmp_boxfilter.cpp | 182 +- cmp_framework/common/cmp_mips.cpp | 950 +++++- cmp_framework/common/hdr_encode.cpp | 2 +- cmp_framework/common/hdr_encode.h | 2 +- cmp_framework/compute_base.cpp | 16 +- docs/makefile | 4 +- docs/source/analysis/index.rst | 4 +- .../build_from_source/build_instructions.rst | 208 +- docs/source/command_line_tool/commandline.rst | 97 +- .../developer_sdk/cmp_framework/index.rst | 116 +- .../media/compressonator_window.jpg | Bin 88765 -> 91812 bytes .../user_guide/applicationoptions.rst | 38 +- docs/source/gui_tool/user_guide/imageview.rst | 39 +- .../user_guide/media/appsettings1.png | Bin 0 -> 7488 bytes .../user_guide/media/appsettings2.png | Bin 0 -> 7531 bytes .../user_guide/media/csvfilesupport.png | Bin 0 -> 48513 bytes .../media/image2020-3-26_14-19-1.png | Bin 12252 -> 11856 bytes .../gui_tool/user_guide/media/image42.png | Bin 24131 -> 30351 bytes .../gui_tool/user_guide/media/image43.png | Bin 64776 -> 52043 bytes .../gui_tool/user_guide/media/image435.png | Bin 0 -> 55464 bytes .../gui_tool/user_guide/media/image436.png | Bin 0 -> 13321 bytes .../gui_tool/user_guide/media/image54.png | Bin 17704 -> 10376 bytes .../gui_tool/user_guide/media/image55.png | Bin 18704 -> 11422 bytes .../gui_tool/user_guide/media/image56.png | Bin 1740 -> 1420 bytes .../gui_tool/user_guide/media/image57.png | Bin 5955 -> 3226 bytes .../gui_tool/user_guide/media/image58.png | Bin 28307 -> 37192 bytes .../gui_tool/user_guide/media/image85.png | Bin 25928 -> 29459 bytes .../gui_tool/user_guide/media/image86.png | Bin 25633 -> 29972 bytes .../user_guide/media/psnrdisplayfeature.png | Bin 0 -> 3919 bytes .../gui_tool/user_guide/projectexplorer.rst | 6 +- .../user_guide/texturecompression.rst | 16 +- docs/source/gui_tool/user_guide/userguide.rst | 52 +- docs/source/revisions.rst | 43 +- examples/common/cmakelists.txt | 2 +- examples/common/dds_helpers.cpp | 66 +- examples/core_example1/cmakelists.txt | 5 + examples/core_example1/core_example1.sln | 42 + examples/core_example1/coreexample.cpp | 88 +- .../framework_example1/framework_example1.cpp | 2 +- examples/framework_example1/sdk_example1.sln | 42 + .../framework_example1/sdk_example1.vcxproj | 32 +- .../framework_example2/framework_example2.cpp | 2 +- examples/framework_example2/sdk_example2.sln | 42 + .../framework_example2/sdk_example2.vcxproj | 32 +- .../framework_example3/framework_example3.cpp | 2 +- examples/framework_example3/sdk_example3.sln | 42 + .../framework_example3/sdk_example3.vcxproj | 32 +- examples/framework_example4/cmakelists.txt | 7 +- examples/framework_example4/copyfiles.cmake | 5 +- .../framework_example4/framework_example4.cpp | 11 +- examples/prototype/cmakelists.txt | 12 +- examples/prototype/cmp_prototype.cpp | 12 +- examples/sdk_example1/cmakelists.txt | 2 +- examples/sdk_example1/sdk_example1.cpp | 2 +- examples/sdk_example1/sdk_example1.sln | 42 + examples/sdk_example1/sdk_example1.vcxproj | 32 +- examples/sdk_example2/cmakelists.txt | 2 +- examples/sdk_example2/sdk_example2.cpp | 2 +- examples/sdk_example2/sdk_example2.sln | 42 + examples/sdk_example2/sdk_example2.vcxproj | 32 +- examples/sdk_example3/cmakelists.txt | 2 +- examples/sdk_example3/sdk_example3.cpp | 4 +- examples/sdk_example3/sdk_example3.sln | 42 + examples/sdk_example3/sdk_example3.vcxproj | 32 +- external/cmakelists.txt | 6 +- external/glm/cmakelists.txt | 8 +- external/opencv/cmakelists.txt | 205 +- external/openexr/cmakelists.txt | 17 +- installer/amdcompresscli_64.aip | 254 +- installer/amdcompresscore_64.aip | 31 +- installer/amdcompressframework_64.aip | 54 +- installer/amdcompressgui_64.aip | 601 ++-- installer/amdcompresssdk_64.aip | 21 +- license/license.txt | 2 +- runtime/ktx.dll | 0 scripts/buildcli_linux_package.sh | 97 + scripts/compressonatorcli | 46 + scripts/fetch_dependencies.py | 16 +- scripts/get_version.py | 4 +- scripts/initsetup_mac.sh | 39 + scripts/initsetup_ubuntu.sh | 68 +- scripts/linux_build_apps.sh | 12 +- scripts/update_version.py | 4 +- scripts/windows_build_install.bat | 3 +- scripts/windows_build_sdk.bat | 40 +- vs2017/cmp_compressonatorlib.vcxproj | 73 +- vs2017/cmp_compressonatorlib.vcxproj.filters | 27 + vs2017/cmp_framework.vcxproj | 32 +- 295 files changed, 30624 insertions(+), 10833 deletions(-) delete mode 100644 applications/_plugins/cimage/ktx/ktx.cpp create mode 100644 applications/_plugins/cimage/ktx/ktx1.cpp rename applications/_plugins/cimage/ktx/{ktx.def => ktx1.def} (100%) rename applications/_plugins/cimage/ktx/{cktx.h => ktx1.h} (50%) create mode 100644 applications/_plugins/cimage/ktx/ktxcommon.cpp create mode 100644 applications/_plugins/cimage/ktx/ktxcommon.h create mode 100644 applications/_plugins/cimage/ktx/lib/checkheader.c create mode 100644 applications/_plugins/cimage/ktx/lib/errstr.c create mode 100644 applications/_plugins/cimage/ktx/lib/etcdec.cxx create mode 100644 applications/_plugins/cimage/ktx/lib/etcunpack.cxx create mode 100644 applications/_plugins/cimage/ktx/lib/gl_funcptrs.h create mode 100644 applications/_plugins/cimage/ktx/lib/gles1_funcptrs.h create mode 100644 applications/_plugins/cimage/ktx/lib/gles2_funcptrs.h create mode 100644 applications/_plugins/cimage/ktx/lib/gles3_funcptrs.h create mode 100644 applications/_plugins/cimage/ktx/lib/hashtable.c create mode 100644 applications/_plugins/cimage/ktx/lib/ktx.h create mode 100644 applications/_plugins/cimage/ktx/lib/ktxfilestream.c create mode 100644 applications/_plugins/cimage/ktx/lib/ktxfilestream.h create mode 100644 applications/_plugins/cimage/ktx/lib/ktxint.h create mode 100644 applications/_plugins/cimage/ktx/lib/ktxmemstream.c create mode 100644 applications/_plugins/cimage/ktx/lib/ktxmemstream.h create mode 100644 applications/_plugins/cimage/ktx/lib/ktxstream.h create mode 100644 applications/_plugins/cimage/ktx/lib/loader.c create mode 100644 applications/_plugins/cimage/ktx/lib/swap.c create mode 100644 applications/_plugins/cimage/ktx/lib/uthash.h create mode 100644 applications/_plugins/cimage/ktx/lib/writer.c create mode 100644 applications/_plugins/cimage/ktx2/CMakeLists.txt create mode 100644 applications/_plugins/cimage/ktx2/ktx2.cpp create mode 100644 applications/_plugins/cimage/ktx2/ktx2.def create mode 100644 applications/_plugins/cimage/ktx2/ktx2.h create mode 100644 clang-tidy create mode 100644 cmp_compressonatorlib/buffer/codecbuffer_r8s.cpp create mode 100644 cmp_compressonatorlib/buffer/codecbuffer_r8s.h create mode 100644 cmp_compressonatorlib/buffer/codecbuffer_rg8s.cpp create mode 100644 cmp_compressonatorlib/buffer/codecbuffer_rg8s.h create mode 100644 cmp_compressonatorlib/buffer/codecbuffer_rgb888s.cpp create mode 100644 cmp_compressonatorlib/buffer/codecbuffer_rgb888s.h create mode 100644 cmp_compressonatorlib/buffer/codecbuffer_rgba8888s.cpp create mode 100644 cmp_compressonatorlib/buffer/codecbuffer_rgba8888s.h create mode 100644 docs/source/gui_tool/user_guide/media/appsettings1.png create mode 100644 docs/source/gui_tool/user_guide/media/appsettings2.png create mode 100644 docs/source/gui_tool/user_guide/media/csvfilesupport.png create mode 100644 docs/source/gui_tool/user_guide/media/image435.png create mode 100644 docs/source/gui_tool/user_guide/media/image436.png create mode 100644 docs/source/gui_tool/user_guide/media/psnrdisplayfeature.png create mode 100644 runtime/ktx.dll create mode 100644 scripts/buildcli_linux_package.sh create mode 100644 scripts/compressonatorcli create mode 100644 scripts/initsetup_mac.sh diff --git a/applications/_libs/cmakelists.txt b/applications/_libs/cmakelists.txt index 21be9ea0a..cc667adf6 100644 --- a/applications/_libs/cmakelists.txt +++ b/applications/_libs/cmakelists.txt @@ -6,6 +6,8 @@ add_subdirectory(cmp_fileio) add_subdirectory(cmp_gui) add_subdirectory(cmp_math) add_subdirectory(cmp_mesh) +if (OPTION_BUILD_DRACO) add_subdirectory(cmp_meshcompressor) +endif() add_subdirectory(cmp_meshoptimizer) add_subdirectory(gpu_decode) diff --git a/applications/_libs/cmp_fileio/cmakelists.txt b/applications/_libs/cmp_fileio/cmakelists.txt index ecf1c5c8a..6f4b51ea2 100644 --- a/applications/_libs/cmp_fileio/cmakelists.txt +++ b/applications/_libs/cmp_fileio/cmakelists.txt @@ -11,9 +11,9 @@ target_sources(CMP_FileIO PRIVATE ) target_include_directories(CMP_FileIO PUBLIC - - ${LEGACY_LOCATION} ./ + ${LEGACY_LOCATION} + ${OpenEXR_INCLUDE_DIRS} ) set_target_properties(CMP_FileIO PROPERTIES FOLDER ${FOLDER_NAME}) diff --git a/applications/_libs/cmp_math/cmakelists.txt b/applications/_libs/cmp_math/cmakelists.txt index b4f169cd4..49f979cc9 100644 --- a/applications/_libs/cmp_math/cmakelists.txt +++ b/applications/_libs/cmp_math/cmakelists.txt @@ -1,8 +1,11 @@ add_library(CMP_Math) -# This is the current location of these files. They should be moved here -set(LEGACY_LOCATION ../../../cmp_framework/common) +if(CMP_HOST_WINDOWS) + target_compile_definitions(CMP_Math PUBLIC + -DCMP_USE_XMMINTRIN + ) +endif() target_sources(CMP_Math PRIVATE @@ -19,18 +22,20 @@ target_sources(CMP_Math PRIVATE jmlvec2.h jmlvec3.h jrtcommon.h - ${LEGACY_LOCATION}/mathmacros.h + ${PROJECT_SOURCE_DIR}/cmp_framework/common/mathmacros.h tootlepch.h ) target_include_directories(CMP_Math PUBLIC - ./ + ${PROJECT_SOURCE_DIR}/cmp_core/shaders + ${PROJECT_SOURCE_DIR}/cmp_core/source ) target_link_libraries(CMP_Math PRIVATE - - CMP_Core + # CMP_Core ) -set_target_properties(CMP_Math PROPERTIES FOLDER ${FOLDER_NAME}) +set_target_properties(CMP_Math PROPERTIES + FOLDER "Libs" + ) diff --git a/applications/_libs/cmp_math/cmp_math_common.cpp b/applications/_libs/cmp_math/cmp_math_common.cpp index bad4acd82..5394b4d8a 100644 --- a/applications/_libs/cmp_math/cmp_math_common.cpp +++ b/applications/_libs/cmp_math/cmp_math_common.cpp @@ -1,5 +1,5 @@ //===================================================================== -// Copyright 2018 (c), Advanced Micro Devices, Inc. All rights reserved. +// Copyright 2020 (c), Advanced Micro Devices, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files(the "Software"), to deal @@ -34,7 +34,8 @@ float cpu_sqrtf(float * pIn) { return sqrtf(*pIn); } -#ifndef _LINUX +#ifdef CMP_USE_XMMINTRIN +#ifndef __linux__ //--------------------------------------------- // SSE: Computes square root of a float value //--------------------------------------------- @@ -45,6 +46,7 @@ float sse_sqrtf( float *pIn ) { return val.m128_f32[0]; } #endif +#endif //------------------------------------------------- // CPU: Computes 1 / (square root of a float value) @@ -57,16 +59,29 @@ float cpu_rsqf(float *f) { return 0.0f; } -#ifndef _LINUX +#ifdef CMP_USE_XMMINTRIN +#ifndef __linux__ //------------------------------------------------- // SSE: Computes 1 / (square root of a float value) //------------------------------------------------- -float sse_rsqf(float *v) { +#ifdef CMP_USE_RSQ_RSQR +float sse_rsqf(float* v) +{ __m128 val = _mm_load1_ps(v); - val = _mm_rsqrt_ss(val); + val = _mm_rsqrt_ss(val); float frsq = val.m128_f32[0]; - return (0.5f * frsq) * (3.0f - (*v * frsq) * frsq); + return (0.5f * frsq) * (3.0f - (*v * frsq) * frsq); }; +#else +float sse_rsqf(float *v) { + __m128 val = _mm_set_ss(*v); // Copy float and zero the upper 3 elements + __m128 val1 = _mm_set_ss(1.0f); + val = _mm_sqrt_ss(val); + val = _mm_div_ss(val1, val); + return ( val.m128_f32[0] ); +}; +#endif +#endif #endif //--------------------------------------------- @@ -76,13 +91,15 @@ float cpu_minf(float l1, float r1) { return (l1 < r1 ? l1 : r1); } -#ifndef _LINUX +#ifdef CMP_USE_XMMINTRIN +#ifndef __linux__ float sse_minf( float a, float b ) { // Branchless SSE min. _mm_store_ss( &a, _mm_min_ss(_mm_set_ss(a),_mm_set_ss(b)) ); return a; } #endif +#endif //--------------------------------------------- // CPU: Computes max of two float values @@ -91,13 +108,15 @@ float cpu_maxf(float l1, float r1) { return (l1 > r1 ? l1 : r1); } -#ifndef _LINUX +#ifdef CMP_USE_XMMINTRIN +#ifndef __linux__ float sse_maxf( float a, float b ) { // Branchless SSE max. _mm_store_ss( &a, _mm_max_ss(_mm_set_ss(a),_mm_set_ss(b)) ); return a; } #endif +#endif //================================================ // Clamp the value in the range [minval .. maxval] @@ -111,12 +130,14 @@ float cpu_clampf(float value, float minval, float maxval) { return value; } -#ifndef _LINUX +#ifdef CMP_USE_XMMINTRIN +#ifndef __linux__ float sse_clampf( float val, float minval, float maxval ) { _mm_store_ss( &val, _mm_min_ss( _mm_max_ss(_mm_set_ss(val),_mm_set_ss(minval)), _mm_set_ss(maxval) ) ); return val; } #endif +#endif void cpu_averageRGB(unsigned char *src_rgba_block) { float medianR = 0.0f, medianG = 0.0f, medianB = 0.0f; @@ -184,7 +205,8 @@ float cpu_lerp2(CMP_Vec4uc C1, CMP_Vec4uc CA, CMP_Vec4uc CB, CMP_Vec4uc C2, CMP_ return float(min1+min2); } -#ifndef _LINUX +#ifdef CMP_USE_XMMINTRIN +#ifndef __linux__ float sse_lerp2(CMP_Vec4uc C1, CMP_Vec4uc CA, CMP_Vec4uc CB, CMP_Vec4uc C2, CMP_MATH_BYTE *encode1, CMP_MATH_BYTE *encode2) { // Initial Setup __m128 iC1, iC2, iCA, iCB; //Load auchars into _m128 @@ -301,6 +323,7 @@ void cmp_set_fma3_features() { cmp_lerp2 = fma_lerp2; } #endif +#endif void cmp_set_cpu_features() { @@ -313,7 +336,9 @@ void cmp_set_cpu_features() { cmp_sqrtf = cpu_sqrtf; } -#ifndef _LINUX + +#ifdef CMP_USE_XMMINTRIN +#ifndef __linux__ void cmp_set_sse2_features() { cmp_clampf = sse_clampf; cmp_lerp2 = sse_lerp2; @@ -323,6 +348,7 @@ void cmp_set_sse2_features() { cmp_sqrtf = sse_sqrtf; } #endif +#endif //--------------------------------- // User Interface to the CMP_MATH diff --git a/applications/_libs/cmp_math/cmp_math_common.h b/applications/_libs/cmp_math/cmp_math_common.h index fa2068760..33f4193b5 100644 --- a/applications/_libs/cmp_math/cmp_math_common.h +++ b/applications/_libs/cmp_math/cmp_math_common.h @@ -43,10 +43,12 @@ typedef unsigned char CMP_MATH_BYTE; typedef unsigned int CMP_MATH_DWORD; -#ifndef _LINUX +#ifdef CMP_USE_XMMINTRIN +#ifndef __linux__ extern void cmp_set_fma3_features(); extern void cmp_set_sse2_features(); #endif +#endif extern void cmp_set_cpu_features(); @@ -61,7 +63,7 @@ extern float cpu_rsqf(float *f); extern float cpu_sqrtf(float * pIn); -#ifndef _LINUX +#ifndef __linux__ extern float sse_clampf(float value, float minval, float maxval); extern float sse_lerp2(CMP_Vec4uc C1, CMP_Vec4uc CA, CMP_Vec4uc CB, CMP_Vec4uc C2, CMP_MATH_BYTE *encode1, CMP_MATH_BYTE *encode2); extern float sse_maxf(float l1, float r1); diff --git a/applications/_libs/cmp_math/cmp_math_cpuid.cpp b/applications/_libs/cmp_math/cmp_math_cpuid.cpp index 3fe4c932f..7a2ea5884 100644 --- a/applications/_libs/cmp_math/cmp_math_cpuid.cpp +++ b/applications/_libs/cmp_math/cmp_math_cpuid.cpp @@ -30,7 +30,7 @@ void cmp_cpuid(int cpuInfo[4], int function_id) { // subfunction_id = 0 #ifdef _WIN32 - __cpuidex(cpuInfo, function_id, 0); + __cpuidex(cpuInfo, function_id, 0); // defined in intrin.h #else // To Do //__cpuid_count(0, function_id, cpuInfo[0], cpuInfo[1], cpuInfo[2], cpuInfo[3]); @@ -47,7 +47,7 @@ cmp_cpufeatures cmp_get_cpufeatures() { cpu.feature[i] = 0; } -#ifndef _LINUX +#ifndef __linux__ cmp_cpuid(cpuInfo,0); int nIds = cpuInfo[0]; @@ -118,7 +118,8 @@ void cmp_autodected_cpufeatures(CMP_MATH_BYTE set) { if ((set & CMP_MATH_USE_CPU) > 0) return; -#ifndef _LINUX +#ifdef CMP_USE_XMMINTRIN +#ifndef __linux__ // Auto detect CPU features to enable for (int i = 0; i 0) { @@ -139,6 +140,7 @@ void cmp_autodected_cpufeatures(CMP_MATH_BYTE set) { } } #endif +#endif } diff --git a/applications/_libs/cmp_math/jml.h b/applications/_libs/cmp_math/jml.h index 629765935..9c8dec8ff 100644 --- a/applications/_libs/cmp_math/jml.h +++ b/applications/_libs/cmp_math/jml.h @@ -24,7 +24,7 @@ #ifndef _JML_H_ #define _JML_H_ -#ifdef _LINUX +#ifdef __linux__ #define ALIGN16 #else // helpful alias for 16-byte alignment diff --git a/applications/_libs/cmp_math/jmlfuncs.h b/applications/_libs/cmp_math/jmlfuncs.h index 535b2daec..f1112cf5e 100644 --- a/applications/_libs/cmp_math/jmlfuncs.h +++ b/applications/_libs/cmp_math/jmlfuncs.h @@ -1,5 +1,5 @@ //===================================================================== -// Copyright 2006-2018 (c), Advanced Micro Devices, Inc. All rights reserved. +// Copyright 2006-2020 (c), Advanced Micro Devices, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files(the "Software"), to deal @@ -124,14 +124,27 @@ inline Vec3d Cross(const Vec3d& v1, const Vec3d& v2) { /// Returns a random float between 0 and 1 float RandomFloat(); +#ifdef CMP_USE_RSQ_RSQR +inline float FastRCP(float v) +{ + __m128 a = _mm_load_ss(&v); + __m128 Ra0 = _mm_rcp_ps(a); + __m128 result = _mm_sub_ps(_mm_add_ps(Ra0, Ra0), _mm_mul_ps(_mm_mul_ps(Ra0, a), Ra0)); + float x; + _mm_store_ss(&x, result); + return x; +} +#else inline float FastRCP(float v) { __m128 a = _mm_load_ss(&v); - __m128 Ra0 = _mm_rcp_ps(a); + __m128 val1 = _mm_set_ps(1.0f, 1.0f, 1.0f, 1.0f); + __m128 Ra0 = _mm_div_ps(val1, a); //__m128 Ra0 = _mm_rcp_ps(a); __m128 result = _mm_sub_ps(_mm_add_ps(Ra0, Ra0), _mm_mul_ps(_mm_mul_ps(Ra0, a), Ra0)); float x; _mm_store_ss(&x, result); return x; }; +#endif /// Transformation of a point (w=1) by an arbitrary matrix, multiplies from right /// It is safe for pPoint to equal pPointOut diff --git a/applications/_libs/cmp_math/jmlscalar.h b/applications/_libs/cmp_math/jmlscalar.h index a077081bc..a4ead3864 100644 --- a/applications/_libs/cmp_math/jmlscalar.h +++ b/applications/_libs/cmp_math/jmlscalar.h @@ -27,23 +27,43 @@ #include namespace JML { + inline float RandomFloat() { return (float)rand() / (float) RAND_MAX; }; - -inline float FastSQRT(float v) { +#ifdef CMP_USE_RSQ_RSQR +inline float FastSQRT(float v) +{ __m128 val = _mm_load1_ps(&v); - val = _mm_sqrt_ss(val); + val = _mm_sqrt_ss(val); return val.m128_f32[0]; }; -inline float FastRSQ(float v) { +inline float FastRSQ(float v) +{ __m128 val = _mm_load1_ps(&v); - val = _mm_rsqrt_ss(val); + val = _mm_rsqrt_ss(val); float frsq = val.m128_f32[0]; + return (0.5f * frsq) * (3.0f - (v * frsq) * frsq); +}; +#else +inline float FastSQRT(float v) { + __m128 val = _mm_set_ss(v); + val = _mm_sqrt_ss(val); + return (val.m128_f32[0]); + +}; + +inline float FastRSQ(float v) { + __m128 val = _mm_set_ss(v); + __m128 val1 = _mm_set_ss(1.0f); + val = _mm_sqrt_ss(val); + val = _mm_div_ss(val1, val); + float frsq = val.m128_f32[0]; return (0.5f * frsq) * (3.0f - (v * frsq) * frsq); }; +#endif }; diff --git a/applications/_libs/cmp_math/jmlssevec.h b/applications/_libs/cmp_math/jmlssevec.h index 572504134..5a2704f5c 100644 --- a/applications/_libs/cmp_math/jmlssevec.h +++ b/applications/_libs/cmp_math/jmlssevec.h @@ -1,5 +1,5 @@ //===================================================================== -// Copyright 2006-2018 (c), Advanced Micro Devices, Inc. All rights reserved. +// Copyright 2006-2020 (c), Advanced Micro Devices, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files(the "Software"), to deal @@ -27,74 +27,92 @@ #include #include -#ifdef _LINUX +#ifdef __linux__ class SSEVec4 #else class __declspec(align(16)) SSEVec4 #endif { - public: - - union { +public: + union + { __m128 vec128; -#ifdef _LINUX +#ifdef __linux__ float f32[4]; #endif }; // constructors - inline SSEVec4() {} ; - inline SSEVec4(float x, float y, float z, float w) : vec128(_mm_setr_ps(x, y, z, w)) {}; - inline SSEVec4(__m128 vec) : vec128(vec) {} - inline SSEVec4(const float* data) : vec128(_mm_load_ps(data)) {}; - inline SSEVec4(float scalar) : vec128(_mm_load1_ps(&scalar)) {}; + inline SSEVec4(){}; + inline SSEVec4(float x, float y, float z, float w) + : vec128(_mm_setr_ps(x, y, z, w)){}; + inline SSEVec4(__m128 vec) + : vec128(vec) + { + } + inline SSEVec4(const float* data) + : vec128(_mm_load_ps(data)){}; + inline SSEVec4(float scalar) + : vec128(_mm_load1_ps(&scalar)){}; // copy and assignment - inline SSEVec4(const SSEVec4& init) : vec128(init.vec128) {}; - inline const SSEVec4& operator=(const SSEVec4& lhs) { + inline SSEVec4(const SSEVec4& init) + : vec128(init.vec128){}; + inline const SSEVec4& operator=(const SSEVec4& lhs) + { vec128 = lhs.vec128; return *this; }; // conversion to m128 type for direct use in _mm intrinsics - inline operator __m128() { + inline operator __m128() + { return vec128; }; - inline operator const __m128() const { + inline operator const __m128() const + { return vec128; }; - // indexing -#ifdef _LINUX - inline const float& operator[](int i) const { + // indexing +#ifdef __linux__ + inline const float& operator[](int i) const + { return f32[i]; }; - inline float& operator[](int i) { + inline float& operator[](int i) + { return f32[i]; }; #else - inline const float& operator[](int i) const { + inline const float& operator[](int i) const + { return vec128.m128_f32[i]; }; - inline float& operator[](int i) { + inline float& operator[](int i) + { return vec128.m128_f32[i]; }; #endif // addition - inline SSEVec4 operator+(const SSEVec4& rhs) const { + inline SSEVec4 operator+(const SSEVec4& rhs) const + { return SSEVec4(_mm_add_ps(vec128, rhs.vec128)); }; - inline SSEVec4& operator+=(const SSEVec4& rhs) { + inline SSEVec4& operator+=(const SSEVec4& rhs) + { vec128 = _mm_add_ps(vec128, rhs.vec128); return *this; }; // multiplication - inline SSEVec4 operator*(const SSEVec4& rhs) const { + inline SSEVec4 operator*(const SSEVec4& rhs) const + { return SSEVec4(_mm_mul_ps(vec128, rhs.vec128)); }; - inline SSEVec4& operator*=(const SSEVec4& rhs) { + inline SSEVec4& operator*=(const SSEVec4& rhs) + { vec128 = _mm_mul_ps(vec128, rhs.vec128); return *this; }; @@ -103,67 +121,82 @@ class __declspec(align(16)) SSEVec4 //inline SSEVec4 operator*( float rhs ) const { return SSEVec4( _mm_mul_ps(vec128, _mm_load1_ps(&rhs)) ); }; //inline SSEVec4& operator*=( float rhs ) { vec128 = _mm_mul_ps(vec128, _mm_load1_ps(&rhs)); return *this; }; - // subtraction - inline SSEVec4 operator-(const SSEVec4& rhs) const { + inline SSEVec4 operator-(const SSEVec4& rhs) const + { return SSEVec4(_mm_sub_ps(vec128, rhs.vec128)); }; - inline SSEVec4& operator-= (const SSEVec4& rhs) { + inline SSEVec4& operator-=(const SSEVec4& rhs) + { vec128 = _mm_sub_ps(vec128, rhs.vec128); return *this; }; // division - inline SSEVec4 operator/(const SSEVec4& rhs) const { + inline SSEVec4 operator/(const SSEVec4& rhs) const + { return SSEVec4(_mm_div_ps(vec128, rhs.vec128)); }; - inline SSEVec4& operator/= (const SSEVec4& rhs) { + inline SSEVec4& operator/=(const SSEVec4& rhs) + { vec128 = _mm_div_ps(vec128, rhs.vec128); return *this; }; // scalar division - inline SSEVec4 operator/(float rhs) const { + inline SSEVec4 operator/(float rhs) const + { return SSEVec4(_mm_div_ps(vec128, _mm_load1_ps(&rhs))); }; - inline SSEVec4& operator/=(float rhs) { + inline SSEVec4& operator/=(float rhs) + { vec128 = _mm_div_ps(vec128, _mm_load1_ps(&rhs)); return *this; }; // comparison // these return 0 or 0xffffffff in each component - inline SSEVec4 operator< (const SSEVec4& rhs) const { + inline SSEVec4 operator<(const SSEVec4& rhs) const + { return SSEVec4(_mm_cmplt_ps(vec128, rhs.vec128)); }; - inline SSEVec4 operator> (const SSEVec4& rhs) const { + inline SSEVec4 operator>(const SSEVec4& rhs) const + { return SSEVec4(_mm_cmpgt_ps(vec128, rhs.vec128)); }; - inline SSEVec4 operator<=(const SSEVec4& rhs) const { + inline SSEVec4 operator<=(const SSEVec4& rhs) const + { return SSEVec4(_mm_cmple_ps(vec128, rhs.vec128)); }; - inline SSEVec4 operator>=(const SSEVec4& rhs) const { + inline SSEVec4 operator>=(const SSEVec4& rhs) const + { return SSEVec4(_mm_cmpge_ps(vec128, rhs.vec128)); }; - inline SSEVec4 operator==(const SSEVec4& rhs) const { + inline SSEVec4 operator==(const SSEVec4& rhs) const + { return SSEVec4(_mm_cmpeq_ps(vec128, rhs.vec128)); }; // bitwise operators - inline SSEVec4 operator|(const SSEVec4& rhs) const { + inline SSEVec4 operator|(const SSEVec4& rhs) const + { return SSEVec4(_mm_or_ps(vec128, rhs.vec128)); }; - inline SSEVec4 operator&(const SSEVec4& rhs) const { + inline SSEVec4 operator&(const SSEVec4& rhs) const + { return SSEVec4(_mm_and_ps(vec128, rhs.vec128)); }; - inline SSEVec4 operator^(const SSEVec4& rhs) const { + inline SSEVec4 operator^(const SSEVec4& rhs) const + { return SSEVec4(_mm_xor_ps(vec128, rhs.vec128)); }; - inline const SSEVec4& operator|=(const SSEVec4& rhs) { + inline const SSEVec4& operator|=(const SSEVec4& rhs) + { vec128 = _mm_or_ps(vec128, rhs.vec128); return *this; }; - inline const SSEVec4& operator&=(const SSEVec4& rhs) { + inline const SSEVec4& operator&=(const SSEVec4& rhs) + { vec128 = _mm_and_ps(vec128, rhs.vec128); return *this; }; @@ -171,26 +204,26 @@ class __declspec(align(16)) SSEVec4 // for some horrible reason,there's no bitwise not instruction for SSE, // so we have to do xor with 0xfffffff in order to fake it. // TO get a 0xffffffff, we execute 0=0 - inline SSEVec4 operator~() const { - __m128 zero = _mm_setzero_ps(); + inline SSEVec4 operator~() const + { + __m128 zero = _mm_setzero_ps(); __m128 is_true = _mm_cmpeq_ps(zero, zero); return _mm_xor_ps(is_true, vec128); }; - }; // swizzling macros -#define VEC4_SWIZZLE( vec, x, y, z, w ) SSEVec4( _mm_shuffle_ps( vec.vec128,vec.vec128, _MM_SHUFFLE(x,y,z,w) ) ) -#define VEC4_X( vec ) VEC4_SWIZZLE(vec,0,0,0,0) -#define VEC4_Y( vec ) VEC4_SWIZZLE(vec,1,1,1,1) -#define VEC4_Z( vec ) VEC4_SWIZZLE(vec,2,2,2,2) -#define VEC4_W( vec ) VEC4_SWIZZLE(vec,3,3,3,3) +#define VEC4_SWIZZLE(vec, x, y, z, w) SSEVec4(_mm_shuffle_ps(vec.vec128, vec.vec128, _MM_SHUFFLE(x, y, z, w))) +#define VEC4_X(vec) VEC4_SWIZZLE(vec, 0, 0, 0, 0) +#define VEC4_Y(vec) VEC4_SWIZZLE(vec, 1, 1, 1, 1) +#define VEC4_Z(vec) VEC4_SWIZZLE(vec, 2, 2, 2, 2) +#define VEC4_W(vec) VEC4_SWIZZLE(vec, 3, 3, 3, 3) // a shuffling macro. I don't like Microsoft's, theirs is backwards -#define VEC4_SHUFFLE(x,y,z,w) _MM_SHUFFLE(w,z,y,x) - +#define VEC4_SHUFFLE(x, y, z, w) _MM_SHUFFLE(w, z, y, x) -inline float SSEVec3Dot(const SSEVec4& a, const SSEVec4& b) { +inline float SSEVec3Dot(const SSEVec4& a, const SSEVec4& b) +{ SSEVec4 tmp = a * b; SSEVec4 sum = VEC4_Y(tmp) + VEC4_Z(tmp); sum += tmp; @@ -199,9 +232,8 @@ inline float SSEVec3Dot(const SSEVec4& a, const SSEVec4& b) { return ret; }; - // functions for SQRT, RSQ, RCP, MIN, MAX - +#ifdef CMP_USE_RSQ_RSQR inline SSEVec4 SSEVecQuickRCP(const SSEVec4& v) { return SSEVec4(_mm_rcp_ps(v.vec128)); }; @@ -224,30 +256,70 @@ inline SSEVec4 SSEVecRSQ(const SSEVec4& a) { return (fvecf0pt5 * Ra0) * (fvecf3pt0 - (a * Ra0) * Ra0); }; -inline float SSEVec3Length(const SSEVec4& value) { +#else +inline SSEVec4 SSEVecQuickRCP(const SSEVec4& v) +{ + __m128 val1 = _mm_set_ps(1.0f, 1.0f, 1.0f, 1.0f); + return SSEVec4(_mm_div_ps(val1, v.vec128)); // // return SSEVec4(_mm_rcp_ps(v.vec128)); +}; + +// RCP with newton-raphson iteration. +inline SSEVec4 SSEVecRCP(const SSEVec4& a) +{ + __m128 val1 = _mm_set_ps(1.0f, 1.0f, 1.0f, 1.0f); + __m128 Ra0 = _mm_div_ps(val1, a.vec128); // __m128 Ra0 = _mm_rcp_ps(a.vec128); + return SSEVec4(_mm_sub_ps(_mm_add_ps(Ra0, Ra0), _mm_mul_ps(_mm_mul_ps(Ra0, a.vec128), Ra0))); +}; + +inline SSEVec4 SSEVecQuickRSQ(const SSEVec4& v) +{ + __m128 val1 = _mm_set_ps(1.0f, 1.0f, 1.0f, 1.0f); + __m128 val = _mm_sqrt_ps(v.vec128); + return SSEVec4(_mm_div_ps(val1, val)); // return SSEVec4(_mm_rsqrt_ps(v.vec128)); +}; + +// RSQ with newton-raphson iteration. +inline SSEVec4 SSEVecRSQ(const SSEVec4& a) +{ + static const SSEVec4 fvecf0pt5(0.5f); + static const SSEVec4 fvecf3pt0(3.0f); + + __m128 val1 = _mm_set_ps(1.0f, 1.0f, 1.0f, 1.0f); + __m128 val = _mm_sqrt_ps(a.vec128); + SSEVec4 Ra0 = _mm_div_ps(val1, val); // SSEVec4 Ra0 = _mm_rsqrt_ps(a.vec128); + return (fvecf0pt5 * Ra0) * (fvecf3pt0 - (a * Ra0) * Ra0); +}; +#endif + +inline float SSEVec3Length(const SSEVec4& value) +{ float len = SSEVec3Dot(value, value); return sqrt(len); }; - -inline SSEVec4 SSEVec3Normalize(const SSEVec4& value) { +inline SSEVec4 SSEVec3Normalize(const SSEVec4& value) +{ // TODO: write a FastRSQ scalar function and use that here - float len = SSEVec3Dot(value, value); - float lenrsq = 1.0f / sqrt(len) ; + float len = SSEVec3Dot(value, value); + float lenrsq = 1.0f / sqrt(len); return value * SSEVec4(lenrsq); }; -inline SSEVec4 SSEVecSQRT(const SSEVec4& v) { +inline SSEVec4 SSEVecSQRT(const SSEVec4& v) +{ return SSEVec4(_mm_sqrt_ps(v.vec128)); }; -inline SSEVec4 SSEVecMIN(const SSEVec4& v1, const SSEVec4& v2) { +inline SSEVec4 SSEVecMIN(const SSEVec4& v1, const SSEVec4& v2) +{ return SSEVec4(_mm_min_ps(v1.vec128, v2.vec128)); }; -inline SSEVec4 SSEVecMAX(const SSEVec4& v1, const SSEVec4& v2) { +inline SSEVec4 SSEVecMAX(const SSEVec4& v1, const SSEVec4& v2) +{ return SSEVec4(_mm_max_ps(v1.vec128, v2.vec128)); }; -inline SSEVec4 SSEVecZero() { +inline SSEVec4 SSEVecZero() +{ return _mm_setzero_ps(); }; @@ -256,35 +328,40 @@ inline SSEVec4 SSEVecZero() { // allows more efficient branching // If you dont use these VC++.NET 2K3 will tend to dump the SSE regs // out to the stack and suck them back in to implement a compare -inline int SSEVecIsXEqual(const SSEVec4& a, const SSEVec4& b) { +inline int SSEVecIsXEqual(const SSEVec4& a, const SSEVec4& b) +{ return _mm_ucomieq_ss(a, b); }; -inline int SSEVecIsXNEqual(const SSEVec4& a, const SSEVec4& b) { +inline int SSEVecIsXNEqual(const SSEVec4& a, const SSEVec4& b) +{ return _mm_ucomineq_ss(a, b); }; - // these return true if the corresponding component is 0xffffffff -inline int SSEVecIsXSet(const SSEVec4& vec) { +inline int SSEVecIsXSet(const SSEVec4& vec) +{ // HACK: -- comparisons dont work right when nans are involved // and comparisons also return nan on true. To fix it, we're turning nan into 1.0f // which will work when comparing against zero return SSEVecIsXNEqual(vec & SSEVec4(1.0f), SSEVecZero()); }; -inline int SSEVecIsYSet(const SSEVec4& vec) { +inline int SSEVecIsYSet(const SSEVec4& vec) +{ return SSEVecIsXSet(VEC4_Y(vec)); }; -inline int SSEVecIsZSet(const SSEVec4& vec) { +inline int SSEVecIsZSet(const SSEVec4& vec) +{ return SSEVecIsXSet(VEC4_Z(vec)); }; -inline int SSEVecIsWSet(const SSEVec4& vec) { +inline int SSEVecIsWSet(const SSEVec4& vec) +{ return SSEVecIsXSet(VEC4_W(vec)); }; - /// returns true if any component of the argument is 0xffffffff -inline int SSEVecIsAnySet(const SSEVec4& states) { +inline int SSEVecIsAnySet(const SSEVec4& states) +{ SSEVec4 x(VEC4_X(states)); SSEVec4 y(VEC4_Y(states)); SSEVec4 z(VEC4_Z(states)); @@ -295,15 +372,17 @@ inline int SSEVecIsAnySet(const SSEVec4& states) { return SSEVecIsXSet(res); }; - // loading helpers -inline SSEVec4 SSEVecLoadf(float f) { +inline SSEVec4 SSEVecLoadf(float f) +{ return _mm_load_ss(&f); }; -inline SSEVec4 SSEVecLoad2f(const float* f) { +inline SSEVec4 SSEVecLoad2f(const float* f) +{ return _mm_loadl_pi(SSEVecZero(), (__m64*)f); }; -inline SSEVec4 SSEVecLoad3f(const float* f) { +inline SSEVec4 SSEVecLoad3f(const float* f) +{ // load first two components and zero second two SSEVec4 tmp = SSEVecLoad2f((float*)f); @@ -312,36 +391,34 @@ inline SSEVec4 SSEVecLoad3f(const float* f) { return _mm_shuffle_ps(tmp, tmp2, VEC4_SHUFFLE(0, 1, 0, 1)); }; - - // storing helpers -inline void SSEVecStore2f(const SSEVec4& vec, float* pOut) { +inline void SSEVecStore2f(const SSEVec4& vec, float* pOut) +{ _mm_storel_pi((__m64*)pOut, vec.vec128); }; -inline void SSEVecStore3f(const SSEVec4& vec, float* pOut) { +inline void SSEVecStore3f(const SSEVec4& vec, float* pOut) +{ _mm_storel_pi((__m64*)pOut, vec.vec128); _mm_store_ss((pOut + 2), VEC4_Z(vec)); }; - - // executes a conditional move // target = (condition) ? val : target; -inline SSEVec4 SSEVecSelect(const SSEVec4& condition, const SSEVec4& val, const SSEVec4& target) { - SSEVec4 res = SSEVecZero(); - res = res | (val & condition); +inline SSEVec4 SSEVecSelect(const SSEVec4& condition, const SSEVec4& val, const SSEVec4& target) +{ + SSEVec4 res = SSEVecZero(); + res = res | (val & condition); SSEVec4 inv_cond = ~condition; - res = res | (target & inv_cond); + res = res | (target & inv_cond); return res; - }; // stream output thingie for vectors -inline std::ostream& operator<<(std::ostream& out, const SSEVec4& vec) { +inline std::ostream& operator<<(std::ostream& out, const SSEVec4& vec) +{ out << "<" << vec[0] << "," << vec[1] << "," << vec[2] << "," << vec[3] << ">"; return out; }; - #endif diff --git a/applications/_libs/cmp_math/tootlepch.h b/applications/_libs/cmp_math/tootlepch.h index 9a06d8614..61d20776a 100644 --- a/applications/_libs/cmp_math/tootlepch.h +++ b/applications/_libs/cmp_math/tootlepch.h @@ -17,10 +17,12 @@ #define _SOFTWARE_ONLY_VERSION // Do not use DirectX SDK #endif // !_SOFTWARE_ONLY_VERSION -#ifdef _LINUX +#ifdef __linux__ #define __cdecl -#define _isnan(x) isnan(x) -#define _finite(x) finite(x) +#define cmp_isnan(x) isnan(x) +#define cmp_finite(x) finite(x) +#else +#define cmp_isnan(x) std::isnan(x) #endif #ifdef __cplusplus diff --git a/applications/_libs/cmp_mesh/cmakelists.txt b/applications/_libs/cmp_mesh/cmakelists.txt index e53b64da5..bc4e7e2c3 100644 --- a/applications/_libs/cmp_mesh/cmakelists.txt +++ b/applications/_libs/cmp_mesh/cmakelists.txt @@ -1,8 +1,14 @@ add_library(CMP_Mesh) +file(GLOB_RECURSE MATH_JRT_SRC + jrt/*.cpp + jrt/*.h + ) + target_sources(CMP_Mesh PRIVATE + ${MATH_JRT_SRC} aligned_malloc.cpp aligned_malloc.h bbox.h @@ -45,21 +51,16 @@ target_sources(CMP_Mesh window.h ) -add_subdirectory(jrt) - target_include_directories(CMP_Mesh PUBLIC - ./ ./jrt/ ) target_link_libraries(CMP_Mesh PRIVATE - CMP_Math ) if (OPTION_CMP_DIRECTX) - target_sources(CMP_Mesh PRIVATE overdraw.cpp overdraw.h @@ -67,6 +68,10 @@ if (OPTION_CMP_DIRECTX) tootlelib.h ) + target_include_directories(CMP_Mesh PUBLIC + ${PROJECT_SOURCE_DIR}/../common/lib/ext/apitrace/dxsdk/Include # using d3dx9 for cmp_mesh + ) + set(SOFTWARE_VERSION_ONLY ON) if (SOFTWARE_VERSION_ONLY) @@ -81,9 +86,8 @@ if (OPTION_CMP_DIRECTX) ) endif() - target_link_libraries(CMP_Mesh PRIVATE - ExtDirectX - ) endif() -set_target_properties(CMP_Mesh PROPERTIES FOLDER ${FOLDER_NAME}) +set_target_properties(CMP_Mesh PROPERTIES + FOLDER "Libs" + ) diff --git a/applications/_libs/cmp_mesh/error.h b/applications/_libs/cmp_mesh/error.h index c699e050b..69683e4b1 100644 --- a/applications/_libs/cmp_mesh/error.h +++ b/applications/_libs/cmp_mesh/error.h @@ -14,7 +14,7 @@ extern "C" void error_output(const char* fmt, ...); -#ifdef _LINUX +#ifdef __linux__ #define warnf(args) #define errorf(args) #define assertf(cond, args) @@ -42,7 +42,7 @@ void error_output(const char* fmt, ...); #endif #ifndef ERROR_NDEBUG -#ifdef _LINUX +#ifdef __linux__ #define debugf(args) #else #define debugf(args) do { \ diff --git a/applications/_libs/cmp_mesh/jrt/jrtcommon.h b/applications/_libs/cmp_mesh/jrt/jrtcommon.h index 5cbd89845..9f2fdacdd 100644 --- a/applications/_libs/cmp_mesh/jrt/jrtcommon.h +++ b/applications/_libs/cmp_mesh/jrt/jrtcommon.h @@ -13,7 +13,7 @@ #include -#ifdef _LINUX +#ifdef __linux__ typedef unsigned long long UINT64; #else typedef unsigned __int64 UINT64; diff --git a/applications/_libs/cmp_mesh/jrt/jrtkdtree.h b/applications/_libs/cmp_mesh/jrt/jrtkdtree.h index 3265dc91c..70f69f496 100644 --- a/applications/_libs/cmp_mesh/jrt/jrtkdtree.h +++ b/applications/_libs/cmp_mesh/jrt/jrtkdtree.h @@ -11,7 +11,7 @@ #include "jrttriangleintersection.h" #include "jrtcore.h" -#ifdef _LINUX +#ifdef __linux__ #include "../aligned_malloc.h" #define _aligned_malloc aligned_malloc #define _aligned_free aligned_free diff --git a/applications/_libs/cmp_mesh/jrt/jrttriangleintersection.cpp b/applications/_libs/cmp_mesh/jrt/jrttriangleintersection.cpp index e1737c6ff..6bcd65e1c 100644 --- a/applications/_libs/cmp_mesh/jrt/jrttriangleintersection.cpp +++ b/applications/_libs/cmp_mesh/jrt/jrttriangleintersection.cpp @@ -144,7 +144,7 @@ bool SegTest(float segU1, float segU2, float segV1, float segV2, float Pu, float // locate U = Pu on the segment float alpha = (Pu - segU1) / (segU2 - segU1); - assert(!_isnan(alpha) && _finite(alpha)); + assert(!cmp_isnan(alpha) && _finite(alpha)); if (alpha <= 0.0f || alpha > 1.0f) { return false; @@ -297,7 +297,7 @@ bool RayTriangleIntersect(const JRTCoreTriangle* pTri, const float* origin, cons #endif - assert(!_isnan(t) && _finite(t) && t >= tmin && t <= tmax); + assert(!cmp_isnan(t) && _finite(t) && t >= tmin && t <= tmax); //see: http://graphics.stanford.edu/courses/cs348b-98/gg/intersect.html diff --git a/applications/_libs/cmp_mesh/tootlelib.h b/applications/_libs/cmp_mesh/tootlelib.h index 0535ad970..712278228 100644 --- a/applications/_libs/cmp_mesh/tootlelib.h +++ b/applications/_libs/cmp_mesh/tootlelib.h @@ -7,7 +7,7 @@ #ifndef _TOOTLE_LIB_H_ #define _TOOTLE_LIB_H_ -#ifdef _LINUX +#ifdef __linux__ #define TOOTLE_DLL #else #define TOOTLE_DLL __declspec(dllexport) diff --git a/applications/_libs/cmp_mesh/tootlepch.h b/applications/_libs/cmp_mesh/tootlepch.h index 34dc0be96..7d2de2a01 100644 --- a/applications/_libs/cmp_mesh/tootlepch.h +++ b/applications/_libs/cmp_mesh/tootlepch.h @@ -13,10 +13,12 @@ #endif #endif -#ifdef _LINUX +#ifdef __linux__ #define __cdecl #define _isnan(x) isnan(x) #define _finite(x) finite(x) +#else +#define cmp_isnan(x) std::isnan(x) #endif #ifdef __cplusplus diff --git a/applications/_libs/cmp_meshcompressor/cmakelists.txt b/applications/_libs/cmp_meshcompressor/cmakelists.txt index 49c718775..63ab3bc79 100644 --- a/applications/_libs/cmp_meshcompressor/cmakelists.txt +++ b/applications/_libs/cmp_meshcompressor/cmakelists.txt @@ -1,18 +1,33 @@ +cmake_minimum_required(VERSION 3.10) -add_library(CMP_MeshCompressor) +add_library(CMP_MeshCompressor STATIC "") -file(GLOB_RECURSE DRACO_SRC - draco/*.h - draco/*.cc -) +file(GLOB_RECURSE DracoLib + "./draco/src/draco/*.h" + "./draco/src/draco/attributes/*.h" + "./draco/src/draco/attributes/*.cc" + "./draco/src/draco/compression/*.h" + "./draco/src/draco/compression/*.cc" + "./draco/src/draco/core/*.h" + "./draco/src/draco/core/*.cc" + "./draco/src/draco/io/*.h" + "./draco/src/draco/io/*.cc" + "./draco/src/draco/mesh/*.h" + "./draco/src/draco/mesh/*.cc" + "./draco/src/draco/metadata/*.h" + "./draco/src/draco/metadata/*.cc" + "./draco/src/draco/point_cloud/*.h" + "./draco/src/draco/point_cloud/*.cc" + ) -target_sources(CMP_MeshCompressor PRIVATE - ${DRACO_SRC} -) +target_sources(CMP_MeshCompressor + PRIVATE + ${DracoLib} + ) -target_include_directories(CMP_MeshCompressor PUBLIC - ./draco/src/ - ./ -) +target_include_directories(CMP_MeshCompressor + PRIVATE + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_meshcompressor/draco/src + ) -set_target_properties(CMP_MeshCompressor PROPERTIES FOLDER ${FOLDER_NAME}) +set_target_properties(CMP_MeshCompressor PROPERTIES FOLDER "Libs") diff --git a/applications/_libs/cmp_meshoptimizer/cmakelists.txt b/applications/_libs/cmp_meshoptimizer/cmakelists.txt index 07fa0bc11..576b8e4f0 100644 --- a/applications/_libs/cmp_meshoptimizer/cmakelists.txt +++ b/applications/_libs/cmp_meshoptimizer/cmakelists.txt @@ -1,8 +1,30 @@ +# add_library(CMP_MeshOptimizer SHARED) +# +# target_sources(CMP_MeshOptimizer PRIVATE +# meshoptimizer.h +# indexcodec.cpp +# indexgenerator.cpp +# overdrawanalyzer.cpp +# overdrawoptimizer.cpp +# simplifier.cpp +# stripifier.cpp +# vcacheanalyzer.cpp +# vcacheoptimizer.cpp +# vfetchanalyzer.cpp +# vfetchoptimizer.cpp +# ) +# +# target_compile_definitions(CMP_MeshOptimizer PRIVATE BUILD_AS_PLUGIN_DLL=1) +# +# set_target_properties(CMP_MeshOptimizer PROPERTIES +# FOLDER "PluginDynamic/Mesh_Compression" +# RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin/debug/plugins" +# RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin/release/plugins" +# ) add_library(CMP_MeshOptimizer) -target_sources(CMP_MeshOptimizer PRIVATE - +target_sources(CMP_MeshOptimizer PUBLIC indexcodec.cpp indexgenerator.cpp meshoptimizer.h @@ -17,8 +39,13 @@ target_sources(CMP_MeshOptimizer PRIVATE ) target_include_directories(CMP_MeshOptimizer PUBLIC - ./ + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half + ${PROJECT_SOURCE_DIR}/applications/_plugins/common + ) -set_target_properties(CMP_MeshOptimizer PROPERTIES FOLDER ${FOLDER_NAME}) +set_target_properties(CMP_MeshOptimizer PROPERTIES FOLDER "Libs") + + diff --git a/applications/_libs/gpu_decode/cmakelists.txt b/applications/_libs/gpu_decode/cmakelists.txt index b4338f6fc..20eac70fc 100644 --- a/applications/_libs/gpu_decode/cmakelists.txt +++ b/applications/_libs/gpu_decode/cmakelists.txt @@ -1,26 +1,19 @@ - -add_library(GPU_Decode) - -target_sources(GPU_Decode -PRIVATE - gpu_decode.cpp - gpu_decode.h - gpu_decodebase.cpp - gpu_decodebase.h +set(GPU_DECODE_H + gpu_decodebase.cpp + gpu_decode.cpp ) -target_include_directories(GPU_Decode -PUBLIC - ./ - ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib - ${PROJECT_SOURCE_DIR}/cmp_framework/common - ${PROJECT_SOURCE_DIR}/cmp_framework/common/half +set(GPU_DECODE_SRC + gpu_decodebase.h + gpu_decode.h ) -target_link_libraries(GPU_Decode -PRIVATE - CompressonatorLIB - Plugin_PluginManager +add_library(CMP_GpuDecode STATIC ${GPU_DECODE_H} ${GPU_DECODE_SRC}) + +target_include_directories(CMP_GpuDecode PRIVATE + ${PROJECT_SOURCE_DIR}/applications/_plugins/common + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib ) -set_target_properties(GPU_Decode PROPERTIES FOLDER ${FOLDER_NAME}) +set_target_properties(CMP_GpuDecode PROPERTIES FOLDER "Libs") diff --git a/applications/_libs/gpu_decode/gpu_decodebase.cpp b/applications/_libs/gpu_decode/gpu_decodebase.cpp index 5246786e9..79a9f665b 100644 --- a/applications/_libs/gpu_decode/gpu_decodebase.cpp +++ b/applications/_libs/gpu_decode/gpu_decodebase.cpp @@ -25,12 +25,10 @@ //===================================================================== #ifdef _WIN32 -#ifndef DISABLE_TESTCODE #include "common.h" #include "compressonator.h" #include "gpu_decodebase.h" - using namespace GPU_Decode; LRESULT CALLBACK WndProc2(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { @@ -140,6 +138,6 @@ HRESULT RenderWindow::InitWindow(int width, int height,WNDPROC callback) { return S_OK; } -#endif + #endif diff --git a/applications/_libs/gpu_decode/gpu_decodebase.h b/applications/_libs/gpu_decode/gpu_decodebase.h index 4ba6af002..d2002d35a 100644 --- a/applications/_libs/gpu_decode/gpu_decodebase.h +++ b/applications/_libs/gpu_decode/gpu_decodebase.h @@ -27,7 +27,6 @@ #define H_GPU_DECODE_BASE #ifdef _WIN32 -#ifndef DISABLE_TESTCODE #include "compressonator.h" #include //uncomment to show image on console window for debug @@ -77,6 +76,6 @@ class TextureControl: public RenderWindow { extern LRESULT CALLBACK WndProc2(HWND, UINT, WPARAM, LPARAM); -#endif // !H_GPU_DECODE_BASE + #endif #endif diff --git a/applications/_plugins/c3dmodel_loaders/drc/cmakelists.txt b/applications/_plugins/c3dmodel_loaders/drc/cmakelists.txt index 77fc9c98e..8ce4bba3f 100644 --- a/applications/_plugins/c3dmodel_loaders/drc/cmakelists.txt +++ b/applications/_plugins/c3dmodel_loaders/drc/cmakelists.txt @@ -1,27 +1,34 @@ -add_library(Plugin_C3DModel_Loaders_drc) +add_library(ModelLoader_drc SHARED) -target_sources(Plugin_C3DModel_Loaders_drc PRIVATE +target_sources(ModelLoader_drc PRIVATE modelloader_drc.cpp modelloader_drc.h ) -target_link_libraries(Plugin_C3DModel_Loaders_drc PRIVATE - - CompressonatorLIB +target_link_libraries(ModelLoader_drc PRIVATE + CMP_Compressonator + CMP_Framework CMP_MeshCompressor - CMP_MeshOptimizer - - Plugin_PluginManager - Plugin_TCPluginAPI ) # Find the QtWidgets library find_package(Qt5 REQUIRED COMPONENTS Core Widgets HINTS ${QT_PACKAGE_ROOT}) -target_include_directories(Plugin_C3DModel_Loaders_drc PUBLIC +target_include_directories(ModelLoader_drc PUBLIC ./ + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half + ${PROJECT_SOURCE_DIR}/applications/_plugins/common + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_meshoptimizer + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_meshcompressor/draco/src ) -set_target_properties(Plugin_C3DModel_Loaders_drc PROPERTIES FOLDER ${FOLDER_NAME}) +target_compile_definitions(ModelLoader_drc PRIVATE BUILD_AS_PLUGIN_DLL=1) + +set_target_properties(ModelLoader_drc PROPERTIES + FOLDER "Plugin_Dynamic/3DModel_Loaders" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin/debug/plugins" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin/release/plugins" + ) diff --git a/applications/_plugins/c3dmodel_loaders/drc/modelloader_drc.cpp b/applications/_plugins/c3dmodel_loaders/drc/modelloader_drc.cpp index 5d4d0b3dd..e2b1c9d88 100644 --- a/applications/_plugins/c3dmodel_loaders/drc/modelloader_drc.cpp +++ b/applications/_plugins/c3dmodel_loaders/drc/modelloader_drc.cpp @@ -9,7 +9,6 @@ using namespace draco; - #ifdef BUILD_AS_PLUGIN_DLL DECLARE_PLUGIN(Plugin_ModelLoader_drc) SET_PLUGIN_TYPE("3DMODEL_LOADER") diff --git a/applications/_plugins/c3dmodel_loaders/gltf_20/cmakelists.txt b/applications/_plugins/c3dmodel_loaders/gltf_20/cmakelists.txt index 06fbb7a68..ac621eb93 100644 --- a/applications/_plugins/c3dmodel_loaders/gltf_20/cmakelists.txt +++ b/applications/_plugins/c3dmodel_loaders/gltf_20/cmakelists.txt @@ -1,28 +1,36 @@ -add_library(Plugin_C3DModel_Loaders_glTF_20) +add_library(ModelLoader_gltf SHARED) -target_sources(Plugin_C3DModel_Loaders_glTF_20 PRIVATE +target_sources(ModelLoader_gltf PRIVATE gltf_20.cpp gltf_20.h ) -target_link_libraries(Plugin_C3DModel_Loaders_glTF_20 PRIVATE - - CompressonatorLIB +target_link_libraries(ModelLoader_gltf PRIVATE + CMP_Compressonator + CMP_Framework CMP_MeshCompressor - CMP_MeshOptimizer - - Plugin_Common_Misc - Plugin_GLTF - Plugin_PluginManager - Plugin_TCPluginAPI + CMP_GUI_Gltf + CMP_Common # CModel Data ) find_package(Qt5 REQUIRED COMPONENTS Core Widgets HINTS ${QT_PACKAGE_ROOT}) -target_include_directories(Plugin_C3DModel_Loaders_glTF_20 PUBLIC - ${PROJECT_SOURCE_DIR}/../common/lib/ext/glm/ +target_include_directories(ModelLoader_gltf PUBLIC ./ + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/gltf + ${PROJECT_SOURCE_DIR}/../common/lib/ext/glm/ + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half + ${PROJECT_SOURCE_DIR}/applications/_plugins/common + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_meshoptimizer + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_meshcompressor/draco/src ) -set_target_properties(Plugin_C3DModel_Loaders_glTF_20 PROPERTIES FOLDER ${FOLDER_NAME}) +target_compile_definitions(ModelLoader_gltf PRIVATE BUILD_AS_PLUGIN_DLL=1) + +set_target_properties(ModelLoader_gltf PROPERTIES + FOLDER "Plugin_Dynamic/3DModel_Loaders" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin/debug/plugins" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin/release/plugins" + ) diff --git a/applications/_plugins/c3dmodel_loaders/obj/cmakelists.txt b/applications/_plugins/c3dmodel_loaders/obj/cmakelists.txt index c41b11db3..b8531b5e6 100644 --- a/applications/_plugins/c3dmodel_loaders/obj/cmakelists.txt +++ b/applications/_plugins/c3dmodel_loaders/obj/cmakelists.txt @@ -1,5 +1,5 @@ -add_library(Plugin_C3DModel_Loaders_obj) +add_library(ModelLoader_obj SHARED) file(GLOB_RECURSE SRCS @@ -7,26 +7,30 @@ file(GLOB_RECURSE SRCS "*.h" ) -target_sources(Plugin_C3DModel_Loaders_obj PRIVATE +target_sources(ModelLoader_obj PRIVATE ${SRCS} ) -target_link_libraries(Plugin_C3DModel_Loaders_obj PRIVATE - - CompressonatorLIB +target_link_libraries(ModelLoader_obj PRIVATE + CMP_Compressonator + CMP_Framework CMP_MeshCompressor - CMP_MeshOptimizer - - Plugin_TCPluginAPI - Plugin_PluginManager - Plugin_GLTF - Plugin_Common_UtilFuncs + CMP_Common ) -target_include_directories(Plugin_C3DModel_Loaders_obj PUBLIC - +target_include_directories(ModelLoader_obj PUBLIC ./ + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half + ${PROJECT_SOURCE_DIR}/applications/_plugins/common + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_meshoptimizer ) -set_target_properties(Plugin_C3DModel_Loaders_obj PROPERTIES FOLDER ${FOLDER_NAME}) +target_compile_definitions(ModelLoader_obj PRIVATE BUILD_AS_PLUGIN_DLL=1) + +set_target_properties(ModelLoader_obj PROPERTIES + FOLDER "Plugin_Dynamic/3DModel_Loaders" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin/debug/plugins" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin/release/plugins" + ) diff --git a/applications/_plugins/c3dmodel_viewers/gltf_dx12_ex/cmakelists.txt b/applications/_plugins/c3dmodel_viewers/gltf_dx12_ex/cmakelists.txt index 7c66daea0..378dd2f79 100644 --- a/applications/_plugins/c3dmodel_viewers/gltf_dx12_ex/cmakelists.txt +++ b/applications/_plugins/c3dmodel_viewers/gltf_dx12_ex/cmakelists.txt @@ -1,36 +1,69 @@ +# this must be before add_executable +# link_directories( +# ${PROJECT_EXTERNAL_LIBDIR}/directxtex/DirectXTex-jun2020b/DirectXTex/Bin/Desktop_2017/x64 +# ) -add_library(Plugin_C3DModel_Viewers_glTF_DX12_EX) +add_library(ModelViewer_DX12 SHARED) -file(GLOB_RECURSE SRCS - *.cpp - *.h +file(GLOB_RECURSE GLTF_DX12_SRCS + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/gltf/DX12/*.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/gltf/DX12/*.h ) -target_sources(Plugin_C3DModel_Viewers_glTF_DX12_EX PRIVATE - ${SRCS} +file(GLOB_RECURSE DX12UTIL_SRCS + dx12util/*.cpp + dx12util/*.h ) -target_include_directories(Plugin_C3DModel_Viewers_glTF_DX12_EX PUBLIC +target_sources(ModelViewer_DX12 PRIVATE + ${GLTF_DX12_SRCS} + ${DX12UTIL_SRCS} + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/qtimgui/imgui_dx12.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/qtimgui/imgui_dx12renderer.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/qtimgui/imgui_dx12renderer.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/model_viewer/modeltexture.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/model_viewer/modeltexture.cpp + gltf_dx12_ex.h + gltf_dx12_ex.cpp + gltf_dx12deviceex.h + gltf_dx12deviceex.cpp + gltf_dx12rendererex.h + gltf_dx12rendererex.cpp +) +target_include_directories(ModelViewer_DX12 PUBLIC ./ ./media ./dx12util -) + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half + ${PROJECT_SOURCE_DIR}/applications/_libs/gpu_decode + ${PROJECT_SOURCE_DIR}/applications/_plugins/common + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/d3dx12 + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/gltf + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/gltf/dx12 + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/model_viewer + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_meshoptimizer + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/qtimgui + ${PROJECT_SOURCE_DIR}/../common/lib/ext/glm + ${Qt5Gui_INCLUDE_DIRS} + ) -target_link_libraries(Plugin_C3DModel_Viewers_glTF_DX12_EX PRIVATE - - CompressonatorLIB - ExtDirectX - Plugin_Common_D3DX12 - Plugin_GLTF_DX12 - GPU_Decode - Plugin_CmdLine - Plugin_Model_Viewer - Plugin_Common_QtImgui_DX12 - Plugin_Common_Misc +target_link_libraries(ModelViewer_DX12 PRIVATE + CMP_Compressonator + CMP_Framework + CMP_MeshCompressor + CMP_GUI_Gltf + CMP_GpuDecode + CMP_Imgui + CMP_Common # CModel Data ) -set_target_properties(Plugin_C3DModel_Viewers_glTF_DX12_EX PROPERTIES +target_compile_definitions(ModelViewer_DX12 PRIVATE BUILD_AS_PLUGIN_DLL=1) + +set_target_properties(ModelViewer_DX12 PROPERTIES AUTOMOC ON - FOLDER ${FOLDER_NAME} + FOLDER "Plugin_Dynamic/3DModel_Viewers" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin/debug/plugins" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin/release/plugins" ) diff --git a/applications/_plugins/c3dmodel_viewers/gltf_dx12_ex/gltf_dx12deviceex.cpp b/applications/_plugins/c3dmodel_viewers/gltf_dx12_ex/gltf_dx12deviceex.cpp index 1744750c0..ff1373a67 100644 --- a/applications/_plugins/c3dmodel_viewers/gltf_dx12_ex/gltf_dx12deviceex.cpp +++ b/applications/_plugins/c3dmodel_viewers/gltf_dx12_ex/gltf_dx12deviceex.cpp @@ -24,6 +24,7 @@ #include "gltfcommon.h" #include "misc.h" +#include "error.h" #ifdef ENABLE_RENDER_CODE #include "gltf_dx12rendererex.h" diff --git a/applications/_plugins/c3dmodel_viewers/gltf_dx12_ex/gltf_dx12rendererex.cpp b/applications/_plugins/c3dmodel_viewers/gltf_dx12_ex/gltf_dx12rendererex.cpp index b19d45876..ff3b8ed6b 100644 --- a/applications/_plugins/c3dmodel_viewers/gltf_dx12_ex/gltf_dx12rendererex.cpp +++ b/applications/_plugins/c3dmodel_viewers/gltf_dx12_ex/gltf_dx12rendererex.cpp @@ -17,12 +17,12 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. - #include "misc.h" #include "gltfpbr.h" #include "bloom.h" #include "threadpool.h" #include "gltf_dx12rendererex.h" +#include "error.h" #include @@ -33,8 +33,16 @@ using namespace DirectX; // OnCreate // //-------------------------------------------------------------------------------------- -void glTF_DX12RendererEx::OnCreate(ID3D12Device* pDevice, ID3D12CommandQueue* pDirectQueue, UINT node, UINT nodemask, void *pluginManager, void *msghandler, ImGuiRenderer_DX12 *ImGuiRenderer, QImGUI_WindowWrapper_DX12 *window) { - m_pDevice = pDevice; +void glTF_DX12RendererEx::OnCreate(ID3D12Device* pDevice, + ID3D12CommandQueue* pDirectQueue, + UINT node, + UINT nodemask, + void* pluginManager, + void* msghandler, + ImGuiRenderer_DX12* ImGuiRenderer, + QImGUI_WindowWrapper_DX12* window) +{ + m_pDevice = pDevice; m_pDirectQueue = pDirectQueue; m_gltfDepth = NULL; @@ -47,13 +55,12 @@ void glTF_DX12RendererEx::OnCreate(ID3D12Device* pDevice, ID3D12CommandQueue* pD m_Heaps.OnCreate(pDevice, 2000, 3000, 10, 3, 40, 1000, nodemask); // Create a 'dynamic' constant buffers ring - m_ConstantBufferRing.OnCreate(pDevice, cNumSwapBufs, 1*1024 * 1024, &m_Heaps, 1500, node, nodemask); + m_ConstantBufferRing.OnCreate(pDevice, cNumSwapBufs, 1 * 1024 * 1024, &m_Heaps, 1500, node, nodemask); // Create a 'static' constant buffers pool m_StaticBufferPool.OnCreate(pDevice, 30 * 1024 * 1024, USE_VID_MEM, node, nodemask); m_StaticConstantBufferPool.OnCreate(pDevice, 2 * 1024 * 1024, &m_Heaps, 100, USE_VID_MEM, node, nodemask); - // Create a commandlist ring for the Direct queue m_CommandListRing.OnCreate(pDevice, m_pDirectQueue, cNumSwapBufs, 8, nodemask); @@ -64,20 +71,19 @@ void glTF_DX12RendererEx::OnCreate(ID3D12Device* pDevice, ID3D12CommandQueue* pD m_GPUTimer.OnCreate(pDevice, cNumSwapBufs, node, nodemask); // Quick helper to upload resources, it has it's own commandList. - m_UploadHeap.OnCreate(pDevice, 64 * 1024 * 1024, m_pDirectQueue, node, nodemask); // initialize an upload heap (uses suballocation for faster results) + m_UploadHeap.OnCreate(pDevice, 64 * 1024 * 1024, m_pDirectQueue, node, nodemask); // initialize an upload heap (uses suballocation for faster results) if (window) ImGuiRenderer->initialize(window, pDevice, &m_UploadHeap, &m_Heaps, &m_ConstantBufferRing, node, nodemask); - // Create the depth buffer view m_Heaps.AllocDSVDescriptor(1, &m_DepthBufferDSV); - // Shadowmap resource and view #ifdef USE_SHADOWMAPS - m_ShadowMap.InitDepthStencil(pDevice, &CD3DX12_RESOURCE_DESC::Tex2D(DXGI_FORMAT_R32_TYPELESS, 1024, 1024, 1, 1, 1, 0, D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL), node, nodemask); - m_ShadowMapViewPort = { 0.0f, 0.0f, (float)m_ShadowMap.GetWidth(), (float)m_ShadowMap.GetHeight(), 0.0f, 1.0f }; + m_ShadowMap.InitDepthStencil( + pDevice, &CD3DX12_RESOURCE_DESC::Tex2D(DXGI_FORMAT_R32_TYPELESS, 1024, 1024, 1, 1, 1, 0, D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL), node, nodemask); + m_ShadowMapViewPort = {0.0f, 0.0f, (float)m_ShadowMap.GetWidth(), (float)m_ShadowMap.GetHeight(), 0.0f, 1.0f}; m_Heaps.AllocDSVDescriptor(1, &m_ShadowMapDSV); m_ShadowMap.CreateDSV(0, &m_ShadowMapDSV); m_ShadowMap.Resource()->SetName(L"m_pShadowMap"); @@ -87,7 +93,8 @@ void glTF_DX12RendererEx::OnCreate(ID3D12Device* pDevice, ID3D12CommandQueue* pD m_bloom.OnCreate(pDevice, node, &m_Heaps, &m_StaticBufferPool, &m_ConstantBufferRing); #endif - m_skyDome.OnCreate(pDevice, &m_UploadHeap, DXGI_FORMAT_R16G16B16A16_UNORM, &m_Heaps, &m_StaticBufferPool, &m_ConstantBufferRing, 4, pluginManager, msghandler); + m_skyDome.OnCreate( + pDevice, &m_UploadHeap, DXGI_FORMAT_R16G16B16A16_UNORM, &m_Heaps, &m_StaticBufferPool, &m_ConstantBufferRing, 4, pluginManager, msghandler); m_Heaps.AllocRTVDescriptor(1, &m_HDRRTV); m_Heaps.AllocRTVDescriptor(1, &m_HDRRTVMSAA); @@ -98,10 +105,8 @@ void glTF_DX12RendererEx::OnCreate(ID3D12Device* pDevice, ID3D12CommandQueue* pD // Create tonemapping pass m_toneMapping.OnCreate(pDevice, node, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, &m_Heaps, &m_StaticBufferPool, &m_ConstantBufferRing); - // Make sure upload heap has finished uploading before continuing m_UploadHeap.FlushAndFinish(); - } //-------------------------------------------------------------------------------------- @@ -109,8 +114,8 @@ void glTF_DX12RendererEx::OnCreate(ID3D12Device* pDevice, ID3D12CommandQueue* pD // OnDestroy // //-------------------------------------------------------------------------------------- -void glTF_DX12RendererEx::OnDestroy() { - +void glTF_DX12RendererEx::OnDestroy() +{ m_skyDome.OnDestroy(); #ifdef USE_BLOOM @@ -131,7 +136,6 @@ void glTF_DX12RendererEx::OnDestroy() { m_StaticBufferPool.OnDestroy(); m_ConstantBufferRing.OnDestroy(); m_Heaps.OnDestroy(); - } //-------------------------------------------------------------------------------------- @@ -139,9 +143,9 @@ void glTF_DX12RendererEx::OnDestroy() { // OnCreateWindowSizeDependentResources // //-------------------------------------------------------------------------------------- -void glTF_DX12RendererEx::OnCreateWindowSizeDependentResources(ID3D12Device* pDevice, DWORD Width, DWORD Height, UINT node, UINT nodemask) { - - m_Width = Width; +void glTF_DX12RendererEx::OnCreateWindowSizeDependentResources(ID3D12Device* pDevice, DWORD Width, DWORD Height, UINT node, UINT nodemask) +{ + m_Width = Width; m_Height = Height; #ifdef USE_BLOOM @@ -150,29 +154,32 @@ void glTF_DX12RendererEx::OnCreateWindowSizeDependentResources(ID3D12Device* pDe #endif // Create depth buffer - m_pDepthBuffer.InitDepthStencil(pDevice, &CD3DX12_RESOURCE_DESC::Tex2D(DXGI_FORMAT_R32_TYPELESS, Width, Height, 1, 1, 4, 0, D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL), node, nodemask); + m_pDepthBuffer.InitDepthStencil( + pDevice, &CD3DX12_RESOURCE_DESC::Tex2D(DXGI_FORMAT_R32_TYPELESS, Width, Height, 1, 1, 4, 0, D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL), node, nodemask); m_pDepthBuffer.CreateDSV(0, &m_DepthBufferDSV); m_pDepthBuffer.Resource()->SetName(L"DepthBuffer"); // Create Texture + RTV with x4 MSAA - CD3DX12_RESOURCE_DESC RDescMSAA = CD3DX12_RESOURCE_DESC::Tex2D(DXGI_FORMAT_R16G16B16A16_UNORM, Width, Height, 1, 1, 4, 0, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET); + CD3DX12_RESOURCE_DESC RDescMSAA = + CD3DX12_RESOURCE_DESC::Tex2D(DXGI_FORMAT_R16G16B16A16_UNORM, Width, Height, 1, 1, 4, 0, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET); m_pHDRMSAA.InitRendertarget(pDevice, &RDescMSAA, node, nodemask); m_pHDRMSAA.CreateSRV(0, &m_HDRSRVMSAA); m_pHDRMSAA.CreateRTV(0, &m_HDRRTVMSAA); m_pHDRMSAA.Resource()->SetName(L"HDRMSAA"); // Create Texture + RTV, to hold trhe resolved scene - CD3DX12_RESOURCE_DESC RDesc = CD3DX12_RESOURCE_DESC::Tex2D(DXGI_FORMAT_R16G16B16A16_UNORM, Width, Height, 1, 1, 1, 0, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET); + CD3DX12_RESOURCE_DESC RDesc = + CD3DX12_RESOURCE_DESC::Tex2D(DXGI_FORMAT_R16G16B16A16_UNORM, Width, Height, 1, 1, 1, 0, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET); m_HDR.InitRendertarget(pDevice, &RDesc, node, nodemask); m_HDR.CreateSRV(0, &m_HDRSRV); m_HDR.CreateRTV(0, &m_HDRRTV); m_HDR.Resource()->SetName(L"HDR"); // Set the viewport - m_ViewPort = { 0.0f, 0.0f, static_cast(Width), static_cast(Height), 0.0f, 1.0f }; + m_ViewPort = {0.0f, 0.0f, static_cast(Width), static_cast(Height), 0.0f, 1.0f}; // Create scissor rectangle - m_RectScissor = { 0, 0, (LONG)Width, (LONG)Height }; + m_RectScissor = {0, 0, (LONG)Width, (LONG)Height}; } //-------------------------------------------------------------------------------------- @@ -180,7 +187,8 @@ void glTF_DX12RendererEx::OnCreateWindowSizeDependentResources(ID3D12Device* pDe // OnDestroyWindowSizeDependentResources // //-------------------------------------------------------------------------------------- -void glTF_DX12RendererEx::OnDestroyWindowSizeDependentResources() { +void glTF_DX12RendererEx::OnDestroyWindowSizeDependentResources() +{ m_HDR.OnDestroy(); m_pHDRMSAA.OnDestroy(); m_pDepthBuffer.OnDestroy(); @@ -188,15 +196,14 @@ void glTF_DX12RendererEx::OnDestroyWindowSizeDependentResources() { #ifdef USE_BLOOM m_bloom.OnDestroyWindowSizeDependentResources(); #endif - } //-------------------------------------------------------------------------------------- // LoadScene //-------------------------------------------------------------------------------------- -void glTF_DX12RendererEx::LoadScene(GLTFCommon *gltfData, void *pluginManager, void *msghandler) { - +void glTF_DX12RendererEx::LoadScene(GLTFCommon* gltfData, void* pluginManager, void* msghandler) +{ ID3D12GraphicsCommandList* pCmdLst = m_UploadHeap.GetCommandList(); DWORD ini = GetTickCount(); @@ -204,48 +211,31 @@ void glTF_DX12RendererEx::LoadScene(GLTFCommon *gltfData, void *pluginManager, v //create the glTF's textures, VBs, IBs, shaders and descriptors #ifdef USE_SHADOWMAPS - if (m_gltfDepth = new GltfDepthPass()) { - m_gltfDepth->OnCreate( - m_pDevice, - &m_UploadHeap, - &m_Heaps, - &m_ConstantBufferRing, - &m_StaticBufferPool, - gltfData, - pluginManager, - msghandler - ); + if (m_gltfDepth = new GltfDepthPass()) + { + m_gltfDepth->OnCreate(m_pDevice, &m_UploadHeap, &m_Heaps, &m_ConstantBufferRing, &m_StaticBufferPool, gltfData, pluginManager, msghandler); } #endif - if (m_gltfPBR = new GltfPbr()) { - m_gltfPBR->OnCreate( - m_pDevice, - &m_UploadHeap, - &m_Heaps, - &m_ConstantBufferRing, - &m_StaticBufferPool, - gltfData, - &m_skyDome, + if (m_gltfPBR = new GltfPbr()) + { + m_gltfPBR->OnCreate(m_pDevice, + &m_UploadHeap, + &m_Heaps, + &m_ConstantBufferRing, + &m_StaticBufferPool, + gltfData, + &m_skyDome, #ifdef USE_SHADOWMAPS - &m_ShadowMap, + &m_ShadowMap, #endif - pluginManager, - msghandler - ); + pluginManager, + msghandler); } - if (m_gltfBBox = new GltfBBoxPass()) { - m_gltfBBox->OnCreate( - m_pDevice, - &m_UploadHeap, - &m_Heaps, - &m_ConstantBufferRing, - &m_StaticBufferPool, - gltfData, - pluginManager, - msghandler - ); + if (m_gltfBBox = new GltfBBoxPass()) + { + m_gltfBBox->OnCreate(m_pDevice, &m_UploadHeap, &m_Heaps, &m_ConstantBufferRing, &m_StaticBufferPool, gltfData, pluginManager, msghandler); } m_TimeToLoadScene = (GetTickCount() - ini) + gltfData->m_CommonLoadTime; @@ -254,12 +244,11 @@ void glTF_DX12RendererEx::LoadScene(GLTFCommon *gltfData, void *pluginManager, v m_StaticBufferPool.UploadData(m_UploadHeap.GetCommandList()); m_UploadHeap.FlushAndFinish(); -#if (USE_VID_MEM==true) +#if (USE_VID_MEM == true) //once everything is uploaded we dont need he upload heaps anymore m_StaticBufferPool.FreeUploadHeap(); m_StaticConstantBufferPool.FreeUploadHeap(); #endif - } //-------------------------------------------------------------------------------------- @@ -267,22 +256,25 @@ void glTF_DX12RendererEx::LoadScene(GLTFCommon *gltfData, void *pluginManager, v // UnloadScene // //-------------------------------------------------------------------------------------- -void glTF_DX12RendererEx::UnloadScene() { - if (m_gltfBBox) { +void glTF_DX12RendererEx::UnloadScene() +{ + if (m_gltfBBox) + { m_gltfBBox->OnDestroy(); delete m_gltfBBox; } - if (m_gltfPBR) { + if (m_gltfPBR) + { m_gltfPBR->OnDestroy(); delete m_gltfPBR; } - if (m_gltfDepth) { + if (m_gltfDepth) + { m_gltfDepth->OnDestroy(); delete m_gltfDepth; } - } //-------------------------------------------------------------------------------------- @@ -290,7 +282,12 @@ void glTF_DX12RendererEx::UnloadScene() { // OnRender // //-------------------------------------------------------------------------------------- -void glTF_DX12RendererEx::OnRender(State *pState, ID3D12Resource* pRenderTarget, D3D12_CPU_DESCRIPTOR_HANDLE *pRenderTargetRTV, ImGuiRenderer_DX12 *ImGuiRenderer, UserInterface *UI) { +void glTF_DX12RendererEx::OnRender(State* pState, + ID3D12Resource* pRenderTarget, + D3D12_CPU_DESCRIPTOR_HANDLE* pRenderTargetRTV, + ImGuiRenderer_DX12* ImGuiRenderer, + UserInterface* UI) +{ // Timing values // UINT64 gpuFrequency; @@ -310,7 +307,6 @@ void glTF_DX12RendererEx::OnRender(State *pState, ID3D12Resource* pRenderTarget, m_GPUTimer.GetTimeStamp(pCmdLst, "Begin Frame"); - // Clear GBuffer and depth stencil // pCmdLst->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(pRenderTarget, D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET)); @@ -322,14 +318,13 @@ void glTF_DX12RendererEx::OnRender(State *pState, ID3D12Resource* pRenderTarget, m_GPUTimer.GetTimeStamp(pCmdLst, "Clear Shadow Map"); #endif - float clearColor[] = { 0.0f, 0.0f, 0.0f, 1.0f }; + float clearColor[] = {0.0f, 0.0f, 0.0f, 1.0f}; pCmdLst->ClearRenderTargetView(m_HDRRTVMSAA.GetCPU(), clearColor, 0, nullptr); m_GPUTimer.GetTimeStamp(pCmdLst, "Clear HDR"); pCmdLst->ClearDepthStencilView(m_DepthBufferDSV.GetCPU(), D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr); m_GPUTimer.GetTimeStamp(pCmdLst, "Clear Depth"); - // Shadow map ----------------------------------------------------------------------- // #ifdef USE_SHADOWMAPS @@ -337,9 +332,10 @@ void glTF_DX12RendererEx::OnRender(State *pState, ID3D12Resource* pRenderTarget, pCmdLst->RSSetViewports(1, &m_ShadowMapViewPort); pCmdLst->OMSetRenderTargets(0, NULL, true, &m_ShadowMapDSV.GetCPU()); - if (m_gltfDepth) { - GltfDepthPass::per_batch *cbDepthPerBatch = m_gltfDepth->SetPerBatchConstants(); - cbDepthPerBatch->mViewProj = pState->light.GetView() * pState->light.GetProjection(); + if (m_gltfDepth) + { + GltfDepthPass::per_batch* cbDepthPerBatch = m_gltfDepth->SetPerBatchConstants(); + cbDepthPerBatch->mViewProj = pState->light.GetView() * pState->light.GetProjection(); m_gltfDepth->Draw(pCmdLst); } @@ -347,48 +343,53 @@ void glTF_DX12RendererEx::OnRender(State *pState, ID3D12Resource* pRenderTarget, // Render Scene to the MSAA HDR RT ------------------------------------------------ // - pCmdLst->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_ShadowMap.Resource(), D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)); + pCmdLst->ResourceBarrier( + 1, &CD3DX12_RESOURCE_BARRIER::Transition(m_ShadowMap.Resource(), D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)); #endif pCmdLst->RSSetViewports(1, &m_ViewPort); pCmdLst->RSSetScissorRects(1, &m_RectScissor); pCmdLst->OMSetRenderTargets(1, &m_HDRRTVMSAA.GetCPU(), true, &m_DepthBufferDSV.GetCPU()); - // Render skydome // - if (pState->bDrawSkyDome) { + if (pState->bDrawSkyDome) + { XMMATRIX clipToView = XMMatrixInverse(NULL, pState->camera.GetView() * pState->camera.GetProjection()); m_skyDome.Draw(pCmdLst, clipToView); } // Render scene // - if (m_gltfPBR) { + if (m_gltfPBR) + { //set per frame constant buffer values - GltfPbr::per_batch *cbPerBatch = m_gltfPBR->SetPerBatchConstants(); - cbPerBatch->mCameraViewProj = pState->camera.GetView() * pState->camera.GetProjection(); - cbPerBatch->cameraPos = pState->camera.GetPosition(); - cbPerBatch->mLightViewProj = pState->light.GetView() * pState->light.GetProjection(); - cbPerBatch->lightDirection = pState->light.GetDirection(); - cbPerBatch->lightColor = XMVectorSet(1.0f, 1.0f, 1.0f, 0.0f) *pState->spotLightIntensity; - cbPerBatch->depthBias = pState->depthBias; - cbPerBatch->iblFactor = pState->iblFactor; + GltfPbr::per_batch* cbPerBatch = m_gltfPBR->SetPerBatchConstants(); + cbPerBatch->mCameraViewProj = pState->camera.GetView() * pState->camera.GetProjection(); + cbPerBatch->cameraPos = pState->camera.GetPosition(); + cbPerBatch->mLightViewProj = pState->light.GetView() * pState->light.GetProjection(); + cbPerBatch->lightDirection = pState->light.GetDirection(); + cbPerBatch->lightColor = XMVectorSet(1.0f, 1.0f, 1.0f, 0.0f) * pState->spotLightIntensity; + cbPerBatch->depthBias = pState->depthBias; + cbPerBatch->iblFactor = pState->iblFactor; m_gltfPBR->Draw(pCmdLst); UI->m_TotalNumIndices = m_gltfPBR->m_TotalNumIndices; } // draw bounding boxes - if (m_gltfBBox) { - if (pState->bDrawBoundingBoxes) { - XMMATRIX *pCam = m_gltfBBox->SetPerBatchConstants(); - *pCam = pState->camera.GetView() * pState->camera.GetProjection(); + if (m_gltfBBox) + { + if (pState->bDrawBoundingBoxes) + { + XMMATRIX* pCam = m_gltfBBox->SetPerBatchConstants(); + *pCam = pState->camera.GetView() * pState->camera.GetProjection(); m_gltfBBox->Draw(pCmdLst); } } #ifdef USE_SHADOWMAPS - pCmdLst->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_ShadowMap.Resource(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_DEPTH_WRITE)); + pCmdLst->ResourceBarrier( + 1, &CD3DX12_RESOURCE_BARRIER::Transition(m_ShadowMap.Resource(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_DEPTH_WRITE)); #endif m_GPUTimer.GetTimeStamp(pCmdLst, "Rendering Scene"); @@ -396,12 +397,17 @@ void glTF_DX12RendererEx::OnRender(State *pState, ID3D12Resource* pRenderTarget, // Resolve ------------------------------------------------------------------------ // pCmdLst->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_HDR.Resource(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_RESOLVE_DEST)); - pCmdLst->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_pHDRMSAA.Resource(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_RESOLVE_SOURCE)); + pCmdLst->ResourceBarrier( + 1, &CD3DX12_RESOURCE_BARRIER::Transition(m_pHDRMSAA.Resource(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_RESOLVE_SOURCE)); pCmdLst->ResolveSubresource(m_HDR.Resource(), 0, m_pHDRMSAA.Resource(), 0, DXGI_FORMAT_R16G16B16A16_UNORM); - pCmdLst->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_HDR.Resource(), D3D12_RESOURCE_STATE_RESOLVE_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)); // the bloom needs to read from this - pCmdLst->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_pHDRMSAA.Resource(), D3D12_RESOURCE_STATE_RESOLVE_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET)); + pCmdLst->ResourceBarrier( + 1, + &CD3DX12_RESOURCE_BARRIER::Transition( + m_HDR.Resource(), D3D12_RESOURCE_STATE_RESOLVE_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)); // the bloom needs to read from this + pCmdLst->ResourceBarrier( + 1, &CD3DX12_RESOURCE_BARRIER::Transition(m_pHDRMSAA.Resource(), D3D12_RESOURCE_STATE_RESOLVE_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET)); m_GPUTimer.GetTimeStamp(pCmdLst, "Resolve"); @@ -427,7 +433,8 @@ void glTF_DX12RendererEx::OnRender(State *pState, ID3D12Resource* pRenderTarget, pCmdLst->RSSetScissorRects(1, &m_RectScissor); pCmdLst->OMSetRenderTargets(1, pRenderTargetRTV, true, NULL); - if (UI->m_showimgui && ImGuiRenderer) { + if (UI->m_showimgui && ImGuiRenderer) + { ImGuiRenderer->Draw(pCmdLst); } @@ -441,16 +448,13 @@ void glTF_DX12RendererEx::OnRender(State *pState, ID3D12Resource* pRenderTarget, m_GPUTimer.CollectTimings(pCmdLst); - // Close & Submit the command list ---------------------------------------------------- // ThrowIfFailed(pCmdLst->Close()); - ID3D12CommandList* CmdListList[] = { pCmdLst }; + ID3D12CommandList* CmdListList[] = {pCmdLst}; m_pDirectQueue->ExecuteCommandLists(1, CmdListList); // issue a fence so we can tell when this frame ended m_FrameFence.IssueFence(m_pDirectQueue); - } - diff --git a/applications/_plugins/c3dmodel_viewers/gltf_opengl/cmakelists.txt b/applications/_plugins/c3dmodel_viewers/gltf_opengl/cmakelists.txt index e422572be..4a50001d9 100644 --- a/applications/_plugins/c3dmodel_viewers/gltf_opengl/cmakelists.txt +++ b/applications/_plugins/c3dmodel_viewers/gltf_opengl/cmakelists.txt @@ -1,5 +1,5 @@ -add_library(Plugin_C3DModel_Viewers_glTF_OpenGL) +add_library(ModelViewer_OpenGL SHARED) file(GLOB_RECURSE SRCS @@ -8,34 +8,40 @@ file(GLOB_RECURSE SRCS *.h ) -target_sources(Plugin_C3DModel_Viewers_glTF_OpenGL PRIVATE - +target_sources(ModelViewer_OpenGL PRIVATE ${SRCS} -) + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/qtimgui/imgui_opengl.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/qtimgui/imgui_openglrenderer.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/qtimgui/imgui_openglrenderer.h -target_include_directories(Plugin_C3DModel_Viewers_glTF_OpenGL PUBLIC + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_meshoptimizer/simplifier.cpp +) +target_include_directories(ModelViewer_OpenGL PUBLIC ./ + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half + ${PROJECT_SOURCE_DIR}/applications/_libs/gpu_decode + ${PROJECT_SOURCE_DIR}/applications/_plugins/common + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_meshoptimizer + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/qtimgui + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_math ) -target_link_libraries(Plugin_C3DModel_Viewers_glTF_OpenGL PRIVATE - - CompressonatorLIB - CMP_Math - +target_link_libraries(ModelViewer_OpenGL PRIVATE + CMP_Compressonator + CMP_Framework + CMP_Imgui + CMP_Common # CModel Data ExtOpenGL ExtQt5OpenGL - - Plugin_GLTF - Plugin_UserInterface - Plugin_Common_Imgui - Plugin_Common_QtImgui_OpenGL - Plugin_TCPluginAPI - Plugin_Model_Viewer - Plugin_CmdLine ) -set_target_properties(Plugin_C3DModel_Viewers_glTF_OpenGL PROPERTIES +target_compile_definitions(ModelViewer_OpenGL PRIVATE BUILD_AS_PLUGIN_DLL=1) + +set_target_properties(ModelViewer_OpenGL PROPERTIES AUTOMOC ON - FOLDER ${FOLDER_NAME} + FOLDER "Plugin_Dynamic/3DModel_Viewers" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin/debug/plugins" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin/release/plugins" ) diff --git a/applications/_plugins/c3dmodel_viewers/vulkan/cmakelists.txt b/applications/_plugins/c3dmodel_viewers/vulkan/cmakelists.txt index 15545630e..d99a626e0 100644 --- a/applications/_plugins/c3dmodel_viewers/vulkan/cmakelists.txt +++ b/applications/_plugins/c3dmodel_viewers/vulkan/cmakelists.txt @@ -1,25 +1,55 @@ -add_library(Plugin_C3DModel_Viewers_Vulkan) +add_library(ModelViewer_Vulkan SHARED) -file(GLOB_RECURSE SRCS - ./*.cpp - ./*.h + file(GLOB_RECURSE GLTF_VULKAN_UTIL + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/gltf/vulkan/*.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/gltf/vulkan/*.h + ) + +file(GLOB_RECURSE VULKAN_UTIL + util/*.cpp + util/*.h ) -target_sources(Plugin_C3DModel_Viewers_Vulkan PRIVATE + file(GLOB_RECURSE MODEL_VIEWER + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/model_viewer/*.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/model_viewer/*.cpp + ) - ${SRCS} +target_sources(ModelViewer_Vulkan PRIVATE + ${VULKAN_UTIL} + ${GLTF_VULKAN_UTIL} + ${MODEL_VIEWER} + vulkan_device.cpp + vulkan_device.h + vulkan_main.cpp + vulkan_main.h + vulkan_renderer.cpp + vulkan_renderer.h ) -target_include_directories(Plugin_C3DModel_Viewers_Vulkan PUBLIC +target_include_directories(ModelViewer_Vulkan PUBLIC ./ ./util + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib + ${PROJECT_SOURCE_DIR}/cmp_framework/common/ + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half + ${PROJECT_SOURCE_DIR}/applications/_plugins/common + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/gltf + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/gltf/vulkan + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/model_viewer + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_meshoptimizer + ${PROJECT_SOURCE_DIR}/applications/_libs/gpu_decode + ${PROJECT_SOURCE_DIR}/applications/_plugins/c3dmodel_viewers/vulkan/util + ${PROJECT_SOURCE_DIR}/applications/_plugins/c3dmodel_viewers/gltf_dx12_ex/dx12util + ${PROJECT_SOURCE_DIR}/../common/lib/ext/glm + ${Vulkan_INCLUDE_DIRS} ) -target_link_libraries(Plugin_C3DModel_Viewers_Vulkan +target_link_libraries(ModelViewer_Vulkan PRIVATE ExtVulkan @@ -27,15 +57,18 @@ target_link_libraries(Plugin_C3DModel_Viewers_Vulkan ExtGlslang ExtQt5Widgets - CompressonatorLIB - - GPU_Decode - Plugin_CmdLine - Plugin_Common_Imgui - Plugin_Common_Misc - Plugin_GLTF_Vulkan - Plugin_Model_Viewer - Plugin_UserInterface + CMP_Compressonator + CMP_Framework + CMP_GpuDecode + CMP_GUI_Gltf + CMP_Imgui + CMP_Common # CModel Data ) -set_target_properties(Plugin_C3DModel_Viewers_Vulkan PROPERTIES FOLDER ${FOLDER_NAME}) +target_compile_definitions(ModelViewer_Vulkan PRIVATE BUILD_AS_PLUGIN_DLL=1) + +set_target_properties(ModelViewer_Vulkan PROPERTIES + FOLDER "Plugin_Dynamic/3DModel_Viewers" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin/debug/plugins" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin/release/plugins" + ) diff --git a/applications/_plugins/c3dmodel_viewers/vulkan/vulkan_device.cpp b/applications/_plugins/c3dmodel_viewers/vulkan/vulkan_device.cpp index c9c066d24..443ca7647 100644 --- a/applications/_plugins/c3dmodel_viewers/vulkan/vulkan_device.cpp +++ b/applications/_plugins/c3dmodel_viewers/vulkan/vulkan_device.cpp @@ -24,6 +24,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#define CMP_PI 3.1428 + #include "vulkan_device.h" #include "gltfcommon.h" #include "ImguiVK.h" @@ -156,7 +158,7 @@ int Vulkan_Device::OnCreate(void *hWnd) { m_state.toneMapper = 0; m_state.glow = 0.1f; m_state.bDrawBoundingBoxes = false; - m_state.light.SetFov(glm::pi() / 2, 1024, 1024); + m_state.light.SetFov(CMP_PI / 2, 1024, 1024); //m_state.light.UpdateCamera(0.0f, 1.7f, 3.0f); m_state.light.UpdateCamera(3.67f + 3.14159f, 0.58f, 3.0f); #endif @@ -275,7 +277,7 @@ void Vulkan_Device::OnResize(uint32_t width, uint32_t height) { m_Width = width; m_Height = height; #ifdef ENABLE_RENDER_CODE - m_state.camera.SetFov(glm::pi() / 4, m_Width, m_Height); + m_state.camera.SetFov(CMP_PI / 4, m_Width, m_Height); #endif } diff --git a/applications/_plugins/canalysis/analysis/canalysis.cpp b/applications/_plugins/canalysis/analysis/canalysis.cpp index a303e9ccb..28338699a 100644 --- a/applications/_plugins/canalysis/analysis/canalysis.cpp +++ b/applications/_plugins/canalysis/analysis/canalysis.cpp @@ -61,8 +61,14 @@ #include #include #include -#include #include +#ifdef _CMP_CPP17_ // Build code using std::c++17 +#include +namespace sfs = std::filesystem; +#else +#include +namespace sfs = std::experimental::filesystem; +#endif using namespace std; @@ -167,11 +173,11 @@ void Plugin_Canalysis::write(const REPORT_DATA& data, char *resultsFile, char op bool nodeExist = false; if (m_srcFile.size() > 0 && m_destFile.size() > 0) { - std::filesystem::path src(m_srcFile); + sfs::path src(m_srcFile); diffName = src.stem().generic_string(); diffName.append("_"); - std::filesystem::path dest(m_destFile); + sfs::path dest(m_destFile); diffName.append(dest.stem().generic_string()); diffNodeName = diffName; } @@ -206,7 +212,7 @@ void Plugin_Canalysis::write(const REPORT_DATA& data, char *resultsFile, char op modifyElement(child, "SSIM_RED", f2Str(data.SSIM_Red, 4)); nodeExist = true; } else if (option == 'p') { //psnr - modifyElement(child, "MSE", f2Str(data.MSE, 1).c_str()); + modifyElement(child, "MSE", f2Str(data.MSE, 4).c_str()); modifyElement(child, "PSNR", f2Str(data.PSNR, 1).c_str()); modifyElement(child, "PSNR_BLUE", f2Str(data.PSNR_Blue, 1).c_str()); modifyElement(child, "PSNR_GREEN", f2Str(data.PSNR_Green, 1).c_str()); @@ -331,7 +337,7 @@ void Plugin_Canalysis::write(const REPORT_DATA& data, char *resultsFile, char op return; } - std::filesystem::path result(resultsFile); + sfs::path result(resultsFile); int lastindex = result.generic_string().find_last_of("/"); std::string goldFile = result.generic_string().substr(0, lastindex + 1); goldFile.append("golden.xml"); @@ -558,6 +564,7 @@ void Plugin_Canalysis::generateBCtestResult(QImage *src, QImage *dest, REPORT_D } #ifdef USE_OPENCV +/* bool Plugin_Canalysis::psnr(QImage *src, const cv::Mat& srcimg, QImage *dest, const cv::Mat& destimg, REPORT_DATA &myReport, CMP_Feedback_Proc pFeedbackProc) { double bMSE = 0, gMSE = 0, rMSE = 0, MSE = 0; double MAX = 255.0; // Maximum possible pixel range. For our BMP's, which have 8 bits, it's 255. @@ -644,6 +651,7 @@ bool Plugin_Canalysis::psnr(QImage *src, const cv::Mat& srcimg, QImage *dest, co return (myReport.PSNR != -1); } +*/ #endif void Plugin_Canalysis::setActiveChannels() { @@ -723,7 +731,10 @@ int Plugin_Canalysis::TC_ImageDiff(const char * in1, } if (m_MipSrcImages != NULL && m_MipDestImages != NULL) { - setActiveChannels(); + + // analyize the destination image type and set active channels to compare the source with + setActiveChannels(); + // Analysis is only on top MipLevel and first cubemap face! // Need to update the code to handle all faces of cubemaps if (m_MipSrcImages->QImage_list[0].size() >0) { @@ -744,7 +755,43 @@ int Plugin_Canalysis::TC_ImageDiff(const char * in1, return -1; } + if ((m_MipSrcImages->mipset == NULL) || (m_MipDestImages->mipset == NULL)) + { + printf("Error: unabled to read mipset data"); + return -1; + } + // Calculate MSE & PSNR + CMP_AnalysisData pAnalysisData = { 0 }; + pAnalysisData.channelBitMap = m_RGBAChannels; + if (CMP_MipSetAnlaysis(m_MipSrcImages->mipset, m_MipDestImages->decompressedMipSet, 0, 0, &pAnalysisData) != CMP_OK) + { + printf("Error: unabled to calculate MSE and PSNR"); + return -1; + } + + if (analysisData) + { + analysisData->PSNR = report.data.PSNR = pAnalysisData.psnr; + analysisData->PSNR_Red = report.data.PSNR_Red = pAnalysisData.psnrR; + analysisData->PSNR_Green = report.data.PSNR_Green = pAnalysisData.psnrG; + analysisData->PSNR_Blue = report.data.PSNR_Blue = pAnalysisData.psnrB; + analysisData->MSE = report.data.MSE = pAnalysisData.mse; + } + + // Test images + if (srcImage->width() == 4 && srcImage->height() == 4) + { + generateBCtestResult(srcImage, destImage, report.data); + bool testpassed = report.data.PSNR > 0; + if (!testpassed) + { + printf("Error: Images analysis fail\n"); + return -1; + } + } + + // Do SSIM using OpenCV if (srcImage != NULL && destImage != NULL) { m_srcFile = in1; m_destFile = in2; @@ -757,6 +804,7 @@ int Plugin_Canalysis::TC_ImageDiff(const char * in1, return -1; } + if (cmipImages == NULL) { //cmdline enable both ssim and psnr #ifdef USE_OPENCV @@ -767,12 +815,6 @@ int Plugin_Canalysis::TC_ImageDiff(const char * in1, return -1; } - bool testpassed = psnr(srcImage, srcimg, destImage, destimg, report.data); - if (!testpassed) { - printf("Error: Images analysis fail\n"); - return -1; - } - m_SSIM = getSSIM(srcimg, destimg, pFeedbackProc); processSSIMResults(); @@ -790,12 +832,6 @@ int Plugin_Canalysis::TC_ImageDiff(const char * in1, analysisData->SSIM_Red = report.data.SSIM_Red; analysisData->SSIM_Green = report.data.SSIM_Green; analysisData->SSIM_Blue = report.data.SSIM_Blue; - analysisData->PSNR = report.data.PSNR; - analysisData->PSNR_Red = report.data.PSNR_Red; - analysisData->PSNR_Green = report.data.PSNR_Green; - analysisData->PSNR_Blue = report.data.PSNR_Blue; - analysisData->MSE = report.data.MSE; - } } // @@ -885,7 +921,7 @@ int Plugin_Canalysis::TC_ImageDiff(const char * in1, } else { QString appLocalPath = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation); QString redirectOut = appLocalPath + "/diff.bmp"; - redirectOut.replace("CompressonatorCLI", "Compressonator"); + redirectOut.replace("compressonatorcli", "compressonator"); saved = diffImage->save(redirectOut); if (saved) { if (m_MipDiffImages) @@ -911,7 +947,7 @@ int Plugin_Canalysis::TC_ImageDiff(const char * in1, } else { QString appLocalPath = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation); QString redirectOut = appLocalPath + "/diff.bmp"; - redirectOut.replace("CompressonatorCLI", "Compressonator"); + redirectOut.replace("compressonatorcli", "compressonator"); saved = diffImage->save(redirectOut); delete diffImage; if (saved) { @@ -978,24 +1014,56 @@ int Plugin_Canalysis::TC_PSNR_MSE(const char * in1, const char * in2, char *res return -1; } -#ifdef USE_OPENCV - cv::Mat srcimg = QtOcv::image2Mat(*srcImage); - cv::Mat destimg = QtOcv::image2Mat(*destImage); - if (!&srcimg || !&destimg) { - printf("Error: Images fail to allocate for ssim analysis\n"); - return -1; - } - bool testpassed = psnr(srcImage, srcimg, destImage, destimg, report.data); - if (!testpassed) { + report.data.PSNR_Blue = -1; + report.data.PSNR_Green = -1; + report.data.PSNR_Red = -1; + + CMP_AnalysisData pAnalysisData = {0}; + pAnalysisData.channelBitMap = m_RGBAChannels; + if (CMP_MipSetAnlaysis(m_MipSrcImages->mipset, m_MipDestImages->decompressedMipSet, 0, 0, &pAnalysisData) != CMP_OK) + { + printf("Error: unabled to calculate MSE and PSNR"); + return -1; + } + + + report.data.PSNR = pAnalysisData.psnr; + report.data.PSNR_Red = pAnalysisData.psnrR; + report.data.PSNR_Green = pAnalysisData.psnrG; + report.data.PSNR_Blue = pAnalysisData.psnrB; + report.data.MSE = pAnalysisData.mse; + + // cv::Mat srcimg = QtOcv::image2Mat(*srcImage); + // cv::Mat destimg = QtOcv::image2Mat(*destImage); + // if (!&srcimg || !&destimg) { + // printf("Error: Images fail to allocate for ssim analysis\n"); + // return -1; + // } + // + // bool testpassed = psnr(srcImage, srcimg, destImage, destimg, report.data); + // if (!testpassed) { + // printf("Error: Images analysis fail\n"); + // return -1; + // } + + // Test images + if (srcImage->width() == 4 && srcImage->height() == 4) + { + generateBCtestResult(srcImage, destImage, report.data); + bool testpassed = report.data.PSNR > 0; + if (!testpassed) + { printf("Error: Images analysis fail\n"); return -1; } + } - write(report.data, resultsFile,'p'); -#endif - //cout << report; - } else { + + write(report.data, resultsFile,'p'); + + } + else { printf("Error: Image(s) cannot be loaded\n"); return -1; } diff --git a/applications/_plugins/canalysis/analysis/canalysis.h b/applications/_plugins/canalysis/analysis/canalysis.h index 1379c108b..11bc0b234 100644 --- a/applications/_plugins/canalysis/analysis/canalysis.h +++ b/applications/_plugins/canalysis/analysis/canalysis.h @@ -72,7 +72,8 @@ class Plugin_Canalysis : public PluginInterface_Analysis { #ifdef USE_OPENCV cv::Scalar m_SSIM; - bool psnr(QImage *src, const cv::Mat& srcimg, QImage *dest, const cv::Mat& destimg, REPORT_DATA &myReport, CMP_Feedback_Proc pFeedbackProc = NULL); + // removed as of v4.1 + // bool psnr(QImage *src, const cv::Mat& srcimg, QImage *dest, const cv::Mat& destimg, REPORT_DATA &myReport, CMP_Feedback_Proc pFeedbackProc = NULL); #endif char m_results_path[MAX_PATH]; std::string m_srcFile; diff --git a/applications/_plugins/canalysis/cmakelists.txt b/applications/_plugins/canalysis/cmakelists.txt index 273096672..a4796db06 100644 --- a/applications/_plugins/canalysis/cmakelists.txt +++ b/applications/_plugins/canalysis/cmakelists.txt @@ -1,45 +1,88 @@ +cmake_minimum_required(VERSION 3.10) +add_library(Image_Analysis STATIC "") + +target_sources(Image_Analysis + PRIVATE + analysis/canalysis.cpp + analysis/canalysis.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/atiformats.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/pluginmanager.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/pluginmanager.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/textureio.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/textureio.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/ssim.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/ssim.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpimageloader.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpimageloader.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/common/cvmatandqimage.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/common/cvmatandqimage.cpp + ) + +target_include_directories(Image_Analysis + PRIVATE + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half + ${PROJECT_SOURCE_DIR}/applications/_libs/gpu_decode + ${PROJECT_SOURCE_DIR}/applications/_plugins/common + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/common + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components + ${PROJECT_SOURCE_DIR}/../common/lib/ext/rapidxml + + ${OpenEXR_INCLUDE_DIRS} + ${OpenCV_INCLUDE_DIRS} + ${Qt5Gui_INCLUDE_DIRS} + ) + +if (CMP_HOST_WINDOWS) + # update this and enable for plugin builds + # target_link_libraries(Image_Analysis + # PRIVATE + # CMP_Compressonator + # Plugin_TCPluginAPI + # Plugin_PluginManager + # Plugin_CImage_EXR + # ExtOpenCV + # ExtOpenEXR + # ExtRapidXML + # + # PUBLIC + # Plugin_Common_TestReport + # ) + +else() + + find_package(OpenCV) + if (OpenCV_FOUND) + target_include_directories(Image_Analysis + PRIVATE + ${OpenCV_INCLUDE_DIRS}) + else() + message(FATAL_ERROR "Package OpenCV are required, but not found. In Unix, run initsetup_unix.sh or sudo apt-get install libopencv-dev to install the libs.") + endif() + + # Qt5 include path - users install required + set_property(TARGET Image_Analysis PROPERTY POSITION_INDEPENDENT_CODE TRUE) + + find_package(Qt5Gui) + if(Qt5Gui_FOUND) + target_include_directories(Image_Analysis + PRIVATE + "${Qt5Gui_INCLUDE_DIRS}") + else() + message(FATAL_ERROR "Package Qt5 (Qt5Gui) are required, but not found. In Unix, run initsetup_unix.sh or sudo apt-get install qtdeclarative5-dev. If is window, please make sure install qt and add the bin path (i.e. C:\Qt\5.7\msvc2015_64\bin) to environment PATH.") + endif() + + if(APPLE) + target_include_directories(Image_Analysis + PRIVATE + /usr/local/include/openexr/) + endif() + + if (UNIX) + target_compile_definitions(Image_Analysis PRIVATE _LINUX) + endif() +endif() + +set_target_properties(Image_Analysis PROPERTIES FOLDER "Plugin_Static/Analysis") -add_library(Plugin_CAnalysis) - - -target_sources(Plugin_CAnalysis PRIVATE - analysis/canalysis.cpp - analysis/canalysis.h - ${PROJECT_SOURCE_DIR}/applications/_plugins/common/ssim.h - ${PROJECT_SOURCE_DIR}/applications/_plugins/common/ssim.cpp - ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpimageloader.h - ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpimageloader.cpp - ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/common/cvmatandqimage.h - ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/common/cvmatandqimage.cpp -) - -target_include_directories(Plugin_CAnalysis PUBLIC - ./analysis - ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib - ${PROJECT_SOURCE_DIR}/cmp_framework/common - ${PROJECT_SOURCE_DIR}/cmp_framework/common/half - ${PROJECT_SOURCE_DIR}/applications/_plugins/common - ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/common - ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components - ${PROJECT_SOURCE_DIR}/applications/_libs/gpu_decode - ${Qt5Gui_INCLUDE_DIRS} - ${OpenEXR_INCLUDE_DIRS} -) - -target_link_libraries(Plugin_CAnalysis - - PRIVATE - CompressonatorLIB - Plugin_TCPluginAPI - Plugin_PluginManager - Plugin_CImage_EXR - ExtOpenCV - ExtOpenEXR - ExtRapidXML - - PUBLIC - Plugin_Common_TestReport -) - -set_target_properties(Plugin_CAnalysis PROPERTIES FOLDER ${FOLDER_NAME}) diff --git a/applications/_plugins/ccmp_sdk/bc4/bc4.cpp b/applications/_plugins/ccmp_sdk/bc4/bc4.cpp index 8cfda4a70..c1105ba75 100644 --- a/applications/_plugins/ccmp_sdk/bc4/bc4.cpp +++ b/applications/_plugins/ccmp_sdk/bc4/bc4.cpp @@ -96,9 +96,11 @@ int Plugin_BC4::TC_Init(void *kernel_options) { memset(&g_BC4Encode, 0, sizeof(CMP_BC15Options)); SetDefaultBC15Options(&g_BC4Encode); - g_BC4Encode.m_src_width = m_KernelOptions->width; - g_BC4Encode.m_src_height = m_KernelOptions->height; - g_BC4Encode.m_fquality = m_KernelOptions->fquality; + g_BC4Encode.m_src_width = m_KernelOptions->width; + g_BC4Encode.m_src_height = m_KernelOptions->height; + g_BC4Encode.m_fquality = m_KernelOptions->fquality; + g_BC4Encode.m_bIsSNORM = (m_KernelOptions->format == CMP_FORMAT_BC4_S); + g_BC4Encode.m_sintsrc = (m_KernelOptions->srcformat == CMP_FORMAT_RGBA_8888_S); m_KernelOptions->data = &g_BC4Encode; m_KernelOptions->size = sizeof(g_BC4Encode); diff --git a/applications/_plugins/ccmp_sdk/bc5/bc5.cpp b/applications/_plugins/ccmp_sdk/bc5/bc5.cpp index eebcd5e06..100273b08 100644 --- a/applications/_plugins/ccmp_sdk/bc5/bc5.cpp +++ b/applications/_plugins/ccmp_sdk/bc5/bc5.cpp @@ -106,6 +106,8 @@ int Plugin_BC5::TC_Init(void *kernel_options) { g_BC5Encode.m_src_width = m_KernelOptions->width; g_BC5Encode.m_src_height = m_KernelOptions->height; g_BC5Encode.m_fquality = m_KernelOptions->fquality; + g_BC5Encode.m_bIsSNORM = (m_KernelOptions->format == CMP_FORMAT_BC5_S); + g_BC5Encode.m_sintsrc = (m_KernelOptions->srcformat == CMP_FORMAT_RGBA_8888_S); m_KernelOptions->data = &g_BC5Encode; m_KernelOptions->size = sizeof(g_BC5Encode); diff --git a/applications/_plugins/cfilter/boxfilter.cpp b/applications/_plugins/cfilter/boxfilter.cpp index a3c748104..62ff1a6ed 100644 --- a/applications/_plugins/cfilter/boxfilter.cpp +++ b/applications/_plugins/cfilter/boxfilter.cpp @@ -21,13 +21,37 @@ // THE SOFTWARE. // //============================================================================== +//-------------------------------------------------------------------------------------- +// The MIT License (MIT) +// +// Copyright (c) 2004-2020 Microsoft Corp +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, +// merge, publish, distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be included in all copies +// or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +//-------------------------------------------------------------------------------------- #include "boxfilter.h" -#define BUILD_AS_PLUGIN_DLL - // Windows Header Files: #ifdef _WIN32 #include +#include #endif #include @@ -37,9 +61,17 @@ #include #include + #ifndef _WIN32 #include "textureio.h" #endif + +#ifdef _WIN32 +#ifndef USE_DIRECTX9 +#pragma comment(lib, "d3d11.lib") +#endif +#endif + #include #ifdef BUILD_AS_PLUGIN_DLL @@ -47,22 +79,31 @@ DECLARE_PLUGIN(Plugin_BoxFilter) SET_PLUGIN_TYPE("FILTERS") SET_PLUGIN_NAME("MIPMAP") #else -void* make_Plugin_BoxFilter() { +void* make_Plugin_BoxFilter() +{ return new Plugin_BoxFilter; } #endif +#ifdef USE_DIRECTX9 CD3DXModule g_D3DX; CD3D9Module g_D3D9; -CMIPS *CFilterMips = NULL; +#endif + +CMIPS* CFilterMips = NULL; -void CMP_SetMipLevelGammaLinearB(MipLevel* pCurMipLevel, CMP_BYTE* pdata, CMP_FLOAT Gamma, CMP_INT numchannels) { - for (int y = 0; y < pCurMipLevel->m_nHeight; y++) { - for (int x = 0; x < pCurMipLevel->m_nWidth; x++) { +void CMP_SetMipLevelGammaLinearB(MipLevel* pCurMipLevel, CMP_BYTE* pdata, CMP_FLOAT Gamma, CMP_INT numchannels) +{ + for (int y = 0; y < pCurMipLevel->m_nHeight; y++) + { + for (int x = 0; x < pCurMipLevel->m_nWidth; x++) + { // calc Gamma for the all color channels - for (int i = 0; i < 3 && i < numchannels; i++) { + for (int i = 0; i < 3 && i < numchannels; i++) + { CMP_FLOAT normpixel = 0.0f; - if (*pdata > 0) { + if (*pdata > 0) + { normpixel = *pdata; normpixel = normpixel / 255.0f; normpixel = powf(normpixel, Gamma) * 255.0f; @@ -73,33 +114,99 @@ void CMP_SetMipLevelGammaLinearB(MipLevel* pCurMipLevel, CMP_BYTE* pdata, CMP_FL else if (normpixel < 0) normpixel = 0; - *pdata = (CMP_BYTE) round(normpixel); + *pdata = (CMP_BYTE)round(normpixel); } pdata++; } // if alpha skip it if (numchannels > 3) - *pdata++; + pdata++; + } + } +} + + +void CMP_SetMipLevelGammaLinearSB(MipLevel* pCurMipLevel, CMP_SBYTE* pdata, CMP_FLOAT Gamma, CMP_INT numchannels) +{ + for (int y = 0; y < pCurMipLevel->m_nHeight; y++) + { + for (int x = 0; x < pCurMipLevel->m_nWidth; x++) + { + // calc Gamma for the all color channels + for (int i = 0; i < 3 && i < numchannels; i++) + { + CMP_FLOAT normpixel = 0.0f; + if (*pdata > 0) + { + normpixel = *pdata; + normpixel = normpixel / 127.0f; + normpixel = powf(normpixel, Gamma) * 127.0f; + + // need to check for signed components + if (normpixel > 127) + normpixel = 127; + else if (normpixel < -127) + normpixel = -127; + + *pdata = (CMP_SBYTE)round(normpixel); + } + pdata++; + } + // if alpha skip it + if (numchannels > 3) + pdata++; } } } template -void CMP_SetMipLevelGammaf(MipLevel* pCurMipLevel, T* pdata, CMP_FLOAT Gamma, CMP_INT numchannels) { - for (int y = 0; y < pCurMipLevel->m_nHeight; y++) { - for (int x = 0; x < pCurMipLevel->m_nWidth; x++) { +void CMP_SetMipLevelGammaf(MipLevel* pCurMipLevel, T* pdata, CMP_FLOAT Gamma, CMP_INT numchannels) +{ + for (int y = 0; y < pCurMipLevel->m_nHeight; y++) + { + for (int x = 0; x < pCurMipLevel->m_nWidth; x++) + { // calc Gamma for the all color channels for (int i = 0; i < 3 && i < numchannels; i++) *pdata++ = pow(*pdata, Gamma); // if alpha skip it if (numchannels > 3) - *pdata++; + pdata++; } } } +static CMP_FLOAT F16toF32(CMP_HALFSHORT f) +{ + CMP_HALF A; + A.setBits(f); + return ((CMP_FLOAT)A); +} + +void CMP_SetMipLevelGammaHalfShort(MipLevel* pCurMipLevel, CMP_HALFSHORT* pdata, CMP_FLOAT Gamma, CMP_INT numchannels) +{ + for (int y = 0; y < pCurMipLevel->m_nHeight; y++) + { + for (int x = 0; x < pCurMipLevel->m_nWidth; x++) + { + // calc Gamma for the all color channels + for (int i = 0; i < 3 && i < numchannels; i++) + { + CMP_FLOAT pixf = F16toF32(*pdata); // convert short int to float + pixf = pow(pixf, Gamma); // Do gamma using float + CMP_HALF pixh = pixf; // back to half type + *pdata = pixh.bits(); // back to short int bits + pdata++; // move on to next pixel + } + // if alpha skip it + if (numchannels > 3) + pdata++; + } + } +} -void CMP_SetMipSetGamma(MipSet* pMipSet, CMP_FLOAT Gamma) { +void CMP_SetMipSetGamma(MipSet* pMipSet, CMP_FLOAT Gamma) +{ CMIPS CMips; MipLevel* pCurMipLevel; CMP_INT maxFaceOrSlice; @@ -107,50 +214,63 @@ void CMP_SetMipSetGamma(MipSet* pMipSet, CMP_FLOAT Gamma) { maxFaceOrSlice = 6; else maxFaceOrSlice = 1; - for (CMP_INT nCurMipLevel = 0; nCurMipLevel < pMipSet->m_nMipLevels; nCurMipLevel++) { - for (CMP_INT nFaceOrSlice = 0; nFaceOrSlice < maxFaceOrSlice; nFaceOrSlice++) { + for (CMP_INT nCurMipLevel = 0; nCurMipLevel < pMipSet->m_nMipLevels; nCurMipLevel++) + { + for (CMP_INT nFaceOrSlice = 0; nFaceOrSlice < maxFaceOrSlice; nFaceOrSlice++) + { pCurMipLevel = CMips.GetMipLevel(pMipSet, nCurMipLevel, nFaceOrSlice); if (pMipSet->m_ChannelFormat == CF_8bit) - CMP_SetMipLevelGammaLinearB(pCurMipLevel, pCurMipLevel->m_pbData, Gamma, 4); - else if (pMipSet->m_ChannelFormat == CF_Float16) - CMP_SetMipLevelGammaf(pCurMipLevel, pCurMipLevel->m_phfsData, Gamma, 4); - else if (pMipSet->m_ChannelFormat == CF_Float32) - CMP_SetMipLevelGammaf(pCurMipLevel, pCurMipLevel->m_pfData, Gamma, 4); + { + if (pMipSet->m_format == CMP_FORMAT_RGBA_8888_S) + CMP_SetMipLevelGammaLinearSB(pCurMipLevel, pCurMipLevel->m_psbData, Gamma, 4); + else + CMP_SetMipLevelGammaLinearB(pCurMipLevel, pCurMipLevel->m_pbData, Gamma, 4); + } + else if (pMipSet->m_ChannelFormat == CF_Float16) CMP_SetMipLevelGammaHalfShort(pCurMipLevel, pCurMipLevel->m_phfsData, Gamma, 4); + else if (pMipSet->m_ChannelFormat == CF_Float32) CMP_SetMipLevelGammaf(pCurMipLevel, pCurMipLevel->m_pfData, Gamma, 4); } } } - - - template -void GenerateMipLevelF(MipLevel* pCurMipLevel, MipLevel* pPrevMipLevelOne, MipLevel* pPrevMipLevelTwo, T* curMipData, T* prevMip1Data, T* prevMip2Data) { +void GenerateMipLevelF(MipLevel* pCurMipLevel, MipLevel* pPrevMipLevelOne, MipLevel* pPrevMipLevelTwo, T* curMipData, T* prevMip1Data, T* prevMip2Data) +{ assert(pCurMipLevel); assert(pPrevMipLevelOne); - if (pCurMipLevel && pPrevMipLevelOne) { - if (!pPrevMipLevelTwo) { + if (pCurMipLevel && pPrevMipLevelOne) + { + if (!pPrevMipLevelTwo) + { bool bDiffHeights = pCurMipLevel->m_nHeight != pPrevMipLevelOne->m_nHeight; bool bDiffWidths = pCurMipLevel->m_nWidth != pPrevMipLevelOne->m_nWidth; assert(bDiffHeights || bDiffWidths); T* pDst = curMipData; - for (int y = 0; y < pCurMipLevel->m_nHeight; y++) { + for (int y = 0; y < pCurMipLevel->m_nHeight; y++) + { T* pSrc = prevMip1Data + (2 * y * pPrevMipLevelOne->m_nWidth * 4); T* pSrc2; - if (bDiffHeights) { + if (bDiffHeights) + { pSrc2 = pSrc + (pPrevMipLevelOne->m_nWidth * 4); - } else { + } + else + { //if no change in height, then use same line as source pSrc2 = pSrc; } - for (int x = 0; x < pCurMipLevel->m_nWidth; x++, pSrc += 8, pSrc2 += 8) { + for (int x = 0; x < pCurMipLevel->m_nWidth; x++, pSrc += 8, pSrc2 += 8) + { T c1[4], c2[4], c3[4], c4[4]; memcpy(c1, pSrc, sizeof(c1)); memcpy(c3, pSrc2, sizeof(c3)); - if (bDiffWidths) { + if (bDiffWidths) + { memcpy(c2, pSrc + 4, sizeof(c2)); memcpy(c4, pSrc2 + 4, sizeof(c4)); - } else { + } + else + { memcpy(c2, pSrc, sizeof(c2)); memcpy(c4, pSrc2, sizeof(c4)); } @@ -158,37 +278,47 @@ void GenerateMipLevelF(MipLevel* pCurMipLevel, MipLevel* pPrevMipLevelOne, MipLe *pDst++ = (c1[i] + c2[i] + c3[i] + c4[i]) / T(4.f); } } - } else { + } + else + { //working with volume texture, avg both slices together as well as 4 corners bool bDiffHeights = pCurMipLevel->m_nHeight != pPrevMipLevelOne->m_nHeight; bool bDiffWidths = pCurMipLevel->m_nWidth != pPrevMipLevelOne->m_nWidth; //don't need to check that either height or width is diff, b/c slices are diff T* pDst = curMipData; - for (int y = 0; y < pCurMipLevel->m_nHeight; y++) { + for (int y = 0; y < pCurMipLevel->m_nHeight; y++) + { T *pSrc, *pSrc2, *pOtherSrc, *pOtherSrc2; pSrc = prevMip1Data + (2 * y * pPrevMipLevelOne->m_nWidth * 4); pOtherSrc = prevMip2Data + (2 * y * pPrevMipLevelTwo->m_nWidth * 4); - if (bDiffHeights) { + if (bDiffHeights) + { //point to next line, same column pSrc2 = pSrc + (pPrevMipLevelOne->m_nWidth * 4); pOtherSrc2 = pOtherSrc + (pPrevMipLevelTwo->m_nWidth * 4); - } else { + } + else + { //if no change in height, then use same line as source pSrc2 = pSrc; pOtherSrc2 = pOtherSrc; } - for (int x = 0; x < pCurMipLevel->m_nWidth; x++, pSrc += 8, pSrc2 += 8, pOtherSrc += 8, pOtherSrc2 += 8) { + for (int x = 0; x < pCurMipLevel->m_nWidth; x++, pSrc += 8, pSrc2 += 8, pOtherSrc += 8, pOtherSrc2 += 8) + { T c1[4], c2[4], c3[4], c4[4], c5[4], c6[4], c7[4], c8[4]; memcpy(c1, pSrc, sizeof(c1)); memcpy(c3, pSrc2, sizeof(c3)); memcpy(c5, pOtherSrc, sizeof(c5)); memcpy(c7, pOtherSrc2, sizeof(c7)); - if (bDiffWidths) { + if (bDiffWidths) + { memcpy(c2, pSrc + 4, sizeof(c2)); memcpy(c4, pSrc2 + 4, sizeof(c4)); memcpy(c6, pOtherSrc + 4, sizeof(c6)); memcpy(c8, pOtherSrc2 + 4, sizeof(c8)); - } else { + } + else + { memcpy(c2, pSrc, sizeof(c2)); memcpy(c4, pSrc2, sizeof(c4)); memcpy(c6, pOtherSrc, sizeof(c6)); @@ -202,47 +332,74 @@ void GenerateMipLevelF(MipLevel* pCurMipLevel, MipLevel* pPrevMipLevelOne, MipLe } } +Plugin_BoxFilter::Plugin_BoxFilter() +{ +#ifdef _WIN32 +#ifndef USE_DIRECTX9 + m_pTexture2DSourceTexture = nullptr; + m_pDevice = nullptr; + m_pContext = nullptr; +#endif +#endif +} -Plugin_BoxFilter::Plugin_BoxFilter() {} +Plugin_BoxFilter::~Plugin_BoxFilter() +{ +#ifdef _WIN32 +#ifndef USE_DIRECTX9 + // Clear any old states + if (m_pContext) + { + m_pContext->ClearState(); + m_pContext->Flush(); + } -Plugin_BoxFilter::~Plugin_BoxFilter() { + CleanupDX(); +#endif +#endif CFilterMips = NULL; } // Not used return error! -int Plugin_BoxFilter::TC_PluginSetSharedIO(void* SharedCMips) { - // check if already initialized - if (CFilterMips != NULL) - return CMP_OK; - - if (SharedCMips && CFilterMips == NULL) { - CFilterMips = reinterpret_cast(SharedCMips); +int Plugin_BoxFilter::TC_PluginSetSharedIO(void* SharedCMips) +{ + if (SharedCMips && CFilterMips == NULL) + { + CFilterMips = reinterpret_cast(SharedCMips); + PrintStatusLine = CFilterMips->PrintLine; #ifdef _WIN32 +#ifdef USE_DIRECTX9 std::string strD3DX = _T(""); - if (LoadD3DX(g_D3DX, strD3DX) == LOAD_FAILED) { - Error(_T("D3DXFilter Plugin"), EL_Error, CMP_ERR_UNABLE_TO_LOAD_FILE); + if (LoadD3DX(g_D3DX, strD3DX) == LOAD_FAILED) + { + Error("D3DXFilter Plugin", CMP_ERR_UNABLE_TO_LOAD_FILE, "Load D3DX failed"); return CMP_ERR_UNABLE_TO_LOAD_FILE; } // Ensure DX9 is present & can be loaded g_D3D9.LoadModule(); IDirect3D9* pD3D = g_D3D9.Direct3DCreate9(D3D_SDK_VERSION); - if (pD3D == NULL) { - Error(_T("D3DXFilter Plugin"), EL_Error, CMP_ERR_UNABLE_TO_LOAD_FILE); + if (pD3D == NULL) + { + Error("D3DXFilter Plugin", CMP_ERR_UNABLE_TO_LOAD_FILE, "D3DX9 not found"); return CMP_ERR_UNABLE_TO_LOAD_FILE; - } else + } + else pD3D->Release(); +#endif #endif return CMP_OK; - } else + } + else CFilterMips = NULL; return CMP_ERR_GENERIC; } -int Plugin_BoxFilter::TC_PluginGetVersion(TC_PluginVersion* pPluginVersion) { +int Plugin_BoxFilter::TC_PluginGetVersion(TC_PluginVersion* pPluginVersion) +{ //MessageBox(0,"TC_PluginGetVersion","Plugin_WIC",MB_OK); #ifdef _WIN32 pPluginVersion->guid = g_GUID; @@ -267,59 +424,92 @@ int Plugin_BoxFilter::TC_CFilter(MipSet* pMipSet, CMP_MipSet* pMipSetDst, CMP_CF if (pCFilterParams->fGammaCorrection < 0) return CMP_ERR_GAMMA_OUTOFRANGE; - if (pCFilterParams->nFilterType == 0) { + if (pCFilterParams->nFilterType == 0) + { int nPrevMipLevels = pMipSet->m_nMipLevels; int nWidth = pMipSet->m_nWidth; int nHeight = pMipSet->m_nHeight; - while (nWidth > pCFilterParams->nMinSize && nHeight > pCFilterParams->nMinSize) { + while (nWidth > pCFilterParams->nMinSize && nHeight > pCFilterParams->nMinSize) + { nWidth = (std::max)(nWidth >> 1, 1); nHeight = (std::max)(nHeight >> 1, 1); int nCurMipLevel = pMipSet->m_nMipLevels; int maxFacesOrSlices = (std::max)((pMipSet->m_TextureType == TT_VolumeTexture) ? (CMP_MaxFacesOrSlices(pMipSet, nCurMipLevel - 1) >> 1) - : CMP_MaxFacesOrSlices(pMipSet, nCurMipLevel - 1),1); - for (int nFaceOrSlice = 0; nFaceOrSlice < maxFacesOrSlices; nFaceOrSlice++) { + : CMP_MaxFacesOrSlices(pMipSet, nCurMipLevel - 1), + 1); + for (int nFaceOrSlice = 0; nFaceOrSlice < maxFacesOrSlices; nFaceOrSlice++) + { MipLevel* pThisMipLevel = CFilterMips->GetMipLevel(pMipSet, nCurMipLevel, nFaceOrSlice); if (!pThisMipLevel) continue; assert(CFilterMips->GetMipLevel(pMipSet, nCurMipLevel - 1, nFaceOrSlice)->m_pbData); //prev miplevel ok - if (pThisMipLevel->m_pbData) { // Space for mip level already allocated ? - if (pThisMipLevel->m_nWidth != nWidth || pThisMipLevel->m_nHeight != nHeight) { + if (pThisMipLevel->m_pbData) + { // Space for mip level already allocated ? + if (pThisMipLevel->m_nWidth != nWidth || pThisMipLevel->m_nHeight != nHeight) + { // Wrong size - release & reallocate //CFilterMips->FreeMipLevelData(pThisMipLevel); - if (CFilterMips->AllocateMipLevelData(pThisMipLevel, nWidth, nHeight, pMipSet->m_ChannelFormat, pMipSet->m_TextureDataType) == NULL) { + if (CFilterMips->AllocateMipLevelData(pThisMipLevel, nWidth, nHeight, pMipSet->m_ChannelFormat, pMipSet->m_TextureDataType) == NULL) + { return PE_Unknown; } } - } else if (CFilterMips->AllocateMipLevelData(pThisMipLevel, nWidth, nHeight, pMipSet->m_ChannelFormat, pMipSet->m_TextureDataType) == NULL) { + } + else if (CFilterMips->AllocateMipLevelData(pThisMipLevel, nWidth, nHeight, pMipSet->m_ChannelFormat, pMipSet->m_TextureDataType) == NULL) + { return PE_Unknown; } assert(pThisMipLevel->m_pbData); - if (pMipSet->m_TextureType != TT_VolumeTexture) { + if (pMipSet->m_TextureType != TT_VolumeTexture) + { MipLevel* tempMipOne = CFilterMips->GetMipLevel(pMipSet, nCurMipLevel - 1, nFaceOrSlice); if (pMipSet->m_ChannelFormat == CF_8bit) - GenerateMipLevelF(pThisMipLevel, tempMipOne, NULL, pThisMipLevel->m_pbData, tempMipOne->m_pbData); + { + if (pMipSet->m_format == CMP_FORMAT_RGBA_8888_S) + { + GenerateMipLevelF(pThisMipLevel, tempMipOne, NULL, pThisMipLevel->m_psbData, tempMipOne->m_psbData); + } + else + GenerateMipLevelF(pThisMipLevel, tempMipOne, NULL, pThisMipLevel->m_pbData, tempMipOne->m_pbData); + } else if (pMipSet->m_ChannelFormat == CF_Float16) GenerateMipLevelF(pThisMipLevel, tempMipOne, NULL, pThisMipLevel->m_phfsData, tempMipOne->m_phfsData); else if (pMipSet->m_ChannelFormat == CF_Float32) GenerateMipLevelF(pThisMipLevel, tempMipOne, NULL, pThisMipLevel->m_pfData, tempMipOne->m_pfData); - } else { - if (CMP_MaxFacesOrSlices(pMipSet, nCurMipLevel - 1) > 1) { + } + else + { + if (CMP_MaxFacesOrSlices(pMipSet, nCurMipLevel - 1) > 1) + { MipLevel* tempMipOne = CFilterMips->GetMipLevel(pMipSet, nCurMipLevel - 1, nFaceOrSlice * 2); MipLevel* tempMipTwo = CFilterMips->GetMipLevel(pMipSet, nCurMipLevel - 1, nFaceOrSlice * 2 + 1); //prev miplevel had 2 or more slices, so avg together slices if (pMipSet->m_ChannelFormat == CF_8bit) - GenerateMipLevelF(pThisMipLevel, tempMipOne, tempMipTwo, pThisMipLevel->m_pbData, tempMipOne->m_pbData, tempMipTwo->m_pbData); + { + if (pMipSet->m_format == CMP_FORMAT_RGBA_8888_S) + GenerateMipLevelF( + pThisMipLevel, tempMipOne, tempMipTwo, pThisMipLevel->m_psbData, tempMipOne->m_psbData, tempMipTwo->m_psbData); + else + GenerateMipLevelF(pThisMipLevel, tempMipOne, tempMipTwo, pThisMipLevel->m_pbData, tempMipOne->m_pbData, tempMipTwo->m_pbData); + } else if (pMipSet->m_ChannelFormat == CF_Float16) GenerateMipLevelF(pThisMipLevel, tempMipOne, tempMipTwo, pThisMipLevel->m_phfsData, tempMipOne->m_phfsData, tempMipTwo->m_phfsData); else if (pMipSet->m_ChannelFormat == CF_Float32) GenerateMipLevelF(pThisMipLevel, tempMipOne, tempMipTwo, pThisMipLevel->m_pfData, tempMipOne->m_pfData, tempMipTwo->m_pfData); - } else { + } + else + { MipLevel* tempMipOne = CFilterMips->GetMipLevel(pMipSet, nCurMipLevel - 1, nFaceOrSlice); if (pMipSet->m_ChannelFormat == CF_8bit) - GenerateMipLevelF(pThisMipLevel, tempMipOne, NULL, pThisMipLevel->m_pbData, tempMipOne->m_pbData); + { + if (pMipSet->m_format == CMP_FORMAT_RGBA_8888_S) + GenerateMipLevelF(pThisMipLevel, tempMipOne, NULL, pThisMipLevel->m_psbData, tempMipOne->m_psbData); + else + GenerateMipLevelF(pThisMipLevel, tempMipOne, NULL, pThisMipLevel->m_pbData, tempMipOne->m_pbData); + } else if (pMipSet->m_ChannelFormat == CF_Float16) GenerateMipLevelF(pThisMipLevel, tempMipOne, NULL, pThisMipLevel->m_phfsData, tempMipOne->m_phfsData); else if (pMipSet->m_ChannelFormat == CF_Float32) @@ -335,7 +525,9 @@ int Plugin_BoxFilter::TC_CFilter(MipSet* pMipSet, CMP_MipSet* pMipSetDst, CMP_CF if (nWidth == 1 || nHeight == 1) break; } - } else { + } + else + { #ifdef _WIN32 result = GenMipLevelsUsingD3DXFilter(pMipSet, pCFilterParams); #else @@ -352,35 +544,163 @@ int Plugin_BoxFilter::TC_CFilter(MipSet* pMipSet, CMP_MipSet* pMipSetDst, CMP_CF //-------------------------------------------------------------------------------------------- // DirectX MipMap Filter //-------------------------------------------------------------------------------------------- -void Plugin_BoxFilter::Error(TCHAR* pszCaption, TC_ErrorLevel errorLevel, UINT nErrorString) { +void Plugin_BoxFilter::Error(CMP_CHAR* pszCaption, CMP_UINT nErrorNo, CMP_CHAR* errMsg) +{ // Add code to print message to caller + // printf("Error [%x]: %s %s\n", nErrorNo, pszCaption,errMsg); + if (CFilterMips) + { + CFilterMips->PrintError("Error [%x]: %s %s", nErrorNo, pszCaption, errMsg); + } } #ifdef _WIN32 -#define HANDLE_ERROR(err) \ - { \ - Error(_T("D3DXFilter Plugin"), EL_Error, err); \ - goto Cleanup; \ +#define HANDLE_ERROR(err, msg) \ + { \ + Error("D3DXFilter Plugin", err, msg); \ + goto Cleanup; \ } -LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ return DefWindowProc(hWnd, message, wParam, lParam); } -int Plugin_BoxFilter::GenMipLevelsUsingD3DXFilter(MipSet* pMipSet, CMP_CFilterParams* pD3DXFilterParams) { - if (pMipSet->m_ChannelFormat != CF_8bit) - return PE_Unknown; +void PrintDX(const char* Format, ...) +{ + // define a pointer to save argument list + va_list args; + char buff[1024]; + // process the arguments into our debug buffer + va_start(args, Format); + vsprintf_s(buff, Format, args); + va_end(args); + + if (CFilterMips) + { + CFilterMips->Print(buff); + } + else + { + printf(buff); + } +} + +#ifndef USE_DIRECTX9 +void Plugin_BoxFilter::CleanupDX() +{ + // Compute + SAFE_RELEASE(m_pTexture2DSourceTexture); + SAFE_RELEASE(m_pContext); + SAFE_RELEASE(m_pDevice); +} + +HRESULT Plugin_BoxFilter::CreateDXDevice() +{ + HRESULT hr = S_OK; + UINT uCreationFlags = D3D11_CREATE_DEVICE_SINGLETHREADED; +#if defined(_DEBUG) + uCreationFlags |= D3D11_CREATE_DEVICE_DEBUG; +#endif + D3D_FEATURE_LEVEL flOut = D3D_FEATURE_LEVEL_9_1; + + hr = D3D11CreateDevice(nullptr, // Use default graphics card to support printf + D3D_DRIVER_TYPE_HARDWARE, // Try to create a hardware accelerated device + nullptr, // Do not use external software rasterizer module + uCreationFlags, // Device creation flags + nullptr, // Try to get greatest feature level available + 0, // # of elements in the previous array + D3D11_SDK_VERSION, // SDK version + &m_pDevice, // Device out + &flOut, // Actual feature level created + &m_pContext); // Context out + + if (FAILED(hr)) + { + // Failure on creating a hardware device, try to create a ref device + SAFE_RELEASE(m_pDevice); + SAFE_RELEASE(m_pContext); + + hr = D3D11CreateDevice(nullptr, // Use default graphics card + D3D_DRIVER_TYPE_REFERENCE, // Try to create a hardware accelerated device + nullptr, // Do not use external software rasterizer module + uCreationFlags, // Device creation flags + nullptr, // Try to get greatest feature level available + 0, // # of elements in the previous array + D3D11_SDK_VERSION, // SDK version + &m_pDevice, // Device out + &flOut, // Actual feature level created + &m_pContext); // Context out + if (FAILED(hr)) + { + _com_error err(hr); + LPCTSTR errMsg = err.ErrorMessage(); + PrintDX("Error %s\n", errMsg); + return hr; + } + } + else + { + if (FAILED(hr)) + { + _com_error err(hr); + LPCTSTR errMsg = err.ErrorMessage(); + PrintDX("Error %s\n", errMsg); + return hr; + } + } + + return hr; +} + +HRESULT Plugin_BoxFilter::Create2DTexture(MipSet* pMipSet, DWORD dwMipLevels) +{ + MipLevel* miplevel = CFilterMips->GetMipLevel(pMipSet, 0); + + HRESULT hr = 0; + unsigned int miscFlags = 0; + std::unique_ptr initData(new (std::nothrow) D3D11_SUBRESOURCE_DATA[1]); + if (!initData) + return E_OUTOFMEMORY; + + initData[0].pSysMem = miplevel->m_pbData; // pixel data + initData[0].SysMemPitch = miplevel->m_nWidth * 4; + initData[0].SysMemSlicePitch = miplevel->m_dwLinearSize; + + D3D11_TEXTURE2D_DESC desc = {}; + desc.Width = miplevel->m_nWidth; + desc.Height = miplevel->m_nHeight; + desc.MipLevels = dwMipLevels; + desc.ArraySize = 1; + desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + desc.CPUAccessFlags = 0; + desc.MiscFlags = miscFlags & ~static_cast(D3D11_RESOURCE_MISC_TEXTURECUBE); + + hr = m_pDevice->CreateTexture2D(&desc, initData.get(), reinterpret_cast(&m_pTexture2DSourceTexture)); + + return (hr); +} +#endif + +int Plugin_BoxFilter::GenMipLevelsUsingD3DXFilter(MipSet* pMipSet, CMP_CFilterParams* pD3DXFilterParams) +{ + if (!((pMipSet->m_ChannelFormat == CF_8bit) || (pMipSet->m_ChannelFormat == CF_Float16))) + { + if (CFilterMips) + CFilterMips->PrintError("Feature is only avaiable for 8 bit per channel images"); + return PE_Unknown; + } - DWORD dwFlags = pD3DXFilterParams ? pD3DXFilterParams->dwMipFilterOptions : D3DX_FILTER_TRIANGLE; + DWORD dwFlags = pD3DXFilterParams->dwMipFilterOptions; - CMP_ERROR retVal = CMP_ERR_GENERIC; // Mapping to DX11 updates todo - IDirect3D9* pD3D = NULL; // IDXGIFactory2 DXGIAdapter2 IDXGIDevice3 - IDirect3DDevice9* pDevice = NULL; // ID3D11Device2 ID3D11DeviceContext2 - IDirect3DTexture9* pTexture = NULL; // ID3D11Texture2D - IDirect3DSurface9* pSurface = NULL; // - HWND hWnd = NULL; // + CMP_ERROR retVal = CMP_ERR_GENERIC; + HWND hWnd = NULL; // HMODULE hInstance = GetModuleHandle(NULL); @@ -389,17 +709,58 @@ int Plugin_BoxFilter::GenMipLevelsUsingD3DXFilter(MipSet* pMipSet, CMP_CFilterPa RegisterClass(&wndClass); // Set the window's initial style - hWnd = CreateWindow(szWindowClass, szWindowClass, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 100, 100, NULL, NULL, hInstance, NULL); + hWnd = CreateWindow( + szWindowClass, szWindowClass, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, pMipSet->m_nWidth, pMipSet->m_nHeight, NULL, NULL, hInstance, NULL); if (hWnd == NULL) - HANDLE_ERROR(CMP_ERR_UNABLE_TO_INIT_D3DX); + HANDLE_ERROR(CMP_ERR_UNABLE_TO_INIT_D3DX, "Unable to init D3DX9"); + + HRESULT hr = 0; + +#ifdef USE_DIRECTX9 + + D3DFORMAT d3dformat; + + switch (pMipSet->m_ChannelFormat) + { + case CF_Float32: + d3dformat = D3DFMT_A32B32G32R32F; + break; + case CF_Float16: + d3dformat = D3DFMT_A16B16G16R16F; + break; + case CF_16bit: + d3dformat = D3DFMT_A16B16G16R16; + break; + case CF_8bit: + d3dformat = D3DFMT_A8R8G8B8; + break; + default: + // CF format Not mapped at this time! + // CF_2101010 10-bit integer data in the color channels & 2-bit integer data in the alpha channel. + // CF_32bit 32-bit integer data. + // CF_Float9995E 32-bit partial precision float. + // CF_YUV_420 YUV Chroma formats + // CF_YUV_422 YUV Chroma formats + // CF_YUV_444 YUV Chroma formats + // CF_YUV_4444 YUV Chroma formats + if (CFilterMips) + CFilterMips->PrintError("Feature is not supported for the images channel type!"); + return PE_Unknown; + break; + } + + IDirect3D9* pD3D = NULL; + IDirect3DDevice9* m_pDevice = NULL; + IDirect3DTexture9* m_pTexture2DSourceTexture = NULL; + IDirect3DSurface9* pSurface = NULL; pD3D = g_D3D9.Direct3DCreate9(D3D_SDK_VERSION); if (pD3D == NULL) - HANDLE_ERROR(CMP_ERR_UNABLE_TO_INIT_D3DX); + HANDLE_ERROR(CMP_ERR_UNABLE_TO_INIT_D3DX, "Unable get pD3D"); D3DPRESENT_PARAMETERS presentParams; - presentParams.BackBufferWidth = 100; - presentParams.BackBufferHeight = 100; + presentParams.BackBufferWidth = pMipSet->m_nWidth; + presentParams.BackBufferHeight = pMipSet->m_nHeight; presentParams.BackBufferCount = 0; presentParams.BackBufferFormat = D3DFMT_UNKNOWN; presentParams.MultiSampleType = D3DMULTISAMPLE_NONE; @@ -413,72 +774,96 @@ int Plugin_BoxFilter::GenMipLevelsUsingD3DXFilter(MipSet* pMipSet, CMP_CFilterPa presentParams.FullScreen_RefreshRateInHz = 0; presentParams.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; - HRESULT hr = 0; - hr = pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_MIXED_VERTEXPROCESSING, &presentParams, &pDevice); + hr = pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_MIXED_VERTEXPROCESSING, &presentParams, &m_pDevice); if (FAILED(hr)) - HANDLE_ERROR(CMP_ERR_UNABLE_TO_INIT_D3DX); - + HANDLE_ERROR(CMP_ERR_UNABLE_TO_INIT_D3DX, "Failed to create D3DX9 device"); +#else + if (!CreateDXDevice()) + { + HANDLE_ERROR(CMP_ERR_UNABLE_TO_INIT_D3DX, "Failed to create device"); + } +#endif DWORD dwMipLevels = 1; int nWidth = pMipSet->m_nWidth; int nHeight = pMipSet->m_nHeight; - while (nWidth > pD3DXFilterParams->nMinSize || nHeight > pD3DXFilterParams->nMinSize) { + while (nWidth > pD3DXFilterParams->nMinSize || nHeight > pD3DXFilterParams->nMinSize) + { nWidth = max(nWidth >> 1, 1); nHeight = max(nHeight >> 1, 1); dwMipLevels++; + if ((nWidth <= pD3DXFilterParams->nMinSize) || (nHeight <= pD3DXFilterParams->nMinSize)) + break; + if (dwMipLevels == pMipSet->m_nMaxMipLevels) + break; } - pDevice->CreateTexture(pMipSet->m_nWidth, pMipSet->m_nHeight, dwMipLevels, 0, D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &pTexture, NULL); + DWORD dwLevel; + +#ifdef USE_DIRECTX9 + m_pDevice->CreateTexture(pMipSet->m_nWidth, pMipSet->m_nHeight, dwMipLevels, 0, d3dformat, D3DPOOL_SCRATCH, &m_pTexture2DSourceTexture, NULL); + if (FAILED(hr)) + HANDLE_ERROR(CMP_ERR_UNABLE_TO_INIT_D3DX, "Failed to create texture"); +#else + hr = Create2DTexture(pMipSet, dwMipLevels); if (FAILED(hr)) - HANDLE_ERROR(CMP_ERR_UNABLE_TO_INIT_D3DX); + HANDLE_ERROR(CMP_ERR_UNABLE_TO_INIT_D3DX, "Failed to create texture"); +#endif +#ifdef USE_DIRECTX9 D3DLOCKED_RECT rect; - hr = pTexture->LockRect(0, &rect, NULL, 0); + hr = m_pTexture2DSourceTexture->LockRect(0, &rect, NULL, 0); if (FAILED(hr)) - HANDLE_ERROR(CMP_ERR_UNABLE_TO_INIT_D3DX); + HANDLE_ERROR(CMP_ERR_UNABLE_TO_INIT_D3DX, "Failed to lock rect"); memcpy(rect.pBits, CFilterMips->GetMipLevel(pMipSet, 0)->m_pbData, CFilterMips->GetMipLevel(pMipSet, 0)->m_dwLinearSize); - hr = pTexture->UnlockRect(0); + hr = m_pTexture2DSourceTexture->UnlockRect(0); if (FAILED(hr)) - HANDLE_ERROR(CMP_ERR_UNABLE_TO_INIT_D3DX); + HANDLE_ERROR(CMP_ERR_UNABLE_TO_INIT_D3DX, "Failed to unlock rect"); - hr = g_D3DX.D3DXFilterTexture(pTexture, NULL, D3DX_DEFAULT, dwFlags); + hr = g_D3DX.D3DXFilterTexture(m_pTexture2DSourceTexture, NULL, D3DX_DEFAULT, dwFlags); if (FAILED(hr)) - HANDLE_ERROR(CMP_ERR_UNABLE_TO_INIT_D3DX); + HANDLE_ERROR(CMP_ERR_UNABLE_TO_INIT_D3DX, "Failed D3DXFilterTexture"); + DWORD dwLevels = m_pTexture2DSourceTexture->GetLevelCount(); - DWORD dwLevels = pTexture->GetLevelCount(); ASSERT(dwLevels == dwMipLevels); - DWORD dwLevel; - for (dwLevel = 1; dwLevel < dwLevels; dwLevel++) { - hr = pTexture->GetSurfaceLevel(dwLevel, &pSurface); + for (dwLevel = 1; dwLevel < dwLevels; dwLevel++) + { + hr = m_pTexture2DSourceTexture->GetSurfaceLevel(dwLevel, &pSurface); if (FAILED(hr)) - HANDLE_ERROR(CMP_ERR_UNABLE_TO_INIT_D3DX); + HANDLE_ERROR(CMP_ERR_UNABLE_TO_INIT_D3DX, "Failed GetSurfaceLevel"); D3DSURFACE_DESC desc; hr = pSurface->GetDesc(&desc); if (FAILED(hr)) - HANDLE_ERROR(CMP_ERR_UNABLE_TO_INIT_D3DX); + HANDLE_ERROR(CMP_ERR_UNABLE_TO_INIT_D3DX, "Failed GetDesc"); + + if (CFilterMips->GetMipLevel(pMipSet, dwLevel)->m_pbData) + { // Space for mip level already allocated ? - if (CFilterMips->GetMipLevel(pMipSet, dwLevel)->m_pbData) { // Space for mip level already allocated ? if (CFilterMips->GetMipLevel(pMipSet, dwLevel)->m_nWidth != (int)desc.Width || - CFilterMips->GetMipLevel(pMipSet, dwLevel)->m_nHeight != (int)desc.Height) { + CFilterMips->GetMipLevel(pMipSet, dwLevel)->m_nHeight != (int)desc.Height) + { // Wrong size - release & reallocate CFilterMips->FreeMipLevelData(CFilterMips->GetMipLevel(pMipSet, dwLevel)); if (CFilterMips->AllocateMipLevelData( - CFilterMips->GetMipLevel(pMipSet, dwLevel), desc.Width, desc.Height, pMipSet->m_ChannelFormat, pMipSet->m_TextureDataType) == NULL) { - HANDLE_ERROR(CMP_ERR_UNABLE_TO_INIT_D3DX); + CFilterMips->GetMipLevel(pMipSet, dwLevel), desc.Width, desc.Height, pMipSet->m_ChannelFormat, pMipSet->m_TextureDataType) == NULL) + { + HANDLE_ERROR(CMP_ERR_UNABLE_TO_INIT_D3DX, "Failed AllocateMipLevelData"); } } - } else if (CFilterMips->AllocateMipLevelData( - CFilterMips->GetMipLevel(pMipSet, dwLevel), desc.Width, desc.Height, pMipSet->m_ChannelFormat, pMipSet->m_TextureDataType) == NULL) { - HANDLE_ERROR(CMP_ERR_UNABLE_TO_INIT_D3DX); + } + else if (CFilterMips->AllocateMipLevelData( + CFilterMips->GetMipLevel(pMipSet, dwLevel), desc.Width, desc.Height, pMipSet->m_ChannelFormat, pMipSet->m_TextureDataType) == NULL) + { + HANDLE_ERROR(CMP_ERR_UNABLE_TO_INIT_D3DX, " Failed GetMipLevel"); } hr = pSurface->LockRect(&rect, NULL, 0); if (FAILED(hr)) - HANDLE_ERROR(CMP_ERR_UNABLE_TO_INIT_D3DX); + HANDLE_ERROR(CMP_ERR_UNABLE_TO_INIT_D3DX, "Failed LockRect 2"); memcpy(CFilterMips->GetMipLevel(pMipSet, dwLevel)->m_pbData, rect.pBits, CFilterMips->GetMipLevel(pMipSet, dwLevel)->m_dwLinearSize); @@ -486,6 +871,19 @@ int Plugin_BoxFilter::GenMipLevelsUsingD3DXFilter(MipSet* pMipSet, CMP_CFilterPa pSurface->Release(); pSurface = NULL; } +#else + + D3D11_TEXTURE2D_DESC srcDesc; + m_pTexture2DSourceTexture->GetDesc(&srcDesc); + + D3D11_TEXTURE2D_DESC desc = srcDesc; + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.MiscFlags = 0; + + ID3D11Texture2D* pMipLevel = nullptr; + m_pDevice->CreateTexture2D(&desc, nullptr, &pMipLevel); +#endif retVal = CMP_OK; @@ -496,28 +894,37 @@ int Plugin_BoxFilter::GenMipLevelsUsingD3DXFilter(MipSet* pMipSet, CMP_CFilterPa pMipSet->m_nMipLevels = dwMipLevels; Cleanup: - if (pSurface) { +#ifdef USE_DIRECTX9 + if (pSurface) + { pSurface->Release(); pSurface = NULL; } - if (pTexture) { - pTexture->Release(); - pTexture = NULL; +#endif + if (m_pTexture2DSourceTexture) + { + m_pTexture2DSourceTexture->Release(); + m_pTexture2DSourceTexture = NULL; } - if (pDevice) { - pDevice->Release(); - pDevice = NULL; + if (m_pDevice) + { + m_pDevice->Release(); + m_pDevice = NULL; } - if (pD3D) { + +#ifdef USE_DIRECTX9 + if (pD3D) + { pD3D->Release(); pD3D = NULL; } - if (hWnd) { +#endif + if (hWnd) + { DestroyWindow(hWnd); hWnd = NULL; } - return retVal; } #endif diff --git a/applications/_plugins/cfilter/boxfilter.h b/applications/_plugins/cfilter/boxfilter.h index 91f6bbbdc..2997b4755 100644 --- a/applications/_plugins/cfilter/boxfilter.h +++ b/applications/_plugins/cfilter/boxfilter.h @@ -28,9 +28,15 @@ #include "plugininterface.h" #ifdef _WIN32 +#define USE_DIRECTX9 +#ifdef USE_DIRECTX9 #include "d3d9.h" #include "d3dxmodule.h" #include "d3d9module.h" +#else +#include +#include +#endif // {3AF62198-7326-48FA-B1FB-1D12A355694D} static const GUID g_GUID = {0x3af62198, 0x7326, 0x48fa, {0xb1, 0xfb, 0x1d, 0x12, 0xa3, 0x55, 0x69, 0x4d}}; @@ -41,8 +47,18 @@ static const GUID g_GUID = {0}; #define TC_PLUGIN_VERSION_MAJOR 1 #define TC_PLUGIN_VERSION_MINOR 0 -class Plugin_BoxFilter : public PluginInterface_Filters { - public: +#define SAFE_RELEASE(x) \ + { \ + if (x) \ + { \ + x->Release(); \ + x = nullptr; \ + } \ + } + +class Plugin_BoxFilter : public PluginInterface_Filters +{ +public: Plugin_BoxFilter(); virtual ~Plugin_BoxFilter(); @@ -50,10 +66,22 @@ class Plugin_BoxFilter : public PluginInterface_Filters { int TC_PluginGetVersion(TC_PluginVersion* pPluginVersion); int TC_CFilter(MipSet* pMipSet, CMP_MipSet* pMipSetDst, CMP_CFilterParams* pD3DXFilterParams); - private: - void Error(TCHAR* pszCaption, TC_ErrorLevel errorLevel, UINT nErrorString); +private: + void Error(CMP_CHAR* pszCaption, CMP_UINT nErrorString, CMP_CHAR* errMsg); + #ifdef _WIN32 int GenMipLevelsUsingD3DXFilter(MipSet* pMipSet, CMP_CFilterParams* pD3DXFilterParams); + + #ifndef USE_DIRECTX9 + // DirectX + ID3D11Device* m_pDevice; + ID3D11DeviceContext* m_pContext; + ID3D11Texture2D* m_pTexture2DSourceTexture; + + void CleanupDX(); + HRESULT CreateDXDevice(); + HRESULT Create2DTexture(MipSet* pMipSet, DWORD dwMipLevels) + #endif #endif }; diff --git a/applications/_plugins/cfilter/cmakelists.txt b/applications/_plugins/cfilter/cmakelists.txt index 5748bcde4..0c87421b7 100644 --- a/applications/_plugins/cfilter/cmakelists.txt +++ b/applications/_plugins/cfilter/cmakelists.txt @@ -5,9 +5,9 @@ link_directories( ) endif() -add_library(Plugin_CFilter_MipMap SHARED) +add_library(Image_MipMap SHARED) -target_sources(Plugin_CFilter_MipMap PRIVATE +target_sources(Image_MipMap PRIVATE boxfilter.cpp boxfilter.h @@ -19,25 +19,24 @@ target_sources(Plugin_CFilter_MipMap PRIVATE ${PROJECT_SOURCE_DIR}/applications/_plugins/cmp_d3dx/d3d9module.h ) -target_include_directories(Plugin_CFilter_MipMap PUBLIC +target_include_directories(Image_MipMap PUBLIC ./ ${PROJECT_SOURCE_DIR}/../common/lib/ext/apitrace/dxsdk/Include ${PROJECT_SOURCE_DIR}/applications/_plugins/cmp_d3dx - #${PROJECT_SOURCE_DIR}/../Common/Lib/Ext/DirectX-SDK/June-2010/Include + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib # compressonator.h + ${PROJECT_SOURCE_DIR}/cmp_framework # compute_base.h + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half # half.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common # tc_pluginapi.h ) -target_link_libraries(Plugin_CFilter_MipMap PRIVATE - - CMP_FileIO - CompressonatorLIB - Plugin_CmdLine - Plugin_PluginManager - Plugin_TCPluginAPI +target_link_libraries(Image_MipMap PRIVATE + CMP_Compressonator ) -set_target_properties(Plugin_CFilter_MipMap PROPERTIES - FOLDER ${FOLDER_NAME} +target_compile_definitions(Image_MipMap PRIVATE BUILD_AS_PLUGIN_DLL=1) + +set_target_properties(Image_MipMap PROPERTIES + FOLDER "Plugin_Dynamic/ImageFx" RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin/debug/plugins" RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin/release/plugins" ) - diff --git a/applications/_plugins/cfilter_fx/CMakeLists.txt b/applications/_plugins/cfilter_fx/CMakeLists.txt index 5485ca216..137f1b1cf 100644 --- a/applications/_plugins/cfilter_fx/CMakeLists.txt +++ b/applications/_plugins/cfilter_fx/CMakeLists.txt @@ -6,30 +6,32 @@ link_directories( ) endif() -add_library(Plugin_CFilter_Fx SHARED) +add_library(Image_FilterFX SHARED) -target_sources(Plugin_CFilter_Fx PRIVATE +target_sources(Image_FilterFX PRIVATE filterfx.cpp filterfx.h ) -target_include_directories(Plugin_CFilter_Fx PUBLIC +target_include_directories(Image_FilterFX PUBLIC ./ ${PROJECT_SOURCE_DIR}/../common/lib/ext/apitrace/dxsdk/Include + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib # compressonator.h + ${PROJECT_SOURCE_DIR}/cmp_framework # compute_base.h + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half # half.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common # tc_pluginapi.h ) -target_link_libraries(Plugin_CFilter_Fx PRIVATE +target_link_libraries(Image_FilterFX PRIVATE + CMP_Compressonator - CMP_FileIO - CompressonatorLIB - Plugin_CmdLine - Plugin_PluginManager - Plugin_TCPluginAPI ) -set_target_properties(Plugin_CFilter_Fx PROPERTIES - FOLDER ${FOLDER_NAME} +target_compile_definitions(Image_FilterFX PRIVATE BUILD_AS_PLUGIN_DLL=1) + +set_target_properties(Image_FilterFX PROPERTIES + FOLDER "Plugin_Dynamic/ImageFx" RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin/debug/plugins" RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin/release/plugins" ) diff --git a/applications/_plugins/cgpudecode/directx/cmakelists.txt b/applications/_plugins/cgpudecode/directx/cmakelists.txt index 2d5f5f1c0..88ead6f21 100644 --- a/applications/_plugins/cgpudecode/directx/cmakelists.txt +++ b/applications/_plugins/cgpudecode/directx/cmakelists.txt @@ -1,26 +1,40 @@ -add_library(Plugin_CGPUDecode_DirectX) +link_directories( + ${PROJECT_SOURCE_DIR}/../common/lib/ext/directxtex/DirectXTex-jun2020b/DirectXTex/Bin/Desktop_2017/x64/$<$:debug>$<$:release> + ) + +add_library(GPUDecode_DirectX SHARED) file(GLOB_RECURSE SOURCES ./*.cpp ./*.h ) -target_sources(Plugin_CGPUDecode_DirectX PRIVATE +target_sources(GPUDecode_DirectX PRIVATE ${SOURCES} ddsview.fx ) -target_link_libraries(Plugin_CGPUDecode_DirectX PRIVATE - CompressonatorLIB - GPU_Decode - ExtDirectX - ExtDirectXTex - Plugin_TCPluginAPI +target_link_libraries(GPUDecode_DirectX PRIVATE + CMP_Compressonator + CMP_Framework + CMP_GpuDecode + + ExtDirectX ) -target_include_directories(Plugin_CGPUDecode_DirectX PRIVATE +target_include_directories(GPUDecode_DirectX PRIVATE + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half + ${PROJECT_SOURCE_DIR}/applications/_libs/gpu_decode + ${PROJECT_SOURCE_DIR}/applications/_plugins/common ${PROJECT_SOURCE_DIR}/../common/lib/ext/directxtex/DirectXTex-jun2020b/DirectXTex ) -set_target_properties(Plugin_CGPUDecode_DirectX PROPERTIES FOLDER ${FOLDER_NAME}) +target_compile_definitions(GPUDecode_DirectX PRIVATE BUILD_AS_PLUGIN_DLL=1) + +set_target_properties(GPUDecode_DirectX PROPERTIES + FOLDER "Plugin_Dynamic/GPU_DecodeView" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin/debug/plugins" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin/release/plugins" + ) diff --git a/applications/_plugins/cgpudecode/directx/gpu_directx.cpp b/applications/_plugins/cgpudecode/directx/gpu_directx.cpp index dc8bfcdcb..edc0ad1c9 100644 --- a/applications/_plugins/cgpudecode/directx/gpu_directx.cpp +++ b/applications/_plugins/cgpudecode/directx/gpu_directx.cpp @@ -19,6 +19,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // +//-------------------------------------------------------------------------------------- // #define DECOMPRESS_SAVE_TO_BMP @@ -740,6 +741,18 @@ CMP_ERROR WINAPI GPU_DirectX::Decompress( //pDestTexture->dwHeight = m_height; uint8_t *pxdata = sratchimage.GetPixels(); memcpy(pDestTexture->pData, pxdata, pDestTexture->dwDataSize); + + // handle special cases + // adjust destination format as described by the captured buffers meta data + TexMetadata mdata = sratchimage.GetMetadata(); + if (pDestTexture->format == CMP_FORMAT_RGBA_8888_S) + { + // switch data type as captured data is not snorm + // This case should be handled properly, code is needed to setup snorm captures + if (mdata.format == DXGI_FORMAT_R8G8B8A8_UNORM) + pDestTexture->format = CMP_FORMAT_ARGB_8888; + } + } CleanupDevice(); diff --git a/applications/_plugins/cgpudecode/opengl/cmakelists.txt b/applications/_plugins/cgpudecode/opengl/cmakelists.txt index 53f09eec2..a3af69602 100644 --- a/applications/_plugins/cgpudecode/opengl/cmakelists.txt +++ b/applications/_plugins/cgpudecode/opengl/cmakelists.txt @@ -1,7 +1,7 @@ -add_library(Plugin_CGPUDecode_OpenGL) +add_library(GPUDecode_OpenGL SHARED) -target_sources(Plugin_CGPUDecode_OpenGL PUBLIC +target_sources(GPUDecode_OpenGL PUBLIC copengl.cpp copengl.h @@ -10,14 +10,21 @@ target_sources(Plugin_CGPUDecode_OpenGL PUBLIC shader.h ) -target_link_libraries(Plugin_CGPUDecode_OpenGL + +target_include_directories(GPUDecode_OpenGL PRIVATE + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half + ${PROJECT_SOURCE_DIR}/applications/_libs/gpu_decode + ${PROJECT_SOURCE_DIR}/applications/_plugins/common +) + +target_link_libraries(GPUDecode_OpenGL PRIVATE - CompressonatorLIB + CMP_Compressonator + CMP_Framework + CMP_GpuDecode - Plugin_PluginManager - Plugin_TCPluginAPI - GPU_Decode ExtOpenGL ExtGLEW @@ -25,4 +32,10 @@ target_link_libraries(Plugin_CGPUDecode_OpenGL ExtGLEW ) -set_target_properties(Plugin_CGPUDecode_OpenGL PROPERTIES FOLDER ${FOLDER_NAME}) +target_compile_definitions(GPUDecode_OpenGL PRIVATE BUILD_AS_PLUGIN_DLL=1) + +set_target_properties(GPUDecode_OpenGL PROPERTIES + FOLDER "Plugin_Dynamic/GPU_DecodeView" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin/debug/plugins" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin/release/plugins" + ) diff --git a/applications/_plugins/cgpudecode/opengl/gpu_opengl.cpp b/applications/_plugins/cgpudecode/opengl/gpu_opengl.cpp index 1fce12230..40380b40a 100644 --- a/applications/_plugins/cgpudecode/opengl/gpu_opengl.cpp +++ b/applications/_plugins/cgpudecode/opengl/gpu_opengl.cpp @@ -78,6 +78,7 @@ GPU_OpenGL::~GPU_OpenGL() { } //==================================================================================== +// #define SHOW_WINDOW void GPU_OpenGL::GLRender() { // OpenGL animation code goes here @@ -321,15 +322,21 @@ CMP_ERROR WINAPI GPU_OpenGL::Decompress( pSourceTexture->format == CMP_FORMAT_ETC2_RGBA || pSourceTexture->format == CMP_FORMAT_ETC2_RGBA1 ) - glReadPixels(0, 0, pDestTexture->dwWidth, pDestTexture->dwHeight, GL_BGRA_EXT, GL_UNSIGNED_BYTE, pDestTexture->pData); + glReadPixels(0, 0, pDestTexture->dwWidth, pDestTexture->dwHeight, GL_BGRA_EXT, GL_UNSIGNED_BYTE, pDestTexture->pData); else if (pSourceTexture->format == CMP_FORMAT_ETC2_SRGB || pSourceTexture->format == CMP_FORMAT_ETC2_SRGBA || pSourceTexture->format == CMP_FORMAT_ETC2_SRGBA1 ) - glReadPixels(0, 0, pDestTexture->dwWidth, pDestTexture->dwHeight, GL_BGRA_EXT, GL_BYTE, pDestTexture->pData); + glReadPixels(0, 0, pDestTexture->dwWidth, pDestTexture->dwHeight, GL_BGRA_EXT, GL_BYTE, pDestTexture->pData); else { - if(pDestTexture->format == CMP_FORMAT_ARGB_16F) + if (pDestTexture->format == CMP_FORMAT_ARGB_16F) + { glReadPixels(0, 0, pDestTexture->dwWidth, pDestTexture->dwHeight, GL_RGBA, GL_HALF_FLOAT, pDestTexture->pData); + } + else if (pDestTexture->format == CMP_FORMAT_RGBA_8888_S) + { + glReadPixels(0, 0, pDestTexture->dwWidth, pDestTexture->dwHeight, GL_RGBA_SNORM, GL_BYTE, pDestTexture->pData); + } else glReadPixels(0, 0, pDestTexture->dwWidth, pDestTexture->dwHeight, GL_RGBA, GL_UNSIGNED_BYTE, pDestTexture->pData); } diff --git a/applications/_plugins/cgpudecode/vulkan/cmakelists.txt b/applications/_plugins/cgpudecode/vulkan/cmakelists.txt index 14eeb54ed..19dedd6c0 100644 --- a/applications/_plugins/cgpudecode/vulkan/cmakelists.txt +++ b/applications/_plugins/cgpudecode/vulkan/cmakelists.txt @@ -1,40 +1,52 @@ -add_library(Plugin_CGPUDecode_Vulkan) +add_library(GPUDecode_Vulkan SHARED) file(GLOB_RECURSE SHADERS - *.spv ) -file(GLOB_RECURSE SOURCES - - *.cpp - *.h - *.hpp - *.inl +# file(GLOB_RECURSE SOURCES +# *.cpp +# *.h +# *.hpp +# *.inl +# ) + +target_sources(GPUDecode_Vulkan PRIVATE +# ${SOURCES} +${PROJECT_SOURCE_DIR}/applications/_plugins/cgpudecode/vulkan/cvulkan.cpp +${PROJECT_SOURCE_DIR}/applications/_plugins/cgpudecode/vulkan/cvulkan.h +${PROJECT_SOURCE_DIR}/applications/_plugins/cgpudecode/vulkan/vulkandebug.cpp +${PROJECT_SOURCE_DIR}/applications/_plugins/cgpudecode/vulkan/vulkandebug.h +${PROJECT_SOURCE_DIR}/applications/_plugins/cgpudecode/vulkan/vulkanswapchain.hpp +${PROJECT_SOURCE_DIR}/applications/_plugins/cgpudecode/vulkan/vulkantools.cpp +${PROJECT_SOURCE_DIR}/applications/_plugins/cgpudecode/vulkan/vulkantools.h +${PROJECT_SOURCE_DIR}/applications/_plugins/cgpudecode/vulkan/gpu_vulkan.cpp +${PROJECT_SOURCE_DIR}/applications/_plugins/cgpudecode/vulkan/gpu_vulkan.h ) -target_sources(Plugin_CGPUDecode_Vulkan PRIVATE - - ${SOURCES} +target_include_directories(GPUDecode_Vulkan PRIVATE + ./ + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half + ${PROJECT_SOURCE_DIR}/applications/_libs/gpu_decode + ${PROJECT_SOURCE_DIR}/applications/_plugins/common + ${PROJECT_SOURCE_DIR}/../common/lib/ext/glm ) -target_include_directories(Plugin_CGPUDecode_Vulkan PRIVATE - - ./ -) - -target_link_libraries(Plugin_CGPUDecode_Vulkan PRIVATE - +target_link_libraries(GPUDecode_Vulkan PRIVATE CMP_Common - CompressonatorLIB - - Plugin_PluginManager - Plugin_TCPluginAPI - GPU_Decode + CMP_Compressonator + CMP_GpuDecode ExtVulkan ExtGLM ) -set_target_properties(Plugin_CGPUDecode_Vulkan PROPERTIES FOLDER ${FOLDER_NAME}) +target_compile_definitions(GPUDecode_Vulkan PRIVATE BUILD_AS_PLUGIN_DLL=1) + +set_target_properties(GPUDecode_Vulkan PROPERTIES + FOLDER "Plugin_Dynamic/GPU_DecodeView" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin/debug/plugins" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin/release/plugins" + ) diff --git a/applications/_plugins/cgpudecode/vulkan/gpu_vulkan.cpp b/applications/_plugins/cgpudecode/vulkan/gpu_vulkan.cpp index 5af6e0023..2d80cbc6c 100644 --- a/applications/_plugins/cgpudecode/vulkan/gpu_vulkan.cpp +++ b/applications/_plugins/cgpudecode/vulkan/gpu_vulkan.cpp @@ -31,68 +31,77 @@ #include "gpu_vulkan.h" -#pragma comment(lib, "opengl32.lib") // Open GL Link requirements in Base Code +#pragma comment(lib, "opengl32.lib") // Open GL Link requirements in Base Code using namespace GPU_Decode; #define VK_VERSION_MAJOR(version) ((uint32_t)(version) >> 22) #define VK_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3ff) // Synchronization semaphores -struct { +struct +{ VkSemaphore presentDone; VkSemaphore renderDone; } semaphores; -struct { - VkImage image; +struct +{ + VkImage image; VkDeviceMemory mem; - VkImageView view; + VkImageView view; } depthStencil; // Vertex layout -struct Vertex { +struct Vertex +{ float coordinate[3]; float uv[2]; }; -struct { - VkBuffer buffer; - VkDeviceMemory memory; - VkPipelineVertexInputStateCreateInfo inputState; - std::vector bindingDescriptions; +struct +{ + VkBuffer buffer; + VkDeviceMemory memory; + VkPipelineVertexInputStateCreateInfo inputState; + std::vector bindingDescriptions; std::vector attributeDescriptions; } vertices; -struct { - int count; - VkBuffer buffer; +struct +{ + int count; + VkBuffer buffer; VkDeviceMemory memory; } indices; -struct { +struct +{ glm::mat4 projection; glm::mat4 model; - float lodBias = 0.0f; + float lodBias = 0.0f; } uboVS; -struct Texture { - VkSampler sampler; - VkImage image; - VkImageLayout imageLayout; +struct Texture +{ + VkSampler sampler; + VkImage image; + VkImageLayout imageLayout; VkDeviceMemory deviceMemory; - VkImageView view; - uint32_t width, height; - uint32_t mipLevels; + VkImageView view; + uint32_t width, height; + uint32_t mipLevels; } texture; -struct { +struct +{ VkPipeline solid; } pipelines; -VkClearColorValue defaultClearColor = { { 0.025f, 0.025f, 0.025f, 1.0f } }; +VkClearColorValue defaultClearColor = {{0.025f, 0.025f, 0.025f, 1.0f}}; GPU_Vulkan::GPU_Vulkan(std::uint32_t Width, std::uint32_t Height, WNDPROC callback) - : RenderWindow("Vulkan") { + : RenderWindow("Vulkan") +{ m_initOk = false; //set default width and height if is 0 if (Width <= 0) @@ -100,10 +109,10 @@ GPU_Vulkan::GPU_Vulkan(std::uint32_t Width, std::uint32_t Height, WNDPROC callba if (Height <= 0) Height = 480; - m_descriptorPool = VK_NULL_HANDLE; - if (FAILED(InitWindow(Width, Height, callback))) { + if (FAILED(InitWindow(Width, Height, callback))) + { fprintf(stderr, "[Vulkan] Failed to initialize Window. Please make sure Vulkan SDK is available.\n"); assert(0); } @@ -111,13 +120,15 @@ GPU_Vulkan::GPU_Vulkan(std::uint32_t Width, std::uint32_t Height, WNDPROC callba EnableWindowContext(m_hWnd, &m_hDC, &m_hRC); } -GPU_Vulkan::~GPU_Vulkan() { +GPU_Vulkan::~GPU_Vulkan() +{ if (m_initOk) clean(); } //-------------------------------------------------------------------------------------- -void GPU_Vulkan::VkRender() { +void GPU_Vulkan::VkRender() +{ if (!m_prepared) return; vkDeviceWaitIdle(device); @@ -125,71 +136,80 @@ void GPU_Vulkan::VkRender() { vkDeviceWaitIdle(device); } -VkResult GPU_Vulkan::createInstance(bool enableValidation) { +VkResult GPU_Vulkan::createInstance(bool enableValidation) +{ m_tenableValidation = enableValidation; VkApplicationInfo appInfo = {}; - appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; - appInfo.pApplicationName = "vulkanview"; - appInfo.pEngineName = "vulkanview"; + appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + appInfo.pApplicationName = "vulkanview"; + appInfo.pEngineName = "vulkanview"; appInfo.apiVersion = VK_API_VERSION_1_0; - std::vector enabledExtensions = { VK_KHR_SURFACE_EXTENSION_NAME }; + std::vector enabledExtensions = {VK_KHR_SURFACE_EXTENSION_NAME}; #ifdef _WIN32 enabledExtensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); #endif VkInstanceCreateInfo instanceCreateInfo = {}; - instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - instanceCreateInfo.pNext = NULL; - instanceCreateInfo.pApplicationInfo = &appInfo; - if (enabledExtensions.size() > 0) { - if (enableValidation) { + instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + instanceCreateInfo.pNext = NULL; + instanceCreateInfo.pApplicationInfo = &appInfo; + if (enabledExtensions.size() > 0) + { + if (enableValidation) + { enabledExtensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); } - instanceCreateInfo.enabledExtensionCount = (uint32_t)enabledExtensions.size(); + instanceCreateInfo.enabledExtensionCount = (uint32_t)enabledExtensions.size(); instanceCreateInfo.ppEnabledExtensionNames = enabledExtensions.data(); } - if (enableValidation) { - instanceCreateInfo.enabledLayerCount = vkDebug::validationLayerCount; + if (enableValidation) + { + instanceCreateInfo.enabledLayerCount = vkDebug::validationLayerCount; instanceCreateInfo.ppEnabledLayerNames = vkDebug::validationLayerNames; } return vkCreateInstance(&instanceCreateInfo, nullptr, &instance); } -VkResult GPU_Vulkan::createDevice(VkDeviceQueueCreateInfo requestedQueues, bool enableValidation) { - std::vector enabledExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; +VkResult GPU_Vulkan::createDevice(VkDeviceQueueCreateInfo requestedQueues, bool enableValidation) +{ + std::vector enabledExtensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME}; - VkDeviceCreateInfo deviceCreateInfo = {}; - deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - deviceCreateInfo.pNext = NULL; + VkDeviceCreateInfo deviceCreateInfo = {}; + deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + deviceCreateInfo.pNext = NULL; deviceCreateInfo.queueCreateInfoCount = 1; - deviceCreateInfo.pQueueCreateInfos = &requestedQueues; - deviceCreateInfo.pEnabledFeatures = NULL; + deviceCreateInfo.pQueueCreateInfos = &requestedQueues; + deviceCreateInfo.pEnabledFeatures = NULL; - if (enabledExtensions.size() > 0) { - deviceCreateInfo.enabledExtensionCount = (uint32_t)enabledExtensions.size(); + if (enabledExtensions.size() > 0) + { + deviceCreateInfo.enabledExtensionCount = (uint32_t)enabledExtensions.size(); deviceCreateInfo.ppEnabledExtensionNames = enabledExtensions.data(); } - if (enableValidation) { - deviceCreateInfo.enabledLayerCount = vkDebug::validationLayerCount; + if (enableValidation) + { + deviceCreateInfo.enabledLayerCount = vkDebug::validationLayerCount; deviceCreateInfo.ppEnabledLayerNames = vkDebug::validationLayerNames; } return vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &device); } -bool GPU_Vulkan::initVulkan(bool enableValidation) { +bool GPU_Vulkan::initVulkan(bool enableValidation) +{ VkResult err; m_initOk = false; // Vulkan instance err = createInstance(enableValidation); - if (err) { - fprintf(stderr, "\nCould not create Vulkan instance : %s Vulkan not Supported!\n",vkTools::errorString(err)); + if (err) + { + fprintf(stderr, "\nCould not create Vulkan instance : %s Vulkan not Supported!\n", vkTools::errorString(err)); vkTools::exitFatal("Could not create Vulkan instance : \n" + vkTools::errorString(err), "Vulkan not Supported!"); return false; } @@ -197,7 +217,8 @@ bool GPU_Vulkan::initVulkan(bool enableValidation) { // Physical device // Get number of available physical devices err = vkEnumeratePhysicalDevices(instance, &gpuCount, nullptr); - if (err != VK_SUCCESS) { + if (err != VK_SUCCESS) + { fprintf(stderr, "\n[Vulkan Error] Could not get Vulkan devices: %s\n", vkTools::errorString(err)); return false; } @@ -210,7 +231,8 @@ bool GPU_Vulkan::initVulkan(bool enableValidation) { // Enumerate devices physicalDevices.resize(gpuCount); err = vkEnumeratePhysicalDevices(instance, &gpuCount, physicalDevices.data()); - if (err != VK_SUCCESS) { + if (err != VK_SUCCESS) + { fprintf(stderr, "\n[Vulkan Error] Could not enumerate phyiscal devices: %s\n", vkTools::errorString(err)); vkTools::exitFatal("Could not enumerate phyiscal devices : \n" + vkTools::errorString(err), "Vulkan not Supported!"); return false; @@ -234,7 +256,8 @@ bool GPU_Vulkan::initVulkan(bool enableValidation) { queueProps.resize(queueCount); vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, queueProps.data()); - for (graphicsQueueIndex = 0; graphicsQueueIndex < queueCount; graphicsQueueIndex++) { + for (graphicsQueueIndex = 0; graphicsQueueIndex < queueCount; graphicsQueueIndex++) + { if (queueProps[graphicsQueueIndex].queueFlags & VK_QUEUE_GRAPHICS_BIT) break; } @@ -244,15 +267,16 @@ bool GPU_Vulkan::initVulkan(bool enableValidation) { assert(graphicsQueueIndex < queueCount); // Vulkan device - std::array queuePriorities = { 0.0f }; + std::array queuePriorities = {0.0f}; VkDeviceQueueCreateInfo queueCreateInfo = {}; - queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queueCreateInfo.queueFamilyIndex = graphicsQueueIndex; - queueCreateInfo.queueCount = 1; - queueCreateInfo.pQueuePriorities = queuePriorities.data(); + queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueCreateInfo.queueFamilyIndex = graphicsQueueIndex; + queueCreateInfo.queueCount = 1; + queueCreateInfo.pQueuePriorities = queuePriorities.data(); err = createDevice(queueCreateInfo, enableValidation); - if (err != VK_SUCCESS) { + if (err != VK_SUCCESS) + { fprintf(stderr, "\n[Vulkan Error] Could not createDevice: %s\n", vkTools::errorString(err)); return false; } @@ -276,7 +300,8 @@ bool GPU_Vulkan::initVulkan(bool enableValidation) { // Create a semaphore used to synchronize image presentation // Ensures that the image is displayed before we start submitting new commands to the queu err = vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &semaphores.presentDone); - if (err != VK_SUCCESS) { + if (err != VK_SUCCESS) + { fprintf(stderr, "\n[Vulkan Error] vkCreateSemaphore present: %s\n", vkTools::errorString(err)); return false; } @@ -284,7 +309,8 @@ bool GPU_Vulkan::initVulkan(bool enableValidation) { // Create a semaphore used to synchronize command submission // Ensures that the image is not presented until all commands have been sumbitted and executed err = vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &semaphores.renderDone); - if (err != VK_SUCCESS) { + if (err != VK_SUCCESS) + { fprintf(stderr, "\n[Vulkan Error] vkCreateSemaphore render: %s\n", vkTools::errorString(err)); return false; } @@ -292,18 +318,19 @@ bool GPU_Vulkan::initVulkan(bool enableValidation) { // Set up submit info structure // Semaphores will stay the same during application lifetime // Command buffer submission info is set by each example - submitInfo = vkTools::initializers::submitInfo(); - submitInfo.pWaitDstStageMask = &submitPipelineStages; - submitInfo.waitSemaphoreCount = 1; - submitInfo.pWaitSemaphores = &semaphores.presentDone; + submitInfo = vkTools::initializers::submitInfo(); + submitInfo.pWaitDstStageMask = &submitPipelineStages; + submitInfo.waitSemaphoreCount = 1; + submitInfo.pWaitSemaphores = &semaphores.presentDone; submitInfo.signalSemaphoreCount = 1; - submitInfo.pSignalSemaphores = &semaphores.renderDone; + submitInfo.pSignalSemaphores = &semaphores.renderDone; m_initOk = true; return true; } -void GPU_Vulkan::submitPostPresentBarrier(VkImage image) { +void GPU_Vulkan::submitPostPresentBarrier(VkImage image) +{ VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo(); VkResult vkRes = vkBeginCommandBuffer(postPresentCmdBuffer, &cmdBufInfo); @@ -312,32 +339,34 @@ void GPU_Vulkan::submitPostPresentBarrier(VkImage image) { assert(!vkRes); VkImageMemoryBarrier postPresentBarrier = vkTools::initializers::imageMemoryBarrier(); - postPresentBarrier.srcAccessMask = 0; - postPresentBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - postPresentBarrier.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - postPresentBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - postPresentBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - postPresentBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - postPresentBarrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; - postPresentBarrier.image = image; - - vkCmdPipelineBarrier( - postPresentCmdBuffer, - VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - 0, - 0, nullptr, // No memory barriers, - 0, nullptr, // No buffer barriers, - 1, &postPresentBarrier); + postPresentBarrier.srcAccessMask = 0; + postPresentBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + postPresentBarrier.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + postPresentBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + postPresentBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + postPresentBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + postPresentBarrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; + postPresentBarrier.image = image; + + vkCmdPipelineBarrier(postPresentCmdBuffer, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + 0, + 0, + nullptr, // No memory barriers, + 0, + nullptr, // No buffer barriers, + 1, + &postPresentBarrier); vkRes = vkEndCommandBuffer(postPresentCmdBuffer); if (vkRes != VK_SUCCESS) fprintf(stderr, "\n[Vulkan Error] vkEndCommandBuffer = %d\n", vkRes); assert(!vkRes); - VkSubmitInfo submitInfo = vkTools::initializers::submitInfo(); + VkSubmitInfo submitInfo = vkTools::initializers::submitInfo(); submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &postPresentCmdBuffer; + submitInfo.pCommandBuffers = &postPresentCmdBuffer; vkRes = vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE); if (vkRes != VK_SUCCESS) @@ -345,7 +374,8 @@ void GPU_Vulkan::submitPostPresentBarrier(VkImage image) { assert(!vkRes); } -void GPU_Vulkan::submitPrePresentBarrier(VkImage image) { +void GPU_Vulkan::submitPrePresentBarrier(VkImage image) +{ VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo(); VkResult vkRes = vkBeginCommandBuffer(prePresentCmdBuffer, &cmdBufInfo); @@ -354,32 +384,34 @@ void GPU_Vulkan::submitPrePresentBarrier(VkImage image) { assert(!vkRes); VkImageMemoryBarrier prePresentBarrier = vkTools::initializers::imageMemoryBarrier(); - prePresentBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - prePresentBarrier.dstAccessMask = 0; - prePresentBarrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - prePresentBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - prePresentBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - prePresentBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - prePresentBarrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; - prePresentBarrier.image = image; - - vkCmdPipelineBarrier( - prePresentCmdBuffer, - VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_FLAGS_NONE, - 0, nullptr, // No memory barriers, - 0, nullptr, // No buffer barriers, - 1, &prePresentBarrier); + prePresentBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + prePresentBarrier.dstAccessMask = 0; + prePresentBarrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + prePresentBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + prePresentBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + prePresentBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + prePresentBarrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; + prePresentBarrier.image = image; + + vkCmdPipelineBarrier(prePresentCmdBuffer, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_FLAGS_NONE, + 0, + nullptr, // No memory barriers, + 0, + nullptr, // No buffer barriers, + 1, + &prePresentBarrier); vkRes = vkEndCommandBuffer(prePresentCmdBuffer); if (vkRes != VK_SUCCESS) fprintf(stderr, "\n[Vulkan Error] vkEndCommandBuffer = %d\n", vkRes); assert(!vkRes); - VkSubmitInfo submitInfo = vkTools::initializers::submitInfo(); + VkSubmitInfo submitInfo = vkTools::initializers::submitInfo(); submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &prePresentCmdBuffer; + submitInfo.pCommandBuffers = &prePresentCmdBuffer; vkRes = vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE); if (vkRes != VK_SUCCESS) @@ -387,7 +419,8 @@ void GPU_Vulkan::submitPrePresentBarrier(VkImage image) { assert(!vkRes); } -void GPU_Vulkan::draw() { +void GPU_Vulkan::draw() +{ VkResult err; // Get next image in the swap chain (back/front buffer) @@ -398,7 +431,7 @@ void GPU_Vulkan::draw() { // Command buffer to be sumitted to the queue submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer]; + submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer]; // Submit to queue err = vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE); @@ -417,28 +450,27 @@ void GPU_Vulkan::draw() { assert(!err); } -void GPU_Vulkan::createCommandPool() { +void GPU_Vulkan::createCommandPool() +{ VkCommandPoolCreateInfo cmdPoolInfo = {}; - cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - cmdPoolInfo.queueFamilyIndex = swapChain.queueNodeIndex; - cmdPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - VkResult vkRes = vkCreateCommandPool(device, &cmdPoolInfo, nullptr, &cmdPool); + cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + cmdPoolInfo.queueFamilyIndex = swapChain.queueNodeIndex; + cmdPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + VkResult vkRes = vkCreateCommandPool(device, &cmdPoolInfo, nullptr, &cmdPool); if (vkRes != VK_SUCCESS) fprintf(stderr, "\n[Vulkan Error] vkQueueWaitIdle = %d\n", vkRes); assert(!vkRes); } -void GPU_Vulkan::createSetupCommandBuffer() { - if (setupCmdBuffer != VK_NULL_HANDLE) { +void GPU_Vulkan::createSetupCommandBuffer() +{ + if (setupCmdBuffer != VK_NULL_HANDLE) + { vkFreeCommandBuffers(device, cmdPool, 1, &setupCmdBuffer); setupCmdBuffer = VK_NULL_HANDLE; } - VkCommandBufferAllocateInfo cmdBufAllocateInfo = - vkTools::initializers::commandBufferAllocateInfo( - cmdPool, - VK_COMMAND_BUFFER_LEVEL_PRIMARY, - 1); + VkCommandBufferAllocateInfo cmdBufAllocateInfo = vkTools::initializers::commandBufferAllocateInfo(cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1); VkResult vkRes = vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, &setupCmdBuffer); if (vkRes != VK_SUCCESS) @@ -447,7 +479,7 @@ void GPU_Vulkan::createSetupCommandBuffer() { assert(!vkRes); VkCommandBufferBeginInfo cmdBufInfo = {}; - cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; vkRes = vkBeginCommandBuffer(setupCmdBuffer, &cmdBufInfo); if (vkRes != VK_SUCCESS) @@ -456,16 +488,14 @@ void GPU_Vulkan::createSetupCommandBuffer() { assert(!vkRes); } -void GPU_Vulkan::createCommandBuffers() { +void GPU_Vulkan::createCommandBuffers() +{ // Create one command buffer per frame buffer in the swap chain drawCmdBuffers.resize(swapChain.m_imageCount); VkCommandBufferAllocateInfo cmdBufAllocateInfo = - vkTools::initializers::commandBufferAllocateInfo( - cmdPool, - VK_COMMAND_BUFFER_LEVEL_PRIMARY, - (uint32_t)drawCmdBuffers.size()); + vkTools::initializers::commandBufferAllocateInfo(cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, (uint32_t)drawCmdBuffers.size()); VkResult vkRes = vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, drawCmdBuffers.data()); if (vkRes != VK_SUCCESS) @@ -473,10 +503,14 @@ void GPU_Vulkan::createCommandBuffers() { assert(!vkRes); } -VkBool32 GPU_Vulkan::getMemoryType(uint32_t typeBits, VkFlags properties, uint32_t * typeIndex) { - for (uint32_t i = 0; i < 32; i++) { - if ((typeBits & 1) == 1) { - if ((deviceMemoryProperties.memoryTypes[i].propertyFlags & properties) == properties) { +VkBool32 GPU_Vulkan::getMemoryType(uint32_t typeBits, VkFlags properties, uint32_t* typeIndex) +{ + for (uint32_t i = 0; i < 32; i++) + { + if ((typeBits & 1) == 1) + { + if ((deviceMemoryProperties.memoryTypes[i].propertyFlags & properties) == properties) + { *typeIndex = i; return true; } @@ -486,41 +520,42 @@ VkBool32 GPU_Vulkan::getMemoryType(uint32_t typeBits, VkFlags properties, uint32 return false; } -void GPU_Vulkan::setupDepthStencil() { +void GPU_Vulkan::setupDepthStencil() +{ VkImageCreateInfo image = {}; - image.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - image.pNext = NULL; - image.imageType = VK_IMAGE_TYPE_2D; - image.format = depthFormat; - image.extent = { m_width, m_height, 1 }; - image.mipLevels = 1; - image.arrayLayers = 1; - image.samples = VK_SAMPLE_COUNT_1_BIT; - image.tiling = VK_IMAGE_TILING_OPTIMAL; - image.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - image.flags = 0; + image.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + image.pNext = NULL; + image.imageType = VK_IMAGE_TYPE_2D; + image.format = depthFormat; + image.extent = {m_width, m_height, 1}; + image.mipLevels = 1; + image.arrayLayers = 1; + image.samples = VK_SAMPLE_COUNT_1_BIT; + image.tiling = VK_IMAGE_TILING_OPTIMAL; + image.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + image.flags = 0; VkMemoryAllocateInfo mem_alloc = {}; - mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - mem_alloc.pNext = NULL; - mem_alloc.allocationSize = 0; - mem_alloc.memoryTypeIndex = 0; - - VkImageViewCreateInfo depthStencilView = {}; - depthStencilView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - depthStencilView.pNext = NULL; - depthStencilView.viewType = VK_IMAGE_VIEW_TYPE_2D; - depthStencilView.format = depthFormat; - depthStencilView.flags = 0; - depthStencilView.subresourceRange = {}; - depthStencilView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; - depthStencilView.subresourceRange.baseMipLevel = 0; - depthStencilView.subresourceRange.levelCount = 1; + mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + mem_alloc.pNext = NULL; + mem_alloc.allocationSize = 0; + mem_alloc.memoryTypeIndex = 0; + + VkImageViewCreateInfo depthStencilView = {}; + depthStencilView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + depthStencilView.pNext = NULL; + depthStencilView.viewType = VK_IMAGE_VIEW_TYPE_2D; + depthStencilView.format = depthFormat; + depthStencilView.flags = 0; + depthStencilView.subresourceRange = {}; + depthStencilView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + depthStencilView.subresourceRange.baseMipLevel = 0; + depthStencilView.subresourceRange.levelCount = 1; depthStencilView.subresourceRange.baseArrayLayer = 0; - depthStencilView.subresourceRange.layerCount = 1; + depthStencilView.subresourceRange.layerCount = 1; VkMemoryRequirements memReqs; - VkResult err; + VkResult err; err = vkCreateImage(device, &image, nullptr, &depthStencil.image); if (err != VK_SUCCESS) @@ -540,80 +575,88 @@ void GPU_Vulkan::setupDepthStencil() { assert(!err); depthStencilView.image = depthStencil.image; - err = vkCreateImageView(device, &depthStencilView, nullptr, &depthStencil.view); + err = vkCreateImageView(device, &depthStencilView, nullptr, &depthStencil.view); if (err != VK_SUCCESS) fprintf(stderr, "\n[Vulkan Error] vkCreateImageView = %d\n", err); assert(!err); } -void GPU_Vulkan::setupRenderPass() { +void GPU_Vulkan::setupRenderPass() +{ + // Color buffer format + VkFormat colorformat = VK_FORMAT_B8G8R8A8_UNORM; + + if ((m_pSourceTexture->format == CMP_FORMAT_BC4_S) || (m_pSourceTexture->format == CMP_FORMAT_BC5_S) || + (m_pSourceTexture->format == CMP_FORMAT_RGBA_8888_S)) + colorformat = VK_FORMAT_B8G8R8A8_SNORM; + VkAttachmentDescription attachments[2]; - attachments[0].format = colorformat; - attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; - attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[0].format = colorformat; + attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - - attachments[1].format = depthFormat; - attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; - attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + attachments[1].format = depthFormat; + attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; VkAttachmentReference colorReference = {}; - colorReference.attachment = 0; - colorReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + colorReference.attachment = 0; + colorReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; VkAttachmentReference depthReference = {}; - depthReference.attachment = 1; - depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - VkSubpassDescription subpass = {}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.flags = 0; - subpass.inputAttachmentCount = 0; - subpass.pInputAttachments = NULL; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &colorReference; - subpass.pResolveAttachments = NULL; + depthReference.attachment = 1; + depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.flags = 0; + subpass.inputAttachmentCount = 0; + subpass.pInputAttachments = NULL; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &colorReference; + subpass.pResolveAttachments = NULL; subpass.pDepthStencilAttachment = &depthReference; subpass.preserveAttachmentCount = 0; - subpass.pPreserveAttachments = NULL; + subpass.pPreserveAttachments = NULL; // Subpass dependencies for layout transitions std::array dependencies; - dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; - dependencies[0].dstSubpass = 0; - dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; - dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; + dependencies[0].dstSubpass = 0; + dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; + dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; - dependencies[1].srcSubpass = 0; - dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; - dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; + dependencies[1].srcSubpass = 0; + dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; + dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; VkRenderPassCreateInfo renderPassInfo = {}; - renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderPassInfo.pNext = NULL; - renderPassInfo.attachmentCount = 2; - renderPassInfo.pAttachments = attachments; - renderPassInfo.subpassCount = 1; - renderPassInfo.pSubpasses = &subpass; - renderPassInfo.dependencyCount = 2; - renderPassInfo.pDependencies = dependencies.data(); + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassInfo.pNext = NULL; + renderPassInfo.attachmentCount = 2; + renderPassInfo.pAttachments = attachments; + renderPassInfo.subpassCount = 1; + renderPassInfo.pSubpasses = &subpass; + renderPassInfo.dependencyCount = 2; + renderPassInfo.pDependencies = dependencies.data(); VkResult err; @@ -623,43 +666,47 @@ void GPU_Vulkan::setupRenderPass() { assert(!err); } -void GPU_Vulkan::createPipelineCache() { +void GPU_Vulkan::createPipelineCache() +{ VkPipelineCacheCreateInfo pipelineCacheCreateInfo = {}; - pipelineCacheCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; - VkResult err = vkCreatePipelineCache(device, &pipelineCacheCreateInfo, nullptr, &pipelineCache); + pipelineCacheCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; + VkResult err = vkCreatePipelineCache(device, &pipelineCacheCreateInfo, nullptr, &pipelineCache); if (err != VK_SUCCESS) fprintf(stderr, "\n[Vulkan Error] vkCreatePipelineCache = %d\n", err); assert(!err); } -void GPU_Vulkan::setupFrameBuffer() { +void GPU_Vulkan::setupFrameBuffer() +{ VkImageView attachments[2]; // Depth/Stencil attachment is the same for all frame buffers attachments[1] = depthStencil.view; VkFramebufferCreateInfo frameBufferCreateInfo = {}; - frameBufferCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - frameBufferCreateInfo.pNext = NULL; - frameBufferCreateInfo.renderPass = renderPass; - frameBufferCreateInfo.attachmentCount = 2; - frameBufferCreateInfo.pAttachments = attachments; - frameBufferCreateInfo.width = m_width; - frameBufferCreateInfo.height = m_height; - frameBufferCreateInfo.layers = 1; + frameBufferCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + frameBufferCreateInfo.pNext = NULL; + frameBufferCreateInfo.renderPass = renderPass; + frameBufferCreateInfo.attachmentCount = 2; + frameBufferCreateInfo.pAttachments = attachments; + frameBufferCreateInfo.width = m_width; + frameBufferCreateInfo.height = m_height; + frameBufferCreateInfo.layers = 1; // Create frame buffers for every swap chain image frameBuffers.resize(swapChain.m_imageCount); - for (uint32_t i = 0; i < frameBuffers.size(); i++) { + for (uint32_t i = 0; i < frameBuffers.size(); i++) + { attachments[0] = swapChain.buffers[i].view; - VkResult err = vkCreateFramebuffer(device, &frameBufferCreateInfo, nullptr, &frameBuffers[i]); + VkResult err = vkCreateFramebuffer(device, &frameBufferCreateInfo, nullptr, &frameBuffers[i]); if (err != VK_SUCCESS) fprintf(stderr, "\n[Vulkan Error] vkCreateFramebuffer = %d\n", err); assert(!err); } } -void GPU_Vulkan::flushSetupCommandBuffer() { +void GPU_Vulkan::flushSetupCommandBuffer() +{ VkResult err; if (setupCmdBuffer == VK_NULL_HANDLE) @@ -670,10 +717,10 @@ void GPU_Vulkan::flushSetupCommandBuffer() { fprintf(stderr, "\n[Vulkan Error] vkEndCommandBuffer = %d\n", err); assert(!err); - VkSubmitInfo submitInfo = {}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + VkSubmitInfo submitInfo = {}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &setupCmdBuffer; + submitInfo.pCommandBuffers = &setupCmdBuffer; err = vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE); if (err != VK_SUCCESS) @@ -689,15 +736,11 @@ void GPU_Vulkan::flushSetupCommandBuffer() { setupCmdBuffer = VK_NULL_HANDLE; } -VkBool32 GPU_Vulkan::createBuffer( - VkBufferUsageFlags usage, - VkDeviceSize size, - void * data, - VkBuffer *buffer, - VkDeviceMemory *memory) { +VkBool32 GPU_Vulkan::createBuffer(VkBufferUsageFlags usage, VkDeviceSize size, void* data, VkBuffer* buffer, VkDeviceMemory* memory) +{ VkMemoryRequirements memReqs; - VkMemoryAllocateInfo memAlloc = vkTools::initializers::memoryAllocateInfo(); - VkBufferCreateInfo bufferCreateInfo = vkTools::initializers::bufferCreateInfo(usage, size); + VkMemoryAllocateInfo memAlloc = vkTools::initializers::memoryAllocateInfo(); + VkBufferCreateInfo bufferCreateInfo = vkTools::initializers::bufferCreateInfo(usage, size); VkResult err = vkCreateBuffer(device, &bufferCreateInfo, nullptr, buffer); if (err != VK_SUCCESS) @@ -710,13 +753,14 @@ VkBool32 GPU_Vulkan::createBuffer( if (err != VK_SUCCESS) fprintf(stderr, "\n[Vulkan Error] vkAllocateMemory = %d\n", err); assert(!err); - if (data != nullptr) { - void *mapped; + if (data != nullptr) + { + void* mapped; err = vkMapMemory(device, *memory, 0, size, 0, &mapped); if (err != VK_SUCCESS) fprintf(stderr, "\n[Vulkan Error] vkMapMemory = %d\n", err); assert(!err); - memcpy(mapped, data,(std::size_t) size); + memcpy(mapped, data, (std::size_t)size); vkUnmapMemory(device, *memory); } err = vkBindBufferMemory(device, *buffer, *memory, 0); @@ -726,22 +770,32 @@ VkBool32 GPU_Vulkan::createBuffer( return true; } -VkBool32 GPU_Vulkan::createBuffer(VkBufferUsageFlags usage, VkDeviceSize size, void * data, VkBuffer * buffer, VkDeviceMemory * memory, VkDescriptorBufferInfo * descriptor) { +VkBool32 GPU_Vulkan::createBuffer(VkBufferUsageFlags usage, + VkDeviceSize size, + void* data, + VkBuffer* buffer, + VkDeviceMemory* memory, + VkDescriptorBufferInfo* descriptor) +{ VkBool32 res = createBuffer(usage, size, data, buffer, memory); - if (res) { + if (res) + { descriptor->offset = 0; descriptor->buffer = *buffer; - descriptor->range = size; + descriptor->range = size; return true; - } else { + } + else + { return false; } } -void GPU_Vulkan::updateUniformBuffers() { +void GPU_Vulkan::updateUniformBuffers() +{ // Vertex shader- use default - uint8_t *pData; - VkResult err = vkMapMemory(device, uniformDataVS.memory, 0, sizeof(uboVS), 0, (void **)&pData); + uint8_t* pData; + VkResult err = vkMapMemory(device, uniformDataVS.memory, 0, sizeof(uboVS), 0, (void**)&pData); if (err != VK_SUCCESS) fprintf(stderr, "\n[Vulkan Error] vkMapMemory = %d\n", err); assert(!err); @@ -750,68 +804,72 @@ void GPU_Vulkan::updateUniformBuffers() { } // Create an image memory barrier for changing the layout of an image and put it into an active command buffer -void GPU_Vulkan::setImageLayout(VkImage image, VkImageAspectFlags aspectMask, VkImageLayout oldImageLayout, VkImageLayout newImageLayout) { +void GPU_Vulkan::setImageLayout(VkImage image, VkImageAspectFlags aspectMask, VkImageLayout oldImageLayout, VkImageLayout newImageLayout) +{ // Create an image barrier object - VkImageMemoryBarrier imageMemoryBarrier = vkTools::initializers::imageMemoryBarrier();; - imageMemoryBarrier.oldLayout = oldImageLayout; - imageMemoryBarrier.newLayout = newImageLayout; - imageMemoryBarrier.image = image; - imageMemoryBarrier.subresourceRange.aspectMask = aspectMask; + VkImageMemoryBarrier imageMemoryBarrier = vkTools::initializers::imageMemoryBarrier(); + ; + imageMemoryBarrier.oldLayout = oldImageLayout; + imageMemoryBarrier.newLayout = newImageLayout; + imageMemoryBarrier.image = image; + imageMemoryBarrier.subresourceRange.aspectMask = aspectMask; imageMemoryBarrier.subresourceRange.baseMipLevel = 0; - imageMemoryBarrier.subresourceRange.levelCount = 1; - imageMemoryBarrier.subresourceRange.layerCount = 1; + imageMemoryBarrier.subresourceRange.levelCount = 1; + imageMemoryBarrier.subresourceRange.layerCount = 1; // New layout is transfer destination (copy, blit) // Make sure any reads from and writes to the image have been finished - if (newImageLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + if (newImageLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) + { imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT; } // New layout is shader read (sampler, input attachment) // Make sure any writes to the image have been finished - if (newImageLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + if (newImageLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) + { imageMemoryBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT; imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; } // New layout is transfer source // Make sure any reads from and writes to the image have been finished - if (newImageLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { + if (newImageLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) + { imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT; } // Put barrier on top - VkPipelineStageFlags srcStageFlags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + VkPipelineStageFlags srcStageFlags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; VkPipelineStageFlags destStageFlags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; // Put barrier inside setup command buffer - vkCmdPipelineBarrier( - setupCmdBuffer, - srcStageFlags, - destStageFlags, - VK_FLAGS_NONE, - 0, nullptr, - 0, nullptr, - 1, &imageMemoryBarrier); + vkCmdPipelineBarrier(setupCmdBuffer, srcStageFlags, destStageFlags, VK_FLAGS_NONE, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); } - - // Create an image memory barrier for changing the layout of // an image and put it into an active command buffer -void GPU_Vulkan::setImageLayout(VkCommandBuffer cmdBuffer, VkImage image, VkImageAspectFlags aspectMask, VkImageLayout oldImageLayout, VkImageLayout newImageLayout, VkImageSubresourceRange subresourceRange) { +void GPU_Vulkan::setImageLayout(VkCommandBuffer cmdBuffer, + VkImage image, + VkImageAspectFlags aspectMask, + VkImageLayout oldImageLayout, + VkImageLayout newImageLayout, + VkImageSubresourceRange subresourceRange) +{ // Create an image barrier object - VkImageMemoryBarrier imageMemoryBarrier = vkTools::initializers::imageMemoryBarrier();; - imageMemoryBarrier.oldLayout = oldImageLayout; - imageMemoryBarrier.newLayout = newImageLayout; - imageMemoryBarrier.image = image; + VkImageMemoryBarrier imageMemoryBarrier = vkTools::initializers::imageMemoryBarrier(); + ; + imageMemoryBarrier.oldLayout = oldImageLayout; + imageMemoryBarrier.newLayout = newImageLayout; + imageMemoryBarrier.image = image; imageMemoryBarrier.subresourceRange = subresourceRange; // Only sets masks for layouts used in this example // For a more complete version that can be used with other layouts see vkTools::setImageLayout // Source layouts (old) - switch (oldImageLayout) { + switch (oldImageLayout) + { case VK_IMAGE_LAYOUT_UNDEFINED: // Only valid as initial layout, memory contents are not preserved // Can be accessed directly, no source dependency required @@ -830,7 +888,8 @@ void GPU_Vulkan::setImageLayout(VkCommandBuffer cmdBuffer, VkImage image, VkImag } // Target layouts (new) - switch (newImageLayout) { + switch (newImageLayout) + { case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: // Transfer source (copy, blit) // Make sure any reads from the image have been finished @@ -848,25 +907,18 @@ void GPU_Vulkan::setImageLayout(VkCommandBuffer cmdBuffer, VkImage image, VkImag } // Put barrier on top of pipeline - VkPipelineStageFlags srcStageFlags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + VkPipelineStageFlags srcStageFlags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; VkPipelineStageFlags destStageFlags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; // Put barrier inside setup command buffer - vkCmdPipelineBarrier( - cmdBuffer, - srcStageFlags, - destStageFlags, - VK_FLAGS_NONE, - 0, nullptr, - 0, nullptr, - 1, &imageMemoryBarrier); + vkCmdPipelineBarrier(cmdBuffer, srcStageFlags, destStageFlags, VK_FLAGS_NONE, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); } - - -VkFormat GPU_Vulkan::MIP2VK_Format(const CMP_Texture* pSourceTexture) { +VkFormat GPU_Vulkan::MIP2VK_Format(const CMP_Texture* pSourceTexture) +{ VkFormat m_VKnum; - switch (pSourceTexture->format) { + switch (pSourceTexture->format) + { case CMP_FORMAT_BC1: case CMP_FORMAT_DXT1: m_VKnum = VK_FORMAT_BC1_RGB_UNORM_BLOCK; @@ -962,38 +1014,39 @@ VkFormat GPU_Vulkan::MIP2VK_Format(const CMP_Texture* pSourceTexture) { return m_VKnum; } -CMP_ERROR GPU_Vulkan::loadTexture(const CMP_Texture* pSourceTexture) { +CMP_ERROR GPU_Vulkan::loadTexture(const CMP_Texture* pSourceTexture) +{ VkResult res; VkFormat format = MIP2VK_Format(pSourceTexture); - if (format == VK_FORMAT_UNDEFINED) return(CMP_ERR_UNSUPPORTED_SOURCE_FORMAT); - + if (format == VK_FORMAT_UNDEFINED) + return (CMP_ERR_UNSUPPORTED_SOURCE_FORMAT); VkFormatProperties formatProperties; - texture.width = static_cast(pSourceTexture->dwWidth); - texture.height = static_cast(pSourceTexture->dwHeight); + texture.width = static_cast(pSourceTexture->dwWidth); + texture.height = static_cast(pSourceTexture->dwHeight); texture.mipLevels = 1; // Get device properites for the requested texture format vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProperties); VkMemoryAllocateInfo memAllocInfo = vkTools::initializers::memoryAllocateInfo(); - VkMemoryRequirements memReqs = {}; - + VkMemoryRequirements memReqs = {}; { // Create a host-visible staging buffer that contains the raw image data - VkBuffer stagingBuffer; + VkBuffer stagingBuffer; VkDeviceMemory stagingMemory; // This buffer is used as a transfer source for the buffer copy VkBufferCreateInfo bufferCreateInfo = vkTools::initializers::bufferCreateInfo(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, pSourceTexture->dwDataSize); - bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; res = vkCreateBuffer(device, &bufferCreateInfo, nullptr, &stagingBuffer); - if (res != VK_SUCCESS) { - return(CMP_ERR_GENERIC); + if (res != VK_SUCCESS) + { + return (CMP_ERR_GENERIC); } // Get memory requirements for the staging buffer (alignment, memory type bits) @@ -1003,22 +1056,24 @@ CMP_ERROR GPU_Vulkan::loadTexture(const CMP_Texture* pSourceTexture) { // Get memory type that can be mapped to host memory getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &memAllocInfo.memoryTypeIndex); - res = vkAllocateMemory(device, &memAllocInfo, nullptr, &stagingMemory); - if (res != VK_SUCCESS) { - return(CMP_ERR_GENERIC); + if (res != VK_SUCCESS) + { + return (CMP_ERR_GENERIC); } res = vkBindBufferMemory(device, stagingBuffer, stagingMemory, 0); - if (res != VK_SUCCESS) { - return(CMP_ERR_GENERIC); + if (res != VK_SUCCESS) + { + return (CMP_ERR_GENERIC); } // Copy texture data into staging buffer - uint8_t *data; - res = vkMapMemory(device, stagingMemory, 0, memReqs.size, 0, (void **)&data); - if (res != VK_SUCCESS) { - return(CMP_ERR_GENERIC); + uint8_t* data; + res = vkMapMemory(device, stagingMemory, 0, memReqs.size, 0, (void**)&data); + if (res != VK_SUCCESS) + { + return (CMP_ERR_GENERIC); } memcpy(data, pSourceTexture->pData, pSourceTexture->dwDataSize); @@ -1028,36 +1083,36 @@ CMP_ERROR GPU_Vulkan::loadTexture(const CMP_Texture* pSourceTexture) { // Setup buffer copy regions for each mip level // Can loop this section of code for miplevels { - VkBufferImageCopy bufferCopyRegion = {}; - bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - bufferCopyRegion.imageSubresource.mipLevel = 0; + VkBufferImageCopy bufferCopyRegion = {}; + bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + bufferCopyRegion.imageSubresource.mipLevel = 0; bufferCopyRegion.imageSubresource.baseArrayLayer = 0; - bufferCopyRegion.imageSubresource.layerCount = 1; - bufferCopyRegion.imageExtent.width = static_cast(pSourceTexture->dwWidth); - bufferCopyRegion.imageExtent.height = static_cast(pSourceTexture->dwHeight); - bufferCopyRegion.imageExtent.depth = 1; - bufferCopyRegion.bufferOffset = 0; + bufferCopyRegion.imageSubresource.layerCount = 1; + bufferCopyRegion.imageExtent.width = static_cast(pSourceTexture->dwWidth); + bufferCopyRegion.imageExtent.height = static_cast(pSourceTexture->dwHeight); + bufferCopyRegion.imageExtent.depth = 1; + bufferCopyRegion.bufferOffset = 0; bufferCopyRegions.push_back(bufferCopyRegion); } // Create optimal tiled target image VkImageCreateInfo imageCreateInfo = vkTools::initializers::imageCreateInfo(); - imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; - imageCreateInfo.format = format; - imageCreateInfo.mipLevels = texture.mipLevels; - imageCreateInfo.arrayLayers = 1; - imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; - imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - imageCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT; - imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - // Set initial layout of the image to undefined - imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - imageCreateInfo.extent = { texture.width, texture.height, 1 }; - imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; + imageCreateInfo.format = format; + imageCreateInfo.mipLevels = texture.mipLevels; + imageCreateInfo.arrayLayers = 1; + imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT; + imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; //VK_IMAGE_LAYOUT_PREINITIALIZED + imageCreateInfo.extent = {texture.width, texture.height, 1}; + imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; res = vkCreateImage(device, &imageCreateInfo, nullptr, &texture.image); - if (res != VK_SUCCESS) { - return(CMP_ERR_GENERIC); + if (res != VK_SUCCESS) + { + return (CMP_ERR_GENERIC); } vkGetImageMemoryRequirements(device, texture.image, &memReqs); @@ -1065,15 +1120,16 @@ CMP_ERROR GPU_Vulkan::loadTexture(const CMP_Texture* pSourceTexture) { memAllocInfo.allocationSize = memReqs.size; getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memAllocInfo.memoryTypeIndex); - res = vkAllocateMemory(device, &memAllocInfo, nullptr, &texture.deviceMemory); - if (res != VK_SUCCESS) { - return(CMP_ERR_GENERIC); + if (res != VK_SUCCESS) + { + return (CMP_ERR_GENERIC); } res = vkBindImageMemory(device, texture.image, texture.deviceMemory, 0); - if (res != VK_SUCCESS) { - return(CMP_ERR_GENERIC); + if (res != VK_SUCCESS) + { + return (CMP_ERR_GENERIC); } VkCommandBuffer copyCmd = createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); @@ -1093,32 +1149,24 @@ CMP_ERROR GPU_Vulkan::loadTexture(const CMP_Texture* pSourceTexture) { // Optimal image will be used as destination for the copy, so we must transfer from our // initial undefined image layout to the transfer destination layout - setImageLayout( - copyCmd, - texture.image, - VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - subresourceRange); + setImageLayout(copyCmd, + texture.image, + VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, // VK_IMAGE_LAYOUT_PREINITIALIZED, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + subresourceRange); // Copy mip levels from staging buffer - vkCmdCopyBufferToImage( - copyCmd, - stagingBuffer, - texture.image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - static_cast(bufferCopyRegions.size()), - bufferCopyRegions.data()); + vkCmdCopyBufferToImage(copyCmd, + stagingBuffer, + texture.image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + static_cast(bufferCopyRegions.size()), + bufferCopyRegions.data()); // Change texture image layout to shader read after all mip levels have been copied texture.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - setImageLayout( - copyCmd, - texture.image, - VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - texture.imageLayout, - subresourceRange); + setImageLayout(copyCmd, texture.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, texture.imageLayout, subresourceRange); flushCommandBuffer(copyCmd, queue, true); @@ -1135,23 +1183,24 @@ CMP_ERROR GPU_Vulkan::loadTexture(const CMP_Texture* pSourceTexture) { // for the same texture with different settings // Similar to the samplers available with OpenGL 3.3 VkSamplerCreateInfo sampler = vkTools::initializers::samplerCreateInfo(); - sampler.magFilter = VK_FILTER_LINEAR; - sampler.minFilter = VK_FILTER_LINEAR; - sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; - sampler.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; - sampler.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; - sampler.mipLodBias = 0.0f; - sampler.compareOp = VK_COMPARE_OP_NEVER; - sampler.minLod = 0.0f; + sampler.magFilter = VK_FILTER_LINEAR; + sampler.minFilter = VK_FILTER_LINEAR; + sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler.mipLodBias = 0.0f; + sampler.compareOp = VK_COMPARE_OP_NEVER; + sampler.minLod = 0.0f; // Set max level-of-detail to mip level count of the texture - sampler.maxLod = (float)texture.mipLevels; - sampler.maxAnisotropy = 1.0; + sampler.maxLod = (float)texture.mipLevels; + sampler.maxAnisotropy = 1.0; // anisotropic filtering sampler.anisotropyEnable = VK_FALSE; - sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; - res = vkCreateSampler(device, &sampler, nullptr, &texture.sampler); - if (res != VK_SUCCESS) { - return(CMP_ERR_GENERIC); + sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; + res = vkCreateSampler(device, &sampler, nullptr, &texture.sampler); + if (res != VK_SUCCESS) + { + return (CMP_ERR_GENERIC); } // Create image view @@ -1159,43 +1208,42 @@ CMP_ERROR GPU_Vulkan::loadTexture(const CMP_Texture* pSourceTexture) { // are abstracted by image views containing additional // information and sub resource ranges VkImageViewCreateInfo view = vkTools::initializers::imageViewCreateInfo(); - view.image = VK_NULL_HANDLE; - view.viewType = VK_IMAGE_VIEW_TYPE_2D; - view.format = format; - view.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; + view.image = VK_NULL_HANDLE; + view.viewType = VK_IMAGE_VIEW_TYPE_2D; + view.format = format; + view.components = {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A}; // The subresource range describes the set of mip levels (and array layers) that can be accessed through this image view // It's possible to create multiple image views for a single image referring to different (and/or overlapping) ranges of the image - view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - view.subresourceRange.baseMipLevel = 0; + view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + view.subresourceRange.baseMipLevel = 0; view.subresourceRange.baseArrayLayer = 0; - view.subresourceRange.layerCount = 1; + view.subresourceRange.layerCount = 1; // Linear tiling usually won't support mip maps // Only set mip map count if optimal tiling is used view.subresourceRange.levelCount = texture.mipLevels; - view.image = texture.image; - res = vkCreateImageView(device, &view, nullptr, &texture.view); - if (res != VK_SUCCESS) { - return(CMP_ERR_GENERIC); + view.image = texture.image; + res = vkCreateImageView(device, &view, nullptr, &texture.view); + if (res != VK_SUCCESS) + { + return (CMP_ERR_GENERIC); } - return(CMP_OK); + return (CMP_OK); } //============================================================== -VkCommandBuffer GPU_Vulkan::createCommandBuffer(VkCommandBufferLevel level, bool begin) { +VkCommandBuffer GPU_Vulkan::createCommandBuffer(VkCommandBufferLevel level, bool begin) +{ VkCommandBuffer cmdBuffer; - VkCommandBufferAllocateInfo cmdBufAllocateInfo = - vkTools::initializers::commandBufferAllocateInfo( - cmdPool, - level, - 1); + VkCommandBufferAllocateInfo cmdBufAllocateInfo = vkTools::initializers::commandBufferAllocateInfo(cmdPool, level, 1); VK_CHECK_RESULT_ERR_PTR(vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, &cmdBuffer)); // If requested, also start the new command buffer - if (begin) { + if (begin) + { VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo(); VK_CHECK_RESULT_ERR_PTR(vkBeginCommandBuffer(cmdBuffer, &cmdBufInfo)); } @@ -1203,22 +1251,24 @@ VkCommandBuffer GPU_Vulkan::createCommandBuffer(VkCommandBufferLevel level, bool return cmdBuffer; } -void GPU_Vulkan::flushCommandBuffer(VkCommandBuffer commandBuffer, VkQueue queue, bool free = true) { - if (commandBuffer == VK_NULL_HANDLE) { +void GPU_Vulkan::flushCommandBuffer(VkCommandBuffer commandBuffer, VkQueue queue, bool free = true) +{ + if (commandBuffer == VK_NULL_HANDLE) + { return; } VK_CHECK_RESULT(vkEndCommandBuffer(commandBuffer)); - VkSubmitInfo submitInfo = {}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + VkSubmitInfo submitInfo = {}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &commandBuffer; + submitInfo.pCommandBuffers = &commandBuffer; // Create fence to ensure that the command buffer has finished executing VkFenceCreateInfo fenceInfo = vkTools::initializers::fenceCreateInfo(VK_FLAGS_NONE); - VkFence fence; - VkResult vkRes = vkCreateFence(device, &fenceInfo, nullptr, &fence); + VkFence fence; + VkResult vkRes = vkCreateFence(device, &fenceInfo, nullptr, &fence); if (vkRes != VK_SUCCESS) fprintf(stderr, "\n[Vulkan Error] vkCreateFence = %d\n", vkRes); assert(!vkRes); @@ -1237,19 +1287,21 @@ void GPU_Vulkan::flushCommandBuffer(VkCommandBuffer commandBuffer, VkQueue queue vkDestroyFence(device, fence, nullptr); - if (free) { + if (free) + { vkFreeCommandBuffers(device, cmdPool, 1, &commandBuffer); } } //============================================================== -VkPipelineShaderStageCreateInfo GPU_Vulkan::loadShader(const char * fileName, VkShaderStageFlagBits stage) { +VkPipelineShaderStageCreateInfo GPU_Vulkan::loadShader(const char* fileName, VkShaderStageFlagBits stage) +{ VkPipelineShaderStageCreateInfo shaderStage = {}; - shaderStage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - shaderStage.stage = stage; - shaderStage.module = vkTools::loadShader(fileName, device, stage); - shaderStage.pName = "main"; + shaderStage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shaderStage.stage = stage; + shaderStage.module = vkTools::loadShader(fileName, device, stage); + shaderStage.pName = "main"; if (shaderStage.module == NULL) fprintf(stderr, "\n[Vulkan Error] shaderStage.module is NULL\n"); assert(shaderStage.module != NULL); @@ -1259,8 +1311,10 @@ VkPipelineShaderStageCreateInfo GPU_Vulkan::loadShader(const char * fileName, Vk // ToDo remove asserts and replace with cmp_res -CMP_ERROR GPU_Vulkan::prepare(const CMP_Texture* pSourceTexture) { - if (m_tenableValidation) { +CMP_ERROR GPU_Vulkan::prepare(const CMP_Texture* pSourceTexture) +{ + if (m_tenableValidation) + { vkDebug::setupDebugging(instance, VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT, NULL); } @@ -1275,98 +1329,66 @@ CMP_ERROR GPU_Vulkan::prepare(const CMP_Texture* pSourceTexture) { // Setup vertices for a single uv-mapped quad #define dim 1.0f std::vector vertexBuffer = { - { { dim, dim, 0.0f },{ 1.0f, 1.0f } }, - { { -dim, dim, 0.0f },{ 0.0f, 1.0f } }, - { { -dim, -dim, 0.0f },{ 0.0f, 0.0f } }, - { { dim, -dim, 0.0f },{ 1.0f, 0.0f } } - }; + {{dim, dim, 0.0f}, {1.0f, 1.0f}}, {{-dim, dim, 0.0f}, {0.0f, 1.0f}}, {{-dim, -dim, 0.0f}, {0.0f, 0.0f}}, {{dim, -dim, 0.0f}, {1.0f, 0.0f}}}; #undef dim - createBuffer( - VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, - vertexBuffer.size() * sizeof(Vertex), - vertexBuffer.data(), - &vertices.buffer, - &vertices.memory); + createBuffer(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, vertexBuffer.size() * sizeof(Vertex), vertexBuffer.data(), &vertices.buffer, &vertices.memory); // Setup indices - std::vector indexBuffer = { 0,1,2, 2,3,0 }; - indices.count = (int)indexBuffer.size(); + std::vector indexBuffer = {0, 1, 2, 2, 3, 0}; + indices.count = (int)indexBuffer.size(); - createBuffer( - VK_BUFFER_USAGE_INDEX_BUFFER_BIT, - indexBuffer.size() * sizeof(uint32_t), - indexBuffer.data(), - &indices.buffer, - &indices.memory); + createBuffer(VK_BUFFER_USAGE_INDEX_BUFFER_BIT, indexBuffer.size() * sizeof(uint32_t), indexBuffer.data(), &indices.buffer, &indices.memory); // setupVertexDescriptions // Binding description vertices.bindingDescriptions.resize(1); - vertices.bindingDescriptions[0] = - vkTools::initializers::vertexInputBindingDescription( - 0, //vertex buffer bind id - sizeof(Vertex), - VK_VERTEX_INPUT_RATE_VERTEX); + vertices.bindingDescriptions[0] = vkTools::initializers::vertexInputBindingDescription(0, //vertex buffer bind id + sizeof(Vertex), + VK_VERTEX_INPUT_RATE_VERTEX); // Attribute descriptions // Describes memory layout and shader positions vertices.attributeDescriptions.resize(2); // Location 0 : Position - vertices.attributeDescriptions[0] = - vkTools::initializers::vertexInputAttributeDescription( - 0,//vertex buffer bind id - 0, - VK_FORMAT_R32G32B32_SFLOAT, - 0); + vertices.attributeDescriptions[0] = vkTools::initializers::vertexInputAttributeDescription(0, //vertex buffer bind id + 0, + VK_FORMAT_R32G32B32_SFLOAT, + 0); // Location 1 : Texture coordinates - vertices.attributeDescriptions[1] = - vkTools::initializers::vertexInputAttributeDescription( - 0, //vertex buffer bind id - 1, - VK_FORMAT_R32G32_SFLOAT, - sizeof(float) * 3); - - vertices.inputState = vkTools::initializers::pipelineVertexInputStateCreateInfo(); - vertices.inputState.vertexBindingDescriptionCount = (uint32_t) vertices.bindingDescriptions.size(); - vertices.inputState.pVertexBindingDescriptions = vertices.bindingDescriptions.data(); + vertices.attributeDescriptions[1] = vkTools::initializers::vertexInputAttributeDescription(0, //vertex buffer bind id + 1, + VK_FORMAT_R32G32_SFLOAT, + sizeof(float) * 3); + + vertices.inputState = vkTools::initializers::pipelineVertexInputStateCreateInfo(); + vertices.inputState.vertexBindingDescriptionCount = (uint32_t)vertices.bindingDescriptions.size(); + vertices.inputState.pVertexBindingDescriptions = vertices.bindingDescriptions.data(); vertices.inputState.vertexAttributeDescriptionCount = (uint32_t)vertices.attributeDescriptions.size(); - vertices.inputState.pVertexAttributeDescriptions = vertices.attributeDescriptions.data(); + vertices.inputState.pVertexAttributeDescriptions = vertices.attributeDescriptions.data(); // Vertex shader uniform buffer block - createBuffer( - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - sizeof(uboVS), - &uboVS, - &uniformDataVS.buffer, - &uniformDataVS.memory, - &uniformDataVS.descriptor); + createBuffer(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, sizeof(uboVS), &uboVS, &uniformDataVS.buffer, &uniformDataVS.memory, &uniformDataVS.descriptor); updateUniformBuffers(); CMP_ERROR cmp_res = loadTexture(pSourceTexture); - if (cmp_res != CMP_OK) { + if (cmp_res != CMP_OK) + { return (cmp_res); } + //=========================== //setupDescriptorSetLayout + //=========================== std::vector setLayoutBindings = { // Binding 0 : Vertex shader uniform buffer - vkTools::initializers::descriptorSetLayoutBinding( - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - VK_SHADER_STAGE_VERTEX_BIT, - 0), + vkTools::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0), // Binding 1 : Fragment shader image sampler - vkTools::initializers::descriptorSetLayoutBinding( - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - VK_SHADER_STAGE_FRAGMENT_BIT, - 1) - }; + vkTools::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 1)}; VkDescriptorSetLayoutCreateInfo descriptorLayout = - vkTools::initializers::descriptorSetLayoutCreateInfo( - setLayoutBindings.data(), - (uint32_t)setLayoutBindings.size()); + vkTools::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings.data(), (uint32_t)setLayoutBindings.size()); VkResult err = vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout); if (err != VK_SUCCESS) @@ -1374,10 +1396,10 @@ CMP_ERROR GPU_Vulkan::prepare(const CMP_Texture* pSourceTexture) { assert(!err); - VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = - vkTools::initializers::pipelineLayoutCreateInfo( - &descriptorSetLayout, - 1); + //=========================== + // preparePipelines + //=========================== + VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = vkTools::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1); err = vkCreatePipelineLayout(device, &pPipelineLayoutCreateInfo, nullptr, &pipelineLayout); if (err != VK_SUCCESS) @@ -1386,51 +1408,25 @@ CMP_ERROR GPU_Vulkan::prepare(const CMP_Texture* pSourceTexture) { // preparePipelines VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = - vkTools::initializers::pipelineInputAssemblyStateCreateInfo( - VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, - 0, - VK_FALSE); + vkTools::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE); VkPipelineRasterizationStateCreateInfo rasterizationState = - vkTools::initializers::pipelineRasterizationStateCreateInfo( - VK_POLYGON_MODE_FILL, - VK_CULL_MODE_NONE, - VK_FRONT_FACE_COUNTER_CLOCKWISE, - 0); - - VkPipelineColorBlendAttachmentState blendAttachmentState = - vkTools::initializers::pipelineColorBlendAttachmentState( - 0xf, - VK_FALSE); - - VkPipelineColorBlendStateCreateInfo colorBlendState = - vkTools::initializers::pipelineColorBlendStateCreateInfo( - 1, - &blendAttachmentState); + vkTools::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0); + + VkPipelineColorBlendAttachmentState blendAttachmentState = vkTools::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE); + + VkPipelineColorBlendStateCreateInfo colorBlendState = vkTools::initializers::pipelineColorBlendStateCreateInfo(1, &blendAttachmentState); VkPipelineDepthStencilStateCreateInfo depthStencilState = - vkTools::initializers::pipelineDepthStencilStateCreateInfo( - VK_TRUE, - VK_TRUE, - VK_COMPARE_OP_LESS_OR_EQUAL); - - VkPipelineViewportStateCreateInfo viewportState = - vkTools::initializers::pipelineViewportStateCreateInfo(1, 1, 0); - - VkPipelineMultisampleStateCreateInfo multisampleState = - vkTools::initializers::pipelineMultisampleStateCreateInfo( - VK_SAMPLE_COUNT_1_BIT, - 0); - - std::vector dynamicStateEnables = { - VK_DYNAMIC_STATE_VIEWPORT, - VK_DYNAMIC_STATE_SCISSOR - }; + vkTools::initializers::pipelineDepthStencilStateCreateInfo(VK_TRUE, VK_TRUE, VK_COMPARE_OP_LESS_OR_EQUAL); + + VkPipelineViewportStateCreateInfo viewportState = vkTools::initializers::pipelineViewportStateCreateInfo(1, 1, 0); + + VkPipelineMultisampleStateCreateInfo multisampleState = vkTools::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT, 0); + + std::vector dynamicStateEnables = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR}; VkPipelineDynamicStateCreateInfo dynamicState = - vkTools::initializers::pipelineDynamicStateCreateInfo( - dynamicStateEnables.data(), - (uint32_t)dynamicStateEnables.size(), - (uint32_t)0); + vkTools::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables.data(), (uint32_t)dynamicStateEnables.size(), (uint32_t)0); // Load shaders std::array shaderStages; @@ -1438,22 +1434,18 @@ CMP_ERROR GPU_Vulkan::prepare(const CMP_Texture* pSourceTexture) { shaderStages[0] = loadShader("texture.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); shaderStages[1] = loadShader("texture.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); - VkGraphicsPipelineCreateInfo pipelineCreateInfo = - vkTools::initializers::pipelineCreateInfo( - pipelineLayout, - renderPass, - 0); + VkGraphicsPipelineCreateInfo pipelineCreateInfo = vkTools::initializers::pipelineCreateInfo(pipelineLayout, renderPass, 0); - pipelineCreateInfo.pVertexInputState = &vertices.inputState; + pipelineCreateInfo.pVertexInputState = &vertices.inputState; pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState; pipelineCreateInfo.pRasterizationState = &rasterizationState; - pipelineCreateInfo.pColorBlendState = &colorBlendState; - pipelineCreateInfo.pMultisampleState = &multisampleState; - pipelineCreateInfo.pViewportState = &viewportState; - pipelineCreateInfo.pDepthStencilState = &depthStencilState; - pipelineCreateInfo.pDynamicState = &dynamicState; - pipelineCreateInfo.stageCount = (uint32_t)shaderStages.size(); - pipelineCreateInfo.pStages = shaderStages.data(); + pipelineCreateInfo.pColorBlendState = &colorBlendState; + pipelineCreateInfo.pMultisampleState = &multisampleState; + pipelineCreateInfo.pViewportState = &viewportState; + pipelineCreateInfo.pDepthStencilState = &depthStencilState; + pipelineCreateInfo.pDynamicState = &dynamicState; + pipelineCreateInfo.stageCount = (uint32_t)shaderStages.size(); + pipelineCreateInfo.pStages = shaderStages.data(); err = vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.solid); if (err != VK_SUCCESS) @@ -1462,16 +1454,10 @@ CMP_ERROR GPU_Vulkan::prepare(const CMP_Texture* pSourceTexture) { //setupDescriptorPool // Example uses one ubo and one image sampler - std::vector poolSizes = { - vkTools::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1), - vkTools::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1) - }; + std::vector poolSizes = {vkTools::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1), + vkTools::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1)}; - VkDescriptorPoolCreateInfo descriptorPoolInfo = - vkTools::initializers::descriptorPoolCreateInfo( - (uint32_t)poolSizes.size(), - poolSizes.data(), - (uint32_t)2); + VkDescriptorPoolCreateInfo descriptorPoolInfo = vkTools::initializers::descriptorPoolCreateInfo((uint32_t)poolSizes.size(), poolSizes.data(), (uint32_t)2); VkResult vkRes = vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &m_descriptorPool); if (err != VK_SUCCESS) @@ -1479,11 +1465,7 @@ CMP_ERROR GPU_Vulkan::prepare(const CMP_Texture* pSourceTexture) { assert(!vkRes); //setupDescriptorSet - VkDescriptorSetAllocateInfo allocInfo = - vkTools::initializers::descriptorSetAllocateInfo( - m_descriptorPool, - &descriptorSetLayout, - 1); + VkDescriptorSetAllocateInfo allocInfo = vkTools::initializers::descriptorSetAllocateInfo(m_descriptorPool, &descriptorSetLayout, 1); vkRes = vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet); if (err != VK_SUCCESS) @@ -1491,26 +1473,13 @@ CMP_ERROR GPU_Vulkan::prepare(const CMP_Texture* pSourceTexture) { assert(!vkRes); // Image descriptor for the color map texture - VkDescriptorImageInfo texDescriptor = - vkTools::initializers::descriptorImageInfo( - texture.sampler, - texture.view, - VK_IMAGE_LAYOUT_GENERAL); + VkDescriptorImageInfo texDescriptor = vkTools::initializers::descriptorImageInfo(texture.sampler, texture.view, VK_IMAGE_LAYOUT_GENERAL); std::vector writeDescriptorSets = { // Binding 0 : Vertex shader uniform buffer - vkTools::initializers::writeDescriptorSet( - descriptorSet, - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - 0, - &uniformDataVS.descriptor), + vkTools::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformDataVS.descriptor), // Binding 1 : Fragment shader texture sampler - vkTools::initializers::writeDescriptorSet( - descriptorSet, - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - 1, - &texDescriptor) - }; + vkTools::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &texDescriptor)}; vkUpdateDescriptorSets(device, (uint32_t)writeDescriptorSets.size(), writeDescriptorSets.data(), 0, NULL); @@ -1518,19 +1487,20 @@ CMP_ERROR GPU_Vulkan::prepare(const CMP_Texture* pSourceTexture) { VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo(); VkClearValue clearValues[2]; - clearValues[0].color = defaultClearColor; - clearValues[1].depthStencil = { 1.0f, 0 }; - - VkRenderPassBeginInfo renderPassBeginInfo = vkTools::initializers::renderPassBeginInfo(); - renderPassBeginInfo.renderPass = renderPass; - renderPassBeginInfo.renderArea.offset.x = 0; - renderPassBeginInfo.renderArea.offset.y = 0; - renderPassBeginInfo.renderArea.extent.width = m_width; + clearValues[0].color = defaultClearColor; + clearValues[1].depthStencil = {1.0f, 0}; + + VkRenderPassBeginInfo renderPassBeginInfo = vkTools::initializers::renderPassBeginInfo(); + renderPassBeginInfo.renderPass = renderPass; + renderPassBeginInfo.renderArea.offset.x = 0; + renderPassBeginInfo.renderArea.offset.y = 0; + renderPassBeginInfo.renderArea.extent.width = m_width; renderPassBeginInfo.renderArea.extent.height = m_height; - renderPassBeginInfo.clearValueCount = 2; - renderPassBeginInfo.pClearValues = clearValues; + renderPassBeginInfo.clearValueCount = 2; + renderPassBeginInfo.pClearValues = clearValues; - for (size_t i = 0; i < drawCmdBuffers.size(); ++i) { + for (size_t i = 0; i < drawCmdBuffers.size(); ++i) + { // Set target frame buffer renderPassBeginInfo.framebuffer = frameBuffers[i]; @@ -1541,24 +1511,16 @@ CMP_ERROR GPU_Vulkan::prepare(const CMP_Texture* pSourceTexture) { vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); - VkViewport viewport = vkTools::initializers::viewport( - (float)m_width, - (float)m_height, - 0.0f, - 1.0f); + VkViewport viewport = vkTools::initializers::viewport((float)m_width, (float)m_height, 0.0f, 1.0f); vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport); - VkRect2D scissor = vkTools::initializers::rect2D( - m_width, - m_height, - 0, - 0); + VkRect2D scissor = vkTools::initializers::rect2D(m_width, m_height, 0, 0); vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor); vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, NULL); vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.solid); - VkDeviceSize offsets[1] = { 0 }; + VkDeviceSize offsets[1] = {0}; vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &vertices.buffer, offsets); vkCmdBindIndexBuffer(drawCmdBuffers[i], indices.buffer, 0, VK_INDEX_TYPE_UINT32); @@ -1577,26 +1539,30 @@ CMP_ERROR GPU_Vulkan::prepare(const CMP_Texture* pSourceTexture) { return (cmp_res); } -void GPU_Vulkan::destroyCommandBuffers() { +void GPU_Vulkan::destroyCommandBuffers() +{ vkFreeCommandBuffers(device, cmdPool, (uint32_t)drawCmdBuffers.size(), drawCmdBuffers.data()); } -void GPU_Vulkan::clean() { +void GPU_Vulkan::clean() +{ // Clean up Vulkan resources swapChain.cleanup(); if (m_descriptorPool != VK_NULL_HANDLE) vkDestroyDescriptorPool(device, m_descriptorPool, nullptr); - if (setupCmdBuffer != VK_NULL_HANDLE) { + if (setupCmdBuffer != VK_NULL_HANDLE) + { vkFreeCommandBuffers(device, cmdPool, 1, &setupCmdBuffer); - } destroyCommandBuffers(); vkDestroyRenderPass(device, renderPass, nullptr); - for (uint32_t i = 0; i < frameBuffers.size(); i++) { + for (uint32_t i = 0; i < frameBuffers.size(); i++) + { vkDestroyFramebuffer(device, frameBuffers[i], nullptr); } - for (auto& shaderModule : shaderModules) { + for (auto& shaderModule : shaderModules) + { vkDestroyShaderModule(device, shaderModule, nullptr); } vkDestroyImageView(device, depthStencil.view, nullptr); @@ -1612,22 +1578,24 @@ void GPU_Vulkan::clean() { vkDestroyDevice(device, nullptr); - if (m_tenableValidation) { + if (m_tenableValidation) + { vkDebug::freeDebugCallback(instance); } vkDestroyInstance(instance, nullptr); } -bool GPU_Vulkan::memory_type_from_properties( uint32_t typeBits, - VkFlags requirements_mask, - uint32_t *typeIndex) { +bool GPU_Vulkan::memory_type_from_properties(uint32_t typeBits, VkFlags requirements_mask, uint32_t* typeIndex) +{ // Search memtypes to find first index with those properties - for (uint32_t i = 0; i < deviceMemoryProperties.memoryTypeCount; i++) { - if ((typeBits & 1) == 1) { + for (uint32_t i = 0; i < deviceMemoryProperties.memoryTypeCount; i++) + { + if ((typeBits & 1) == 1) + { // Type is available, does it match user properties? - if ((deviceMemoryProperties.memoryTypes[i].propertyFlags & - requirements_mask) == requirements_mask) { + if ((deviceMemoryProperties.memoryTypes[i].propertyFlags & requirements_mask) == requirements_mask) + { *typeIndex = i; return true; } @@ -1638,10 +1606,8 @@ bool GPU_Vulkan::memory_type_from_properties( uint32_t typeBits, return false; } -void GPU_Vulkan::set_image_layout( VkImage image, - VkImageAspectFlags aspectMask, - VkImageLayout old_image_layout, - VkImageLayout new_image_layout) { +void GPU_Vulkan::set_image_layout(VkImage image, VkImageAspectFlags aspectMask, VkImageLayout old_image_layout, VkImageLayout new_image_layout) +{ /* DEPENDS on info.cmd and info.queue initialized */ if (postPresentCmdBuffer == VK_NULL_HANDLE) @@ -1651,96 +1617,99 @@ void GPU_Vulkan::set_image_layout( VkImage image, fprintf(stderr, "\n[Vulkan Error] queue = VK_NULL_HANDLE\n"); assert(queue != VK_NULL_HANDLE); - VkImageMemoryBarrier image_memory_barrier = {}; - image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - image_memory_barrier.pNext = NULL; - image_memory_barrier.srcAccessMask = 0; - image_memory_barrier.dstAccessMask = 0; - image_memory_barrier.oldLayout = old_image_layout; - image_memory_barrier.newLayout = new_image_layout; - image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.image = image; - image_memory_barrier.subresourceRange.aspectMask = aspectMask; - image_memory_barrier.subresourceRange.baseMipLevel = 0; - image_memory_barrier.subresourceRange.levelCount = 1; + VkImageMemoryBarrier image_memory_barrier = {}; + image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_memory_barrier.pNext = NULL; + image_memory_barrier.srcAccessMask = 0; + image_memory_barrier.dstAccessMask = 0; + image_memory_barrier.oldLayout = old_image_layout; + image_memory_barrier.newLayout = new_image_layout; + image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.image = image; + image_memory_barrier.subresourceRange.aspectMask = aspectMask; + image_memory_barrier.subresourceRange.baseMipLevel = 0; + image_memory_barrier.subresourceRange.levelCount = 1; image_memory_barrier.subresourceRange.baseArrayLayer = 0; - image_memory_barrier.subresourceRange.layerCount = 1; + image_memory_barrier.subresourceRange.layerCount = 1; - if (old_image_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { - image_memory_barrier.srcAccessMask = - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + if (old_image_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) + { + image_memory_barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; } - if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) + { image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; } - if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { + if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) + { image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; } - if (old_image_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + if (old_image_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) + { image_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; } - if (old_image_layout == VK_IMAGE_LAYOUT_PREINITIALIZED) { + if (old_image_layout == VK_IMAGE_LAYOUT_PREINITIALIZED) + { image_memory_barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; } - if (new_image_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { - image_memory_barrier.srcAccessMask = - VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT; + if (new_image_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) + { + image_memory_barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT; image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; } - if (new_image_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { - image_memory_barrier.dstAccessMask = - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + if (new_image_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) + { + image_memory_barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; } - if (new_image_layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { - image_memory_barrier.dstAccessMask = - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + if (new_image_layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) + { + image_memory_barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; } - VkPipelineStageFlags src_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + VkPipelineStageFlags src_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; VkPipelineStageFlags dest_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - vkCmdPipelineBarrier(postPresentCmdBuffer, src_stages, dest_stages, 0, 0, NULL, 0, NULL, - 1, &image_memory_barrier); + vkCmdPipelineBarrier(postPresentCmdBuffer, src_stages, dest_stages, 0, 0, NULL, 0, NULL, 1, &image_memory_barrier); } -void GPU_Vulkan::write(const CMP_Texture* pDestTexture) { - +void GPU_Vulkan::write(const CMP_Texture* pDestTexture) +{ VkResult res; - VkImageCreateInfo image_create_info = {}; - image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - image_create_info.pNext = NULL; - image_create_info.imageType = VK_IMAGE_TYPE_2D; - image_create_info.format = swapChain.colorFormat; - image_create_info.extent.width = m_width; - image_create_info.extent.height = m_height; - image_create_info.extent.depth = 1; - image_create_info.mipLevels = 1; - image_create_info.arrayLayers = 1; - image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; - image_create_info.tiling = VK_IMAGE_TILING_LINEAR; - image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT; + VkImageCreateInfo image_create_info = {}; + image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + image_create_info.pNext = NULL; + image_create_info.imageType = VK_IMAGE_TYPE_2D; + image_create_info.format = swapChain.colorFormat; + image_create_info.extent.width = m_width; + image_create_info.extent.height = m_height; + image_create_info.extent.depth = 1; + image_create_info.mipLevels = 1; + image_create_info.arrayLayers = 1; + image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; + image_create_info.tiling = VK_IMAGE_TILING_LINEAR; + image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT; image_create_info.queueFamilyIndexCount = 0; - image_create_info.pQueueFamilyIndices = NULL; - image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - image_create_info.flags = 0; + image_create_info.pQueueFamilyIndices = NULL; + image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + image_create_info.flags = 0; VkMemoryAllocateInfo mem_alloc = {}; - mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - mem_alloc.pNext = NULL; - mem_alloc.allocationSize = 0; - mem_alloc.memoryTypeIndex = 0; + mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + mem_alloc.pNext = NULL; + mem_alloc.allocationSize = 0; + mem_alloc.memoryTypeIndex = 0; - VkImage mappableImage; + VkImage mappableImage; VkDeviceMemory mappableMemory; //Create a mappable image @@ -1756,9 +1725,7 @@ void GPU_Vulkan::write(const CMP_Texture* pDestTexture) { //Find the memory type that is host mappable bool pass = memory_type_from_properties( - mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | - VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - &mem_alloc.memoryTypeIndex); + mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &mem_alloc.memoryTypeIndex); assert(pass && "No mappable, coherent memory"); @@ -1779,119 +1746,154 @@ void GPU_Vulkan::write(const CMP_Texture* pDestTexture) { VkImageMemoryBarrier imageMemoryBarrier = vkTools::initializers::imageMemoryBarrier(); // Transition destination image to transfer destination layout - vkTools::insertImageMemoryBarrier( - copyCmd, - mappableImage, - 0, - VK_ACCESS_TRANSFER_WRITE_BIT, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); + vkTools::insertImageMemoryBarrier(copyCmd, + mappableImage, + 0, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VkImageSubresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}); // Transition swapchain image from present to transfer source layout - vkTools::insertImageMemoryBarrier( - copyCmd, - swapChain.buffers[currentBuffer].image, - VK_ACCESS_MEMORY_READ_BIT, - VK_ACCESS_TRANSFER_READ_BIT, - VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); - + vkTools::insertImageMemoryBarrier(copyCmd, + swapChain.buffers[currentBuffer].image, + VK_ACCESS_MEMORY_READ_BIT, + VK_ACCESS_TRANSFER_READ_BIT, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VkImageSubresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}); VkImageCopy copy_region; - copy_region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - copy_region.srcSubresource.mipLevel = 0; + copy_region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copy_region.srcSubresource.mipLevel = 0; copy_region.srcSubresource.baseArrayLayer = 0; - copy_region.srcSubresource.layerCount = 1; - copy_region.srcOffset.x = 0; - copy_region.srcOffset.y = 0; - copy_region.srcOffset.z = 0; - copy_region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - copy_region.dstSubresource.mipLevel = 0; + copy_region.srcSubresource.layerCount = 1; + copy_region.srcOffset.x = 0; + copy_region.srcOffset.y = 0; + copy_region.srcOffset.z = 0; + copy_region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copy_region.dstSubresource.mipLevel = 0; copy_region.dstSubresource.baseArrayLayer = 0; - copy_region.dstSubresource.layerCount = 1; - copy_region.dstOffset.x = 0; - copy_region.dstOffset.y = 0; - copy_region.dstOffset.z = 0; - copy_region.extent.width = m_width; - copy_region.extent.height = m_height; - copy_region.extent.depth = 1; + copy_region.dstSubresource.layerCount = 1; + copy_region.dstOffset.x = 0; + copy_region.dstOffset.y = 0; + copy_region.dstOffset.z = 0; + copy_region.extent.width = m_width; + copy_region.extent.height = m_height; + copy_region.extent.depth = 1; //issue copy command - vkCmdCopyImage(copyCmd, swapChain.buffers[currentBuffer].image, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, mappableImage, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region); + vkCmdCopyImage(copyCmd, + swapChain.buffers[currentBuffer].image, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + mappableImage, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + ©_region); // Transition destination image to general layout, which is the required layout for mapping the image memory later on - vkTools::insertImageMemoryBarrier( - copyCmd, - mappableImage, - VK_ACCESS_TRANSFER_WRITE_BIT, - VK_ACCESS_MEMORY_READ_BIT, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_IMAGE_LAYOUT_GENERAL, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); + vkTools::insertImageMemoryBarrier(copyCmd, + mappableImage, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_ACCESS_MEMORY_READ_BIT, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_GENERAL, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VkImageSubresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}); // Transition back the swap chain image - vkTools::insertImageMemoryBarrier( - copyCmd, - swapChain.buffers[currentBuffer].image, - VK_ACCESS_TRANSFER_READ_BIT, - VK_ACCESS_MEMORY_READ_BIT, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); + vkTools::insertImageMemoryBarrier(copyCmd, + swapChain.buffers[currentBuffer].image, + VK_ACCESS_TRANSFER_READ_BIT, + VK_ACCESS_MEMORY_READ_BIT, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VkImageSubresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}); flushCommandBuffer(copyCmd, queue); VkImageSubresource subres = {}; - subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - subres.mipLevel = 0; - subres.arrayLayer = 0; + subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subres.mipLevel = 0; + subres.arrayLayer = 0; VkSubresourceLayout sr_layout; - vkGetImageSubresourceLayout(device, mappableImage, &subres, - &sr_layout); + vkGetImageSubresourceLayout(device, mappableImage, &subres, &sr_layout); - char *ptr; - res = vkMapMemory(device, mappableMemory, 0, pDestTexture->dwDataSize, 0, (void **)&ptr); // mem_reqs.size, 0,(void **)&ptr); + char* ptr; + res = vkMapMemory(device, mappableMemory, 0, pDestTexture->dwDataSize, 0, (void**)&ptr); if (res != VK_SUCCESS) fprintf(stderr, "\n[Vulkan Error] vkMapMemory = %d\n", res); assert(res == VK_SUCCESS); ptr += sr_layout.offset; - int *pData = (int *)pDestTexture->pData; + int* pData = (int*)pDestTexture->pData; size_t x, y; - for (y = 0; y < m_height; y++) { - const int *row = (const int *)ptr; - int swapped; + for (y = 0; y < m_height; y++) + { + const int* row = (const int*)ptr; + int swapped; - if (swapChain.colorFormat == VK_FORMAT_B8G8R8A8_UNORM || - swapChain.colorFormat == VK_FORMAT_B8G8R8A8_SRGB) { - for (x = 0; x < m_width; x++) { + if (swapChain.colorFormat == VK_FORMAT_B8G8R8A8_UNORM || swapChain.colorFormat == VK_FORMAT_B8G8R8A8_SRGB) + { + for (x = 0; x < m_width; x++) + { swapped = (*row & 0xff00ff00) | (*row & 0x000000ff) << 16 | (*row & 0x00ff0000) >> 16; - *pData = *row; + *pData = *row; row++; pData++; } - } else if (swapChain.colorFormat == VK_FORMAT_R8G8B8A8_UNORM) { - for (x = 0; x < m_width; x++) { + } + else if (swapChain.colorFormat == VK_FORMAT_R8G8B8A8_UNORM) + { + // change uint to sint buffer + if (pDestTexture->format == CMP_FORMAT_RGBA_8888_S) + { + union _sdata + { + char sb[4]; + int data; + } sdata; + + union _udata + { + char ub[4]; + int data; + } udata; + + for (x = 0; x < m_width; x++) + { + udata.data = *row; + sdata.sb[0] = udata.ub[0] - 127; + sdata.sb[1] = udata.ub[1] - 127; + sdata.sb[2] = udata.ub[2] - 127; + sdata.sb[3] = udata.ub[3] - 127; + *pData = sdata.data; + + row++; + pData++; + } + } + else + for (x = 0; x < m_width; x++) + { *pData = *row; row++; pData++; } - } else if (swapChain.colorFormat == VK_FORMAT_R16G16B16A16_SFLOAT) { - for (x = 0; x < m_width; x++) { + } + else if (swapChain.colorFormat == VK_FORMAT_R16G16B16A16_SFLOAT) + { + for (x = 0; x < m_width; x++) + { //only "int" type work, use int type and copy twice for this case *pData = *row; row++; @@ -1900,7 +1902,9 @@ void GPU_Vulkan::write(const CMP_Texture* pDestTexture) { row++; pData++; } - } else { + } + else + { //printf("Unrecognized image format - will not write image files"); break; } @@ -1911,23 +1915,24 @@ void GPU_Vulkan::write(const CMP_Texture* pDestTexture) { vkUnmapMemory(device, mappableMemory); vkFreeMemory(device, mappableMemory, NULL); vkDestroyImage(device, mappableImage, NULL); - } -bool GPU_Vulkan::isSupportVersion() { +bool GPU_Vulkan::isSupportVersion() +{ VkPhysicalDeviceProperties deviceProperties; vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties); uint32_t driverVersion = deviceProperties.driverVersion; - uint32_t major = VK_VERSION_MAJOR(driverVersion); - uint32_t minor = VK_VERSION_MINOR(driverVersion); + uint32_t major = VK_VERSION_MAJOR(driverVersion); + uint32_t minor = VK_VERSION_MINOR(driverVersion); if (major > 1 || minor > 6) return false; else return true; } -bool GPU_Vulkan::isSupportASTC() { +bool GPU_Vulkan::isSupportASTC() +{ VkPhysicalDeviceFeatures deviceFeature; vkGetPhysicalDeviceFeatures(physicalDevice, &deviceFeature); @@ -1938,22 +1943,26 @@ bool GPU_Vulkan::isSupportASTC() { } //-------------------------------------------------------------------------------------- -#pragma warning( suppress : 6262 ) +#pragma warning(suppress : 6262) + +CMP_ERROR WINAPI GPU_Vulkan::Decompress(const CMP_Texture* pSourceTexture, CMP_Texture* pDestTexture) +{ + assert(pSourceTexture); -CMP_ERROR WINAPI GPU_Vulkan::Decompress( - const CMP_Texture* pSourceTexture, - CMP_Texture* pDestTexture -) { CMP_ERROR cmp_res; - m_width = pSourceTexture->dwWidth; - m_height = pSourceTexture->dwHeight; + m_pSourceTexture = pSourceTexture; + m_width = pSourceTexture->dwWidth; + m_height = pSourceTexture->dwHeight; - if (!initVulkan(m_tenableValidation)) return CMP_ERR_UNABLE_TO_INIT_DECOMPRESSLIB; + if (!initVulkan(m_tenableValidation)) + return CMP_ERR_UNABLE_TO_INIT_DECOMPRESSLIB; // Check for device feature for ASTC decode only - if (pSourceTexture->format == CMP_FORMAT_ASTC) { - if (!isSupportASTC()) { + if (pSourceTexture->format == CMP_FORMAT_ASTC) + { + if (!isSupportASTC()) + { m_initOk = false; return CMP_ERR_UNSUPPORTED_GPU_ASTC_DECODE; } @@ -1963,13 +1972,12 @@ CMP_ERROR WINAPI GPU_Vulkan::Decompress( cmp_res = prepare(pSourceTexture); - if (cmp_res != CMP_OK) { + if (cmp_res != CMP_OK) + { swapChain.cleanup(); return (cmp_res); } - - #ifdef SHOW_WINDOW // Activate the window -- for debug view purpose ShowWindow(m_hWnd, SW_SHOW); @@ -1977,12 +1985,16 @@ CMP_ERROR WINAPI GPU_Vulkan::Decompress( // Wait in Main message loop, until render is complete!! // then exit - MSG msg = { 0 }; - while (WM_QUIT != msg.message) { - if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { + MSG msg = {0}; + while (WM_QUIT != msg.message) + { + if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) + { TranslateMessage(&msg); DispatchMessage(&msg); - } else { + } + else + { VkRender(); write(pDestTexture); break; @@ -1991,4 +2003,3 @@ CMP_ERROR WINAPI GPU_Vulkan::Decompress( return CMP_OK; } - diff --git a/applications/_plugins/cgpudecode/vulkan/gpu_vulkan.h b/applications/_plugins/cgpudecode/vulkan/gpu_vulkan.h index 94aa7651b..d460fea84 100644 --- a/applications/_plugins/cgpudecode/vulkan/gpu_vulkan.h +++ b/applications/_plugins/cgpudecode/vulkan/gpu_vulkan.h @@ -91,6 +91,7 @@ class GPU_Vulkan : public RenderWindow { uint32_t m_height; bool m_initOk; + const CMP_Texture* m_pSourceTexture = nullptr; VulkanSwapChain swapChain; @@ -100,8 +101,6 @@ class GPU_Vulkan : public RenderWindow { uint32_t currentBuffer = 0; // Handle to the device graphics queue that command buffers are submitted to VkQueue queue; - // Color buffer format - VkFormat colorformat = VK_FORMAT_B8G8R8A8_UNORM; // Depth format is selected during Vulkan initialization VkFormat depthFormat; // number of vulkan devices diff --git a/applications/_plugins/cgpudecode/vulkan/vulkanswapchain.hpp b/applications/_plugins/cgpudecode/vulkan/vulkanswapchain.hpp index 904296a5a..0fba0f735 100644 --- a/applications/_plugins/cgpudecode/vulkan/vulkanswapchain.hpp +++ b/applications/_plugins/cgpudecode/vulkan/vulkanswapchain.hpp @@ -239,9 +239,10 @@ class VulkanSwapChain colorSpace = surfaceFormats[0].colorSpace; } } - + } + // Connect to the instance und device and get all required function pointers void connect(VkInstance instance, VkPhysicalDevice physicalDevice, VkDevice device) { diff --git a/applications/_plugins/cgpudecode/vulkan/vulkantools.cpp b/applications/_plugins/cgpudecode/vulkan/vulkantools.cpp index 6ff142e40..d79e011fa 100644 --- a/applications/_plugins/cgpudecode/vulkan/vulkantools.cpp +++ b/applications/_plugins/cgpudecode/vulkan/vulkantools.cpp @@ -73,8 +73,10 @@ VkBool32 getSupportedDepthFormat(VkPhysicalDevice physicalDevice, VkFormat *dept VK_FORMAT_D32_SFLOAT, VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D16_UNORM_S8_UINT, - VK_FORMAT_D16_UNORM - }; + VK_FORMAT_D16_UNORM, + VK_FORMAT_B8G8R8A8_UNORM, + VK_FORMAT_B8G8R8A8_SNORM + }; for (auto& format : depthFormats) { VkFormatProperties formatProps; diff --git a/applications/_plugins/cimage/astc/cmakelists.txt b/applications/_plugins/cimage/astc/cmakelists.txt index 6ad1a2082..928b6ce62 100644 --- a/applications/_plugins/cimage/astc/cmakelists.txt +++ b/applications/_plugins/cimage/astc/cmakelists.txt @@ -1,19 +1,28 @@ +cmake_minimum_required(VERSION 3.10) -add_library(Plugin_CImage_ASTC) +add_library(Image_ASTC STATIC "") -target_sources(Plugin_CImage_ASTC PRIVATE +target_sources(Image_ASTC + PRIVATE + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/tc_pluginapi.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/tc_plugininternal.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/tc_plugininternal.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/utilfuncs.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/utilfuncs.cpp + ./castc.cpp + ./castc.h + ) - castc.h - castc.cpp -) +target_include_directories(Image_ASTC + PRIVATE + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half + ${PROJECT_SOURCE_DIR}/applications/_plugins/common + ) -target_link_libraries(Plugin_CImage_ASTC PRIVATE +if (UNIX) + target_compile_definitions(Image_ASTC PRIVATE _LINUX) +endif() - CompressonatorLIB - CMP_FrameworkLIB +set_target_properties(Image_ASTC PROPERTIES FOLDER "Plugin_Static/ImageIO") - Plugin_TCPluginAPI - Plugin_Common_UtilFuncs -) - -set_target_properties(Plugin_CImage_ASTC PROPERTIES FOLDER ${FOLDER_NAME}) diff --git a/applications/_plugins/cimage/dds/cmakelists.txt b/applications/_plugins/cimage/dds/cmakelists.txt index 0549b58af..7e1e92b94 100644 --- a/applications/_plugins/cimage/dds/cmakelists.txt +++ b/applications/_plugins/cimage/dds/cmakelists.txt @@ -1,8 +1,9 @@ +cmake_minimum_required(VERSION 3.10) -add_library(Plugin_CImage_DDS) - -target_sources(Plugin_CImage_DDS PRIVATE +add_library(Image_DDS STATIC "") +target_sources(Image_DDS + PRIVATE dds.cpp dds.h dds_dx10.cpp @@ -11,15 +12,20 @@ target_sources(Plugin_CImage_DDS PRIVATE dds_file.h dds_helpers.cpp dds_helpers.h -) - -target_link_libraries(Plugin_CImage_DDS PRIVATE - - CompressonatorLIB - - Plugin_TCPluginAPI - - ExtOpenEXR -) - -set_target_properties(Plugin_CImage_DDS PROPERTIES FOLDER ${FOLDER_NAME}) + ) + +target_include_directories(Image_DDS + PRIVATE + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half + ${PROJECT_SOURCE_DIR}/applications/_plugins/common + ) + +target_link_libraries(Image_DDS + PRIVATE + CMP_Compressonator + ) + +set_target_properties(Image_DDS PROPERTIES + FOLDER "Plugin_Static/ImageIO" + ) diff --git a/applications/_plugins/cimage/dds/dds.cpp b/applications/_plugins/cimage/dds/dds.cpp index 2192fdb51..63cef2c8e 100644 --- a/applications/_plugins/cimage/dds/dds.cpp +++ b/applications/_plugins/cimage/dds/dds.cpp @@ -42,7 +42,7 @@ #define _CRT_SECURE_NO_WARNINGS -CMIPS *DDS_CMips = NULL; +CMIPS* DDS_CMips = NULL; const char* g_pszFilename; #ifdef BUILD_AS_PLUGIN_DLL @@ -50,198 +50,263 @@ DECLARE_PLUGIN(Plugin_DDS) SET_PLUGIN_TYPE("IMAGE") SET_PLUGIN_NAME("DDS") #else -void *make_Plugin_DDS() { +void* make_Plugin_DDS() +{ return new Plugin_DDS; } #endif -Plugin_DDS::Plugin_DDS() { +Plugin_DDS::Plugin_DDS() +{ #ifdef _WIN32 HRESULT hr; // Initialize COM (needed for WIC) - if( FAILED( hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED) ) ) { + if (FAILED(hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED))) + { if (DDS_CMips) DDS_CMips->PrintError("Failed to initialize COM (%08X)\n", hr); } #endif } -Plugin_DDS::~Plugin_DDS() { +Plugin_DDS::~Plugin_DDS() +{ } -int Plugin_DDS::TC_PluginSetSharedIO(void *Shared) { - if (Shared) { - DDS_CMips = static_cast(Shared); +int Plugin_DDS::TC_PluginSetSharedIO(void* Shared) +{ + if (Shared) + { + DDS_CMips = static_cast(Shared); return 0; } return 1; } -int Plugin_DDS::TC_PluginGetVersion(TC_PluginVersion* pPluginVersion) { +int Plugin_DDS::TC_PluginGetVersion(TC_PluginVersion* pPluginVersion) +{ #ifdef _WIN32 - pPluginVersion->guid = g_GUID; + pPluginVersion->guid = g_GUID; #endif - pPluginVersion->dwAPIVersionMajor = TC_API_VERSION_MAJOR; - pPluginVersion->dwAPIVersionMinor = TC_API_VERSION_MINOR; - pPluginVersion->dwPluginVersionMajor = TC_PLUGIN_VERSION_MAJOR; - pPluginVersion->dwPluginVersionMinor = TC_PLUGIN_VERSION_MINOR; + pPluginVersion->dwAPIVersionMajor = TC_API_VERSION_MAJOR; + pPluginVersion->dwAPIVersionMinor = TC_API_VERSION_MINOR; + pPluginVersion->dwPluginVersionMajor = TC_PLUGIN_VERSION_MAJOR; + pPluginVersion->dwPluginVersionMinor = TC_PLUGIN_VERSION_MINOR; return 0; } // No longer supported -int Plugin_DDS::TC_PluginFileLoadTexture(const char* pszFilename, CMP_Texture *srcTexture) { +int Plugin_DDS::TC_PluginFileLoadTexture(const char* pszFilename, CMP_Texture* srcTexture) +{ (pszFilename); (srcTexture); return 0; } // No longer supported -int Plugin_DDS::TC_PluginFileSaveTexture(const char* pszFilename, CMP_Texture *srcTexture) { +int Plugin_DDS::TC_PluginFileSaveTexture(const char* pszFilename, CMP_Texture* srcTexture) +{ (pszFilename); (srcTexture); #ifdef _WIN32 HRESULT hr = S_OK; - return hr==S_OK?0:-1; // np: need to fix this : make all pligins return long type! + return hr == S_OK ? 0 : -1; // np: need to fix this : make all pligins return long type! #else return 0; #endif } -int Plugin_DDS::TC_PluginFileLoadTexture(const char* pszFilename, MipSet* pMipSet) { +int Plugin_DDS::TC_PluginFileLoadTexture(const char* pszFilename, MipSet* pMipSet) +{ g_pszFilename = pszFilename; - FILE* pFile = NULL; - pFile = fopen(pszFilename, ("rb")); - if(pFile == NULL) { - DDS_CMips->PrintError("Error [%x]: DDS Plugin Failed to load texture file %s\n",IDS_ERROR_FILE_OPEN,pszFilename); + FILE* pFile = NULL; + pFile = fopen(pszFilename, ("rb")); + if (pFile == NULL) + { + DDS_CMips->PrintError("Error [%x]: DDS Plugin Failed to load texture file %s\n", IDS_ERROR_FILE_OPEN, pszFilename); return PE_Unknown; } CMP_DWORD dwFileHeader; - fread(&dwFileHeader,sizeof(CMP_DWORD), 1, pFile); - if(dwFileHeader != DDS_HEADER) { + fread(&dwFileHeader, sizeof(CMP_DWORD), 1, pFile); + if (dwFileHeader != DDS_HEADER) + { fclose(pFile); - DDS_CMips->PrintError("Error [%x]: DDS Plugin Failed to load texture file %s\n",IDS_ERROR_NOT_DDS,pszFilename); + DDS_CMips->PrintError("Error [%x]: DDS Plugin Failed to load texture file %s\n", IDS_ERROR_NOT_DDS, pszFilename); return PE_Unknown; } DDSD2 ddsd; - if(fread(&ddsd, sizeof(DDSD2), 1, pFile) != 1) { + if (fread(&ddsd, sizeof(DDSD2), 1, pFile) != 1) + { fclose(pFile); - DDS_CMips->PrintError("Error [%x]: DDS Plugin Failed to load texture file %s\n",IDS_ERROR_NOT_DDS,pszFilename); + DDS_CMips->PrintError("Error [%x]: DDS Plugin Failed to load texture file %s\n", IDS_ERROR_NOT_DDS, pszFilename); return PE_Unknown; } - if(!(ddsd.dwFlags & DDSD_MIPMAPCOUNT)) + if (!(ddsd.dwFlags & DDSD_MIPMAPCOUNT)) ddsd.dwMipMapCount = 1; - else if(ddsd.dwMipMapCount == 0) { + else if (ddsd.dwMipMapCount == 0) + { fclose(pFile); - DDS_CMips->PrintError("Error [%x]: DDS Plugin Failed to load texture file %s\n",IDS_ERROR_NOT_DDS,pszFilename); + DDS_CMips->PrintError("Error [%x]: DDS Plugin Failed to load texture file %s\n", IDS_ERROR_NOT_DDS, pszFilename); return PE_Unknown; } - if(ddsd.ddpfPixelFormat.dwFourCC == CMP_MAKEFOURCC('D', 'X', '1', '0')) + // if ((DDSHeader->ddspf.flags & DDS_FOURCC) && (MAKEFOURCC('D', 'X', '1', '0') == DDSHeader->ddspf.fourCC)) + if (ddsd.ddpfPixelFormat.dwFourCC == CMP_MAKEFOURCC('D', 'X', '1', '0')) return LoadDDS_DX10(pFile, &ddsd, pMipSet); - else if(ddsd.ddpfPixelFormat.dwFourCC == D3DFMT_A32B32G32R32F) + + // Prep for next revision + // DDS_FILE_HEADER* DDSHeader = reinterpret_cast(&ddsd); + // Prechecks to set into mipset + // if (DDSHeader->flags & DDS_HEADER_FLAGS_VOLUME) { + // // fill in volume mipset props + // } + // else if (DDSHeader->caps2 & DDS_CUBEMAP) + // { + // // fill in cubemap mipset props + // } + + + // Prep for next revision + DDS_FILE_HEADER* DDSHeader = reinterpret_cast(&ddsd); + DDS_PIXELFORMAT DDSPF_L16 = {sizeof(DDS_PIXELFORMAT), DDS_LUMINANCE, 0, 16, 0xffff, 0, 0, 0}; + if ((DDSHeader->ddspf.flags & DDPF_LUMINANCE) && (DDSHeader->ddspf.RGBBitCount == 16) && (DDSHeader->ddspf.RBitMask & 0xffff) && + (DDSHeader->ddspf.GBitMask == 0) && (DDSHeader->ddspf.BBitMask == 0)) + { + return LoadDDS_R16(pFile, &ddsd, pMipSet); + } + + if ((DDSHeader->ddspf.flags & DDS_BUMPDUDV) && + (DDSHeader->ddspf.RGBBitCount == 32)) + { + pMipSet->m_format = CMP_FORMAT_RGBA_8888_S; + return LoadDDS_RGB8888_S(pFile, &ddsd, pMipSet, (ddsd.ddpfPixelFormat.dwFlags & DDPF_ALPHAPIXELS) ? true : false); + } + + + // Try known FourCC first for legcay support + if (ddsd.ddpfPixelFormat.dwFourCC == D3DFMT_A32B32G32R32F) return LoadDDS_ABGR32F(pFile, &ddsd, pMipSet); - else if(ddsd.ddpfPixelFormat.dwFourCC == D3DFMT_A16B16G16R16F) + if (ddsd.ddpfPixelFormat.dwFourCC == D3DFMT_A16B16G16R16F) return LoadDDS_ABGR16F(pFile, &ddsd, pMipSet); - else if(ddsd.ddpfPixelFormat.dwFourCC == D3DFMT_G32R32F) + if (ddsd.ddpfPixelFormat.dwFourCC == D3DFMT_G32R32F) return LoadDDS_GR32F(pFile, &ddsd, pMipSet); - else if(ddsd.ddpfPixelFormat.dwFourCC == D3DFMT_R32F) + if (ddsd.ddpfPixelFormat.dwFourCC == D3DFMT_R32F) return LoadDDS_R32F(pFile, &ddsd, pMipSet); - else if(ddsd.ddpfPixelFormat.dwFourCC == D3DFMT_R16F) + if (ddsd.ddpfPixelFormat.dwFourCC == D3DFMT_R16F) return LoadDDS_R16F(pFile, &ddsd, pMipSet); - else if(ddsd.ddpfPixelFormat.dwFourCC == D3DFMT_G16R16F) + if (ddsd.ddpfPixelFormat.dwFourCC == D3DFMT_G16R16F) return LoadDDS_G16R16F(pFile, &ddsd, pMipSet); - else if(ddsd.ddpfPixelFormat.dwFourCC == D3DFMT_A16B16G16R16) + if (ddsd.ddpfPixelFormat.dwFourCC == D3DFMT_A16B16G16R16) return LoadDDS_ABGR16(pFile, &ddsd, pMipSet); - else if(ddsd.ddpfPixelFormat.dwFourCC == D3DFMT_Q16W16V16U16) + if (ddsd.ddpfPixelFormat.dwFourCC == D3DFMT_Q16W16V16U16) return LoadDDS_ABGR16(pFile, &ddsd, pMipSet); - else if(ddsd.ddpfPixelFormat.dwFourCC == D3DFMT_G16R16) + if (ddsd.ddpfPixelFormat.dwFourCC == D3DFMT_G16R16) return LoadDDS_G16R16(pFile, &ddsd, pMipSet); - else if(ddsd.ddpfPixelFormat.dwFourCC == D3DFMT_L16) + if (ddsd.ddpfPixelFormat.dwFourCC == D3DFMT_L16) return LoadDDS_R16(pFile, &ddsd, pMipSet); - else if(ddsd.ddpfPixelFormat.dwFourCC) + if (ddsd.ddpfPixelFormat.dwFourCC) return LoadDDS_FourCC(pFile, &ddsd, pMipSet); - else if(ddsd.ddpfPixelFormat.dwLuminanceBitCount==8 && (ddsd.ddpfPixelFormat.dwFlags & DDPF_LUMINANCE)) + + if (ddsd.ddpfPixelFormat.dwLuminanceBitCount == 8 && (ddsd.ddpfPixelFormat.dwFlags & DDPF_LUMINANCE)) return LoadDDS_G8(pFile, &ddsd, pMipSet); - else if(ddsd.ddpfPixelFormat.dwLuminanceBitCount==16 && (ddsd.ddpfPixelFormat.dwFlags & DDPF_LUMINANCE) && (ddsd.ddpfPixelFormat.dwFlags & DDPF_ALPHAPIXELS)) + if (ddsd.ddpfPixelFormat.dwLuminanceBitCount == 16 && (ddsd.ddpfPixelFormat.dwFlags & DDPF_LUMINANCE) && + (ddsd.ddpfPixelFormat.dwFlags & DDPF_ALPHAPIXELS)) return LoadDDS_AG8(pFile, &ddsd, pMipSet); - else if(ddsd.ddpfPixelFormat.dwLuminanceBitCount==16 && (ddsd.ddpfPixelFormat.dwFlags & DDPF_LUMINANCE)&& (ddsd.ddpfPixelFormat.dwGBitMask==0xffff)) + if (ddsd.ddpfPixelFormat.dwLuminanceBitCount == 16 && (ddsd.ddpfPixelFormat.dwFlags & DDPF_LUMINANCE) && (ddsd.ddpfPixelFormat.dwGBitMask == 0xffff)) return LoadDDS_G16(pFile, &ddsd, pMipSet); - else if(ddsd.ddpfPixelFormat.dwAlphaBitDepth==8 && (ddsd.ddpfPixelFormat.dwFlags & DDPF_ALPHA)) + if (ddsd.ddpfPixelFormat.dwAlphaBitDepth == 8 && (ddsd.ddpfPixelFormat.dwFlags & DDPF_ALPHA)) return LoadDDS_A8(pFile, &ddsd, pMipSet); - else if((ddsd.ddpfPixelFormat.dwFlags & DDPF_RGB) && !(ddsd.ddpfPixelFormat.dwFlags & DDPF_ALPHAPIXELS) && (ddsd.ddpfPixelFormat.dwRGBBitCount==16)) + if ((ddsd.ddpfPixelFormat.dwFlags & DDPF_RGB) && !(ddsd.ddpfPixelFormat.dwFlags & DDPF_ALPHAPIXELS) && (ddsd.ddpfPixelFormat.dwRGBBitCount == 16)) return LoadDDS_RGB565(pFile, &ddsd, pMipSet); - else if((ddsd.ddpfPixelFormat.dwFlags & DDPF_RGB) && !(ddsd.ddpfPixelFormat.dwFlags & DDPF_ALPHAPIXELS) && (ddsd.ddpfPixelFormat.dwRGBBitCount==24)) + if ((ddsd.ddpfPixelFormat.dwFlags & DDPF_RGB) && !(ddsd.ddpfPixelFormat.dwFlags & DDPF_ALPHAPIXELS) && (ddsd.ddpfPixelFormat.dwRGBBitCount == 24)) return LoadDDS_RGB888(pFile, &ddsd, pMipSet); - else if(ddsd.ddpfPixelFormat.dwRGBBitCount==32 && (ddsd.ddpfPixelFormat.dwRBitMask==0x3ff || ddsd.ddpfPixelFormat.dwRBitMask==0x3ff00000)) + if (ddsd.ddpfPixelFormat.dwRGBBitCount == 32 && (ddsd.ddpfPixelFormat.dwRBitMask == 0x3ff || ddsd.ddpfPixelFormat.dwRBitMask == 0x3ff00000)) return LoadDDS_ARGB2101010(pFile, &ddsd, pMipSet); - else if(ddsd.ddpfPixelFormat.dwRGBBitCount==32 && ddsd.ddpfPixelFormat.dwRBitMask==0xffff && ddsd.ddpfPixelFormat.dwGBitMask==0xffff0000) + if (ddsd.ddpfPixelFormat.dwRGBBitCount == 32 && ddsd.ddpfPixelFormat.dwRBitMask == 0xffff && ddsd.ddpfPixelFormat.dwGBitMask == 0xffff0000) return LoadDDS_G16R16(pFile, &ddsd, pMipSet); - else if(ddsd.ddpfPixelFormat.dwLuminanceBitCount==16 && (ddsd.ddpfPixelFormat.dwFlags & DDPF_LUMINANCE)&&(ddsd.ddpfPixelFormat.dwRBitMask==0xffff)) { // 16bpp Gray + if (ddsd.ddpfPixelFormat.dwLuminanceBitCount == 16 && (ddsd.ddpfPixelFormat.dwFlags & DDPF_LUMINANCE) && (ddsd.ddpfPixelFormat.dwRBitMask == 0xffff)) + { // 16bpp Gray pMipSet->m_format = CMP_FORMAT_ABGR_16; return LoadDDS_ABGR16(pFile, &ddsd, pMipSet); - } else if(ddsd.ddpfPixelFormat.dwRGBBitCount==16 && ddsd.ddpfPixelFormat.dwRBitMask==0xffff) { + } + if (ddsd.ddpfPixelFormat.dwRGBBitCount == 16 && ddsd.ddpfPixelFormat.dwRBitMask == 0xffff) + { pMipSet->m_format = CMP_FORMAT_R_16; return LoadDDS_R16(pFile, &ddsd, pMipSet); - } else if(ddsd.ddpfPixelFormat.dwRGBBitCount==32) { + } + if (ddsd.ddpfPixelFormat.dwRGBBitCount == 32) + { + pMipSet->m_format = CMP_FORMAT_ARGB_8888; return LoadDDS_RGB8888(pFile, &ddsd, pMipSet, (ddsd.ddpfPixelFormat.dwFlags & DDPF_ALPHAPIXELS) ? true : false); } fclose(pFile); - DDS_CMips->PrintError("Error [%x]: DDS Plugin Failed to load texture file %s\n",IDS_ERROR_UNSUPPORTED_TYPE,pszFilename); + DDS_CMips->PrintError("Error [%x]: DDS Plugin Failed to load texture file %s\n", IDS_ERROR_UNSUPPORTED_TYPE, pszFilename); return PE_Unknown; - } -int Plugin_DDS::TC_PluginFileSaveTexture(const char* pszFilename, MipSet* pMipSet) { +int Plugin_DDS::TC_PluginFileSaveTexture(const char* pszFilename, MipSet* pMipSet) +{ assert(pszFilename); assert(pMipSet); FILE* pFile = NULL; - pFile = fopen( pszFilename, ("wb")); - if(pFile == NULL) { + pFile = fopen(pszFilename, ("wb")); + if (pFile == NULL) + { return PE_Unknown; } - fwrite(&DDS_HEADER,sizeof(CMP_DWORD), 1, pFile); + fwrite(&DDS_HEADER, sizeof(CMP_DWORD), 1, pFile); - if(pMipSet->m_dwFourCC == CMP_FOURCC_G8) + if (pMipSet->m_dwFourCC == CMP_FOURCC_G8) return SaveDDS_G8(pFile, pMipSet); - else if(pMipSet->m_dwFourCC == CMP_FOURCC_A8) + else if (pMipSet->m_dwFourCC == CMP_FOURCC_A8) return SaveDDS_A8(pFile, pMipSet); - else if(IsD3D10Format(pMipSet)) + else if (IsD3D10Format(pMipSet)) return SaveDDS_DX10(pFile, pMipSet); - else if(pMipSet->m_dwFourCC) + else if (pMipSet->m_dwFourCC) return SaveDDS_FourCC(pFile, pMipSet); - else if(pMipSet->m_ChannelFormat == CF_Float16) { - if(pMipSet->m_TextureDataType == TDT_R) + else if (pMipSet->m_ChannelFormat == CF_Float16) + { + if (pMipSet->m_TextureDataType == TDT_R) return SaveDDS_R16F(pFile, pMipSet); - else if(pMipSet->m_TextureDataType == TDT_RG) + else if (pMipSet->m_TextureDataType == TDT_RG) return SaveDDS_RG16F(pFile, pMipSet); else return SaveDDS_ABGR16F(pFile, pMipSet); - } else if(pMipSet->m_ChannelFormat == CF_Float32) { - if(pMipSet->m_TextureDataType == TDT_R) + } + else if (pMipSet->m_ChannelFormat == CF_Float32) + { + if (pMipSet->m_TextureDataType == TDT_R) return SaveDDS_R32F(pFile, pMipSet); - else if(pMipSet->m_TextureDataType == TDT_RG) + else if (pMipSet->m_TextureDataType == TDT_RG) return SaveDDS_RG32F(pFile, pMipSet); else return SaveDDS_ABGR32F(pFile, pMipSet); - } else if(pMipSet->m_ChannelFormat == CF_2101010) + } + else if (pMipSet->m_ChannelFormat == CF_2101010) return SaveDDS_ARGB2101010(pFile, pMipSet); - else if(pMipSet->m_ChannelFormat == CF_16bit) { - if(pMipSet->m_TextureDataType == TDT_R) + else if (pMipSet->m_ChannelFormat == CF_16bit) + { + if (pMipSet->m_TextureDataType == TDT_R) return SaveDDS_R16(pFile, pMipSet); - else if(pMipSet->m_TextureDataType == TDT_RG) + else if (pMipSet->m_TextureDataType == TDT_RG) return SaveDDS_RG16(pFile, pMipSet); else return SaveDDS_ABGR16(pFile, pMipSet); - } else if(pMipSet->m_TextureDataType == TDT_ARGB) - return SaveDDS_ARGB8888(pFile, pMipSet); - else + } + else if (pMipSet->m_TextureDataType == TDT_RGB) + { return SaveDDS_RGB888(pFile, pMipSet); -} + } + + if (pMipSet->m_format == CMP_FORMAT_RGBA_8888_S) + return SaveDDS_RGBA8888_S(pFile, pMipSet); + return SaveDDS_ARGB8888(pFile, pMipSet); +} diff --git a/applications/_plugins/cimage/dds/dds_dx10.cpp b/applications/_plugins/cimage/dds/dds_dx10.cpp index 04a65b20d..505306a38 100644 --- a/applications/_plugins/cimage/dds/dds_dx10.cpp +++ b/applications/_plugins/cimage/dds/dds_dx10.cpp @@ -205,7 +205,7 @@ TC_PluginError LoadDDS_DX10(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) { case DXGI_FORMAT_BC4_SNORM: pMipSet->m_compressed = true; pMipSet->m_format = CMP_FORMAT_BC4_S; - err = LoadDDS_DX10_FourCC(pFile, pDDSD, pMipSet, CMP_FOURCC_BC4); + err = LoadDDS_DX10_FourCC(pFile, pDDSD, pMipSet, CMP_FOURCC_BC4S); break; case DXGI_FORMAT_BC5_TYPELESS: @@ -218,7 +218,7 @@ TC_PluginError LoadDDS_DX10(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) { case DXGI_FORMAT_BC5_SNORM: pMipSet->m_format = CMP_FORMAT_BC5_S; pMipSet->m_compressed = true; - err = LoadDDS_DX10_FourCC(pFile, pDDSD, pMipSet, CMP_FOURCC_BC5); + err = LoadDDS_DX10_FourCC(pFile, pDDSD, pMipSet, CMP_FOURCC_BC5S); break; case DXGI_FORMAT_BC6H_TYPELESS: diff --git a/applications/_plugins/cimage/dds/dds_file.cpp b/applications/_plugins/cimage/dds/dds_file.cpp index f792e1e40..1f6576d1c 100644 --- a/applications/_plugins/cimage/dds/dds_file.cpp +++ b/applications/_plugins/cimage/dds/dds_file.cpp @@ -24,7 +24,6 @@ #pragma once - // Windows Header Files: #ifdef _WIN32 #include @@ -49,38 +48,47 @@ #include "dds_file.h" #include "dds_helpers.h" #include "texture.h" +#include "atiformats.h" - -TC_PluginError LoadDDS_FourCC(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) { - void* extra; +TC_PluginError LoadDDS_FourCC(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) +{ + void* extra; TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_Compressed, TDT_XRGB, PreLoopFourCC, LoopFourCC, PostLoopFourCC); fclose(pFile); + + // Try to set MipSet format for know FourCC formats + if (pMipSet->m_format == CMP_FORMAT_Unknown) + pMipSet->m_format = CMP_FourCC2Format(pDDSD->ddpfPixelFormat.dwFourCC); + return err; } -TC_PluginError LoadDDS_RGB565(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) { - void* extra; +TC_PluginError LoadDDS_RGB565(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) +{ + void* extra; TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_8bit, TDT_XRGB, PreLoopRGB565, LoopRGB565, PostLoopRGB565); + //pMipSet->m_format = CMP_FORMAT_RGB_565; fclose(pFile); return err; } -TC_PluginError LoadDDS_RGB888(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) { - void* extra; - TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_8bit, TDT_XRGB, - PreLoopRGB888, LoopRGB888, PostLoopRGB888); +TC_PluginError LoadDDS_RGB888(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) +{ + void* extra; + TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_8bit, TDT_XRGB, PreLoopRGB888, LoopRGB888, PostLoopRGB888); + pMipSet->m_format = CMP_FORMAT_ARGB_8888; fclose(pFile); return err; } -TC_PluginError LoadDDS_RGB8888(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet, bool bAlpha) { +TC_PluginError LoadDDS_RGB8888(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet, bool bAlpha) +{ ARGB8888Struct* pARGB8888Struct = (ARGB8888Struct*)calloc(sizeof(ARGB8888Struct), 1); - void* extra = pARGB8888Struct; + void* extra = pARGB8888Struct; //if (pDDSD->ddpfPixelFormat.dwFlags & DDPF_RGB) // pMipSet->m_format = CMP_FORMAT_ABGR_8888; //else - pMipSet->m_format = CMP_FORMAT_ARGB_8888; /* if(pDDSD->dwFlags & DDSD_PIXELFORMAT) @@ -101,39 +109,104 @@ TC_PluginError LoadDDS_RGB8888(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet, bool */ pARGB8888Struct->nFlags |= EF_UseBitMasks; - if(pARGB8888Struct->nFlags & EF_UseBitMasks) { + if (pARGB8888Struct->nFlags & EF_UseBitMasks) + { //using bitmasks pARGB8888Struct->pMemory = malloc(4 * pDDSD->dwWidth * pDDSD->dwHeight); - if (pMipSet->m_swizzle) { + if (pMipSet->m_swizzle) + { pARGB8888Struct->nBMask = pDDSD->ddpfPixelFormat.dwRBitMask; pARGB8888Struct->nGMask = pDDSD->ddpfPixelFormat.dwGBitMask; pARGB8888Struct->nRMask = pDDSD->ddpfPixelFormat.dwBBitMask; - } else { + } + else + { pARGB8888Struct->nRMask = pDDSD->ddpfPixelFormat.dwRBitMask; pARGB8888Struct->nGMask = pDDSD->ddpfPixelFormat.dwGBitMask; pARGB8888Struct->nBMask = pDDSD->ddpfPixelFormat.dwBBitMask; } + int shift = 0; + int tempMask = pARGB8888Struct->nRMask; + while (!(tempMask & 0xFF) && tempMask) + { + shift += 8; + tempMask >>= 8; + } + pARGB8888Struct->nRShift = shift; + + shift = 0; + tempMask = pARGB8888Struct->nGMask; + while (!(tempMask & 0xFF) && tempMask) + { + shift += 8; + tempMask >>= 8; + } + pARGB8888Struct->nGShift = shift; + + shift = 0; + tempMask = pARGB8888Struct->nBMask; + while (!(tempMask & 0xFF) && tempMask) + { + shift += 8; + tempMask >>= 8; + } + pARGB8888Struct->nBShift = shift; + } + + pMipSet->m_TextureDataType = bAlpha ? TDT_ARGB : TDT_XRGB; + + TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_8bit, pMipSet->m_TextureDataType, PreLoopRGB8888, LoopRGB8888, PostLoopRGB8888); + fclose(pFile); + return err; +} + +TC_PluginError LoadDDS_RGB8888_S(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet, bool bAlpha) +{ + ARGB8888Struct* pARGB8888Struct = (ARGB8888Struct*)calloc(sizeof(ARGB8888Struct), 1); + void* extra = pARGB8888Struct; + + pARGB8888Struct->nFlags |= EF_UseBitMasks; + + if (pARGB8888Struct->nFlags & EF_UseBitMasks) + { + //using bitmasks + pARGB8888Struct->pMemory = malloc(4 * pDDSD->dwWidth * pDDSD->dwHeight); + if (pMipSet->m_swizzle) + { + pARGB8888Struct->nBMask = pDDSD->ddpfPixelFormat.dwRBitMask; + pARGB8888Struct->nGMask = pDDSD->ddpfPixelFormat.dwGBitMask; + pARGB8888Struct->nRMask = pDDSD->ddpfPixelFormat.dwBBitMask; + } + else + { + pARGB8888Struct->nRMask = pDDSD->ddpfPixelFormat.dwRBitMask; + pARGB8888Struct->nGMask = pDDSD->ddpfPixelFormat.dwGBitMask; + pARGB8888Struct->nBMask = pDDSD->ddpfPixelFormat.dwBBitMask; + } - int shift = 0; + int shift = 0; int tempMask = pARGB8888Struct->nRMask; - while(!(tempMask & 0xFF) && tempMask) { + while (!(tempMask & 0xFF) && tempMask) + { shift += 8; tempMask >>= 8; } pARGB8888Struct->nRShift = shift; - shift = 0; + shift = 0; tempMask = pARGB8888Struct->nGMask; - while(!(tempMask & 0xFF) && tempMask) { + while (!(tempMask & 0xFF) && tempMask) + { shift += 8; tempMask >>= 8; } pARGB8888Struct->nGShift = shift; - shift = 0; + shift = 0; tempMask = pARGB8888Struct->nBMask; - while(!(tempMask & 0xFF) && tempMask) { + while (!(tempMask & 0xFF) && tempMask) + { shift += 8; tempMask >>= 8; } @@ -142,127 +215,150 @@ TC_PluginError LoadDDS_RGB8888(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet, bool pMipSet->m_TextureDataType = bAlpha ? TDT_ARGB : TDT_XRGB; - TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_8bit, pMipSet->m_TextureDataType, - PreLoopRGB8888, LoopRGB8888, PostLoopRGB8888); + TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_8bit, pMipSet->m_TextureDataType, PreLoopRGB8888, LoopRGB8888_S, PostLoopRGB8888); fclose(pFile); return err; } -TC_PluginError LoadDDS_ARGB2101010(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) { - pMipSet->m_TextureDataType = TDT_ARGB; - ChannelFormat channelFormat = CF_2101010; - void* pChannelFormat = &channelFormat; - TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, pChannelFormat, channelFormat, pMipSet->m_TextureDataType, - PreLoopDefault, (pDDSD->ddpfPixelFormat.dwRBitMask==0x3ff00000) ? LoopR10G10B10A2 : LoopDefault, PostLoopDefault); + +TC_PluginError LoadDDS_ARGB2101010(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) +{ + pMipSet->m_TextureDataType = TDT_ARGB; + ChannelFormat channelFormat = CF_2101010; + void* pChannelFormat = &channelFormat; + TC_PluginError err = GenericLoadFunction(pFile, + pDDSD, + pMipSet, + pChannelFormat, + channelFormat, + pMipSet->m_TextureDataType, + PreLoopDefault, + (pDDSD->ddpfPixelFormat.dwRBitMask == 0x3ff00000) ? LoopR10G10B10A2 : LoopDefault, + PostLoopDefault); + pMipSet->m_format = CMP_FORMAT_ARGB_2101010; fclose(pFile); return err; } -TC_PluginError LoadDDS_ABGR32F(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) { - void* extra; - TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_Float32, TDT_ARGB, - PreLoopABGR32F, LoopABGR32F, PostLoopABGR32F); +TC_PluginError LoadDDS_ABGR32F(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) +{ + void* extra; + TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_Float32, TDT_ARGB, PreLoopABGR32F, LoopABGR32F, PostLoopABGR32F); + pMipSet->m_format = CMP_FORMAT_ARGB_32F; fclose(pFile); return err; } -TC_PluginError LoadDDS_GR32F(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) { - void* extra; - TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_Float32, TDT_RG, - PreLoopABGR32F, LoopABGR32F, PostLoopABGR32F); +TC_PluginError LoadDDS_GR32F(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) +{ + void* extra; + TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_Float32, TDT_RG, PreLoopABGR32F, LoopABGR32F, PostLoopABGR32F); + //pMipSet->m_format = CMP_FORMAT_GR32F; fclose(pFile); return err; } -TC_PluginError LoadDDS_R32F(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) { - void* extra; - TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_Float32, TDT_R, - PreLoopABGR32F, LoopABGR32F, PostLoopABGR32F); +TC_PluginError LoadDDS_R32F(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) +{ + void* extra; + TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_Float32, TDT_R, PreLoopABGR32F, LoopABGR32F, PostLoopABGR32F); + //pMipSet->m_format = CMP_FORMAT_R32F; fclose(pFile); return err; } -TC_PluginError LoadDDS_R16F(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) { - void* extra; - TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_Float16, TDT_R, - PreLoopABGR16F, LoopABGR16F, PostLoopABGR16F); +TC_PluginError LoadDDS_R16F(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) +{ + void* extra; + TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_Float16, TDT_R, PreLoopABGR16F, LoopABGR16F, PostLoopABGR16F); + //pMipSet->m_format = CMP_FORMAT_R16F; fclose(pFile); return err; } -TC_PluginError LoadDDS_G16R16F(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) { - void* extra; - TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_Float16, TDT_RG, - PreLoopABGR16F, LoopABGR16F, PostLoopABGR16F); +TC_PluginError LoadDDS_G16R16F(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) +{ + void* extra; + TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_Float16, TDT_RG, PreLoopABGR16F, LoopABGR16F, PostLoopABGR16F); + //pMipSet->m_format = CMP_FORMAT_G16R16F; fclose(pFile); return err; } -TC_PluginError LoadDDS_ABGR16F(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) { - void* extra; - TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_Float16, TDT_ARGB, - PreLoopABGR16F, LoopABGR16F, PostLoopABGR16F); +TC_PluginError LoadDDS_ABGR16F(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) +{ + void* extra; + TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_Float16, TDT_ARGB, PreLoopABGR16F, LoopABGR16F, PostLoopABGR16F); + pMipSet->m_format = CMP_FORMAT_ABGR_16F; fclose(pFile); return err; } -TC_PluginError LoadDDS_G8(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) { - void* extra; - TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_Compressed, TDT_XRGB, - PreLoopG8, LoopG8, PostLoopG8); +TC_PluginError LoadDDS_G8(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) +{ + void* extra; + TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_Compressed, TDT_XRGB, PreLoopG8, LoopG8, PostLoopG8); + //pMipSet->m_format = CMP_FORMAT_G8; fclose(pFile); return err; } -TC_PluginError LoadDDS_AG8(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) { - void* extra; - TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_Compressed, TDT_ARGB, - PreLoopAG8, LoopAG8, PostLoopAG8); +TC_PluginError LoadDDS_AG8(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) +{ + void* extra; + TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_Compressed, TDT_ARGB, PreLoopAG8, LoopAG8, PostLoopAG8); + //pMipSet->m_format = CMP_FORMAT_AG8; fclose(pFile); return err; } -TC_PluginError LoadDDS_G16(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) { - void* extra; - TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_Compressed, TDT_XRGB, - PreLoopG16, LoopG16, PostLoopG16); +TC_PluginError LoadDDS_G16(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) +{ + void* extra; + TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_Compressed, TDT_XRGB, PreLoopG16, LoopG16, PostLoopG16); + //pMipSet->m_format = CMP_FORMAT_G16; fclose(pFile); return err; } -TC_PluginError LoadDDS_A8(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) { - void* extra; - TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_Compressed, TDT_ARGB, - PreLoopA8, LoopA8, PostLoopA8); +TC_PluginError LoadDDS_A8(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) +{ + void* extra; + TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_Compressed, TDT_ARGB, PreLoopA8, LoopA8, PostLoopA8); + //pMipSet->m_format = CMP_FORMAT_A8; fclose(pFile); return err; } -TC_PluginError LoadDDS_ABGR16(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) { - void* extra; - TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_16bit, TDT_ARGB, - PreLoopABGR16, LoopABGR16, PostLoopABGR16); +TC_PluginError LoadDDS_ABGR16(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) +{ + void* extra; + TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_16bit, TDT_ARGB, PreLoopABGR16, LoopABGR16, PostLoopABGR16); + pMipSet->m_format = CMP_FORMAT_ABGR_16; fclose(pFile); return err; } -TC_PluginError LoadDDS_G16R16(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) { - void* extra; - TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_16bit, TDT_RG, - PreLoopG16R16, LoopABGR16, PostLoopG16R16); +TC_PluginError LoadDDS_G16R16(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) +{ + void* extra; + TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_16bit, TDT_RG, PreLoopG16R16, LoopABGR16, PostLoopG16R16); + //pMipSet->m_format = CMP_FORMAT_G16R16; fclose(pFile); return err; } -TC_PluginError LoadDDS_R16(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) { - void* extra; - TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_16bit, TDT_R, - PreLoopG16R16, LoopABGR16, PostLoopG16R16); +TC_PluginError LoadDDS_R16(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet) +{ + void* extra; + TC_PluginError err = GenericLoadFunction(pFile, pDDSD, pMipSet, extra, CF_16bit, TDT_R, PreLoopR16, LoopR16, PostLoopR16); + pMipSet->m_format = CMP_FORMAT_R_16; fclose(pFile); return err; } -TC_PluginError SaveDDS_RGB888(FILE* pFile, const MipSet* pMipSet) { +TC_PluginError SaveDDS_RGB888(FILE* pFile, const MipSet* pMipSet) +{ assert(pFile); assert(pMipSet); @@ -270,25 +366,28 @@ TC_PluginError SaveDDS_RGB888(FILE* pFile, const MipSet* pMipSet) { DDSD2 ddsd2; SetupDDSD(ddsd2, pMipSet, false); - ddsd2.ddpfPixelFormat.dwRBitMask = 0x00ff0000; - ddsd2.ddpfPixelFormat.dwGBitMask = 0x0000ff00; - ddsd2.ddpfPixelFormat.dwBBitMask = 0x000000ff; - ddsd2.lPitch = pMipSet->m_nWidth * 3; - ddsd2.ddpfPixelFormat.dwRGBBitCount = 24; - ddsd2.ddpfPixelFormat.dwFlags=DDPF_RGB; + ddsd2.ddpfPixelFormat.dwRBitMask = 0x00ff0000; + ddsd2.ddpfPixelFormat.dwGBitMask = 0x0000ff00; + ddsd2.ddpfPixelFormat.dwBBitMask = 0x000000ff; + ddsd2.lPitch = pMipSet->m_nWidth * 3; + ddsd2.ddpfPixelFormat.dwRGBBitCount = 24; + ddsd2.ddpfPixelFormat.dwFlags = DDPF_RGB; ddsd2.ddpfPixelFormat.dwRGBAlphaBitMask = 0x00000000; // Write the data fwrite(&ddsd2, sizeof(DDSD2), 1, pFile); int nSlices = (pMipSet->m_TextureType == TT_2D) ? 1 : CMP_MaxFacesOrSlices(pMipSet, 0); - for(int nSlice = 0; nSlice < nSlices; nSlice++) { - for(int nMipLevel = 0 ; nMipLevel < pMipSet->m_nMipLevels ; nMipLevel++) { + for (int nSlice = 0; nSlice < nSlices; nSlice++) + { + for (int nMipLevel = 0; nMipLevel < pMipSet->m_nMipLevels; nMipLevel++) + { CMP_BYTE* pData = DDS_CMips->GetMipLevel(pMipSet, nMipLevel, nSlice)->m_pbData; - CMP_BYTE* pEnd = pData + DDS_CMips->GetMipLevel(pMipSet, nMipLevel, nSlice)->m_dwLinearSize; - while(pData < pEnd) { + CMP_BYTE* pEnd = pData + DDS_CMips->GetMipLevel(pMipSet, nMipLevel, nSlice)->m_dwLinearSize; + while (pData < pEnd) + { fwrite(pData, 3, 1, pFile); - pData += 4; + pData += 3; } } } @@ -298,7 +397,8 @@ TC_PluginError SaveDDS_RGB888(FILE* pFile, const MipSet* pMipSet) { return PE_OK; } -TC_PluginError SaveDDS_ARGB8888(FILE* pFile, const MipSet* pMipSet) { +TC_PluginError SaveDDS_ARGB8888(FILE* pFile, const MipSet* pMipSet) +{ assert(pFile); assert(pMipSet); @@ -306,29 +406,33 @@ TC_PluginError SaveDDS_ARGB8888(FILE* pFile, const MipSet* pMipSet) { DDSD2 ddsd2; SetupDDSD(ddsd2, pMipSet, false); - ddsd2.ddpfPixelFormat.dwRBitMask = 0x00ff0000; - ddsd2.ddpfPixelFormat.dwGBitMask = 0x0000ff00; - ddsd2.ddpfPixelFormat.dwBBitMask = 0x000000ff; - ddsd2.lPitch = pMipSet->m_nWidth * 4; - ddsd2.ddpfPixelFormat.dwRGBBitCount = 32; - ddsd2.ddpfPixelFormat.dwFlags=DDPF_ALPHAPIXELS|DDPF_RGB; + ddsd2.lPitch = pMipSet->m_nWidth * 4; + ddsd2.ddpfPixelFormat.dwRGBBitCount = 32; + + ddsd2.ddpfPixelFormat.dwFlags = DDPF_ALPHAPIXELS | DDPF_RGB; + ddsd2.ddpfPixelFormat.dwRBitMask = 0x00ff0000; + ddsd2.ddpfPixelFormat.dwGBitMask = 0x0000ff00; + ddsd2.ddpfPixelFormat.dwBBitMask = 0x000000ff; ddsd2.ddpfPixelFormat.dwRGBAlphaBitMask = 0xff000000; // Write the data fwrite(&ddsd2, sizeof(DDSD2), 1, pFile); int nSlices = (pMipSet->m_TextureType == TT_2D) ? 1 : CMP_MaxFacesOrSlices(pMipSet, 0); - for(int nSlice = 0; nSlice < nSlices; nSlice++) - for(int nMipLevel = 0 ; nMipLevel < pMipSet->m_nMipLevels ; nMipLevel++) { + for (int nSlice = 0; nSlice < nSlices; nSlice++) + for (int nMipLevel = 0; nMipLevel < pMipSet->m_nMipLevels; nMipLevel++) + { CMP_BYTE* pbData = DDS_CMips->GetMipLevel(pMipSet, nMipLevel, nSlice)->m_pbData; - CMP_BYTE temp; - int i = 0; - int height = DDS_CMips->GetMipLevel(pMipSet, nMipLevel, nSlice)->m_nHeight; - int width = DDS_CMips->GetMipLevel(pMipSet, nMipLevel, nSlice)->m_nWidth; - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - temp = pbData[i]; - pbData[i] = pbData[i + 2]; + CMP_BYTE temp; + int i = 0; + int height = DDS_CMips->GetMipLevel(pMipSet, nMipLevel, nSlice)->m_nHeight; + int width = DDS_CMips->GetMipLevel(pMipSet, nMipLevel, nSlice)->m_nWidth; + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + temp = pbData[i]; + pbData[i] = pbData[i + 2]; pbData[i + 2] = temp; i += 4; } @@ -340,7 +444,8 @@ TC_PluginError SaveDDS_ARGB8888(FILE* pFile, const MipSet* pMipSet) { return PE_OK; } -TC_PluginError SaveDDS_ARGB2101010(FILE* pFile, const MipSet* pMipSet) { +TC_PluginError SaveDDS_RGBA8888_S(FILE* pFile, const MipSet* pMipSet) +{ assert(pFile); assert(pMipSet); @@ -348,24 +453,90 @@ TC_PluginError SaveDDS_ARGB2101010(FILE* pFile, const MipSet* pMipSet) { DDSD2 ddsd2; SetupDDSD(ddsd2, pMipSet, false); - ddsd2.ddpfPixelFormat.dwRBitMask = 0x000003ff; - ddsd2.ddpfPixelFormat.dwGBitMask = 0x000ffc00; - ddsd2.ddpfPixelFormat.dwBBitMask = 0x3ff00000; - ddsd2.ddpfPixelFormat.dwRGBAlphaBitMask = 0xc0000000; - ddsd2.lPitch = pMipSet->m_nWidth * 4; + ddsd2.lPitch = pMipSet->m_nWidth * 4; ddsd2.ddpfPixelFormat.dwRGBBitCount = 32; - ddsd2.ddpfPixelFormat.dwFlags=DDPF_ALPHAPIXELS|DDPF_RGB; + + ddsd2.ddpfPixelFormat.dwFlags = DDS_BUMPDUDV; + ddsd2.ddpfPixelFormat.dwRBitMask = 0x000000ff; + ddsd2.ddpfPixelFormat.dwGBitMask = 0x0000ff00; + ddsd2.ddpfPixelFormat.dwBBitMask = 0x00ff0000; + ddsd2.ddpfPixelFormat.dwRGBAlphaBitMask = 0xff000000; + + + if (pMipSet->m_TextureDataType == TDT_ARGB) + ddsd2.ddpfPixelFormat.dwFlags |= DDPF_ALPHAPIXELS; // Write the data fwrite(&ddsd2, sizeof(DDSD2), 1, pFile); int nSlices = (pMipSet->m_TextureType == TT_2D) ? 1 : CMP_MaxFacesOrSlices(pMipSet, 0); - for(int nSlice = 0; nSlice < nSlices; nSlice++) - for(int nMipLevel = 0 ; nMipLevel < pMipSet->m_nMipLevels ; nMipLevel++) - if (pMipSet->m_swizzle) { + for (int nSlice = 0; nSlice < nSlices; nSlice++) + for (int nMipLevel = 0; nMipLevel < pMipSet->m_nMipLevels; nMipLevel++) + { + CMP_BYTE* pbData = DDS_CMips->GetMipLevel(pMipSet, nMipLevel, nSlice)->m_pbData; + CMP_BYTE* pData = new uint8_t[DDS_CMips->GetMipLevel(pMipSet, nMipLevel)->m_dwLinearSize]; + CMP_BYTE R,G,B,A; + int i = 0; + int height = DDS_CMips->GetMipLevel(pMipSet, nMipLevel, nSlice)->m_nHeight; + int width = DDS_CMips->GetMipLevel(pMipSet, nMipLevel, nSlice)->m_nWidth; + + // Map MipSet data to bitMapp output + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + R = pbData[i]; + G = pbData[i + 1]; + B = pbData[i + 2]; + pData[i ] = R; + pData[i+1] = G; + pData[i+2] = B; + if (pMipSet->m_TextureDataType == TDT_ARGB) + A = pbData[i + 3]; + else + A = 127; + pData[i + 3] = A; + i += 4; + } + } + + fwrite(pData, (DDS_CMips->GetMipLevel(pMipSet, nMipLevel)->m_dwLinearSize), 1, pFile); + delete pData; + } + fclose(pFile); + + return PE_OK; +} + +TC_PluginError SaveDDS_ARGB2101010(FILE* pFile, const MipSet* pMipSet) +{ + assert(pFile); + assert(pMipSet); + + // Initialise surface descriptor + DDSD2 ddsd2; + SetupDDSD(ddsd2, pMipSet, false); + + ddsd2.ddpfPixelFormat.dwRBitMask = 0x000003ff; + ddsd2.ddpfPixelFormat.dwGBitMask = 0x000ffc00; + ddsd2.ddpfPixelFormat.dwBBitMask = 0x3ff00000; + ddsd2.ddpfPixelFormat.dwRGBAlphaBitMask = 0xc0000000; + ddsd2.lPitch = pMipSet->m_nWidth * 4; + ddsd2.ddpfPixelFormat.dwRGBBitCount = 32; + ddsd2.ddpfPixelFormat.dwFlags = DDPF_ALPHAPIXELS | DDPF_RGB; + + // Write the data + fwrite(&ddsd2, sizeof(DDSD2), 1, pFile); + + int nSlices = (pMipSet->m_TextureType == TT_2D) ? 1 : CMP_MaxFacesOrSlices(pMipSet, 0); + for (int nSlice = 0; nSlice < nSlices; nSlice++) + for (int nMipLevel = 0; nMipLevel < pMipSet->m_nMipLevels; nMipLevel++) + if (pMipSet->m_swizzle) + { // to do swizzle data fwrite(DDS_CMips->GetMipLevel(pMipSet, nMipLevel, nSlice)->m_pbData, DDS_CMips->GetMipLevel(pMipSet, nMipLevel)->m_dwLinearSize, 1, pFile); - } else + } + else fwrite(DDS_CMips->GetMipLevel(pMipSet, nMipLevel, nSlice)->m_pbData, DDS_CMips->GetMipLevel(pMipSet, nMipLevel)->m_dwLinearSize, 1, pFile); fclose(pFile); @@ -373,7 +544,8 @@ TC_PluginError SaveDDS_ARGB2101010(FILE* pFile, const MipSet* pMipSet) { return PE_OK; } -TC_PluginError SaveDDS_ABGR16(FILE* pFile, const MipSet* pMipSet) { +TC_PluginError SaveDDS_ABGR16(FILE* pFile, const MipSet* pMipSet) +{ assert(pFile); assert(pMipSet); @@ -381,16 +553,16 @@ TC_PluginError SaveDDS_ABGR16(FILE* pFile, const MipSet* pMipSet) { DDSD2 ddsd2; SetupDDSD(ddsd2, pMipSet, false); - ddsd2.lPitch = pMipSet->m_nWidth * 8; - ddsd2.ddpfPixelFormat.dwFlags = DDPF_FOURCC|DDPF_ALPHAPIXELS; + ddsd2.lPitch = pMipSet->m_nWidth * 8; + ddsd2.ddpfPixelFormat.dwFlags = DDPF_FOURCC | DDPF_ALPHAPIXELS; ddsd2.ddpfPixelFormat.dwFourCC = D3DFMT_A16B16G16R16; // Write the data fwrite(&ddsd2, sizeof(DDSD2), 1, pFile); int nSlices = (pMipSet->m_TextureType == TT_2D) ? 1 : CMP_MaxFacesOrSlices(pMipSet, 0); - for(int nSlice = 0; nSlice < nSlices; nSlice++) - for(int nMipLevel = 0 ; nMipLevel < pMipSet->m_nMipLevels ; nMipLevel++) + for (int nSlice = 0; nSlice < nSlices; nSlice++) + for (int nMipLevel = 0; nMipLevel < pMipSet->m_nMipLevels; nMipLevel++) fwrite(DDS_CMips->GetMipLevel(pMipSet, nMipLevel, nSlice)->m_pbData, DDS_CMips->GetMipLevel(pMipSet, nMipLevel)->m_dwLinearSize, 1, pFile); fclose(pFile); @@ -398,7 +570,8 @@ TC_PluginError SaveDDS_ABGR16(FILE* pFile, const MipSet* pMipSet) { return PE_OK; } -TC_PluginError SaveDDS_R16(FILE* pFile, const MipSet* pMipSet) { +TC_PluginError SaveDDS_R16(FILE* pFile, const MipSet* pMipSet) +{ assert(pFile); assert(pMipSet); @@ -406,16 +579,16 @@ TC_PluginError SaveDDS_R16(FILE* pFile, const MipSet* pMipSet) { DDSD2 ddsd2; SetupDDSD(ddsd2, pMipSet, false); - ddsd2.lPitch = pMipSet->m_nWidth * 2; - ddsd2.ddpfPixelFormat.dwFlags = DDPF_FOURCC|DDPF_ALPHAPIXELS; + ddsd2.lPitch = pMipSet->m_nWidth * 2; + ddsd2.ddpfPixelFormat.dwFlags = DDPF_FOURCC | DDPF_ALPHAPIXELS; ddsd2.ddpfPixelFormat.dwFourCC = D3DFMT_L16; // Write the data fwrite(&ddsd2, sizeof(DDSD2), 1, pFile); int nSlices = (pMipSet->m_TextureType == TT_2D) ? 1 : CMP_MaxFacesOrSlices(pMipSet, 0); - for(int nSlice = 0; nSlice < nSlices; nSlice++) - for(int nMipLevel = 0 ; nMipLevel < pMipSet->m_nMipLevels ; nMipLevel++) + for (int nSlice = 0; nSlice < nSlices; nSlice++) + for (int nMipLevel = 0; nMipLevel < pMipSet->m_nMipLevels; nMipLevel++) fwrite(DDS_CMips->GetMipLevel(pMipSet, nMipLevel, nSlice)->m_pbData, DDS_CMips->GetMipLevel(pMipSet, nMipLevel)->m_dwLinearSize, 1, pFile); fclose(pFile); @@ -423,7 +596,8 @@ TC_PluginError SaveDDS_R16(FILE* pFile, const MipSet* pMipSet) { return PE_OK; } -TC_PluginError SaveDDS_RG16(FILE* pFile, const MipSet* pMipSet) { +TC_PluginError SaveDDS_RG16(FILE* pFile, const MipSet* pMipSet) +{ assert(pFile); assert(pMipSet); @@ -431,16 +605,16 @@ TC_PluginError SaveDDS_RG16(FILE* pFile, const MipSet* pMipSet) { DDSD2 ddsd2; SetupDDSD(ddsd2, pMipSet, false); - ddsd2.lPitch = pMipSet->m_nWidth * 4; - ddsd2.ddpfPixelFormat.dwFlags = DDPF_FOURCC|DDPF_ALPHAPIXELS; + ddsd2.lPitch = pMipSet->m_nWidth * 4; + ddsd2.ddpfPixelFormat.dwFlags = DDPF_FOURCC | DDPF_ALPHAPIXELS; ddsd2.ddpfPixelFormat.dwFourCC = D3DFMT_G16R16; // Write the data fwrite(&ddsd2, sizeof(DDSD2), 1, pFile); int nSlices = (pMipSet->m_TextureType == TT_2D) ? 1 : CMP_MaxFacesOrSlices(pMipSet, 0); - for(int nSlice = 0; nSlice < nSlices; nSlice++) - for(int nMipLevel = 0 ; nMipLevel < pMipSet->m_nMipLevels ; nMipLevel++) + for (int nSlice = 0; nSlice < nSlices; nSlice++) + for (int nMipLevel = 0; nMipLevel < pMipSet->m_nMipLevels; nMipLevel++) fwrite(DDS_CMips->GetMipLevel(pMipSet, nMipLevel, nSlice)->m_pbData, DDS_CMips->GetMipLevel(pMipSet, nMipLevel)->m_dwLinearSize, 1, pFile); fclose(pFile); @@ -448,7 +622,8 @@ TC_PluginError SaveDDS_RG16(FILE* pFile, const MipSet* pMipSet) { return PE_OK; } -TC_PluginError SaveDDS_ABGR16F(FILE* pFile, const MipSet* pMipSet) { +TC_PluginError SaveDDS_ABGR16F(FILE* pFile, const MipSet* pMipSet) +{ assert(pFile); assert(pMipSet); @@ -456,16 +631,16 @@ TC_PluginError SaveDDS_ABGR16F(FILE* pFile, const MipSet* pMipSet) { DDSD2 ddsd2; SetupDDSD(ddsd2, pMipSet, false); - ddsd2.lPitch = pMipSet->m_nWidth * 8; - ddsd2.ddpfPixelFormat.dwFlags = DDPF_FOURCC|DDPF_ALPHAPIXELS; + ddsd2.lPitch = pMipSet->m_nWidth * 8; + ddsd2.ddpfPixelFormat.dwFlags = DDPF_FOURCC | DDPF_ALPHAPIXELS; ddsd2.ddpfPixelFormat.dwFourCC = D3DFMT_A16B16G16R16F; // Write the data fwrite(&ddsd2, sizeof(DDSD2), 1, pFile); int nSlices = (pMipSet->m_TextureType == TT_2D) ? 1 : CMP_MaxFacesOrSlices(pMipSet, 0); - for(int nSlice = 0; nSlice < nSlices; nSlice++) - for(int nMipLevel = 0 ; nMipLevel < pMipSet->m_nMipLevels ; nMipLevel++) + for (int nSlice = 0; nSlice < nSlices; nSlice++) + for (int nMipLevel = 0; nMipLevel < pMipSet->m_nMipLevels; nMipLevel++) fwrite(DDS_CMips->GetMipLevel(pMipSet, nMipLevel, nSlice)->m_pbData, DDS_CMips->GetMipLevel(pMipSet, nMipLevel)->m_dwLinearSize, 1, pFile); fclose(pFile); @@ -473,7 +648,8 @@ TC_PluginError SaveDDS_ABGR16F(FILE* pFile, const MipSet* pMipSet) { return PE_OK; } -TC_PluginError SaveDDS_R16F(FILE* pFile, const MipSet* pMipSet) { +TC_PluginError SaveDDS_R16F(FILE* pFile, const MipSet* pMipSet) +{ assert(pFile); assert(pMipSet); @@ -481,16 +657,16 @@ TC_PluginError SaveDDS_R16F(FILE* pFile, const MipSet* pMipSet) { DDSD2 ddsd2; SetupDDSD(ddsd2, pMipSet, false); - ddsd2.lPitch = pMipSet->m_nWidth * 2; - ddsd2.ddpfPixelFormat.dwFlags = DDPF_FOURCC|DDPF_ALPHAPIXELS; + ddsd2.lPitch = pMipSet->m_nWidth * 2; + ddsd2.ddpfPixelFormat.dwFlags = DDPF_FOURCC | DDPF_ALPHAPIXELS; ddsd2.ddpfPixelFormat.dwFourCC = D3DFMT_R16F; // Write the data fwrite(&ddsd2, sizeof(DDSD2), 1, pFile); int nSlices = (pMipSet->m_TextureType == TT_2D) ? 1 : CMP_MaxFacesOrSlices(pMipSet, 0); - for(int nSlice = 0; nSlice < nSlices; nSlice++) - for(int nMipLevel = 0 ; nMipLevel < pMipSet->m_nMipLevels ; nMipLevel++) + for (int nSlice = 0; nSlice < nSlices; nSlice++) + for (int nMipLevel = 0; nMipLevel < pMipSet->m_nMipLevels; nMipLevel++) fwrite(DDS_CMips->GetMipLevel(pMipSet, nMipLevel, nSlice)->m_pbData, DDS_CMips->GetMipLevel(pMipSet, nMipLevel)->m_dwLinearSize, 1, pFile); fclose(pFile); @@ -498,7 +674,8 @@ TC_PluginError SaveDDS_R16F(FILE* pFile, const MipSet* pMipSet) { return PE_OK; } -TC_PluginError SaveDDS_RG16F(FILE* pFile, const MipSet* pMipSet) { +TC_PluginError SaveDDS_RG16F(FILE* pFile, const MipSet* pMipSet) +{ assert(pFile); assert(pMipSet); @@ -506,16 +683,16 @@ TC_PluginError SaveDDS_RG16F(FILE* pFile, const MipSet* pMipSet) { DDSD2 ddsd2; SetupDDSD(ddsd2, pMipSet, false); - ddsd2.lPitch = pMipSet->m_nWidth * 4; - ddsd2.ddpfPixelFormat.dwFlags = DDPF_FOURCC|DDPF_ALPHAPIXELS; + ddsd2.lPitch = pMipSet->m_nWidth * 4; + ddsd2.ddpfPixelFormat.dwFlags = DDPF_FOURCC | DDPF_ALPHAPIXELS; ddsd2.ddpfPixelFormat.dwFourCC = D3DFMT_G16R16F; // Write the data fwrite(&ddsd2, sizeof(DDSD2), 1, pFile); int nSlices = (pMipSet->m_TextureType == TT_2D) ? 1 : CMP_MaxFacesOrSlices(pMipSet, 0); - for(int nSlice = 0; nSlice < nSlices; nSlice++) - for(int nMipLevel = 0 ; nMipLevel < pMipSet->m_nMipLevels ; nMipLevel++) + for (int nSlice = 0; nSlice < nSlices; nSlice++) + for (int nMipLevel = 0; nMipLevel < pMipSet->m_nMipLevels; nMipLevel++) fwrite(DDS_CMips->GetMipLevel(pMipSet, nMipLevel, nSlice)->m_pbData, DDS_CMips->GetMipLevel(pMipSet, nMipLevel)->m_dwLinearSize, 1, pFile); fclose(pFile); @@ -523,7 +700,8 @@ TC_PluginError SaveDDS_RG16F(FILE* pFile, const MipSet* pMipSet) { return PE_OK; } -TC_PluginError SaveDDS_ABGR32F(FILE* pFile, const MipSet* pMipSet) { +TC_PluginError SaveDDS_ABGR32F(FILE* pFile, const MipSet* pMipSet) +{ assert(pFile); assert(pMipSet); @@ -531,16 +709,16 @@ TC_PluginError SaveDDS_ABGR32F(FILE* pFile, const MipSet* pMipSet) { DDSD2 ddsd2; SetupDDSD(ddsd2, pMipSet, false); - ddsd2.lPitch = pMipSet->m_nWidth * 16; - ddsd2.ddpfPixelFormat.dwFlags=DDPF_FOURCC|DDPF_ALPHAPIXELS; + ddsd2.lPitch = pMipSet->m_nWidth * 16; + ddsd2.ddpfPixelFormat.dwFlags = DDPF_FOURCC | DDPF_ALPHAPIXELS; ddsd2.ddpfPixelFormat.dwFourCC = D3DFMT_A32B32G32R32F; // Write the data fwrite(&ddsd2, sizeof(DDSD2), 1, pFile); int nSlices = (pMipSet->m_TextureType == TT_2D) ? 1 : CMP_MaxFacesOrSlices(pMipSet, 0); - for(int nSlice = 0; nSlice < nSlices; nSlice++) - for(int nMipLevel = 0 ; nMipLevel < pMipSet->m_nMipLevels ; nMipLevel++) + for (int nSlice = 0; nSlice < nSlices; nSlice++) + for (int nMipLevel = 0; nMipLevel < pMipSet->m_nMipLevels; nMipLevel++) fwrite(DDS_CMips->GetMipLevel(pMipSet, nMipLevel, nSlice)->m_pbData, DDS_CMips->GetMipLevel(pMipSet, nMipLevel)->m_dwLinearSize, 1, pFile); fclose(pFile); @@ -548,7 +726,8 @@ TC_PluginError SaveDDS_ABGR32F(FILE* pFile, const MipSet* pMipSet) { return PE_OK; } -TC_PluginError SaveDDS_R32F(FILE* pFile, const MipSet* pMipSet) { +TC_PluginError SaveDDS_R32F(FILE* pFile, const MipSet* pMipSet) +{ assert(pFile); assert(pMipSet); @@ -556,16 +735,16 @@ TC_PluginError SaveDDS_R32F(FILE* pFile, const MipSet* pMipSet) { DDSD2 ddsd2; SetupDDSD(ddsd2, pMipSet, false); - ddsd2.lPitch = pMipSet->m_nWidth * 4; - ddsd2.ddpfPixelFormat.dwFlags=DDPF_FOURCC|DDPF_ALPHAPIXELS; + ddsd2.lPitch = pMipSet->m_nWidth * 4; + ddsd2.ddpfPixelFormat.dwFlags = DDPF_FOURCC | DDPF_ALPHAPIXELS; ddsd2.ddpfPixelFormat.dwFourCC = D3DFMT_R32F; // Write the data fwrite(&ddsd2, sizeof(DDSD2), 1, pFile); int nSlices = (pMipSet->m_TextureType == TT_2D) ? 1 : CMP_MaxFacesOrSlices(pMipSet, 0); - for(int nSlice = 0; nSlice < nSlices; nSlice++) - for(int nMipLevel = 0 ; nMipLevel < pMipSet->m_nMipLevels ; nMipLevel++) + for (int nSlice = 0; nSlice < nSlices; nSlice++) + for (int nMipLevel = 0; nMipLevel < pMipSet->m_nMipLevels; nMipLevel++) fwrite(DDS_CMips->GetMipLevel(pMipSet, nMipLevel, nSlice)->m_pbData, DDS_CMips->GetMipLevel(pMipSet, nMipLevel)->m_dwLinearSize, 1, pFile); fclose(pFile); @@ -573,7 +752,8 @@ TC_PluginError SaveDDS_R32F(FILE* pFile, const MipSet* pMipSet) { return PE_OK; } -TC_PluginError SaveDDS_RG32F(FILE* pFile, const MipSet* pMipSet) { +TC_PluginError SaveDDS_RG32F(FILE* pFile, const MipSet* pMipSet) +{ assert(pFile); assert(pMipSet); @@ -581,16 +761,16 @@ TC_PluginError SaveDDS_RG32F(FILE* pFile, const MipSet* pMipSet) { DDSD2 ddsd2; SetupDDSD(ddsd2, pMipSet, false); - ddsd2.lPitch = pMipSet->m_nWidth * 8; - ddsd2.ddpfPixelFormat.dwFlags=DDPF_FOURCC|DDPF_ALPHAPIXELS; + ddsd2.lPitch = pMipSet->m_nWidth * 8; + ddsd2.ddpfPixelFormat.dwFlags = DDPF_FOURCC | DDPF_ALPHAPIXELS; ddsd2.ddpfPixelFormat.dwFourCC = D3DFMT_G32R32F; // Write the data fwrite(&ddsd2, sizeof(DDSD2), 1, pFile); int nSlices = (pMipSet->m_TextureType == TT_2D) ? 1 : CMP_MaxFacesOrSlices(pMipSet, 0); - for(int nSlice = 0; nSlice < nSlices; nSlice++) - for(int nMipLevel = 0 ; nMipLevel < pMipSet->m_nMipLevels ; nMipLevel++) + for (int nSlice = 0; nSlice < nSlices; nSlice++) + for (int nMipLevel = 0; nMipLevel < pMipSet->m_nMipLevels; nMipLevel++) fwrite(DDS_CMips->GetMipLevel(pMipSet, nMipLevel, nSlice)->m_pbData, DDS_CMips->GetMipLevel(pMipSet, nMipLevel)->m_dwLinearSize, 1, pFile); fclose(pFile); @@ -598,27 +778,36 @@ TC_PluginError SaveDDS_RG32F(FILE* pFile, const MipSet* pMipSet) { return PE_OK; } -TC_PluginError SaveDDS_FourCC(FILE* pFile, const MipSet* pMipSet) { +TC_PluginError SaveDDS_FourCC(FILE* pFile, const MipSet* pMipSet) +{ assert(pFile); assert(pMipSet); DDSD2 ddsd2; SetupDDSD(ddsd2, pMipSet, true); - ddsd2.ddpfPixelFormat.dwFlags=DDPF_FOURCC; - if(pMipSet->m_TextureDataType == TDT_ARGB) - ddsd2.ddpfPixelFormat.dwFlags |= DDPF_ALPHAPIXELS; - if(pMipSet->m_Flags & MS_AlphaPremult) - ddsd2.ddpfPixelFormat.dwFlags |= DDPF_ALPHAPREMULT; - ddsd2.ddpfPixelFormat.dwFourCC = pMipSet->m_dwFourCC; + ddsd2.ddpfPixelFormat.dwFlags = DDPF_FOURCC; + + if (pMipSet->m_ChannelFormat != CF_Compressed) + { + if (pMipSet->m_TextureDataType == TDT_ARGB) + ddsd2.ddpfPixelFormat.dwFlags |= DDPF_ALPHAPIXELS; + if (pMipSet->m_Flags & MS_AlphaPremult) + ddsd2.ddpfPixelFormat.dwFlags |= DDPF_ALPHAPREMULT; + } + else + { + ddsd2.dwDepth = 1; + } + + ddsd2.ddpfPixelFormat.dwFourCC = pMipSet->m_dwFourCC; ddsd2.ddpfPixelFormat.dwPrivateFormatBitCount = pMipSet->m_dwFourCC2; - // Write the data fwrite(&ddsd2, sizeof(DDSD2), 1, pFile); int nSlices = (pMipSet->m_TextureType == TT_2D) ? 1 : CMP_MaxFacesOrSlices(pMipSet, 0); - for(int nSlice = 0; nSlice < nSlices; nSlice++) - for(int nMipLevel = 0 ; nMipLevel < pMipSet->m_nMipLevels ; nMipLevel++) + for (int nSlice = 0; nSlice < nSlices; nSlice++) + for (int nMipLevel = 0; nMipLevel < pMipSet->m_nMipLevels; nMipLevel++) fwrite(DDS_CMips->GetMipLevel(pMipSet, nMipLevel, nSlice)->m_pbData, DDS_CMips->GetMipLevel(pMipSet, nMipLevel)->m_dwLinearSize, 1, pFile); fclose(pFile); @@ -626,24 +815,25 @@ TC_PluginError SaveDDS_FourCC(FILE* pFile, const MipSet* pMipSet) { return PE_OK; } -TC_PluginError SaveDDS_G8(FILE* pFile, const MipSet* pMipSet) { +TC_PluginError SaveDDS_G8(FILE* pFile, const MipSet* pMipSet) +{ assert(pFile); assert(pMipSet); DDSD2 ddsd2; SetupDDSD(ddsd2, pMipSet, false); - ddsd2.lPitch = pMipSet->m_nWidth * 8; - ddsd2.ddpfPixelFormat.dwFlags=DDPF_LUMINANCE; + ddsd2.lPitch = pMipSet->m_nWidth * 8; + ddsd2.ddpfPixelFormat.dwFlags = DDPF_LUMINANCE; ddsd2.ddpfPixelFormat.dwLuminanceBitCount = 8; - ddsd2.ddpfPixelFormat.dwLuminanceBitMask = 0xff; + ddsd2.ddpfPixelFormat.dwLuminanceBitMask = 0xff; // Write the data fwrite(&ddsd2, sizeof(DDSD2), 1, pFile); int nSlices = (pMipSet->m_TextureType == TT_2D) ? 1 : CMP_MaxFacesOrSlices(pMipSet, 0); - for(int nSlice = 0; nSlice < nSlices; nSlice++) - for(int nMipLevel = 0 ; nMipLevel < pMipSet->m_nMipLevels ; nMipLevel++) + for (int nSlice = 0; nSlice < nSlices; nSlice++) + for (int nMipLevel = 0; nMipLevel < pMipSet->m_nMipLevels; nMipLevel++) fwrite(DDS_CMips->GetMipLevel(pMipSet, nMipLevel, nSlice)->m_pbData, DDS_CMips->GetMipLevel(pMipSet, nMipLevel)->m_dwLinearSize, 1, pFile); fclose(pFile); @@ -651,28 +841,28 @@ TC_PluginError SaveDDS_G8(FILE* pFile, const MipSet* pMipSet) { return PE_OK; } -TC_PluginError SaveDDS_A8(FILE* pFile, const MipSet* pMipSet) { +TC_PluginError SaveDDS_A8(FILE* pFile, const MipSet* pMipSet) +{ assert(pFile); assert(pMipSet); DDSD2 ddsd2; SetupDDSD(ddsd2, pMipSet, false); - ddsd2.lPitch = pMipSet->m_nWidth * 8; - ddsd2.ddpfPixelFormat.dwFlags=DDPF_ALPHA; - ddsd2.ddpfPixelFormat.dwAlphaBitDepth = 8; + ddsd2.lPitch = pMipSet->m_nWidth * 8; + ddsd2.ddpfPixelFormat.dwFlags = DDPF_ALPHA; + ddsd2.ddpfPixelFormat.dwAlphaBitDepth = 8; ddsd2.ddpfPixelFormat.dwRGBAlphaBitMask = 0xff; // Write the data fwrite(&ddsd2, sizeof(DDSD2), 1, pFile); int nSlices = (pMipSet->m_TextureType == TT_2D) ? 1 : CMP_MaxFacesOrSlices(pMipSet, 0); - for(int nSlice = 0; nSlice < nSlices; nSlice++) - for(int nMipLevel = 0 ; nMipLevel < pMipSet->m_nMipLevels ; nMipLevel++) + for (int nSlice = 0; nSlice < nSlices; nSlice++) + for (int nMipLevel = 0; nMipLevel < pMipSet->m_nMipLevels; nMipLevel++) fwrite(DDS_CMips->GetMipLevel(pMipSet, nMipLevel, nSlice)->m_pbData, DDS_CMips->GetMipLevel(pMipSet, nMipLevel)->m_dwLinearSize, 1, pFile); fclose(pFile); return PE_OK; } - diff --git a/applications/_plugins/cimage/dds/dds_file.h b/applications/_plugins/cimage/dds/dds_file.h index 14dc36928..54fd315db 100644 --- a/applications/_plugins/cimage/dds/dds_file.h +++ b/applications/_plugins/cimage/dds/dds_file.h @@ -21,6 +21,17 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // +//-------------------------------------------------------------------------------------- +// This header defines constants and structures that are useful when parsing +// DDS files. DDS files were originally designed to use several structures +// and constants that are native to DirectDraw and are defined in ddraw.h, +// such as DDSURFACEDESC2 and DDSCAPS2. This file defines similar +// (compatible) constants and structures so that one can use DDS files +// without needing to include ddraw.h. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +//-------------------------------------------------------------------------------------- #ifndef _DDS_FILE_H #define _DDS_FILE_H @@ -36,456 +47,531 @@ #ifndef _WIN32 -typedef struct _DDCOLORKEY { - CMP_DWORD dwColorSpaceLowValue; // low boundary of color space that is to +typedef struct _DDCOLORKEY +{ + CMP_DWORD dwColorSpaceLowValue; // low boundary of color space that is to // be treated as Color Key, inclusive - CMP_DWORD dwColorSpaceHighValue; // high boundary of color space that is + CMP_DWORD dwColorSpaceHighValue; // high boundary of color space that is // to be treated as Color Key, inclusive } DDCOLORKEY; -typedef struct _DDSCAPS2 { - CMP_DWORD dwCaps; // capabilities of surface wanted - CMP_DWORD dwCaps2; - CMP_DWORD dwCaps3; - union { - CMP_DWORD dwCaps4; - CMP_DWORD dwVolumeDepth; +typedef struct _DDSCAPS2 +{ + CMP_DWORD dwCaps; // capabilities of surface wanted + CMP_DWORD dwCaps2; + CMP_DWORD dwCaps3; + union + { + CMP_DWORD dwCaps4; + CMP_DWORD dwVolumeDepth; }; } DDSCAPS2; -typedef struct _DDPIXELFORMAT { - CMP_DWORD dwSize; // size of structure - CMP_DWORD dwFlags; // pixel format flags - CMP_DWORD dwFourCC; // (FOURCC code) - union { - CMP_DWORD dwRGBBitCount; // how many bits per pixel - CMP_DWORD dwYUVBitCount; // how many bits per pixel - CMP_DWORD dwZBufferBitDepth; // how many total bits/pixel in z buffer (including any stencil bits) - CMP_DWORD dwAlphaBitDepth; // how many bits for alpha channels - CMP_DWORD dwLuminanceBitCount; // how many bits per pixel - CMP_DWORD dwBumpBitCount; // how many bits per "buxel", total - CMP_DWORD dwPrivateFormatBitCount;// Bits per pixel of private driver formats. Only valid in texture +typedef struct _DDPIXELFORMAT +{ + CMP_DWORD dwSize; // size of structure + CMP_DWORD dwFlags; // pixel format flags + CMP_DWORD dwFourCC; // (FOURCC code) + union + { + CMP_DWORD dwRGBBitCount; // how many bits per pixel + CMP_DWORD dwYUVBitCount; // how many bits per pixel + CMP_DWORD dwZBufferBitDepth; // how many total bits/pixel in z buffer (including any stencil bits) + CMP_DWORD dwAlphaBitDepth; // how many bits for alpha channels + CMP_DWORD dwLuminanceBitCount; // how many bits per pixel + CMP_DWORD dwBumpBitCount; // how many bits per "buxel", total + CMP_DWORD dwPrivateFormatBitCount; // Bits per pixel of private driver formats. Only valid in texture // format list and if DDPF_D3DFORMAT is set }; - union { - CMP_DWORD dwRBitMask; // mask for red bit - CMP_DWORD dwYBitMask; // mask for Y bits - CMP_DWORD dwStencilBitDepth; // how many stencil bits (note: dwZBufferBitDepth-dwStencilBitDepth is total Z-only bits) - CMP_DWORD dwLuminanceBitMask; // mask for luminance bits - CMP_DWORD dwBumpDuBitMask; // mask for bump map U delta bits - CMP_DWORD dwOperations; // DDPF_D3DFORMAT Operations + union + { + CMP_DWORD dwRBitMask; // mask for red bit + CMP_DWORD dwYBitMask; // mask for Y bits + CMP_DWORD dwStencilBitDepth; // how many stencil bits (note: dwZBufferBitDepth-dwStencilBitDepth is total Z-only bits) + CMP_DWORD dwLuminanceBitMask; // mask for luminance bits + CMP_DWORD dwBumpDuBitMask; // mask for bump map U delta bits + CMP_DWORD dwOperations; // DDPF_D3DFORMAT Operations }; - union { - CMP_DWORD dwGBitMask; // mask for green bits - CMP_DWORD dwUBitMask; // mask for U bits - CMP_DWORD dwZBitMask; // mask for Z bits - CMP_DWORD dwBumpDvBitMask; // mask for bump map V delta bits - struct { - CMP_WORD wFlipMSTypes; // Multisample methods supported via flip for this D3DFORMAT - CMP_WORD wBltMSTypes; // Multisample methods supported via blt for this D3DFORMAT + union + { + CMP_DWORD dwGBitMask; // mask for green bits + CMP_DWORD dwUBitMask; // mask for U bits + CMP_DWORD dwZBitMask; // mask for Z bits + CMP_DWORD dwBumpDvBitMask; // mask for bump map V delta bits + struct + { + CMP_WORD wFlipMSTypes; // Multisample methods supported via flip for this D3DFORMAT + CMP_WORD wBltMSTypes; // Multisample methods supported via blt for this D3DFORMAT } MultiSampleCaps; - }; - union { - CMP_DWORD dwBBitMask; // mask for blue bits - CMP_DWORD dwVBitMask; // mask for V bits - CMP_DWORD dwStencilBitMask; // mask for stencil bits - CMP_DWORD dwBumpLuminanceBitMask; // mask for luminance in bump map + union + { + CMP_DWORD dwBBitMask; // mask for blue bits + CMP_DWORD dwVBitMask; // mask for V bits + CMP_DWORD dwStencilBitMask; // mask for stencil bits + CMP_DWORD dwBumpLuminanceBitMask; // mask for luminance in bump map }; - union { - CMP_DWORD dwRGBAlphaBitMask; // mask for alpha channel - CMP_DWORD dwYUVAlphaBitMask; // mask for alpha channel - CMP_DWORD dwLuminanceAlphaBitMask;// mask for alpha channel - CMP_DWORD dwRGBZBitMask; // mask for Z channel - CMP_DWORD dwYUVZBitMask; // mask for Z channel + union + { + CMP_DWORD dwRGBAlphaBitMask; // mask for alpha channel + CMP_DWORD dwYUVAlphaBitMask; // mask for alpha channel + CMP_DWORD dwLuminanceAlphaBitMask; // mask for alpha channel + CMP_DWORD dwRGBZBitMask; // mask for Z channel + CMP_DWORD dwYUVZBitMask; // mask for Z channel }; } DDPIXELFORMAT; -#define DDSD_CAPS 0x00000001l // default -#define DDSD_HEIGHT 0x00000002l -#define DDSD_WIDTH 0x00000004l -#define DDSD_PITCH 0x00000008l -#define DDSD_BACKBUFFERCOUNT 0x00000020l -#define DDSD_ZBUFFERBITDEPTH 0x00000040l -#define DDSD_ALPHABITDEPTH 0x00000080l -#define DDSD_LPSURFACE 0x00000800l -#define DDSD_PIXELFORMAT 0x00001000l -#define DDSD_CKDESTOVERLAY 0x00002000l -#define DDSD_CKDESTBLT 0x00004000l -#define DDSD_CKSRCOVERLAY 0x00008000l -#define DDSD_CKSRCBLT 0x00010000l -#define DDSD_MIPMAPCOUNT 0x00020000l -#define DDSD_REFRESHRATE 0x00040000l -#define DDSD_LINEARSIZE 0x00080000l -#define DDSD_TEXTURESTAGE 0x00100000l -#define DDSD_FVF 0x00200000l -#define DDSD_SRCVBHANDLE 0x00400000l -#define DDSD_DEPTH 0x00800000l -#define DDSD_ALL 0x00fff9eel - -#define DDSCAPS_RESERVED1 0x00000001l -#define DDSCAPS_ALPHA 0x00000002l -#define DDSCAPS_BACKBUFFER 0x00000004l -#define DDSCAPS_COMPLEX 0x00000008l -#define DDSCAPS_FLIP 0x00000010l -#define DDSCAPS_FRONTBUFFER 0x00000020l -#define DDSCAPS_OFFSCREENPLAIN 0x00000040l -#define DDSCAPS_OVERLAY 0x00000080l -#define DDSCAPS_PALETTE 0x00000100l -#define DDSCAPS_PRIMARYSURFACE 0x00000200l -#define DDSCAPS_RESERVED3 0x00000400l -#define DDSCAPS_PRIMARYSURFACELEFT 0x00000000l -#define DDSCAPS_SYSTEMMEMORY 0x00000800l -#define DDSCAPS_TEXTURE 0x00001000l -#define DDSCAPS_3DDEVICE 0x00002000l -#define DDSCAPS_VIDEOMEMORY 0x00004000l -#define DDSCAPS_VISIBLE 0x00008000l -#define DDSCAPS_WRITEONLY 0x00010000l -#define DDSCAPS_ZBUFFER 0x00020000l -#define DDSCAPS_OWNDC 0x00040000l -#define DDSCAPS_LIVEVIDEO 0x00080000l -#define DDSCAPS_HWCODEC 0x00100000l -#define DDSCAPS_MODEX 0x00200000l -#define DDSCAPS_MIPMAP 0x00400000l -#define DDSCAPS_RESERVED2 0x00800000l -#define DDSCAPS_ALLOCONLOAD 0x04000000l -#define DDSCAPS_VIDEOPORT 0x08000000l -#define DDSCAPS_LOCALVIDMEM 0x10000000l -#define DDSCAPS_NONLOCALVIDMEM 0x20000000l -#define DDSCAPS_STANDARDVGAMODE 0x40000000l -#define DDSCAPS_OPTIMIZED 0x80000000l - -#define DDSCAPS2_RESERVED4 0x00000002L -#define DDSCAPS2_HARDWAREDEINTERLACE 0x00000000L -#define DDSCAPS2_HINTDYNAMIC 0x00000004L -#define DDSCAPS2_HINTSTATIC 0x00000008L -#define DDSCAPS2_TEXTUREMANAGE 0x00000010L -#define DDSCAPS2_RESERVED1 0x00000020L -#define DDSCAPS2_RESERVED2 0x00000040L -#define DDSCAPS2_OPAQUE 0x00000080L -#define DDSCAPS2_HINTANTIALIASING 0x00000100L -#define DDSCAPS2_CUBEMAP 0x00000200L -#define DDSCAPS2_CUBEMAP_POSITIVEX 0x00000400L -#define DDSCAPS2_CUBEMAP_NEGATIVEX 0x00000800L -#define DDSCAPS2_CUBEMAP_POSITIVEY 0x00001000L -#define DDSCAPS2_CUBEMAP_NEGATIVEY 0x00002000L -#define DDSCAPS2_CUBEMAP_POSITIVEZ 0x00004000L -#define DDSCAPS2_CUBEMAP_NEGATIVEZ 0x00008000L -#define DDSCAPS2_CUBEMAP_ALLFACES ( DDSCAPS2_CUBEMAP_POSITIVEX |\ - DDSCAPS2_CUBEMAP_NEGATIVEX |\ - DDSCAPS2_CUBEMAP_POSITIVEY |\ - DDSCAPS2_CUBEMAP_NEGATIVEY |\ - DDSCAPS2_CUBEMAP_POSITIVEZ |\ - DDSCAPS2_CUBEMAP_NEGATIVEZ ) - -#define DDSCAPS2_MIPMAPSUBLEVEL 0x00010000L -#define DDSCAPS2_D3DTEXTUREMANAGE 0x00020000L -#define DDSCAPS2_DONOTPERSIST 0x00040000L -#define DDSCAPS2_STEREOSURFACELEFT 0x00080000L -#define DDSCAPS2_VOLUME 0x00200000L -#define DDSCAPS2_NOTUSERLOCKABLE 0x00400000L -#define DDSCAPS2_POINTS 0x00800000L -#define DDSCAPS2_RTPATCHES 0x01000000L -#define DDSCAPS2_NPATCHES 0x02000000L -#define DDSCAPS2_RESERVED3 0x04000000L -#define DDSCAPS2_DISCARDBACKBUFFER 0x10000000L -#define DDSCAPS2_ENABLEALPHACHANNEL 0x20000000L -#define DDSCAPS2_EXTENDEDFORMATPRIMARY 0x40000000L -#define DDSCAPS2_ADDITIONALPRIMARY 0x80000000L -#define DDSCAPS3_MULTISAMPLE_MASK 0x0000001FL -#define DDSCAPS3_MULTISAMPLE_QUALITY_MASK 0x000000E0L -#define DDSCAPS3_MULTISAMPLE_QUALITY_SHIFT 5 -#define DDSCAPS3_RESERVED1 0x00000100L -#define DDSCAPS3_RESERVED2 0x00000200L -#define DDSCAPS3_LIGHTWEIGHTMIPMAP 0x00000400L -#define DDSCAPS3_AUTOGENMIPMAP 0x00000800L -#define DDSCAPS3_DMAP 0x00001000L - -#define DDPF_ALPHAPIXELS 0x00000001l -#define DDPF_ALPHA 0x00000002l -#define DDPF_FOURCC 0x00000004l -#define DDPF_PALETTEINDEXED4 0x00000008l -#define DDPF_PALETTEINDEXEDTO8 0x00000010l -#define DDPF_PALETTEINDEXED8 0x00000020l -#define DDPF_RGB 0x00000040l -#define DDPF_COMPRESSED 0x00000080l -#define DDPF_RGBTOYUV 0x00000100l -#define DDPF_YUV 0x00000200l -#define DDPF_ZBUFFER 0x00000400l -#define DDPF_PALETTEINDEXED1 0x00000800l -#define DDPF_PALETTEINDEXED2 0x00001000l -#define DDPF_ZPIXELS 0x00002000l -#define DDPF_STENCILBUFFER 0x00004000l -#define DDPF_ALPHAPREMULT 0x00008000l -#define DDPF_LUMINANCE 0x00020000l -#define DDPF_BUMPLUMINANCE 0x00040000l -#define DDPF_BUMPDUDV 0x00080000l - -typedef enum DXGI_FORMAT { - DXGI_FORMAT_UNKNOWN = 0, - DXGI_FORMAT_R32G32B32A32_TYPELESS = 1, - DXGI_FORMAT_R32G32B32A32_FLOAT = 2, - DXGI_FORMAT_R32G32B32A32_UINT = 3, - DXGI_FORMAT_R32G32B32A32_SINT = 4, - DXGI_FORMAT_R32G32B32_TYPELESS = 5, - DXGI_FORMAT_R32G32B32_FLOAT = 6, - DXGI_FORMAT_R32G32B32_UINT = 7, - DXGI_FORMAT_R32G32B32_SINT = 8, - DXGI_FORMAT_R16G16B16A16_TYPELESS = 9, - DXGI_FORMAT_R16G16B16A16_FLOAT = 10, - DXGI_FORMAT_R16G16B16A16_UNORM = 11, - DXGI_FORMAT_R16G16B16A16_UINT = 12, - DXGI_FORMAT_R16G16B16A16_SNORM = 13, - DXGI_FORMAT_R16G16B16A16_SINT = 14, - DXGI_FORMAT_R32G32_TYPELESS = 15, - DXGI_FORMAT_R32G32_FLOAT = 16, - DXGI_FORMAT_R32G32_UINT = 17, - DXGI_FORMAT_R32G32_SINT = 18, - DXGI_FORMAT_R32G8X24_TYPELESS = 19, - DXGI_FORMAT_D32_FLOAT_S8X24_UINT = 20, - DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS = 21, - DXGI_FORMAT_X32_TYPELESS_G8X24_UINT = 22, - DXGI_FORMAT_R10G10B10A2_TYPELESS = 23, - DXGI_FORMAT_R10G10B10A2_UNORM = 24, - DXGI_FORMAT_R10G10B10A2_UINT = 25, - DXGI_FORMAT_R11G11B10_FLOAT = 26, - DXGI_FORMAT_R8G8B8A8_TYPELESS = 27, - DXGI_FORMAT_R8G8B8A8_UNORM = 28, - DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29, - DXGI_FORMAT_R8G8B8A8_UINT = 30, - DXGI_FORMAT_R8G8B8A8_SNORM = 31, - DXGI_FORMAT_R8G8B8A8_SINT = 32, - DXGI_FORMAT_R16G16_TYPELESS = 33, - DXGI_FORMAT_R16G16_FLOAT = 34, - DXGI_FORMAT_R16G16_UNORM = 35, - DXGI_FORMAT_R16G16_UINT = 36, - DXGI_FORMAT_R16G16_SNORM = 37, - DXGI_FORMAT_R16G16_SINT = 38, - DXGI_FORMAT_R32_TYPELESS = 39, - DXGI_FORMAT_D32_FLOAT = 40, - DXGI_FORMAT_R32_FLOAT = 41, - DXGI_FORMAT_R32_UINT = 42, - DXGI_FORMAT_R32_SINT = 43, - DXGI_FORMAT_R24G8_TYPELESS = 44, - DXGI_FORMAT_D24_UNORM_S8_UINT = 45, - DXGI_FORMAT_R24_UNORM_X8_TYPELESS = 46, - DXGI_FORMAT_X24_TYPELESS_G8_UINT = 47, - DXGI_FORMAT_R8G8_TYPELESS = 48, - DXGI_FORMAT_R8G8_UNORM = 49, - DXGI_FORMAT_R8G8_UINT = 50, - DXGI_FORMAT_R8G8_SNORM = 51, - DXGI_FORMAT_R8G8_SINT = 52, - DXGI_FORMAT_R16_TYPELESS = 53, - DXGI_FORMAT_R16_FLOAT = 54, - DXGI_FORMAT_D16_UNORM = 55, - DXGI_FORMAT_R16_UNORM = 56, - DXGI_FORMAT_R16_UINT = 57, - DXGI_FORMAT_R16_SNORM = 58, - DXGI_FORMAT_R16_SINT = 59, - DXGI_FORMAT_R8_TYPELESS = 60, - DXGI_FORMAT_R8_UNORM = 61, - DXGI_FORMAT_R8_UINT = 62, - DXGI_FORMAT_R8_SNORM = 63, - DXGI_FORMAT_R8_SINT = 64, - DXGI_FORMAT_A8_UNORM = 65, - DXGI_FORMAT_R1_UNORM = 66, - DXGI_FORMAT_R9G9B9E5_SHAREDEXP = 67, - DXGI_FORMAT_R8G8_B8G8_UNORM = 68, - DXGI_FORMAT_G8R8_G8B8_UNORM = 69, - DXGI_FORMAT_BC1_TYPELESS = 70, - DXGI_FORMAT_BC1_UNORM = 71, - DXGI_FORMAT_BC1_UNORM_SRGB = 72, - DXGI_FORMAT_BC2_TYPELESS = 73, - DXGI_FORMAT_BC2_UNORM = 74, - DXGI_FORMAT_BC2_UNORM_SRGB = 75, - DXGI_FORMAT_BC3_TYPELESS = 76, - DXGI_FORMAT_BC3_UNORM = 77, - DXGI_FORMAT_BC3_UNORM_SRGB = 78, - DXGI_FORMAT_BC4_TYPELESS = 79, - DXGI_FORMAT_BC4_UNORM = 80, - DXGI_FORMAT_BC4_SNORM = 81, - DXGI_FORMAT_BC5_TYPELESS = 82, - DXGI_FORMAT_BC5_UNORM = 83, - DXGI_FORMAT_BC5_SNORM = 84, - DXGI_FORMAT_B5G6R5_UNORM = 85, - DXGI_FORMAT_B5G5R5A1_UNORM = 86, - DXGI_FORMAT_B8G8R8A8_UNORM = 87, - DXGI_FORMAT_B8G8R8X8_UNORM = 88, - DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM = 89, - DXGI_FORMAT_B8G8R8A8_TYPELESS = 90, - DXGI_FORMAT_B8G8R8A8_UNORM_SRGB = 91, - DXGI_FORMAT_B8G8R8X8_TYPELESS = 92, - DXGI_FORMAT_B8G8R8X8_UNORM_SRGB = 93, - DXGI_FORMAT_BC6H_TYPELESS = 94, - DXGI_FORMAT_BC6H_UF16 = 95, - DXGI_FORMAT_BC6H_SF16 = 96, - DXGI_FORMAT_BC7_TYPELESS = 97, - DXGI_FORMAT_BC7_UNORM = 98, - DXGI_FORMAT_BC7_UNORM_SRGB = 99, - DXGI_FORMAT_AYUV = 100, - DXGI_FORMAT_Y410 = 101, - DXGI_FORMAT_Y416 = 102, - DXGI_FORMAT_NV12 = 103, - DXGI_FORMAT_P010 = 104, - DXGI_FORMAT_P016 = 105, - DXGI_FORMAT_420_OPAQUE = 106, - DXGI_FORMAT_YUY2 = 107, - DXGI_FORMAT_Y210 = 108, - DXGI_FORMAT_Y216 = 109, - DXGI_FORMAT_NV11 = 110, - DXGI_FORMAT_AI44 = 111, - DXGI_FORMAT_IA44 = 112, - DXGI_FORMAT_P8 = 113, - DXGI_FORMAT_A8P8 = 114, - DXGI_FORMAT_B4G4R4A4_UNORM = 115, - DXGI_FORMAT_FORCE_UINT = 0xffffffff +#define DDSD_CAPS 0x00000001l // default +#define DDSD_HEIGHT 0x00000002l +#define DDSD_WIDTH 0x00000004l +#define DDSD_PITCH 0x00000008l +#define DDSD_BACKBUFFERCOUNT 0x00000020l +#define DDSD_ZBUFFERBITDEPTH 0x00000040l +#define DDSD_ALPHABITDEPTH 0x00000080l +#define DDSD_LPSURFACE 0x00000800l +#define DDSD_PIXELFORMAT 0x00001000l +#define DDSD_CKDESTOVERLAY 0x00002000l +#define DDSD_CKDESTBLT 0x00004000l +#define DDSD_CKSRCOVERLAY 0x00008000l +#define DDSD_CKSRCBLT 0x00010000l +#define DDSD_MIPMAPCOUNT 0x00020000l +#define DDSD_REFRESHRATE 0x00040000l +#define DDSD_LINEARSIZE 0x00080000l +#define DDSD_TEXTURESTAGE 0x00100000l +#define DDSD_FVF 0x00200000l +#define DDSD_SRCVBHANDLE 0x00400000l +#define DDSD_DEPTH 0x00800000l +#define DDSD_ALL 0x00fff9eel + +#define DDSCAPS_RESERVED1 0x00000001l +#define DDSCAPS_ALPHA 0x00000002l +#define DDSCAPS_BACKBUFFER 0x00000004l +#define DDSCAPS_COMPLEX 0x00000008l +#define DDSCAPS_FLIP 0x00000010l +#define DDSCAPS_FRONTBUFFER 0x00000020l +#define DDSCAPS_OFFSCREENPLAIN 0x00000040l +#define DDSCAPS_OVERLAY 0x00000080l +#define DDSCAPS_PALETTE 0x00000100l +#define DDSCAPS_PRIMARYSURFACE 0x00000200l +#define DDSCAPS_RESERVED3 0x00000400l +#define DDSCAPS_PRIMARYSURFACELEFT 0x00000000l +#define DDSCAPS_SYSTEMMEMORY 0x00000800l +#define DDSCAPS_TEXTURE 0x00001000l +#define DDSCAPS_3DDEVICE 0x00002000l +#define DDSCAPS_VIDEOMEMORY 0x00004000l +#define DDSCAPS_VISIBLE 0x00008000l +#define DDSCAPS_WRITEONLY 0x00010000l +#define DDSCAPS_ZBUFFER 0x00020000l +#define DDSCAPS_OWNDC 0x00040000l +#define DDSCAPS_LIVEVIDEO 0x00080000l +#define DDSCAPS_HWCODEC 0x00100000l +#define DDSCAPS_MODEX 0x00200000l +#define DDSCAPS_MIPMAP 0x00400000l +#define DDSCAPS_RESERVED2 0x00800000l +#define DDSCAPS_ALLOCONLOAD 0x04000000l +#define DDSCAPS_VIDEOPORT 0x08000000l +#define DDSCAPS_LOCALVIDMEM 0x10000000l +#define DDSCAPS_NONLOCALVIDMEM 0x20000000l +#define DDSCAPS_STANDARDVGAMODE 0x40000000l +#define DDSCAPS_OPTIMIZED 0x80000000l + +#define DDSCAPS2_RESERVED4 0x00000002L +#define DDSCAPS2_HARDWAREDEINTERLACE 0x00000000L +#define DDSCAPS2_HINTDYNAMIC 0x00000004L +#define DDSCAPS2_HINTSTATIC 0x00000008L +#define DDSCAPS2_TEXTUREMANAGE 0x00000010L +#define DDSCAPS2_RESERVED1 0x00000020L +#define DDSCAPS2_RESERVED2 0x00000040L +#define DDSCAPS2_OPAQUE 0x00000080L +#define DDSCAPS2_HINTANTIALIASING 0x00000100L +#define DDSCAPS2_CUBEMAP 0x00000200L +#define DDSCAPS2_CUBEMAP_POSITIVEX 0x00000400L +#define DDSCAPS2_CUBEMAP_NEGATIVEX 0x00000800L +#define DDSCAPS2_CUBEMAP_POSITIVEY 0x00001000L +#define DDSCAPS2_CUBEMAP_NEGATIVEY 0x00002000L +#define DDSCAPS2_CUBEMAP_POSITIVEZ 0x00004000L +#define DDSCAPS2_CUBEMAP_NEGATIVEZ 0x00008000L +#define DDSCAPS2_CUBEMAP_ALLFACES \ + (DDSCAPS2_CUBEMAP_POSITIVEX | DDSCAPS2_CUBEMAP_NEGATIVEX | DDSCAPS2_CUBEMAP_POSITIVEY | DDSCAPS2_CUBEMAP_NEGATIVEY | DDSCAPS2_CUBEMAP_POSITIVEZ | \ + DDSCAPS2_CUBEMAP_NEGATIVEZ) + +#define DDSCAPS2_MIPMAPSUBLEVEL 0x00010000L +#define DDSCAPS2_D3DTEXTUREMANAGE 0x00020000L +#define DDSCAPS2_DONOTPERSIST 0x00040000L +#define DDSCAPS2_STEREOSURFACELEFT 0x00080000L +#define DDSCAPS2_VOLUME 0x00200000L +#define DDSCAPS2_NOTUSERLOCKABLE 0x00400000L +#define DDSCAPS2_POINTS 0x00800000L +#define DDSCAPS2_RTPATCHES 0x01000000L +#define DDSCAPS2_NPATCHES 0x02000000L +#define DDSCAPS2_RESERVED3 0x04000000L +#define DDSCAPS2_DISCARDBACKBUFFER 0x10000000L +#define DDSCAPS2_ENABLEALPHACHANNEL 0x20000000L +#define DDSCAPS2_EXTENDEDFORMATPRIMARY 0x40000000L +#define DDSCAPS2_ADDITIONALPRIMARY 0x80000000L +#define DDSCAPS3_MULTISAMPLE_MASK 0x0000001FL +#define DDSCAPS3_MULTISAMPLE_QUALITY_MASK 0x000000E0L +#define DDSCAPS3_MULTISAMPLE_QUALITY_SHIFT 5 +#define DDSCAPS3_RESERVED1 0x00000100L +#define DDSCAPS3_RESERVED2 0x00000200L +#define DDSCAPS3_LIGHTWEIGHTMIPMAP 0x00000400L +#define DDSCAPS3_AUTOGENMIPMAP 0x00000800L +#define DDSCAPS3_DMAP 0x00001000L + +#define DDPF_ALPHAPIXELS 0x00000001l +#define DDPF_ALPHA 0x00000002l +#define DDPF_FOURCC 0x00000004l +#define DDPF_PALETTEINDEXED4 0x00000008l +#define DDPF_PALETTEINDEXEDTO8 0x00000010l +#define DDPF_PALETTEINDEXED8 0x00000020l +#define DDPF_RGB 0x00000040l +#define DDPF_COMPRESSED 0x00000080l +#define DDPF_RGBTOYUV 0x00000100l +#define DDPF_YUV 0x00000200l +#define DDPF_ZBUFFER 0x00000400l +#define DDPF_PALETTEINDEXED1 0x00000800l +#define DDPF_PALETTEINDEXED2 0x00001000l +#define DDPF_ZPIXELS 0x00002000l +#define DDPF_STENCILBUFFER 0x00004000l +#define DDPF_ALPHAPREMULT 0x00008000l +#define DDPF_LUMINANCE 0x00020000l +#define DDPF_BUMPLUMINANCE 0x00040000l +#define DDPF_BUMPDUDV 0x00080000l + +typedef enum DXGI_FORMAT +{ + DXGI_FORMAT_UNKNOWN = 0, + DXGI_FORMAT_R32G32B32A32_TYPELESS = 1, + DXGI_FORMAT_R32G32B32A32_FLOAT = 2, + DXGI_FORMAT_R32G32B32A32_UINT = 3, + DXGI_FORMAT_R32G32B32A32_SINT = 4, + DXGI_FORMAT_R32G32B32_TYPELESS = 5, + DXGI_FORMAT_R32G32B32_FLOAT = 6, + DXGI_FORMAT_R32G32B32_UINT = 7, + DXGI_FORMAT_R32G32B32_SINT = 8, + DXGI_FORMAT_R16G16B16A16_TYPELESS = 9, + DXGI_FORMAT_R16G16B16A16_FLOAT = 10, + DXGI_FORMAT_R16G16B16A16_UNORM = 11, + DXGI_FORMAT_R16G16B16A16_UINT = 12, + DXGI_FORMAT_R16G16B16A16_SNORM = 13, + DXGI_FORMAT_R16G16B16A16_SINT = 14, + DXGI_FORMAT_R32G32_TYPELESS = 15, + DXGI_FORMAT_R32G32_FLOAT = 16, + DXGI_FORMAT_R32G32_UINT = 17, + DXGI_FORMAT_R32G32_SINT = 18, + DXGI_FORMAT_R32G8X24_TYPELESS = 19, + DXGI_FORMAT_D32_FLOAT_S8X24_UINT = 20, + DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS = 21, + DXGI_FORMAT_X32_TYPELESS_G8X24_UINT = 22, + DXGI_FORMAT_R10G10B10A2_TYPELESS = 23, + DXGI_FORMAT_R10G10B10A2_UNORM = 24, + DXGI_FORMAT_R10G10B10A2_UINT = 25, + DXGI_FORMAT_R11G11B10_FLOAT = 26, + DXGI_FORMAT_R8G8B8A8_TYPELESS = 27, + DXGI_FORMAT_R8G8B8A8_UNORM = 28, + DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29, + DXGI_FORMAT_R8G8B8A8_UINT = 30, + DXGI_FORMAT_R8G8B8A8_SNORM = 31, + DXGI_FORMAT_R8G8B8A8_SINT = 32, + DXGI_FORMAT_R16G16_TYPELESS = 33, + DXGI_FORMAT_R16G16_FLOAT = 34, + DXGI_FORMAT_R16G16_UNORM = 35, + DXGI_FORMAT_R16G16_UINT = 36, + DXGI_FORMAT_R16G16_SNORM = 37, + DXGI_FORMAT_R16G16_SINT = 38, + DXGI_FORMAT_R32_TYPELESS = 39, + DXGI_FORMAT_D32_FLOAT = 40, + DXGI_FORMAT_R32_FLOAT = 41, + DXGI_FORMAT_R32_UINT = 42, + DXGI_FORMAT_R32_SINT = 43, + DXGI_FORMAT_R24G8_TYPELESS = 44, + DXGI_FORMAT_D24_UNORM_S8_UINT = 45, + DXGI_FORMAT_R24_UNORM_X8_TYPELESS = 46, + DXGI_FORMAT_X24_TYPELESS_G8_UINT = 47, + DXGI_FORMAT_R8G8_TYPELESS = 48, + DXGI_FORMAT_R8G8_UNORM = 49, + DXGI_FORMAT_R8G8_UINT = 50, + DXGI_FORMAT_R8G8_SNORM = 51, + DXGI_FORMAT_R8G8_SINT = 52, + DXGI_FORMAT_R16_TYPELESS = 53, + DXGI_FORMAT_R16_FLOAT = 54, + DXGI_FORMAT_D16_UNORM = 55, + DXGI_FORMAT_R16_UNORM = 56, + DXGI_FORMAT_R16_UINT = 57, + DXGI_FORMAT_R16_SNORM = 58, + DXGI_FORMAT_R16_SINT = 59, + DXGI_FORMAT_R8_TYPELESS = 60, + DXGI_FORMAT_R8_UNORM = 61, + DXGI_FORMAT_R8_UINT = 62, + DXGI_FORMAT_R8_SNORM = 63, + DXGI_FORMAT_R8_SINT = 64, + DXGI_FORMAT_A8_UNORM = 65, + DXGI_FORMAT_R1_UNORM = 66, + DXGI_FORMAT_R9G9B9E5_SHAREDEXP = 67, + DXGI_FORMAT_R8G8_B8G8_UNORM = 68, + DXGI_FORMAT_G8R8_G8B8_UNORM = 69, + DXGI_FORMAT_BC1_TYPELESS = 70, + DXGI_FORMAT_BC1_UNORM = 71, + DXGI_FORMAT_BC1_UNORM_SRGB = 72, + DXGI_FORMAT_BC2_TYPELESS = 73, + DXGI_FORMAT_BC2_UNORM = 74, + DXGI_FORMAT_BC2_UNORM_SRGB = 75, + DXGI_FORMAT_BC3_TYPELESS = 76, + DXGI_FORMAT_BC3_UNORM = 77, + DXGI_FORMAT_BC3_UNORM_SRGB = 78, + DXGI_FORMAT_BC4_TYPELESS = 79, + DXGI_FORMAT_BC4_UNORM = 80, + DXGI_FORMAT_BC4_SNORM = 81, + DXGI_FORMAT_BC5_TYPELESS = 82, + DXGI_FORMAT_BC5_UNORM = 83, + DXGI_FORMAT_BC5_SNORM = 84, + DXGI_FORMAT_B5G6R5_UNORM = 85, + DXGI_FORMAT_B5G5R5A1_UNORM = 86, + DXGI_FORMAT_B8G8R8A8_UNORM = 87, + DXGI_FORMAT_B8G8R8X8_UNORM = 88, + DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM = 89, + DXGI_FORMAT_B8G8R8A8_TYPELESS = 90, + DXGI_FORMAT_B8G8R8A8_UNORM_SRGB = 91, + DXGI_FORMAT_B8G8R8X8_TYPELESS = 92, + DXGI_FORMAT_B8G8R8X8_UNORM_SRGB = 93, + DXGI_FORMAT_BC6H_TYPELESS = 94, + DXGI_FORMAT_BC6H_UF16 = 95, + DXGI_FORMAT_BC6H_SF16 = 96, + DXGI_FORMAT_BC7_TYPELESS = 97, + DXGI_FORMAT_BC7_UNORM = 98, + DXGI_FORMAT_BC7_UNORM_SRGB = 99, + DXGI_FORMAT_AYUV = 100, + DXGI_FORMAT_Y410 = 101, + DXGI_FORMAT_Y416 = 102, + DXGI_FORMAT_NV12 = 103, + DXGI_FORMAT_P010 = 104, + DXGI_FORMAT_P016 = 105, + DXGI_FORMAT_420_OPAQUE = 106, + DXGI_FORMAT_YUY2 = 107, + DXGI_FORMAT_Y210 = 108, + DXGI_FORMAT_Y216 = 109, + DXGI_FORMAT_NV11 = 110, + DXGI_FORMAT_AI44 = 111, + DXGI_FORMAT_IA44 = 112, + DXGI_FORMAT_P8 = 113, + DXGI_FORMAT_A8P8 = 114, + DXGI_FORMAT_B4G4R4A4_UNORM = 115, + DXGI_FORMAT_FORCE_UINT = 0xffffffff } DXGI_FORMAT; -typedef enum _D3DFORMAT { - D3DFMT_UNKNOWN = 0, - - D3DFMT_R8G8B8 = 20, - D3DFMT_A8R8G8B8 = 21, - D3DFMT_X8R8G8B8 = 22, - D3DFMT_R5G6B5 = 23, - D3DFMT_X1R5G5B5 = 24, - D3DFMT_A1R5G5B5 = 25, - D3DFMT_A4R4G4B4 = 26, - D3DFMT_R3G3B2 = 27, - D3DFMT_A8 = 28, - D3DFMT_A8R3G3B2 = 29, - D3DFMT_X4R4G4B4 = 30, - D3DFMT_A2B10G10R10 = 31, - D3DFMT_A8B8G8R8 = 32, - D3DFMT_X8B8G8R8 = 33, - D3DFMT_G16R16 = 34, - D3DFMT_A2R10G10B10 = 35, - D3DFMT_A16B16G16R16 = 36, - - D3DFMT_A8P8 = 40, - D3DFMT_P8 = 41, - - D3DFMT_L8 = 50, - D3DFMT_A8L8 = 51, - D3DFMT_A4L4 = 52, - - D3DFMT_V8U8 = 60, - D3DFMT_L6V5U5 = 61, - D3DFMT_X8L8V8U8 = 62, - D3DFMT_Q8W8V8U8 = 63, - D3DFMT_V16U16 = 64, - D3DFMT_A2W10V10U10 = 67, - - D3DFMT_UYVY = CMP_MAKEFOURCC('U', 'Y', 'V', 'Y'), - D3DFMT_R8G8_B8G8 = CMP_MAKEFOURCC('R', 'G', 'B', 'G'), - D3DFMT_YUY2 = CMP_MAKEFOURCC('Y', 'U', 'Y', '2'), - D3DFMT_G8R8_G8B8 = CMP_MAKEFOURCC('G', 'R', 'G', 'B'), - D3DFMT_DXT1 = CMP_MAKEFOURCC('D', 'X', 'T', '1'), - D3DFMT_DXT2 = CMP_MAKEFOURCC('D', 'X', 'T', '2'), - D3DFMT_DXT3 = CMP_MAKEFOURCC('D', 'X', 'T', '3'), - D3DFMT_DXT4 = CMP_MAKEFOURCC('D', 'X', 'T', '4'), - D3DFMT_DXT5 = CMP_MAKEFOURCC('D', 'X', 'T', '5'), - - D3DFMT_D16_LOCKABLE = 70, - D3DFMT_D32 = 71, - D3DFMT_D15S1 = 73, - D3DFMT_D24S8 = 75, - D3DFMT_D24X8 = 77, - D3DFMT_D24X4S4 = 79, - D3DFMT_D16 = 80, - - D3DFMT_D32F_LOCKABLE = 82, - D3DFMT_D24FS8 = 83, - - D3DFMT_L16 = 81, - - D3DFMT_VERTEXDATA =100, - D3DFMT_INDEX16 =101, - D3DFMT_INDEX32 =102, - - D3DFMT_Q16W16V16U16 =110, - - D3DFMT_MULTI2_ARGB8 = CMP_MAKEFOURCC('M','E','T','1'), +typedef enum _D3DFORMAT +{ + D3DFMT_UNKNOWN = 0, + + D3DFMT_R8G8B8 = 20, + D3DFMT_A8R8G8B8 = 21, + D3DFMT_X8R8G8B8 = 22, + D3DFMT_R5G6B5 = 23, + D3DFMT_X1R5G5B5 = 24, + D3DFMT_A1R5G5B5 = 25, + D3DFMT_A4R4G4B4 = 26, + D3DFMT_R3G3B2 = 27, + D3DFMT_A8 = 28, + D3DFMT_A8R3G3B2 = 29, + D3DFMT_X4R4G4B4 = 30, + D3DFMT_A2B10G10R10 = 31, + D3DFMT_A8B8G8R8 = 32, + D3DFMT_X8B8G8R8 = 33, + D3DFMT_G16R16 = 34, + D3DFMT_A2R10G10B10 = 35, + D3DFMT_A16B16G16R16 = 36, + + D3DFMT_A8P8 = 40, + D3DFMT_P8 = 41, + + D3DFMT_L8 = 50, + D3DFMT_A8L8 = 51, + D3DFMT_A4L4 = 52, + + D3DFMT_V8U8 = 60, + D3DFMT_L6V5U5 = 61, + D3DFMT_X8L8V8U8 = 62, + D3DFMT_Q8W8V8U8 = 63, + D3DFMT_V16U16 = 64, + D3DFMT_A2W10V10U10 = 67, + + D3DFMT_UYVY = CMP_MAKEFOURCC('U', 'Y', 'V', 'Y'), + D3DFMT_R8G8_B8G8 = CMP_MAKEFOURCC('R', 'G', 'B', 'G'), + D3DFMT_YUY2 = CMP_MAKEFOURCC('Y', 'U', 'Y', '2'), + D3DFMT_G8R8_G8B8 = CMP_MAKEFOURCC('G', 'R', 'G', 'B'), + D3DFMT_DXT1 = CMP_MAKEFOURCC('D', 'X', 'T', '1'), + D3DFMT_DXT2 = CMP_MAKEFOURCC('D', 'X', 'T', '2'), + D3DFMT_DXT3 = CMP_MAKEFOURCC('D', 'X', 'T', '3'), + D3DFMT_DXT4 = CMP_MAKEFOURCC('D', 'X', 'T', '4'), + D3DFMT_DXT5 = CMP_MAKEFOURCC('D', 'X', 'T', '5'), + + D3DFMT_D16_LOCKABLE = 70, + D3DFMT_D32 = 71, + D3DFMT_D15S1 = 73, + D3DFMT_D24S8 = 75, + D3DFMT_D24X8 = 77, + D3DFMT_D24X4S4 = 79, + D3DFMT_D16 = 80, + + D3DFMT_D32F_LOCKABLE = 82, + D3DFMT_D24FS8 = 83, + + D3DFMT_L16 = 81, + + D3DFMT_VERTEXDATA = 100, + D3DFMT_INDEX16 = 101, + D3DFMT_INDEX32 = 102, + + D3DFMT_Q16W16V16U16 = 110, + + D3DFMT_MULTI2_ARGB8 = CMP_MAKEFOURCC('M', 'E', 'T', '1'), // Floating point surface formats // s10e5 formats (16-bits per channel) - D3DFMT_R16F = 111, - D3DFMT_G16R16F = 112, - D3DFMT_A16B16G16R16F = 113, + D3DFMT_R16F = 111, + D3DFMT_G16R16F = 112, + D3DFMT_A16B16G16R16F = 113, // IEEE s23e8 formats (32-bits per channel) - D3DFMT_R32F = 114, - D3DFMT_G32R32F = 115, - D3DFMT_A32B32G32R32F = 116, + D3DFMT_R32F = 114, + D3DFMT_G32R32F = 115, + D3DFMT_A32B32G32R32F = 116, - D3DFMT_CxV8U8 = 117, + D3DFMT_CxV8U8 = 117, } D3DFORMAT; -enum D3D10_RESOURCE_DIMENSION { - D3D10_RESOURCE_DIMENSION_UNKNOWN = 0, - D3D10_RESOURCE_DIMENSION_BUFFER = 1, - D3D10_RESOURCE_DIMENSION_TEXTURE1D = 2, - D3D10_RESOURCE_DIMENSION_TEXTURE2D = 3, - D3D10_RESOURCE_DIMENSION_TEXTURE3D = 4 +enum D3D10_RESOURCE_DIMENSION +{ + D3D10_RESOURCE_DIMENSION_UNKNOWN = 0, + D3D10_RESOURCE_DIMENSION_BUFFER = 1, + D3D10_RESOURCE_DIMENSION_TEXTURE1D = 2, + D3D10_RESOURCE_DIMENSION_TEXTURE2D = 3, + D3D10_RESOURCE_DIMENSION_TEXTURE3D = 4 }; -typedef enum D3D10_RESOURCE_MISC_FLAG { - D3D10_RESOURCE_MISC_GENERATE_MIPS = 0x1L, - D3D10_RESOURCE_MISC_SHARED = 0x2L, - D3D10_RESOURCE_MISC_TEXTURECUBE = 0x4L, - D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX = 0x10L, - D3D10_RESOURCE_MISC_GDI_COMPATIBLE = 0x20L +typedef enum D3D10_RESOURCE_MISC_FLAG +{ + D3D10_RESOURCE_MISC_GENERATE_MIPS = 0x1L, + D3D10_RESOURCE_MISC_SHARED = 0x2L, + D3D10_RESOURCE_MISC_TEXTURECUBE = 0x4L, + D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX = 0x10L, + D3D10_RESOURCE_MISC_GDI_COMPATIBLE = 0x20L } D3D10_RESOURCE_MISC_FLAG; +#else +#include "dxgiformat.h" #endif extern const char* g_pszFilename; // DDSD2 // Required for 64bit compatability -typedef struct _DDSD2 { - CMP_DWORD dwSize; // size of the DDSURFACEDESC structure - CMP_DWORD dwFlags; // determines what fields are valid - CMP_DWORD dwHeight; // height of surface to be created - CMP_DWORD dwWidth; // width of input surface - union { - CMP_DWORD lPitch; // distance to start of next line (return value only) - CMP_DWORD dwLinearSize; // Formless late-allocated optimized surface size +typedef struct _DDSD2 +{ // DDS_HEADER + CMP_DWORD dwSize; // size: size of the DDSURFACEDESC structure + CMP_DWORD dwFlags; // flags: determines what fields are valid + CMP_DWORD dwHeight; // height height of surface to be created + CMP_DWORD dwWidth; // width width of input surface + union + { + CMP_DWORD lPitch; // pitchOrLinearSize distance to start of next line (return value only) + CMP_DWORD dwLinearSize; // Formless late-allocated optimized surface size }; - union { - CMP_DWORD dwBackBufferCount; // number of back buffers requested - CMP_DWORD dwDepth; // the depth if this is a volume texture + union + { + CMP_DWORD dwDepth; // depth the depth if this is a volume texture, only if DDS_HEADER_FLAGS_VOLUME is set + CMP_DWORD dwBackBufferCount; // number of back buffers requested }; - union { - CMP_DWORD dwMipMapCount; // number of mip-map levels requestde - // dwZBufferBitDepth removed, use ddpfPixelFormat one instead - CMP_DWORD dwRefreshRate; // refresh rate (used when display mode is described) - CMP_DWORD dwSrcVBHandle; // The source used in VB::Optimize + union + { + CMP_DWORD dwMipMapCount; // mipMapCount number of mip-map levels requestde + CMP_DWORD dwRefreshRate; // refresh rate (used when display mode is described) + CMP_DWORD dwSrcVBHandle; // The source used in VB::Optimize }; - CMP_DWORD dwAlphaBitDepth; // depth of alpha buffer requested - CMP_DWORD dwReserved; // reserved - CMP_DWORD lpSurface; // pointer to the associated surface memory - union { - DDCOLORKEY ddckCKDestOverlay; // color key for destination overlay use - CMP_DWORD dwEmptyFaceColor; // Physical color for empty cubemap faces + CMP_DWORD dwAlphaBitDepth; // reserved1[0] depth of alpha buffer requested + CMP_DWORD dwReserved; // reserved1[1] + CMP_DWORD lpSurface; // reserved1[2] pointer to the associated surface memory + union + { + DDCOLORKEY ddckCKDestOverlay; // reserved1[3,4] color key for destination overlay use + CMP_DWORD dwEmptyFaceColor; // Physical color for empty cubemap faces }; - DDCOLORKEY ddckCKDestBlt; // color key for destination blt use - DDCOLORKEY ddckCKSrcOverlay; // color key for source overlay use - DDCOLORKEY ddckCKSrcBlt; // color key for source blt use - union { - DDPIXELFORMAT ddpfPixelFormat; // pixel format description of the surface (FourCC's) - CMP_DWORD dwFVF; // vertex format description of vertex buffers + DDCOLORKEY ddckCKDestBlt; // reserved1[5,6] color key for destination blt use + DDCOLORKEY ddckCKSrcOverlay; // reserved1[7,8] color key for source overlay use + DDCOLORKEY ddckCKSrcBlt; // reserved1[9,10] color key for source blt use + union + { + DDPIXELFORMAT ddpfPixelFormat; // DDS_PIXELFORMAT pixel format description of the surface (FourCC's) + CMP_DWORD dwFVF; // vertex format description of vertex buffers }; - DDSCAPS2 ddsCaps; // direct draw surface capabilities - CMP_DWORD dwTextureStage; // stage in multitexture cascade - // for extended DDSHeader10 info see DDS_DX10 + DDSCAPS2 ddsCaps; // caps,caps2,cap3,caps4 direct draw surface capabilities + CMP_DWORD dwTextureStage; // reserved2 stage in multitexture cascade + // for extended DDSHeader10 info see DDS_DX10 } DDSD2; +// Simplified version +struct DDS_PIXELFORMAT +{ + uint32_t size; + uint32_t flags; + uint32_t fourCC; + uint32_t RGBBitCount; + uint32_t RBitMask; + uint32_t GBitMask; + uint32_t BBitMask; + uint32_t ABitMask; +}; + +struct DDS_FILE_HEADER +{ + uint32_t size; + uint32_t flags; + uint32_t height; + uint32_t width; + uint32_t pitchOrLinearSize; + uint32_t depth; // only if DDS_HEADER_FLAGS_VOLUME is set in flags + uint32_t mipMapCount; + uint32_t reserved1[11]; + DDS_PIXELFORMAT ddspf; + uint32_t caps; + uint32_t caps2; + uint32_t caps3; + uint32_t caps4; + uint32_t reserved2; +}; + +struct DDS_FILE_HEADER_DXT10 +{ + DXGI_FORMAT dxgiFormat; + uint32_t resourceDimension; + uint32_t miscFlag; // see D3D11_RESOURCE_MISC_FLAG + uint32_t arraySize; + uint32_t miscFlags2; // see DDS_MISC_FLAGS2 +}; + #define MAX_FORMAT_LENGTH 160 #define MAX_ERROR_LENGTH 240 static const CMP_DWORD DDS_HEADER = CMP_MAKEFOURCC('D', 'D', 'S', ' '); +#define DDS_FOURCC 0x00000004 // DDPF_FOURCC +#define DDS_RGB 0x00000040 // DDPF_RGB +#define DDS_RGBA 0x00000041 // DDPF_RGB | DDPF_ALPHAPIXELS +#define DDS_LUMINANCE 0x00020000 // DDPF_LUMINANCE +#define DDS_LUMINANCEA 0x00020001 // DDPF_LUMINANCE | DDPF_ALPHAPIXELS +#define DDS_ALPHAPIXELS 0x00000001 // DDPF_ALPHAPIXELS +#define DDS_ALPHA 0x00000002 // DDPF_ALPHA +#define DDS_PAL8 0x00000020 // DDPF_PALETTEINDEXED8 +#define DDS_PAL8A 0x00000021 // DDPF_PALETTEINDEXED8 | DDPF_ALPHAPIXELS +#define DDS_BUMPDUDV 0x00080000 // DDPF_BUMPDUDV + +#define DDS_HEADER_FLAGS_TEXTURE 0x00001007 // DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT +#define DDS_HEADER_FLAGS_MIPMAP 0x00020000 // DDSD_MIPMAPCOUNT +#define DDS_HEADER_FLAGS_VOLUME 0x00800000 // DDSD_DEPTH +#define DDS_HEADER_FLAGS_PITCH 0x00000008 // DDSD_PITCH +#define DDS_HEADER_FLAGS_LINEARSIZE 0x00080000 // DDSD_LINEARSIZE +#define DDS_CUBEMAP 0x00000200 // DDSCAPS2_CUBEMAP +#define DDS_FLAGS_VOLUME 0x00200000 // DDSCAPS2_VOLUME TC_PluginError LoadDDS_ABGR32F(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet); TC_PluginError LoadDDS_ABGR16F(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet); @@ -497,6 +583,7 @@ TC_PluginError LoadDDS_FourCC(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet); TC_PluginError LoadDDS_RGB565(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet); TC_PluginError LoadDDS_RGB888(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet); TC_PluginError LoadDDS_RGB8888(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet, bool bAlpha); +TC_PluginError LoadDDS_RGB8888_S(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet, bool bAlpha); TC_PluginError LoadDDS_ARGB2101010(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet); TC_PluginError LoadDDS_ABGR16(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet); TC_PluginError LoadDDS_G16R16(FILE* pFile, DDSD2* pDDSD, MipSet* pMipSet); @@ -513,6 +600,7 @@ TC_PluginError SaveDDS_ABGR16F(FILE* pFile, const MipSet* pMipSet); TC_PluginError SaveDDS_RG16F(FILE* pFile, const MipSet* pMipSet); TC_PluginError SaveDDS_R16F(FILE* pFile, const MipSet* pMipSet); TC_PluginError SaveDDS_ARGB8888(FILE* pFile, const MipSet* pMipSet); +TC_PluginError SaveDDS_RGBA8888_S(FILE* pFile, const MipSet* pMipSet); TC_PluginError SaveDDS_ARGB2101010(FILE* pFile, const MipSet* pMipSet); TC_PluginError SaveDDS_ABGR16(FILE* pFile, const MipSet* pMipSet); TC_PluginError SaveDDS_R16(FILE* pFile, const MipSet* pMipSet); @@ -522,6 +610,4 @@ TC_PluginError SaveDDS_FourCC(FILE* pFile, const MipSet* pMipSet); TC_PluginError SaveDDS_G8(FILE* pFile, const MipSet* pMipSet); TC_PluginError SaveDDS_A8(FILE* pFile, const MipSet* pMipSet); - - #endif diff --git a/applications/_plugins/cimage/dds/dds_helpers.cpp b/applications/_plugins/cimage/dds/dds_helpers.cpp index c81f2b305..6f34160aa 100644 --- a/applications/_plugins/cimage/dds/dds_helpers.cpp +++ b/applications/_plugins/cimage/dds/dds_helpers.cpp @@ -105,6 +105,7 @@ TC_PluginError GenericLoadFunction(FILE*& pFile, DDSD2*& pDDSD, MipSet*& pMipSet TC_PluginError err; DetermineTextureType(pDDSD, pMipSet); + if(!DDS_CMips->AllocateMipSet(pMipSet, channelFormat, textureDataType, pMipSet->m_TextureType, pDDSD->dwWidth, pDDSD->dwHeight, pMipSet->m_nDepth)) return PE_Unknown; @@ -396,6 +397,56 @@ TC_PluginError LoopRGB8888(FILE*& pFile, DDSD2*&, MipSet*& pMipSet, void*& extra return PE_OK; } + +TC_PluginError LoopRGB8888_S(FILE*& pFile, DDSD2*&, MipSet*& pMipSet, void*& extra, int nMipLevel, int nFaceOrSlice, CMP_DWORD dwWidth, CMP_DWORD dwHeight) +{ + MipLevel* pMipLevel = DDS_CMips->GetMipLevel(pMipSet, nMipLevel, nFaceOrSlice); + + // Allocate the permanent buffer and unpack the bitmap data into it + if (!DDS_CMips->AllocateMipLevelData(pMipLevel, dwWidth, dwHeight, CF_8bit, pMipSet->m_TextureDataType)) + { + return PE_Unknown; + } + ARGB8888Struct* pARGB8888Struct = reinterpret_cast(extra); + if (!(pARGB8888Struct->nFlags & EF_UseBitMasks)) + { + //not using bitmasks + if (fread(pMipLevel->m_pbData, pMipLevel->m_dwLinearSize, 1, pFile) != 1) + { + return PE_Unknown; + } + } + else + { + //using bitmasks + if (fread(pARGB8888Struct->pMemory, pMipLevel->m_dwLinearSize, 1, pFile) != 1) + { + return PE_Unknown; + } + CMP_SBYTE* pData = pMipLevel->m_psbData; + CMP_DWORD* pTempPtr = (CMP_DWORD*)pARGB8888Struct->pMemory; + CMP_DWORD* pEnd = (CMP_DWORD*)pARGB8888Struct->pMemory + (pMipLevel->m_dwLinearSize / 4); + CMP_SBYTE R, G, B, A; + while (pTempPtr < pEnd) + { + + R = static_cast((*pTempPtr & pARGB8888Struct->nRMask) >> pARGB8888Struct->nRShift); + G = static_cast((*pTempPtr & pARGB8888Struct->nGMask) >> pARGB8888Struct->nGShift); + B = static_cast((*pTempPtr & pARGB8888Struct->nBMask) >> pARGB8888Struct->nBShift); + A = static_cast((*pTempPtr & 0xFF000000) >> 24); + A = (pMipSet->m_TextureDataType == TDT_ARGB ? A : 127); + *pData++ = R; + *pData++ = G; + *pData++ = B; + *pData++ = A; + // printf("[%d,%d,%d,%d]\n",R,G,B,A); + pTempPtr++; + } + } + return PE_OK; +} + + TC_PluginError PostLoopRGB8888(FILE*&, DDSD2*&, MipSet*&, void*&) { return PE_OK; } @@ -717,32 +768,37 @@ TC_PluginError PostLoopG16R16(FILE*&, DDSD2*&, MipSet*&, void*&) { } bool SetupDDSD(DDSD2& ddsd2, const MipSet* pMipSet, bool bCompressed) { - memset(&ddsd2, 0, sizeof(DDSD2)); - ddsd2.dwSize = sizeof(DDSD2); assert(pMipSet); - if(pMipSet == NULL) + if (pMipSet == NULL) return false; - ddsd2.dwWidth = pMipSet->m_nWidth; - ddsd2.dwHeight = pMipSet->m_nHeight; + memset(&ddsd2, 0, sizeof(DDSD2)); + ddsd2.dwSize = sizeof(DDSD2); + ddsd2.dwWidth = pMipSet->m_nWidth; + ddsd2.dwHeight = pMipSet->m_nHeight; ddsd2.dwMipMapCount = pMipSet->m_nMipLevels; - ddsd2.dwFlags = DDSD_CAPS|DDSD_WIDTH|DDSD_HEIGHT|DDSD_PIXELFORMAT|DDSD_MIPMAPCOUNT; + ddsd2.dwFlags = DDSD_CAPS|DDSD_WIDTH|DDSD_HEIGHT|DDSD_PIXELFORMAT|DDSD_MIPMAPCOUNT; + if(bCompressed) { - ddsd2.dwFlags |= DDSD_LINEARSIZE; - ddsd2.dwLinearSize = DDS_CMips->GetMipLevel(pMipSet, 0)->m_dwLinearSize; + ddsd2.dwFlags |= DDSD_LINEARSIZE; + ddsd2.dwLinearSize = DDS_CMips->GetMipLevel(pMipSet, 0)->m_dwLinearSize; } else - ddsd2.dwFlags |= DDSD_PITCH; + ddsd2.dwFlags |= DDSD_PITCH; ddsd2.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); - ddsd2.ddsCaps.dwCaps = DDSCAPS_TEXTURE|DDSCAPS_COMPLEX|DDSCAPS_MIPMAP; + + ddsd2.ddsCaps.dwCaps = DDSCAPS_TEXTURE; + + if (pMipSet->m_nMipLevels > 1) + ddsd2.ddsCaps.dwCaps |= DDSCAPS_COMPLEX | DDSCAPS_MIPMAP; if(pMipSet->m_TextureType == TT_CubeMap) { - ddsd2.ddsCaps.dwCaps2 |= DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_ALLFACES; + ddsd2.ddsCaps.dwCaps2 |= DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_ALLFACES; } else if(pMipSet->m_TextureType == TT_VolumeTexture) { - ddsd2.dwFlags |= DDSD_DEPTH; - ddsd2.dwDepth = pMipSet->m_nDepth; - ddsd2.ddsCaps.dwCaps2 |= DDSCAPS2_VOLUME; + ddsd2.dwFlags |= DDSD_DEPTH; + ddsd2.dwDepth = pMipSet->m_nDepth; + ddsd2.ddsCaps.dwCaps2 |= DDSCAPS2_VOLUME; } return true; @@ -987,15 +1043,25 @@ TC_PluginError LoopR16G16(FILE*& pFile, DDSD2*&, MipSet*& pMipSet, void*&, return PE_OK; } +TC_PluginError PreLoopR16(FILE*&, DDSD2*&, MipSet*& pMipSet, void*&) +{ + return PE_OK; +} + + TC_PluginError LoopR16(FILE*& pFile, DDSD2*&, MipSet*& pMipSet, void*&, int nMipLevel, int nFaceOrSlice, CMP_DWORD dwWidth, CMP_DWORD dwHeight) { + MipLevel* pMipLevel = DDS_CMips->GetMipLevel(pMipSet, nMipLevel, nFaceOrSlice); - // Allocate the permanent buffer and unpack the bitmap data into it - if(!DDS_CMips->AllocateMipLevelData(pMipLevel, dwWidth, dwHeight, CF_16bit, pMipSet->m_TextureDataType)) { + // Allocate the permanent buffer and unpack the bitmap data into it, for CMP we are always using RGBA buffer + if (!DDS_CMips->AllocateMipLevelData(pMipLevel, dwWidth, dwHeight, CF_16bit, TDT_ARGB)) // pMipSet->m_TextureDataType)) + { return PE_Unknown; } + pMipSet->m_TextureDataType = TDT_ARGB; CMP_DWORD dwSize = pMipLevel->m_dwLinearSize / 4; + CMP_BYTE* pTempData = (CMP_BYTE*) malloc(dwSize); assert(pTempData); if(!pTempData) @@ -1007,13 +1073,14 @@ TC_PluginError LoopR16(FILE*& pFile, DDSD2*&, MipSet*& pMipSet, void*&, return PE_Unknown; } + // Load as gray scale CMP_WORD* pSrc = (CMP_WORD*) pTempData; CMP_WORD* pEnd = (CMP_WORD*) (pTempData + dwSize); CMP_WORD* pDest = pMipLevel->m_pwData; while(pSrc < pEnd) { + *pDest++ = *pSrc; + *pDest++ = *pSrc; *pDest++ = *pSrc++; - *pDest++ = 0; - *pDest++ = 0; *pDest++ = _UI16_MAX; } @@ -1022,6 +1089,12 @@ TC_PluginError LoopR16(FILE*& pFile, DDSD2*&, MipSet*& pMipSet, void*&, return PE_OK; } +TC_PluginError PostLoopR16(FILE*&, DDSD2*&, MipSet*& pMipSet, void*&) +{ + return PE_OK; +} + + TC_PluginError LoopR8(FILE*& pFile, DDSD2*&, MipSet*& pMipSet, void*&, int nMipLevel, int nFaceOrSlice, CMP_DWORD dwWidth, CMP_DWORD dwHeight) { MipLevel* pMipLevel = DDS_CMips->GetMipLevel(pMipSet, nMipLevel, nFaceOrSlice); diff --git a/applications/_plugins/cimage/dds/dds_helpers.h b/applications/_plugins/cimage/dds/dds_helpers.h index 07c6c5a30..12aab2b52 100644 --- a/applications/_plugins/cimage/dds/dds_helpers.h +++ b/applications/_plugins/cimage/dds/dds_helpers.h @@ -71,6 +71,7 @@ TC_PluginError LoopRGB888(FILE*& pFile, DDSD2*&, MipSet*& pMipSet, void*& extra, TC_PluginError PostLoopRGB888(FILE*&, DDSD2*&, MipSet*&, void*& extra); TC_PluginError PreLoopRGB8888(FILE*&, DDSD2*&, MipSet*& pMipSet, void*&); TC_PluginError LoopRGB8888(FILE*& pFile, DDSD2*&, MipSet*& pMipSet, void*& extra, int nMipLevel, int nFaceOrSlice, CMP_DWORD dwWidth, CMP_DWORD dwHeight); +TC_PluginError LoopRGB8888_S(FILE*& pFile, DDSD2*&, MipSet*& pMipSet, void*& extra, int nMipLevel, int nFaceOrSlice, CMP_DWORD dwWidth, CMP_DWORD dwHeight); TC_PluginError PostLoopRGB8888(FILE*&, DDSD2*&, MipSet*&, void*&); TC_PluginError PreLoopABGR32F(FILE*&, DDSD2*&, MipSet*& pMipSet, void*&); TC_PluginError LoopABGR32F(FILE*& pFile, DDSD2*&, MipSet*& pMipSet, void*&, int nMipLevel, int nFaceOrSlice, CMP_DWORD dwWidth, CMP_DWORD dwHeight); @@ -96,19 +97,28 @@ TC_PluginError PostLoopA8(FILE*&, DDSD2*&, MipSet*&, void*&); TC_PluginError PreLoopABGR16(FILE*&, DDSD2*&, MipSet*& pMipSet, void*&); TC_PluginError LoopABGR16(FILE*& pFile, DDSD2*&, MipSet*& pMipSet, void*&,int nMipLevel, int nFaceOrSlice, CMP_DWORD dwWidth, CMP_DWORD dwHeight); TC_PluginError PostLoopABGR16(FILE*&, DDSD2*&, MipSet*&, void*&); + TC_PluginError PreLoopG16R16(FILE*&, DDSD2*&, MipSet*& pMipSet, void*&); TC_PluginError LoopG16R16(FILE*& pFile, DDSD2*&, MipSet*& pMipSet, void*&, int nMipLevel, int nFaceOrSlice, CMP_DWORD dwWidth, CMP_DWORD dwHeight); TC_PluginError PostLoopG16R16(FILE*&, DDSD2*&, MipSet*&, void*&); + TC_PluginError PreLoopABGR32(FILE*&, DDSD2*&, MipSet*& pMipSet, void*&); TC_PluginError LoopABGR32(FILE*& pFile, DDSD2*&, MipSet*& pMipSet, void*&,int nMipLevel, int nFaceOrSlice, CMP_DWORD dwWidth, CMP_DWORD dwHeight); TC_PluginError PostLoopABGR32(FILE*&, DDSD2*&, MipSet*&, void*&); + TC_PluginError LoopR32G32(FILE*& pFile, DDSD2*&, MipSet*& pMipSet, void*&,int nMipLevel, int nFaceOrSlice, CMP_DWORD dwWidth, CMP_DWORD dwHeight); TC_PluginError LoopR10G10B10A2(FILE*& pFile, DDSD2*&, MipSet*& pMipSet, void*&,int nMipLevel, int nFaceOrSlice, CMP_DWORD dwWidth, CMP_DWORD dwHeight); TC_PluginError LoopR9G9B9E5(FILE*& pFile, DDSD2*&, MipSet*& pMipSet, void*&, int nMipLevel, int nFaceOrSlice, CMP_DWORD dwWidth, CMP_DWORD dwHeight); + TC_PluginError LoopR16G16(FILE*& pFile, DDSD2*&, MipSet*& pMipSet, void*&,int nMipLevel, int nFaceOrSlice, CMP_DWORD dwWidth, CMP_DWORD dwHeight); TC_PluginError LoopR32(FILE*& pFile, DDSD2*&, MipSet*& pMipSet, void*&, int nMipLevel, int nFaceOrSlice, CMP_DWORD dwWidth, CMP_DWORD dwHeight); TC_PluginError LoopR8G8(FILE*& pFile, DDSD2*&, MipSet*& pMipSet, void*&,int nMipLevel, int nFaceOrSlice, CMP_DWORD dwWidth, CMP_DWORD dwHeight); + +TC_PluginError PreLoopR16(FILE*&, DDSD2*&, MipSet*&, void*&); TC_PluginError LoopR16(FILE*& pFile, DDSD2*&, MipSet*& pMipSet, void*&, int nMipLevel, int nFaceOrSlice, CMP_DWORD dwWidth, CMP_DWORD dwHeight); -TC_PluginError LoopR8(FILE*& pFile, DDSD2*&, MipSet*& pMipSet, void*&,int nMipLevel, int nFaceOrSlice, CMP_DWORD dwWidth, CMP_DWORD dwHeight); +TC_PluginError PostLoopR16(FILE*&, DDSD2*&, MipSet*&, void*&); + +TC_PluginError LoopR8(FILE*& pFile, DDSD2*&, MipSet*& pMipSet, void*&, int nMipLevel, int nFaceOrSlice, CMP_DWORD dwWidth, CMP_DWORD dwHeight); + bool SetupDDSD(DDSD2& ddsd2, const MipSet* pMipSet, bool bCompressed); bool SetupDDSD_DX10(DDSD2& ddsd2, const MipSet* pMipSet, bool bCompressed); diff --git a/applications/_plugins/cimage/exr/cmakelists.txt b/applications/_plugins/cimage/exr/cmakelists.txt index 2c1d769c5..6e3bc352b 100644 --- a/applications/_plugins/cimage/exr/cmakelists.txt +++ b/applications/_plugins/cimage/exr/cmakelists.txt @@ -1,24 +1,39 @@ - -add_library(Plugin_CImage_EXR) - -target_sources(Plugin_CImage_EXR PRIVATE - - exr.h - exr.cpp -) - -target_link_libraries(Plugin_CImage_EXR PRIVATE - - CompressonatorLIB - CMP_FileIO - - Plugin_Common_cExr - Plugin_Common_UtilFuncs - Plugin_TCPluginAPI - Plugin_PluginManager - - ExtOpenEXR - # ExtZlib -) - -set_target_properties(Plugin_CImage_EXR PROPERTIES FOLDER ${FOLDER_NAME}) +cmake_minimum_required(VERSION 3.10) + +add_library(Image_EXR STATIC "") + +target_sources(Image_EXR + PRIVATE + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/tc_pluginapi.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/tc_plugininternal.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/tc_plugininternal.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/utilfuncs.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/utilfuncs.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/cmp_fileio.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/cmp_fileio.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/cexr.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/cexr.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/namespacealias.h + ./exr.h + ./exr.cpp + ) + +target_include_directories(Image_EXR + PRIVATE + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half + ${PROJECT_SOURCE_DIR}/applications/_plugins/common + ${OpenEXR_INCLUDE_DIRS} + ) + +if (APPLE) + target_include_directories(Image_EXR + PRIVATE + /usr/local/include/OpenEXR/) +endif() + +if (UNIX) + target_compile_definitions(Image_EXR PRIVATE _LINUX) +endif() + +set_target_properties(Image_EXR PROPERTIES FOLDER "Plugin_Static/ImageIO") diff --git a/applications/_plugins/cimage/exr/exr.cpp b/applications/_plugins/cimage/exr/exr.cpp index 474aeac98..62210c2b1 100644 --- a/applications/_plugins/cimage/exr/exr.cpp +++ b/applications/_plugins/cimage/exr/exr.cpp @@ -59,7 +59,7 @@ IMath.lib;Half.lib;IlmImf.lib;IlmThread.lib;Iex.lib;zlibstatic_d.lib; #endif #include -#include "EXR.h" +#include "exr.h" #include #include #include "tc_pluginapi.h" diff --git a/applications/_plugins/cimage/ktx/cmakelists.txt b/applications/_plugins/cimage/ktx/cmakelists.txt index 0c22460af..16462c782 100644 --- a/applications/_plugins/cimage/ktx/cmakelists.txt +++ b/applications/_plugins/cimage/ktx/cmakelists.txt @@ -1,35 +1,66 @@ +cmake_minimum_required(VERSION 3.10) -add_library(Plugin_CImage_KTX) +add_library(Image_KTX STATIC "") -if (CMP_HOST_WINDOWS) - add_dependencies(Plugin_CImage_KTX extern_ktx) -endif() +# Enabled KTX1 Only +file(GLOB_RECURSE KTX_Lib + lib/*.h + lib/*.cpp + lib/*.c + ) + +target_sources(Image_KTX + PRIVATE + ${KTX_Lib} + ./ktx1.cpp + ./ktx1.h + ./softfloat.cpp + ./softfloat.h + ) -target_sources(Plugin_CImage_KTX PRIVATE - ktx.cpp - cktx.h - softfloat.cpp - softfloat.h -) +target_include_directories(Image_KTX + PRIVATE + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib + ${PROJECT_SOURCE_DIR}/applications/_libs/gpu_decode + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half + ${PROJECT_SOURCE_DIR}/applications/_plugins/common -target_include_directories(Plugin_CImage_KTX PRIVATE + ${PROJECT_SOURCE_DIR}/../common/lib/ext/glew/1.9.0/include + ./lib/ ./ - ${VULKAN_SDK_PATH}/include/ -) - -target_link_libraries(Plugin_CImage_KTX PRIVATE - - CompressonatorLIB - CMP_FrameworkLIB - GPU_Decode - Plugin_TCPluginAPI - ExtGLEW - ExtOpenGL - ExtKTX -) - -if (CMP_HOST_APPLE) - target_compile_definitions(Plugin_CImage_KTX PUBLIC KTX_USE_GETPROC=1) + ) + + +# Enabled KTX2 Features +if (CMP_HOST_WINDOWS) + target_include_directories(Image_KTX + PRIVATE + ${PROJECT_SOURCE_DIR}/../common/lib/ext/ktx/include + ${PROJECT_SOURCE_DIR}/../common/lib/ext/ktx/lib + ${VULKAN_SDK_PATH}/include/ + ) + + target_link_libraries(Image_KTX PRIVATE + CMP_Compressonator + CMP_Framework + ExtKTX + ) endif() -set_target_properties(Plugin_CImage_KTX PROPERTIES FOLDER ${FOLDER_NAME}) +if (UNIX) + target_compile_definitions(Image_KTX PRIVATE _LINUX) + find_package(OpenGL) + if (OpenGL_FOUND) + if(APPLE) + target_include_directories(Image_KTX + PRIVATE + /usr/local/include) + endif() + endif() +endif() + + +set_target_properties(Image_KTX PROPERTIES + FOLDER "Plugin_Static/ImageIO" + # USE_FOLDERS ON + ) diff --git a/applications/_plugins/cimage/ktx/ktx.cpp b/applications/_plugins/cimage/ktx/ktx.cpp deleted file mode 100644 index 162d5a57e..000000000 --- a/applications/_plugins/cimage/ktx/ktx.cpp +++ /dev/null @@ -1,961 +0,0 @@ -//===================================================================== -// Copyright 2016 (c), Advanced Micro Devices, Inc. All rights reserved. -//===================================================================== -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files(the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions : -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -// Windows Header Files: -#ifdef _WIN32 -#include -#endif - -#include "cktx.h" - -#include "tc_pluginapi.h" -#include "tc_plugininternal.h" -#include "common.h" - -#include "softfloat.h" - -#ifndef _WIN32 -#include "textureio.h" -#endif - -#include -#include "gl_format.h" - -#include -#include -#include - -#ifdef _WIN32 -#pragma comment(lib, "opengl32.lib") // Open GL -#pragma comment(lib, "Glu32.lib") // Glu -#endif - -#if defined(_WIN32) //&& !defined(NO_LEGACY_BEHAVIOR) -#pragma comment(lib, "glew32.lib") // glew -#else -#ifdef _WIN32 -#ifdef _DEBUG -#pragma comment(lib, "libglew32d.lib") // glew -#else -#pragma comment(lib, "libglew32.lib") // glew -#endif -#else -#pragma comment(lib, "libglew32.lib") // glew -#endif -#endif - -CMIPS* KTX_CMips; - -using namespace std; - -#ifdef BUILD_AS_PLUGIN_DLL -DECLARE_PLUGIN(Plugin_KTX) -SET_PLUGIN_TYPE("IMAGE") -SET_PLUGIN_NAME("KTX") -#else -void* make_Plugin_KTX() { - return new Plugin_KTX; -} -#endif - -static void writeId(std::ostream& dst) { - dst << "glTF Compressonator v1.0"; -} - -Plugin_KTX::Plugin_KTX() { -} - -Plugin_KTX::~Plugin_KTX() { -} - -int Plugin_KTX::TC_PluginSetSharedIO(void* Shared) { - if (Shared) { - KTX_CMips = static_cast(Shared); - return 0; - } - return 1; -} - -int Plugin_KTX::TC_PluginGetVersion(TC_PluginVersion* pPluginVersion) { -#ifdef _WIN32 - pPluginVersion->guid = g_GUID; -#endif - pPluginVersion->dwAPIVersionMajor = TC_API_VERSION_MAJOR; - pPluginVersion->dwAPIVersionMinor = TC_API_VERSION_MINOR; - pPluginVersion->dwPluginVersionMajor = TC_PLUGIN_VERSION_MAJOR; - pPluginVersion->dwPluginVersionMinor = TC_PLUGIN_VERSION_MINOR; - return 0; -} - -int Plugin_KTX::TC_PluginFileLoadTexture(const char* pszFilename, CMP_Texture* srcTexture) { - return -1; -} - -int Plugin_KTX::TC_PluginFileSaveTexture(const char* pszFilename, CMP_Texture* srcTexture) { - return -1; -} - -int Plugin_KTX::TC_PluginFileLoadTexture(const char* pszFilename, MipSet* pMipSet) { - assert(pszFilename); - assert(pMipSet); - - bool isKTX2 = false; - if (pszFilename[strlen(pszFilename) - 1] == '2') { - isKTX2 = true; - } - - ktxTexture1* texture1 = nullptr; - ktxTexture2* texture2 = nullptr; - - ktxTexture* texture = nullptr; - - KTX_error_code loadStatus; - - bool isCompressed = false; - ktx_uint32_t glInternalformat; - ktx_uint32_t glType; - ktx_uint32_t glFormat; - if (!isKTX2) { - loadStatus = ktxTexture1_CreateFromNamedFile(pszFilename, KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, &texture1); - - glInternalformat = texture1->glInternalformat; - glType = texture1->glType; - glFormat = texture1->glFormat; - - texture = ktxTexture(texture1); - - isCompressed = texture->isCompressed; - } else { - loadStatus = ktxTexture2_CreateFromNamedFile(pszFilename, KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, &texture2); - - glInternalformat = glGetInternalFormatFromVkFormat((VkFormat)texture2->vkFormat); - glType = glGetTypeFromInternalFormat(glInternalformat); - glFormat = glGetFormatFromInternalFormat(glInternalformat); - - texture = ktxTexture(texture2); - - isCompressed = texture->isCompressed; - } - - if (loadStatus != KTX_SUCCESS) { - if (KTX_CMips) { - KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) opening file = %s \n"), EL_Error, IDS_ERROR_FILE_OPEN, pszFilename); - } - return -1; - } - - int channelByteSize = 0; - - if (isCompressed) { - pMipSet->m_compressed = true; - pMipSet->m_nBlockHeight = 4; - pMipSet->m_nBlockWidth = 4; - pMipSet->m_nBlockDepth = 1; - pMipSet->m_ChannelFormat = CF_Compressed; - channelByteSize = 1; - - - switch (glInternalformat) { - case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: - pMipSet->m_format = CMP_FORMAT_BC1; - pMipSet->m_TextureDataType = TDT_XRGB; - break; - case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: - pMipSet->m_format = CMP_FORMAT_BC1; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: - pMipSet->m_format = CMP_FORMAT_BC2; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: - pMipSet->m_format = CMP_FORMAT_BC3; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case RGB_BP_UNorm: - pMipSet->m_format = CMP_FORMAT_BC7; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case R_ATI1N_UNorm: - pMipSet->m_format = CMP_FORMAT_ATI1N; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case R_ATI1N_SNorm: - pMipSet->m_format = CMP_FORMAT_ATI2N; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case RG_ATI2N_UNorm: - pMipSet->m_format = CMP_FORMAT_ATI2N_XY; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case RG_ATI2N_SNorm: - pMipSet->m_format = CMP_FORMAT_ATI2N_DXT5; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case RGB_BP_UNSIGNED_FLOAT: - pMipSet->m_format = CMP_FORMAT_BC6H; - pMipSet->m_TextureDataType = TDT_ARGB; - channelByteSize = 2; - break; - case RGB_BP_SIGNED_FLOAT: - pMipSet->m_format = CMP_FORMAT_BC6H_SF; - pMipSet->m_TextureDataType = TDT_ARGB; - channelByteSize = 2; - break; - case ATC_RGB_AMD: - pMipSet->m_format = CMP_FORMAT_ATC_RGB; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case ATC_RGBA_EXPLICIT_ALPHA_AMD: - pMipSet->m_format = CMP_FORMAT_ATC_RGBA_Explicit; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case ATC_RGBA_INTERPOLATED_ALPHA_AMD: - pMipSet->m_format = CMP_FORMAT_ATC_RGBA_Interpolated; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case GL_COMPRESSED_RGBA_ASTC_4x4_KHR: - pMipSet->m_format = CMP_FORMAT_ASTC; - pMipSet->m_nBlockWidth = 4; - pMipSet->m_nBlockHeight = 4; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case GL_COMPRESSED_RGBA_ASTC_5x4_KHR: - pMipSet->m_format = CMP_FORMAT_ASTC; - pMipSet->m_nBlockWidth = 5; - pMipSet->m_nBlockHeight = 4; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case GL_COMPRESSED_RGBA_ASTC_5x5_KHR: - pMipSet->m_format = CMP_FORMAT_ASTC; - pMipSet->m_nBlockWidth = 5; - pMipSet->m_nBlockHeight = 5; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case GL_COMPRESSED_RGBA_ASTC_6x5_KHR: - pMipSet->m_format = CMP_FORMAT_ASTC; - pMipSet->m_nBlockWidth = 6; - pMipSet->m_nBlockHeight = 5; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case GL_COMPRESSED_RGBA_ASTC_6x6_KHR: - pMipSet->m_format = CMP_FORMAT_ASTC; - pMipSet->m_nBlockWidth = 6; - pMipSet->m_nBlockHeight = 6; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case GL_COMPRESSED_RGBA_ASTC_8x5_KHR: - pMipSet->m_format = CMP_FORMAT_ASTC; - pMipSet->m_nBlockWidth = 8; - pMipSet->m_nBlockHeight = 5; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case GL_COMPRESSED_RGBA_ASTC_8x6_KHR: - pMipSet->m_format = CMP_FORMAT_ASTC; - pMipSet->m_nBlockWidth = 8; - pMipSet->m_nBlockHeight = 6; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case GL_COMPRESSED_RGBA_ASTC_10x5_KHR: - pMipSet->m_format = CMP_FORMAT_ASTC; - pMipSet->m_nBlockWidth = 10; - pMipSet->m_nBlockHeight = 5; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case GL_COMPRESSED_RGBA_ASTC_10x6_KHR: - pMipSet->m_format = CMP_FORMAT_ASTC; - pMipSet->m_nBlockWidth = 10; - pMipSet->m_nBlockHeight = 6; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case GL_COMPRESSED_RGBA_ASTC_8x8_KHR: - pMipSet->m_format = CMP_FORMAT_ASTC; - pMipSet->m_nBlockWidth = 8; - pMipSet->m_nBlockHeight = 8; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case GL_COMPRESSED_RGBA_ASTC_10x8_KHR: - pMipSet->m_format = CMP_FORMAT_ASTC; - pMipSet->m_nBlockWidth = 10; - pMipSet->m_nBlockHeight = 8; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case GL_COMPRESSED_RGBA_ASTC_10x10_KHR: - pMipSet->m_format = CMP_FORMAT_ASTC; - pMipSet->m_nBlockWidth = 10; - pMipSet->m_nBlockHeight = 10; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case GL_COMPRESSED_RGBA_ASTC_12x10_KHR: - pMipSet->m_format = CMP_FORMAT_ASTC; - pMipSet->m_nBlockWidth = 12; - pMipSet->m_nBlockHeight = 10; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case GL_COMPRESSED_RGBA_ASTC_12x12_KHR: - pMipSet->m_format = CMP_FORMAT_ASTC; - pMipSet->m_nBlockWidth = 12; - pMipSet->m_nBlockHeight = 12; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case ETC1_RGB8_OES: - pMipSet->m_format = CMP_FORMAT_ETC_RGB; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case GL_COMPRESSED_RGB8_ETC2: - pMipSet->m_format = CMP_FORMAT_ETC2_RGB; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case COMPRESSED_FORMAT_DXT5_RxBG: - pMipSet->m_format = CMP_FORMAT_DXT5_RxBG; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case COMPRESSED_FORMAT_DXT5_RBxG: - pMipSet->m_format = CMP_FORMAT_DXT5_RBxG; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case COMPRESSED_FORMAT_DXT5_xRBG: - pMipSet->m_format = CMP_FORMAT_DXT5_xRBG; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case COMPRESSED_FORMAT_DXT5_RGxB: - pMipSet->m_format = CMP_FORMAT_DXT5_RGxB; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case COMPRESSED_FORMAT_DXT5_xGxR: - pMipSet->m_format = CMP_FORMAT_DXT5_xGxR; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - default: - if (KTX_CMips) { - KTX_CMips->PrintError( - ("Error(%d): KTX Plugin ID(%d) unsupported internl GL format %x\n"), EL_Error, IDS_ERROR_UNSUPPORTED_TYPE, glInternalformat); - } - return -1; - } - } else { - pMipSet->m_compressed = false; - switch (glType) { - case GL_UNSIGNED_BYTE: - pMipSet->m_ChannelFormat = CF_8bit; - switch (glFormat) { - case GL_RED: - pMipSet->m_format = CMP_FORMAT_R_8; - pMipSet->m_TextureDataType = TDT_R; - break; - case GL_RG: - pMipSet->m_format = CMP_FORMAT_RG_8; - pMipSet->m_TextureDataType = TDT_RG; - break; - case GL_RGB: - pMipSet->m_format = CMP_FORMAT_RGB_888; - pMipSet->m_TextureDataType = TDT_XRGB; - break; - case GL_RGBA: - case GL_RGBA8: - pMipSet->m_format = CMP_FORMAT_ARGB_8888; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case GL_BGR: - pMipSet->m_swizzle = true; - pMipSet->m_format = CMP_FORMAT_RGB_888; - pMipSet->m_TextureDataType = TDT_XRGB; - break; - case GL_BGRA: - pMipSet->m_swizzle = true; - pMipSet->m_format = CMP_FORMAT_ARGB_8888; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - } - break; - case GL_UNSIGNED_SHORT: - pMipSet->m_ChannelFormat = CF_16bit; - switch (glFormat) { - case GL_RED: - pMipSet->m_format = CMP_FORMAT_R_16; - pMipSet->m_TextureDataType = TDT_R; - break; - case GL_RG: - pMipSet->m_format = CMP_FORMAT_RG_16; - pMipSet->m_TextureDataType = TDT_RG; - break; - case GL_RGBA: - pMipSet->m_format = CMP_FORMAT_ARGB_16; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case GL_BGRA: - pMipSet->m_swizzle = true; - pMipSet->m_format = CMP_FORMAT_ARGB_16; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - } - break; - case GL_HALF_FLOAT: - pMipSet->m_ChannelFormat = CF_Float16; - switch (glFormat) { - case GL_RED: - pMipSet->m_format = CMP_FORMAT_R_16F; - pMipSet->m_TextureDataType = TDT_R; - break; - case GL_RG: - pMipSet->m_format = CMP_FORMAT_RG_16F; - pMipSet->m_TextureDataType = TDT_RG; - break; - case GL_RGBA: - pMipSet->m_format = CMP_FORMAT_ARGB_16F; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case GL_BGRA: - pMipSet->m_swizzle = true; - pMipSet->m_format = CMP_FORMAT_ARGB_16F; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - } - break; - case GL_UNSIGNED_INT_2_10_10_10_REV: - pMipSet->m_format = CMP_FORMAT_ARGB_2101010; - pMipSet->m_TextureDataType = TDT_ARGB; - pMipSet->m_ChannelFormat = CF_2101010; - break; - case GL_FLOAT: - pMipSet->m_ChannelFormat = CF_Float32; - switch (glFormat) { - case GL_RED: - pMipSet->m_format = CMP_FORMAT_R_32F; - pMipSet->m_TextureDataType = TDT_R; - break; - case GL_RG: - pMipSet->m_format = CMP_FORMAT_RG_32F; - pMipSet->m_TextureDataType = TDT_RG; - break; - case GL_RGBA: - pMipSet->m_format = CMP_FORMAT_ARGB_32F; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - case GL_BGRA: - pMipSet->m_swizzle = true; - pMipSet->m_format = CMP_FORMAT_ARGB_32F; - pMipSet->m_TextureDataType = TDT_ARGB; - break; - } - break; - break; - default: - if (KTX_CMips) { - KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) unsupported GL format %x\n"), EL_Error, IDS_ERROR_UNSUPPORTED_TYPE, glFormat); - } - return -1; - } - } - - if (texture->isCubemap) { - pMipSet->m_TextureType = TT_CubeMap; - } else if (texture->baseDepth > 1 && texture->numFaces == 1) { - pMipSet->m_TextureType = TT_VolumeTexture; - } else if (texture->baseDepth == 1 && texture->numFaces == 1) { - pMipSet->m_TextureType = TT_2D; - } else { - if (KTX_CMips) { - KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) unsupported texture format\n"), EL_Error, IDS_ERROR_UNSUPPORTED_TYPE); - } - return -1; - } - - pMipSet->m_nMipLevels = texture->numLevels; - - // Allocate MipSet header - KTX_CMips->AllocateMipSet(pMipSet, - pMipSet->m_ChannelFormat, - pMipSet->m_TextureDataType, - pMipSet->m_TextureType, - texture->baseWidth, - texture->baseHeight, - texture->numFaces - ); - - int w = pMipSet->m_nWidth; - int h = pMipSet->m_nHeight; - - unsigned int totalByteRead = 0; - - unsigned int faceSize = 0; - unsigned int dataSize = 0; - unsigned int numArrayElement = texture->numLayers; - - for (int nMipLevel = 0; nMipLevel < texture->numLevels; nMipLevel++) { - if ((w <= 1) || (h <= 1)) { - break; - } else { - w = max(1, pMipSet->m_nWidth >> nMipLevel); - h = max(1, pMipSet->m_nHeight >> nMipLevel); - } - - for (int face = 0; face < texture->numFaces; ++face) { - // Determine buffer size and set Mip Set Levels - MipLevel* pMipLevel = KTX_CMips->GetMipLevel(pMipSet, nMipLevel, face); - - int channelCount = 0; - - if (pMipSet->m_compressed) { - dataSize = w * h * channelByteSize; - KTX_CMips->AllocateCompressedMipLevelData(pMipLevel, w, h, dataSize); - } else { - channelByteSize = 0; - switch (glType) { - case GL_UNSIGNED_BYTE: - channelByteSize = 1; - break; - case GL_UNSIGNED_SHORT: - channelByteSize = 2; - break; - case GL_HALF_FLOAT: - channelByteSize = 2; - break; - case GL_FLOAT: - channelByteSize = 4; - break; - default: - return -1; - } - - switch (glFormat) { - case GL_RED: - channelCount = 1; - break; - case GL_RG: - channelCount = 2; - break; - case GL_RGB: - channelCount = 3; - break; - case GL_BGR: - channelCount = 3; - break; - case GL_RGBA: - channelCount = 4; - break; - case GL_BGRA: - channelCount = 4; - break; - default: - return -1; - } - - KTX_CMips->AllocateMipLevelData(pMipLevel, w, h, pMipSet->m_ChannelFormat, pMipSet->m_TextureDataType); - } - - - CMP_BYTE* pData = (CMP_BYTE*)(pMipLevel->m_pbData); - - if (!pData) { - if (KTX_CMips) { - KTX_CMips->PrintError( - ("Error(%d): KTX Plugin ID(%d) Read image data failed, Out of Memory. Format %x\n"), EL_Error, IDS_ERROR_UNSUPPORTED_TYPE, glFormat); - } - return -1; - } - - // - // Read image data into temporary buffer - // - - ktx_size_t offset = 0; - KTX_error_code dataStatus = ktxTexture_GetImageOffset(ktxTexture(texture), nMipLevel, 0, face, &offset); - uint8_t* imageData = ktxTexture_GetData(ktxTexture(texture)) + offset; - - if (!pMipSet->m_compressed) { - size_t readSize = channelByteSize * channelCount * w * h; - std::vector pixelData(readSize); - - memcpy(&pixelData[0], imageData, readSize); - - int pixelSize = channelCount * channelByteSize; - int targetPixelSize = channelCount * channelByteSize; - if (channelCount == 3) { - // XRGB conversion. - targetPixelSize = 4 * channelByteSize; - } - - int py = 0; - for (py = 0; py < h; py++) { - int px = 0; - for (px = 0; px < w; px++) { - memcpy(&pData[targetPixelSize * px + py * targetPixelSize * w], &pixelData[pixelSize * px + py * pixelSize * w], pixelSize); - } - } - } else { - memcpy(pData, imageData, dataSize); - } - } - } - - return 0; -} - -int Plugin_KTX::TC_PluginFileSaveTexture(const char* pszFilename, MipSet* pMipSet) { - assert(pszFilename); - assert(pMipSet); - - bool isKTX2 = false; - if (pszFilename[strlen(pszFilename) - 1] == '2') { - isKTX2 = true; - } - - if (pMipSet->m_pMipLevelTable == NULL) { - if (KTX_CMips) - KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) saving file = %s "), EL_Error, IDS_ERROR_ALLOCATEMIPSET, pszFilename); - return -1; - } - - if (KTX_CMips->GetMipLevel(pMipSet, 0) == NULL) { - if (KTX_CMips) - KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) saving file = %s "), EL_Error, IDS_ERROR_ALLOCATEMIPSET, pszFilename); - return -1; - } - - // - - ktxTextureCreateInfo textureCreateInfo; - /*!< Internal format for the texture, e.g., GL_RGB8. Ignored when creating a ktxTexture2. */ - textureCreateInfo.baseWidth = pMipSet->m_nWidth; /*!< Width of the base level of the texture. */ - textureCreateInfo.baseHeight = pMipSet->m_nHeight; /*!< Height of the base level of the texture. */ - textureCreateInfo.baseDepth = 1; /*!< Depth of the base level of the texture. */ - textureCreateInfo.numDimensions = 2; /*!< Number of dimensions in the texture, 1, 2 or 3. */ - textureCreateInfo.numLevels = pMipSet->m_nMipLevels; /*!< Number of mip levels in the texture. Should be 1 if @c generateMipmaps is KTX_TRUE; */ - textureCreateInfo.numLayers = 1; /*!< Number of array layers in the texture. */ - textureCreateInfo.numFaces = (pMipSet->m_TextureType == TT_CubeMap) ? 6 : 1; /*!< Number of faces: 6 for cube maps, 1 otherwise. */ - textureCreateInfo.isArray = KTX_FALSE; /*!< Set to KTX_TRUE if the texture is to be an array texture. Means OpenGL will use a GL_TEXTURE_*_ARRAY target. */ - textureCreateInfo.generateMipmaps = KTX_FALSE; /*!< Set to KTX_TRUE if mipmaps should be generated for the texture when loading into a 3D API. */ - - bool isCompressed = CMP_IsCompressedFormat(pMipSet->m_format); - - switch (pMipSet->m_TextureDataType) { - case TDT_R: { //single component-- can be Luminance and Alpha case, here only cover R - if (!isCompressed) { - textureCreateInfo.glInternalformat = GL_R8; - textureCreateInfo.vkFormat = VK_FORMAT_R8_UNORM; - if (pMipSet->m_ChannelFormat == CF_Float16) { - textureCreateInfo.glInternalformat = GL_R16F; - textureCreateInfo.vkFormat = VK_FORMAT_R16_SFLOAT; - } else if (pMipSet->m_ChannelFormat == CF_Float32) { - textureCreateInfo.glInternalformat = GL_R32F; - textureCreateInfo.vkFormat = VK_FORMAT_R32_SFLOAT; - } - } else { - textureCreateInfo.glInternalformat = GL_RED; - // TODO: KTX2/Vulkan - } - } - break; - case TDT_RG: { //two component - if (!isCompressed) { - textureCreateInfo.glInternalformat = GL_RG8; - textureCreateInfo.vkFormat = VK_FORMAT_R8G8_UNORM; - if (pMipSet->m_ChannelFormat == CF_Float16) { - textureCreateInfo.glInternalformat = GL_RG16F; - textureCreateInfo.vkFormat = VK_FORMAT_R16G16_SFLOAT; - } else if (pMipSet->m_ChannelFormat == CF_Float32) { - textureCreateInfo.glInternalformat = GL_RG32F; - textureCreateInfo.vkFormat = VK_FORMAT_R32G32_SFLOAT; - } - } else { - textureCreateInfo.glInternalformat = GL_COMPRESSED_RG; - // TODO: KTX2/Vulkan - } - } - break; - case TDT_XRGB: { //normally 3 component - if (!isCompressed) { - textureCreateInfo.glInternalformat = GL_RGB8; - textureCreateInfo.vkFormat = VK_FORMAT_R8G8B8_UNORM; - if (pMipSet->m_ChannelFormat == CF_Float16) { - textureCreateInfo.glInternalformat = GL_RGB16F; - textureCreateInfo.vkFormat = VK_FORMAT_R16G16B16_SFLOAT; - } else if (pMipSet->m_ChannelFormat == CF_Float32) { - textureCreateInfo.glInternalformat = GL_RGB32F; - textureCreateInfo.vkFormat = VK_FORMAT_R32G32B32_SFLOAT; - } - } else { - if (pMipSet->m_format == CMP_FORMAT_BC1 || pMipSet->m_format == CMP_FORMAT_DXT1) { - textureCreateInfo.glInternalformat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; - // TODO: KTX2/Vulkan - } else { - textureCreateInfo.glInternalformat = GL_RGB8; - textureCreateInfo.vkFormat = VK_FORMAT_R8G8B8_UNORM; - if (pMipSet->m_ChannelFormat == CF_Float16) { - textureCreateInfo.glInternalformat = GL_RGB16F; - textureCreateInfo.vkFormat = VK_FORMAT_R16G16B16_SFLOAT; - } else if (pMipSet->m_ChannelFormat == CF_Float32) { - textureCreateInfo.glInternalformat = GL_RGB32F; - textureCreateInfo.vkFormat = VK_FORMAT_R32G32B32_SFLOAT; - } - } - } - } - break; - case TDT_ARGB: { //4 component - if (!isCompressed) { - textureCreateInfo.glInternalformat = GL_RGBA8; - textureCreateInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM; - if (pMipSet->m_ChannelFormat == CF_Float16) { - textureCreateInfo.glInternalformat = GL_RGBA16F; - textureCreateInfo.vkFormat = VK_FORMAT_R16G16B16A16_SFLOAT; - } else if (pMipSet->m_ChannelFormat == CF_Float32) { - textureCreateInfo.glInternalformat = GL_RGBA32F; - textureCreateInfo.vkFormat = VK_FORMAT_R32G32B32A32_SFLOAT; - } - } else { - switch (pMipSet->m_format) { - case CMP_FORMAT_BC1: - case CMP_FORMAT_DXT1: - textureCreateInfo.glInternalformat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; - // TODO: KTX2/Vulkan - break; - case CMP_FORMAT_BC2: - case CMP_FORMAT_DXT3: - textureCreateInfo.glInternalformat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; - // TODO: KTX2/Vulkan - break; - case CMP_FORMAT_BC3: - case CMP_FORMAT_DXT5: - textureCreateInfo.glInternalformat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; - // TODO: KTX2/Vulkan - break; - case CMP_FORMAT_BC7: - textureCreateInfo.glInternalformat = RGB_BP_UNorm; - // TODO: KTX2/Vulkan - break; - case CMP_FORMAT_ATI1N: - textureCreateInfo.glInternalformat = R_ATI1N_UNorm; - // TODO: KTX2/Vulkan - break; - case CMP_FORMAT_ATI2N: - textureCreateInfo.glInternalformat = R_ATI1N_SNorm; - // TODO: KTX2/Vulkan - break; - case CMP_FORMAT_ATI2N_XY: - textureCreateInfo.glInternalformat = RG_ATI2N_UNorm; - // TODO: KTX2/Vulkan - break; - case CMP_FORMAT_ATI2N_DXT5: - textureCreateInfo.glInternalformat = RG_ATI2N_SNorm; - // TODO: KTX2/Vulkan - break; - case CMP_FORMAT_ATC_RGB: - textureCreateInfo.glInternalformat = ATC_RGB_AMD; - // TODO: KTX2/Vulkan - break; - case CMP_FORMAT_ATC_RGBA_Explicit: - textureCreateInfo.glInternalformat = ATC_RGBA_EXPLICIT_ALPHA_AMD; - // TODO: KTX2/Vulkan - break; - case CMP_FORMAT_ATC_RGBA_Interpolated: - textureCreateInfo.glInternalformat = ATC_RGBA_INTERPOLATED_ALPHA_AMD; - // TODO: KTX2/Vulkan - break; - case CMP_FORMAT_BC4: - textureCreateInfo.glInternalformat = GL_COMPRESSED_RED_RGTC1; - // TODO: KTX2/Vulkan - break; - case CMP_FORMAT_BC4_S: - textureCreateInfo.glInternalformat = GL_COMPRESSED_SIGNED_RED_RGTC1; - // TODO: KTX2/Vulkan - break; - case CMP_FORMAT_BC5: - textureCreateInfo.glInternalformat = GL_COMPRESSED_RG_RGTC2; - // TODO: KTX2/Vulkan - break; - case CMP_FORMAT_BC5_S: - textureCreateInfo.glInternalformat = GL_COMPRESSED_SIGNED_RG_RGTC2; - // TODO: KTX2/Vulkan - break; - case CMP_FORMAT_BC6H: - textureCreateInfo.glInternalformat = GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT; - textureCreateInfo.vkFormat = VK_FORMAT_BC6H_UFLOAT_BLOCK; - break; - case CMP_FORMAT_BC6H_SF: - textureCreateInfo.glInternalformat = GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT; - textureCreateInfo.vkFormat = VK_FORMAT_BC6H_SFLOAT_BLOCK; - break; - case CMP_FORMAT_ASTC: - if ((pMipSet->m_nBlockWidth == 4) && (pMipSet->m_nBlockHeight == 4)) { - textureCreateInfo.glInternalformat = GL_COMPRESSED_RGBA_ASTC_4x4_KHR; - // TODO: KTX2/Vulkan - } else if ((pMipSet->m_nBlockWidth == 5) && (pMipSet->m_nBlockHeight == 4)) { - textureCreateInfo.glInternalformat = GL_COMPRESSED_RGBA_ASTC_5x4_KHR; - // TODO: KTX2/Vulkan - } else if ((pMipSet->m_nBlockWidth == 5) && (pMipSet->m_nBlockHeight == 5)) { - textureCreateInfo.glInternalformat = GL_COMPRESSED_RGBA_ASTC_5x5_KHR; - // TODO: KTX2/Vulkan - } else if ((pMipSet->m_nBlockWidth == 6) && (pMipSet->m_nBlockHeight == 5)) { - textureCreateInfo.glInternalformat = GL_COMPRESSED_RGBA_ASTC_6x5_KHR; - // TODO: KTX2/Vulkan - } else if ((pMipSet->m_nBlockWidth == 6) && (pMipSet->m_nBlockHeight == 6)) { - textureCreateInfo.glInternalformat = GL_COMPRESSED_RGBA_ASTC_6x6_KHR; - // TODO: KTX2/Vulkan - } else if ((pMipSet->m_nBlockWidth == 8) && (pMipSet->m_nBlockHeight == 5)) { - textureCreateInfo.glInternalformat = GL_COMPRESSED_RGBA_ASTC_8x5_KHR; - // TODO: KTX2/Vulkan - } else if ((pMipSet->m_nBlockWidth == 8) && (pMipSet->m_nBlockHeight == 6)) { - textureCreateInfo.glInternalformat = GL_COMPRESSED_RGBA_ASTC_8x6_KHR; - // TODO: KTX2/Vulkan - } else if ((pMipSet->m_nBlockWidth == 8) && (pMipSet->m_nBlockHeight == 8)) { - textureCreateInfo.glInternalformat = GL_COMPRESSED_RGBA_ASTC_8x8_KHR; - // TODO: KTX2/Vulkan - } else if ((pMipSet->m_nBlockWidth == 10) && (pMipSet->m_nBlockHeight == 5)) { - textureCreateInfo.glInternalformat = GL_COMPRESSED_RGBA_ASTC_10x5_KHR; - // TODO: KTX2/Vulkan - } else if ((pMipSet->m_nBlockWidth == 10) && (pMipSet->m_nBlockHeight == 6)) { - textureCreateInfo.glInternalformat = GL_COMPRESSED_RGBA_ASTC_10x6_KHR; - // TODO: KTX2/Vulkan - } else if ((pMipSet->m_nBlockWidth == 10) && (pMipSet->m_nBlockHeight == 8)) { - textureCreateInfo.glInternalformat = GL_COMPRESSED_RGBA_ASTC_10x8_KHR; - // TODO: KTX2/Vulkan - } else if ((pMipSet->m_nBlockWidth == 10) && (pMipSet->m_nBlockHeight == 10)) { - textureCreateInfo.glInternalformat = GL_COMPRESSED_RGBA_ASTC_10x10_KHR; - // TODO: KTX2/Vulkan - } else if ((pMipSet->m_nBlockWidth == 12) && (pMipSet->m_nBlockHeight == 10)) { - textureCreateInfo.glInternalformat = GL_COMPRESSED_RGBA_ASTC_12x10_KHR; - // TODO: KTX2/Vulkan - } else if ((pMipSet->m_nBlockWidth == 12) && (pMipSet->m_nBlockHeight == 12)) { - textureCreateInfo.glInternalformat = GL_COMPRESSED_RGBA_ASTC_12x12_KHR; - // TODO: KTX2/Vulkan - } else { - textureCreateInfo.glInternalformat = GL_COMPRESSED_RGBA_ASTC_4x4_KHR; - // TODO: KTX2/Vulkan - } - break; - case CMP_FORMAT_BASIS: - textureCreateInfo.glInternalformat = GL_RGBA8; - textureCreateInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM; - if (pMipSet->m_ChannelFormat == CF_Float16) { - textureCreateInfo.glInternalformat = GL_RGBA16F; - textureCreateInfo.vkFormat = VK_FORMAT_R16G16B16A16_SFLOAT; - } else if (pMipSet->m_ChannelFormat == CF_Float32) { - textureCreateInfo.glInternalformat = GL_RGBA32F; - textureCreateInfo.vkFormat = VK_FORMAT_R32G32B32A32_SFLOAT; - } - break; - case CMP_FORMAT_ETC_RGB: - textureCreateInfo.glInternalformat = ETC1_RGB8_OES; - // TODO: KTX2/Vulkan - break; - case CMP_FORMAT_ETC2_RGB: - textureCreateInfo.glInternalformat = GL_COMPRESSED_RGB8_ETC2; - // TODO: KTX2/Vulkan - break; - case CMP_FORMAT_DXT5_xGBR: - textureCreateInfo.glInternalformat = COMPRESSED_FORMAT_DXT5_xGBR; - // TODO: KTX2/Vulkan - break; - case CMP_FORMAT_DXT5_RxBG: - textureCreateInfo.glInternalformat = COMPRESSED_FORMAT_DXT5_RxBG; - // TODO: KTX2/Vulkan - break; - case CMP_FORMAT_DXT5_RBxG: - textureCreateInfo.glInternalformat = COMPRESSED_FORMAT_DXT5_RBxG; - // TODO: KTX2/Vulkan - break; - case CMP_FORMAT_DXT5_xRBG: - textureCreateInfo.glInternalformat = COMPRESSED_FORMAT_DXT5_xRBG; - // TODO: KTX2/Vulkan - break; - case CMP_FORMAT_DXT5_RGxB: - textureCreateInfo.glInternalformat = COMPRESSED_FORMAT_DXT5_RGxB; - // TODO: KTX2/Vulkan - break; - case CMP_FORMAT_DXT5_xGxR: - textureCreateInfo.glInternalformat = COMPRESSED_FORMAT_DXT5_xGxR; - // TODO: KTX2/Vulkan - break; - } - } - } - break; - default: - break; - } - - // - - ktxTexture1* texture1 = nullptr; - ktxTexture2* texture2 = nullptr; - - ktxTexture* texture = nullptr; - - KTX_error_code createStatus; - if (!isKTX2) { - createStatus = ktxTexture1_Create(&textureCreateInfo, KTX_TEXTURE_CREATE_ALLOC_STORAGE, &texture1); - texture = ktxTexture(texture1); - } else { - createStatus = ktxTexture2_Create(&textureCreateInfo, KTX_TEXTURE_CREATE_ALLOC_STORAGE, &texture2); - texture = ktxTexture(texture2); - } - if (createStatus != KTX_SUCCESS) { - if (KTX_CMips) { - KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) saving file = %s "), EL_Error, IDS_ERROR_FILE_OPEN, pszFilename); - } - return -1; - } - - int nSlices = (pMipSet->m_TextureType == TT_2D) ? 1 : CMP_MaxFacesOrSlices(pMipSet, 0); - for (int nSlice = 0; nSlice < nSlices; nSlice++) { - for (int nMipLevel = 0; nMipLevel < pMipSet->m_nMipLevels; nMipLevel++) { - KTX_error_code setMemory = setMemory = ktxTexture_SetImageFromMemory(texture, - nMipLevel, - 0, - nSlice, - KTX_CMips->GetMipLevel(pMipSet, nMipLevel, nSlice)->m_pbData, - KTX_CMips->GetMipLevel(pMipSet, nMipLevel, nSlice)->m_dwLinearSize); - - if (setMemory != KTX_SUCCESS) { - return -1; - } - } - } - - // - - if (isKTX2 && pMipSet->m_format == CMP_FORMAT_BASIS) { - ktx_uint32_t basisQuality = (ktx_uint32_t)pMipSet->pData; // m_userData; - KTX_error_code basisStatus = ktxTexture2_CompressBasis(texture2, basisQuality); - if (basisStatus != KTX_SUCCESS) { - return -1; - } - } - - // - - std::stringstream writer; - writeId(writer); - ktxHashList_AddKVPair(&texture->kvDataHead, KTX_WRITER_KEY, (ktx_uint32_t)writer.str().length() + 1, writer.str().c_str()); - - // - - KTX_error_code save = ktxTexture_WriteToNamedFile(texture, pszFilename); - if (save != KTX_SUCCESS) { - return -1; - } - - return 0; -} diff --git a/applications/_plugins/cimage/ktx/ktx1.cpp b/applications/_plugins/cimage/ktx/ktx1.cpp new file mode 100644 index 000000000..266c954ae --- /dev/null +++ b/applications/_plugins/cimage/ktx/ktx1.cpp @@ -0,0 +1,1346 @@ +//===================================================================== +// Copyright 2016 (c), Advanced Micro Devices, Inc. All rights reserved. +//===================================================================== +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#ifdef _WIN32 +#include +#endif + +#include "ktx1.h" +#include "tc_pluginapi.h" +#include "tc_plugininternal.h" +#include "common.h" +#include "softfloat.h" + +#include +#include +#include +#include + +#if defined(_WIN32) //&& !defined(NO_LEGACY_BEHAVIOR) +#pragma comment(lib, "glew32.lib") // glew +#else +#ifdef _WIN32 +#ifdef _DEBUG +#pragma comment(lib, "libglew32d.lib") // glew +#else +#pragma comment(lib, "libglew32.lib") // glew +#endif +#else +#pragma comment(lib, "libglew32.lib") // glew +#endif +#endif + +using namespace std; + +using namespace std; + +CMIPS *KTX_CMips; + +#ifdef BUILD_AS_PLUGIN_DLL +DECLARE_PLUGIN(Plugin_KTX) +SET_PLUGIN_TYPE("IMAGE") +SET_PLUGIN_NAME("KTX") +#else +void *make_Plugin_KTX() { return new Plugin_KTX; } +#endif + +uint32_t Endian_Conversion(uint32_t dword) +{ + return (((dword>>24)&0x000000FF) | ((dword>>8)&0x0000FF00) | ((dword<<8)&0x00FF0000) | ((dword<<24)&0xFF000000)); +} + +Plugin_KTX::Plugin_KTX() +{ +} + +Plugin_KTX::~Plugin_KTX() +{ +} + +int Plugin_KTX::TC_PluginSetSharedIO(void* Shared) +{ + if (Shared) + { + KTX_CMips = static_cast(Shared); + return 0; + } + return 1; +} + +int Plugin_KTX::TC_PluginGetVersion(TC_PluginVersion* pPluginVersion) +{ +#ifdef _WIN32 + pPluginVersion->guid = g_GUID; +#endif + pPluginVersion->dwAPIVersionMajor = TC_API_VERSION_MAJOR; + pPluginVersion->dwAPIVersionMinor = TC_API_VERSION_MINOR; + pPluginVersion->dwPluginVersionMajor = TC_PLUGIN_VERSION_MAJOR; + pPluginVersion->dwPluginVersionMinor = TC_PLUGIN_VERSION_MINOR; + return 0; +} + +int Plugin_KTX::TC_PluginFileLoadTexture(const char* pszFilename, CMP_Texture *srcTexture) +{ + return -1; +} + +int Plugin_KTX::TC_PluginFileSaveTexture(const char* pszFilename, CMP_Texture *srcTexture) +{ + return -1; +} + +uint32_t ktx_u32_byterev(uint32_t v) +{ + return (v >> 24) | ((v >> 8) & 0xFF00) | ((v << 8) & 0xFF0000) | (v << 24); +} + +// perform endianness switch on raw data +static void switch_endianness2(void *dataptr, int bytes) +{ + int i; + uint8_t *data = (uint8_t *) dataptr; + + for (i = 0; i < bytes / 2; i++) + { + uint8_t d0 = data[0]; + uint8_t d1 = data[1]; + data[0] = d1; + data[1] = d0; + data += 2; + } +} + +static void switch_endianness4(void *dataptr, int bytes) +{ + int i; + uint8_t *data = (uint8_t *) dataptr; + + for (i = 0; i < bytes / 4; i++) + { + uint8_t d0 = data[0]; + uint8_t d1 = data[1]; + uint8_t d2 = data[2]; + uint8_t d3 = data[3]; + data[0] = d3; + data[1] = d2; + data[2] = d1; + data[3] = d0; + data += 4; + } +} + +static void copy_scanline(void *dst, const void *src, int pixels, int method) +{ + +#define id(x) (x) +#define u16_sf16(x) float_to_sf16( x * (1.0f/65535.0f), SF_NEARESTEVEN ) +#define f32_sf16(x) sf32_to_sf16( x, SF_NEARESTEVEN ) + +#define COPY_R( dsttype, srctype, convfunc, oneval ) \ + do { \ + srctype *s = (srctype *)src; \ + dsttype *d = (dsttype *)dst; \ + for(i=0;iPrintError(("Error(%d): KTX Plugin ID(%d) opening file = %s \n"), EL_Error, IDS_ERROR_FILE_OPEN, pszFilename); + return -1; + } + + //using libktx + KTX_header fheader; + KTX_texinfo texinfo; + if (fread(&fheader, sizeof(KTX_header), 1, pFile) != 1) + { + if (KTX_CMips) + KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) invalid KTX header. Filename = %s \n"), EL_Error, IDS_ERROR_NOT_KTX, pszFilename); + fclose(pFile); + return -1; + } + + if (_ktxCheckHeader(&fheader, &texinfo) != KTX_SUCCESS) + { + if (KTX_CMips) + KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) invalid KTX header. Filename = %s \n"), EL_Error, IDS_ERROR_NOT_KTX, pszFilename); + fclose(pFile); + return -1; + } + + if (texinfo.compressed) + { + pMipSet->m_compressed = true; + pMipSet->m_nBlockHeight = 4; + pMipSet->m_nBlockWidth = 4; + pMipSet->m_nBlockDepth = 1; + pMipSet->m_ChannelFormat = CF_Compressed; + + switch (fheader.glInternalFormat) + { + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: + pMipSet->m_format = CMP_FORMAT_BC1; + pMipSet->m_TextureDataType = TDT_XRGB; + break; + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + pMipSet->m_format = CMP_FORMAT_BC1; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: + pMipSet->m_format = CMP_FORMAT_BC2; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + pMipSet->m_format = CMP_FORMAT_BC3; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_COMPRESSED_RED_RGTC1: + //pMipSet->m_format = CMP_FORMAT_ATI1N; + pMipSet->m_format = CMP_FORMAT_BC4; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_COMPRESSED_SIGNED_RED_RGTC1: + //pMipSet->m_format = CMP_FORMAT_ATI2N; + pMipSet->m_format = CMP_FORMAT_BC4_S; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_COMPRESSED_RG_RGTC2: + //pMipSet->m_format = CMP_FORMAT_ATI2N_XY; + pMipSet->m_format = CMP_FORMAT_BC5; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_COMPRESSED_SIGNED_RG_RGTC2: + //pMipSet->m_format = CMP_FORMAT_ATI2N_DXT5; + pMipSet->m_format = CMP_FORMAT_BC5_S; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case RGB_BP_UNorm: + pMipSet->m_format = CMP_FORMAT_BC7; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case RGB_BP_UNSIGNED_FLOAT: + pMipSet->m_format = CMP_FORMAT_BC6H; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case RGB_BP_SIGNED_FLOAT: + pMipSet->m_format = CMP_FORMAT_BC6H_SF; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case ATC_RGB_AMD: + pMipSet->m_format = CMP_FORMAT_ATC_RGB; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case ATC_RGBA_EXPLICIT_ALPHA_AMD: + pMipSet->m_format = CMP_FORMAT_ATC_RGBA_Explicit; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case ATC_RGBA_INTERPOLATED_ALPHA_AMD: + pMipSet->m_format = CMP_FORMAT_ATC_RGBA_Interpolated; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_COMPRESSED_RGBA_ASTC_4x4_KHR: + pMipSet->m_format = CMP_FORMAT_ASTC; + pMipSet->m_nBlockWidth = 4; + pMipSet->m_nBlockHeight = 4; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_COMPRESSED_RGBA_ASTC_5x4_KHR: + pMipSet->m_format = CMP_FORMAT_ASTC; + pMipSet->m_nBlockWidth = 5; + pMipSet->m_nBlockHeight = 4; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_COMPRESSED_RGBA_ASTC_5x5_KHR: + pMipSet->m_format = CMP_FORMAT_ASTC; + pMipSet->m_nBlockWidth = 5; + pMipSet->m_nBlockHeight = 5; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_COMPRESSED_RGBA_ASTC_6x5_KHR: + pMipSet->m_format = CMP_FORMAT_ASTC; + pMipSet->m_nBlockWidth = 6; + pMipSet->m_nBlockHeight = 5; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_COMPRESSED_RGBA_ASTC_6x6_KHR: + pMipSet->m_format = CMP_FORMAT_ASTC; + pMipSet->m_nBlockWidth = 6; + pMipSet->m_nBlockHeight = 6; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_COMPRESSED_RGBA_ASTC_8x5_KHR: + pMipSet->m_format = CMP_FORMAT_ASTC; + pMipSet->m_nBlockWidth = 8; + pMipSet->m_nBlockHeight = 5; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_COMPRESSED_RGBA_ASTC_8x6_KHR: + pMipSet->m_format = CMP_FORMAT_ASTC; + pMipSet->m_nBlockWidth = 8; + pMipSet->m_nBlockHeight = 6; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_COMPRESSED_RGBA_ASTC_10x5_KHR: + pMipSet->m_format = CMP_FORMAT_ASTC; + pMipSet->m_nBlockWidth = 10; + pMipSet->m_nBlockHeight = 5; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_COMPRESSED_RGBA_ASTC_10x6_KHR: + pMipSet->m_format = CMP_FORMAT_ASTC; + pMipSet->m_nBlockWidth = 10; + pMipSet->m_nBlockHeight = 6; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_COMPRESSED_RGBA_ASTC_8x8_KHR: + pMipSet->m_format = CMP_FORMAT_ASTC; + pMipSet->m_nBlockWidth = 8; + pMipSet->m_nBlockHeight = 8; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_COMPRESSED_RGBA_ASTC_10x8_KHR: + pMipSet->m_format = CMP_FORMAT_ASTC; + pMipSet->m_nBlockWidth = 10; + pMipSet->m_nBlockHeight = 8; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_COMPRESSED_RGBA_ASTC_10x10_KHR: + pMipSet->m_format = CMP_FORMAT_ASTC; + pMipSet->m_nBlockWidth = 10; + pMipSet->m_nBlockHeight = 10; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_COMPRESSED_RGBA_ASTC_12x10_KHR: + pMipSet->m_format = CMP_FORMAT_ASTC; + pMipSet->m_nBlockWidth = 12; + pMipSet->m_nBlockHeight = 10; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_COMPRESSED_RGBA_ASTC_12x12_KHR: + pMipSet->m_format = CMP_FORMAT_ASTC; + pMipSet->m_nBlockWidth = 12; + pMipSet->m_nBlockHeight = 12; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case ETC1_RGB8_OES: + pMipSet->m_format = CMP_FORMAT_ETC_RGB; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_COMPRESSED_RGB8_ETC2: + pMipSet->m_format = CMP_FORMAT_ETC2_RGB; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_COMPRESSED_SRGB8_ETC2: + pMipSet->m_format = CMP_FORMAT_ETC2_SRGB; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_COMPRESSED_RGBA8_ETC2_EAC: + pMipSet->m_format = CMP_FORMAT_ETC2_RGBA; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: + pMipSet->m_format = CMP_FORMAT_ETC2_RGBA1; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: + pMipSet->m_format = CMP_FORMAT_ETC2_SRGBA; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: + pMipSet->m_format = CMP_FORMAT_ETC2_SRGBA1; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case COMPRESSED_FORMAT_DXT5_RxBG : + pMipSet->m_format = CMP_FORMAT_DXT5_RxBG; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case COMPRESSED_FORMAT_DXT5_RBxG : + pMipSet->m_format = CMP_FORMAT_DXT5_RBxG; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case COMPRESSED_FORMAT_DXT5_xRBG : + pMipSet->m_format = CMP_FORMAT_DXT5_xRBG; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case COMPRESSED_FORMAT_DXT5_RGxB : + pMipSet->m_format = CMP_FORMAT_DXT5_RGxB; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case COMPRESSED_FORMAT_DXT5_xGxR : + pMipSet->m_format = CMP_FORMAT_DXT5_xGxR; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + default: + if (KTX_CMips) + KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) unsupported GL format %x\n"), EL_Error, IDS_ERROR_UNSUPPORTED_TYPE, fheader.glFormat); + fclose(pFile); + return -1; + } + } + else + { + pMipSet->m_compressed = false; + switch (fheader.glType) + { + case GL_UNSIGNED_BYTE: + pMipSet->m_ChannelFormat = CF_8bit; + switch (fheader.glFormat) + { + case GL_RED: + pMipSet->m_format = CMP_FORMAT_R_8; + pMipSet->m_TextureDataType = TDT_R; + break; + case GL_RG: + pMipSet->m_format = CMP_FORMAT_RG_8; + pMipSet->m_TextureDataType = TDT_RG; + break; + case GL_RGB: + pMipSet->m_format = CMP_FORMAT_RGB_888; + pMipSet->m_TextureDataType = TDT_RGB; + break; + case GL_RGBA: + case GL_RGBA8: + pMipSet->m_format = CMP_FORMAT_ARGB_8888; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_BGR: // CMP Needs a new format to handle this correctly (swizzled) + pMipSet->m_swizzle = true; + pMipSet->m_format = CMP_FORMAT_RGB_888; + pMipSet->m_TextureDataType = TDT_RGB; + break; + case GL_BGRA: + pMipSet->m_swizzle = true; + pMipSet->m_format = CMP_FORMAT_ARGB_8888; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + } + break; + case GL_UNSIGNED_SHORT: + pMipSet->m_ChannelFormat = CF_16bit; + switch (fheader.glFormat) + { + case GL_RED: + pMipSet->m_format = CMP_FORMAT_R_16; + pMipSet->m_TextureDataType = TDT_R; + break; + case GL_RG: + pMipSet->m_format = CMP_FORMAT_RG_16; + pMipSet->m_TextureDataType = TDT_RG; + break; + case GL_RGBA: + pMipSet->m_format = CMP_FORMAT_ARGB_16; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_BGRA: + pMipSet->m_swizzle = true; + pMipSet->m_format = CMP_FORMAT_ARGB_16; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + } + break; + case GL_HALF_FLOAT: + pMipSet->m_ChannelFormat = CF_Float16; + switch (fheader.glFormat) + { + case GL_RED: + pMipSet->m_format = CMP_FORMAT_R_16F; + pMipSet->m_TextureDataType = TDT_R; + break; + case GL_RG: + pMipSet->m_format = CMP_FORMAT_RG_16F; + pMipSet->m_TextureDataType = TDT_RG; + break; + case GL_RGBA: + pMipSet->m_format = CMP_FORMAT_ARGB_16F; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_BGRA: + pMipSet->m_swizzle = true; + pMipSet->m_format = CMP_FORMAT_ARGB_16F; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + } + break; + case GL_UNSIGNED_INT_2_10_10_10_REV: + pMipSet->m_format = CMP_FORMAT_ARGB_2101010; + pMipSet->m_TextureDataType = TDT_ARGB; + pMipSet->m_ChannelFormat = CF_2101010; + break; + case GL_FLOAT: + pMipSet->m_ChannelFormat = CF_Float32; + switch (fheader.glFormat) + { + case GL_RED: + pMipSet->m_format = CMP_FORMAT_R_32F; + pMipSet->m_TextureDataType = TDT_R; + break; + case GL_RG: + pMipSet->m_format = CMP_FORMAT_RG_32F; + pMipSet->m_TextureDataType = TDT_RG; + break; + case GL_RGBA: + pMipSet->m_format = CMP_FORMAT_ARGB_32F; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_BGRA: + pMipSet->m_swizzle = true; + pMipSet->m_format = CMP_FORMAT_ARGB_32F; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + } + break; + break; + default: + if (KTX_CMips) + KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) unsupported GL format %x\n"), EL_Error, IDS_ERROR_UNSUPPORTED_TYPE, fheader.glFormat); + fclose(pFile); + return -1; + } + } + + switch (texinfo.glTarget) + { + case GL_TEXTURE_2D: + pMipSet->m_TextureType = TT_2D; + break; + case GL_TEXTURE_3D: + pMipSet->m_TextureType = TT_VolumeTexture; + break; + case GL_TEXTURE_CUBE_MAP: + pMipSet->m_TextureType = TT_CubeMap; + break; + default: + if (KTX_CMips) + KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) unsupported texture format %x\n"), EL_Error, IDS_ERROR_UNSUPPORTED_TYPE, texinfo.glTarget); + fclose(pFile); + return -1; + } + + if (fheader.numberOfMipmapLevels == 0) fheader.numberOfMipmapLevels = 1; + if (fheader.numberOfArrayElements != 0) + { + if (KTX_CMips) + KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) array textures not supported %x\n"), EL_Error, IDS_ERROR_UNSUPPORTED_TYPE, fheader.numberOfArrayElements); + fclose(pFile); + return -1; + } + + + pMipSet->m_nMipLevels = fheader.numberOfMipmapLevels; + if (fheader.numberOfFaces < 1) fheader.numberOfFaces = 1; // depthsupport + pMipSet->m_nDepth = fheader.numberOfFaces; + pMipSet->dwWidth = fheader.pixelWidth; + pMipSet->dwHeight = fheader.pixelHeight; + + // Allocate MipSet header + KTX_CMips->AllocateMipSet(pMipSet, + pMipSet->m_ChannelFormat, + pMipSet->m_TextureDataType, + pMipSet->m_TextureType, + fheader.pixelWidth, + fheader.pixelHeight, + pMipSet->m_nDepth // depthsupport + ); + + if (pMipSet->m_nMipLevels > pMipSet->m_nMaxMipLevels) + { + pMipSet->m_nMipLevels = pMipSet->m_nMaxMipLevels; + } + + fclose(pFile); + pFile = NULL; + pFile = fopen(pszFilename, "rb"); + if (pFile == NULL) + { + if (KTX_CMips) + KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) loading file = %s "), EL_Error, IDS_ERROR_FILE_OPEN, pszFilename); + return -1; + } + + //skip key value data + int imageSizeOffset = sizeof(KTX_header) + fheader.bytesOfKeyValueData; + if (fseek(pFile, imageSizeOffset, SEEK_SET)) + { + if (KTX_CMips) + KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) Seek past key/vals in KTX compressed bitmap file failed. Format %x\n"), EL_Error, IDS_ERROR_UNSUPPORTED_TYPE, fheader.glFormat); + fclose(pFile); + return -1; + } + + + int w = pMipSet->m_nWidth; + int h = pMipSet->m_nHeight; + + unsigned int totalByteRead=0; + + unsigned int faceSize = 0; + unsigned int faceSizeRounded = 0; + unsigned int numArrayElement = fheader.numberOfArrayElements; + + for (int nMipLevel = 0; nMipLevel < fheader.numberOfMipmapLevels; nMipLevel++) + { + if ((w <= 0) || (h <= 0)) break; + + totalByteRead = fread(&faceSize, 1, sizeof(khronos_uint32_t), pFile); + if (totalByteRead == 0) { + if (KTX_CMips) + KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) Read image data size failed. Format %x\n"), EL_Error, IDS_ERROR_UNSUPPORTED_TYPE, fheader.glFormat); + fclose(pFile); + return -1; + } + if (fheader.endianness == KTX_ENDIAN_REF_REV) { + _ktxSwapEndian32(&faceSize, 1); + } + faceSizeRounded = (faceSize + 3) & ~(khronos_uint32_t)3; + + for (int face = 0; face < fheader.numberOfFaces; ++face) + { + // Determine buffer size and set Mip Set Levels + MipLevel *pMipLevel = KTX_CMips->GetMipLevel(pMipSet, nMipLevel, face); + if (pMipSet->m_compressed) + KTX_CMips->AllocateCompressedMipLevelData(pMipLevel, w, h, faceSizeRounded); + else + KTX_CMips->AllocateMipLevelData(pMipLevel, w, h, pMipSet->m_ChannelFormat, pMipSet->m_TextureDataType); + + // handle padded KTX data + // store size to be read at this miplevel to check with face size in KTX file + + int sizeTobeRead = 0; + int numChannel = 0; + int bytesPerChannel = 0; + + if (pMipSet->m_compressed) + { + sizeTobeRead = faceSizeRounded; + } + else + { + switch (pMipSet->m_TextureDataType) + { + case TDT_XRGB: + numChannel = 3; + break; + case TDT_ARGB: + case TDT_NORMAL_MAP: + numChannel = 4; + break; + + case TDT_R: + numChannel = 1; + break; + + case TDT_RG: + numChannel = 2; + break; + + default: + return -1; + } + + switch (pMipSet->m_ChannelFormat) + { + case CF_8bit: + case CF_2101010: + case CF_Float9995E: + bytesPerChannel = 1; + break; + + case CF_16bit: + case CF_Float16: + bytesPerChannel = 2; + break; + + case CF_32bit: + case CF_Float32: + bytesPerChannel = 4; + break; + + default: + return -1; + } + + sizeTobeRead = w * h * numChannel * bytesPerChannel; + } + CMP_BYTE* pData = (CMP_BYTE*)(pMipLevel->m_pbData); + + if (!pData) + { + if (KTX_CMips) + KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) Read image data failed, Out of Memory. Format %x\n"), EL_Error, IDS_ERROR_UNSUPPORTED_TYPE, fheader.glFormat); + fclose(pFile); + return -1; + } + + //read image data + unsigned int bytesRead = 0; + //size to be read has to be same as face size, else padding is done in the KTX file + if (sizeTobeRead == faceSizeRounded) + { + bytesRead = fread(pData, 1, faceSizeRounded, pFile); + } + else if(faceSizeRounded > sizeTobeRead) // padding in KTX file + { + std::vector pTempData; + pTempData.resize(faceSizeRounded); + bytesRead = fread(pTempData.data(), 1, faceSizeRounded, pFile); + int paddedBytes = faceSizeRounded - sizeTobeRead; + + int n = 0; + for (int i = 0; i < pTempData.size(); i++) + { + for (int w_i = 0; w_i < w; w_i++) + { + for (int j = 0; j < numChannel*bytesPerChannel; j++) + { + pData[n++] = pTempData[i++]; + } + } + //skip padded bytes + i += (paddedBytes / h)-1; + } + } + + if (bytesRead != faceSizeRounded) + { + if (KTX_CMips) + KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) Read image data failed. Unexpectec EOF. Format %x\n"), EL_Error, IDS_ERROR_UNSUPPORTED_TYPE, fheader.glFormat); + fclose(pFile); + return -1; + } + } + // next miplevel width and height + w = max(0, w >> 1); + h = max(0, h >> 1); + } + + return 0; +} + +int Plugin_KTX::TC_PluginFileSaveTexture(const char* pszFilename, MipSet* pMipSet) +{ + assert(pszFilename); + assert(pMipSet); + + FILE* pFile = NULL; + pFile = fopen(pszFilename, "wb"); + if (pFile == NULL) + { + if (KTX_CMips) + KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) saving file = %s "), EL_Error, IDS_ERROR_FILE_OPEN, pszFilename); + return -1; + } + + //using libktx + KTX_texture_info textureinfo; + KTX_image_info* inputMip = new KTX_image_info[pMipSet->m_nMipLevels]; + + unsigned pDataLen = 0; + CMP_BYTE* pData = NULL; + bool isCompressed = false; + + if (pMipSet->m_TextureType == TT_CubeMap) + textureinfo.numberOfFaces = 6; + else + textureinfo.numberOfFaces = 1; + + //todo: handle array textures + textureinfo.numberOfArrayElements = 0; + + if (pMipSet->m_pMipLevelTable == NULL) + { + if (KTX_CMips) + KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) saving file = %s "), EL_Error, IDS_ERROR_ALLOCATEMIPSET, pszFilename); + return -1; + } + + if (KTX_CMips->GetMipLevel(pMipSet, 0) == NULL) + { + if (KTX_CMips) + KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) saving file = %s "), EL_Error, IDS_ERROR_ALLOCATEMIPSET, pszFilename); + return -1; + } + + int nSlices = (pMipSet->m_TextureType == TT_2D) ? 1 : CMP_MaxFacesOrSlices(pMipSet, 0); + + for (int nSlice = 0; nSlice < nSlices; nSlice++) + { + for (int nMipLevel = 0; nMipLevel < pMipSet->m_nMipLevels; nMipLevel++) + { + inputMip[nMipLevel].size = KTX_CMips->GetMipLevel(pMipSet, nMipLevel)->m_dwLinearSize; + inputMip[nMipLevel].data = KTX_CMips->GetMipLevel(pMipSet, nMipLevel, nSlice)->m_pbData; + } + } + + switch (pMipSet->m_format) + { + //uncompressed format case + case CMP_FORMAT_ARGB_8888 : + case CMP_FORMAT_RGB_888 : + case CMP_FORMAT_RG_8 : + case CMP_FORMAT_R_8 : + isCompressed = false; + textureinfo.glType = GL_UNSIGNED_BYTE; + textureinfo.glTypeSize = 1; + break; + case CMP_FORMAT_ARGB_2101010 : + textureinfo.glType = GL_UNSIGNED_INT_2_10_10_10_REV; + textureinfo.glTypeSize = 1; + break; + case CMP_FORMAT_ARGB_16 : + textureinfo.glType = GL_UNSIGNED_SHORT; + textureinfo.glTypeSize = 2; + break; + case CMP_FORMAT_RG_16 : + textureinfo.glType = GL_UNSIGNED_SHORT; + textureinfo.glTypeSize = 2; + break; + case CMP_FORMAT_R_16 : + textureinfo.glType = GL_UNSIGNED_SHORT; + textureinfo.glTypeSize = 2; + break; + case CMP_FORMAT_ARGB_16F : + case CMP_FORMAT_RG_16F : + case CMP_FORMAT_R_16F : + textureinfo.glType = GL_HALF_FLOAT; + textureinfo.glTypeSize = 2; + break; + case CMP_FORMAT_ARGB_32F : + case CMP_FORMAT_RGB_32F : + case CMP_FORMAT_RG_32F : + case CMP_FORMAT_R_32F : + textureinfo.glType = GL_FLOAT; + textureinfo.glTypeSize = 4; + break; + //compressed format case + case CMP_FORMAT_ATI1N : + case CMP_FORMAT_ATI2N : + case CMP_FORMAT_ATI2N_XY : + case CMP_FORMAT_ATI2N_DXT5 : + case CMP_FORMAT_ATC_RGB : + case CMP_FORMAT_ATC_RGBA_Explicit : + case CMP_FORMAT_ATC_RGBA_Interpolated : + case CMP_FORMAT_BC1 : + case CMP_FORMAT_BC2 : + case CMP_FORMAT_BC3 : + case CMP_FORMAT_BC4 : + case CMP_FORMAT_BC4_S: + case CMP_FORMAT_BC5: + case CMP_FORMAT_BC5_S: + case CMP_FORMAT_BC6H: + case CMP_FORMAT_BC6H_SF: + case CMP_FORMAT_BC7 : + case CMP_FORMAT_DXT1 : + case CMP_FORMAT_DXT3 : + case CMP_FORMAT_DXT5 : + case CMP_FORMAT_DXT5_xGBR : + case CMP_FORMAT_DXT5_RxBG : + case CMP_FORMAT_DXT5_RBxG : + case CMP_FORMAT_DXT5_xRBG : + case CMP_FORMAT_DXT5_RGxB : + case CMP_FORMAT_DXT5_xGxR : + case CMP_FORMAT_ETC_RGB : + case CMP_FORMAT_ETC2_RGB: + case CMP_FORMAT_ETC2_SRGB: + case CMP_FORMAT_ETC2_RGBA: + case CMP_FORMAT_ETC2_RGBA1: + case CMP_FORMAT_ETC2_SRGBA: + case CMP_FORMAT_ETC2_SRGBA1: + case CMP_FORMAT_ASTC : +#ifdef USE_GTC + case CMP_FORMAT_GTC: +#endif +#ifdef USE_BASIS + case CMP_FORMAT_BASIS: +#endif + isCompressed = true; + textureinfo.glType = 0; + textureinfo.glTypeSize = 1; + textureinfo.glFormat = 0; + break; + //default case + default: + isCompressed = false; + textureinfo.glType = GL_UNSIGNED_BYTE; + textureinfo.glTypeSize = 1; + break; + } + + switch (pMipSet->m_TextureDataType) + { + case TDT_R: //single component-- can be Luminance and Alpha case, here only cover R + { + if (!isCompressed) + { + textureinfo.glFormat = textureinfo.glBaseInternalFormat = GL_RED; + textureinfo.glInternalFormat = GL_RED; + } + else + { + textureinfo.glBaseInternalFormat = GL_RED; + textureinfo.glInternalFormat = GL_RED; + } + } + break; + case TDT_RG: //two component + { + if (!isCompressed) + { + textureinfo.glFormat = textureinfo.glBaseInternalFormat = GL_RG; + textureinfo.glInternalFormat = GL_RG; + } + else + { + textureinfo.glBaseInternalFormat = GL_RG; + textureinfo.glInternalFormat = GL_COMPRESSED_RG; + } + } + break; + case TDT_XRGB: //normally 3 component + { + if (!isCompressed) + { + textureinfo.glFormat = textureinfo.glBaseInternalFormat = GL_RGB; + textureinfo.glInternalFormat = GL_RGB; + } + else + { + + switch (pMipSet->m_format) + { + case CMP_FORMAT_BC1 : + case CMP_FORMAT_DXT1: + textureinfo.glBaseInternalFormat = GL_RGB; + textureinfo.glInternalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; + break; + case CMP_FORMAT_ETC_RGB: + textureinfo.glBaseInternalFormat = GL_RGB; + textureinfo.glInternalFormat = ETC1_RGB8_OES; + break; + case CMP_FORMAT_ETC2_RGB: + textureinfo.glBaseInternalFormat = GL_RGB; + textureinfo.glInternalFormat = GL_COMPRESSED_RGB8_ETC2; + break; + case CMP_FORMAT_ETC2_SRGB: + textureinfo.glBaseInternalFormat = GL_SRGB; + textureinfo.glInternalFormat = GL_COMPRESSED_SRGB8_ETC2; + break; + default: + textureinfo.glBaseInternalFormat = GL_RGB; + textureinfo.glInternalFormat = GL_RGB; //other compressed format not found in qtangle header + break; + } + } + } + break; + case TDT_ARGB: //4 component + { + if (!isCompressed) + { + textureinfo.glFormat = textureinfo.glBaseInternalFormat = GL_RGBA; + textureinfo.glInternalFormat = GL_RGBA8; + } + else + { + textureinfo.glBaseInternalFormat = GL_RGBA; + switch (pMipSet->m_format) + { + case CMP_FORMAT_BC1: + case CMP_FORMAT_DXT1: + textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + break; + case CMP_FORMAT_BC2: + case CMP_FORMAT_DXT3: + textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + break; + case CMP_FORMAT_BC3: + case CMP_FORMAT_DXT5: + textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + break; + case CMP_FORMAT_BC4: + textureinfo.glInternalFormat = GL_COMPRESSED_RED_RGTC1; + break; + case CMP_FORMAT_BC4_S: + textureinfo.glInternalFormat = GL_COMPRESSED_SIGNED_RED_RGTC1; + break; + case CMP_FORMAT_BC5: + textureinfo.glInternalFormat = GL_COMPRESSED_RG_RGTC2; + break; + case CMP_FORMAT_BC5_S: + textureinfo.glInternalFormat = GL_COMPRESSED_SIGNED_RG_RGTC2; + break; + case CMP_FORMAT_BC6H: + textureinfo.glInternalFormat = GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT; + break; + case CMP_FORMAT_BC6H_SF: + textureinfo.glInternalFormat = GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT; + break; + case CMP_FORMAT_BC7: + textureinfo.glInternalFormat = RGB_BP_UNorm; + break; + case CMP_FORMAT_ATI1N: + textureinfo.glInternalFormat = R_ATI1N_UNorm; + break; + case CMP_FORMAT_ATI2N: + textureinfo.glInternalFormat = R_ATI1N_SNorm; + break; + case CMP_FORMAT_ATI2N_XY: + textureinfo.glInternalFormat = RG_ATI2N_UNorm; + break; + case CMP_FORMAT_ATI2N_DXT5: + textureinfo.glInternalFormat = RG_ATI2N_SNorm; + break; + case CMP_FORMAT_ATC_RGB: + textureinfo.glInternalFormat = ATC_RGB_AMD; + break; + case CMP_FORMAT_ATC_RGBA_Explicit: + textureinfo.glInternalFormat = ATC_RGBA_EXPLICIT_ALPHA_AMD; + break; + case CMP_FORMAT_ATC_RGBA_Interpolated: + textureinfo.glInternalFormat = ATC_RGBA_INTERPOLATED_ALPHA_AMD; + break; + + case CMP_FORMAT_ASTC: + if ((pMipSet->m_nBlockWidth == 4) && (pMipSet->m_nBlockHeight == 4)) + textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_ASTC_4x4_KHR; + else if ((pMipSet->m_nBlockWidth == 5) && (pMipSet->m_nBlockHeight == 4)) + textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_ASTC_5x4_KHR; + else if ((pMipSet->m_nBlockWidth == 5) && (pMipSet->m_nBlockHeight == 5)) + textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_ASTC_5x5_KHR; + else if ((pMipSet->m_nBlockWidth == 6) && (pMipSet->m_nBlockHeight == 5)) + textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_ASTC_6x5_KHR; + else if ((pMipSet->m_nBlockWidth == 6) && (pMipSet->m_nBlockHeight == 6)) + textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_ASTC_6x6_KHR; + else if ((pMipSet->m_nBlockWidth == 8) && (pMipSet->m_nBlockHeight == 5)) + textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_ASTC_8x5_KHR; + else if ((pMipSet->m_nBlockWidth == 8) && (pMipSet->m_nBlockHeight == 6)) + textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_ASTC_8x6_KHR; + else if ((pMipSet->m_nBlockWidth == 8) && (pMipSet->m_nBlockHeight == 8)) + textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_ASTC_8x8_KHR; + else if ((pMipSet->m_nBlockWidth == 10) && (pMipSet->m_nBlockHeight == 5)) + textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_ASTC_10x5_KHR; + else if ((pMipSet->m_nBlockWidth == 10) && (pMipSet->m_nBlockHeight == 6)) + textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_ASTC_10x6_KHR; + else if ((pMipSet->m_nBlockWidth == 10) && (pMipSet->m_nBlockHeight == 8)) + textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_ASTC_10x8_KHR; + else if ((pMipSet->m_nBlockWidth == 10) && (pMipSet->m_nBlockHeight == 10)) + textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_ASTC_10x10_KHR; + else if ((pMipSet->m_nBlockWidth == 12) && (pMipSet->m_nBlockHeight == 10)) + textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_ASTC_12x10_KHR; + else if ((pMipSet->m_nBlockWidth == 12) && (pMipSet->m_nBlockHeight == 12)) + textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_ASTC_12x12_KHR; + else + textureinfo.glInternalFormat = GL_COMPRESSED_RGBA_ASTC_4x4_KHR; + break; + case CMP_FORMAT_ETC_RGB: + textureinfo.glInternalFormat = ETC1_RGB8_OES; + break; + case CMP_FORMAT_ETC2_RGB: + textureinfo.glInternalFormat = GL_COMPRESSED_RGB8_ETC2; + break; + case CMP_FORMAT_ETC2_SRGB: + textureinfo.glInternalFormat = GL_COMPRESSED_SRGB8_ETC2; + break; + case CMP_FORMAT_ETC2_RGBA: + textureinfo.glInternalFormat = GL_COMPRESSED_RGBA8_ETC2_EAC; + break; + case CMP_FORMAT_ETC2_RGBA1: + textureinfo.glInternalFormat = GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; + break; + case CMP_FORMAT_ETC2_SRGBA: + textureinfo.glBaseInternalFormat = GL_SRGB8_ALPHA8; + textureinfo.glInternalFormat = GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC; + break; + case CMP_FORMAT_ETC2_SRGBA1: + textureinfo.glBaseInternalFormat = GL_SRGB8_ALPHA8; + textureinfo.glInternalFormat = GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2; + break; + case CMP_FORMAT_DXT5_xGBR: + textureinfo.glInternalFormat = COMPRESSED_FORMAT_DXT5_xGBR; + break; + case CMP_FORMAT_DXT5_RxBG: + textureinfo.glInternalFormat = COMPRESSED_FORMAT_DXT5_RxBG; + break; + case CMP_FORMAT_DXT5_RBxG: + textureinfo.glInternalFormat = COMPRESSED_FORMAT_DXT5_RBxG; + break; + case CMP_FORMAT_DXT5_xRBG: + textureinfo.glInternalFormat = COMPRESSED_FORMAT_DXT5_xRBG; + break; + case CMP_FORMAT_DXT5_RGxB: + textureinfo.glInternalFormat = COMPRESSED_FORMAT_DXT5_RGxB; + break; + case CMP_FORMAT_DXT5_xGxR: + textureinfo.glInternalFormat = COMPRESSED_FORMAT_DXT5_xGxR; + break; + + } + } + } + break; + default: + break; + } + + textureinfo.pixelWidth = pMipSet->m_nWidth; + textureinfo.pixelHeight = pMipSet->m_nHeight; + textureinfo.pixelDepth = 0; //for 1D, 2D and cube texture , depth =0; + + //1D + if (pMipSet->m_nHeight == 1 && pData != NULL) { + delete(pData); + pData = NULL; + pDataLen = 0; + } + + textureinfo.numberOfMipmapLevels = pMipSet->m_nMipLevels; + + KTX_error_code save = ktxWriteKTXF(pFile, &textureinfo, pDataLen, pData, pMipSet->m_nMipLevels, inputMip); + if (save == KTX_SUCCESS) { + fclose(pFile); + } + else { + if (KTX_CMips) + KTX_CMips->PrintError(("Error(%d): KTX Plugin ID(%d) saving file = %s "), EL_Error, IDS_ERROR_FILE_OPEN, pszFilename); + fclose(pFile); + return -1; + } + + return 0; +} + + diff --git a/applications/_plugins/cimage/ktx/ktx.def b/applications/_plugins/cimage/ktx/ktx1.def similarity index 100% rename from applications/_plugins/cimage/ktx/ktx.def rename to applications/_plugins/cimage/ktx/ktx1.def diff --git a/applications/_plugins/cimage/ktx/cktx.h b/applications/_plugins/cimage/ktx/ktx1.h similarity index 50% rename from applications/_plugins/cimage/ktx/cktx.h rename to applications/_plugins/cimage/ktx/ktx1.h index da5f8a478..1207439cc 100644 --- a/applications/_plugins/cimage/ktx/cktx.h +++ b/applications/_plugins/cimage/ktx/ktx1.h @@ -8,10 +8,10 @@ // to use, copy, modify, merge, publish, distribute, sublicense, and / or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions : -// +// // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE @@ -25,45 +25,44 @@ #define _PLUGIN_IMAGE_KTX_H #include "plugininterface.h" - -#include #include "stdint.h" #include "ktx.h" -#include "ktxvulkan.h" #include "ktxint.h" -#include "KHR/khrplatform.h" -#include "GL/glew.h" // {737CE1F6-F448-499E-B8B5-585F9A22893C} #ifdef _WIN32 -static const GUID g_GUID = {0x737ce1f6, 0xf448, 0x499e, {0xb8, 0xb5, 0x58, 0x5f, 0x9a, 0x22, 0x89, 0x3c}}; +static const GUID g_GUID = +{ 0x737ce1f6, 0xf448, 0x499e, { 0xb8, 0xb5, 0x58, 0x5f, 0x9a, 0x22, 0x89, 0x3c } }; #else static const GUID g_GUID = {0}; #endif -#define TC_PLUGIN_VERSION_MAJOR 1 -#define TC_PLUGIN_VERSION_MINOR 0 +#define TC_PLUGIN_VERSION_MAJOR 1 +#define TC_PLUGIN_VERSION_MINOR 0 -class Plugin_KTX : public PluginInterface_Image { - public: - Plugin_KTX(); - virtual ~Plugin_KTX(); +class Plugin_KTX : public PluginInterface_Image +{ + public: + Plugin_KTX(); + virtual ~Plugin_KTX(); + + int TC_PluginSetSharedIO(void* Shared); + int TC_PluginGetVersion(TC_PluginVersion* pPluginVersion); + int TC_PluginFileLoadTexture(const char* pszFilename, MipSet* pMipSet); + int TC_PluginFileSaveTexture(const char* pszFilename, MipSet* pMipSet); + int TC_PluginFileLoadTexture(const char* pszFilename, CMP_Texture *srcTexture); + int TC_PluginFileSaveTexture(const char* pszFilename, CMP_Texture *srcTexture); - int TC_PluginSetSharedIO(void* Shared); - int TC_PluginGetVersion(TC_PluginVersion* pPluginVersion); - int TC_PluginFileLoadTexture(const char* pszFilename, MipSet* pMipSet); - int TC_PluginFileSaveTexture(const char* pszFilename, MipSet* pMipSet); - int TC_PluginFileLoadTexture(const char* pszFilename, CMP_Texture* srcTexture); - int TC_PluginFileSaveTexture(const char* pszFilename, CMP_Texture* srcTexture); }; -#define IDS_ERROR_FILE_OPEN 1 -#define IDS_ERROR_NOT_KTX 2 -#define IDS_ERROR_UNSUPPORTED_TYPE 3 -#define IDS_ERROR_ALLOCATEMIPSET 4 + +#define IDS_ERROR_FILE_OPEN 1 +#define IDS_ERROR_NOT_KTX 2 +#define IDS_ERROR_UNSUPPORTED_TYPE 3 +#define IDS_ERROR_ALLOCATEMIPSET 4 #define IDS_ERROR_ALLOCATEMIPSLEVELDATA 5 -extern void* make_Plugin_KTX(); +extern void *make_Plugin_KTX(); // ---------------- KTX File Definitions ------------------------ @@ -75,9 +74,9 @@ for each keyValuePair that fits in bytesOfKeyValueData Byte keyAndValue[keyAndValueByteSize] Byte valuePadding[3 - ((keyAndValueByteSize + 3) % 4)] end - -for each mipmap_level in numberOfMipLevels* - UInt32 imageSize; + +for each mipmap_level in numberOfMipmapLevels* + UInt32 imageSize; for each array_element in numberOfArrayElements* for each face in numberOfFaces for each z_slice in pixelDepth* @@ -99,26 +98,32 @@ end */ -uint8_t FileIdentifier[12] = {0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A}; - -struct ktx_header { - uint8_t identifier[12]; - uint32_t endianness; // should be 0x04030201 if 0x01020304 then all data below must be switched - uint32_t gl_Type; // 0 = compressed data else use OpenGL Spec to determine uncompressed data type (OpenGL 4.4 tables 8.3) - uint32_t gl_TypeSize; // endness data size for texture data stored in file 0=size of gl_type, 1 = for compressed data - uint32_t gl_Format; // 0 = compressed data else use OpenGL spec to determine format (IoebGL 4.4 table 8.3 ) - uint32_t gl_InternalFormat; // for compressed data use OpenGL 4.4 table 8.14 else use tables 8.12 & 8.13 - uint32_t gl_BaseInternalFormat; // use OpenGL 4.4 table 8.11 - uint32_t pixelWidth; // size of texture image for level 0, No rounding to block size is applied to compressed textures - uint32_t pixelHeight; // - uint32_t pixelDepth; // pixelDepth = 0 for 2D and cube textures - uint32_t numberOfArrayElements; // 0 for no array element (not a texture array) else specifies the number of array elements - uint32_t numberOfFaces; // number of cube map faces (for cubemap or cubemap arrays = 6, else 1) - uint32_t numberOfMipLevels; // 1 for non-mipmapped textures, 0 for mipped mapmapped: full pyramid should be generated from level 0 at load time - uint32_t bytesOfKeyValueData; // Key value pairs +uint8_t FileIdentifier[12] = { + 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A }; -enum scanline_copy_method { +struct ktx_header +{ + uint8_t identifier[12]; + uint32_t endianness; // should be 0x04030201 if 0x01020304 then all data below must be switched + uint32_t gl_Type; // 0 = compressed data else use OpenGL Spec to determine uncompressed data type (OpenGL 4.4 tables 8.3) + uint32_t gl_TypeSize; // endness data size for texture data stored in file 0=size of gl_type, 1 = for compressed data + uint32_t gl_Format; // 0 = compressed data else use OpenGL spec to determine format (IoebGL 4.4 table 8.3 ) + uint32_t gl_InternalFormat; // for compressed data use OpenGL 4.4 table 8.14 else use tables 8.12 & 8.13 + uint32_t gl_BaseInternalFormat; // use OpenGL 4.4 table 8.11 + uint32_t pixelWidth; // size of texture image for level 0, No rounding to block size is applied to compressed textures + uint32_t pixelHeight; // + uint32_t pixelDepth; // pixelDepth = 0 for 2D and cube textures + uint32_t numberOfArrayElements; // 0 for no array element (not a texture array) else specifies the number of array elements + uint32_t numberOfFaces; // number of cube map faces (for cubemap or cubemap arrays = 6, else 1) + uint32_t numberOfMipmapLevels; // 1 for non-mipmapped textures, 0 for mipped mapmapped: full pyramid should be generated from level 0 at load time + uint32_t bytesOfKeyValueData; // Key value pairs +}; + + + +enum scanline_copy_method +{ R8_TO_RGBA8, RG8_TO_RGBA8, RGB8_TO_RGBA8, @@ -159,25 +164,28 @@ enum scanline_copy_method { LA32F_TO_RGBA16F }; -#define R_ATI1N_UNorm 0x8DBB // GL_COMPRESSED_RED_RGTC1 -#define R_ATI1N_SNorm 0x8DBC // GL_COMPRESSED_SIGNED_RED_RGTC1 -#define RG_ATI2N_UNorm 0x8DBD // GL_COMPRESSED_RG_RGTC2 -#define RG_ATI2N_SNorm 0x8DBE // GL_COMPRESSED_SIGNED_RG_RGTC2 -#define RGB_BP_UNSIGNED_FLOAT 0x8E8F // GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB //bc6 -#define RGB_BP_SIGNED_FLOAT 0x8E8E // GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB -#define RGB_BP_UNorm 0x8E8C // GL_COMPRESSED_RGBA_BPTC_UNORM_ARB //bc7 -#define ATC_RGB_AMD 0x8C92 -#define ATC_RGBA_EXPLICIT_ALPHA_AMD 0x8C93 -#define ATC_RGBA_INTERPOLATED_ALPHA_AMD 0x87EE -#define COMPRESSED_RED_RGTC1 0x8DBB //bc4 -#define COMPRESSED_RG_RGTC2 0x8DBD //bc5 -#define ETC1_RGB8_OES 0x8D64 -#define COMPRESSED_FORMAT_DXT5_xGBR 0x83F3 -#define COMPRESSED_FORMAT_DXT5_RxBG 0x83F4 -#define COMPRESSED_FORMAT_DXT5_RBxG 0x83F5 -#define COMPRESSED_FORMAT_DXT5_xRBG 0x83F6 -#define COMPRESSED_FORMAT_DXT5_RGxB 0x83F7 -#define COMPRESSED_FORMAT_DXT5_xGxR 0x83F8 + +#define R_ATI1N_UNorm 0x8DBB // GL_COMPRESSED_RED_RGTC1 +#define R_ATI1N_SNorm 0x8DBC // GL_COMPRESSED_SIGNED_RED_RGTC1 +#define RG_ATI2N_UNorm 0x8DBD // GL_COMPRESSED_RG_RGTC2 +#define RG_ATI2N_SNorm 0x8DBE // GL_COMPRESSED_SIGNED_RG_RGTC2 +#define RGB_BP_UNSIGNED_FLOAT 0x8E8F // GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB //bc6 +#define RGB_BP_SIGNED_FLOAT 0x8E8E // GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB +#define RGB_BP_UNorm 0x8E8C // GL_COMPRESSED_RGBA_BPTC_UNORM_ARB //bc7 +#define COMPRESSED_RED_RGTC1 0x8DBB //bc4 +#define COMPRESSED_RG_RGTC2 0x8DBD //bc5 ATI2_XY + +// Legacy setting should move to reserved temp space!! +#define ATC_RGB_AMD 0x8C92 +#define ATC_RGBA_EXPLICIT_ALPHA_AMD 0x8C93 +#define ATC_RGBA_INTERPOLATED_ALPHA_AMD 0x87EE +#define ETC1_RGB8_OES 0x8D64 +#define COMPRESSED_FORMAT_DXT5_xGBR 0x83F3 +#define COMPRESSED_FORMAT_DXT5_RxBG 0x83F4 +#define COMPRESSED_FORMAT_DXT5_RBxG 0x83F5 +#define COMPRESSED_FORMAT_DXT5_xRBG 0x83F6 +#define COMPRESSED_FORMAT_DXT5_RGxB 0x83F7 +#define COMPRESSED_FORMAT_DXT5_xGxR 0x83F8 //---------------------------------------------------------------- // Definitions from etcpack v2.74 @@ -186,18 +194,17 @@ enum scanline_copy_method { // #define GL_SRGB 0x8C40 // #define GL_SRGB8 0x8C41 // #define GL_SRGB8_ALPHA8 0x8C43 -// #define GL_ETC1_RGB8_OES 0x8d64 // #define GL_COMPRESSED_R11_EAC 0x9270 // #define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 // #define GL_COMPRESSED_RG11_EAC 0x9272 // #define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 -// #define GL_COMPRESSED_SRGB8_ETC2 0x9275 -// #define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 -// #define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 -// #define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 -// #define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 -#define GL_COMPRESSED_RGB8_ETC2 0x9274 +#define GL_COMPRESSED_RGB8_ETC2 0x9274 +#define GL_COMPRESSED_SRGB8_ETC2 0x9275 +#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 +#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 +#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 //// enums copied from GL/GL.h //#define GL_RED 0x1903 diff --git a/applications/_plugins/cimage/ktx/ktxcommon.cpp b/applications/_plugins/cimage/ktx/ktxcommon.cpp new file mode 100644 index 000000000..802fbd617 --- /dev/null +++ b/applications/_plugins/cimage/ktx/ktxcommon.cpp @@ -0,0 +1,26 @@ +//===================================================================== +// Copyright 2020 (c), Advanced Micro Devices, Inc. All rights reserved. +//===================================================================== +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#include "ktxcommon.h" + + diff --git a/applications/_plugins/cimage/ktx/ktxcommon.h b/applications/_plugins/cimage/ktx/ktxcommon.h new file mode 100644 index 000000000..ded5c5542 --- /dev/null +++ b/applications/_plugins/cimage/ktx/ktxcommon.h @@ -0,0 +1,30 @@ +//===================================================================== +// Copyright 2016 (c), Advanced Micro Devices, Inc. All rights reserved. +//===================================================================== +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#ifndef _PLUGIN_IMAGE_KTXCOMMON_H +#define _PLUGIN_IMAGE_KTXCOMMON_H + +#include "plugininterface.h" +#include "stdint.h" + +#endif diff --git a/applications/_plugins/cimage/ktx/lib/checkheader.c b/applications/_plugins/cimage/ktx/lib/checkheader.c new file mode 100644 index 000000000..9d565c16a --- /dev/null +++ b/applications/_plugins/cimage/ktx/lib/checkheader.c @@ -0,0 +1,169 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4: */ + +/* $Id: 0afbdd40d2414a4a65d75a5dd9feeacee9e4d547 $ */ + +/** + * @internal + * @file checkheader.c + * @~English + * + * @brief Function to verify a KTX file header + * + * @author Mark Callow, HI Corporation + */ + +/* +Copyright (c) 2010 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +unaltered in all copies or substantial portions of the Materials. +Any additions, deletions, or changes to the original source files +must be clearly indicated in accompanying documentation. + +If only executable code is distributed, then the accompanying +documentation must state that "this software is based in part on the +work of the Khronos Group." + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +/* + * Author: Georg Kolling, Imagination Technology with modifications + * by Mark Callow, HI Corporation. + */ +#include + +#include "ktx.h" +#include "ktxint.h" + +/** + * @internal + * @~English + * @brief Check a KTX file header. + * + * As well as checking that the header identifies a KTX file, the function + * sanity checks the values and returns information about the texture in a + * KTX_texinfo structure. + * + * @param header pointer to the KTX header to check + * @param texinfo pointer to a KTX_texinfo structure in which to return + * information about the texture. + * + * @author Georg Kolling, Imagination Technology + * @author Mark Callow, HI Corporation + */ +KTX_error_code _ktxCheckHeader(KTX_header* header, KTX_texinfo* texinfo) +{ + khronos_uint8_t identifier_reference[12] = KTX_IDENTIFIER_REF; + khronos_uint32_t max_dim; + + /* Compare identifier, is this a KTX file? */ + if (memcmp(header->identifier, identifier_reference, 12) != 0) + { + return KTX_UNKNOWN_FILE_FORMAT; + } + + if (header->endianness == KTX_ENDIAN_REF_REV) + { + /* Convert endianness of header fields. */ + _ktxSwapEndian32(&header->glType, 12); + + if (header->glTypeSize != 1 && + header->glTypeSize != 2 && + header->glTypeSize != 4) + { + /* Only 8-, 16-, and 32-bit types supported so far. */ + return KTX_INVALID_VALUE; + } + } + else if (header->endianness != KTX_ENDIAN_REF) + { + return KTX_INVALID_VALUE; + } + + /* Check glType and glFormat */ + texinfo->compressed = 0; + if (header->glType == 0 || header->glFormat == 0) + { + if (header->glType + header->glFormat != 0) + { + /* either both or none of glType, glFormat must be zero */ + return KTX_INVALID_VALUE; + } + texinfo->compressed = 1; + } + + /* Check texture dimensions. KTX files can store 8 types of textures: + 1D, 2D, 3D, cube, and array variants of these. There is currently + no GL extension for 3D array textures. */ + if ((header->pixelWidth == 0) || + (header->pixelDepth > 0 && header->pixelHeight == 0)) + { + /* texture must have width */ + /* texture must have height if it has depth */ + return KTX_INVALID_VALUE; + } + + texinfo->textureDimensions = 1; + texinfo->glTarget = GL_TEXTURE_1D; + texinfo->generateMipmaps = 0; + if (header->pixelHeight > 0) + { + texinfo->textureDimensions = 2; + texinfo->glTarget = GL_TEXTURE_2D; + } + if (header->pixelDepth > 0) + { + texinfo->textureDimensions = 3; + texinfo->glTarget = GL_TEXTURE_3D; + } + + if (header->numberOfFaces == 6) + { + if (texinfo->textureDimensions == 2) + { + texinfo->glTarget = GL_TEXTURE_CUBE_MAP; + } + else + { + /* cube map needs 2D faces */ + return KTX_INVALID_VALUE; + } + } + else if (header->numberOfFaces != 1) + { + /* numberOfFaces must be either 1 or 6 */ + return KTX_INVALID_VALUE; + } + + /* Check number of mipmap levels */ + if (header->numberOfMipmapLevels == 0) + { + texinfo->generateMipmaps = 1; + header->numberOfMipmapLevels = 1; + } + /* This test works for arrays too because height or depth will be 0. */ + max_dim = MAX(MAX(header->pixelWidth, header->pixelHeight), header->pixelDepth); + if (max_dim < ((khronos_uint32_t)1 << (header->numberOfMipmapLevels - 1))) + { + /* Can't have more mip levels than 1 + log2(max(width, height, depth)) */ + return KTX_INVALID_VALUE; + } + + return KTX_SUCCESS; +} diff --git a/applications/_plugins/cimage/ktx/lib/errstr.c b/applications/_plugins/cimage/ktx/lib/errstr.c new file mode 100644 index 000000000..5b26f69be --- /dev/null +++ b/applications/_plugins/cimage/ktx/lib/errstr.c @@ -0,0 +1,79 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4: */ + +/* $Id: 66fc783ff8cf9a7836f10cccf03607bad0860246 $ */ + +/** + * @file errstr.c + * @~English + * + * @brief Function to return a string corresponding to a KTX error code. + * + * @author Mark Callow, HI Corporation + */ + +/* +Copyright (c) 2010 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +unaltered in all copies or substantial portions of the Materials. +Any additions, deletions, or changes to the original source files +must be clearly indicated in accompanying documentation. + +If only executable code is distributed, then the accompanying +documentation must state that "this software is based in part on the +work of the Khronos Group." + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +#include "ktx.h" + +static const char* const errorStrings[] = { + "Operation succeeded", /* KTX_SUCCESS */ + "File open failed", /* KTX_FILE_OPEN_FAILED */ + "File write failed", /* KTX_FILE_WRITE_ERROR */ + "GL error occurred", /* KTX_GL_ERROR */ + "Operation not allowed in the current state", /* KTX_INVALID_OPERATION */ + "Invalid parameter value", /* KTX_INVALID_VALUE */ + "Key not found", /* KTX_NOT_FOUND */ + "Out of memory", /* KTX_OUT_OF_MEMORY */ + "Unexpected end of file", /* KTX_UNEXPECTED_END_OF_FILE */ + "Not a KTX file", /* KTX_UNKNOWN_FILE_FORMAT */ + "Texture type not supported by GL context" /* KTX_UNSUPPORTED_TEXTURE_TYPE */ +}; +static const int lastErrorCode = (sizeof(errorStrings) / sizeof(char*)) - 1; + + +/** + * @~English + * @brief Return a string corresponding to a KTX error code. + * + * @param error the error code for which to return a string + * + * @return pointer to the message string. + * + * @internal Use UTF-8 for translated message strings. + * + * @author Mark Callow, HI Corporation + */ +const char* const ktxErrorString(KTX_error_code error) +{ + if (error > lastErrorCode) + return "Unrecognized error code"; + return errorStrings[error]; +} diff --git a/applications/_plugins/cimage/ktx/lib/etcdec.cxx b/applications/_plugins/cimage/ktx/lib/etcdec.cxx new file mode 100644 index 000000000..5731979b4 --- /dev/null +++ b/applications/_plugins/cimage/ktx/lib/etcdec.cxx @@ -0,0 +1,1845 @@ +/* + +@~English +@page license License + +@section etcdec etcdec.cxx License + +etcdec.cxx is made available under the terms and conditions of the following +License Agreement. + +Software License Agreement + +PLEASE REVIEW THE FOLLOWING TERMS AND CONDITIONS PRIOR TO USING THE +ERICSSON TEXTURE COMPRESSION CODEC SOFTWARE (THE "SOFTWARE"). THE USE +OF THE SOFTWARE IS SUBJECT TO THE TERMS AND CONDITIONS OF THE +FOLLOWING SOFTWARE LICENSE AGREEMENT (THE "SLA"). IF YOU DO NOT ACCEPT +SUCH TERMS AND CONDITIONS YOU MAY NOT USE THE SOFTWARE. + +Subject to the terms and conditions of the SLA, the licensee of the +Software (the "Licensee") hereby, receives a non-exclusive, +non-transferable, limited, free-of-charge, perpetual and worldwide +license, to copy, use, distribute and modify the Software, but only +for the purpose of developing, manufacturing, selling, using and +distributing products including the Software in binary form, which +products are used for compression and/or decompression according to +the Khronos standard specifications OpenGL, OpenGL ES and +WebGL. Notwithstanding anything of the above, Licensee may distribute +[etcdec.cxx] in source code form provided (i) it is in unmodified +form; and (ii) it is included in software owned by Licensee. + +If Licensee institutes, or threatens to institute, patent litigation +against Ericsson or Ericsson's affiliates for using the Software for +developing, having developed, manufacturing, having manufactured, +selling, offer for sale, importing, using, leasing, operating, +repairing and/or distributing products (i) within the scope of the +Khronos framework; or (ii) using software or other intellectual +property rights owned by Ericsson or its affiliates and provided under +the Khronos framework, Ericsson shall have the right to terminate this +SLA with immediate effect. Moreover, if Licensee institutes, or +threatens to institute, patent litigation against any other licensee +of the Software for using the Software in products within the scope of +the Khronos framework, Ericsson shall have the right to terminate this +SLA with immediate effect. However, should Licensee institute, or +threaten to institute, patent litigation against any other licensee of +the Software based on such other licensee's use of any other software +together with the Software, then Ericsson shall have no right to +terminate this SLA. + +This SLA does not transfer to Licensee any ownership to any Ericsson +or third party intellectual property rights. All rights not expressly +granted by Ericsson under this SLA are hereby expressly +reserved. Furthermore, nothing in this SLA shall be construed as a +right to use or sell products in a manner which conveys or purports to +convey whether explicitly, by principles of implied license, or +otherwise, any rights to any third party, under any patent of Ericsson +or of Ericsson's affiliates covering or relating to any combination of +the Software with any other software or product (not licensed +hereunder) where the right applies specifically to the combination and +not to the software or product itself. + +THE SOFTWARE IS PROVIDED "AS IS". ERICSSON MAKES NO REPRESENTATIONS OF +ANY KIND, EXTENDS NO WARRANTIES OR CONDITIONS OF ANY KIND, EITHER +EXPRESS, IMPLIED OR STATUTORY; INCLUDING, BUT NOT LIMITED TO, EXPRESS, +IMPLIED OR STATUTORY WARRANTIES OR CONDITIONS OF TITLE, +MERCHANTABILITY, SATISFACTORY QUALITY, SUITABILITY, AND FITNESS FOR A +PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE +OF THE SOFTWARE IS WITH THE LICENSEE. SHOULD THE SOFTWARE PROVE +DEFECTIVE, THE LICENSEE ASSUMES THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. ERICSSON MAKES NO WARRANTY THAT THE MANUFACTURE, +SALE, OFFERING FOR SALE, DISTRIBUTION, LEASE, USE OR IMPORTATION UNDER +THE SLA WILL BE FREE FROM INFRINGEMENT OF PATENTS, COPYRIGHTS OR OTHER +INTELLECTUAL PROPERTY RIGHTS OF OTHERS, AND THE VALIDITY OF THE +LICENSE AND THE SLA ARE SUBJECT TO LICENSEE'S SOLE RESPONSIBILITY TO +MAKE SUCH DETERMINATION AND ACQUIRE SUCH LICENSES AS MAY BE NECESSARY +WITH RESPECT TO PATENTS, COPYRIGHT AND OTHER INTELLECTUAL PROPERTY OF +THIRD PARTIES. + +THE LICENSEE ACKNOWLEDGES AND ACCEPTS THAT THE SOFTWARE (I) IS NOT +LICENSED FOR; (II) IS NOT DESIGNED FOR OR INTENDED FOR; AND (III) MAY +NOT BE USED FOR; ANY MISSION CRITICAL APPLICATIONS SUCH AS, BUT NOT +LIMITED TO OPERATION OF NUCLEAR OR HEALTHCARE COMPUTER SYSTEMS AND/OR +NETWORKS, AIRCRAFT OR TRAIN CONTROL AND/OR COMMUNICATION SYSTEMS OR +ANY OTHER COMPUTER SYSTEMS AND/OR NETWORKS OR CONTROL AND/OR +COMMUNICATION SYSTEMS ALL IN WHICH CASE THE FAILURE OF THE SOFTWARE +COULD LEAD TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL, MATERIAL OR +ENVIRONMENTAL DAMAGE. LICENSEE'S RIGHTS UNDER THIS LICENSE WILL +TERMINATE AUTOMATICALLY AND IMMEDIATELY WITHOUT NOTICE IF LICENSEE +FAILS TO COMPLY WITH THIS PARAGRAPH. + +IN NO EVENT SHALL ERICSSON BE LIABLE FOR ANY DAMAGES WHATSOEVER, +INCLUDING BUT NOT LIMITED TO PERSONAL INJURY, ANY GENERAL, SPECIAL, +INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING +BUT NOT LIMITED TO LOSS OF PROFITS, BUSINESS INTERUPTIONS, OR ANY +OTHER COMMERCIAL DAMAGES OR LOSSES, LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY THE LICENSEE OR THIRD +PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER +SOFTWARE) REGARDLESS OF THE THEORY OF LIABILITY (CONTRACT, TORT, OR +OTHERWISE), EVEN IF THE LICENSEE OR ANY OTHER PARTY HAS BEEN ADVISED +OF THE POSSIBILITY OF SUCH DAMAGES. + +Licensee acknowledges that "ERICSSON ///" is the corporate trademark +of Telefonaktiebolaget LM Ericsson and that both "Ericsson" and the +figure "///" are important features of the trade names of +Telefonaktiebolaget LM Ericsson. Nothing contained in these terms and +conditions shall be deemed to grant Licensee any right, title or +interest in the word "Ericsson" or the figure "///". No delay or +omission by Ericsson to exercise any right or power shall impair any +such right or power to be construed to be a waiver thereof. Consent by +Ericsson to, or waiver of, a breach by the Licensee shall not +constitute consent to, waiver of, or excuse for any other different or +subsequent breach. + +This SLA shall be governed by the substantive law of Sweden. Any +dispute, controversy or claim arising out of or in connection with +this SLA, or the breach, termination or invalidity thereof, shall be +submitted to the exclusive jurisdiction of the Swedish Courts. + +*/ + +//// etcpack v2.74 +//// +//// NO WARRANTY +//// +//// BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE THE PROGRAM IS PROVIDED +//// "AS IS". ERICSSON MAKES NO REPRESENTATIONS OF ANY KIND, EXTENDS NO +//// WARRANTIES OR CONDITIONS OF ANY KIND; EITHER EXPRESS, IMPLIED OR +//// STATUTORY; INCLUDING, BUT NOT LIMITED TO, EXPRESS, IMPLIED OR +//// STATUTORY WARRANTIES OR CONDITIONS OF TITLE, MERCHANTABILITY, +//// SATISFACTORY QUALITY, SUITABILITY AND FITNESS FOR A PARTICULAR +//// PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +//// PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME +//// THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. ERICSSON +//// MAKES NO WARRANTY THAT THE MANUFACTURE, SALE, OFFERING FOR SALE, +//// DISTRIBUTION, LEASE, USE OR IMPORTATION UNDER THE LICENSE WILL BE FREE +//// FROM INFRINGEMENT OF PATENTS, COPYRIGHTS OR OTHER INTELLECTUAL +//// PROPERTY RIGHTS OF OTHERS, AND THE VALIDITY OF THE LICENSE IS SUBJECT +//// TO YOUR SOLE RESPONSIBILITY TO MAKE SUCH DETERMINATION AND ACQUIRE +//// SUCH LICENSES AS MAY BE NECESSARY WITH RESPECT TO PATENTS, COPYRIGHT +//// AND OTHER INTELLECTUAL PROPERTY OF THIRD PARTIES. +//// +//// FOR THE AVOIDANCE OF DOUBT THE PROGRAM (I) IS NOT LICENSED FOR; (II) +//// IS NOT DESIGNED FOR OR INTENDED FOR; AND (III) MAY NOT BE USED FOR; +//// ANY MISSION CRITICAL APPLICATIONS SUCH AS, BUT NOT LIMITED TO +//// OPERATION OF NUCLEAR OR HEALTHCARE COMPUTER SYSTEMS AND/OR NETWORKS, +//// AIRCRAFT OR TRAIN CONTROL AND/OR COMMUNICATION SYSTEMS OR ANY OTHER +//// COMPUTER SYSTEMS AND/OR NETWORKS OR CONTROL AND/OR COMMUNICATION +//// SYSTEMS ALL IN WHICH CASE THE FAILURE OF THE PROGRAM COULD LEAD TO +//// DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL, MATERIAL OR ENVIRONMENTAL +//// DAMAGE. YOUR RIGHTS UNDER THIS LICENSE WILL TERMINATE AUTOMATICALLY +//// AND IMMEDIATELY WITHOUT NOTICE IF YOU FAIL TO COMPLY WITH THIS +//// PARAGRAPH. +//// +//// IN NO EVENT WILL ERICSSON, BE LIABLE FOR ANY DAMAGES WHATSOEVER, +//// INCLUDING BUT NOT LIMITED TO PERSONAL INJURY, ANY GENERAL, SPECIAL, +//// INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN +//// CONNECTION WITH THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT +//// NOT LIMITED TO LOSS OF PROFITS, BUSINESS INTERUPTIONS, OR ANY OTHER +//// COMMERCIAL DAMAGES OR LOSSES, LOSS OF DATA OR DATA BEING RENDERED +//// INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF +//// THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) REGARDLESS OF THE +//// THEORY OF LIABILITY (CONTRACT, TORT OR OTHERWISE), EVEN IF SUCH HOLDER +//// OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +//// +//// (C) Ericsson AB 2013. All Rights Reserved. +//// + +#include +#include + +// Typedefs +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef short int16; + +// Macros to help with bit extraction/insertion +#define SHIFT(size,startpos) ((startpos)-(size)+1) +#define MASK(size, startpos) (((2<<(size-1))-1) << SHIFT(size,startpos)) +#define PUTBITS( dest, data, size, startpos) dest = ((dest & ~MASK(size, startpos)) | ((data << SHIFT(size, startpos)) & MASK(size,startpos))) +#define SHIFTHIGH(size, startpos) (((startpos)-32)-(size)+1) +#define MASKHIGH(size, startpos) (((1<<(size))-1) << SHIFTHIGH(size,startpos)) +#define PUTBITSHIGH(dest, data, size, startpos) dest = ((dest & ~MASKHIGH(size, startpos)) | ((data << SHIFTHIGH(size, startpos)) & MASKHIGH(size,startpos))) +#define GETBITS(source, size, startpos) (( (source) >> ((startpos)-(size)+1) ) & ((1<<(size)) -1)) +#define GETBITSHIGH(source, size, startpos) (( (source) >> (((startpos)-32)-(size)+1) ) & ((1<<(size)) -1)) +#ifndef PGMOUT +#define PGMOUT 0 +#endif +// Thumb macros and definitions +#define R_BITS59T 4 +#define G_BITS59T 4 +#define B_BITS59T 4 +#define R_BITS58H 4 +#define G_BITS58H 4 +#define B_BITS58H 4 +#define MAXIMUM_ERROR (255*255*16*1000) +#define R 0 +#define G 1 +#define B 2 +#define BLOCKHEIGHT 4 +#define BLOCKWIDTH 4 +#define BINPOW(power) (1<<(power)) +#define TABLE_BITS_59T 3 +#define TABLE_BITS_58H 3 + +// Helper Macros +#define CLAMP(ll,x,ul) (((x)<(ll)) ? (ll) : (((x)>(ul)) ? (ul) : (x))) +#define JAS_ROUND(x) (((x) < 0.0 ) ? ((int)((x)-0.5)) : ((int)((x)+0.5))) + +#define RED_CHANNEL(img,width,x,y,channels) img[channels*(y*width+x)+0] +#define GREEN_CHANNEL(img,width,x,y,channels) img[channels*(y*width+x)+1] +#define BLUE_CHANNEL(img,width,x,y,channels) img[channels*(y*width+x)+2] +#define ALPHA_CHANNEL(img,width,x,y,channels) img[channels*(y*width+x)+3] + + +// Global tables +static uint8 table59T[8] = {3,6,11,16,23,32,41,64}; // 3-bit table for the 59 bit T-mode +static uint8 table58H[8] = {3,6,11,16,23,32,41,64}; // 3-bit table for the 58 bit H-mode +static int compressParams[16][4] = {{-8, -2, 2, 8}, {-8, -2, 2, 8}, {-17, -5, 5, 17}, {-17, -5, 5, 17}, {-29, -9, 9, 29}, {-29, -9, 9, 29}, {-42, -13, 13, 42}, {-42, -13, 13, 42}, {-60, -18, 18, 60}, {-60, -18, 18, 60}, {-80, -24, 24, 80}, {-80, -24, 24, 80}, {-106, -33, 33, 106}, {-106, -33, 33, 106}, {-183, -47, 47, 183}, {-183, -47, 47, 183}}; +static int unscramble[4] = {2, 3, 1, 0}; +int alphaTableInitialized = 0; +int alphaTable[256][8]; +int alphaBase[16][4] = { + {-15,-9,-6,-3}, + {-13,-10,-7,-3}, + {-13,-8,-5,-2}, + {-13,-6,-4,-2}, + {-12,-8,-6,-3}, + {-11,-9,-7,-3}, + {-11,-8,-7,-4}, + {-11,-8,-5,-3}, + { -10,-8,-6,-2}, + { -10,-8,-5,-2}, + { -10,-8,-4,-2}, + { -10,-7,-5,-2}, + { -10,-7,-4,-3}, + { -10,-3,-2, -1}, + { -9,-8,-6,-4}, + { -9,-7,-5,-3} + }; + +// Global variables +int formatSigned = 0; + +// Enums + enum{PATTERN_H = 0, + PATTERN_T = 1}; + + +// Code used to create the valtab +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2013. All Rights Reserved. +void setupAlphaTable() +{ + if(alphaTableInitialized) + return; + alphaTableInitialized = 1; + + //read table used for alpha compression + int buf; + for(int i = 16; i<32; i++) + { + for(int j=0; j<8; j++) + { + buf=alphaBase[i-16][3-j%4]; + if(j<4) + alphaTable[i][j]=buf; + else + alphaTable[i][j]=(-buf-1); + } + } + + //beyond the first 16 values, the rest of the table is implicit.. so calculate that! + for(int i=0; i<256; i++) + { + //fill remaining slots in table with multiples of the first ones. + int mul = i/16; + int old = 16+i%16; + for(int j = 0; j<8; j++) + { + alphaTable[i][j]=alphaTable[old][j]*mul; + //note: we don't do clamping here, though we could, because we'll be clamped afterwards anyway. + } + } +} + +// Read a word in big endian style +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2013. All Rights Reserved. +void read_big_endian_2byte_word(unsigned short *blockadr, FILE *f) +{ + uint8 bytes[2]; + unsigned short block; + // This is to silence -Wunused-result from GCC 4.8+. + size_t numitems; + + numitems = fread(&bytes[0], 1, 1, f); + numitems = fread(&bytes[1], 1, 1, f); + + block = 0; + block |= bytes[0]; + block = block << 8; + block |= bytes[1]; + + blockadr[0] = block; +} + +// Read a word in big endian style +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2013. All Rights Reserved. +void read_big_endian_4byte_word(unsigned int *blockadr, FILE *f) +{ + uint8 bytes[4]; + unsigned int block; + // This is to silence -Wunused-result from GCC 4.8+. + size_t numitems; + + numitems = fread(&bytes[0], 1, 1, f); + numitems = fread(&bytes[1], 1, 1, f); + numitems = fread(&bytes[2], 1, 1, f); + numitems = fread(&bytes[3], 1, 1, f); + + block = 0; + block |= bytes[0]; + block = block << 8; + block |= bytes[1]; + block = block << 8; + block |= bytes[2]; + block = block << 8; + block |= bytes[3]; + + blockadr[0] = block; +} + +// The format stores the bits for the three extra modes in a roundabout way to be able to +// fit them without increasing the bit rate. This function converts them into something +// that is easier to work with. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2013. All Rights Reserved. +void unstuff57bits(unsigned int planar_word1, unsigned int planar_word2, unsigned int &planar57_word1, unsigned int &planar57_word2) +{ + // Get bits from twotimer configuration for 57 bits + // + // Go to this bit layout: + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // ----------------------------------------------------------------------------------------------- + // |R0 |G01G02 |B01B02 ;B03 |RH1 |RH2|GH | + // ----------------------------------------------------------------------------------------------- + // + // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // ----------------------------------------------------------------------------------------------- + // |BH |RV |GV |BV | not used | + // ----------------------------------------------------------------------------------------------- + // + // From this: + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // ------------------------------------------------------------------------------------------------ + // |//|R0 |G01|/|G02 |B01|/ // //|B02 |//|B03 |RH1 |df|RH2| + // ------------------------------------------------------------------------------------------------ + // + // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // ----------------------------------------------------------------------------------------------- + // |GH |BH |RV |GV |BV | + // ----------------------------------------------------------------------------------------------- + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| + // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + + uint8 RO, GO1, GO2, BO1, BO2, BO3, RH1, RH2, GH, BH, RV, GV, BV; + + RO = GETBITSHIGH( planar_word1, 6, 62); + GO1 = GETBITSHIGH( planar_word1, 1, 56); + GO2 = GETBITSHIGH( planar_word1, 6, 54); + BO1 = GETBITSHIGH( planar_word1, 1, 48); + BO2 = GETBITSHIGH( planar_word1, 2, 44); + BO3 = GETBITSHIGH( planar_word1, 3, 41); + RH1 = GETBITSHIGH( planar_word1, 5, 38); + RH2 = GETBITSHIGH( planar_word1, 1, 32); + GH = GETBITS( planar_word2, 7, 31); + BH = GETBITS( planar_word2, 6, 24); + RV = GETBITS( planar_word2, 6, 18); + GV = GETBITS( planar_word2, 7, 12); + BV = GETBITS( planar_word2, 6, 5); + + planar57_word1 = 0; planar57_word2 = 0; + PUTBITSHIGH( planar57_word1, RO, 6, 63); + PUTBITSHIGH( planar57_word1, GO1, 1, 57); + PUTBITSHIGH( planar57_word1, GO2, 6, 56); + PUTBITSHIGH( planar57_word1, BO1, 1, 50); + PUTBITSHIGH( planar57_word1, BO2, 2, 49); + PUTBITSHIGH( planar57_word1, BO3, 3, 47); + PUTBITSHIGH( planar57_word1, RH1, 5, 44); + PUTBITSHIGH( planar57_word1, RH2, 1, 39); + PUTBITSHIGH( planar57_word1, GH, 7, 38); + PUTBITS( planar57_word2, BH, 6, 31); + PUTBITS( planar57_word2, RV, 6, 25); + PUTBITS( planar57_word2, GV, 7, 19); + PUTBITS( planar57_word2, BV, 6, 12); +} + +// The format stores the bits for the three extra modes in a roundabout way to be able to +// fit them without increasing the bit rate. This function converts them into something +// that is easier to work with. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2013. All Rights Reserved. +void unstuff58bits(unsigned int thumbH_word1, unsigned int thumbH_word2, unsigned int &thumbH58_word1, unsigned int &thumbH58_word2) +{ + // Go to this layout: + // + // |63 62 61 60 59 58|57 56 55 54 53 52 51|50 49|48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33|32 | + // |-------empty-----|part0---------------|part1|part2------------------------------------------|part3| + // + // from this: + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------| + // |//|part0 |// // //|part1|//|part2 |df|part3| + // --------------------------------------------------------------------------------------------------| + + unsigned int part0, part1, part2, part3; + + // move parts + part0 = GETBITSHIGH( thumbH_word1, 7, 62); + part1 = GETBITSHIGH( thumbH_word1, 2, 52); + part2 = GETBITSHIGH( thumbH_word1,16, 49); + part3 = GETBITSHIGH( thumbH_word1, 1, 32); + thumbH58_word1 = 0; + PUTBITSHIGH( thumbH58_word1, part0, 7, 57); + PUTBITSHIGH( thumbH58_word1, part1, 2, 50); + PUTBITSHIGH( thumbH58_word1, part2, 16, 48); + PUTBITSHIGH( thumbH58_word1, part3, 1, 32); + + thumbH58_word2 = thumbH_word2; +} + +// The format stores the bits for the three extra modes in a roundabout way to be able to +// fit them without increasing the bit rate. This function converts them into something +// that is easier to work with. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2013. All Rights Reserved. +void unstuff59bits(unsigned int thumbT_word1, unsigned int thumbT_word2, unsigned int &thumbT59_word1, unsigned int &thumbT59_word2) +{ + // Get bits from twotimer configuration 59 bits. + // + // Go to this bit layout: + // + // |63 62 61 60 59|58 57 56 55|54 53 52 51|50 49 48 47|46 45 44 43|42 41 40 39|38 37 36 35|34 33 32| + // |----empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|--dist--| + // + // |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| + // |----------------------------------------index bits---------------------------------------------| + // + // + // From this: + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // ----------------------------------------------------------------------------------------------- + // |// // //|R0a |//|R0b |G0 |B0 |R1 |G1 |B1 |da |df|db| + // ----------------------------------------------------------------------------------------------- + // + // |31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| + // |----------------------------------------index bits---------------------------------------------| + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // ----------------------------------------------------------------------------------------------- + // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |df|fp| + // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bt|bt| + // ------------------------------------------------------------------------------------------------ + + uint8 R0a; + + // Fix middle part + thumbT59_word1 = thumbT_word1 >> 1; + // Fix db (lowest bit of d) + PUTBITSHIGH( thumbT59_word1, thumbT_word1, 1, 32); + // Fix R0a (top two bits of R0) + R0a = GETBITSHIGH( thumbT_word1, 2, 60); + PUTBITSHIGH( thumbT59_word1, R0a, 2, 58); + + // Zero top part (not needed) + PUTBITSHIGH( thumbT59_word1, 0, 5, 63); + + thumbT59_word2 = thumbT_word2; +} + +// The color bits are expanded to the full color +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2013. All Rights Reserved. +void decompressColor(int R_B, int G_B, int B_B, uint8 (colors_RGB444)[2][3], uint8 (colors)[2][3]) +{ + // The color should be retrieved as: + // + // c = round(255/(r_bits^2-1))*comp_color + // + // This is similar to bit replication + // + // Note -- this code only work for bit replication from 4 bits and up --- 3 bits needs + // two copy operations. + + colors[0][R] = (colors_RGB444[0][R] << (8 - R_B)) | (colors_RGB444[0][R] >> (R_B - (8-R_B)) ); + colors[0][G] = (colors_RGB444[0][G] << (8 - G_B)) | (colors_RGB444[0][G] >> (G_B - (8-G_B)) ); + colors[0][B] = (colors_RGB444[0][B] << (8 - B_B)) | (colors_RGB444[0][B] >> (B_B - (8-B_B)) ); + colors[1][R] = (colors_RGB444[1][R] << (8 - R_B)) | (colors_RGB444[1][R] >> (R_B - (8-R_B)) ); + colors[1][G] = (colors_RGB444[1][G] << (8 - G_B)) | (colors_RGB444[1][G] >> (G_B - (8-G_B)) ); + colors[1][B] = (colors_RGB444[1][B] << (8 - B_B)) | (colors_RGB444[1][B] >> (B_B - (8-B_B)) ); +} + +void calculatePaintColors59T(uint8 d, uint8 p, uint8 (colors)[2][3], uint8 (possible_colors)[4][3]) +{ + ////////////////////////////////////////////// + // + // C3 C1 C4----C1---C2 + // | | | + // | | | + // |-------| | + // | | | + // | | | + // C4 C2 C3 + // + ////////////////////////////////////////////// + + // C4 + possible_colors[3][R] = CLAMP(0,colors[1][R] - table59T[d],255); + possible_colors[3][G] = CLAMP(0,colors[1][G] - table59T[d],255); + possible_colors[3][B] = CLAMP(0,colors[1][B] - table59T[d],255); + + if (p == PATTERN_T) + { + // C3 + possible_colors[0][R] = colors[0][R]; + possible_colors[0][G] = colors[0][G]; + possible_colors[0][B] = colors[0][B]; + // C2 + possible_colors[1][R] = CLAMP(0,colors[1][R] + table59T[d],255); + possible_colors[1][G] = CLAMP(0,colors[1][G] + table59T[d],255); + possible_colors[1][B] = CLAMP(0,colors[1][B] + table59T[d],255); + // C1 + possible_colors[2][R] = colors[1][R]; + possible_colors[2][G] = colors[1][G]; + possible_colors[2][B] = colors[1][B]; + + } + else + { + printf("Invalid pattern. Terminating"); + exit(1); + } +} +// Decompress a T-mode block (simple packing) +// Simple 59T packing: +//|63 62 61 60 59|58 57 56 55|54 53 52 51|50 49 48 47|46 45 44 43|42 41 40 39|38 37 36 35|34 33 32| +//|----empty-----|---red 0---|--green 0--|--blue 0---|---red 1---|--green 1--|--blue 1---|--dist--| +// +//|31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00| +//|----------------------------------------index bits---------------------------------------------| +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2013. All Rights Reserved. +void decompressBlockTHUMB59Tc(unsigned int block_part1, unsigned int block_part2, uint8 *img,int width,int height,int startx,int starty, int channels) +{ + uint8 colorsRGB444[2][3]; + uint8 colors[2][3]; + uint8 paint_colors[4][3]; + uint8 distance; + uint8 block_mask[4][4]; + + // First decode left part of block. + colorsRGB444[0][R]= GETBITSHIGH(block_part1, 4, 58); + colorsRGB444[0][G]= GETBITSHIGH(block_part1, 4, 54); + colorsRGB444[0][B]= GETBITSHIGH(block_part1, 4, 50); + + colorsRGB444[1][R]= GETBITSHIGH(block_part1, 4, 46); + colorsRGB444[1][G]= GETBITSHIGH(block_part1, 4, 42); + colorsRGB444[1][B]= GETBITSHIGH(block_part1, 4, 38); + + distance = GETBITSHIGH(block_part1, TABLE_BITS_59T, 34); + + // Extend the two colors to RGB888 + decompressColor(R_BITS59T, G_BITS59T, B_BITS59T, colorsRGB444, colors); + calculatePaintColors59T(distance, PATTERN_T, colors, paint_colors); + + // Choose one of the four paint colors for each texel + for (uint8 x = 0; x < BLOCKWIDTH; ++x) + { + for (uint8 y = 0; y < BLOCKHEIGHT; ++y) + { + //block_mask[x][y] = GETBITS(block_part2,2,31-(y*4+x)*2); + block_mask[x][y] = GETBITS(block_part2,1,(y+x*4)+16)<<1; + block_mask[x][y] |= GETBITS(block_part2,1,(y+x*4)); + img[channels*((starty+y)*width+startx+x)+R] = + CLAMP(0,paint_colors[block_mask[x][y]][R],255); // RED + img[channels*((starty+y)*width+startx+x)+G] = + CLAMP(0,paint_colors[block_mask[x][y]][G],255); // GREEN + img[channels*((starty+y)*width+startx+x)+B] = + CLAMP(0,paint_colors[block_mask[x][y]][B],255); // BLUE + } + } +} + +void decompressBlockTHUMB59T(unsigned int block_part1, unsigned int block_part2, uint8 *img, int width, int height, int startx, int starty) +{ + decompressBlockTHUMB59Tc(block_part1, block_part2, img, width, height, startx, starty, 3); +} + +// Calculate the paint colors from the block colors +// using a distance d and one of the H- or T-patterns. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2013. All Rights Reserved. +void calculatePaintColors58H(uint8 d, uint8 p, uint8 (colors)[2][3], uint8 (possible_colors)[4][3]) +{ + + ////////////////////////////////////////////// + // + // C3 C1 C4----C1---C2 + // | | | + // | | | + // |-------| | + // | | | + // | | | + // C4 C2 C3 + // + ////////////////////////////////////////////// + + // C4 + possible_colors[3][R] = CLAMP(0,colors[1][R] - table58H[d],255); + possible_colors[3][G] = CLAMP(0,colors[1][G] - table58H[d],255); + possible_colors[3][B] = CLAMP(0,colors[1][B] - table58H[d],255); + + if (p == PATTERN_H) + { + // C1 + possible_colors[0][R] = CLAMP(0,colors[0][R] + table58H[d],255); + possible_colors[0][G] = CLAMP(0,colors[0][G] + table58H[d],255); + possible_colors[0][B] = CLAMP(0,colors[0][B] + table58H[d],255); + // C2 + possible_colors[1][R] = CLAMP(0,colors[0][R] - table58H[d],255); + possible_colors[1][G] = CLAMP(0,colors[0][G] - table58H[d],255); + possible_colors[1][B] = CLAMP(0,colors[0][B] - table58H[d],255); + // C3 + possible_colors[2][R] = CLAMP(0,colors[1][R] + table58H[d],255); + possible_colors[2][G] = CLAMP(0,colors[1][G] + table58H[d],255); + possible_colors[2][B] = CLAMP(0,colors[1][B] + table58H[d],255); + } + else + { + printf("Invalid pattern. Terminating"); + exit(1); + } +} + +// Decompress an H-mode block +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2013. All Rights Reserved. +void decompressBlockTHUMB58Hc(unsigned int block_part1, unsigned int block_part2, uint8 *img, int width, int height, int startx, int starty, int channels) +{ + unsigned int col0, col1; + uint8 colors[2][3]; + uint8 colorsRGB444[2][3]; + uint8 paint_colors[4][3]; + uint8 distance; + uint8 block_mask[4][4]; + + // First decode left part of block. + colorsRGB444[0][R]= GETBITSHIGH(block_part1, 4, 57); + colorsRGB444[0][G]= GETBITSHIGH(block_part1, 4, 53); + colorsRGB444[0][B]= GETBITSHIGH(block_part1, 4, 49); + + colorsRGB444[1][R]= GETBITSHIGH(block_part1, 4, 45); + colorsRGB444[1][G]= GETBITSHIGH(block_part1, 4, 41); + colorsRGB444[1][B]= GETBITSHIGH(block_part1, 4, 37); + + distance = 0; + distance = (GETBITSHIGH(block_part1, 2, 33)) << 1; + + col0 = GETBITSHIGH(block_part1, 12, 57); + col1 = GETBITSHIGH(block_part1, 12, 45); + + if(col0 >= col1) + { + distance |= 1; + } + + // Extend the two colors to RGB888 + decompressColor(R_BITS58H, G_BITS58H, B_BITS58H, colorsRGB444, colors); + + calculatePaintColors58H(distance, PATTERN_H, colors, paint_colors); + + // Choose one of the four paint colors for each texel + for (uint8 x = 0; x < BLOCKWIDTH; ++x) + { + for (uint8 y = 0; y < BLOCKHEIGHT; ++y) + { + //block_mask[x][y] = GETBITS(block_part2,2,31-(y*4+x)*2); + block_mask[x][y] = GETBITS(block_part2,1,(y+x*4)+16)<<1; + block_mask[x][y] |= GETBITS(block_part2,1,(y+x*4)); + img[channels*((starty+y)*width+startx+x)+R] = + CLAMP(0,paint_colors[block_mask[x][y]][R],255); // RED + img[channels*((starty+y)*width+startx+x)+G] = + CLAMP(0,paint_colors[block_mask[x][y]][G],255); // GREEN + img[channels*((starty+y)*width+startx+x)+B] = + CLAMP(0,paint_colors[block_mask[x][y]][B],255); // BLUE + } + } +} +void decompressBlockTHUMB58H(unsigned int block_part1, unsigned int block_part2, uint8 *img, int width, int height, int startx, int starty) +{ + decompressBlockTHUMB58Hc(block_part1, block_part2, img, width, height, startx, starty, 3); +} + +// Decompress the planar mode. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2013. All Rights Reserved. +void decompressBlockPlanar57c(unsigned int compressed57_1, unsigned int compressed57_2, uint8 *img, int width, int height, int startx, int starty, int channels) +{ + uint8 colorO[3], colorH[3], colorV[3]; + + colorO[0] = GETBITSHIGH( compressed57_1, 6, 63); + colorO[1] = GETBITSHIGH( compressed57_1, 7, 57); + colorO[2] = GETBITSHIGH( compressed57_1, 6, 50); + colorH[0] = GETBITSHIGH( compressed57_1, 6, 44); + colorH[1] = GETBITSHIGH( compressed57_1, 7, 38); + colorH[2] = GETBITS( compressed57_2, 6, 31); + colorV[0] = GETBITS( compressed57_2, 6, 25); + colorV[1] = GETBITS( compressed57_2, 7, 19); + colorV[2] = GETBITS( compressed57_2, 6, 12); + + colorO[0] = (colorO[0] << 2) | (colorO[0] >> 4); + colorO[1] = (colorO[1] << 1) | (colorO[1] >> 6); + colorO[2] = (colorO[2] << 2) | (colorO[2] >> 4); + + colorH[0] = (colorH[0] << 2) | (colorH[0] >> 4); + colorH[1] = (colorH[1] << 1) | (colorH[1] >> 6); + colorH[2] = (colorH[2] << 2) | (colorH[2] >> 4); + + colorV[0] = (colorV[0] << 2) | (colorV[0] >> 4); + colorV[1] = (colorV[1] << 1) | (colorV[1] >> 6); + colorV[2] = (colorV[2] << 2) | (colorV[2] >> 4); + + int xx, yy; + + for( xx=0; xx<4; xx++) + { + for( yy=0; yy<4; yy++) + { + img[channels*width*(starty+yy) + channels*(startx+xx) + 0] = CLAMP(0, ((xx*(colorH[0]-colorO[0]) + yy*(colorV[0]-colorO[0]) + 4*colorO[0] + 2) >> 2),255); + img[channels*width*(starty+yy) + channels*(startx+xx) + 1] = CLAMP(0, ((xx*(colorH[1]-colorO[1]) + yy*(colorV[1]-colorO[1]) + 4*colorO[1] + 2) >> 2),255); + img[channels*width*(starty+yy) + channels*(startx+xx) + 2] = CLAMP(0, ((xx*(colorH[2]-colorO[2]) + yy*(colorV[2]-colorO[2]) + 4*colorO[2] + 2) >> 2),255); + + //Equivalent method + /*img[channels*width*(starty+yy) + channels*(startx+xx) + 0] = (int)CLAMP(0, JAS_ROUND((xx*(colorH[0]-colorO[0])/4.0 + yy*(colorV[0]-colorO[0])/4.0 + colorO[0])), 255); + img[channels*width*(starty+yy) + channels*(startx+xx) + 1] = (int)CLAMP(0, JAS_ROUND((xx*(colorH[1]-colorO[1])/4.0 + yy*(colorV[1]-colorO[1])/4.0 + colorO[1])), 255); + img[channels*width*(starty+yy) + channels*(startx+xx) + 2] = (int)CLAMP(0, JAS_ROUND((xx*(colorH[2]-colorO[2])/4.0 + yy*(colorV[2]-colorO[2])/4.0 + colorO[2])), 255);*/ + + } + } +} +void decompressBlockPlanar57(unsigned int compressed57_1, unsigned int compressed57_2, uint8 *img, int width, int height, int startx, int starty) +{ + decompressBlockPlanar57c(compressed57_1, compressed57_2, img, width, height, startx, starty, 3); +} +// Decompress an ETC1 block (or ETC2 using individual or differential mode). +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2013. All Rights Reserved. +void decompressBlockDiffFlipC(unsigned int block_part1, unsigned int block_part2, uint8 *img, int width, int height, int startx, int starty, int channels) +{ + uint8 avg_color[3], enc_color1[3], enc_color2[3]; + signed char diff[3]; + int table; + int index,shift; + int r,g,b; + int diffbit; + int flipbit; + + diffbit = (GETBITSHIGH(block_part1, 1, 33)); + flipbit = (GETBITSHIGH(block_part1, 1, 32)); + + if( !diffbit ) + { + // We have diffbit = 0. + + // First decode left part of block. + avg_color[0]= GETBITSHIGH(block_part1, 4, 63); + avg_color[1]= GETBITSHIGH(block_part1, 4, 55); + avg_color[2]= GETBITSHIGH(block_part1, 4, 47); + + // Here, we should really multiply by 17 instead of 16. This can + // be done by just copying the four lower bits to the upper ones + // while keeping the lower bits. + avg_color[0] |= (avg_color[0] <<4); + avg_color[1] |= (avg_color[1] <<4); + avg_color[2] |= (avg_color[2] <<4); + + table = GETBITSHIGH(block_part1, 3, 39) << 1; + + unsigned int pixel_indices_MSB, pixel_indices_LSB; + + pixel_indices_MSB = GETBITS(block_part2, 16, 31); + pixel_indices_LSB = GETBITS(block_part2, 16, 15); + + if( (flipbit) == 0 ) + { + // We should not flip + shift = 0; + for(int x=startx; x> shift) & 1) << 1; + index |= ((pixel_indices_LSB >> shift) & 1); + shift++; + index=unscramble[index]; + + r=RED_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[0]+compressParams[table][index],255); + g=GREEN_CHANNEL(img,width,x,y,channels)=CLAMP(0,avg_color[1]+compressParams[table][index],255); + b=BLUE_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[2]+compressParams[table][index],255); + } + } + } + else + { + // We should flip + shift = 0; + for(int x=startx; x> shift) & 1) << 1; + index |= ((pixel_indices_LSB >> shift) & 1); + shift++; + index=unscramble[index]; + + r=RED_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[0]+compressParams[table][index],255); + g=GREEN_CHANNEL(img,width,x,y,channels)=CLAMP(0,avg_color[1]+compressParams[table][index],255); + b=BLUE_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[2]+compressParams[table][index],255); + } + shift+=2; + } + } + + // Now decode other part of block. + avg_color[0]= GETBITSHIGH(block_part1, 4, 59); + avg_color[1]= GETBITSHIGH(block_part1, 4, 51); + avg_color[2]= GETBITSHIGH(block_part1, 4, 43); + + // Here, we should really multiply by 17 instead of 16. This can + // be done by just copying the four lower bits to the upper ones + // while keeping the lower bits. + avg_color[0] |= (avg_color[0] <<4); + avg_color[1] |= (avg_color[1] <<4); + avg_color[2] |= (avg_color[2] <<4); + + table = GETBITSHIGH(block_part1, 3, 36) << 1; + pixel_indices_MSB = GETBITS(block_part2, 16, 31); + pixel_indices_LSB = GETBITS(block_part2, 16, 15); + + if( (flipbit) == 0 ) + { + // We should not flip + shift=8; + for(int x=startx+2; x> shift) & 1) << 1; + index |= ((pixel_indices_LSB >> shift) & 1); + shift++; + index=unscramble[index]; + + r=RED_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[0]+compressParams[table][index],255); + g=GREEN_CHANNEL(img,width,x,y,channels)=CLAMP(0,avg_color[1]+compressParams[table][index],255); + b=BLUE_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[2]+compressParams[table][index],255); + } + } + } + else + { + // We should flip + shift=2; + for(int x=startx; x> shift) & 1) << 1; + index |= ((pixel_indices_LSB >> shift) & 1); + shift++; + index=unscramble[index]; + + r=RED_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[0]+compressParams[table][index],255); + g=GREEN_CHANNEL(img,width,x,y,channels)=CLAMP(0,avg_color[1]+compressParams[table][index],255); + b=BLUE_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[2]+compressParams[table][index],255); + } + shift += 2; + } + } + } + else + { + // We have diffbit = 1. + +// 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 +// --------------------------------------------------------------------------------------------------- +// | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| +// | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | +// --------------------------------------------------------------------------------------------------- +// +// +// c) bit layout in bits 31 through 0 (in both cases) +// +// 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +// -------------------------------------------------------------------------------------------------- +// | most significant pixel index bits | least significant pixel index bits | +// | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | +// -------------------------------------------------------------------------------------------------- + + // First decode left part of block. + enc_color1[0]= GETBITSHIGH(block_part1, 5, 63); + enc_color1[1]= GETBITSHIGH(block_part1, 5, 55); + enc_color1[2]= GETBITSHIGH(block_part1, 5, 47); + + // Expand from 5 to 8 bits + avg_color[0] = (enc_color1[0] <<3) | (enc_color1[0] >> 2); + avg_color[1] = (enc_color1[1] <<3) | (enc_color1[1] >> 2); + avg_color[2] = (enc_color1[2] <<3) | (enc_color1[2] >> 2); + + table = GETBITSHIGH(block_part1, 3, 39) << 1; + + unsigned int pixel_indices_MSB, pixel_indices_LSB; + + pixel_indices_MSB = GETBITS(block_part2, 16, 31); + pixel_indices_LSB = GETBITS(block_part2, 16, 15); + + if( (flipbit) == 0 ) + { + // We should not flip + shift = 0; + for(int x=startx; x> shift) & 1) << 1; + index |= ((pixel_indices_LSB >> shift) & 1); + shift++; + index=unscramble[index]; + + r=RED_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[0]+compressParams[table][index],255); + g=GREEN_CHANNEL(img,width,x,y,channels)=CLAMP(0,avg_color[1]+compressParams[table][index],255); + b=BLUE_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[2]+compressParams[table][index],255); + } + } + } + else + { + // We should flip + shift = 0; + for(int x=startx; x> shift) & 1) << 1; + index |= ((pixel_indices_LSB >> shift) & 1); + shift++; + index=unscramble[index]; + + r=RED_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[0]+compressParams[table][index],255); + g=GREEN_CHANNEL(img,width,x,y,channels)=CLAMP(0,avg_color[1]+compressParams[table][index],255); + b=BLUE_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[2]+compressParams[table][index],255); + } + shift+=2; + } + } + + // Now decode right part of block. + diff[0]= GETBITSHIGH(block_part1, 3, 58); + diff[1]= GETBITSHIGH(block_part1, 3, 50); + diff[2]= GETBITSHIGH(block_part1, 3, 42); + + // Extend sign bit to entire byte. + diff[0] = (diff[0] << 5); + diff[1] = (diff[1] << 5); + diff[2] = (diff[2] << 5); + diff[0] = diff[0] >> 5; + diff[1] = diff[1] >> 5; + diff[2] = diff[2] >> 5; + + // Calculale second color + enc_color2[0]= enc_color1[0] + diff[0]; + enc_color2[1]= enc_color1[1] + diff[1]; + enc_color2[2]= enc_color1[2] + diff[2]; + + // Expand from 5 to 8 bits + avg_color[0] = (enc_color2[0] <<3) | (enc_color2[0] >> 2); + avg_color[1] = (enc_color2[1] <<3) | (enc_color2[1] >> 2); + avg_color[2] = (enc_color2[2] <<3) | (enc_color2[2] >> 2); + + table = GETBITSHIGH(block_part1, 3, 36) << 1; + pixel_indices_MSB = GETBITS(block_part2, 16, 31); + pixel_indices_LSB = GETBITS(block_part2, 16, 15); + + if( (flipbit) == 0 ) + { + // We should not flip + shift=8; + for(int x=startx+2; x> shift) & 1) << 1; + index |= ((pixel_indices_LSB >> shift) & 1); + shift++; + index=unscramble[index]; + + r=RED_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[0]+compressParams[table][index],255); + g=GREEN_CHANNEL(img,width,x,y,channels)=CLAMP(0,avg_color[1]+compressParams[table][index],255); + b=BLUE_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[2]+compressParams[table][index],255); + } + } + } + else + { + // We should flip + shift=2; + for(int x=startx; x> shift) & 1) << 1; + index |= ((pixel_indices_LSB >> shift) & 1); + shift++; + index=unscramble[index]; + + r=RED_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[0]+compressParams[table][index],255); + g=GREEN_CHANNEL(img,width,x,y,channels)=CLAMP(0,avg_color[1]+compressParams[table][index],255); + b=BLUE_CHANNEL(img,width,x,y,channels) =CLAMP(0,avg_color[2]+compressParams[table][index],255); + } + shift += 2; + } + } + } +} +void decompressBlockDiffFlip(unsigned int block_part1, unsigned int block_part2, uint8 *img, int width, int height, int startx, int starty) +{ + decompressBlockDiffFlipC(block_part1, block_part2, img, width, height, startx, starty, 3); +} + +// Decompress an ETC2 RGB block +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2013. All Rights Reserved. +void decompressBlockETC2c(unsigned int block_part1, unsigned int block_part2, uint8 *img, int width, int height, int startx, int starty, int channels) +{ + int diffbit; + signed char color1[3]; + signed char diff[3]; + signed char red, green, blue; + + diffbit = (GETBITSHIGH(block_part1, 1, 33)); + + if( diffbit ) + { + // We have diffbit = 1; + + // Base color + color1[0]= GETBITSHIGH(block_part1, 5, 63); + color1[1]= GETBITSHIGH(block_part1, 5, 55); + color1[2]= GETBITSHIGH(block_part1, 5, 47); + + // Diff color + diff[0]= GETBITSHIGH(block_part1, 3, 58); + diff[1]= GETBITSHIGH(block_part1, 3, 50); + diff[2]= GETBITSHIGH(block_part1, 3, 42); + + // Extend sign bit to entire byte. + diff[0] = (diff[0] << 5); + diff[1] = (diff[1] << 5); + diff[2] = (diff[2] << 5); + diff[0] = diff[0] >> 5; + diff[1] = diff[1] >> 5; + diff[2] = diff[2] >> 5; + + red = color1[0] + diff[0]; + green = color1[1] + diff[1]; + blue = color1[2] + diff[2]; + + if(red < 0 || red > 31) + { + unsigned int block59_part1, block59_part2; + unstuff59bits(block_part1, block_part2, block59_part1, block59_part2); + decompressBlockTHUMB59Tc(block59_part1, block59_part2, img, width, height, startx, starty, channels); + } + else if (green < 0 || green > 31) + { + unsigned int block58_part1, block58_part2; + unstuff58bits(block_part1, block_part2, block58_part1, block58_part2); + decompressBlockTHUMB58Hc(block58_part1, block58_part2, img, width, height, startx, starty, channels); + } + else if(blue < 0 || blue > 31) + { + unsigned int block57_part1, block57_part2; + + unstuff57bits(block_part1, block_part2, block57_part1, block57_part2); + decompressBlockPlanar57c(block57_part1, block57_part2, img, width, height, startx, starty, channels); + } + else + { + decompressBlockDiffFlipC(block_part1, block_part2, img, width, height, startx, starty, channels); + } + } + else + { + // We have diffbit = 0; + decompressBlockDiffFlipC(block_part1, block_part2, img, width, height, startx, starty, channels); + } +} +void decompressBlockETC2(unsigned int block_part1, unsigned int block_part2, uint8 *img, int width, int height, int startx, int starty) +{ + decompressBlockETC2c(block_part1, block_part2, img, width, height, startx, starty, 3); +} +// Decompress an ETC2 block with punchthrough alpha +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2013. All Rights Reserved. +void decompressBlockDifferentialWithAlphaC(unsigned int block_part1, unsigned int block_part2, uint8* img, uint8* alpha, int width, int height, int startx, int starty, int channelsRGB) +{ + + uint8 avg_color[3], enc_color1[3], enc_color2[3]; + signed char diff[3]; + int table; + int index,shift; + int r,g,b; + int diffbit; + int flipbit; + int channelsA; + + if(channelsRGB == 3) + { + // We will decode the alpha data to a separate memory area. + channelsA = 1; + } + else + { + // We will decode the RGB data and the alpha data to the same memory area, + // interleaved as RGBA. + channelsA = 4; + alpha = &img[0+3]; + } + + //the diffbit now encodes whether or not the entire alpha channel is 255. + diffbit = (GETBITSHIGH(block_part1, 1, 33)); + flipbit = (GETBITSHIGH(block_part1, 1, 32)); + + // First decode left part of block. + enc_color1[0]= GETBITSHIGH(block_part1, 5, 63); + enc_color1[1]= GETBITSHIGH(block_part1, 5, 55); + enc_color1[2]= GETBITSHIGH(block_part1, 5, 47); + + // Expand from 5 to 8 bits + avg_color[0] = (enc_color1[0] <<3) | (enc_color1[0] >> 2); + avg_color[1] = (enc_color1[1] <<3) | (enc_color1[1] >> 2); + avg_color[2] = (enc_color1[2] <<3) | (enc_color1[2] >> 2); + + table = GETBITSHIGH(block_part1, 3, 39) << 1; + + unsigned int pixel_indices_MSB, pixel_indices_LSB; + + pixel_indices_MSB = GETBITS(block_part2, 16, 31); + pixel_indices_LSB = GETBITS(block_part2, 16, 15); + + if( (flipbit) == 0 ) + { + // We should not flip + shift = 0; + for(int x=startx; x> shift) & 1) << 1; + index |= ((pixel_indices_LSB >> shift) & 1); + shift++; + index=unscramble[index]; + + int mod = compressParams[table][index]; + if(diffbit==0&&(index==1||index==2)) + { + mod=0; + } + + r=RED_CHANNEL(img,width,x,y,channelsRGB) =CLAMP(0,avg_color[0]+mod,255); + g=GREEN_CHANNEL(img,width,x,y,channelsRGB)=CLAMP(0,avg_color[1]+mod,255); + b=BLUE_CHANNEL(img,width,x,y,channelsRGB) =CLAMP(0,avg_color[2]+mod,255); + if(diffbit==0&&index==1) + { + alpha[(y*width+x)*channelsA]=0; + r=RED_CHANNEL(img,width,x,y,channelsRGB)=0; + g=GREEN_CHANNEL(img,width,x,y,channelsRGB)=0; + b=BLUE_CHANNEL(img,width,x,y,channelsRGB)=0; + } + else + { + alpha[(y*width+x)*channelsA]=255; + } + + } + } + } + else + { + // We should flip + shift = 0; + for(int x=startx; x> shift) & 1) << 1; + index |= ((pixel_indices_LSB >> shift) & 1); + shift++; + index=unscramble[index]; + int mod = compressParams[table][index]; + if(diffbit==0&&(index==1||index==2)) + { + mod=0; + } + r=RED_CHANNEL(img,width,x,y,channelsRGB) =CLAMP(0,avg_color[0]+mod,255); + g=GREEN_CHANNEL(img,width,x,y,channelsRGB)=CLAMP(0,avg_color[1]+mod,255); + b=BLUE_CHANNEL(img,width,x,y,channelsRGB) =CLAMP(0,avg_color[2]+mod,255); + if(diffbit==0&&index==1) + { + alpha[(y*width+x)*channelsA]=0; + r=RED_CHANNEL(img,width,x,y,channelsRGB)=0; + g=GREEN_CHANNEL(img,width,x,y,channelsRGB)=0; + b=BLUE_CHANNEL(img,width,x,y,channelsRGB)=0; + } + else + { + alpha[(y*width+x)*channelsA]=255; + } + } + shift+=2; + } + } + // Now decode right part of block. + diff[0]= GETBITSHIGH(block_part1, 3, 58); + diff[1]= GETBITSHIGH(block_part1, 3, 50); + diff[2]= GETBITSHIGH(block_part1, 3, 42); + + // Extend sign bit to entire byte. + diff[0] = (diff[0] << 5); + diff[1] = (diff[1] << 5); + diff[2] = (diff[2] << 5); + diff[0] = diff[0] >> 5; + diff[1] = diff[1] >> 5; + diff[2] = diff[2] >> 5; + + // Calculate second color + enc_color2[0]= enc_color1[0] + diff[0]; + enc_color2[1]= enc_color1[1] + diff[1]; + enc_color2[2]= enc_color1[2] + diff[2]; + + // Expand from 5 to 8 bits + avg_color[0] = (enc_color2[0] <<3) | (enc_color2[0] >> 2); + avg_color[1] = (enc_color2[1] <<3) | (enc_color2[1] >> 2); + avg_color[2] = (enc_color2[2] <<3) | (enc_color2[2] >> 2); + + table = GETBITSHIGH(block_part1, 3, 36) << 1; + pixel_indices_MSB = GETBITS(block_part2, 16, 31); + pixel_indices_LSB = GETBITS(block_part2, 16, 15); + + if( (flipbit) == 0 ) + { + // We should not flip + shift=8; + for(int x=startx+2; x> shift) & 1) << 1; + index |= ((pixel_indices_LSB >> shift) & 1); + shift++; + index=unscramble[index]; + int mod = compressParams[table][index]; + if(diffbit==0&&(index==1||index==2)) + { + mod=0; + } + + r=RED_CHANNEL(img,width,x,y,channelsRGB) =CLAMP(0,avg_color[0]+mod,255); + g=GREEN_CHANNEL(img,width,x,y,channelsRGB)=CLAMP(0,avg_color[1]+mod,255); + b=BLUE_CHANNEL(img,width,x,y,channelsRGB) =CLAMP(0,avg_color[2]+mod,255); + if(diffbit==0&&index==1) + { + alpha[(y*width+x)*channelsA]=0; + r=RED_CHANNEL(img,width,x,y,channelsRGB)=0; + g=GREEN_CHANNEL(img,width,x,y,channelsRGB)=0; + b=BLUE_CHANNEL(img,width,x,y,channelsRGB)=0; + } + else + { + alpha[(y*width+x)*channelsA]=255; + } + } + } + } + else + { + // We should flip + shift=2; + for(int x=startx; x> shift) & 1) << 1; + index |= ((pixel_indices_LSB >> shift) & 1); + shift++; + index=unscramble[index]; + int mod = compressParams[table][index]; + if(diffbit==0&&(index==1||index==2)) + { + mod=0; + } + + r=RED_CHANNEL(img,width,x,y,channelsRGB) =CLAMP(0,avg_color[0]+mod,255); + g=GREEN_CHANNEL(img,width,x,y,channelsRGB)=CLAMP(0,avg_color[1]+mod,255); + b=BLUE_CHANNEL(img,width,x,y,channelsRGB) =CLAMP(0,avg_color[2]+mod,255); + if(diffbit==0&&index==1) + { + alpha[(y*width+x)*channelsA]=0; + r=RED_CHANNEL(img,width,x,y,channelsRGB)=0; + g=GREEN_CHANNEL(img,width,x,y,channelsRGB)=0; + b=BLUE_CHANNEL(img,width,x,y,channelsRGB)=0; + } + else + { + alpha[(y*width+x)*channelsA]=255; + } + } + shift += 2; + } + } +} +void decompressBlockDifferentialWithAlpha(unsigned int block_part1, unsigned int block_part2, uint8* img, uint8* alpha, int width, int height, int startx, int starty) +{ + decompressBlockDifferentialWithAlphaC(block_part1, block_part2, img, alpha, width, height, startx, starty, 3); +} + + +// similar to regular decompression, but alpha channel is set to 0 if pixel index is 2, otherwise 255. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2013. All Rights Reserved. +void decompressBlockTHUMB59TAlphaC(unsigned int block_part1, unsigned int block_part2, uint8 *img, uint8* alpha, int width, int height, int startx, int starty, int channelsRGB) +{ + + uint8 colorsRGB444[2][3]; + uint8 colors[2][3]; + uint8 paint_colors[4][3]; + uint8 distance; + uint8 block_mask[4][4]; + int channelsA; + + if(channelsRGB == 3) + { + // We will decode the alpha data to a separate memory area. + channelsA = 1; + } + else + { + // We will decode the RGB data and the alpha data to the same memory area, + // interleaved as RGBA. + channelsA = 4; + alpha = &img[0+3]; + } + + // First decode left part of block. + colorsRGB444[0][R]= GETBITSHIGH(block_part1, 4, 58); + colorsRGB444[0][G]= GETBITSHIGH(block_part1, 4, 54); + colorsRGB444[0][B]= GETBITSHIGH(block_part1, 4, 50); + + colorsRGB444[1][R]= GETBITSHIGH(block_part1, 4, 46); + colorsRGB444[1][G]= GETBITSHIGH(block_part1, 4, 42); + colorsRGB444[1][B]= GETBITSHIGH(block_part1, 4, 38); + + distance = GETBITSHIGH(block_part1, TABLE_BITS_59T, 34); + + // Extend the two colors to RGB888 + decompressColor(R_BITS59T, G_BITS59T, B_BITS59T, colorsRGB444, colors); + calculatePaintColors59T(distance, PATTERN_T, colors, paint_colors); + + // Choose one of the four paint colors for each texel + for (uint8 x = 0; x < BLOCKWIDTH; ++x) + { + for (uint8 y = 0; y < BLOCKHEIGHT; ++y) + { + //block_mask[x][y] = GETBITS(block_part2,2,31-(y*4+x)*2); + block_mask[x][y] = GETBITS(block_part2,1,(y+x*4)+16)<<1; + block_mask[x][y] |= GETBITS(block_part2,1,(y+x*4)); + img[channelsRGB*((starty+y)*width+startx+x)+R] = + CLAMP(0,paint_colors[block_mask[x][y]][R],255); // RED + img[channelsRGB*((starty+y)*width+startx+x)+G] = + CLAMP(0,paint_colors[block_mask[x][y]][G],255); // GREEN + img[channelsRGB*((starty+y)*width+startx+x)+B] = + CLAMP(0,paint_colors[block_mask[x][y]][B],255); // BLUE + if(block_mask[x][y]==2) + { + alpha[channelsA*(x+startx+(y+starty)*width)]=0; + img[channelsRGB*((starty+y)*width+startx+x)+R] =0; + img[channelsRGB*((starty+y)*width+startx+x)+G] =0; + img[channelsRGB*((starty+y)*width+startx+x)+B] =0; + } + else + alpha[channelsA*(x+startx+(y+starty)*width)]=255; + } + } +} +void decompressBlockTHUMB59TAlpha(unsigned int block_part1, unsigned int block_part2, uint8 *img, uint8* alpha, int width, int height, int startx, int starty) +{ + decompressBlockTHUMB59TAlphaC(block_part1, block_part2, img, alpha, width, height, startx, starty, 3); +} + + +// Decompress an H-mode block with alpha +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2013. All Rights Reserved. +void decompressBlockTHUMB58HAlphaC(unsigned int block_part1, unsigned int block_part2, uint8 *img, uint8* alpha, int width, int height, int startx, int starty, int channelsRGB) +{ + unsigned int col0, col1; + uint8 colors[2][3]; + uint8 colorsRGB444[2][3]; + uint8 paint_colors[4][3]; + uint8 distance; + uint8 block_mask[4][4]; + int channelsA; + + if(channelsRGB == 3) + { + // We will decode the alpha data to a separate memory area. + channelsA = 1; + } + else + { + // We will decode the RGB data and the alpha data to the same memory area, + // interleaved as RGBA. + channelsA = 4; + alpha = &img[0+3]; + } + + // First decode left part of block. + colorsRGB444[0][R]= GETBITSHIGH(block_part1, 4, 57); + colorsRGB444[0][G]= GETBITSHIGH(block_part1, 4, 53); + colorsRGB444[0][B]= GETBITSHIGH(block_part1, 4, 49); + + colorsRGB444[1][R]= GETBITSHIGH(block_part1, 4, 45); + colorsRGB444[1][G]= GETBITSHIGH(block_part1, 4, 41); + colorsRGB444[1][B]= GETBITSHIGH(block_part1, 4, 37); + + distance = 0; + distance = (GETBITSHIGH(block_part1, 2, 33)) << 1; + + col0 = GETBITSHIGH(block_part1, 12, 57); + col1 = GETBITSHIGH(block_part1, 12, 45); + + if(col0 >= col1) + { + distance |= 1; + } + + // Extend the two colors to RGB888 + decompressColor(R_BITS58H, G_BITS58H, B_BITS58H, colorsRGB444, colors); + + calculatePaintColors58H(distance, PATTERN_H, colors, paint_colors); + + // Choose one of the four paint colors for each texel + for (uint8 x = 0; x < BLOCKWIDTH; ++x) + { + for (uint8 y = 0; y < BLOCKHEIGHT; ++y) + { + //block_mask[x][y] = GETBITS(block_part2,2,31-(y*4+x)*2); + block_mask[x][y] = GETBITS(block_part2,1,(y+x*4)+16)<<1; + block_mask[x][y] |= GETBITS(block_part2,1,(y+x*4)); + img[channelsRGB*((starty+y)*width+startx+x)+R] = + CLAMP(0,paint_colors[block_mask[x][y]][R],255); // RED + img[channelsRGB*((starty+y)*width+startx+x)+G] = + CLAMP(0,paint_colors[block_mask[x][y]][G],255); // GREEN + img[channelsRGB*((starty+y)*width+startx+x)+B] = + CLAMP(0,paint_colors[block_mask[x][y]][B],255); // BLUE + + if(block_mask[x][y]==2) + { + alpha[channelsA*(x+startx+(y+starty)*width)]=0; + img[channelsRGB*((starty+y)*width+startx+x)+R] =0; + img[channelsRGB*((starty+y)*width+startx+x)+G] =0; + img[channelsRGB*((starty+y)*width+startx+x)+B] =0; + } + else + alpha[channelsA*(x+startx+(y+starty)*width)]=255; + } + } +} +void decompressBlockTHUMB58HAlpha(unsigned int block_part1, unsigned int block_part2, uint8 *img, uint8* alpha, int width, int height, int startx, int starty) +{ + decompressBlockTHUMB58HAlphaC(block_part1, block_part2, img, alpha, width, height, startx, starty, 3); +} +// Decompression function for ETC2_RGBA1 format. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2013. All Rights Reserved. +void decompressBlockETC21BitAlphaC(unsigned int block_part1, unsigned int block_part2, uint8 *img, uint8* alphaimg, int width, int height, int startx, int starty, int channelsRGB) +{ + int diffbit; + signed char color1[3]; + signed char diff[3]; + signed char red, green, blue; + int channelsA; + + if(channelsRGB == 3) + { + // We will decode the alpha data to a separate memory area. + channelsA = 1; + } + else + { + // We will decode the RGB data and the alpha data to the same memory area, + // interleaved as RGBA. + channelsA = 4; + alphaimg = &img[0+3]; + } + + diffbit = (GETBITSHIGH(block_part1, 1, 33)); + + if( diffbit ) + { + // We have diffbit = 1, meaning no transparent pixels. regular decompression. + + // Base color + color1[0]= GETBITSHIGH(block_part1, 5, 63); + color1[1]= GETBITSHIGH(block_part1, 5, 55); + color1[2]= GETBITSHIGH(block_part1, 5, 47); + + // Diff color + diff[0]= GETBITSHIGH(block_part1, 3, 58); + diff[1]= GETBITSHIGH(block_part1, 3, 50); + diff[2]= GETBITSHIGH(block_part1, 3, 42); + + // Extend sign bit to entire byte. + diff[0] = (diff[0] << 5); + diff[1] = (diff[1] << 5); + diff[2] = (diff[2] << 5); + diff[0] = diff[0] >> 5; + diff[1] = diff[1] >> 5; + diff[2] = diff[2] >> 5; + + red = color1[0] + diff[0]; + green = color1[1] + diff[1]; + blue = color1[2] + diff[2]; + + if(red < 0 || red > 31) + { + unsigned int block59_part1, block59_part2; + unstuff59bits(block_part1, block_part2, block59_part1, block59_part2); + decompressBlockTHUMB59Tc(block59_part1, block59_part2, img, width, height, startx, starty, channelsRGB); + } + else if (green < 0 || green > 31) + { + unsigned int block58_part1, block58_part2; + unstuff58bits(block_part1, block_part2, block58_part1, block58_part2); + decompressBlockTHUMB58Hc(block58_part1, block58_part2, img, width, height, startx, starty, channelsRGB); + } + else if(blue < 0 || blue > 31) + { + unsigned int block57_part1, block57_part2; + + unstuff57bits(block_part1, block_part2, block57_part1, block57_part2); + decompressBlockPlanar57c(block57_part1, block57_part2, img, width, height, startx, starty, channelsRGB); + } + else + { + decompressBlockDifferentialWithAlphaC(block_part1, block_part2, img, alphaimg, width, height, startx, starty, channelsRGB); + } + for(int x=startx; x> 5; + diff[1] = diff[1] >> 5; + diff[2] = diff[2] >> 5; + + red = color1[0] + diff[0]; + green = color1[1] + diff[1]; + blue = color1[2] + diff[2]; + if(red < 0 || red > 31) + { + unsigned int block59_part1, block59_part2; + unstuff59bits(block_part1, block_part2, block59_part1, block59_part2); + decompressBlockTHUMB59TAlphaC(block59_part1, block59_part2, img, alphaimg, width, height, startx, starty, channelsRGB); + } + else if(green < 0 || green > 31) + { + unsigned int block58_part1, block58_part2; + unstuff58bits(block_part1, block_part2, block58_part1, block58_part2); + decompressBlockTHUMB58HAlphaC(block58_part1, block58_part2, img, alphaimg, width, height, startx, starty, channelsRGB); + } + else if(blue < 0 || blue > 31) + { + unsigned int block57_part1, block57_part2; + + unstuff57bits(block_part1, block_part2, block57_part1, block57_part2); + decompressBlockPlanar57c(block57_part1, block57_part2, img, width, height, startx, starty, channelsRGB); + for(int x=startx; xtopos) + return ((1<>(frompos-topos); + return ((1<255) + val=255; + return val; +} + +// Decodes tha alpha component in a block coded with GL_COMPRESSED_RGBA8_ETC2_EAC. +// Note that this decoding is slightly different from that of GL_COMPRESSED_R11_EAC. +// However, a hardware decoder can share gates between the two formats as explained +// in the specification under GL_COMPRESSED_R11_EAC. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2013. All Rights Reserved. +void decompressBlockAlphaC(uint8* data, uint8* img, int width, int height, int ix, int iy, int channels) +{ + int alpha = data[0]; + int table = data[1]; + + int bit=0; + int byte=2; + //extract an alpha value for each pixel. + for(int x=0; x<4; x++) + { + for(int y=0; y<4; y++) + { + //Extract table index + int index=0; + for(int bitpos=0; bitpos<3; bitpos++) + { + index|=getbit(data[byte],7-bit,2-bitpos); + bit++; + if(bit>7) + { + bit=0; + byte++; + } + } + img[(ix+x+(iy+y)*width)*channels]=clamp(alpha +alphaTable[table][index]); + } + } +} +void decompressBlockAlpha(uint8* data, uint8* img, int width, int height, int ix, int iy) +{ + decompressBlockAlphaC(data, img, width, height, ix, iy, 1); +} + +// Does decompression and then immediately converts from 11 bit signed to a 16-bit format. +// +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2013. All Rights Reserved. +int16 get16bits11signed(int base, int table, int mul, int index) +{ + int elevenbase = base-128; + if(elevenbase==-128) + elevenbase=-127; + elevenbase*=8; + //i want the positive value here + int tabVal = -alphaBase[table][3-index%4]-1; + //and the sign, please + int sign = 1-(index/4); + + if(sign) + tabVal=tabVal+1; + int elevenTabVal = tabVal*8; + + if(mul!=0) + elevenTabVal*=mul; + else + elevenTabVal/=8; + + if(sign) + elevenTabVal=-elevenTabVal; + + //calculate sum + int elevenbits = elevenbase+elevenTabVal; + + //clamp.. + if(elevenbits>=1024) + elevenbits=1023; + else if(elevenbits<-1023) + elevenbits=-1023; + //this is the value we would actually output.. + //but there aren't any good 11-bit file or uncompressed GL formats + //so we extend to 15 bits signed. + sign = elevenbits<0; + elevenbits=abs(elevenbits); + int16 fifteenbits = (elevenbits<<5)+(elevenbits>>5); + int16 sixteenbits=fifteenbits; + + if(sign) + sixteenbits=-sixteenbits; + + return sixteenbits; +} + +// Does decompression and then immediately converts from 11 bit signed to a 16-bit format +// Calculates the 11 bit value represented by base, table, mul and index, and extends it to 16 bits. +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2013. All Rights Reserved. +uint16 get16bits11bits(int base, int table, int mul, int index) +{ + int elevenbase = base*8+4; + + //i want the positive value here + int tabVal = -alphaBase[table][3-index%4]-1; + //and the sign, please + int sign = 1-(index/4); + + if(sign) + tabVal=tabVal+1; + int elevenTabVal = tabVal*8; + + if(mul!=0) + elevenTabVal*=mul; + else + elevenTabVal/=8; + + if(sign) + elevenTabVal=-elevenTabVal; + + //calculate sum + int elevenbits = elevenbase+elevenTabVal; + + //clamp.. + if(elevenbits>=256*8) + elevenbits=256*8-1; + else if(elevenbits<0) + elevenbits=0; + //elevenbits now contains the 11 bit alpha value as defined in the spec. + + //extend to 16 bits before returning, since we don't have any good 11-bit file formats. + uint16 sixteenbits = (elevenbits<<5)+(elevenbits>>6); + + return sixteenbits; +} + +// Decompresses a block using one of the GL_COMPRESSED_R11_EAC or GL_COMPRESSED_SIGNED_R11_EAC-formats +// NO WARRANTY --- SEE STATEMENT IN TOP OF FILE (C) Ericsson AB 2013. All Rights Reserved. +void decompressBlockAlpha16bitC(uint8* data, uint8* img, int width, int height, int ix, int iy, int channels) +{ + int alpha = data[0]; + int table = data[1]; + + if(formatSigned) + { + //if we have a signed format, the base value is given as a signed byte. We convert it to (0-255) here, + //so more code can be shared with the unsigned mode. + alpha = *((signed char*)(&data[0])); + alpha = alpha+128; + } + + int bit=0; + int byte=2; + //extract an alpha value for each pixel. + for(int x=0; x<4; x++) + { + for(int y=0; y<4; y++) + { + //Extract table index + int index=0; + for(int bitpos=0; bitpos<3; bitpos++) + { + index|=getbit(data[byte],7-bit,2-bitpos); + bit++; + if(bit>7) + { + bit=0; + byte++; + } + } + int windex = channels*(2*(ix+x+(iy+y)*width)); +#if !PGMOUT + if(formatSigned) + { + *(int16 *)&img[windex] = get16bits11signed(alpha,(table%16),(table/16),index); + } + else + { + *(uint16 *)&img[windex] = get16bits11bits(alpha,(table%16),(table/16),index); + } +#else + //make data compatible with the .pgm format. See the comment in compressBlockAlpha16() for details. + uint16 uSixteen; + if (formatSigned) + { + //the pgm-format only allows unsigned images, + //so we add 2^15 to get a 16-bit value. + uSixteen = get16bits11signed(alpha,(table%16),(table/16),index) + 256*128; + } + else + { + uSixteen = get16bits11bits(alpha,(table%16),(table/16),index); + } + //byte swap for pgm + img[windex] = uSixteen/256; + img[windex+1] = uSixteen%256; +#endif + + } + } +} + +void decompressBlockAlpha16bit(uint8* data, uint8* img, int width, int height, int ix, int iy) +{ + decompressBlockAlpha16bitC(data, img, width, height, ix, iy, 1); +} diff --git a/applications/_plugins/cimage/ktx/lib/etcunpack.cxx b/applications/_plugins/cimage/ktx/lib/etcunpack.cxx new file mode 100644 index 000000000..66c41bb24 --- /dev/null +++ b/applications/_plugins/cimage/ktx/lib/etcunpack.cxx @@ -0,0 +1,296 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4: */ + +/* $Id: d555f6fdc46c28e4db334b44fdb8ac5b4bcef91a $ */ + +/* @internal + * @~English + * @file + * + * Unpack a texture compressed with ETC1 + * + * @author Mark Callow, HI Corporation. + */ + +/* +Copyright (c) 2010 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +unaltered in all copies or substantial portions of the Materials. +Any additions, deletions, or changes to the original source files +must be clearly indicated in accompanying documentation. + +If only executable code is distributed, then the accompanying +documentation must state that "this software is based in part on the +work of the Khronos Group." + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +#include +#include +#include "ktx.h" +#include "ktxint.h" + +#if SUPPORT_SOFTWARE_ETC_UNPACK +typedef unsigned int uint; +typedef unsigned char uint8; + +extern void decompressBlockETC2c(uint block_part1, uint block_part2, uint8* img, + int width, int height, int startx, int starty, int channels); +extern void decompressBlockETC21BitAlphaC(uint block_part1, uint block_part2, uint8* img, uint8* alphaimg, + int width, int height, int startx, int starty, int channels); +extern void decompressBlockAlphaC(uint8* data, uint8* img, + int width, int height, int startx, int starty, int channels); +extern void decompressBlockAlpha16bitC(uint8* data, uint8* img, + int width, int height, int startx, int starty, int channels); + +extern void setupAlphaTable(); + +// This global variable affects the behaviour of decompressBlockAlpha16bitC. +extern int formatSigned; + +static void +readBigEndian4byteWord(khronos_uint32_t* pBlock, const GLubyte *s) +{ + *pBlock = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]; +} + + +/* Unpack an ETC1_RGB8_OES format compressed texture */ +extern "C" KTX_error_code +_ktxUnpackETC(const GLubyte* srcETC, const GLenum srcFormat, + khronos_uint32_t activeWidth, khronos_uint32_t activeHeight, + GLubyte** dstImage, + GLenum* format, GLenum* internalFormat, GLenum* type, + GLint R16Formats, GLboolean supportsSRGB) +{ + unsigned int width, height; + unsigned int block_part1, block_part2; + unsigned int x, y; + /*const*/ GLubyte* src = (GLubyte*)srcETC; + // AF_11BIT is used to compress R11 & RG11 though its not alpha data. + enum {AF_NONE, AF_1BIT, AF_8BIT, AF_11BIT} alphaFormat = AF_NONE; + int dstChannels, dstChannelBytes; + + switch (srcFormat) { + case GL_COMPRESSED_SIGNED_R11_EAC: + if (R16Formats & _KTX_R16_FORMATS_SNORM) { + dstChannelBytes = sizeof(GLshort); + dstChannels = 1; + formatSigned = GL_TRUE; + *internalFormat = GL_R16_SNORM; + *format = GL_RED; + *type = GL_SHORT; + alphaFormat = AF_11BIT; + } else + return KTX_UNSUPPORTED_TEXTURE_TYPE; + break; + + case GL_COMPRESSED_R11_EAC: + if (R16Formats & _KTX_R16_FORMATS_NORM) { + dstChannelBytes = sizeof(GLshort); + dstChannels = 1; + formatSigned = GL_FALSE; + *internalFormat = GL_R16; + *format = GL_RED; + *type = GL_UNSIGNED_SHORT; + alphaFormat = AF_11BIT; + } else + return KTX_UNSUPPORTED_TEXTURE_TYPE; + break; + + case GL_COMPRESSED_SIGNED_RG11_EAC: + if (R16Formats & _KTX_R16_FORMATS_SNORM) { + dstChannelBytes = sizeof(GLshort); + dstChannels = 2; + formatSigned = GL_TRUE; + *internalFormat = GL_RG16_SNORM; + *format = GL_RG; + *type = GL_SHORT; + alphaFormat = AF_11BIT; + } else + return KTX_UNSUPPORTED_TEXTURE_TYPE; + break; + + case GL_COMPRESSED_RG11_EAC: + if (R16Formats & _KTX_R16_FORMATS_NORM) { + dstChannelBytes = sizeof(GLshort); + dstChannels = 2; + formatSigned = GL_FALSE; + *internalFormat = GL_RG16; + *format = GL_RG; + *type = GL_UNSIGNED_SHORT; + alphaFormat = AF_11BIT; + } else + return KTX_UNSUPPORTED_TEXTURE_TYPE; + break; + + case GL_ETC1_RGB8_OES: + case GL_COMPRESSED_RGB8_ETC2: + dstChannelBytes = sizeof(GLubyte); + dstChannels = 3; + *internalFormat = GL_RGB8; + *format = GL_RGB; + *type = GL_UNSIGNED_BYTE; + break; + + case GL_COMPRESSED_RGBA8_ETC2_EAC: + dstChannelBytes = sizeof(GLubyte); + dstChannels = 4; + *internalFormat = GL_RGBA8; + *format = GL_RGBA; + *type = GL_UNSIGNED_BYTE; + alphaFormat = AF_8BIT; + break; + + case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: + dstChannelBytes = sizeof(GLubyte); + dstChannels = 4; + *internalFormat = GL_RGBA8; + *format = GL_RGBA; + *type = GL_UNSIGNED_BYTE; + alphaFormat = AF_1BIT; + break; + + case GL_COMPRESSED_SRGB8_ETC2: + if (supportsSRGB) { + dstChannelBytes = sizeof(GLubyte); + dstChannels = 3; + *internalFormat = GL_SRGB8; + *format = GL_RGB; + *type = GL_UNSIGNED_BYTE; + } else + return KTX_UNSUPPORTED_TEXTURE_TYPE; + break; + + case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: + if (supportsSRGB) { + dstChannelBytes = sizeof(GLubyte); + dstChannels = 4; + *internalFormat = GL_SRGB8_ALPHA8; + *format = GL_RGBA; + *type = GL_UNSIGNED_BYTE; + alphaFormat = AF_8BIT; + } else + return KTX_UNSUPPORTED_TEXTURE_TYPE; + break; + + case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: + if (supportsSRGB) { + dstChannelBytes = sizeof(GLubyte); + dstChannels = 4; + *internalFormat = GL_SRGB8_ALPHA8; + *format = GL_RGBA; + *type = GL_UNSIGNED_BYTE; + alphaFormat = AF_1BIT; + } else + return KTX_UNSUPPORTED_TEXTURE_TYPE; + break; + + default: + assert(0); // Upper levels should be passing only one of the above srcFormats. + } + + /* active_{width,height} show how many pixels contain active data, + * (the rest are just for making sure we have a 2*a x 4*b size). + */ + + /* Compute the full width & height. */ + width = ((activeWidth+3)/4)*4; + height = ((activeHeight+3)/4)*4; + + /* printf("Width = %d, Height = %d\n", width, height); */ + /* printf("active pixel area: top left %d x %d area.\n", activeWidth, activeHeight); */ + + *dstImage = (GLubyte*)malloc(dstChannels*dstChannelBytes*width*height); + if (!*dstImage) { + return KTX_OUT_OF_MEMORY; + } + + if (alphaFormat != AF_NONE) + setupAlphaTable(); + + // NOTE: none of the decompress functions actually use the parameter + if (alphaFormat == AF_11BIT) { + // One or two 11-bit alpha channels for R or RG. + for (y=0; y < height/4; y++) { + for (x=0; x < width/4; x++) { + decompressBlockAlpha16bitC(src, *dstImage, width, height, 4*x, 4*y, dstChannels); + src += 8; + if (srcFormat == GL_COMPRESSED_RG11_EAC || srcFormat == GL_COMPRESSED_SIGNED_RG11_EAC) { + decompressBlockAlpha16bitC(src, *dstImage + dstChannelBytes, width, height, 4*x, 4*y, dstChannels); + src += 8; + } + } + } + } else { + for (y=0; y < height/4; y++) { + for (x=0; x < width/4; x++) { + // Decode alpha channel for RGBA + if (alphaFormat == AF_8BIT) { + decompressBlockAlphaC(src, *dstImage + 3, width, height, 4*x, 4*y, dstChannels); + src += 8; + } + // Decode color dstChannels + readBigEndian4byteWord(&block_part1, src); + src += 4; + readBigEndian4byteWord(&block_part2, src); + src += 4; + if (alphaFormat == AF_1BIT) + decompressBlockETC21BitAlphaC(block_part1, block_part2, *dstImage, 0, width, height, 4*x, 4*y, dstChannels); + else + decompressBlockETC2c(block_part1, block_part2, *dstImage, width, height, 4*x, 4*y, dstChannels); + } + } + } + + /* Ok, now write out the active pixels to the destination image. + * (But only if the active pixels differ from the total pixels) + */ + + if( !(height == activeHeight && width == activeWidth) ) { + int dstPixelBytes = dstChannels * dstChannelBytes; + int dstRowBytes = dstPixelBytes * width; + int activeRowBytes = activeWidth * dstPixelBytes; + GLubyte *newimg = (GLubyte*)malloc(dstPixelBytes * activeWidth * activeHeight); + unsigned int xx, yy; + int zz; + + if (!newimg) { + free(*dstImage); + return KTX_OUT_OF_MEMORY; + } + + /* Convert from total area to active area: */ + + for (yy = 0; yy < activeHeight; yy++) { + for (xx = 0; xx < activeWidth; xx++) { + for (zz = 0; zz < dstPixelBytes; zz++) { + newimg[ yy*activeRowBytes + xx*dstPixelBytes + zz ] = (*dstImage)[ yy*dstRowBytes + xx*dstPixelBytes + zz]; + } + } + } + + free(*dstImage); + *dstImage = newimg; + } + + return KTX_SUCCESS; +} + +#endif /* SUPPORT_SOFTWARE_ETC_UNPACK */ diff --git a/applications/_plugins/cimage/ktx/lib/gl_funcptrs.h b/applications/_plugins/cimage/ktx/lib/gl_funcptrs.h new file mode 100644 index 000000000..1e5bbb22a --- /dev/null +++ b/applications/_plugins/cimage/ktx/lib/gl_funcptrs.h @@ -0,0 +1,82 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4: */ + +/* $Id: 07d53c927b8cc3343d240d80e5d359afa0e050b7 $ */ + +/* +Copyright (c) 2010 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +unaltered in all copies or substantial portions of the Materials. +Any additions, deletions, or changes to the original source files +must be clearly indicated in accompanying documentation. + +If only executable code is distributed, then the accompanying +documentation must state that "this software is based in part on the +work of the Khronos Group." + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +/* + * Author: Mark Callow based on code from Georg Kolling + */ + +#ifndef GL_FUNCPTRS_H +#define GL_FUNCPTRS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if KTX_USE_GETPROC + // Not defined in glew.h. + typedef void (GL_APIENTRY* PFNGLTEXIMAGE1DPROC) ( + GLenum target, GLint level, GLint internalformat, + GLsizei width, GLint border, GLenum format, GLenum type, + const GLvoid *pixels + ); +#endif + +extern PFNGLTEXIMAGE1DPROC pfGlTexImage1D; +extern PFNGLTEXIMAGE3DPROC pfGlTexImage3D; +extern PFNGLCOMPRESSEDTEXIMAGE1DPROC pfGlCompressedTexImage1D; +extern PFNGLCOMPRESSEDTEXIMAGE3DPROC pfGlCompressedTexImage3D; +extern PFNGLGENERATEMIPMAPPROC pfGlGenerateMipmap; +extern PFNGLGETSTRINGIPROC pfGlGetStringi; + +#define DECLARE_GL_FUNCPTRS \ + PFNGLTEXIMAGE1DPROC pfGlTexImage1D; \ + PFNGLTEXIMAGE3DPROC pfGlTexImage3D; \ + PFNGLCOMPRESSEDTEXIMAGE1DPROC pfGlCompressedTexImage1D; \ + PFNGLCOMPRESSEDTEXIMAGE3DPROC pfGlCompressedTexImage3D; \ + PFNGLGENERATEMIPMAPPROC pfGlGenerateMipmap; \ + PFNGLGETSTRINGIPROC pfGlGetStringi; + +#define INITIALIZE_GL_FUNCPTRS \ + pfGlTexImage1D = glTexImage1D; \ + pfGlTexImage3D = glTexImage3D; \ + pfGlCompressedTexImage1D = glCompressedTexImage1D; \ + pfGlCompressedTexImage3D = glCompressedTexImage3D; \ + pfGlGenerateMipmap = glGenerateMipmap; \ + pfGlGetStringi = glGetStringi; + +#ifdef __cplusplus +} +#endif + +#endif /* GL_FUNCPTRS_H */ diff --git a/applications/_plugins/cimage/ktx/lib/gles1_funcptrs.h b/applications/_plugins/cimage/ktx/lib/gles1_funcptrs.h new file mode 100644 index 000000000..e8376a8a1 --- /dev/null +++ b/applications/_plugins/cimage/ktx/lib/gles1_funcptrs.h @@ -0,0 +1,81 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4: */ + +/* $Id: e9ec772d88c4c93ce716eaa4864e8481736bc2bd $ */ + +/* +Copyright (c) 2010 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +unaltered in all copies or substantial portions of the Materials. +Any additions, deletions, or changes to the original source files +must be clearly indicated in accompanying documentation. + +If only executable code is distributed, then the accompanying +documentation must state that "this software is based in part on the +work of the Khronos Group." + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +/* + * Author: Mark Callow based on code from Georg Kolling + */ + +#ifndef GLES1_FUNCPTRS_H +#define GLES1_FUNCPTRS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* remove these where already defined as typedefs (GCC 4 complains of duplicate definitions) */ +typedef void (GL_APIENTRY* PFNGLTEXIMAGE1DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (GL_APIENTRY* PFNGLTEXIMAGE3DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (GL_APIENTRY* PFNGLCOMPRESSEDTEXIMAGE1DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const GLvoid *data); +typedef void (GL_APIENTRY* PFNGLCOMPRESSEDTEXIMAGE3DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *data); +typedef void (GL_APIENTRY* PFNGLGENERATEMIPMAPPROC) (GLenum target); +typedef const GLubyte* (GL_APIENTRY* PFNGLGETSTRINGIPROC) (GLenum name, GLuint index); + +extern PFNGLTEXIMAGE1DPROC pfGlTexImage1D; +extern PFNGLTEXIMAGE3DPROC pfGlTexImage3D; +extern PFNGLCOMPRESSEDTEXIMAGE1DPROC pfGlCompressedTexImage1D; +extern PFNGLCOMPRESSEDTEXIMAGE3DPROC pfGlCompressedTexImage3D; +extern PFNGLGENERATEMIPMAPPROC pfGlGenerateMipmap; +extern PFNGLGETSTRINGIPROC pfGlGetStringi; + +#define DECLARE_GL_FUNCPTRS \ + PFNGLTEXIMAGE1DPROC pfGlTexImage1D; \ + PFNGLTEXIMAGE3DPROC pfGlTexImage3D; \ + PFNGLCOMPRESSEDTEXIMAGE1DPROC pfGlCompressedTexImage1D; \ + PFNGLCOMPRESSEDTEXIMAGE3DPROC pfGlCompressedTexImage3D; \ + PFNGLGENERATEMIPMAPPROC pfGlGenerateMipmap; \ + PFNGLGETSTRINGIPROC pfGlGetStringi; + +#define INITIALIZE_GL_FUNCPTRS \ + pfGlTexImage1D = 0; \ + pfGlTexImage3D = 0; \ + pfGlCompressedTexImage1D = 0; \ + pfGlCompressedTexImage3D = 0; \ + pfGlGenerateMipmap = 0; \ + pfGlGetStringi = 0; + +#ifdef __cplusplus +} +#endif + +#endif /* GLES1_FUNCPTRS_H */ diff --git a/applications/_plugins/cimage/ktx/lib/gles2_funcptrs.h b/applications/_plugins/cimage/ktx/lib/gles2_funcptrs.h new file mode 100644 index 000000000..f63d26635 --- /dev/null +++ b/applications/_plugins/cimage/ktx/lib/gles2_funcptrs.h @@ -0,0 +1,80 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4: */ + +/* $Id: b3ccdda542bf0389ad16fa35f5e17bd4b72c9bf4 $ */ + +/* +Copyright (c) 2010 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +unaltered in all copies or substantial portions of the Materials. +Any additions, deletions, or changes to the original source files +must be clearly indicated in accompanying documentation. + +If only executable code is distributed, then the accompanying +documentation must state that "this software is based in part on the +work of the Khronos Group." + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +/* + * Author: Mark Callow based on code from Georg Kolling + */ + +#ifndef GLES2_FUNCPTRS_H +#define GLES2_FUNCPTRS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* remove these where already defined as typedefs (GCC 4 complains of duplicate definitions) */ +typedef void (GL_APIENTRY* PFNGLTEXIMAGE1DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (GL_APIENTRY* PFNGLTEXIMAGE3DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (GL_APIENTRY* PFNGLCOMPRESSEDTEXIMAGE1DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const GLvoid *data); +typedef void (GL_APIENTRY* PFNGLCOMPRESSEDTEXIMAGE3DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *data); +typedef const GLubyte* (GLAPIENTRY* PFNGLGETSTRINGIPROC) (GLenum name, GLuint index); + +extern PFNGLTEXIMAGE1DPROC pfGlTexImage1D; +extern PFNGLTEXIMAGE3DPROC pfGlTexImage3D; +extern PFNGLCOMPRESSEDTEXIMAGE1DPROC pfGlCompressedTexImage1D; +extern PFNGLCOMPRESSEDTEXIMAGE3DPROC pfGlCompressedTexImage3D; +extern PFNGLGENERATEMIPMAPPROC pfGlGenerateMipmap; +extern PFNGLGETSTRINGIPROC pfGlGetStringi; + +#define DECLARE_GL_FUNCPTRS \ + PFNGLTEXIMAGE1DPROC pfGlTexImage1D; \ + PFNGLTEXIMAGE3DPROC pfGlTexImage3D; \ + PFNGLCOMPRESSEDTEXIMAGE1DPROC pfGlCompressedTexImage1D; \ + PFNGLCOMPRESSEDTEXIMAGE3DPROC pfGlCompressedTexImage3D; \ + PFNGLGENERATEMIPMAPPROC pfGlGenerateMipmap; \ + PFNGLGETSTRINGIPROC pfGlGetStringi; + +#define INITIALIZE_GL_FUNCPTRS \ + pfGlTexImage1D = 0; \ + pfGlTexImage3D = 0; \ + pfGlCompressedTexImage1D = 0; \ + pfGlCompressedTexImage3D = 0; \ + pfGlGenerateMipmap = glGenerateMipmap; \ + pfGlGetStringi = 0; + +#ifdef __cplusplus +} +#endif + +#endif /* GLES2_FUNCPTRS_H */ diff --git a/applications/_plugins/cimage/ktx/lib/gles3_funcptrs.h b/applications/_plugins/cimage/ktx/lib/gles3_funcptrs.h new file mode 100644 index 000000000..c4d55a44a --- /dev/null +++ b/applications/_plugins/cimage/ktx/lib/gles3_funcptrs.h @@ -0,0 +1,85 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4: */ + +/* $Id: e7d280e42d27a4bbebf0bc43c2b48fd5d77af25a $ */ + +/* +Copyright (c) 2010 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +unaltered in all copies or substantial portions of the Materials. +Any additions, deletions, or changes to the original source files +must be clearly indicated in accompanying documentation. + +If only executable code is distributed, then the accompanying +documentation must state that "this software is based in part on the +work of the Khronos Group." + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +/* + * Author: Mark Callow based on code from Georg Kolling + */ + +#ifndef GLES3_FUNCPTRS_H +#define GLES3_FUNCPTRS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* remove these where already defined as typedefs */ +typedef void (GL_APIENTRY* PFNGLTEXIMAGE1DPROC) ( + GLenum target, GLint level, GLint internalformat, + GLsizei width, GLint border, GLenum format, + GLenum type, const GLvoid *pixels + ); +typedef void (GL_APIENTRY* PFNGLCOMPRESSEDTEXIMAGE1DPROC) ( + GLenum target, GLint level, GLenum internalformat, + GLsizei width, GLint border, GLsizei imageSize, + const GLvoid *data + ); + +extern PFNGLTEXIMAGE1DPROC pfGlTexImage1D; +extern PFNGLTEXIMAGE3DPROC pfGlTexImage3D; +extern PFNGLCOMPRESSEDTEXIMAGE1DPROC pfGlCompressedTexImage1D; +extern PFNGLCOMPRESSEDTEXIMAGE3DPROC pfGlCompressedTexImage3D; +extern PFNGLGENERATEMIPMAPPROC pfGlGenerateMipmap; +extern PFNGLGETSTRINGIPROC pfGlGetStringi; + +#define DECLARE_GL_FUNCPTRS \ + PFNGLTEXIMAGE1DPROC pfGlTexImage1D; \ + PFNGLTEXIMAGE3DPROC pfGlTexImage3D; \ + PFNGLCOMPRESSEDTEXIMAGE1DPROC pfGlCompressedTexImage1D; \ + PFNGLCOMPRESSEDTEXIMAGE3DPROC pfGlCompressedTexImage3D; \ + PFNGLGENERATEMIPMAPPROC pfGlGenerateMipmap; \ + PFNGLGETSTRINGIPROC pfGlGetStringi; + +#define INITIALIZE_GL_FUNCPTRS \ + pfGlTexImage1D = 0; \ + pfGlTexImage3D = glTexImage3D; \ + pfGlCompressedTexImage1D = 0; \ + pfGlCompressedTexImage3D = glCompressedTexImage3D; \ + pfGlGenerateMipmap = glGenerateMipmap; \ + pfGlGetStringi = glGetStringi; + +#ifdef __cplusplus +} +#endif + +#endif /* GLES3_FUNCPTRS_H */ diff --git a/applications/_plugins/cimage/ktx/lib/hashtable.c b/applications/_plugins/cimage/ktx/lib/hashtable.c new file mode 100644 index 000000000..e5bb82aab --- /dev/null +++ b/applications/_plugins/cimage/ktx/lib/hashtable.c @@ -0,0 +1,333 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4: */ + +/* $Id: c4b6d4c928f6a816d5bf1d8005ea2e95f663b9c0 $ */ + +/** + * @file hashtable.c + * @~English + * + * @brief Functions for creating and using a hash table of key-value + * pairs. + * + * @author Mark Callow, HI Corporation + */ + +/* +Copyright (c) 2010 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +unaltered in all copies or substantial portions of the Materials. +Any additions, deletions, or changes to the original source files +must be clearly indicated in accompanying documentation. + +If only executable code is distributed, then the accompanying +documentation must state that "this software is based in part on the +work of the Khronos Group". + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +#include +#include +#include +#include + +// This is to avoid compile warnings. strlen is defined as returning +// size_t and is used by the uthash macros. This avoids having to +// make changes to uthash and a bunch of casts in this file. The +// casts would be required because the key and value lengths in KTX +// are specified as 4 byte quantities so we can't change _keyAndValue +// below to use size_t. +#define strlen(x) ((unsigned int)strlen(x)) + +#include "uthash.h" + +#include "ktx.h" +#include "ktxint.h" + + +/** + * @internal + * @struct _keyAndValue + * @brief Hash table entry structure + */ +typedef struct _keyAndValue { + unsigned int keyLen; /*!< Length of the key */ + char* key; /*!< Pointer to key string */ + unsigned int valueLen; /*!< Length of the value */ + void* value; /*!< Pointer to the value */ + UT_hash_handle hh; /*!< handle used by UT hash */ +} key_and_value_t; +/** + * @internal + * @typedef key_and_value_t + * @brief type of a hash table entry. + */ + +/** + * @~English + * @brief Create an empty hash table for storying key-value pairs. + * + * @return pointer to the newly created hash table or NULL if there + * is not enough memory. + * + * + */ +KTX_hash_table +ktxHashTable_Create() +{ + key_and_value_t** kvt = (key_and_value_t**)malloc(sizeof (key_and_value_t**)); + *kvt = NULL; + return (KTX_hash_table)kvt; +} + + +/** + * @~English + * @brief Destroy a hash table. + * + * All memory associated with the hash table and its keys and values + * is freed. + * + * @param [in] This pointer to the hash table to be destroyed. + */ + /* + * @memberof ktxHashTable @public + */ +void +ktxHashTable_Destroy(KTX_hash_table This) +{ + key_and_value_t* kv; + + for(kv = *(key_and_value_t**)This; kv != NULL;) { + key_and_value_t* tmp = (key_and_value_t*)kv->hh.next; + HASH_DELETE(hh, /*head*/*(key_and_value_t**)This, kv); + free(kv); + kv = tmp; + } + free(This); +} + + +/** + * @~English + * @brief Adds a key value pair to a hash table + * + * @param [in] This pointer to the target hash table. + * @param [in] key pointer to the UTF8 NUL-terminated string to be used as the key. + * @param [in] valueLen the number of bytes of data in @p value. + * @param [in] value pointer to the bytes of data constituting the value. + * + * @return KTX_SUCCESS or one of the following error codes. + * + * @exception KTX_INVALID_VALUE if @p This, @p key or @p value are NULL, @p key is an + * empty string or @p valueLen == 0. + */ +/* + * @memberof ktxHashTable @public + */ +KTX_error_code +ktxHashTable_AddKVPair(KTX_hash_table This, const char* key, unsigned int valueLen, const void* value) +{ + if (This && key && value && valueLen != 0) { + unsigned int keyLen = (unsigned int)strlen(key) + 1; + /* key_and_value_t* head = *(key_and_value_t**)This; */ + key_and_value_t* kv; + + if (keyLen == 1) + return KTX_INVALID_VALUE; /* Empty string */ + + /* Allocate all the memory as a block */ + kv = (key_and_value_t*)malloc(sizeof(key_and_value_t) + keyLen + valueLen); + /* Put key first */ + kv->key = (char *)kv + sizeof(key_and_value_t); + kv->keyLen = keyLen; + /* then value */ + kv->value = kv->key + keyLen; + kv->valueLen = valueLen; + memcpy(kv->key, key, keyLen); + memcpy(kv->value, value, valueLen); + + HASH_ADD_KEYPTR( hh, /*head*/*(key_and_value_t**)This, kv->key, kv->keyLen-1, kv); + return KTX_SUCCESS; + } else + return KTX_INVALID_VALUE; +} + + +/** + * @~English + * @brief Looks up a key a hash table and returns the value. + * + * @param [in] This pointer to the target hash table. + * @param [in] key pointer to a UTF8 NUL-terminated string to find. + * @param [in,out] pValueLen @p *pValueLen is set to the number of bytes of + * data in the returned value. + * @param [in,out] ppValue @p *ppValue is set to the point to the value for + * @p key. + * + * @return KTX_SUCCESS or one of the following error codes. + * + * @exception KTX_INVALID_VALUE if @p This, @p key or @p pValueLen or @p ppValue + * is NULL. + * @exception KTX_NOT_FOUND an entry matching @p key was not found. + */ +/* + * @memberof ktxHashTable @public + */ +KTX_error_code +ktxHashTable_FindValue(KTX_hash_table This, const char* key, unsigned int* pValueLen, void** ppValue) +{ + if (This && key && pValueLen && ppValue) { + key_and_value_t* kv; + /* key_and_value_t* head = *(key_and_value_t**)This; */ + + HASH_FIND_STR( /*head*/*(key_and_value_t**)This, key, kv ); /* kv: output pointer */ + + if (kv) { + *pValueLen = kv->valueLen; + *ppValue = kv->value; + return KTX_SUCCESS; + } else + return KTX_NOT_FOUND; + } else + return KTX_INVALID_VALUE; +} + + +/** + * @~English + * @brief Serialize a hash table to a block of data suitable for writing + * to a file. + * + * The caller is responsible for freeing the data block returned by this + * function. + * + * @param [in] This pointer to the target hash table. + * @param [in,out] pKvdLen @p *pKvdLen is set to the number of bytes of + * data in the returned data block. + * @param [in,out] ppKvd @p *ppKvd is set to the point to the block of + * memory containing the serialized data. + * + * @return KTX_SUCCESS or one of the following error codes. + * + * @exception KTX_INVALID_VALUE if @p This, @p pKvdLen or @p ppKvd is NULL. + * @exception KTX_OUT_OF_MEMORY there was not enough memory to serialize the data. + */ + /* @memberof ktxHashTable @public + */ +KTX_error_code +ktxHashTable_Serialize(KTX_hash_table This, unsigned int* pKvdLen, unsigned char** ppKvd) +{ + + if (This && pKvdLen && ppKvd) { + key_and_value_t* kv; + unsigned int bytesOfKeyValueData = 0; + unsigned int keyValueLen; + unsigned char* sd; + char padding[4] = {0, 0, 0, 0}; + + for (kv = *(key_and_value_t**)This; kv != NULL; kv = kv->hh.next) { + /* sizeof(*sd) is to make space to write keyAndValueByteSize */ + keyValueLen = kv->keyLen + kv->valueLen + sizeof(khronos_uint32_t); + /* Add valuePadding */ + keyValueLen += 3 - ((keyValueLen + 3) % 4); + bytesOfKeyValueData += keyValueLen; + } + sd = malloc(bytesOfKeyValueData); + if (!sd) + return KTX_OUT_OF_MEMORY; + + *pKvdLen = bytesOfKeyValueData; + *ppKvd = sd; + + for (kv = *(key_and_value_t **)This; kv != NULL; kv = kv->hh.next) { + int padLen; + + keyValueLen = kv->keyLen + kv->valueLen; + *(khronos_uint32_t*)sd = keyValueLen; + sd += sizeof(khronos_uint32_t); + memcpy(sd, kv->key, kv->keyLen); + sd += kv->keyLen; + memcpy(sd, kv->value, kv->valueLen); + sd += kv->valueLen; + padLen = 3 - ((keyValueLen + 3) % 4); + memcpy(sd, padding, padLen); + sd += padLen; + } + return KTX_SUCCESS; + } else + return KTX_INVALID_VALUE; +} + + +/** + * @~English + * @brief Create a hash table from a block of serialized key-value + * data read from a file. + * + * The caller is responsible for freeing the returned hash table. + * + * @param [in] kvdLen the length of the serialized key-value data. + * @param [in] pKvd pointer to the serialized key-value data. + * @param [in,out] pHt @p *pHt is set to point to the created hash + * table. + * + * @return KTX_SUCCESS or one of the following error codes. + * + * @exception KTX_INVALID_VALUE if @p pKvd or @p pHt is NULL or kvdLen == 0. + * @exception KTX_OUT_OF_MEMORY there was not enough memory to create the hash + * table. + */ + /* @memberof ktxHashTable @public + */ +KTX_error_code +ktxHashTable_Deserialize(unsigned int kvdLen, void* pKvd, KTX_hash_table* pHt) +{ + KTX_hash_table kvt; + char* src = pKvd; + + if (kvdLen == 0 || pKvd == NULL || pHt == NULL) + return KTX_INVALID_VALUE; + + kvt = ktxHashTable_Create(); + if (kvt == NULL) + return KTX_OUT_OF_MEMORY; + + while (src < (char *)pKvd + kvdLen) { + char* key; + unsigned int keyLen; + void* value; + khronos_uint32_t keyAndValueByteSize = *((khronos_uint32_t*)src); + + src += sizeof(keyAndValueByteSize); + key = src; + keyLen = (unsigned int)strlen(key) + 1; + value = key + keyLen; + + ktxHashTable_AddKVPair(kvt, key, keyAndValueByteSize - keyLen, value); + /* Round keyAndValueByteSize */ + keyAndValueByteSize = (keyAndValueByteSize + 3) & ~(khronos_uint32_t)3; + src += keyAndValueByteSize; + } + + *pHt = kvt; + return KTX_SUCCESS; +} + + diff --git a/applications/_plugins/cimage/ktx/lib/ktx.h b/applications/_plugins/cimage/ktx/lib/ktx.h new file mode 100644 index 000000000..49d9900a4 --- /dev/null +++ b/applications/_plugins/cimage/ktx/lib/ktx.h @@ -0,0 +1,512 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4: */ + +#ifndef KTX_H_A55A6F00956F42F3A137C11929827FE1 +#define KTX_H_A55A6F00956F42F3A137C11929827FE1 + +/** + * @file + * @~English + * + * @brief Declares the public functions and structures of the + * KTX API. + * + * @author Georg Kolling, Imagination Technology + * @author with modifications by Mark Callow, HI Corporation + * + * $Id: bcd735e6de9f8497c2cf50ced3800ac401c6d049 $ + * $Date$ + * + * @todo Find a way so that applications do not have to define KTX_OPENGL{,_ES*} + * when using the library. + */ + +/* + * This file copyright (c) 2010 The Khronos Group, Inc. + */ + +/* +@~English + +LibKTX contains code + +@li (c) 2010 The Khronos Group Inc. +@li (c) 2008 and (c) 2010 HI Corporation +@li (c) 2005 Ericsson AB +@li (c) 2003-2010, Troy D. Hanson +@li (c) 2015 Mark Callow + +The KTX load tests contain code + +@li (c) 2013 The Khronos Group Inc. +@li (c) 2008 and (c) 2010 HI Corporation +@li (c) 1997-2014 Sam Lantinga +@li (c) 2015 Mark Callow + +@section default Default License + +With the exception of the files listed explicitly below, the source +files are made available under the following BSD-like license. Most +files contain this license explicitly. Some files refer to LICENSE.md +which contains this same text. Such files are licensed under this +Default License. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +unaltered in all copies or substantial portions of the Materials. +Any additions, deletions, or changes to the original source files +must be clearly indicated in accompanying documentation. + +If only executable code is distributed, then the accompanying +documentation must state that "this software is based in part on the +work of the Khronos Group." + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + +@section hi_mark hi_mark{,_sq}.ktx + +The HI logo textures are © & ™ HI Corporation and are +provided for use only in testing the KTX loader. Any other use requires +specific prior written permission from HI. Furthermore the name HI may +not be used to endorse or promote products derived from this software +without specific prior written permission. + +@section uthash uthash.h + +uthash.h is made available under the following revised BSD license. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +@section SDL2 include/SDL2/ + +These files are part of the SDL2 source distributed by the [SDL project] +(http://libsdl.org) under the terms of the [zlib license] +(http://www.zlib.net/zlib_license.html). +*/ + +/** + * @~English + * @mainpage The KTX Library + * + * @section intro_sec Introduction + * + * libktx is a small library of functions for creating KTX (Khronos + * TeXture) files and instantiating OpenGL® and OpenGL® ES + * textures from them. + * + * For information about the KTX format see the + * + * formal specification. + * + * The library is open source software. Most of the code is licensed under a + * modified BSD license. The code for unpacking ETC1, ETC2 and EAC compressed + * textures has a separate license that restricts it to uses associated with + * Khronos Group APIs. See @ref license for more details. + * + * See @ref history for the list of changes. + * + * @author Mark Callow, HI Corporation + * @author Georg Kolling, Imagination Technology + * @author Jacob Ström, Ericsson AB + * + * @version 2.0.X + * + * $Date$ + */ + +#include + +#include "KHR/khrplatform.h" +#define KTX_OPENGL 1 +#if KTX_OPENGL + + #ifdef _WIN32 + #include + #undef KTX_USE_GETPROC /* Must use GETPROC on Windows */ + #define KTX_USE_GETPROC 1 + #else + #if !defined(KTX_USE_GETPROC) + #define KTX_USE_GETPROC 0 + #endif + #endif + #if KTX_USE_GETPROC + #include + #else + #define GL_GLEXT_PROTOTYPES + #include + #endif + + #define GL_APIENTRY APIENTRY + #define KTX_GLFUNCPTRS "gl_funcptrs.h" + +#elif KTX_OPENGL_ES1 + + #include + #include + + #define KTX_GLFUNCPTRS "gles1_funcptrs.h" + +#elif KTX_OPENGL_ES2 + + #define GL_GLEXT_PROTOTYPES + #include + #include + + #define KTX_GLFUNCPTRS "gles2_funcptrs.h" + +#elif KTX_OPENGL_ES3 + + #define GL_GLEXT_PROTOTYPES + #include + #include + + #define KTX_GLFUNCPTRS "gles3_funcptrs.h" + +#else +#error Please #define one of KTX_OPENGL, KTX_OPENGL_ES1, KTX_OPENGL_ES2 or KTX_OPENGL_ES3 as 1 +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Key String for standard orientation value. + */ +#define KTX_ORIENTATION_KEY "KTXorientation" +/** + * @brief Standard format for 2D orientation value. + */ +#define KTX_ORIENTATION2_FMT "S=%c,T=%c" +/** + * @brief Standard format for 3D orientation value. + */ +#define KTX_ORIENTATION3_FMT "S=%c,T=%c,R=%c" +/** + * @brief Required unpack alignment + */ +#define KTX_GL_UNPACK_ALIGNMENT 4 + +/** + * @brief Error codes returned by library functions. + */ +typedef enum KTX_error_code_t { + KTX_SUCCESS = 0, /*!< Operation was successful. */ + KTX_FILE_OPEN_FAILED, /*!< The target file could not be opened. */ + KTX_FILE_WRITE_ERROR, /*!< An error occurred while writing to the file. */ + KTX_GL_ERROR, /*!< GL operations resulted in an error. */ + KTX_INVALID_OPERATION, /*!< The operation is not allowed in the current state. */ + KTX_INVALID_VALUE, /*!< A parameter value was not valid */ + KTX_NOT_FOUND, /*!< Requested key was not found */ + KTX_OUT_OF_MEMORY, /*!< Not enough memory to complete the operation. */ + KTX_UNEXPECTED_END_OF_FILE, /*!< The file did not contain enough data */ + KTX_UNKNOWN_FILE_FORMAT, /*!< The file not a KTX file */ + KTX_UNSUPPORTED_TEXTURE_TYPE, /*!< The KTX file specifies an unsupported texture type. */ +} KTX_error_code; + +/** + * @brief structure used to pass information about the texture to ktxWriteKTX + */ +typedef struct KTX_texture_info_t +{ + /** + * @brief The type of the image data. + * + * Values are the same as in the @p type parameter of + * glTexImage*D. Must be 0 for compressed images. + */ + khronos_uint32_t glType; + /** + * @brief The data type size to be used in case of endianness + * conversion. + * + * This value is used in the event conversion is required when the + * KTX file is loaded. It should be the size in bytes corresponding + * to glType. Must be 1 for compressed images. + */ + khronos_uint32_t glTypeSize; + /** + * @brief The format of the image(s). + * + * Values are the same as in the format parameter + * of glTexImage*D. Must be 0 for compressed images. + */ + khronos_uint32_t glFormat; + /** @brief The internalformat of the image(s). + * + * Values are the same as for the internalformat parameter of + * glTexImage*2D. Note: it will not be used when a KTX file + * containing an uncompressed texture is loaded into OpenGL ES. + */ + khronos_uint32_t glInternalFormat; + /** @brief The base internalformat of the image(s) + * + * For non-compressed textures, should be the same as glFormat. + * For compressed textures specifies the base internal, e.g. + * GL_RGB, GL_RGBA. + */ + khronos_uint32_t glBaseInternalFormat; + /** @brief Width of the image for texture level 0, in pixels. */ + khronos_uint32_t pixelWidth; + /** @brief Height of the texture image for level 0, in pixels. + * + * Must be 0 for 1D textures. + */ + khronos_uint32_t pixelHeight; + /** @brief Depth of the texture image for level 0, in pixels. + * + * Must be 0 for 1D, 2D and cube textures. + */ + khronos_uint32_t pixelDepth; + /** @brief The number of array elements. + * + * Must be 0 if not an array texture. + */ + khronos_uint32_t numberOfArrayElements; + /** @brief The number of cubemap faces. + * + * Must be 6 for cubemaps and cubemap arrays, 1 otherwise. Cubemap + * faces must be provided in the order: +X, -X, +Y, -Y, +Z, -Z. + */ + khronos_uint32_t numberOfFaces; + /** @brief The number of mipmap levels. + * + * 1 for non-mipmapped texture. 0 indicates that a full mipmap pyramid should + * be generated from level 0 at load time (this is usually not allowed for + * compressed formats). Mipmaps must be provided in order from largest size to + * smallest size. The first mipmap level is always level 0. + */ + khronos_uint32_t numberOfMipmapLevels; +} KTX_texture_info; + + +/** + * @brief Structure used to pass image data to ktxWriteKTX. + */ +typedef struct KTX_image_info { + GLsizei size; /*!< Size of the image data in bytes. */ + GLubyte* data; /*!< Pointer to the image data. */ +} KTX_image_info; + + +/** + * @brief Structure used to return texture dimensions + */ +typedef struct KTX_dimensions { + GLsizei width; /*!< */ + GLsizei height; /*!< */ + GLsizei depth; /*!< */ +} KTX_dimensions; + +/** + * @brief Opaque handle to a KTX_hash_table. + */ +typedef void* KTX_hash_table; + +/* ktxLoadTextureF + * + * Loads a texture from a stdio FILE. + */ +KTX_error_code +ktxLoadTextureF(FILE*, GLuint* pTexture, GLenum* pTarget, + KTX_dimensions* pDimensions, GLboolean* pIsMipmapped, + GLenum* pGlerror, + unsigned int* pKvdLen, unsigned char** ppKvd); + +/* ktxLoadTextureN + * + * Loads a texture from a KTX file on disk. + */ +KTX_error_code +ktxLoadTextureN(const char* const filename, GLuint* pTexture, GLenum* pTarget, + KTX_dimensions* pDimensions, GLboolean* pIsMipmapped, + GLenum* pGlerror, + unsigned int* pKvdLen, unsigned char** ppKvd); + +/* ktxLoadTextureM + * + * Loads a texture from a KTX file in memory. + */ +KTX_error_code +ktxLoadTextureM(const void* bytes, GLsizei size, GLuint* pTexture, GLenum* pTarget, + KTX_dimensions* pDimensions, GLboolean* pIsMipmapped, + GLenum* pGlerror, + unsigned int* pKvdLen, unsigned char** ppKvd); + +/* ktxWriteKTXF + * + * Writes a KTX file using supplied data. + */ +KTX_error_code +ktxWriteKTXF(FILE*, const KTX_texture_info* imageInfo, + GLsizei bytesOfKeyValueData, const void* keyValueData, + GLuint numImages, KTX_image_info images[]); + +/* ktxWriteKTXN + * + * Writes a KTX file using supplied data. + */ +KTX_error_code +ktxWriteKTXN(const char* dstname, const KTX_texture_info* imageInfo, + GLsizei bytesOfKeyValueData, const void* keyValueData, + GLuint numImages, KTX_image_info images[]); + +/* ktxWriteKTXM + * + * Writes a KTX file into memory using supplied data. + */ +KTX_error_code +ktxWriteKTXM(unsigned char** bytes, GLsizei* size, const KTX_texture_info* textureInfo, + GLsizei bytesOfKeyValueData, const void* keyValueData, + GLuint numImages, KTX_image_info images[]); + +/* ktxErrorString() + * + * Returns a string corresponding to a KTX error code. + */ +const char* const ktxErrorString(KTX_error_code error); + +/* ktxHashTable_Create() + * + * Creates a key-value hash table + */ +KTX_hash_table ktxHashTable_Create(); + +/* ktxHashTable_Destroy() + * + * Destroys a key-value hash table + */ +void ktxHashTable_Destroy(KTX_hash_table This); + +/* ktxHashTable_AddKVPair() + * + * Adds a key-value pair to a hash table. + */ +KTX_error_code +ktxHashTable_AddKVPair(KTX_hash_table This, const char* key, + unsigned int valueLen, const void* value); + + +/* ktxHashTable_FindValue() + * + * Looks up a key and returns the value. + */ +KTX_error_code +ktxHashTable_FindValue(KTX_hash_table This, const char* key, + unsigned int* pValueLen, void** pValue); + + +/* ktxHashTable_Serialize() + * + * Serializes the hash table to a block of memory suitable for + * writing to a KTX file. + */ +KTX_error_code +ktxHashTable_Serialize(KTX_hash_table This, unsigned int* kvdLen, unsigned char** kvd); + + +/* ktxHashTable_Deserialize() + * + * Creates a hash table from the serialized data read from a + * a KTX file. + */ +KTX_error_code +ktxHashTable_Deserialize(unsigned int kvdLen, void* kvd, KTX_hash_table* pKvt); + + +#ifdef __cplusplus +} +#endif + +/** +@page history KTX Library Revision History + +@section v5 Version 2.0.X +Changed: +@li New build system + +@section v4 Version 2.0.1 +Added: +@li CMake build files. Thanks to Pavel Rotjberg for the initial version. + +Changed: +@li ktxWriteKTXF to check the validity of the type & format combinations + passed to it. + +Fixed: +@li Public Bugzilla 999: 16-bit luminance texture cannot be written. +@li compile warnings from compilers stricter than MS Visual C++. Thanks to + Pavel Rotjberg. + +@section v3 Version 2.0 +Added: +@li support for decoding ETC2 and EAC formats in the absence of a hardware + decoder. +@li support for converting textures with legacy LUMINANCE, LUMINANCE_ALPHA, + etc. formats to the equivalent R, RG, etc. format with an + appropriate swizzle, when loading in OpenGL Core Profile contexts. +@li ktxErrorString function to return a string corresponding to an error code. +@li tests for ktxLoadTexture[FN] that run under OpenGL ES 3.0 and OpenGL 3.3. + The latter includes an EGL on WGL wrapper that makes porting apps between + OpenGL ES and OpenGL easier on Windows. +@li more texture formats to ktxLoadTexture[FN] and toktx tests. + +Changed: +@li ktxLoadTexture[FMN] to discover the capabilities of the GL context at + run time and load textures, or not, according to those capabilities. + +Fixed: +@li failure of ktxWriteKTXF to pad image rows to 4 bytes as required by the KTX + format. +@li ktxWriteKTXF exiting with KTX_FILE_WRITE_ERROR when attempting to write + more than 1 byte of face-LOD padding. + +Although there is only a very minor API change, the addition of ktxErrorString, the functional changes +are large enough to justify bumping the major revision number. + +@section v2 Version 1.0.1 +Implemented ktxLoadTextureM. +Fixed the following: +@li Public Bugzilla 571: crash when null passed for pIsMipmapped. +@li Public Bugzilla 572: memory leak when unpacking ETC textures. +@li Public Bugzilla 573: potential crash when unpacking ETC textures with unused padding pixels. +@li Public Bugzilla 576: various small fixes. + +Thanks to Krystian Bigaj for the ktxLoadTextureM implementation and these fixes. + +@section v1 Version 1.0 +Initial release. + +*/ + +#endif /* KTX_H_A55A6F00956F42F3A137C11929827FE1 */ diff --git a/applications/_plugins/cimage/ktx/lib/ktxfilestream.c b/applications/_plugins/cimage/ktx/lib/ktxfilestream.c new file mode 100644 index 000000000..99788f262 --- /dev/null +++ b/applications/_plugins/cimage/ktx/lib/ktxfilestream.c @@ -0,0 +1,154 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4: */ + +/** + * @file + * @~English + * + * @brief Implementation of ktxStream for FILE. + * + * @author Maksim Kolesin, Under Development + * @author Georg Kolling, Imagination Technology + * @author Mark Callow, HI Corporation + */ + +/* +Copyright (c) 2010 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +unaltered in all copies or substantial portions of the Materials. +Any additions, deletions, or changes to the original source files +must be clearly indicated in accompanying documentation. + +If only executable code is distributed, then the accompanying +documentation must state that "this software is based in part on the +work of the Khronos Group". + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +#include +#include + +#include "KHR/khrplatform.h" +#include "ktx.h" +#include "ktxint.h" +#include "ktxfilestream.h" + +/** + * @internal + * @~English + * @brief Read bytes from a ktxFileStream. + * + * @param [out] dst pointer to a block of memory with a size + of at least @p size bytes, converted to a void*. + * @param [in] size total size of bytes to be read. + * @param [in] src pointer to a FILE object, converted to a void*, that specifies an input stream. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE @p dst is @c NULL or @p src is @c NULL. + * @exception KTX_UNEXPECTED_END_OF_FILE the file does not contain the expected amount of data. + */ +static +KTX_error_code ktxFileStream_read(void* dst, const GLsizei size, void* src) +{ + if (!dst || !src) + return KTX_INVALID_VALUE; + + if (fread(dst, size, 1, (FILE*)src) != 1) + return KTX_UNEXPECTED_END_OF_FILE; + + return KTX_SUCCESS; +} + +/** + * @internal + * @~English + * @brief Skip bytes in a ktxFileStream. + * + * @param [in] count number of bytes to be skipped. + * @param [in] src pointer to a FILE object, converted to a void*, that specifies an input stream. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE @p dst is @c NULL or @p count is less than zero. + * @exception KTX_UNEXPECTED_END_OF_FILE the file does not contain the expected amount of data. + */ +static +KTX_error_code ktxFileStream_skip(const GLsizei count, void* src) +{ + if (!src || (count < 0)) + return KTX_INVALID_VALUE; + + if (fseek((FILE*)src, count, SEEK_CUR) != 0) + return KTX_UNEXPECTED_END_OF_FILE; + + return KTX_SUCCESS; +} + +/** + * @internal + * @~English + * @brief Write bytes to a ktxFileStream. + * + * @param [in] src pointer to the array of elements to be written, converted to a const void*. + * @param [in] size size in bytes of each element to be written. + * @param [in] count number of elements, each one with a @p size of size bytes. + * @param [out] dst pointer to a FILE object, converted to a void*, that specifies an output stream. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE @p dst is @c NULL or @p src is @c NULL. + * @exception KTX_FILE_WRITE_ERROR a system error occurred while writing the file. + */ +static +KTX_error_code ktxFileStream_write(const void *src, const GLsizei size, const GLsizei count, void* dst) +{ + if (!dst || !src) + return KTX_INVALID_VALUE; + + if (fwrite(src, size, count, (FILE*)dst) != count) + return KTX_FILE_WRITE_ERROR; + + return KTX_SUCCESS; +} + +/** + * @internal + * @~English + * @brief Initializes a ktxFileStream. + * + * @param [in] stream + * @param [in] file + * + * @return KTX_SUCCESS on success, KTX_INVALID_VALUE on error. + * + * @exception KTX_INVALID_VALUE @p stream is @c NULL or @p file is @c NULL. + */ +KTX_error_code ktxFileInit(struct ktxStream* stream, FILE* file) +{ + if (!stream || !file) + return KTX_INVALID_VALUE; + + stream->src = (void*)file; + stream->read = ktxFileStream_read; + stream->skip = ktxFileStream_skip; + stream->write = ktxFileStream_write; + + return KTX_SUCCESS; +} diff --git a/applications/_plugins/cimage/ktx/lib/ktxfilestream.h b/applications/_plugins/cimage/ktx/lib/ktxfilestream.h new file mode 100644 index 000000000..00cd2e74c --- /dev/null +++ b/applications/_plugins/cimage/ktx/lib/ktxfilestream.h @@ -0,0 +1,49 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4: */ + +/* +Copyright (c) 2010 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +unaltered in all copies or substantial portions of the Materials. +Any additions, deletions, or changes to the original source files +must be clearly indicated in accompanying documentation. + +If only executable code is distributed, then the accompanying +documentation must state that "this software is based in part on the +work of the Khronos Group". + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +/* + * Author: Maksim Kolesin from original code + * by Mark Callow and Georg Kolling + */ + +#ifndef KTXFILESTREAM_H +#define KTXFILESTREAM_H + +#include "ktx.h" +#include "ktxstream.h" + +/* + * ktxFileInit: Initialize a ktxStream to a ktxFileStream with a FILE object + */ +KTX_error_code ktxFileInit(struct ktxStream* stream, FILE* file); + +#endif /* KTXFILESTREAM_H */ \ No newline at end of file diff --git a/applications/_plugins/cimage/ktx/lib/ktxint.h b/applications/_plugins/cimage/ktx/lib/ktxint.h new file mode 100644 index 000000000..94deae6a2 --- /dev/null +++ b/applications/_plugins/cimage/ktx/lib/ktxint.h @@ -0,0 +1,336 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4: */ + +/* $Id: 7f8f35be3acc819647c85c65682529b18a792866 $ */ + +/* +Copyright (c) 2010 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +unaltered in all copies or substantial portions of the Materials. +Any additions, deletions, or changes to the original source files +must be clearly indicated in accompanying documentation. + +If only executable code is distributed, then the accompanying +documentation must state that "this software is based in part on the +work of the Khronos Group." + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +/* + * Author: Mark Callow from original code by Georg Kolling + */ + +#ifndef KTXINT_H +#define KTXINT_H + +/* Define this to include the ETC unpack software in the library. */ +#ifndef SUPPORT_SOFTWARE_ETC_UNPACK + /* Include for all GL versions because have seen OpenGL ES 3 + * implementaions that do not support ETC1 (ARM Mali emulator v1.0)! + */ + #define SUPPORT_SOFTWARE_ETC_UNPACK 0 +#endif + +#ifndef SUPPORT_LEGACY_FORMAT_CONVERSION + #if KTX_OPENGL + #define SUPPORT_LEGACY_FORMAT_CONVERSION 1 + #elif KTX_OPENGL_ES1 + /* ES1, ES2 & ES3 support the legacy formats */ + #define SUPPORT_LEGACY_FORMAT_CONVERSION 0 + #endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + +#define KTX_IDENTIFIER_REF { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A } +#define KTX_ENDIAN_REF (0x04030201) +#define KTX_ENDIAN_REF_REV (0x01020304) +#define KTX_HEADER_SIZE (64) + +/** + * @internal + * @brief KTX file header + * + * See the KTX specification for descriptions + * + */ +typedef struct KTX_header_t { + khronos_uint8_t identifier[12]; + khronos_uint32_t endianness; + khronos_uint32_t glType; + khronos_uint32_t glTypeSize; + khronos_uint32_t glFormat; + khronos_uint32_t glInternalFormat; + khronos_uint32_t glBaseInternalFormat; + khronos_uint32_t pixelWidth; + khronos_uint32_t pixelHeight; + khronos_uint32_t pixelDepth; + khronos_uint32_t numberOfArrayElements; + khronos_uint32_t numberOfFaces; + khronos_uint32_t numberOfMipmapLevels; + khronos_uint32_t bytesOfKeyValueData; +} KTX_header; + +/* This will cause compilation to fail if the struct size doesn't match */ +typedef int KTX_header_SIZE_ASSERT [sizeof(KTX_header) == KTX_HEADER_SIZE]; + +/** + * @internal + * @brief _ktxCheckHeader returns texture information in this structure + * + * TO DO: document properly + */ +typedef struct KTX_texinfo_t { + /* Data filled in by _ktxCheckHeader() */ + khronos_uint32_t textureDimensions; + khronos_uint32_t glTarget; + khronos_uint32_t compressed; + khronos_uint32_t generateMipmaps; +} KTX_texinfo; + +/** + * @internal + * @brief used to pass GL context capabilites to subroutines. + */ +#define _KTX_NO_R16_FORMATS 0x0 +#define _KTX_R16_FORMATS_NORM 0x1 +#define _KTX_R16_FORMATS_SNORM 0x2 +#define _KTX_ALL_R16_FORMATS (_KTX_R16_FORMATS_NORM | _KTX_R16_FORMATS_SNORM) +extern GLint _ktxR16Formats; +extern GLboolean _ktxSupportsSRGB; + + +/* + * These defines are needed to compile the KTX library. When + * these things are not available in the GL header in use at + * compile time, the library provides its own support, handles + * the expected run-time errors or just needs the token value. + */ +#ifndef GL_LUMINANCE +#define GL_ALPHA 0x1906 +#define GL_LUMINANCE 0x1909 +#define GL_LUMINANCE_ALPHA 0x190A +#endif +#ifndef GL_INTENSITY +#define GL_INTENSITY 0x8049 +#endif +#if SUPPORT_LEGACY_FORMAT_CONVERSION +/* For loading legacy KTX files. */ +#ifndef GL_LUMINANCE4 +#define GL_ALPHA4 0x803B +#define GL_ALPHA8 0x803C +#define GL_ALPHA12 0x803D +#define GL_ALPHA16 0x803E +#define GL_LUMINANCE4 0x803F +#define GL_LUMINANCE8 0x8040 +#define GL_LUMINANCE12 0x8041 +#define GL_LUMINANCE16 0x8042 +#define GL_LUMINANCE4_ALPHA4 0x8043 +#define GL_LUMINANCE6_ALPHA2 0x8044 +#define GL_LUMINANCE8_ALPHA8 0x8045 +#define GL_LUMINANCE12_ALPHA4 0x8046 +#define GL_LUMINANCE12_ALPHA12 0x8047 +#define GL_LUMINANCE16_ALPHA16 0x8048 +#endif +#ifndef GL_INTENSITY4 +#define GL_INTENSITY4 0x804A +#define GL_INTENSITY8 0x804B +#define GL_INTENSITY12 0x804C +#define GL_INTENSITY16 0x804D +#endif +#ifndef GL_SLUMINANCE +#define GL_SLUMINANCE_ALPHA 0x8C44 +#define GL_SLUMINANCE8_ALPHA8 0x8C45 +#define GL_SLUMINANCE 0x8C46 +#define GL_SLUMINANCE8 0x8C47 +#endif +#endif /* SUPPORT_LEGACY_FORMAT_CONVERSION */ +#ifndef GL_TEXTURE_1D +#define GL_TEXTURE_1D 0x0DE0 +#endif +#ifndef GL_TEXTURE_3D +#define GL_TEXTURE_3D 0x806F +#endif +#ifndef GL_TEXTURE_CUBE_MAP +#define GL_TEXTURE_CUBE_MAP 0x8513 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#endif +#ifndef GL_TEXTURE_CUBE_MAP_ARRAY +#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009 +#endif +/* from GL_EXT_texture_array */ +#ifndef GL_TEXTURE_1D_ARRAY_EXT +#define GL_TEXTURE_1D_ARRAY_EXT 0x8C18 +#define GL_TEXTURE_2D_ARRAY_EXT 0x8C1A +#endif +#ifndef GL_GENERATE_MIPMAP +#define GL_GENERATE_MIPMAP 0x8191 +#endif + +/* For writer.c */ +#if !defined(GL_BGR) +#define GL_BGR 0x80E0 +#define GL_BGRA 0x80E1 +#endif +#if !defined(GL_RED_INTEGER) +#define GL_RED_INTEGER 0x8D94 +#define GL_RGB_INTEGER 0x8D98 +#define GL_RGBA_INTEGER 0x8D99 +#endif +#if !defined(GL_GREEN_INTEGER) +#define GL_GREEN_INTEGER 0x8D95 +#define GL_BLUE_INTEGER 0x8D96 +#endif +#if !defined(GL_ALPHA_INTEGER) +#define GL_ALPHA_INTEGER 0x8D97 +#endif +#if !defined (GL_BGR_INTEGER) +#define GL_BGR_INTEGER 0x8D9A +#define GL_BGRA_INTEGER 0x8D9B +#endif +#if !defined(GL_INT) +#define GL_INT 0x1404 +#define GL_UNSIGNED_INT 0x1405 +#endif +#if !defined(GL_HALF_FLOAT) +typedef unsigned short GLhalf; +#define GL_HALF_FLOAT 0x140B +#endif +#if !defined(GL_UNSIGNED_BYTE_3_3_2) +#define GL_UNSIGNED_BYTE_3_3_2 0x8032 +#define GL_UNSIGNED_INT_8_8_8_8 0x8035 +#define GL_UNSIGNED_INT_10_10_10_2 0x8036 +#endif +#if !defined(GL_UNSIGNED_BYTE_2_3_3_REV) +#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 +#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 +#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 +#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 +#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 +#endif +#if !defined(GL_UNSIGNED_INT_24_8) +#define GL_DEPTH_STENCIL 0x84F9 +#define GL_UNSIGNED_INT_24_8 0x84FA +#endif +#if !defined(GL_UNSIGNED_INT_5_9_9_9_REV) +#define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E +#endif +#if !defined(GL_UNSIGNED_INT_10F_11F_11F_REV) +#define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B +#endif +#if !defined (GL_FLOAT_32_UNSIGNED_INT_24_8_REV) +#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD +#endif + +#ifndef GL_ETC1_RGB8_OES +#define GL_ETC1_RGB8_OES 0x8D64 +#endif + +#if SUPPORT_SOFTWARE_ETC_UNPACK +#ifndef GL_COMPRESSED_R11_EAC +#define GL_COMPRESSED_R11_EAC 0x9270 +#define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 +#define GL_COMPRESSED_RG11_EAC 0x9272 +#define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 +#define GL_COMPRESSED_RGB8_ETC2 0x9274 +#define GL_COMPRESSED_SRGB8_ETC2 0x9275 +#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 +#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 +#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 +#endif +#ifndef GL_R16_SNORM +#define GL_R16_SNORM 0x8F98 +#define GL_RG16_SNORM 0x8F99 +#endif +#ifndef GL_RED +#define GL_RED 0x1903 +#define GL_GREEN 0x1904 +#define GL_BLUE 0x1905 +#define GL_RG 0x8227 +#define GL_RG_INTEGER 0x8228 +#endif +#ifndef GL_R16 +#define GL_R16 0x822A +#define GL_RG16 0x822C +#endif +#ifndef GL_RGB8 +#define GL_RGB8 0x8051 +#define GL_RGBA8 0x8058 +#endif +#ifndef GL_SRGB8 +#define GL_SRGB8 0x8C41 +#define GL_SRGB8_ALPHA8 0x8C43 +#endif +#endif + +#ifndef GL_MAJOR_VERSION +#define GL_MAJOR_VERSION 0x821B +#define GL_MINOR_VERSION 0x821C +#endif + +#ifndef GL_CONTEXT_PROFILE_MASK +#define GL_CONTEXT_PROFILE_MASK 0x9126 +#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001 +#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002 +#endif + +#ifndef GL_NUM_EXTENSIONS +#define GL_NUM_EXTENSIONS 0x821D +#endif + +#ifndef MAX +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#endif + +/* CheckHeader + * + * Reads the KTX file header and performs some sanity checking on the values + */ +KTX_error_code _ktxCheckHeader(KTX_header* header, KTX_texinfo* texinfo); + +/* + * SwapEndian16: Swaps endianness in an array of 16-bit values + */ +void _ktxSwapEndian16(khronos_uint16_t* pData16, int count); + +/* + * SwapEndian32: Swaps endianness in an array of 32-bit values + */ +void _ktxSwapEndian32(khronos_uint32_t* pData32, int count); + +/* + * UncompressETC: uncompresses an ETC compressed texture image + */ +KTX_error_code _ktxUnpackETC(const GLubyte* srcETC, const GLenum srcFormat, + khronos_uint32_t active_width, khronos_uint32_t active_height, + GLubyte** dstImage, + GLenum* format, GLenum* internalFormat, GLenum* type, + GLint R16Formats, GLboolean supportsSRGB); + +#ifdef __cplusplus +} +#endif + +#endif /* KTXINT_H */ diff --git a/applications/_plugins/cimage/ktx/lib/ktxmemstream.c b/applications/_plugins/cimage/ktx/lib/ktxmemstream.c new file mode 100644 index 000000000..7a98517e4 --- /dev/null +++ b/applications/_plugins/cimage/ktx/lib/ktxmemstream.c @@ -0,0 +1,225 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4: */ + +/** + * @file + * @~English + * + * @brief Implementation of ktxStream for memory. + * + * @author Maksim Kolesin, Under Development + * @author Georg Kolling, Imagination Technology + * @author Mark Callow, HI Corporation + */ + +/* +Copyright (c) 2010 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +unaltered in all copies or substantial portions of the Materials. +Any additions, deletions, or changes to the original source files +must be clearly indicated in accompanying documentation. + +If only executable code is distributed, then the accompanying +documentation must state that "this software is based in part on the +work of the Khronos Group". + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +#include +#include + +#include "KHR/khrplatform.h" +#include "ktx.h" +#include "ktxint.h" +#include "ktxmemstream.h" + +/** + * @internal + * @~English + * @brief Expand a ktxMem to fit to a newsize. + * + * @param [in] mem pointer to ktxMem struct to expand. + * @param [in] newsize minimum new size required. + * + * @return KTX_SUCCESS on success, KTX_OUT_OF_MEMORY on error. + * + * @exception KTX_OUT_OF_MEMORY System failed to allocate sufficient memory. + */ +static +KTX_error_code ktxMem_expand(struct ktxMem *mem, const GLsizei newsize) +{ + GLsizei new_alloc_size = mem->alloc_size; + while (new_alloc_size < newsize) + new_alloc_size <<= 1; + + if (new_alloc_size == mem->alloc_size) + return KTX_SUCCESS; + + mem->bytes = (unsigned char*)realloc(mem->bytes, new_alloc_size); + if(!mem->bytes) + { + mem->alloc_size = 0; + mem->used_size = 0; + return KTX_OUT_OF_MEMORY; + } + + mem->alloc_size = new_alloc_size; + return KTX_SUCCESS; +} + +/** + * @internal + * @~English + * @brief Read bytes from a ktxMemStream. + * + * @param [out] dst pointer to memory where to copy read bytes. + * @param [in] count number of bytes to read. + * @param [in] src pointer to ktxMem struct, converted to a void*, that specifies an input stream. + * + * @return KTX_SUCCESS on success, KTX_INVALID_VALUE on error. + * + * @exception KTX_INVALID_VALUE @p dst is @c NULL or @p mem is @c NULL or not sufficient data is available in ktxMem. + */ +static +KTX_error_code ktxMemStream_read(void* dst, const GLsizei count, void* src) +{ + struct ktxMem* mem = (struct ktxMem*)src; + + if(!dst || !mem || (mem->pos + count > mem->used_size) || (mem->pos + count < mem->pos)) + return KTX_INVALID_VALUE; + + memcpy(dst, mem->bytes + mem->pos, count); + mem->pos += count; + + return KTX_SUCCESS; +} + +/** + * @internal + * @~English + * @brief Skip bytes in a ktxFileStream. + * + * @param [in] count number of bytes to skip. + * @param [in] src pointer to a ktxMem struct, converted to a void*, that specifies an input stream. + * + * @return KTX_SUCCESS on success, KTX_INVALID_VALUE on error. + * + * @exception KTX_INVALID_VALUE @p mem is @c NULL or not sufficient data is available in ktxMem. + */ +static +KTX_error_code ktxMemStream_skip(const GLsizei count, void* src) +{ + struct ktxMem* mem = (struct ktxMem*)src; + + if(!mem || (mem->pos + count > mem->used_size) || (mem->pos + count < mem->pos)) + return KTX_INVALID_VALUE; + + mem->pos += count; + + return KTX_SUCCESS; +} + +/** + * @internal + * @~English + * @brief Write bytes to a ktxFileStream. + * + * @param [in] src pointer to the array of elements to be written, converted to a const void*. + * @param [in] size size in bytes of each element to be written. + * @param [in] count number of elements, each one with a @p size of size bytes. + * @param [out] dst pointer to a ktxMem struct, converted to a void*, that specifies an output stream. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE @p dst is @c NULL or @p mem is @c NULL. + * @exception KTX_OUT_OF_MEMORY See ktxMem_expand() for causes. + */ +static +KTX_error_code ktxMemStream_write(const void* src, const GLsizei size, const GLsizei count, void* dst) +{ + struct ktxMem* mem = (struct ktxMem*)dst; + KTX_error_code rc = KTX_SUCCESS; + + if(!dst || !mem) + return KTX_INVALID_VALUE; + + if(mem->alloc_size < mem->used_size + size*count) + { + rc = ktxMem_expand(mem, mem->used_size + size*count); + if(rc != KTX_SUCCESS) + return rc; + } + + memcpy(mem->bytes + mem->used_size, src, size*count); + mem->used_size += size*count; + + return KTX_SUCCESS; +} + +/** + * @brief Default allocation size for a ktxMemStream. + */ +#define KTX_MEM_DEFAULT_ALLOCATED_SIZE 256 + +/** + * @internal + * @~English + * @brief Initialize a ktxMemStream. + * + * @param [in] stream pointer to a ktxStream struct to initialize. + * @param [in] mem pointer to a ktxMem struct to use in ktxMemStream. + * @param [in] bytes pointer to an array of bytes to use as initial data. + * @param [in] size size of array of initial data for ktxMemStream. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE @p stream is @c NULL or @p mem is @c NULL or @p size is less than 0. + * @exception KTX_OUT_OF_MEMORY system failed to allocate sufficient memory. + */ +KTX_error_code ktxMemInit(struct ktxStream* stream, struct ktxMem* mem, const void* bytes, GLsizei size) +{ + if (!stream || !mem || size < 0) + return KTX_INVALID_VALUE; + + if(!bytes) + { + if (size == 0) + size = KTX_MEM_DEFAULT_ALLOCATED_SIZE; + mem->bytes = (unsigned char*)malloc(size); + if (!mem->bytes) + return KTX_OUT_OF_MEMORY; + mem->alloc_size = size; + mem->used_size = 0; + mem->pos = 0; + } + else + { + mem->bytes = (unsigned char*)bytes; + mem->used_size = size; + mem->alloc_size = size; + mem->pos = 0; + } + + stream->src = mem; + stream->read = ktxMemStream_read; + stream->skip = ktxMemStream_skip; + stream->write = ktxMemStream_write; + + return KTX_SUCCESS; +} diff --git a/applications/_plugins/cimage/ktx/lib/ktxmemstream.h b/applications/_plugins/cimage/ktx/lib/ktxmemstream.h new file mode 100644 index 000000000..30e9b0c67 --- /dev/null +++ b/applications/_plugins/cimage/ktx/lib/ktxmemstream.h @@ -0,0 +1,62 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4: */ + +/* +Copyright (c) 2010 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +unaltered in all copies or substantial portions of the Materials. +Any additions, deletions, or changes to the original source files +must be clearly indicated in accompanying documentation. + +If only executable code is distributed, then the accompanying +documentation must state that "this software is based in part on the +work of the Khronos Group". + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +/* + * Author: Maksim Kolesin from original code + * by Mark Callow and Georg Kolling + */ + +#ifndef KTXMEMSTREAM_H +#define KTXMEMSTREAM_H + +#include "ktx.h" +#include "ktxstream.h" + +/** + * @internal + * @brief Structure to store information + * about data allocated for ktxMemStream + */ +struct ktxMem +{ + unsigned char* bytes; + GLsizei alloc_size; + GLsizei used_size; + GLsizei pos; +}; + +/* + * ktxMemInit: Initialize a ktxStream to a ktxMemStream with ktxMem struct and/or array of bytes + */ +KTX_error_code ktxMemInit(struct ktxStream* stream, struct ktxMem* mem, const void* bytes, GLsizei size); + +#endif /* KTXMEMSTREAM_H */ \ No newline at end of file diff --git a/applications/_plugins/cimage/ktx/lib/ktxstream.h b/applications/_plugins/cimage/ktx/lib/ktxstream.h new file mode 100644 index 000000000..b1a230173 --- /dev/null +++ b/applications/_plugins/cimage/ktx/lib/ktxstream.h @@ -0,0 +1,75 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4: */ + +/* +Copyright (c) 2010 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +unaltered in all copies or substantial portions of the Materials. +Any additions, deletions, or changes to the original source files +must be clearly indicated in accompanying documentation. + +If only executable code is distributed, then the accompanying +documentation must state that "this software is based in part on the +work of the Khronos Group". + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +/* + * Author: Maksim Kolesin from original code + * by Mark Callow and Georg Kolling + */ + +#ifndef KTXSTREAM_H +#define KTXSTREAM_H + +#include "ktx.h" + +/** + * @private + * @~English + * @brief type for a pointer to a stream reading function + */ +typedef KTX_error_code(*ktxStream_read)(void* dst, const GLsizei count, void* src); +/** + * @private + * @~English + * @brief type for a pointer to a stream skipping function + */ +typedef KTX_error_code(*ktxStream_skip)(const GLsizei count, void* src); +/** + * @private + * @~English + * @brief type for a pointer to a stream reading function + */ +typedef KTX_error_code(*ktxStream_write)(const void *src, const GLsizei size, const GLsizei count, void* dst); + +/** + * @private + * @~English + * @brief KTX stream interface + */ +struct ktxStream +{ + void* src; /**< pointer to the stream source */ + ktxStream_read read; /**< pointer to function for reading bytes */ + ktxStream_skip skip; /**< pointer to function for skipping bytes */ + ktxStream_write write; /**< pointer to function for writing bytes */ +}; + +#endif /* KTXSTREAM_H */ \ No newline at end of file diff --git a/applications/_plugins/cimage/ktx/lib/loader.c b/applications/_plugins/cimage/ktx/lib/loader.c new file mode 100644 index 000000000..0b29610ae --- /dev/null +++ b/applications/_plugins/cimage/ktx/lib/loader.c @@ -0,0 +1,970 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4: */ + +/* $Id: a1d73255a63f3e703c641f61bafba89453f05efd $ */ + +/** + * @file + * @~English + * + * @brief Functions for instantiating GL or GLES textures from KTX files. + * + * @author Georg Kolling, Imagination Technology + * @author Mark Callow, HI Corporation + */ + +/* +Copyright (c) 2010 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +unaltered in all copies or substantial portions of the Materials. +Any additions, deletions, or changes to the original source files +must be clearly indicated in accompanying documentation. + +If only executable code is distributed, then the accompanying +documentation must state that "this software is based in part on the +work of the Khronos Group". + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +#ifdef _WIN32 +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include + +#include "KHR/khrplatform.h" +#include "ktx.h" +#include "ktxint.h" +#include "ktxstream.h" +#include "ktxfilestream.h" +#include "ktxmemstream.h" + +#include KTX_GLFUNCPTRS + +DECLARE_GL_FUNCPTRS + +/** + * @internal + * @~English + * @brief Additional contextProfile bit indicating an OpenGL ES context. + * + * This is the same value NVIDIA returns when using an OpenGL ES profile + * of their desktop drivers. However it is not specified in any official + * specification as OpenGL ES does not support the GL_CONTEXT_PROFILE_MASK + * query. + */ +#define _CONTEXT_ES_PROFILE_BIT 0x4 + +/** + * @internal + * @~English + * @name Supported Sized Format Macros + * + * These macros describe values that may be used with the sizedFormats + * variable. + */ +/**@{*/ +#define _NON_LEGACY_FORMATS 0x1 /*< @internal Non-legacy sized formats are supported. */ +#define _LEGACY_FORMATS 0x2 /*< @internal Legacy sized formats are supported. */ +/** + * @internal + * @~English + * @brief All sized formats are supported + */ +#define _ALL_SIZED_FORMATS (_NON_LEGACY_FORMATS | _LEGACY_FORMATS) +#define _NO_SIZED_FORMATS 0 /*< @internal No sized formats are supported. */ +/**@}*/ + +/** + * @internal + * @~English + * @brief indicates the profile of the current context. + */ +static GLint contextProfile = 0; +/** + * @internal + * @~English + * @brief Indicates what sized texture formats are supported + * by the current context. + */ +static GLint sizedFormats = _ALL_SIZED_FORMATS; +static GLboolean supportsSwizzle = GL_TRUE; +/** + * @internal + * @~English + * @brief Indicates which R16 & RG16 formats are supported by the current context. + */ +static GLint R16Formats = _KTX_ALL_R16_FORMATS; +/** + * @internal + * @~English + * @brief Indicates if the current context supports sRGB textures. + */ +static GLboolean supportsSRGB = GL_TRUE; +/** + * @internal + * @~English + * @brief Indicates if the current context supports cube map arrays. + */ +static GLboolean supportsCubeMapArrays = GL_FALSE; + +/** + * @internal + * @~English + * @brief Workaround mismatch of glGetString declaration and standard string + * function parameters. + */ +#define glGetString(x) (const char*)glGetString(x) + +/** +* @internal +* @~English +* @brief Workaround mismatch of glGetStringi declaration and standard string +* function parameters. +*/ +#define pfGlGetStringi(x,y) (const char*)pfGlGetStringi(x,y) + +/** + * @internal + * @~English + * @brief Check for existence of OpenGL extension + */ +static GLboolean +hasExtension(const char* extension) +{ + if (pfGlGetStringi == NULL) { + if (strstr(glGetString(GL_EXTENSIONS), extension) != NULL) + return GL_TRUE; + else + return GL_FALSE; + } + else { + int i, n; + + glGetIntegerv(GL_NUM_EXTENSIONS, &n); + for (i = 0; i < n; i++) { + if (strcmp(pfGlGetStringi(GL_EXTENSIONS, i), extension) == 0) + return GL_TRUE; + } + return GL_FALSE; + } +} + +/** + * @internal + * @~English + * @brief Discover the capabilities of the current GL context. + * + * Queries the context and sets several the following internal variables indicating + * the capabilities of the context: + * + * @li sizedFormats + * @li supportsSwizzle + * @li supportsSRGB + * @li b16Formats + * + */ +static void discoverContextCapabilities(void) +{ + GLint majorVersion = 1; + GLint minorVersion = 0; + + // Done here so things will work when GLEW, or equivalent, is being used + // and GL function names are defined as pointers. Initialization at + // declaration would happen before these pointers have been initialized. + INITIALIZE_GL_FUNCPTRS + + if (strstr(glGetString(GL_VERSION), "GL ES") != NULL) + contextProfile = _CONTEXT_ES_PROFILE_BIT; + // MAJOR & MINOR only introduced in GL {,ES} 3.0 + glGetIntegerv(GL_MAJOR_VERSION, &majorVersion); + glGetIntegerv(GL_MINOR_VERSION, &minorVersion); + if (glGetError() != GL_NO_ERROR) { + // < v3.0; resort to the old-fashioned way. + if (contextProfile & _CONTEXT_ES_PROFILE_BIT) + sscanf(glGetString(GL_VERSION), "OpenGL ES %d.%d ", + &majorVersion, &minorVersion); + else + sscanf(glGetString(GL_VERSION), "OpenGL %d.%d ", + &majorVersion, &minorVersion); + } + if (contextProfile & _CONTEXT_ES_PROFILE_BIT) { + if (majorVersion < 3) { + supportsSwizzle = GL_FALSE; + sizedFormats = _NO_SIZED_FORMATS; + R16Formats = _KTX_NO_R16_FORMATS; + supportsSRGB = GL_FALSE; + } else { + sizedFormats = _NON_LEGACY_FORMATS; + if (hasExtension("GL_EXT_texture_cube_map_array")) { + supportsCubeMapArrays = GL_TRUE; + } + } + if (hasExtension("GL_OES_required_internalformat")) { + sizedFormats |= _ALL_SIZED_FORMATS; + } + // There are no OES extensions for sRGB textures or R16 formats. + } else { + // PROFILE_MASK was introduced in OpenGL 3.2. + // Profiles: CONTEXT_CORE_PROFILE_BIT 0x1, CONTEXT_COMPATIBILITY_PROFILE_BIT 0x2. + glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &contextProfile); + if (glGetError() == GL_NO_ERROR) { + // >= 3.2 + if (majorVersion == 3 && minorVersion < 3) + supportsSwizzle = GL_FALSE; + if ((contextProfile & GL_CONTEXT_CORE_PROFILE_BIT)) + sizedFormats &= ~_LEGACY_FORMATS; + if (majorVersion >= 4) + supportsCubeMapArrays = GL_TRUE; + } else { + // < 3.2 + contextProfile = GL_CONTEXT_COMPATIBILITY_PROFILE_BIT; + supportsSwizzle = GL_FALSE; + // sRGB textures introduced in 2.0 + if (majorVersion < 2 && hasExtension("GL_EXT_texture_sRGB")) { + supportsSRGB = GL_FALSE; + } + // R{,G]16 introduced in 3.0; R{,G}16_SNORM introduced in 3.1. + if (majorVersion == 3) { + if (minorVersion == 0) + R16Formats &= ~_KTX_R16_FORMATS_SNORM; + } else if (strstr(glGetString(GL_EXTENSIONS), "GL_ARB_texture_rg") != NULL) { + R16Formats &= ~_KTX_R16_FORMATS_SNORM; + } else { + R16Formats = _KTX_NO_R16_FORMATS; + } + } + if (!supportsCubeMapArrays) { + if (hasExtension("GL_ARB_texture_cube_map_array")) { + supportsCubeMapArrays = GL_TRUE; + } + } + } +} + +#if SUPPORT_LEGACY_FORMAT_CONVERSION +/** + * @internal + * @~English + * @brief Convert deprecated legacy-format texture to modern format. + * + * The function sets the GL_TEXTURE_SWIZZLEs necessary to get the same + * behavior as the legacy format. + * + * @param [in] target texture target on which the swizzle will + * be set. + * @param [in, out] pFormat pointer to variable holding the base format of the + * texture. The new base format is written here. + * @param [in, out] pInternalFormat pointer to variable holding the internalformat + * of the texture. The new internalformat is + * written here. + * @return void unrecognized formats will be passed on to OpenGL. Any loading error + * that arises will be handled in the usual way. + */ +static void convertFormat(GLenum target, GLenum* pFormat, GLenum* pInternalFormat) { + switch (*pFormat) { + case GL_ALPHA: + { + GLint swizzle[] = {GL_ZERO, GL_ZERO, GL_ZERO, GL_RED}; + *pFormat = GL_RED; + glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, swizzle); + switch (*pInternalFormat) { + case GL_ALPHA: + case GL_ALPHA4: + case GL_ALPHA8: + *pInternalFormat = GL_R8; + break; + case GL_ALPHA12: + case GL_ALPHA16: + *pInternalFormat = GL_R16; + break; + } + } + case GL_LUMINANCE: + { + GLint swizzle[] = {GL_RED, GL_RED, GL_RED, GL_ONE}; + *pFormat = GL_RED; + glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, swizzle); + switch (*pInternalFormat) { + case GL_LUMINANCE: + case GL_LUMINANCE4: + case GL_LUMINANCE8: + *pInternalFormat = GL_R8; + break; + case GL_LUMINANCE12: + case GL_LUMINANCE16: + *pInternalFormat = GL_R16; + break; +#if 0 + // XXX Must avoid setting TEXTURE_SWIZZLE in these cases + // XXX Must manually swizzle. + case GL_SLUMINANCE: + case GL_SLUMINANCE8: + *pInternalFormat = GL_SRGB8; + break; +#endif + } + break; + } + case GL_LUMINANCE_ALPHA: + { + GLint swizzle[] = {GL_RED, GL_RED, GL_RED, GL_GREEN}; + *pFormat = GL_RG; + glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, swizzle); + switch (*pInternalFormat) { + case GL_LUMINANCE_ALPHA: + case GL_LUMINANCE4_ALPHA4: + case GL_LUMINANCE6_ALPHA2: + case GL_LUMINANCE8_ALPHA8: + *pInternalFormat = GL_RG8; + break; + case GL_LUMINANCE12_ALPHA4: + case GL_LUMINANCE12_ALPHA12: + case GL_LUMINANCE16_ALPHA16: + *pInternalFormat = GL_RG16; + break; +#if 0 + // XXX Must avoid setting TEXTURE_SWIZZLE in these cases + // XXX Must manually swizzle. + case GL_SLUMINANCE_ALPHA: + case GL_SLUMINANCE8_ALPHA8: + *pInternalFormat = GL_SRGB8_ALPHA8; + break; +#endif + } + break; + } + case GL_INTENSITY: + { + GLint swizzle[] = {GL_RED, GL_RED, GL_RED, GL_RED}; + *pFormat = GL_RED; + glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, swizzle); + switch (*pInternalFormat) { + case GL_INTENSITY: + case GL_INTENSITY4: + case GL_INTENSITY8: + *pInternalFormat = GL_R8; + break; + case GL_INTENSITY12: + case GL_INTENSITY16: + *pInternalFormat = GL_R16; + break; + } + break; + } + default: + break; + } +} +#endif /* SUPPORT_LEGACY_FORMAT_CONVERSION */ + + +/* + * @~English + * @brief Load a GL texture object from a ktxStream. + * + * The function sets the texture object's GL_TEXTURE_MAX_LEVEL parameter + * according to the number of levels in the ktxStream, provided the library + * has been compiled with a version of gl.h where GL_TEXTURE_MAX_LEVEL is + * defined. + * + * It will unpack compressed GL_ETC1_RGB8_OES and GL_ETC2_* format + * textures in software when the format is not supported by the GL context, + * provided the library has been compiled with SUPPORT_SOFTWARE_ETC_UNPACK + * defined as 1. + * + * It will also convert textures with legacy formats to their modern equivalents + * when the format is not supported by the GL context, provided the library + * has been compiled with SUPPORT_LEGACY_FORMAT_CONVERSION defined as 1. + * + * @param [in] stream pointer to the ktxStream from which to load. + * @param [in,out] pTexture name of the GL texture to load. If NULL or if + * *pTexture == 0 the function will generate + * a texture name. The function binds either the + * generated name or the name given in @p *pTexture + * to the texture target returned in @p *pTarget, + * before loading the texture data. If @p pTexture + * is not NULL and a name was generated, the generated + * name will be returned in *pTexture. + * @param [out] pTarget @p *pTarget is set to the texture target used. The + * target is chosen based on the file contents. + * @param [out] pDimensions If @p pDimensions is not NULL, the width, height and + * depth of the texture's base level are returned in the + * fields of the KTX_dimensions structure to which it points. + * @param [out] pIsMipmapped + * If @p pIsMipmapped is not NULL, @p *pIsMipmapped is set + * to GL_TRUE if the KTX texture is mipmapped, GL_FALSE + * otherwise. + * @param [out] pGlerror @p *pGlerror is set to the value returned by + * glGetError when this function returns the error + * KTX_GL_ERROR. glerror can be NULL. + * @param [in,out] pKvdLen If not NULL, @p *pKvdLen is set to the number of bytes + * of key-value data pointed at by @p *ppKvd. Must not be + * NULL, if @p ppKvd is not NULL. + * @param [in,out] ppKvd If not NULL, @p *ppKvd is set to the point to a block of + * memory containing key-value data read from the file. + * The application is responsible for freeing the memory. + * + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE @p target is @c NULL or the size of a mip + * level is greater than the size of the + * preceding level. + * @exception KTX_INVALID_OPERATION @p ppKvd is not NULL but pKvdLen is NULL. + * @exception KTX_UNEXPECTED_END_OF_FILE the file does not contain the + * expected amount of data. + * @exception KTX_OUT_OF_MEMORY Sufficient memory could not be allocated to store + * the requested key-value data. + * @exception KTX_GL_ERROR A GL error was raised by glBindTexture, + * glGenTextures or gl*TexImage*. The GL error + * will be returned in @p *glerror, if glerror + * is not @c NULL. + */ +static +KTX_error_code +ktxLoadTextureS(struct ktxStream* stream, GLuint* pTexture, GLenum* pTarget, + KTX_dimensions* pDimensions, GLboolean* pIsMipmapped, + GLenum* pGlerror, + unsigned int* pKvdLen, unsigned char** ppKvd) +{ + GLint previousUnpackAlignment; + KTX_header header; + KTX_texinfo texinfo; + void* data = NULL; + khronos_uint32_t dataSize = 0; + GLuint texname; + int texnameUser; + khronos_uint32_t faceLodSize; + khronos_uint32_t faceLodSizeRounded; + khronos_uint32_t level; + khronos_uint32_t face; + GLenum glFormat, glInternalFormat; + KTX_error_code errorCode = KTX_SUCCESS; + GLenum errorTmp; + + if (pGlerror) + *pGlerror = GL_NO_ERROR; + + if (ppKvd) { + *ppKvd = NULL; + } + + if (!stream || !stream->read || !stream->skip) { + return KTX_INVALID_VALUE; + } + + if (!pTarget) { + return KTX_INVALID_VALUE; + } + + errorCode = stream->read(&header, KTX_HEADER_SIZE, stream->src); + if (errorCode != KTX_SUCCESS) + return errorCode; + + errorCode = _ktxCheckHeader(&header, &texinfo); + if (errorCode != KTX_SUCCESS) + return errorCode; + + //if (ppKvd) { + // if (pKvdLen == NULL) + // return KTX_INVALID_OPERATION; + // *pKvdLen = header.bytesOfKeyValueData; + // if (*pKvdLen) { + // *ppKvd = (unsigned char*)malloc(*pKvdLen); + // if (*ppKvd == NULL) + // return KTX_OUT_OF_MEMORY; + // errorCode = stream->read(*ppKvd, *pKvdLen, stream->src); + // if (errorCode != KTX_SUCCESS) + // { + // free(*ppKvd); + // *ppKvd = NULL; + // + // return errorCode; + // } + // } + //} else { + /* skip key/value metadata */ + errorCode = stream->skip(header.bytesOfKeyValueData, stream->src); + if (errorCode != KTX_SUCCESS) { + return errorCode; + } + //} + + if (contextProfile == 0) + discoverContextCapabilities(); + + /* KTX files require an unpack alignment of 4 */ + glGetIntegerv(GL_UNPACK_ALIGNMENT, &previousUnpackAlignment); + if (previousUnpackAlignment != KTX_GL_UNPACK_ALIGNMENT) { + glPixelStorei(GL_UNPACK_ALIGNMENT, KTX_GL_UNPACK_ALIGNMENT); + } + + texnameUser = pTexture && *pTexture; + if (texnameUser) { + texname = *pTexture; + } else { + glGenTextures(1, &texname); + } + glBindTexture(texinfo.glTarget, texname); + + /* load as 2D texture if 1D textures are not supported */ + if (texinfo.textureDimensions == 1 && + ((texinfo.compressed && (pfGlCompressedTexImage1D == NULL)) || + (!texinfo.compressed && (pfGlTexImage1D == NULL)))) + { + texinfo.textureDimensions = 2; + texinfo.glTarget = GL_TEXTURE_2D; + header.pixelHeight = 1; + } + + if (header.numberOfArrayElements > 0) + { + if (texinfo.glTarget == GL_TEXTURE_1D) + { + texinfo.glTarget = GL_TEXTURE_1D_ARRAY_EXT; + } + else if (texinfo.glTarget == GL_TEXTURE_2D) + { + texinfo.glTarget = GL_TEXTURE_2D_ARRAY_EXT; + } + else if (texinfo.glTarget == GL_TEXTURE_CUBE_MAP) + { + texinfo.glTarget = GL_TEXTURE_CUBE_MAP_ARRAY; + } + else + { + /* No API for 3D arrays yet */ + return KTX_UNSUPPORTED_TEXTURE_TYPE; + } + texinfo.textureDimensions++; + } + + /* Reject cube map arrays if unsupported. */ + if (texinfo.glTarget == GL_TEXTURE_CUBE_MAP_ARRAY && !supportsCubeMapArrays) + { + return KTX_UNSUPPORTED_TEXTURE_TYPE; + } + + /* Reject 3D texture if unsupported. */ + if (texinfo.textureDimensions == 3 && + ((texinfo.compressed && (pfGlCompressedTexImage3D == NULL)) || + (!texinfo.compressed && (pfGlTexImage3D == NULL)))) + { + return KTX_UNSUPPORTED_TEXTURE_TYPE; + } + + // Prefer glGenerateMipmaps over GL_GENERATE_MIPMAP + if (texinfo.generateMipmaps && (pfGlGenerateMipmap == NULL)) { + glTexParameteri(texinfo.glTarget, GL_GENERATE_MIPMAP, GL_TRUE); + } +#ifdef GL_TEXTURE_MAX_LEVEL + if (!texinfo.generateMipmaps) + glTexParameteri(texinfo.glTarget, GL_TEXTURE_MAX_LEVEL, header.numberOfMipmapLevels - 1); +#endif + + if (texinfo.glTarget == GL_TEXTURE_CUBE_MAP) { + texinfo.glTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X; + } + + glInternalFormat = header.glInternalFormat; + glFormat = header.glFormat; + if (!texinfo.compressed) { +#if SUPPORT_LEGACY_FORMAT_CONVERSION + // If sized legacy formats are supported there is no need to convert. + // If only unsized formats are supported, there is no point in converting + // as the modern formats aren't supported either. + if (sizedFormats == _NON_LEGACY_FORMATS && supportsSwizzle) { + convertFormat(texinfo.glTarget, &glFormat, &glInternalFormat); + errorTmp = glGetError(); + } else if (sizedFormats == _NO_SIZED_FORMATS) + glInternalFormat = header.glBaseInternalFormat; +#else + // When no sized formats are supported, or legacy sized formats are not + // supported, must change internal format. + if (sizedFormats == _NO_SIZED_FORMATS + || (!(sizedFormats & _LEGACY_FORMATS) && + (header.glBaseInternalFormat == GL_ALPHA + || header.glBaseInternalFormat == GL_LUMINANCE + || header.glBaseInternalFormat == GL_LUMINANCE_ALPHA + || header.glBaseInternalFormat == GL_INTENSITY))) { + glInternalFormat = header.glBaseInternalFormat; + } +#endif + } + + for (level = 0; level < header.numberOfMipmapLevels; ++level) + { + GLsizei pixelWidth = MAX(1, header.pixelWidth >> level); + GLsizei pixelHeight = MAX(1, header.pixelHeight >> level); + GLsizei pixelDepth = MAX(1, header.pixelDepth >> level); + + errorCode = stream->read(&faceLodSize, sizeof(khronos_uint32_t), stream->src); + if (errorCode != KTX_SUCCESS) { + goto cleanup; + } + if (header.endianness == KTX_ENDIAN_REF_REV) { + _ktxSwapEndian32(&faceLodSize, 1); + } + faceLodSizeRounded = (faceLodSize + 3) & ~(khronos_uint32_t)3; + if (!data) { + /* allocate memory sufficient for the first level */ + data = malloc(faceLodSizeRounded); + if (!data) { + errorCode = KTX_OUT_OF_MEMORY; + goto cleanup; + } + dataSize = faceLodSizeRounded; + } + else if (dataSize < faceLodSizeRounded) { + /* subsequent levels cannot be larger than the first level */ + errorCode = KTX_INVALID_VALUE; + goto cleanup; + } + + for (face = 0; face < header.numberOfFaces; ++face) + { + errorCode = stream->read(data, faceLodSizeRounded, stream->src); + if (errorCode != KTX_SUCCESS) { + goto cleanup; + } + + /* Perform endianness conversion on texture data */ + if (header.endianness == KTX_ENDIAN_REF_REV && header.glTypeSize == 2) { + _ktxSwapEndian16((khronos_uint16_t*)data, faceLodSize / 2); + } + else if (header.endianness == KTX_ENDIAN_REF_REV && header.glTypeSize == 4) { + _ktxSwapEndian32((khronos_uint32_t*)data, faceLodSize / 4); + } + + if (texinfo.textureDimensions == 1) { + assert(pfGlCompressedTexImage1D != NULL && pfGlTexImage1D != NULL); + if (texinfo.compressed) { + pfGlCompressedTexImage1D(texinfo.glTarget + face, level, + glInternalFormat, pixelWidth, 0, + faceLodSize, data); + } else { + pfGlTexImage1D(texinfo.glTarget + face, level, + glInternalFormat, pixelWidth, 0, + glFormat, header.glType, data); + } + } else if (texinfo.textureDimensions == 2) { + if (header.numberOfArrayElements) { + pixelHeight = header.numberOfArrayElements; + } + if (texinfo.compressed) { + // It is simpler to just attempt to load the format, rather than divine which + // formats are supported by the implementation. In the event of an error, + // software unpacking can be attempted. + glCompressedTexImage2D(texinfo.glTarget + face, level, + glInternalFormat, pixelWidth, pixelHeight, 0, + faceLodSize, data); + } else { + errorTmp = glGetError(); + glTexImage2D(texinfo.glTarget + face, level, + glInternalFormat, pixelWidth, pixelHeight, 0, + glFormat, header.glType, data); + } + } else if (texinfo.textureDimensions == 3) { + assert(pfGlCompressedTexImage3D != NULL && pfGlTexImage3D != NULL); + if (header.numberOfArrayElements) { + pixelDepth = header.numberOfArrayElements; + } + if (texinfo.compressed) { + pfGlCompressedTexImage3D(texinfo.glTarget + face, level, + glInternalFormat, pixelWidth, pixelHeight, pixelDepth, 0, + faceLodSize, data); + } else { + pfGlTexImage3D(texinfo.glTarget + face, level, + glInternalFormat, pixelWidth, pixelHeight, pixelDepth, 0, + glFormat, header.glType, data); + } + } + + errorTmp = glGetError(); +#if SUPPORT_SOFTWARE_ETC_UNPACK + // Renderion is returning INVALID_VALUE. Oops!! + if ((errorTmp == GL_INVALID_ENUM || errorTmp == GL_INVALID_VALUE) + && texinfo.compressed + && texinfo.textureDimensions == 2 + && (glInternalFormat == GL_ETC1_RGB8_OES || (glInternalFormat >= GL_COMPRESSED_R11_EAC && glInternalFormat <= GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC))) + { + GLubyte* unpacked; + GLenum format, internalFormat, type; + + errorCode = _ktxUnpackETC((GLubyte*)data, glInternalFormat, pixelWidth, pixelHeight, + &unpacked, &format, &internalFormat, &type, + R16Formats, supportsSRGB); + if (errorCode != KTX_SUCCESS) { + goto cleanup; + } + if (!sizedFormats & _NON_LEGACY_FORMATS) { + if (internalFormat == GL_RGB8) + internalFormat = GL_RGB; + else if (internalFormat == GL_RGBA8) + internalFormat = GL_RGBA; + } + glTexImage2D(texinfo.glTarget + face, level, + internalFormat, pixelWidth, pixelHeight, 0, + format, type, unpacked); + + free(unpacked); + errorTmp = glGetError(); + } +#endif + if (errorTmp != GL_NO_ERROR) { + if (pGlerror) + *pGlerror = errorTmp; + errorCode = KTX_GL_ERROR; + goto cleanup; + } + } + } + +cleanup: + free(data); + + /* restore previous GL state */ + if (previousUnpackAlignment != KTX_GL_UNPACK_ALIGNMENT) { + glPixelStorei(GL_UNPACK_ALIGNMENT, previousUnpackAlignment); + } + + if (errorCode == KTX_SUCCESS) + { + if (texinfo.generateMipmaps && pfGlGenerateMipmap) { + pfGlGenerateMipmap(texinfo.glTarget); + } + *pTarget = texinfo.glTarget; + if (pTexture) { + *pTexture = texname; + } + if (pDimensions) { + pDimensions->width = header.pixelWidth; + pDimensions->height = header.pixelHeight; + pDimensions->depth = header.pixelDepth; + } + if (pIsMipmapped) { + if (texinfo.generateMipmaps || header.numberOfMipmapLevels > 1) + *pIsMipmapped = GL_TRUE; + else + *pIsMipmapped = GL_FALSE; + } + } else { + if (ppKvd && *ppKvd) + { + free(*ppKvd); + *ppKvd = NULL; + } + + if (!texnameUser) { + glDeleteTextures(1, &texname); + } + } + return errorCode; +} + +/** + * @~English + * @brief Load a GL texture object from a stdio FILE stream. + * + * The function sets the texture object's GL_TEXTURE_MAX_LEVEL parameter + * according to the number of levels in the ktxStream, provided the library + * has been compiled with a version of gl.h where GL_TEXTURE_MAX_LEVEL is + * defined. + * + * It will unpack compressed GL_ETC1_RGB8_OES and GL_ETC2_* format + * textures in software when the format is not supported by the GL context, + * provided the library has been compiled with SUPPORT_SOFTWARE_ETC_UNPACK + * defined as 1. + * + * It will also convert texture with legacy formats to their modern equivalents + * when the format is not supported by the GL context, provided the library + * has been compiled with SUPPORT_LEGACY_FORMAT_CONVERSION defined as 1. + * + * @param [in] file pointer to the stdio FILE stream from which to + * load. + * @param [in,out] pTexture name of the GL texture to load. If NULL or if + * *pTexture == 0 the function will generate + * a texture name. The function binds either the + * generated name or the name given in @p *pTexture + * to the texture target returned in @p *pTarget, + * before loading the texture data. If @p pTexture + * is not NULL and a name was generated, the generated + * name will be returned in *pTexture. + * @param [out] pTarget @p *pTarget is set to the texture target used. The + * target is chosen based on the file contents. + * @param [out] pDimensions If @p pDimensions is not NULL, the width, height and + * depth of the texture's base level are returned in the + * fields of the KTX_dimensions structure to which it points. + * @param [out] pIsMipmapped + * If @p pIsMipmapped is not NULL, @p *pIsMipmapped is set + * to GL_TRUE if the KTX texture is mipmapped, GL_FALSE + * otherwise. + * @param [out] pGlerror @p *pGlerror is set to the value returned by + * glGetError when this function returns the error + * KTX_GL_ERROR. glerror can be NULL. + * @param [in,out] pKvdLen If not NULL, @p *pKvdLen is set to the number of bytes + * of key-value data pointed at by @p *ppKvd. Must not be + * NULL, if @p ppKvd is not NULL. + * @param [in,out] ppKvd If not NULL, @p *ppKvd is set to the point to a block of + * memory containing key-value data read from the file. + * The application is responsible for freeing the memory. + * + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE @p target is @c NULL or the size of a mip + * level is greater than the size of the + * preceding level. + * @exception KTX_INVALID_OPERATION @p ppKvd is not NULL but pKvdLen is NULL. + * @exception KTX_UNEXPECTED_END_OF_FILE the file does not contain the + * expected amount of data. + * @exception KTX_OUT_OF_MEMORY Sufficient memory could not be allocated to store + * the requested key-value data. + * @exception KTX_GL_ERROR A GL error was raised by glBindTexture, + * glGenTextures or gl*TexImage*. The GL error + * will be returned in @p *glerror, if glerror + * is not @c NULL. + */ +KTX_error_code +ktxLoadTextureF(FILE* file, GLuint* pTexture, GLenum* pTarget, + KTX_dimensions* pDimensions, GLboolean* pIsMipmapped, + GLenum* pGlerror, + unsigned int* pKvdLen, unsigned char** ppKvd) +{ + struct ktxStream stream; + KTX_error_code errorCode = KTX_SUCCESS; + + errorCode = ktxFileInit(&stream, file); + if (errorCode != KTX_SUCCESS) + return errorCode; + + return ktxLoadTextureS(&stream, pTexture, pTarget, pDimensions, pIsMipmapped, pGlerror, pKvdLen, ppKvd); +} + +/** + * @~English + * @brief Load a GL texture object from a named file on disk. + * + * @param [in] filename pointer to a C string that contains the path of + * the file to load. + * @param [in,out] pTexture name of the GL texture to load. See + * ktxLoadTextureF() for details. + * @param [out] pTarget @p *pTarget is set to the texture target used. See + * ktxLoadTextureF() for details. + * @param [out] pDimensions @p the texture's base level width depth and height + * are returned in structure to which this points. + * See ktxLoadTextureF() for details. + * @param [out] pIsMipmapped @p pIsMipMapped is set to indicate if the loaded + * texture is mipmapped. See ktxLoadTextureF() for + * details. + * @param [out] pGlerror @p *pGlerror is set to the value returned by + * glGetError when this function returns the error + * KTX_GL_ERROR. glerror can be NULL. + * @param [in,out] pKvdLen If not NULL, @p *pKvdLen is set to the number of bytes + * of key-value data pointed at by @p *ppKvd. Must not be + * NULL, if @p ppKvd is not NULL. + * @param [in,out] ppKvd If not NULL, @p *ppKvd is set to the point to a block of + * memory containing key-value data read from the file. + * The application is responsible for freeing the memory.* + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_FILE_OPEN_FAILED The specified file could not be opened. + * @exception KTX_INVALID_VALUE See ktxLoadTextureF() for causes. + * @exception KTX_INVALID_OPERATION See ktxLoadTextureF() for causes. + * @exception KTX_UNEXPECTED_END_OF_FILE See ktxLoadTextureF() for causes. + * + * @exception KTX_GL_ERROR See ktxLoadTextureF() for causes. + */ +KTX_error_code +ktxLoadTextureN(const char* const filename, GLuint* pTexture, GLenum* pTarget, + KTX_dimensions* pDimensions, GLboolean* pIsMipmapped, + GLenum* pGlerror, + unsigned int* pKvdLen, unsigned char** ppKvd) +{ + KTX_error_code errorCode; + FILE* file = fopen(filename, "rb"); + + if (file) { + errorCode = ktxLoadTextureF(file, pTexture, pTarget, pDimensions, + pIsMipmapped, pGlerror, pKvdLen, ppKvd); + fclose(file); + } else + errorCode = KTX_FILE_OPEN_FAILED; + + return errorCode; +} + +/** + * @~English + * @brief Load a GL texture object from KTX formatted data in memory. + * + * @param [in] bytes pointer to the array of bytes containing + * the KTX format data to load. + * @param [in] size size of the memory array containing the + * KTX format data. + * @param [in,out] pTexture name of the GL texture to load. See + * ktxLoadTextureF() for details. + * @param [out] pTarget @p *pTarget is set to the texture target used. See + * ktxLoadTextureF() for details. + * @param [out] pDimensions @p the texture's base level width depth and height + * are returned in structure to which this points. + * See ktxLoadTextureF() for details. + * @param [out] pIsMipmapped @p *pIsMipMapped is set to indicate if the loaded + * texture is mipmapped. See ktxLoadTextureF() for + * details. + * @param [out] pGlerror @p *pGlerror is set to the value returned by + * glGetError when this function returns the error + * KTX_GL_ERROR. glerror can be NULL. + * @param [in,out] pKvdLen If not NULL, @p *pKvdLen is set to the number of bytes + * of key-value data pointed at by @p *ppKvd. Must not be + * NULL, if @p ppKvd is not NULL. + * @param [in,out] ppKvd If not NULL, @p *ppKvd is set to the point to a block of + * memory containing key-value data read from the file. + * The application is responsible for freeing the memory.* + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_FILE_OPEN_FAILED The specified memory could not be opened as a file. + * @exception KTX_INVALID_VALUE See ktxLoadTextureF() for causes. + * @exception KTX_INVALID_OPERATION See ktxLoadTextureF() for causes. + * @exception KTX_UNEXPECTED_END_OF_FILE See ktxLoadTextureF() for causes. + * + * @exception KTX_GL_ERROR See ktxLoadTextureF() for causes. + */ +KTX_error_code +ktxLoadTextureM(const void* bytes, GLsizei size, GLuint* pTexture, GLenum* pTarget, + KTX_dimensions* pDimensions, GLboolean* pIsMipmapped, + GLenum* pGlerror, + unsigned int* pKvdLen, unsigned char** ppKvd) +{ + struct ktxMem mem; + struct ktxStream stream; + KTX_error_code errorCode = KTX_SUCCESS; + + errorCode = ktxMemInit(&stream, &mem, bytes, size); + if (errorCode != KTX_SUCCESS) + return errorCode; + + return ktxLoadTextureS(&stream, pTexture, pTarget, pDimensions, pIsMipmapped, pGlerror, pKvdLen, ppKvd); +} + + diff --git a/applications/_plugins/cimage/ktx/lib/swap.c b/applications/_plugins/cimage/ktx/lib/swap.c new file mode 100644 index 000000000..7360e522f --- /dev/null +++ b/applications/_plugins/cimage/ktx/lib/swap.c @@ -0,0 +1,65 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4: */ + +/* $Id: 2319952fa702d4c9a8e3c02868541b1d7cff9aa0 $ */ + +/* +Copyright (c) 2010 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +unaltered in all copies or substantial portions of the Materials. +Any additions, deletions, or changes to the original source files +must be clearly indicated in accompanying documentation. + +If only executable code is distributed, then the accompanying +documentation must state that "this software is based in part on the +work of the Khronos Group." + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +#include "KHR/khrplatform.h" + +/* + * SwapEndian16: Swaps endianness in an array of 16-bit values + */ +void +_ktxSwapEndian16(khronos_uint16_t* pData16, int count) +{ + int i; + for (i = 0; i < count; ++i) + { + khronos_uint16_t x = *pData16; + *pData16++ = (x << 8) | (x >> 8); + } +} + +/* + * SwapEndian32: Swaps endianness in an array of 32-bit values + */ +void +_ktxSwapEndian32(khronos_uint32_t* pData32, int count) +{ + int i; + for (i = 0; i < count; ++i) + { + khronos_uint32_t x = *pData32; + *pData32++ = (x << 24) | ((x & 0xFF00) << 8) | ((x & 0xFF0000) >> 8) | (x >> 24); + } +} + + diff --git a/applications/_plugins/cimage/ktx/lib/uthash.h b/applications/_plugins/cimage/ktx/lib/uthash.h new file mode 100644 index 000000000..706c3895d --- /dev/null +++ b/applications/_plugins/cimage/ktx/lib/uthash.h @@ -0,0 +1,960 @@ +/* +Copyright (c) 2003-2010, Troy D. Hanson http://uthash.sourceforge.net +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UTHASH_H +#define UTHASH_H + +#include /* memcmp,strlen */ +#include /* ptrdiff_t */ + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ source) this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#ifdef _MSC_VER /* MS compiler */ +#if _MSC_VER >= 1600 && __cplusplus /* VS2010 or newer in C++ mode */ +#define DECLTYPE(x) (decltype(x)) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#define DECLTYPE(x) +#endif +#else /* GNU, Sun and other compilers */ +#define DECLTYPE(x) (__typeof(x)) +#endif + +#ifdef NO_DECLTYPE +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + char **_da_dst = (char**)(&(dst)); \ + *_da_dst = (char*)(src); \ +} while(0) +#else +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + (dst) = DECLTYPE(dst)(src); \ +} while(0) +#endif + +/* a number of the hash function use uint32_t which isn't defined on win32 */ +#ifdef _MSC_VER +typedef unsigned int uint32_t; +#else +#include /* uint32_t */ +#endif + +#define UTHASH_VERSION 1.9.1 + +#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */ +#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ +#define uthash_free(ptr) free(ptr) /* free fcn */ + +#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ +#define uthash_expand_fyi(tbl) /* can be defined to log expands */ + +/* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS 32 /* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS_LOG2 5 /* lg2 of initial number of buckets */ +#define HASH_BKT_CAPACITY_THRESH 10 /* expand when bucket count reaches */ + +/* calculate the element whose hash handle address is hhe */ +#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) + +#define HASH_FIND(hh,head,keyptr,keylen,out) \ +do { \ + unsigned _hf_bkt,_hf_hashv; \ + out=NULL; \ + if (head) { \ + HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \ + if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv)) { \ + HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \ + keyptr,keylen,out); \ + } \ + } \ +} while (0) + +#ifdef HASH_BLOOM +#define HASH_BLOOM_BITLEN (1ULL << HASH_BLOOM) +#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8) + ((HASH_BLOOM_BITLEN%8) ? 1:0) +#define HASH_BLOOM_MAKE(tbl) \ +do { \ + (tbl)->bloom_nbits = HASH_BLOOM; \ + (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ + if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \ + memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \ + (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ +} while (0); + +#define HASH_BLOOM_FREE(tbl) \ +do { \ + uthash_free((tbl)->bloom_bv); \ +} while (0); + +#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8))) +#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8))) + +#define HASH_BLOOM_ADD(tbl,hashv) \ + HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) + +#define HASH_BLOOM_TEST(tbl,hashv) \ + HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) + +#else +#define HASH_BLOOM_MAKE(tbl) +#define HASH_BLOOM_FREE(tbl) +#define HASH_BLOOM_ADD(tbl,hashv) +#define HASH_BLOOM_TEST(tbl,hashv) (1) +#endif + +#define HASH_MAKE_TABLE(hh,head) \ +do { \ + (head)->hh.tbl = (UT_hash_table*)uthash_malloc( \ + sizeof(UT_hash_table)); \ + if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \ + memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \ + (head)->hh.tbl->tail = &((head)->hh); \ + (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ + (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ + (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ + (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \ + memset((head)->hh.tbl->buckets, 0, \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_MAKE((head)->hh.tbl); \ + (head)->hh.tbl->signature = HASH_SIGNATURE; \ +} while(0) + +#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ + HASH_ADD_KEYPTR(hh,head,&add->fieldname,keylen_in,add) + +#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ +do { \ + unsigned _ha_bkt; \ + (add)->hh.next = NULL; \ + (add)->hh.key = (char*)keyptr; \ + (add)->hh.keylen = keylen_in; \ + if (!(head)) { \ + head = (add); \ + (head)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh,head); \ + } else { \ + (head)->hh.tbl->tail->next = (add); \ + (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ + (head)->hh.tbl->tail = &((add)->hh); \ + } \ + (head)->hh.tbl->num_items++; \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \ + (add)->hh.hashv, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \ + HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv); \ + HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \ + HASH_FSCK(hh,head); \ +} while(0) + +#define HASH_TO_BKT( hashv, num_bkts, bkt ) \ +do { \ + bkt = ((hashv) & ((num_bkts) - 1)); \ +} while(0) + +/* delete "delptr" from the hash table. + * "the usual" patch-up process for the app-order doubly-linked-list. + * The use of _hd_hh_del below deserves special explanation. + * These used to be expressed using (delptr) but that led to a bug + * if someone used the same symbol for the head and deletee, like + * HASH_DELETE(hh,users,users); + * We want that to work, but by changing the head (users) below + * we were forfeiting our ability to further refer to the deletee (users) + * in the patch-up process. Solution: use scratch space to + * copy the deletee pointer, then the latter references are via that + * scratch pointer rather than through the repointed (users) symbol. + */ +#define HASH_DELETE(hh,head,delptr) \ +do { \ + unsigned _hd_bkt; \ + struct UT_hash_handle *_hd_hh_del; \ + if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \ + uthash_free((head)->hh.tbl->buckets ); \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl); \ + head = NULL; \ + } else { \ + _hd_hh_del = &((delptr)->hh); \ + if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \ + (head)->hh.tbl->tail = \ + (UT_hash_handle*)((char*)((delptr)->hh.prev) + \ + (head)->hh.tbl->hho); \ + } \ + if ((delptr)->hh.prev) { \ + ((UT_hash_handle*)((char*)((delptr)->hh.prev) + \ + (head)->hh.tbl->hho))->next = (delptr)->hh.next; \ + } else { \ + DECLTYPE_ASSIGN(head,(delptr)->hh.next); \ + } \ + if (_hd_hh_del->next) { \ + ((UT_hash_handle*)((char*)_hd_hh_del->next + \ + (head)->hh.tbl->hho))->prev = \ + _hd_hh_del->prev; \ + } \ + HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ + (head)->hh.tbl->num_items--; \ + } \ + HASH_FSCK(hh,head); \ +} while (0) + + +/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ +#define HASH_FIND_STR(head,findstr,out) \ + HASH_FIND(hh,head,findstr,strlen(findstr),out) +#define HASH_ADD_STR(head,strfield,add) \ + HASH_ADD(hh,head,strfield,strlen(add->strfield),add) +#define HASH_FIND_INT(head,findint,out) \ + HASH_FIND(hh,head,findint,sizeof(int),out) +#define HASH_ADD_INT(head,intfield,add) \ + HASH_ADD(hh,head,intfield,sizeof(int),add) +#define HASH_FIND_PTR(head,findptr,out) \ + HASH_FIND(hh,head,findptr,sizeof(void *),out) +#define HASH_ADD_PTR(head,ptrfield,add) \ + HASH_ADD(hh,head,ptrfield,sizeof(void *),add) +#define HASH_DEL(head,delptr) \ + HASH_DELETE(hh,head,delptr) + +/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. + * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. + */ +#ifdef HASH_DEBUG +#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0) +#define HASH_FSCK(hh,head) \ +do { \ + unsigned _bkt_i; \ + unsigned _count, _bkt_count; \ + char *_prev; \ + struct UT_hash_handle *_thh; \ + if (head) { \ + _count = 0; \ + for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \ + _bkt_count = 0; \ + _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ + _prev = NULL; \ + while (_thh) { \ + if (_prev != (char*)(_thh->hh_prev)) { \ + HASH_OOPS("invalid hh_prev %p, actual %p\n", \ + _thh->hh_prev, _prev ); \ + } \ + _bkt_count++; \ + _prev = (char*)(_thh); \ + _thh = _thh->hh_next; \ + } \ + _count += _bkt_count; \ + if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ + HASH_OOPS("invalid bucket count %d, actual %d\n", \ + (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ + } \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("invalid hh item count %d, actual %d\n", \ + (head)->hh.tbl->num_items, _count ); \ + } \ + /* traverse hh in app order; check next/prev integrity, count */ \ + _count = 0; \ + _prev = NULL; \ + _thh = &(head)->hh; \ + while (_thh) { \ + _count++; \ + if (_prev !=(char*)(_thh->prev)) { \ + HASH_OOPS("invalid prev %p, actual %p\n", \ + _thh->prev, _prev ); \ + } \ + _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ + _thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \ + (head)->hh.tbl->hho) : NULL ); \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("invalid app item count %d, actual %d\n", \ + (head)->hh.tbl->num_items, _count ); \ + } \ + } \ +} while (0) +#else +#define HASH_FSCK(hh,head) +#endif + +/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to + * the descriptor to which this macro is defined for tuning the hash function. + * The app can #include to get the prototype for write(2). */ +#ifdef HASH_EMIT_KEYS +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ +do { \ + unsigned _klen = fieldlen; \ + write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ + write(HASH_EMIT_KEYS, keyptr, fieldlen); \ +} while (0) +#else +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) +#endif + +/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ +#ifdef HASH_FUNCTION +#define HASH_FCN HASH_FUNCTION +#else +#define HASH_FCN HASH_JEN +#endif + +/* The Bernstein hash function, used in Perl prior to v5.6 */ +#define HASH_BER(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _hb_keylen=keylen; \ + char *_hb_key=(char*)key; \ + (hashv) = 0; \ + while (_hb_keylen--) { (hashv) = ((hashv) * 33) + *_hb_key++; } \ + bkt = (hashv) & (num_bkts-1); \ +} while (0) + + +/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at + * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ +#define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _sx_i; \ + char *_hs_key=(char*)key; \ + hashv = 0; \ + for(_sx_i=0; _sx_i < keylen; _sx_i++) \ + hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ + bkt = hashv & (num_bkts-1); \ +} while (0) + +#define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _fn_i; \ + char *_hf_key=(char*)key; \ + hashv = 2166136261UL; \ + for(_fn_i=0; _fn_i < keylen; _fn_i++) \ + hashv = (hashv * 16777619) ^ _hf_key[_fn_i]; \ + bkt = hashv & (num_bkts-1); \ +} while(0); + +#define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _ho_i; \ + char *_ho_key=(char*)key; \ + hashv = 0; \ + for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ + hashv += _ho_key[_ho_i]; \ + hashv += (hashv << 10); \ + hashv ^= (hashv >> 6); \ + } \ + hashv += (hashv << 3); \ + hashv ^= (hashv >> 11); \ + hashv += (hashv << 15); \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +#define HASH_JEN_MIX(a,b,c) \ +do { \ + a -= b; a -= c; a ^= ( c >> 13 ); \ + b -= c; b -= a; b ^= ( a << 8 ); \ + c -= a; c -= b; c ^= ( b >> 13 ); \ + a -= b; a -= c; a ^= ( c >> 12 ); \ + b -= c; b -= a; b ^= ( a << 16 ); \ + c -= a; c -= b; c ^= ( b >> 5 ); \ + a -= b; a -= c; a ^= ( c >> 3 ); \ + b -= c; b -= a; b ^= ( a << 10 ); \ + c -= a; c -= b; c ^= ( b >> 15 ); \ +} while (0) + +#define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _hj_i,_hj_j,_hj_k; \ + char *_hj_key=(char*)key; \ + hashv = 0xfeedbeef; \ + _hj_i = _hj_j = 0x9e3779b9; \ + _hj_k = keylen; \ + while (_hj_k >= 12) { \ + _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ + + ( (unsigned)_hj_key[2] << 16 ) \ + + ( (unsigned)_hj_key[3] << 24 ) ); \ + _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ + + ( (unsigned)_hj_key[6] << 16 ) \ + + ( (unsigned)_hj_key[7] << 24 ) ); \ + hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ + + ( (unsigned)_hj_key[10] << 16 ) \ + + ( (unsigned)_hj_key[11] << 24 ) ); \ + \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + \ + _hj_key += 12; \ + _hj_k -= 12; \ + } \ + hashv += keylen; \ + switch ( _hj_k ) { \ + case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); \ + case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); \ + case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); \ + case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); \ + case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); \ + case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); \ + case 5: _hj_j += _hj_key[4]; \ + case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); \ + case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); \ + case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); \ + case 1: _hj_i += _hj_key[0]; \ + } \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +/* The Paul Hsieh hash function */ +#undef get16bits +#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) +#define get16bits(d) (*((const uint16_t *) (d))) +#endif + +#if !defined (get16bits) +#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ + +(uint32_t)(((const uint8_t *)(d))[0]) ) +#endif +#define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \ +do { \ + char *_sfh_key=(char*)key; \ + uint32_t _sfh_tmp, _sfh_len = keylen; \ + \ + int _sfh_rem = _sfh_len & 3; \ + _sfh_len >>= 2; \ + hashv = 0xcafebabe; \ + \ + /* Main loop */ \ + for (;_sfh_len > 0; _sfh_len--) { \ + hashv += get16bits (_sfh_key); \ + _sfh_tmp = (get16bits (_sfh_key+2) << 11) ^ hashv; \ + hashv = (hashv << 16) ^ _sfh_tmp; \ + _sfh_key += 2*sizeof (uint16_t); \ + hashv += hashv >> 11; \ + } \ + \ + /* Handle end cases */ \ + switch (_sfh_rem) { \ + case 3: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 16; \ + hashv ^= _sfh_key[sizeof (uint16_t)] << 18; \ + hashv += hashv >> 11; \ + break; \ + case 2: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 11; \ + hashv += hashv >> 17; \ + break; \ + case 1: hashv += *_sfh_key; \ + hashv ^= hashv << 10; \ + hashv += hashv >> 1; \ + } \ + \ + /* Force "avalanching" of final 127 bits */ \ + hashv ^= hashv << 3; \ + hashv += hashv >> 5; \ + hashv ^= hashv << 4; \ + hashv += hashv >> 17; \ + hashv ^= hashv << 25; \ + hashv += hashv >> 6; \ + bkt = hashv & (num_bkts-1); \ +} while(0); + +#ifdef HASH_USING_NO_STRICT_ALIASING +/* The MurmurHash exploits some CPU's (e.g. x86) tolerance for unaligned reads. + * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error. + * So MurmurHash comes in two versions, the faster unaligned one and the slower + * aligned one. We only use the faster one on CPU's where we know it's safe. + * + * Note the preprocessor built-in defines can be emitted using: + * + * gcc -m64 -dM -E - < /dev/null (on gcc) + * cc -## a.c (where a.c is a simple test file) (Sun Studio) + */ +#if (defined(__i386__) || defined(__x86_64__)) +#define HASH_MUR HASH_MUR_UNALIGNED +#else +#define HASH_MUR HASH_MUR_ALIGNED +#endif + +/* Appleby's MurmurHash fast version for unaligned-tolerant archs like i386 */ +#define HASH_MUR_UNALIGNED(key,keylen,num_bkts,hashv,bkt) \ +do { \ + const unsigned int _mur_m = 0x5bd1e995; \ + const int _mur_r = 24; \ + hashv = 0xcafebabe ^ keylen; \ + char *_mur_key = (char *)key; \ + uint32_t _mur_tmp, _mur_len = keylen; \ + \ + for (;_mur_len >= 4; _mur_len-=4) { \ + _mur_tmp = *(uint32_t *)_mur_key; \ + _mur_tmp *= _mur_m; \ + _mur_tmp ^= _mur_tmp >> _mur_r; \ + _mur_tmp *= _mur_m; \ + hashv *= _mur_m; \ + hashv ^= _mur_tmp; \ + _mur_key += 4; \ + } \ + \ + switch(_mur_len) \ + { \ + case 3: hashv ^= _mur_key[2] << 16; \ + case 2: hashv ^= _mur_key[1] << 8; \ + case 1: hashv ^= _mur_key[0]; \ + hashv *= _mur_m; \ + }; \ + \ + hashv ^= hashv >> 13; \ + hashv *= _mur_m; \ + hashv ^= hashv >> 15; \ + \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +/* Appleby's MurmurHash version for alignment-sensitive archs like Sparc */ +#define HASH_MUR_ALIGNED(key,keylen,num_bkts,hashv,bkt) \ +do { \ + const unsigned int _mur_m = 0x5bd1e995; \ + const int _mur_r = 24; \ + hashv = 0xcafebabe ^ keylen; \ + char *_mur_key = (char *)key; \ + uint32_t _mur_len = keylen; \ + int _mur_align = (int)_mur_key & 3; \ + \ + if (_mur_align && (_mur_len >= 4)) { \ + unsigned _mur_t = 0, _mur_d = 0; \ + switch(_mur_align) { \ + case 1: _mur_t |= _mur_key[2] << 16; \ + case 2: _mur_t |= _mur_key[1] << 8; \ + case 3: _mur_t |= _mur_key[0]; \ + } \ + _mur_t <<= (8 * _mur_align); \ + _mur_key += 4-_mur_align; \ + _mur_len -= 4-_mur_align; \ + int _mur_sl = 8 * (4-_mur_align); \ + int _mur_sr = 8 * _mur_align; \ + \ + for (;_mur_len >= 4; _mur_len-=4) { \ + _mur_d = *(unsigned *)_mur_key; \ + _mur_t = (_mur_t >> _mur_sr) | (_mur_d << _mur_sl); \ + unsigned _mur_k = _mur_t; \ + _mur_k *= _mur_m; \ + _mur_k ^= _mur_k >> _mur_r; \ + _mur_k *= _mur_m; \ + hashv *= _mur_m; \ + hashv ^= _mur_k; \ + _mur_t = _mur_d; \ + _mur_key += 4; \ + } \ + _mur_d = 0; \ + if(_mur_len >= _mur_align) { \ + switch(_mur_align) { \ + case 3: _mur_d |= _mur_key[2] << 16; \ + case 2: _mur_d |= _mur_key[1] << 8; \ + case 1: _mur_d |= _mur_key[0]; \ + } \ + unsigned _mur_k = (_mur_t >> _mur_sr) | (_mur_d << _mur_sl); \ + _mur_k *= _mur_m; \ + _mur_k ^= _mur_k >> _mur_r; \ + _mur_k *= _mur_m; \ + hashv *= _mur_m; \ + hashv ^= _mur_k; \ + _mur_k += _mur_align; \ + _mur_len -= _mur_align; \ + \ + switch(_mur_len) \ + { \ + case 3: hashv ^= _mur_key[2] << 16; \ + case 2: hashv ^= _mur_key[1] << 8; \ + case 1: hashv ^= _mur_key[0]; \ + hashv *= _mur_m; \ + } \ + } else { \ + switch(_mur_len) \ + { \ + case 3: _mur_d ^= _mur_key[2] << 16; \ + case 2: _mur_d ^= _mur_key[1] << 8; \ + case 1: _mur_d ^= _mur_key[0]; \ + case 0: hashv ^= (_mur_t >> _mur_sr) | (_mur_d << _mur_sl); \ + hashv *= _mur_m; \ + } \ + } \ + \ + hashv ^= hashv >> 13; \ + hashv *= _mur_m; \ + hashv ^= hashv >> 15; \ + } else { \ + for (;_mur_len >= 4; _mur_len-=4) { \ + unsigned _mur_k = *(unsigned*)_mur_key; \ + _mur_k *= _mur_m; \ + _mur_k ^= _mur_k >> _mur_r; \ + _mur_k *= _mur_m; \ + hashv *= _mur_m; \ + hashv ^= _mur_k; \ + _mur_key += 4; \ + } \ + switch(_mur_len) \ + { \ + case 3: hashv ^= _mur_key[2] << 16; \ + case 2: hashv ^= _mur_key[1] << 8; \ + case 1: hashv ^= _mur_key[0]; \ + hashv *= _mur_m; \ + } \ + \ + hashv ^= hashv >> 13; \ + hashv *= _mur_m; \ + hashv ^= hashv >> 15; \ + } \ + bkt = hashv & (num_bkts-1); \ +} while(0) +#endif /* HASH_USING_NO_STRICT_ALIASING */ + +/* key comparison function; return 0 if keys equal */ +#define HASH_KEYCMP(a,b,len) memcmp(a,b,len) + +/* iterate over items in a known bucket to find desired item */ +#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \ +do { \ + if (head.hh_head) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,head.hh_head)); \ + else out=NULL; \ + while (out) { \ + if (out->hh.keylen == keylen_in) { \ + if ((HASH_KEYCMP(out->hh.key,keyptr,keylen_in)) == 0) break; \ + } \ + if (out->hh.hh_next) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,out->hh.hh_next)); \ + else out = NULL; \ + } \ +} while(0) + +/* add an item to a bucket */ +#define HASH_ADD_TO_BKT(head,addhh) \ +do { \ + head.count++; \ + (addhh)->hh_next = head.hh_head; \ + (addhh)->hh_prev = NULL; \ + if (head.hh_head) { (head).hh_head->hh_prev = (addhh); } \ + (head).hh_head=addhh; \ + if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH) \ + && (addhh)->tbl->noexpand != 1) { \ + HASH_EXPAND_BUCKETS((addhh)->tbl); \ + } \ +} while(0) + +/* remove an item from a given bucket */ +#define HASH_DEL_IN_BKT(hh,head,hh_del) \ + (head).count--; \ + if ((head).hh_head == hh_del) { \ + (head).hh_head = hh_del->hh_next; \ + } \ + if (hh_del->hh_prev) { \ + hh_del->hh_prev->hh_next = hh_del->hh_next; \ + } \ + if (hh_del->hh_next) { \ + hh_del->hh_next->hh_prev = hh_del->hh_prev; \ + } + +/* Bucket expansion has the effect of doubling the number of buckets + * and redistributing the items into the new buckets. Ideally the + * items will distribute more or less evenly into the new buckets + * (the extent to which this is true is a measure of the quality of + * the hash function as it applies to the key domain). + * + * With the items distributed into more buckets, the chain length + * (item count) in each bucket is reduced. Thus by expanding buckets + * the hash keeps a bound on the chain length. This bounded chain + * length is the essence of how a hash provides constant time lookup. + * + * The calculation of tbl->ideal_chain_maxlen below deserves some + * explanation. First, keep in mind that we're calculating the ideal + * maximum chain length based on the *new* (doubled) bucket count. + * In fractions this is just n/b (n=number of items,b=new num buckets). + * Since the ideal chain length is an integer, we want to calculate + * ceil(n/b). We don't depend on floating point arithmetic in this + * hash, so to calculate ceil(n/b) with integers we could write + * + * ceil(n/b) = (n/b) + ((n%b)?1:0) + * + * and in fact a previous version of this hash did just that. + * But now we have improved things a bit by recognizing that b is + * always a power of two. We keep its base 2 log handy (call it lb), + * so now we can write this with a bit shift and logical AND: + * + * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) + * + */ +#define HASH_EXPAND_BUCKETS(tbl) \ +do { \ + unsigned _he_bkt; \ + unsigned _he_bkt_i; \ + struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ + UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ + _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ + 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \ + memset(_he_new_buckets, 0, \ + 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + tbl->ideal_chain_maxlen = \ + (tbl->num_items >> (tbl->log2_num_buckets+1)) + \ + ((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0); \ + tbl->nonideal_items = 0; \ + for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \ + { \ + _he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \ + while (_he_thh) { \ + _he_hh_nxt = _he_thh->hh_next; \ + HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt); \ + _he_newbkt = &(_he_new_buckets[ _he_bkt ]); \ + if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \ + tbl->nonideal_items++; \ + _he_newbkt->expand_mult = _he_newbkt->count / \ + tbl->ideal_chain_maxlen; \ + } \ + _he_thh->hh_prev = NULL; \ + _he_thh->hh_next = _he_newbkt->hh_head; \ + if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev = \ + _he_thh; \ + _he_newbkt->hh_head = _he_thh; \ + _he_thh = _he_hh_nxt; \ + } \ + } \ + tbl->num_buckets *= 2; \ + tbl->log2_num_buckets++; \ + uthash_free( tbl->buckets ); \ + tbl->buckets = _he_new_buckets; \ + tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \ + (tbl->ineff_expands+1) : 0; \ + if (tbl->ineff_expands > 1) { \ + tbl->noexpand=1; \ + uthash_noexpand_fyi(tbl); \ + } \ + uthash_expand_fyi(tbl); \ +} while(0) + + +/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ +/* Note that HASH_SORT assumes the hash handle name to be hh. + * HASH_SRT was added to allow the hash handle name to be passed in. */ +#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) +#define HASH_SRT(hh,head,cmpfcn) \ +do { \ + unsigned _hs_i; \ + unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ + struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ + if (head) { \ + _hs_insize = 1; \ + _hs_looping = 1; \ + _hs_list = &((head)->hh); \ + while (_hs_looping) { \ + _hs_p = _hs_list; \ + _hs_list = NULL; \ + _hs_tail = NULL; \ + _hs_nmerges = 0; \ + while (_hs_p) { \ + _hs_nmerges++; \ + _hs_q = _hs_p; \ + _hs_psize = 0; \ + for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \ + _hs_psize++; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + if (! (_hs_q) ) break; \ + } \ + _hs_qsize = _hs_insize; \ + while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) { \ + if (_hs_psize == 0) { \ + _hs_e = _hs_q; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_qsize--; \ + } else if ( (_hs_qsize == 0) || !(_hs_q) ) { \ + _hs_e = _hs_p; \ + _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ + ((void*)((char*)(_hs_p->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_psize--; \ + } else if (( \ + cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \ + ) <= 0) { \ + _hs_e = _hs_p; \ + _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ + ((void*)((char*)(_hs_p->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_psize--; \ + } else { \ + _hs_e = _hs_q; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_qsize--; \ + } \ + if ( _hs_tail ) { \ + _hs_tail->next = ((_hs_e) ? \ + ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \ + } else { \ + _hs_list = _hs_e; \ + } \ + _hs_e->prev = ((_hs_tail) ? \ + ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \ + _hs_tail = _hs_e; \ + } \ + _hs_p = _hs_q; \ + } \ + _hs_tail->next = NULL; \ + if ( _hs_nmerges <= 1 ) { \ + _hs_looping=0; \ + (head)->hh.tbl->tail = _hs_tail; \ + DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ + } \ + _hs_insize *= 2; \ + } \ + HASH_FSCK(hh,head); \ + } \ +} while (0) + +/* This function selects items from one hash into another hash. + * The end result is that the selected items have dual presence + * in both hashes. There is no copy of the items made; rather + * they are added into the new hash through a secondary hash + * hash handle that must be present in the structure. */ +#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ +do { \ + unsigned _src_bkt, _dst_bkt; \ + void *_last_elt=NULL, *_elt; \ + UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ + ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ + if (src) { \ + for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ + for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ + _src_hh; \ + _src_hh = _src_hh->hh_next) { \ + _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ + if (cond(_elt)) { \ + _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ + _dst_hh->key = _src_hh->key; \ + _dst_hh->keylen = _src_hh->keylen; \ + _dst_hh->hashv = _src_hh->hashv; \ + _dst_hh->prev = _last_elt; \ + _dst_hh->next = NULL; \ + if (_last_elt_hh) { _last_elt_hh->next = _elt; } \ + if (!dst) { \ + DECLTYPE_ASSIGN(dst,_elt); \ + HASH_MAKE_TABLE(hh_dst,dst); \ + } else { \ + _dst_hh->tbl = (dst)->hh_dst.tbl; \ + } \ + HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ + HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \ + (dst)->hh_dst.tbl->num_items++; \ + _last_elt = _elt; \ + _last_elt_hh = _dst_hh; \ + } \ + } \ + } \ + } \ + HASH_FSCK(hh_dst,dst); \ +} while (0) + +#define HASH_CLEAR(hh,head) \ +do { \ + if (head) { \ + uthash_free((head)->hh.tbl->buckets ); \ + uthash_free((head)->hh.tbl); \ + (head)=NULL; \ + } \ +} while(0) + +/* obtain a count of items in the hash */ +#define HASH_COUNT(head) HASH_CNT(hh,head) +#define HASH_CNT(hh,head) (head?(head->hh.tbl->num_items):0) + +typedef struct UT_hash_bucket { + struct UT_hash_handle *hh_head; + unsigned count; + + /* expand_mult is normally set to 0. In this situation, the max chain length + * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If + * the bucket's chain exceeds this length, bucket expansion is triggered). + * However, setting expand_mult to a non-zero value delays bucket expansion + * (that would be triggered by additions to this particular bucket) + * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. + * (The multiplier is simply expand_mult+1). The whole idea of this + * multiplier is to reduce bucket expansions, since they are expensive, in + * situations where we know that a particular bucket tends to be overused. + * It is better to let its chain length grow to a longer yet-still-bounded + * value, than to do an O(n) bucket expansion too often. + */ + unsigned expand_mult; + +} UT_hash_bucket; + +/* random signature used only to find hash tables in external analysis */ +#define HASH_SIGNATURE 0xa0111fe1 +#define HASH_BLOOM_SIGNATURE 0xb12220f2 + +typedef struct UT_hash_table { + UT_hash_bucket *buckets; + unsigned num_buckets, log2_num_buckets; + unsigned num_items; + struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ + ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ + + /* in an ideal situation (all buckets used equally), no bucket would have + * more than ceil(#items/#buckets) items. that's the ideal chain length. */ + unsigned ideal_chain_maxlen; + + /* nonideal_items is the number of items in the hash whose chain position + * exceeds the ideal chain maxlen. these items pay the penalty for an uneven + * hash distribution; reaching them in a chain traversal takes >ideal steps */ + unsigned nonideal_items; + + /* ineffective expands occur when a bucket doubling was performed, but + * afterward, more than half the items in the hash had nonideal chain + * positions. If this happens on two consecutive expansions we inhibit any + * further expansion, as it's not helping; this happens when the hash + * function isn't a good fit for the key domain. When expansion is inhibited + * the hash will still work, albeit no longer in constant time. */ + unsigned ineff_expands, noexpand; + + uint32_t signature; /* used only to find hash tables in external analysis */ +#ifdef HASH_BLOOM + uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ + uint8_t *bloom_bv; + char bloom_nbits; +#endif + +} UT_hash_table; + +typedef struct UT_hash_handle { + struct UT_hash_table *tbl; + void *prev; /* prev element in app order */ + void *next; /* next element in app order */ + struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ + struct UT_hash_handle *hh_next; /* next hh in bucket order */ + void *key; /* ptr to enclosing struct's key */ + unsigned keylen; /* enclosing struct's key len */ + unsigned hashv; /* result of hash-fcn(key) */ +} UT_hash_handle; + +#endif /* UTHASH_H */ diff --git a/applications/_plugins/cimage/ktx/lib/writer.c b/applications/_plugins/cimage/ktx/lib/writer.c new file mode 100644 index 000000000..ca33b7d25 --- /dev/null +++ b/applications/_plugins/cimage/ktx/lib/writer.c @@ -0,0 +1,748 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4: */ + +/* $Id: b7b03494cb4c3d30b64882e8d4b3e44c3221890b $ */ + +/** + * @file writer.c + * @~English + * + * @brief Functions for creating KTX-format files from a set of images. + * + * @author Mark Callow, HI Corporation + */ + +/* +Copyright (c) 2010 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +unaltered in all copies or substantial portions of the Materials. +Any additions, deletions, or changes to the original source files +must be clearly indicated in accompanying documentation. + +If only executable code is distributed, then the accompanying +documentation must state that "this software is based in part on the +work of the Khronos Group". + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +#ifdef _WIN32 +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include + +#include "ktx.h" +#include "ktxint.h" +#include "ktxstream.h" +#include "ktxfilestream.h" +#include "ktxmemstream.h" + +static KTX_error_code validateTypeAndFormat(GLenum format, GLenum type); +static KTX_error_code sizeofGroupAndElement(GLenum format, GLenum type, + GLuint* groupBytes, GLuint* elementBytes, + GLboolean* packed); +static KTX_error_code sizeofGLtype(GLenum type, GLuint* size, GLboolean* packed); + + +/** + * @~English + * @brief Write image(s) in a KTX-format to a ktxStream. + * + * @param [in] stream pointer to the ktxStream from which to load. + * @param [in] textureInfo pointer to a KTX_texture_info structure providing + * information about the images to be included in + * the KTX file. + * @param [in] bytesOfKeyValueData + * specifies the number of bytes of key-value data. + * @param [in] keyValueData a pointer to the keyValue data. + * @param [in] numImages number of images in the following array + * @param [in] images array of KTX_image_info providing image size and + * data. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE @p dst or @p target are @c NULL + * @exception KTX_INVALID_VALUE @c glTypeSize in @p textureInfo is not 1, 2, or 4 or + * is different from the size of the type specified + * in @c glType. + * @exception KTX_INVALID_VALUE @c pixelWidth in @p textureInfo is 0 or pixelDepth != 0 + * && pixelHeight == 0. + * @exception KTX_INVALID_VALUE @c numberOfFaces != 1 || numberOfFaces != 6 or + * numberOfArrayElements or numberOfMipmapLevels are < 0. + * @exception KTX_INVALID_VALUE @c glType in @p textureInfo is an unrecognized type. + * @exception KTX_INVALID_OPERATION + * numberOfFaces == 6 and images are either not 2D or + * are not square. + * @exception KTX_INVALID_OPERATION + * number of images is insufficient for the specified + * number of mipmap levels and faces. + * @exception KTX_INVALID_OPERATION + * the size of a provided image is different than that + * required for the specified width, height or depth + * or for the mipmap level being processed. + * @exception KTX_INVALID_OPERATION + * @c glType and @c glFormat in @p textureInfo are mismatched. + * See OpenGL 4.4 specification section 8.4.4 and + * table 8.5. + * @exception KTX_FILE_WRITE_ERROR a system error occurred while writing the file. + * @exception KTX_OUT_OF_MEMORY system failed to allocate sufficient memory. + */ +static +KTX_error_code +ktxWriteKTXS(struct ktxStream *stream, const KTX_texture_info* textureInfo, + GLsizei bytesOfKeyValueData, const void* keyValueData, + GLuint numImages, KTX_image_info images[]) +{ + KTX_header header = KTX_IDENTIFIER_REF; + GLuint i, level, dimension, cubemap = 0; + GLuint numMipmapLevels, numArrayElements; + GLbyte pad[4] = { 0, 0, 0, 0 }; + KTX_error_code errorCode = KTX_SUCCESS; + GLboolean compressed = GL_FALSE; + GLuint groupBytes=4, elementBytes; + + if (!stream) { + return KTX_INVALID_VALUE; + } + + //endianess int.. if this comes out reversed, all of the other ints will too. + header.endianness = KTX_ENDIAN_REF; + header.glType = textureInfo->glType; + header.glTypeSize = textureInfo->glTypeSize; + header.glFormat = textureInfo->glFormat; + header.glInternalFormat = textureInfo->glInternalFormat; + header.glBaseInternalFormat = textureInfo->glBaseInternalFormat; + header.pixelWidth = textureInfo->pixelWidth; + header.pixelHeight = textureInfo->pixelHeight; + header.pixelDepth = textureInfo->pixelDepth; + header.numberOfArrayElements = textureInfo->numberOfArrayElements; + header.numberOfFaces = textureInfo->numberOfFaces; + header.numberOfMipmapLevels = textureInfo->numberOfMipmapLevels; + header.bytesOfKeyValueData = bytesOfKeyValueData; + + /* Do some sanity checking */ + if (header.glTypeSize != 1 && + header.glTypeSize != 2 && + header.glTypeSize != 4) + { + /* Only 8, 16, and 32-bit types are supported for byte-swapping. + * See UNPACK_SWAP_BYTES & table 8.4 in the OpenGL 4.4 spec. + */ + return KTX_INVALID_VALUE; + } + + if (header.glType == 0 || header.glFormat == 0) + { + if (header.glType + header.glFormat != 0) { + /* either both or neither of glType & glFormat must be zero */ + return KTX_INVALID_VALUE; + } else { + compressed = GL_TRUE; + if (header.glBaseInternalFormat == GL_RED) + groupBytes = 1; + if (header.glBaseInternalFormat == GL_RG) + groupBytes = 2; + if (header.glBaseInternalFormat == GL_RGB) + groupBytes = 3; + if (header.glBaseInternalFormat == GL_RGBA) + groupBytes = 4; + } + } + else + { + GLboolean packed; + + /* Get size of group and element */ + if ((errorCode = sizeofGroupAndElement(header.glFormat, header.glType, + &groupBytes, &elementBytes, &packed)) != KTX_SUCCESS) + { + return errorCode; + } + + /* Check validity of type/format combination for packed types */ + if (packed && (errorCode = validateTypeAndFormat(header.glFormat, header.glType)) != KTX_SUCCESS) + { + return errorCode; + } + + if (header.glTypeSize != elementBytes) + { +#if defined(GL_FLOAT_32_UNSIGNED_INT_24_8_REV) + if (header.glType != GL_FLOAT_32_UNSIGNED_INT_24_8_REV || header.glTypeSize != 1) +#endif + return KTX_INVALID_VALUE; + } + } + + + /* Check texture dimensions. KTX files can store 8 types of textures: + * 1D, 2D, 3D, cube, and array variants of these. There is currently + * no GL extension that would accept 3D array or cube array textures + * but we'll let such files be created. + */ + if ((header.pixelWidth == 0) || + (header.pixelDepth > 0 && header.pixelHeight == 0)) + { + /* texture must have width */ + /* texture must have height if it has depth */ + return KTX_INVALID_VALUE; + } + if (header.pixelHeight > 0 && header.pixelDepth > 0) + dimension = 3; + else if (header.pixelHeight > 0) + dimension = 2; + else + dimension = 1; + + if (header.numberOfFaces == 6) + { + if (dimension != 2) + { + /* cube map needs 2D faces */ + return KTX_INVALID_OPERATION; + } + if (header.pixelWidth != header.pixelHeight) + { + /* cube maps require square images */ + return KTX_INVALID_OPERATION; + } + } + else if (header.numberOfFaces != 1) + { + /* numberOfFaces must be either 1 or 6 */ + return KTX_INVALID_VALUE; + } + + if (header.numberOfArrayElements == 0) + { + numArrayElements = 1; + if (header.numberOfFaces == 6) + cubemap = 1; + } + else + numArrayElements = header.numberOfArrayElements; + + /* Check number of mipmap levels */ + if (header.numberOfMipmapLevels == 0) + { + numMipmapLevels = 1; + } + else + numMipmapLevels = header.numberOfMipmapLevels; + if (numMipmapLevels > 1) { + GLuint max_dim = MAX(MAX(header.pixelWidth, header.pixelHeight), header.pixelDepth); + if (max_dim < ((GLuint)1 << (header.numberOfMipmapLevels - 1))) + { + /* Can't have more mip levels than 1 + log2(max(width, height, depth)) */ + return KTX_INVALID_VALUE; + } + } + + if (numImages < numMipmapLevels * header.numberOfFaces) + { + /* Not enough images */ + return KTX_INVALID_OPERATION; + } + + //write header + errorCode = stream->write(&header, sizeof(KTX_header), 1, stream->src); + if (errorCode != KTX_SUCCESS) + return errorCode; + + //write keyValueData + if (bytesOfKeyValueData != 0) { + if (keyValueData == NULL) + return KTX_INVALID_OPERATION; + + errorCode = stream->write(keyValueData, 1, bytesOfKeyValueData, stream->src); + if (errorCode != KTX_SUCCESS) + return errorCode; + } + + /* Write the image data */ + for (level = 0, i = 0; level < numMipmapLevels; ++level) + { + GLsizei expectedFaceSize; + GLuint face, faceLodSize, faceLodRounding; + GLuint pixelWidth, pixelHeight, pixelDepth; + GLuint packedRowBytes, rowBytes, rowRounding; + + pixelWidth = MAX(1, header.pixelWidth >> level); + pixelHeight = MAX(1, header.pixelHeight >> level); + pixelDepth = MAX(1, header.pixelDepth >> level); + + /* Calculate face sizes for this LoD based on glType, glFormat, width & height */ + expectedFaceSize = groupBytes + * pixelWidth + * pixelHeight + * pixelDepth + * numArrayElements; + + rowRounding = 0; + packedRowBytes = groupBytes * pixelWidth; + /* KTX format specifies UNPACK_ALIGNMENT==4 */ + /* GL spec: rows are not to be padded when elementBytes != 1, 2, 4 or 8. + * As GL currently has no such elements, no test is necessary. + */ + if (!compressed && elementBytes < KTX_GL_UNPACK_ALIGNMENT) { + // Equivalent to UNPACK_ALIGNMENT * ceil((groupSize * pixelWidth) / UNPACK_ALIGNMENT) + rowRounding = 3 - ((packedRowBytes + KTX_GL_UNPACK_ALIGNMENT-1) % KTX_GL_UNPACK_ALIGNMENT); + rowBytes = packedRowBytes + rowRounding; + } + + if (rowRounding == 0) { + faceLodSize = images[i].size; + } else { + /* Need to pad the rows to meet the required UNPACK_ALIGNMENT */ + faceLodSize = rowBytes * pixelHeight * pixelDepth * numArrayElements; + } + faceLodRounding = 3 - ((faceLodSize + 3) % 4); + + errorCode = stream->write(&faceLodSize, sizeof(faceLodSize), 1, stream->src); + if (errorCode != KTX_SUCCESS) + goto cleanup; + + for (face = 0; face < header.numberOfFaces; ++face, ++i) { + if (!compressed) { + /* Sanity check. */ + if (images[i].size != expectedFaceSize) { + errorCode = KTX_INVALID_OPERATION; + goto cleanup; + } + } + if (rowRounding == 0) { + /* Can write whole face at once */ + errorCode = stream->write(images[i].data, faceLodSize, 1, stream->src); + if (errorCode != KTX_SUCCESS) + goto cleanup; + } else { + /* Write the rows individually, padding each one */ + GLuint row; + GLuint numRows = pixelHeight + * pixelDepth + * numArrayElements; + for (row = 0; row < numRows; row++) { + errorCode = stream->write(&images[i].data[row*packedRowBytes], packedRowBytes, 1, stream->src); + if (errorCode != KTX_SUCCESS) + goto cleanup; + + errorCode = stream->write(pad, sizeof(GLbyte), rowRounding, stream->src); + if (errorCode != KTX_SUCCESS) + goto cleanup; + } + } + if (faceLodRounding) { + errorCode = stream->write(pad, sizeof(GLbyte), faceLodRounding, stream->src); + if (errorCode != KTX_SUCCESS) + goto cleanup; + } + } + } + +cleanup: + return errorCode; +} + +/** + * @~English + * @brief Write image(s) in a KTX-formatted stdio FILE stream. + * + * @param [in] file pointer to the FILE stream to write to. + * @param [in] textureInfo pointer to a KTX_texture_info structure providing + * information about the images to be included in + * the KTX file. + * @param [in] bytesOfKeyValueData + * specifies the number of bytes of key-value data. + * @param [in] keyValueData a pointer to the keyValue data. + * @param [in] numImages number of images in the following array + * @param [in] images array of KTX_image_info providing image size and + * data. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_INVALID_VALUE @p dst or @p target are @c NULL + * @exception KTX_INVALID_VALUE @c glTypeSize in @p textureInfo is not 1, 2, or 4 or + * is different from the size of the type specified + * in @c glType. + * @exception KTX_INVALID_VALUE @c pixelWidth in @p textureInfo is 0 or pixelDepth != 0 + * && pixelHeight == 0. + * @exception KTX_INVALID_VALUE @c numberOfFaces != 1 || numberOfFaces != 6 or + * numberOfArrayElements or numberOfMipmapLevels are < 0. + * @exception KTX_INVALID_VALUE @c glType in @p textureInfo is an unrecognized type. + * @exception KTX_INVALID_OPERATION + * numberOfFaces == 6 and images are either not 2D or + * are not square. + * @exception KTX_INVALID_OPERATION + * number of images is insufficient for the specified + * number of mipmap levels and faces. + * @exception KTX_INVALID_OPERATION + * the size of a provided image is different than that + * required for the specified width, height or depth + * or for the mipmap level being processed. + * @exception KTX_INVALID_OPERATION + * @c glType and @c glFormat in @p textureInfo are mismatched. + * See OpenGL 4.4 specification section 8.4.4 and + * table 8.5. + * @exception KTX_FILE_WRITE_ERROR a system error occurred while writing the file. + */ +KTX_error_code +ktxWriteKTXF(FILE *file, const KTX_texture_info* textureInfo, + GLsizei bytesOfKeyValueData, const void* keyValueData, + GLuint numImages, KTX_image_info images[]) +{ + struct ktxStream stream; + KTX_error_code errorCode = KTX_SUCCESS; + + errorCode = ktxFileInit(&stream, file); + if (errorCode != KTX_SUCCESS) + return errorCode; + + return ktxWriteKTXS(&stream, textureInfo, bytesOfKeyValueData, keyValueData, numImages, images); +} + +/** + * @~English + * @brief Write image(s) to a KTX file on disk. + * + * @param [in] dstname pointer to a C string that contains the path of + * the file to load. + * @param [in] textureInfo pointer to a KTX_texture_info structure providing + * information about the images to be included in + * the KTX file. + * @param [in] bytesOfKeyValueData + * specifies the number of bytes of key-value data. + * @param [in] keyValueData a pointer to the keyValue data. + * @param [in] numImages number of images in the following array. + * @param [in] images array of KTX_image_info providing image size and + * data. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + * @exception KTX_FILE_OPEN_FAILED unable to open the specified file for + * writing. + * + * For other exceptions, see ktxWriteKTXF(). + */ +KTX_error_code +ktxWriteKTXN(const char* dstname, const KTX_texture_info* textureInfo, + GLsizei bytesOfKeyValueData, const void* keyValueData, + GLuint numImages, KTX_image_info images[]) +{ + KTX_error_code errorCode; + FILE* dst = fopen(dstname, "wb"); + + if (dst) { + errorCode = ktxWriteKTXF(dst, textureInfo, bytesOfKeyValueData, keyValueData, + numImages, images); + fclose(dst); + } else + errorCode = KTX_FILE_OPEN_FAILED; + + return errorCode; +} + +/** + * @~English + * @brief Write image(s) in KTX format to memory. + * + * @param [out] bytes pointer to the output with KTX data. Application + is responsible for freeing that memory. + * @param [out] size pointer to store size of the memory written. + * @param [in] textureInfo pointer to a KTX_texture_info structure providing + * information about the images to be included in + * the KTX file. + * @param [in] bytesOfKeyValueData + * specifies the number of bytes of key-value data. + * @param [in] keyValueData a pointer to the keyValue data. + * @param [in] numImages number of images in the following array. + * @param [in] images array of KTX_image_info providing image size and + * data. + * + * @return KTX_SUCCESS on success, other KTX_* enum values on error. + * + */ +KTX_error_code +ktxWriteKTXM(unsigned char** bytes, GLsizei* size, const KTX_texture_info* textureInfo, + GLsizei bytesOfKeyValueData, const void* keyValueData, + GLuint numImages, KTX_image_info images[]) +{ + struct ktxMem mem; + struct ktxStream stream; + KTX_error_code rc; + + *bytes = NULL; + + rc = ktxMemInit(&stream, &mem, NULL, 0); + if (rc != KTX_SUCCESS) + return rc; + + rc = ktxWriteKTXS(&stream, textureInfo, bytesOfKeyValueData, keyValueData, numImages, images); + if(rc != KTX_SUCCESS) + { + if(mem.bytes) + { + free(mem.bytes); + } + return rc; + } + + *bytes = mem.bytes; + *size = mem.used_size; + return KTX_SUCCESS; +} + +/* + * @brief Check format and type matching as required by OpenGL. + * + * @param [in] format the format of the image data + * @param [in] type the type of the image data + * + * @return KTX_SUCCESS if matched, KTX_INVALID_OPERATION, if mismatched + * or KTX_INVALID_VALUE if @p type is invalid. + */ +static KTX_error_code +validateTypeAndFormat(GLenum format, GLenum type) +{ + KTX_error_code retVal = KTX_SUCCESS; + + if ((format >= GL_RED_INTEGER && format <= GL_BGRA_INTEGER) && (type == GL_FLOAT || type == GL_HALF_FLOAT)) + { + retVal = KTX_INVALID_OPERATION; // Note: OpenGL 4.4 says GL_INVALID_VALUE but we'll mirror the others. + } + + switch (type) + { + case GL_UNSIGNED_BYTE_3_3_2: + case GL_UNSIGNED_BYTE_2_3_3_REV: + if (format != GL_RGB && format != GL_RGB_INTEGER) + retVal = KTX_INVALID_OPERATION; // Matches OpenGL 4.4 + break; + + case GL_UNSIGNED_SHORT_5_6_5: + case GL_UNSIGNED_SHORT_5_6_5_REV: + if (format != GL_RGB && format != GL_RGB_INTEGER) + retVal = KTX_INVALID_OPERATION; + break; + + case GL_UNSIGNED_SHORT_4_4_4_4: + case GL_UNSIGNED_SHORT_5_5_5_1: + case GL_UNSIGNED_SHORT_4_4_4_4_REV: + case GL_UNSIGNED_SHORT_1_5_5_5_REV: + if (format != GL_RGBA && format != GL_BGRA + && format != GL_RGBA_INTEGER && format != GL_BGRA_INTEGER) + { + retVal = KTX_INVALID_OPERATION; + } + break; + + case GL_UNSIGNED_INT_8_8_8_8: + case GL_UNSIGNED_INT_8_8_8_8_REV: + case GL_UNSIGNED_INT_10_10_10_2: + case GL_UNSIGNED_INT_2_10_10_10_REV: + if (format != GL_RGBA && format != GL_BGRA + && format != GL_RGBA_INTEGER && format != GL_BGRA_INTEGER) + { + retVal = KTX_INVALID_OPERATION; + } + break; + + case GL_UNSIGNED_INT_24_8: + if (format != GL_DEPTH_STENCIL) + retVal = KTX_INVALID_OPERATION; + break; + + case GL_UNSIGNED_INT_10F_11F_11F_REV: + case GL_UNSIGNED_INT_5_9_9_9_REV: + if (format != GL_RGB && format != GL_BGR) + retVal = KTX_INVALID_OPERATION; + break; + + case GL_FLOAT_32_UNSIGNED_INT_24_8_REV: + // Note: OpenGL 4.4 says GL_INVALID_VALUE in one place, + // GL_INVALID_OPERATION in another. The latter is more logical. + retVal = KTX_INVALID_OPERATION; + + default: + retVal = KTX_INVALID_VALUE; + } + + return retVal; +} + + +/* + * @brief Get the size of the group of elements constituting a pixel in + * the given @p type and @p format and the size of an element. + * + * Sizes are returned in basic machine units (bytes). The function also + * indicates if @type is a packed pixel format. + * + * @param [in] format the format of the image data + * @param [in] type the type of the image data + * @param [out] groupBytes pointer to location where to write the size of a group + * @param [out] size pointer to location where to write the size of an element + * @param [out] packed pointer to location where to write flag indicating + * if the type is a packed type. + * + * @return KTX_INVALID_VALUE if the @p type or @p format is invalid. + */ +static KTX_error_code +sizeofGroupAndElement(GLenum format, GLenum type, GLuint* groupBytes, + GLuint* elementBytes, GLboolean* packed) +{ + KTX_error_code retVal; + + if ((retVal = sizeofGLtype(type, elementBytes, packed)) != KTX_SUCCESS) + { + return retVal; + } + + if (*packed) + { + *groupBytes = *elementBytes; + return retVal; + } + + switch (format) { + case GL_ALPHA: + case GL_RED: + case GL_GREEN: + case GL_BLUE: + case GL_LUMINANCE: /* deprecated but needed for ES 1 & 2 */ + case GL_ALPHA_INTEGER: + case GL_RED_INTEGER: + case GL_GREEN_INTEGER: + case GL_BLUE_INTEGER: + /* case GL_LUMINANCE_INTEGER: deprecated */ + *groupBytes = *elementBytes; + break; + case GL_LUMINANCE_ALPHA: + case GL_RG: + /* case GL_LUMINANCE_ALPHA_INTEGER: deprecated */ + case GL_RG_INTEGER: + *groupBytes = *elementBytes * 2; + break; + case GL_RGB: + case GL_BGR: + case GL_RGB_INTEGER: + case GL_BGR_INTEGER: + *groupBytes = *elementBytes * 3; + break; + case GL_RGBA: + case GL_BGRA: + case GL_RGBA_INTEGER: + case GL_BGRA_INTEGER: + *groupBytes = *elementBytes * 4; + break; + default: + retVal = KTX_INVALID_VALUE; + } + + return retVal; +} + +/* + * @brief Get the size of a GL type in basic machine units + * and indicate whether or not it is a packed type. + * + * @param [in] type the type whose size is to be returned. + * @param [out] size pointer to location where to write the size + * @param [out] packed pointer to location where to write flag indicating + * if the type is a packed type. + * + * @return KTX_INVALID_VALUE if the @p type is unrecognized. + */ +static KTX_error_code +sizeofGLtype(GLenum type, GLuint* size, GLboolean* packed) +{ + assert(packed && size); + *packed = GL_FALSE; + + switch (type) { + case GL_BYTE: + *size = sizeof(GLbyte); + break; + + case GL_UNSIGNED_BYTE: + *size = sizeof(GLubyte); + break; + + case GL_UNSIGNED_BYTE_3_3_2: + case GL_UNSIGNED_BYTE_2_3_3_REV: + *packed = GL_TRUE; + *size = sizeof(GLubyte); + break; + + case GL_SHORT: + *size = sizeof(GLshort); + break; + + case GL_UNSIGNED_SHORT: + *size = sizeof(GLushort); + break; + + case GL_UNSIGNED_SHORT_5_6_5: + case GL_UNSIGNED_SHORT_4_4_4_4: + case GL_UNSIGNED_SHORT_5_5_5_1: + case GL_UNSIGNED_SHORT_5_6_5_REV: + case GL_UNSIGNED_SHORT_4_4_4_4_REV: + case GL_UNSIGNED_SHORT_1_5_5_5_REV: + *packed = GL_TRUE; + *size = sizeof(GLushort); + break; + + case GL_INT: + *size = sizeof(GLint); + break; + + case GL_UNSIGNED_INT: + *size = sizeof(GLuint); + break; + + case GL_UNSIGNED_INT_8_8_8_8: + case GL_UNSIGNED_INT_8_8_8_8_REV: + case GL_UNSIGNED_INT_10_10_10_2: + case GL_UNSIGNED_INT_2_10_10_10_REV: + case GL_UNSIGNED_INT_24_8: + case GL_UNSIGNED_INT_10F_11F_11F_REV: + case GL_UNSIGNED_INT_5_9_9_9_REV: + *packed = GL_TRUE; + *size = sizeof(GLuint); + break; + + case GL_HALF_FLOAT: + *size = sizeof(GLhalf); + break; + + case GL_FLOAT: + *size = sizeof(GLfloat); + break; + + case GL_FLOAT_32_UNSIGNED_INT_24_8_REV: + *packed = GL_TRUE; + *size = sizeof(GLfloat) + sizeof(GLint); + break; + + default: + return KTX_INVALID_VALUE; + } + return KTX_SUCCESS; +} diff --git a/applications/_plugins/cimage/ktx2/CMakeLists.txt b/applications/_plugins/cimage/ktx2/CMakeLists.txt new file mode 100644 index 000000000..d7b7f0a52 --- /dev/null +++ b/applications/_plugins/cimage/ktx2/CMakeLists.txt @@ -0,0 +1,59 @@ +cmake_minimum_required(VERSION 3.10) + +add_library(Image_KTX2 STATIC "") + +target_sources(Image_KTX2 + PRIVATE + ktx2.cpp + ktx2.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/cimage/ktx/ktxcommon.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/cimage/ktx/ktxcommon.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/cimage/ktx/softfloat.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/cimage/ktx/softfloat.h + ) + +target_include_directories(Image_KTX2 + PRIVATE + ./ + ${PROJECT_SOURCE_DIR}/applications/_plugins/cimage/ktx + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib + ${PROJECT_SOURCE_DIR}/applications/_libs/gpu_decode + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half + ${PROJECT_SOURCE_DIR}/applications/_plugins/common + ${PROJECT_SOURCE_DIR}/../common/lib/ext/glew/1.9.0/include + ) + + +# Enabled KTX2 Features +if (CMP_HOST_WINDOWS) + target_include_directories(Image_KTX2 + PRIVATE + ${PROJECT_SOURCE_DIR}/../common/lib/ext/ktx/include + ${PROJECT_SOURCE_DIR}/../common/lib/ext/ktx/lib + ${VULKAN_SDK_PATH}/include/ + ) + + target_link_libraries(Image_KTX2 PRIVATE + CMP_Compressonator + CMP_Framework + ExtKTX + ) +endif() + +if (UNIX) + target_compile_definitions(Image_KTX2 PRIVATE _LINUX) + find_package(OpenGL) + if (OpenGL_FOUND) + if(APPLE) + target_include_directories(Image_KTX2 + PRIVATE + /usr/local/include) + endif() + endif() +endif() + + +set_target_properties(Image_KTX2 PROPERTIES + FOLDER "Plugin_Static/ImageIO" + # USE_FOLDERS ON + ) diff --git a/applications/_plugins/cimage/ktx2/ktx2.cpp b/applications/_plugins/cimage/ktx2/ktx2.cpp new file mode 100644 index 000000000..99dcdc30e --- /dev/null +++ b/applications/_plugins/cimage/ktx2/ktx2.cpp @@ -0,0 +1,1119 @@ +//===================================================================== +// Copyright 2020 (c), Advanced Micro Devices, Inc. All rights reserved. +//===================================================================== +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +// Windows Header Files: +#ifdef _WIN32 +#include +#endif + +#include "ktx2.h" +#include "tc_pluginapi.h" +#include "tc_plugininternal.h" +#include "common.h" +#include "softfloat.h" + +#include "textureio.h" + +#include +#include +#include +#include + +#include "gl_format.h" +#pragma comment(lib, "opengl32.lib") // Open GL +#pragma comment(lib, "Glu32.lib") // Glu +#pragma comment(lib, "glew32.lib") // glew + +using namespace std; + +CMIPS* KTX2_CMips; + +#ifdef BUILD_AS_PLUGIN_DLL +DECLARE_PLUGIN(Plugin_KTX2) +SET_PLUGIN_TYPE("IMAGE") +SET_PLUGIN_NAME("KTX2") +#else +void* make_Plugin_KTX2() +{ + return new Plugin_KTX2; +} +#endif + +static void writeId2(std::ostream& dst) +{ + dst << "glTF Compressonator v2.0"; +} + +Plugin_KTX2::Plugin_KTX2() +{ +} + +Plugin_KTX2::~Plugin_KTX2() +{ +} + +int Plugin_KTX2::TC_PluginSetSharedIO(void* Shared) +{ + if (Shared) + { + KTX2_CMips = static_cast(Shared); + return 0; + } + return 1; +} + +int Plugin_KTX2::TC_PluginGetVersion(TC_PluginVersion* pPluginVersion) +{ +#ifdef _WIN32 + pPluginVersion->guid = g_GUID; +#endif + pPluginVersion->dwAPIVersionMajor = TC_API_VERSION_MAJOR; + pPluginVersion->dwAPIVersionMinor = TC_API_VERSION_MINOR; + pPluginVersion->dwPluginVersionMajor = TC_PLUGIN_VERSION_MAJOR; + pPluginVersion->dwPluginVersionMinor = TC_PLUGIN_VERSION_MINOR; + return 0; +} + +int Plugin_KTX2::TC_PluginFileLoadTexture(const char* pszFilename, CMP_Texture* srcTexture) +{ + return -1; +} + +int Plugin_KTX2::TC_PluginFileSaveTexture(const char* pszFilename, CMP_Texture* srcTexture) +{ + return -1; +} + +int Plugin_KTX2::TC_PluginFileLoadTexture(const char* pszFilename, MipSet* pMipSet) +{ + ktxTexture2* texture2 = nullptr; + ktxTexture* texture = nullptr; + + KTX_error_code loadStatus; + bool isCompressed = false; + ktx_uint32_t glInternalformat; + ktx_uint32_t glType; + ktx_uint32_t glFormat; + + loadStatus = ktxTexture2_CreateFromNamedFile(pszFilename, KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, &texture2); + if (loadStatus != KTX_SUCCESS) + { + if (KTX2_CMips) + { + KTX2_CMips->PrintError(("Error(%x): KTX2 Plugin ID(%d) opening file = %s \n"), loadStatus, IDS_ERROR_FILE_OPEN, pszFilename); + } + return -1; + } + + + + // CMP_DFD* extended_format = (CMP_DFD *)texture2->pDfd; + glInternalformat = glGetInternalFormatFromVkFormat((VkFormat)texture2->vkFormat); + glType = glGetTypeFromInternalFormat(glInternalformat); + glFormat = glGetFormatFromInternalFormat(glInternalformat); + + texture = ktxTexture(texture2); + + isCompressed = texture->isCompressed; + + int channelByteSize = 0; + + try { + if (isCompressed) + { + pMipSet->m_compressed = true; + pMipSet->m_nBlockHeight = 4; + pMipSet->m_nBlockWidth = 4; + pMipSet->m_nBlockDepth = 1; + pMipSet->m_ChannelFormat = CF_Compressed; + pMipSet->m_TextureDataType = TDT_ARGB; + pMipSet->m_format = CMP_FORMAT_Unknown; + channelByteSize = 1; + + // Search using VL formats first + switch ((VkFormat)texture2->vkFormat) + { + case VK_FORMAT_BC1_RGB_UNORM_BLOCK: + pMipSet->m_format = CMP_FORMAT_BC1; + break; + case VK_FORMAT_BC2_UNORM_BLOCK: + pMipSet->m_format = CMP_FORMAT_BC2; + break; + case VK_FORMAT_BC3_UNORM_BLOCK: + pMipSet->m_format = CMP_FORMAT_BC3; + // These are unsupport types used to map into cmp formats + // this is a trick for the CMP compressed DXT5 swizzle types + // switch (glInternalformat) + // { + // case COMPRESSED_FORMAT_DXT5_xGBR_TMP: + // pMipSet->m_format = CMP_FORMAT_DXT5_xGBR; + // break; + // case COMPRESSED_FORMAT_DXT5_RxBG_TMP: + // pMipSet->m_format = CMP_FORMAT_DXT5_RxBG; + // break; + // case COMPRESSED_FORMAT_DXT5_RBxG_TMP: + // pMipSet->m_format = CMP_FORMAT_DXT5_RBxG; + // break; + // case COMPRESSED_FORMAT_DXT5_xRBG_TMP: + // pMipSet->m_format = CMP_FORMAT_DXT5_xRBG; + // break; + // case COMPRESSED_FORMAT_DXT5_RGxB_TMP: + // pMipSet->m_format = CMP_FORMAT_DXT5_RGxB; + // break; + // case COMPRESSED_FORMAT_DXT5_xGxR_TMP: + // pMipSet->m_format = CMP_FORMAT_DXT5_xGxR; + // break; + // } + break; + case VK_FORMAT_BC4_UNORM_BLOCK: + pMipSet->m_format = CMP_FORMAT_BC4; + break; + case VK_FORMAT_BC4_SNORM_BLOCK: + pMipSet->m_format = CMP_FORMAT_BC4_S; + // if (glInternalformat == COMPRESSED_FORMAT_ATI1N_UNorm_TMP) + // { + // pMipSet->m_format = CMP_FORMAT_ATI1N; + // } + break; + case VK_FORMAT_BC5_UNORM_BLOCK: + pMipSet->m_format = CMP_FORMAT_BC5; + //if (glInternalformat == COMPRESSED_FORMAT_ATI2N_UNorm_TMP) + //{ + // pMipSet->m_format = CMP_FORMAT_ATI2N; + //} + //else if (glInternalformat == COMPRESSED_FORMAT_ATI2N_XY_UNorm_TMP) + //{ + // pMipSet->m_format = CMP_FORMAT_ATI2N_XY; + //} + break; + case VK_FORMAT_BC5_SNORM_BLOCK: + pMipSet->m_format = CMP_FORMAT_BC5_S; + break; + case VK_FORMAT_BC6H_UFLOAT_BLOCK: + pMipSet->m_format = CMP_FORMAT_BC6H; + break; + case VK_FORMAT_BC6H_SFLOAT_BLOCK: + pMipSet->m_format = CMP_FORMAT_BC6H_SF; + break; + case VK_FORMAT_BC7_UNORM_BLOCK: + pMipSet->m_format = CMP_FORMAT_BC7; + break; + case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: + pMipSet->m_format = CMP_FORMAT_ETC2_RGB; // Skip ETC as ETC2 is backward comp + break; + case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: + pMipSet->m_format = CMP_FORMAT_ETC2_SRGB; + break; + case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK: + pMipSet->m_format = CMP_FORMAT_ETC2_RGBA; + break; + case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: + pMipSet->m_format = CMP_FORMAT_ETC2_RGBA1; + break; + case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK: + pMipSet->m_format = CMP_FORMAT_ETC2_SRGBA; + break; + case VK_FORMAT_ASTC_4x4_UNORM_BLOCK: + pMipSet->m_format = CMP_FORMAT_ASTC; + pMipSet->m_nBlockWidth = 4; + pMipSet->m_nBlockHeight = 4; + break; + case VK_FORMAT_ASTC_5x4_UNORM_BLOCK: + pMipSet->m_format = CMP_FORMAT_ASTC; + pMipSet->m_nBlockWidth = 5; + pMipSet->m_nBlockHeight = 4; + break; + case VK_FORMAT_ASTC_5x5_UNORM_BLOCK: + pMipSet->m_format = CMP_FORMAT_ASTC; + pMipSet->m_nBlockWidth = 5; + pMipSet->m_nBlockHeight = 5; + break; + case VK_FORMAT_ASTC_6x5_UNORM_BLOCK: + pMipSet->m_format = CMP_FORMAT_ASTC; + pMipSet->m_nBlockWidth = 6; + pMipSet->m_nBlockHeight = 5; + break; + case VK_FORMAT_ASTC_6x6_UNORM_BLOCK: + pMipSet->m_format = CMP_FORMAT_ASTC; + pMipSet->m_nBlockWidth = 6; + pMipSet->m_nBlockHeight = 6; + break; + case VK_FORMAT_ASTC_8x5_UNORM_BLOCK: + pMipSet->m_format = CMP_FORMAT_ASTC; + pMipSet->m_nBlockWidth = 8; + pMipSet->m_nBlockHeight = 5; + break; + case VK_FORMAT_ASTC_8x6_UNORM_BLOCK: + pMipSet->m_format = CMP_FORMAT_ASTC; + pMipSet->m_nBlockWidth = 8; + pMipSet->m_nBlockHeight = 6; + break; + case VK_FORMAT_ASTC_8x8_UNORM_BLOCK: + pMipSet->m_format = CMP_FORMAT_ASTC; + pMipSet->m_nBlockWidth = 8; + pMipSet->m_nBlockHeight = 8; + break; + case VK_FORMAT_ASTC_10x5_UNORM_BLOCK: + pMipSet->m_format = CMP_FORMAT_ASTC; + pMipSet->m_nBlockWidth = 10; + pMipSet->m_nBlockHeight = 5; + break; + case VK_FORMAT_ASTC_10x6_UNORM_BLOCK: + pMipSet->m_format = CMP_FORMAT_ASTC; + pMipSet->m_nBlockWidth = 10; + pMipSet->m_nBlockHeight = 6; + break; + case VK_FORMAT_ASTC_10x8_UNORM_BLOCK: + pMipSet->m_format = CMP_FORMAT_ASTC; + pMipSet->m_nBlockWidth = 10; + pMipSet->m_nBlockHeight = 8; + break; + case VK_FORMAT_ASTC_10x10_UNORM_BLOCK: + pMipSet->m_format = CMP_FORMAT_ASTC; + pMipSet->m_nBlockWidth = 10; + pMipSet->m_nBlockHeight = 10; + break; + case VK_FORMAT_ASTC_12x10_UNORM_BLOCK: + pMipSet->m_format = CMP_FORMAT_ASTC; + pMipSet->m_nBlockWidth = 12; + pMipSet->m_nBlockHeight = 10; + break; + case VK_FORMAT_ASTC_12x12_UNORM_BLOCK: + pMipSet->m_format = CMP_FORMAT_ASTC; + pMipSet->m_nBlockWidth = 12; + pMipSet->m_nBlockHeight = 12; + break; + } + } + else + { + pMipSet->m_compressed = false; + + switch (glType) + { + case GL_UNSIGNED_BYTE: + pMipSet->m_ChannelFormat = CF_8bit; + switch (glFormat) + { + case GL_RED: + pMipSet->m_format = CMP_FORMAT_R_8; + pMipSet->m_TextureDataType = TDT_R; + break; + case GL_RG: + pMipSet->m_format = CMP_FORMAT_RG_8; + pMipSet->m_TextureDataType = TDT_RG; + break; + case GL_RGB: + pMipSet->m_format = CMP_FORMAT_RGB_888; + pMipSet->m_TextureDataType = TDT_XRGB; + break; + case GL_RGBA: + case GL_RGBA8: + pMipSet->m_format = CMP_FORMAT_ARGB_8888; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_BGR: + pMipSet->m_swizzle = true; + pMipSet->m_format = CMP_FORMAT_RGB_888; + pMipSet->m_TextureDataType = TDT_XRGB; + break; + case GL_BGRA: + pMipSet->m_swizzle = true; + pMipSet->m_format = CMP_FORMAT_ARGB_8888; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + } + break; + case GL_UNSIGNED_SHORT: + pMipSet->m_ChannelFormat = CF_16bit; + switch (glFormat) + { + case GL_RED: + pMipSet->m_format = CMP_FORMAT_R_16; + pMipSet->m_TextureDataType = TDT_R; + break; + case GL_RG: + pMipSet->m_format = CMP_FORMAT_RG_16; + pMipSet->m_TextureDataType = TDT_RG; + break; + case GL_RGBA: + pMipSet->m_format = CMP_FORMAT_ARGB_16; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_BGRA: + pMipSet->m_swizzle = true; + pMipSet->m_format = CMP_FORMAT_ARGB_16; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + } + break; + case GL_HALF_FLOAT: + pMipSet->m_ChannelFormat = CF_Float16; + switch (glFormat) + { + case GL_RED: + pMipSet->m_format = CMP_FORMAT_R_16F; + pMipSet->m_TextureDataType = TDT_R; + break; + case GL_RG: + pMipSet->m_format = CMP_FORMAT_RG_16F; + pMipSet->m_TextureDataType = TDT_RG; + break; + case GL_RGBA: + pMipSet->m_format = CMP_FORMAT_ARGB_16F; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_BGRA: + pMipSet->m_swizzle = true; + pMipSet->m_format = CMP_FORMAT_ARGB_16F; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + } + break; + case GL_UNSIGNED_INT_2_10_10_10_REV: + pMipSet->m_format = CMP_FORMAT_ARGB_2101010; + pMipSet->m_TextureDataType = TDT_ARGB; + pMipSet->m_ChannelFormat = CF_2101010; + break; + case GL_FLOAT: + pMipSet->m_ChannelFormat = CF_Float32; + switch (glFormat) + { + case GL_RED: + pMipSet->m_format = CMP_FORMAT_R_32F; + pMipSet->m_TextureDataType = TDT_R; + break; + case GL_RG: + pMipSet->m_format = CMP_FORMAT_RG_32F; + pMipSet->m_TextureDataType = TDT_RG; + break; + case GL_RGBA: + pMipSet->m_format = CMP_FORMAT_ARGB_32F; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + case GL_BGRA: + pMipSet->m_swizzle = true; + pMipSet->m_format = CMP_FORMAT_ARGB_32F; + pMipSet->m_TextureDataType = TDT_ARGB; + break; + } + break; + break; + default: + if (KTX2_CMips) + { + KTX2_CMips->PrintError(("Error(%d): KTX2 Plugin ID(%d) unsupported GL format %x\n"), EL_Error, IDS_ERROR_UNSUPPORTED_TYPE, glFormat); + } + return -1; + } + } + + if (texture->isCubemap) + { + pMipSet->m_TextureType = TT_CubeMap; + } + else if (texture->baseDepth > 1 && texture->numFaces == 1) + { + pMipSet->m_TextureType = TT_VolumeTexture; + } + else if (texture->baseDepth == 1 && texture->numFaces == 1) + { + pMipSet->m_TextureType = TT_2D; + } + else + { + if (KTX2_CMips) + { + KTX2_CMips->PrintError(("Error(%d): KTX2 Plugin ID(%d) unsupported texture format\n"), EL_Error, IDS_ERROR_UNSUPPORTED_TYPE); + } + return -1; + } + + pMipSet->m_nMipLevels = texture->numLevels; + + // Allocate MipSet header + KTX2_CMips->AllocateMipSet( + pMipSet, pMipSet->m_ChannelFormat, pMipSet->m_TextureDataType, pMipSet->m_TextureType, texture->baseWidth, texture->baseHeight, texture->numFaces); + + int w = pMipSet->m_nWidth; + int h = pMipSet->m_nHeight; + + unsigned int totalByteRead = 0; + + unsigned int faceSize = 0; + unsigned int MipSetdataSize = 0; + unsigned int numArrayElement = texture->numLayers; + unsigned int TexturedataSize = texture->dataSize; // This is all data in cubemap levels and mip levels. + unsigned int TotalMipSetdataSize = 0; + + for (uint32_t nMipLevel = 0; nMipLevel < texture->numLevels; nMipLevel++) + { + if ((w <= 1) || (h <= 1)) + { + break; + } + else + { + w = max(1, pMipSet->m_nWidth >> nMipLevel); + h = max(1, pMipSet->m_nHeight >> nMipLevel); + } + + for (uint32_t face = 0; face < texture->numFaces; ++face) + { + // Determine buffer size and set Mip Set Levels + MipLevel* pMipLevel = KTX2_CMips->GetMipLevel(pMipSet, nMipLevel, face); + int channelCount = 0; + + + if (pMipSet->m_compressed) + { + // calculate the compressed miplevel size to allocate + CMP_Texture destGPUMipTexture; + destGPUMipTexture.dwSize = sizeof(CMP_Texture); + destGPUMipTexture.dwPitch = 0; + destGPUMipTexture.format = pMipSet->m_format; + destGPUMipTexture.dwWidth = w; + destGPUMipTexture.dwHeight = h; + destGPUMipTexture.nBlockWidth = pMipSet->m_nBlockWidth; + destGPUMipTexture.nBlockHeight = pMipSet->m_nBlockHeight; + MipSetdataSize = CMP_CalculateBufferSize(&destGPUMipTexture); + KTX2_CMips->AllocateCompressedMipLevelData(pMipLevel, w, h, MipSetdataSize); + TotalMipSetdataSize += pMipLevel->m_dwLinearSize; + } + else { + channelByteSize = 0; + switch (glType) + { + case GL_UNSIGNED_BYTE: + channelByteSize = 1; + break; + case GL_UNSIGNED_SHORT: + channelByteSize = 2; + break; + case GL_HALF_FLOAT: + channelByteSize = 2; + break; + case GL_FLOAT: + channelByteSize = 4; + break; + default: + return -1; + } + + switch (glFormat) + { + case GL_RED: + channelCount = 1; + break; + case GL_RG: + channelCount = 2; + break; + case GL_RGB: + channelCount = 3; + break; + case GL_BGR: + channelCount = 3; + break; + case GL_RGBA: + channelCount = 4; + break; + case GL_BGRA: + channelCount = 4; + break; + default: + return -1; + } + + KTX2_CMips->AllocateMipLevelData(pMipLevel, w, h, pMipSet->m_ChannelFormat, pMipSet->m_TextureDataType); + MipSetdataSize = pMipLevel->m_dwLinearSize; + + } + + CMP_BYTE* pData = (CMP_BYTE*)(pMipLevel->m_pbData); + + if (!pData) + { + if (KTX2_CMips) + { + KTX2_CMips->PrintError( + ("Error(%d): KTX2 Plugin ID(%d) Read image data failed, Out of Memory. Format %x\n"), EL_Error, IDS_ERROR_UNSUPPORTED_TYPE, glFormat); + } + return -1; + } + + // + // Read image data into temporary buffer + // + + ktx_size_t offset = 0; + KTX_error_code dataStatus = ktxTexture_GetImageOffset(ktxTexture(texture), nMipLevel, 0, face, &offset); + + if (dataStatus != KTX_SUCCESS) + { + if (KTX2_CMips) + { + KTX2_CMips->PrintError("Error(%d): KTX2 Plugin Read image data offset at %d failed\n", dataStatus,offset); + } + return -1; + } + + + uint8_t* imageData = ktxTexture_GetData(ktxTexture(texture)) + offset; + + if (imageData == nullptr) + { + if (KTX2_CMips) + { + KTX2_CMips->PrintError("Error: KTX2 Plugin Read image data at offset %d is null\n", offset); + } + return -1; + } + + if (!pMipSet->m_compressed) + { + size_t readSize = channelByteSize * channelCount * w * h; + std::vector pixelData(readSize); + + memcpy(&pixelData[0], imageData, readSize); + + int pixelSize = channelCount * channelByteSize; + int targetPixelSize = channelCount * channelByteSize; + if (channelCount == 3) + { + // XRGB conversion. + targetPixelSize = 4 * channelByteSize; + } + + int py = 0; + for (py = 0; py < h; py++) + { + int px = 0; + for (px = 0; px < w; px++) + { + memcpy(&pData[targetPixelSize * px + py * targetPixelSize * w], &pixelData[pixelSize * px + py * pixelSize * w], pixelSize); + } + } + } + else + { + if (TotalMipSetdataSize <= TexturedataSize) + memcpy(pData, imageData, MipSetdataSize); + else + { + if (KTX2_CMips) + { + KTX2_CMips->PrintError("Error: KTX2 Plugin MipSetdataSize error (%d, %d)\n", TexturedataSize, MipSetdataSize); + } + return -1; + } + + } + } + } + } + catch (...) + { + if (KTX2_CMips) + { + KTX2_CMips->PrintError("Error KTX2 Plugin Exception: \n"); + } + return -1; + } + + + return 0; +} + +int Plugin_KTX2::TC_PluginFileSaveTexture(const char* pszFilename, MipSet* pMipSet) +{ + assert(pszFilename); + assert(pMipSet); + assert(pszFilename); + assert(pMipSet); + + if (pMipSet->m_pMipLevelTable == NULL) + { + if (KTX2_CMips) + KTX2_CMips->PrintError(("Error(%d): KTX2 Plugin ID(%d) saving file = %s "), EL_Error, IDS_ERROR_ALLOCATEMIPSET, pszFilename); + return -1; + } + + if (KTX2_CMips->GetMipLevel(pMipSet, 0) == NULL) + { + if (KTX2_CMips) + KTX2_CMips->PrintError(("Error(%d): KTX2 Plugin ID(%d) saving file = %s "), EL_Error, IDS_ERROR_ALLOCATEMIPSET, pszFilename); + return -1; + } + + ktxTextureCreateInfo textureCreateInfo; + /*!< Internal format for the texture, e.g., GL_RGB8. Ignored when creating a ktxTexture2. */ + textureCreateInfo.baseWidth = pMipSet->m_nWidth; /*!< Width of the base level of the texture. */ + textureCreateInfo.baseHeight = pMipSet->m_nHeight; /*!< Height of the base level of the texture. */ + textureCreateInfo.baseDepth = 1; /*!< Depth of the base level of the texture. */ + textureCreateInfo.numDimensions = 2; /*!< Number of dimensions in the texture, 1, 2 or 3. */ + textureCreateInfo.numLevels = pMipSet->m_nMipLevels; /*!< Number of mip levels in the texture. Should be 1 if @c generateMipmaps is KTX_TRUE; */ + textureCreateInfo.numLayers = 1; /*!< Number of array layers in the texture. */ + textureCreateInfo.numFaces = (pMipSet->m_TextureType == TT_CubeMap) ? 6 : 1; /*!< Number of faces: 6 for cube maps, 1 otherwise. */ + textureCreateInfo.isArray = KTX_FALSE; /*!< Set to KTX_TRUE if the texture is to be an array texture. Means OpenGL will use a GL_TEXTURE_*_ARRAY target. */ + textureCreateInfo.generateMipmaps = KTX_FALSE; /*!< Set to KTX_TRUE if mipmaps should be generated for the texture when loading into a 3D API. */ + textureCreateInfo.pDfd = nullptr; + textureCreateInfo.vkFormat = VK_FORMAT_UNDEFINED; + + bool isCompressed = CMP_IsCompressedFormat(pMipSet->m_format); + + switch (pMipSet->m_TextureDataType) + { + case TDT_R: + { //single component-- can be Luminance and Alpha case, here only cover R + if (!isCompressed) + { + // GL_R8; + textureCreateInfo.vkFormat = VK_FORMAT_R8_UNORM; + if (pMipSet->m_ChannelFormat == CF_Float16) + { + // GL_R16F; + textureCreateInfo.vkFormat = VK_FORMAT_R16_SFLOAT; + } + else if (pMipSet->m_ChannelFormat == CF_Float32) + { + // GL_R32F; + textureCreateInfo.vkFormat = VK_FORMAT_R32_SFLOAT; + } + } + else + { + // GL_RED; + textureCreateInfo.vkFormat = VK_FORMAT_R8_UNORM; + } + } + break; + case TDT_RG: + { //two component + if (!isCompressed) + { + // GL_RG8; + textureCreateInfo.vkFormat = VK_FORMAT_R8G8_UNORM; + if (pMipSet->m_ChannelFormat == CF_Float16) + { + // GL_RG16F; + textureCreateInfo.vkFormat = VK_FORMAT_R16G16_SFLOAT; + } + else if (pMipSet->m_ChannelFormat == CF_Float32) + { + // GL_RG32F; + textureCreateInfo.vkFormat = VK_FORMAT_R32G32_SFLOAT; + } + } + else + { + // GL_COMPRESSED_RG; + // TODO: KTX2/Vulkan + } + } + break; + case TDT_XRGB: + { //normally 3 component + if (!isCompressed) + { + // GL_RGB8; + textureCreateInfo.vkFormat = VK_FORMAT_R8G8B8_UNORM; + if (pMipSet->m_ChannelFormat == CF_Float16) + { + // GL_RGB16F; + textureCreateInfo.vkFormat = VK_FORMAT_R16G16B16_SFLOAT; + } + else if (pMipSet->m_ChannelFormat == CF_Float32) + { + // GL_RGB32F; + textureCreateInfo.vkFormat = VK_FORMAT_R32G32B32_SFLOAT; + } + } + else + { + if (pMipSet->m_format == CMP_FORMAT_BC1 || pMipSet->m_format == CMP_FORMAT_DXT1) + { + // GL_COMPRESSED_RGB_S3TC_DXT1_EXT; + // TODO: KTX2/Vulkan + } + else + { + // GL_RGB8; + textureCreateInfo.vkFormat = VK_FORMAT_R8G8B8_UNORM; + if (pMipSet->m_ChannelFormat == CF_Float16) + { + // GL_RGB16F; + textureCreateInfo.vkFormat = VK_FORMAT_R16G16B16_SFLOAT; + } + else if (pMipSet->m_ChannelFormat == CF_Float32) + { + // GL_RGB32F; + textureCreateInfo.vkFormat = VK_FORMAT_R32G32B32_SFLOAT; + } + } + } + } + break; + case TDT_RGB: + { //3 component uncompressed formats + // GL_RGB8; + textureCreateInfo.vkFormat = VK_FORMAT_R8G8B8_UNORM; + if (pMipSet->m_ChannelFormat == CF_Float16) + { + // GL_RGB16F; + textureCreateInfo.vkFormat = VK_FORMAT_R16G16B16_SFLOAT; + } + else if (pMipSet->m_ChannelFormat == CF_Float32) + { + // GL_RGB32F; + textureCreateInfo.vkFormat = VK_FORMAT_R32G32B32_SFLOAT; + } + } + break; + case TDT_ARGB: + { //4 component + if (!isCompressed) + { + // GL_RGBA8; + textureCreateInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM; + if (pMipSet->m_ChannelFormat == CF_Float16) + { + // GL_RGBA16F; + textureCreateInfo.vkFormat = VK_FORMAT_R16G16B16A16_SFLOAT; + } + else if (pMipSet->m_ChannelFormat == CF_Float32) + { + // GL_RGBA32F; + textureCreateInfo.vkFormat = VK_FORMAT_R32G32B32A32_SFLOAT; + } + } + else + { + switch (pMipSet->m_format) + { + case CMP_FORMAT_BC1: + case CMP_FORMAT_DXT1: + // GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + textureCreateInfo.vkFormat = VK_FORMAT_BC1_RGB_UNORM_BLOCK; + break; + case CMP_FORMAT_BC2: + case CMP_FORMAT_DXT3: + // GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + textureCreateInfo.vkFormat = VK_FORMAT_BC2_UNORM_BLOCK; + break; + + case CMP_FORMAT_BC3: + case CMP_FORMAT_DXT5: + // GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + textureCreateInfo.vkFormat = VK_FORMAT_BC3_UNORM_BLOCK; + break; + + case CMP_FORMAT_BC4: + // GL_COMPRESSED_RED_RGTC1; + textureCreateInfo.vkFormat = VK_FORMAT_BC4_UNORM_BLOCK; + break; + case CMP_FORMAT_BC4_S: + // GL_COMPRESSED_SIGNED_RED_RGTC1; + textureCreateInfo.vkFormat = VK_FORMAT_BC4_SNORM_BLOCK; + break; + case CMP_FORMAT_BC5: + // GL_COMPRESSED_RG_RGTC2; + textureCreateInfo.vkFormat = VK_FORMAT_BC5_UNORM_BLOCK; + break; + case CMP_FORMAT_BC5_S: + // GL_COMPRESSED_SIGNED_RG_RGTC2; + textureCreateInfo.vkFormat = VK_FORMAT_BC5_SNORM_BLOCK; + break; + case CMP_FORMAT_BC6H: + // GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT; + textureCreateInfo.vkFormat = VK_FORMAT_BC6H_UFLOAT_BLOCK; + break; + case CMP_FORMAT_BC6H_SF: + // GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT; + textureCreateInfo.vkFormat = VK_FORMAT_BC6H_SFLOAT_BLOCK; + break; + case CMP_FORMAT_BC7: + // RGB_BP_UNorm; + textureCreateInfo.vkFormat = VK_FORMAT_BC7_UNORM_BLOCK; + break; + //case CMP_FORMAT_ATI1N: + // // COMPRESSED_FORMAT_ATI1N_UNorm_TMP; + // textureCreateInfo.vkFormat = VK_FORMAT_BC4_UNORM_BLOCK; + // break; + //case CMP_FORMAT_ATI2N: + // // COMPRESSED_FORMAT_ATI2N_UNorm_TMP; + // textureCreateInfo.vkFormat = VK_FORMAT_BC5_UNORM_BLOCK; + // break; + //case CMP_FORMAT_ATI2N_XY: + // // COMPRESSED_FORMAT_ATI2N_XY_UNorm_TMP; + // textureCreateInfo.vkFormat = VK_FORMAT_BC5_UNORM_BLOCK; + // // break; + // case CMP_FORMAT_ATC_RGB: + // // ATC_RGB_AMD; + // textureCreateInfo.vkFormat = VK_FORMAT_UNDEFINED; + // break; + // case CMP_FORMAT_ATC_RGBA_Explicit: + // // ATC_RGBA_EXPLICIT_ALPHA_AMD; + // textureCreateInfo.vkFormat = VK_FORMAT_UNDEFINED; + // break; + // case CMP_FORMAT_ATC_RGBA_Interpolated: + // // ATC_RGBA_INTERPOLATED_ALPHA_AMD; + // textureCreateInfo.vkFormat = VK_FORMAT_UNDEFINED; + // break; + case CMP_FORMAT_ETC_RGB: + // GL_ETC1_RGB8_OES; + textureCreateInfo.vkFormat = VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK; + break; + case CMP_FORMAT_ETC2_RGB: + // GL_COMPRESSED_RGB8_ETC2; + textureCreateInfo.vkFormat = VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK; + break; + case CMP_FORMAT_ETC2_SRGB: + // GL_COMPRESSED_SRGB8_ETC2; + textureCreateInfo.vkFormat = VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK; + break; + case CMP_FORMAT_ETC2_RGBA: + // GL_COMPRESSED_RGBA8_ETC2_EAC; + textureCreateInfo.vkFormat = VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK; + break; + case CMP_FORMAT_ETC2_RGBA1: + // GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; + textureCreateInfo.vkFormat = VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK; + break; + case CMP_FORMAT_ETC2_SRGBA: + // GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC; + textureCreateInfo.vkFormat = VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK; + break; + case CMP_FORMAT_ETC2_SRGBA1: + // GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2; + textureCreateInfo.vkFormat = VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK; + break; + + // Not supported by GL_COMPRESSION_ formats + // case CMP_FORMAT_DXT5_xGBR: + // // COMPRESSED_FORMAT_DXT5_xGBR_TMP; + // textureCreateInfo.vkFormat = VK_FORMAT_BC3_UNORM_BLOCK; + // break; + // case CMP_FORMAT_DXT5_RxBG: + // // COMPRESSED_FORMAT_DXT5_RxBG_TMP; + // textureCreateInfo.vkFormat = VK_FORMAT_BC3_UNORM_BLOCK; + // break; + // case CMP_FORMAT_DXT5_RBxG: + // // COMPRESSED_FORMAT_DXT5_RBxG_TMP; + // textureCreateInfo.vkFormat = VK_FORMAT_BC3_UNORM_BLOCK; + // break; + // case CMP_FORMAT_DXT5_xRBG: + // // COMPRESSED_FORMAT_DXT5_xRBG_TMP; + // textureCreateInfo.vkFormat = VK_FORMAT_BC3_UNORM_BLOCK; + // break; + // case CMP_FORMAT_DXT5_RGxB: + // // COMPRESSED_FORMAT_DXT5_RGxB_TMP; + // textureCreateInfo.vkFormat = VK_FORMAT_BC3_UNORM_BLOCK; + // break; + // case CMP_FORMAT_DXT5_xGxR: + // // COMPRESSED_FORMAT_DXT5_xGxR_TMP; + // textureCreateInfo.vkFormat = VK_FORMAT_BC3_UNORM_BLOCK; + // break; + case CMP_FORMAT_ASTC: + if ((pMipSet->m_nBlockWidth == 4) && (pMipSet->m_nBlockHeight == 4)) + { + // GL_COMPRESSED_RGBA_ASTC_4x4_KHR; + textureCreateInfo.vkFormat = VK_FORMAT_ASTC_4x4_UNORM_BLOCK; + } + else if ((pMipSet->m_nBlockWidth == 5) && (pMipSet->m_nBlockHeight == 4)) + { + // GL_COMPRESSED_RGBA_ASTC_5x4_KHR; + textureCreateInfo.vkFormat = VK_FORMAT_ASTC_5x4_UNORM_BLOCK; + } + else if ((pMipSet->m_nBlockWidth == 5) && (pMipSet->m_nBlockHeight == 5)) + { + // GL_COMPRESSED_RGBA_ASTC_5x5_KHR; + textureCreateInfo.vkFormat = VK_FORMAT_ASTC_5x5_UNORM_BLOCK; + } + else if ((pMipSet->m_nBlockWidth == 6) && (pMipSet->m_nBlockHeight == 5)) + { + // GL_COMPRESSED_RGBA_ASTC_6x5_KHR; + textureCreateInfo.vkFormat = VK_FORMAT_ASTC_6x5_UNORM_BLOCK; + } + else if ((pMipSet->m_nBlockWidth == 6) && (pMipSet->m_nBlockHeight == 6)) + { + // GL_COMPRESSED_RGBA_ASTC_6x6_KHR; + textureCreateInfo.vkFormat = VK_FORMAT_ASTC_6x6_UNORM_BLOCK; + } + else if ((pMipSet->m_nBlockWidth == 8) && (pMipSet->m_nBlockHeight == 5)) + { + // GL_COMPRESSED_RGBA_ASTC_8x5_KHR; + textureCreateInfo.vkFormat = VK_FORMAT_ASTC_8x5_UNORM_BLOCK; + } + else if ((pMipSet->m_nBlockWidth == 8) && (pMipSet->m_nBlockHeight == 6)) + { + // GL_COMPRESSED_RGBA_ASTC_8x6_KHR; + textureCreateInfo.vkFormat = VK_FORMAT_ASTC_8x6_UNORM_BLOCK; + } + else if ((pMipSet->m_nBlockWidth == 8) && (pMipSet->m_nBlockHeight == 8)) + { + // GL_COMPRESSED_RGBA_ASTC_8x8_KHR; + textureCreateInfo.vkFormat = VK_FORMAT_ASTC_8x8_UNORM_BLOCK; + } + else if ((pMipSet->m_nBlockWidth == 10) && (pMipSet->m_nBlockHeight == 5)) + { + // GL_COMPRESSED_RGBA_ASTC_10x5_KHR; + textureCreateInfo.vkFormat = VK_FORMAT_ASTC_10x5_UNORM_BLOCK; + } + else if ((pMipSet->m_nBlockWidth == 10) && (pMipSet->m_nBlockHeight == 6)) + { + // GL_COMPRESSED_RGBA_ASTC_10x6_KHR; + textureCreateInfo.vkFormat = VK_FORMAT_ASTC_10x6_UNORM_BLOCK; + } + else if ((pMipSet->m_nBlockWidth == 10) && (pMipSet->m_nBlockHeight == 8)) + { + // GL_COMPRESSED_RGBA_ASTC_10x8_KHR; + textureCreateInfo.vkFormat = VK_FORMAT_ASTC_10x8_UNORM_BLOCK; + } + else if ((pMipSet->m_nBlockWidth == 10) && (pMipSet->m_nBlockHeight == 10)) + { + // GL_COMPRESSED_RGBA_ASTC_10x10_KHR; + textureCreateInfo.vkFormat = VK_FORMAT_ASTC_10x10_UNORM_BLOCK; + } + else if ((pMipSet->m_nBlockWidth == 12) && (pMipSet->m_nBlockHeight == 10)) + { + // GL_COMPRESSED_RGBA_ASTC_12x10_KHR; + textureCreateInfo.vkFormat = VK_FORMAT_ASTC_12x10_UNORM_BLOCK; + } + else if ((pMipSet->m_nBlockWidth == 12) && (pMipSet->m_nBlockHeight == 12)) + { + // GL_COMPRESSED_RGBA_ASTC_12x12_KHR; + textureCreateInfo.vkFormat = VK_FORMAT_ASTC_12x12_UNORM_BLOCK; + } + else + { + // GL_COMPRESSED_RGBA_ASTC_4x4_KHR; + textureCreateInfo.vkFormat = VK_FORMAT_ASTC_4x4_UNORM_BLOCK; + } + break; + case CMP_FORMAT_BASIS: + // GL_RGBA8; + textureCreateInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM; + if (pMipSet->m_ChannelFormat == CF_Float16) + { + // GL_RGBA16F; + textureCreateInfo.vkFormat = VK_FORMAT_R16G16B16A16_SFLOAT; + } + else if (pMipSet->m_ChannelFormat == CF_Float32) + { + // GL_RGBA32F; + textureCreateInfo.vkFormat = VK_FORMAT_R32G32B32A32_SFLOAT; + } + break; + } + } + } + break; + } + + + if (textureCreateInfo.vkFormat == VK_FORMAT_UNDEFINED) + { + if (KTX2_CMips) + KTX2_CMips->PrintError("Error: KTX2 plugin. Destination format is not supported.\n"); + return -1; + } + + + ktxTexture2* texture2 = nullptr; + ktxTexture* texture = nullptr; + + KTX_error_code createStatus; + createStatus = ktxTexture2_Create(&textureCreateInfo, KTX_TEXTURE_CREATE_ALLOC_STORAGE, &texture2); + + texture = ktxTexture(texture2); + + if (createStatus != KTX_SUCCESS) + { + if (KTX2_CMips) + { + switch (createStatus) + { + case KTX_UNSUPPORTED_TEXTURE_TYPE: + KTX2_CMips->PrintError("Error(KTX2 UNSUPPORTED TEXTURE TYPE) saving file = %s \n", pszFilename); + break; + default: + KTX2_CMips->PrintError("Error(%d): Create status KTX2 Plugin on saving file = %s \n", createStatus, pszFilename); + } + } + return -1; + } + + int nSlices = (pMipSet->m_TextureType == TT_2D) ? 1 : CMP_MaxFacesOrSlices(pMipSet, 0); + for (int nSlice = 0; nSlice < nSlices; nSlice++) + { + for (int nMipLevel = 0; nMipLevel < pMipSet->m_nMipLevels; nMipLevel++) + { + + MipLevel* pMipLevel = KTX2_CMips->GetMipLevel(pMipSet, nMipLevel, nSlice); + + if (pMipLevel) + { + KTX_error_code setMemory = ktxTexture_SetImageFromMemory(texture, + nMipLevel, + 0, + nSlice, + pMipLevel->m_pbData, + pMipLevel->m_dwLinearSize); + if (setMemory != KTX_SUCCESS) + { + KTX2_CMips->PrintError("Error(%d):SetImageFromMemory KTX2 Plugin on saving file = %s \n", setMemory, pszFilename); + return -1; + } + } + else + { + KTX2_CMips->PrintError("Error:GetMipLevel (%d,%d) KTX2 Plugin on saving file = %s \n", nMipLevel, nSlice, pszFilename); + return -1; + } + } + } + + if (pMipSet->m_format == CMP_FORMAT_BASIS) + { + ktx_uint32_t* basisQuality = reinterpret_cast(pMipSet->pData); // m_userData; + KTX_error_code basisStatus = ktxTexture2_CompressBasis(texture2, *basisQuality); + if (basisStatus != KTX_SUCCESS) + { + KTX2_CMips->PrintError("Error(%d): Basis status KTX2 Plugin on saving file = %s \n", basisStatus, pszFilename); + return -1; + } + } + + std::stringstream writer; + writeId2(writer); + ktxHashList_AddKVPair(&texture->kvDataHead, KTX_WRITER_KEY, (ktx_uint32_t)writer.str().length() + 1, writer.str().c_str()); + + KTX_error_code save = ktxTexture_WriteToNamedFile(texture, pszFilename); + if (save != KTX_SUCCESS) + { + KTX2_CMips->PrintError("Error(%d): WriteToNamedFile KTX2 Plugin on saving file = %s \n", save, pszFilename); + return -1; + } + + return 0; +} diff --git a/applications/_plugins/cimage/ktx2/ktx2.def b/applications/_plugins/cimage/ktx2/ktx2.def new file mode 100644 index 000000000..52ee4f71f --- /dev/null +++ b/applications/_plugins/cimage/ktx2/ktx2.def @@ -0,0 +1,17 @@ +; TGA.def : Declares the module parameters for the DLL. + +LIBRARY TGA + +EXPORTS + TC_PluginInitialise + TC_PluginGetVersion + TC_PluginLoad + TC_PluginUnload + TC_PluginGetAboutInfo + TC_PluginGetHelpInfo + + TC_PluginFileLoadTexture + TC_PluginFileSaveTexture + TC_PluginFileSupportsFormat + TC_PluginFileSupportsMipLevels + TC_PluginFileGetFileSaveParametersDialog diff --git a/applications/_plugins/cimage/ktx2/ktx2.h b/applications/_plugins/cimage/ktx2/ktx2.h new file mode 100644 index 000000000..fd18e90bf --- /dev/null +++ b/applications/_plugins/cimage/ktx2/ktx2.h @@ -0,0 +1,150 @@ +//===================================================================== +// Copyright 2016 (c), Advanced Micro Devices, Inc. All rights reserved. +//===================================================================== +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#ifndef _PLUGIN_IMAGE_KTX2_H +#define _PLUGIN_IMAGE_KTX2_H + +#include "plugininterface.h" +#include "stdint.h" +#include "ktx.h" +#include "ktxint.h" + +#ifdef _WIN32 +// {D755E017-0485-466E-83AF-58BF5DDA8F05} +static const GUID g_GUID = {0xd755e017, 0x485, 0x466e, {0x83, 0xaf, 0x58, 0xbf, 0x5d, 0xda, 0x8f, 0x5}}; + +#else +static const GUID g_GUID = {0}; +#endif + +#define TC_PLUGIN_VERSION_MAJOR 1 +#define TC_PLUGIN_VERSION_MINOR 0 + +class Plugin_KTX2 : public PluginInterface_Image +{ +public: + Plugin_KTX2(); + virtual ~Plugin_KTX2(); + + int TC_PluginSetSharedIO(void* Shared); + int TC_PluginGetVersion(TC_PluginVersion* pPluginVersion); + int TC_PluginFileLoadTexture(const char* pszFilename, MipSet* pMipSet); + int TC_PluginFileSaveTexture(const char* pszFilename, MipSet* pMipSet); + int TC_PluginFileLoadTexture(const char* pszFilename, CMP_Texture* srcTexture); + int TC_PluginFileSaveTexture(const char* pszFilename, CMP_Texture* srcTexture); +}; + + +struct CMP_DFD +{ + uint32_t byteLength; + uint32_t byteOffset; + uint32_t cmp_format; +}; + +typedef struct _DFDSampleType +{ + uint32_t bitOffset : 16; + uint32_t bitLength : 8; + uint32_t channelType : 8; // Includes qualifiers + uint32_t samplePosition0 : 8; + uint32_t samplePosition1 : 8; + uint32_t samplePosition2 : 8; + uint32_t samplePosition3 : 8; + uint32_t lower; + uint32_t upper; +} DFDSampleType; + + +#define IDS_ERROR_FILE_OPEN 1 +#define IDS_ERROR_NOT_KTX 2 +#define IDS_ERROR_UNSUPPORTED_TYPE 3 +#define IDS_ERROR_ALLOCATEMIPSET 4 +#define IDS_ERROR_ALLOCATEMIPSLEVELDATA 5 + +extern void* make_Plugin_KTX2(); + +// uint8_t FileIdentifier[12] = {0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A}; + +#define R_ATI1N_UNorm 0x8DBB // GL_COMPRESSED_RED_RGTC1 +#define R_ATI1N_SNorm 0x8DBC // GL_COMPRESSED_SIGNED_RED_RGTC1 +#define RG_ATI2N_UNorm 0x8DBD // GL_COMPRESSED_RG_RGTC2 +#define RG_ATI2N_SNorm 0x8DBE // GL_COMPRESSED_SIGNED_RG_RGTC2 +#define RGB_BP_UNSIGNED_FLOAT 0x8E8F // GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB //bc6 +#define RGB_BP_SIGNED_FLOAT 0x8E8E // GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB +#define RGB_BP_UNorm 0x8E8C // GL_COMPRESSED_RGBA_BPTC_UNORM_ARB //bc7 +#define ATC_RGB_AMD 0x8C92 +#define ATC_RGBA_EXPLICIT_ALPHA_AMD 0x8C93 +#define ATC_RGBA_INTERPOLATED_ALPHA_AMD 0x87EE +#define COMPRESSED_RED_RGTC1 0x8DBB //bc4 +#define COMPRESSED_RG_RGTC2 0x8DBD //bc5 ATI2_XY +#define ETC1_RGB8_OES 0x8D64 + +// Not supported by GL_COMPRESSED_ Using Custom Temp extension ranges +#define COMPRESSED_FORMAT_DXT5_xGBR_TMP 0x6000 +#define COMPRESSED_FORMAT_DXT5_RxBG_TMP 0x6001 +#define COMPRESSED_FORMAT_DXT5_RBxG_TMP 0x6002 +#define COMPRESSED_FORMAT_DXT5_xRBG_TMP 0x6003 +#define COMPRESSED_FORMAT_DXT5_RGxB_TMP 0x6004 +#define COMPRESSED_FORMAT_DXT5_xGxR_TMP 0x6005 +#define COMPRESSED_FORMAT_ATI1N_UNorm_TMP 0x6007 +#define COMPRESSED_FORMAT_ATI1N_SNorm_TMP 0x6008 +#define COMPRESSED_FORMAT_ATI2N_UNorm_TMP 0x6009 +#define COMPRESSED_FORMAT_ATI2N_SNorm_TMP 0x6010 +#define COMPRESSED_FORMAT_ATI2N_XY_UNorm_TMP 0x6011 + + +//---------------------------------------------------------------- +// Definitions from etcpack v2.74 +//---------------------------------------------------------------- + +// #define GL_SRGB 0x8C40 +// #define GL_SRGB8 0x8C41 +// #define GL_SRGB8_ALPHA8 0x8C43 +// #define GL_COMPRESSED_R11_EAC 0x9270 +// #define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 +// #define GL_COMPRESSED_RG11_EAC 0x9272 +// #define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 + +#define GL_COMPRESSED_RGB8_ETC2 0x9274 +#define GL_COMPRESSED_SRGB8_ETC2 0x9275 +#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 +#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 +#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 + +//// enums copied from GL/GL.h +//#define GL_RED 0x1903 +//#define GL_RG 0x8227 +//#define GL_RGB 0x1907 +//#define GL_RGBA 0x1908 +//#define GL_BGR 0x80E0 +//#define GL_BGRA 0x80E1 +//#define GL_LUMINANCE 0x1909 +//#define GL_LUMINANCE_ALPHA 0x190A +//#define GL_UNSIGNED_BYTE 0x1401 +//#define GL_UNSIGNED_SHORT 0x1403 +//#define GL_HALF_FLOAT 0x140B +//#define GL_FLOAT 0x1406 + +#endif diff --git a/applications/_plugins/cimage/tga/cmakelists.txt b/applications/_plugins/cimage/tga/cmakelists.txt index 210c84809..de62e8cc7 100644 --- a/applications/_plugins/cimage/tga/cmakelists.txt +++ b/applications/_plugins/cimage/tga/cmakelists.txt @@ -1,17 +1,27 @@ +cmake_minimum_required(VERSION 3.10) -add_library(Plugin_CImage_TGA) +add_library(Image_TGA STATIC "") -target_sources(Plugin_CImage_TGA PRIVATE +target_sources(Image_TGA + PRIVATE + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/tc_pluginapi.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/tc_plugininternal.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/tc_plugininternal.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/utilfuncs.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/utilfuncs.cpp + ./tga.cpp + ./tga.h + ) - tga.cpp - tga.h -) +target_include_directories(Image_TGA + PRIVATE + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half + ${PROJECT_SOURCE_DIR}/applications/_plugins/common + ) -target_link_libraries(Plugin_CImage_TGA PRIVATE - CompressonatorLIB - CMP_FrameworkLIB - Plugin_TCPluginAPI - Plugin_Common_UtilFuncs -) +if (UNIX) + target_compile_definitions(Image_TGA PRIVATE _LINUX) +endif() -set_target_properties(Plugin_CImage_TGA PROPERTIES FOLDER ${FOLDER_NAME}) +set_target_properties(Image_TGA PROPERTIES FOLDER "Plugin_Static/ImageIO") diff --git a/applications/_plugins/cmesh/mesh_compressor/cmakelists.txt b/applications/_plugins/cmesh/mesh_compressor/cmakelists.txt index 51b0a7134..e1cfd0af2 100644 --- a/applications/_plugins/cmesh/mesh_compressor/cmakelists.txt +++ b/applications/_plugins/cmesh/mesh_compressor/cmakelists.txt @@ -1,22 +1,34 @@ -add_library(Plugin_CMesh_Mesh_Compressor) +add_library(Mesh_Compressor SHARED) -target_sources(Plugin_CMesh_Mesh_Compressor +target_sources(Mesh_Compressor PRIVATE - mesh_compressor.cpp - mesh_compressor.h + mesh_compressor.cpp + mesh_compressor.h ) -target_link_libraries(Plugin_CMesh_Mesh_Compressor +target_link_libraries(Mesh_Compressor PRIVATE - CMP_MeshCompressor - Plugin_TCPluginAPI - Plugin_PluginManager + CMP_Framework + CMP_MeshCompressor ) -target_include_directories(Plugin_CMesh_Mesh_Compressor PUBLIC - +target_include_directories(Mesh_Compressor PUBLIC + PRIVATE ./ + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half + ${PROJECT_SOURCE_DIR}/applications/_plugins/common + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_mesh + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_meshcompressor/draco/src + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_meshoptimizer ) -set_target_properties(Plugin_CMesh_Mesh_Compressor PROPERTIES FOLDER ${FOLDER_NAME}) + +target_compile_definitions(Mesh_Compressor PRIVATE BUILD_AS_PLUGIN_DLL=1) + +set_target_properties(Mesh_Compressor PROPERTIES + FOLDER "Plugin_Dynamic/Mesh_Compression" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin/debug/plugins" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin/release/plugins" + ) diff --git a/applications/_plugins/cmesh/mesh_optimizer/cmakelists.txt b/applications/_plugins/cmesh/mesh_optimizer/cmakelists.txt index fe8fe1c7c..872c175a4 100644 --- a/applications/_plugins/cmesh/mesh_optimizer/cmakelists.txt +++ b/applications/_plugins/cmesh/mesh_optimizer/cmakelists.txt @@ -1,24 +1,29 @@ -add_library(Plugin_CMesh_Mesh_Optimizer) +add_library(Mesh_Optimizer SHARED) -target_sources(Plugin_CMesh_Mesh_Optimizer +target_sources(Mesh_Optimizer PRIVATE mesh_optimizer.h mesh_optimizer.cpp ) -target_include_directories(Plugin_CMesh_Mesh_Optimizer +target_include_directories(Mesh_Optimizer INTERFACE ./ + ${PROJECT_SOURCE_DIR}/applications/_plugins/common ) -target_link_libraries(Plugin_CMesh_Mesh_Optimizer +target_link_libraries(Mesh_Optimizer PRIVATE - CompressonatorLIB + CMP_Compressonator + CMP_Framework CMP_MeshOptimizer - - Plugin_PluginManager - Plugin_TCPluginAPI ) -set_target_properties(Plugin_CMesh_Mesh_Optimizer PROPERTIES FOLDER ${FOLDER_NAME}) +target_compile_definitions(Mesh_Optimizer PRIVATE BUILD_AS_PLUGIN_DLL=1) + +set_target_properties(Mesh_Optimizer PROPERTIES + FOLDER "Plugin_Dynamic/Mesh_Compression" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin/debug/plugins" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin/release/plugins" + ) diff --git a/applications/_plugins/cmesh/tootle/cmakelists.txt b/applications/_plugins/cmesh/tootle/cmakelists.txt index 38017996c..ff0c3f5a6 100644 --- a/applications/_plugins/cmesh/tootle/cmakelists.txt +++ b/applications/_plugins/cmesh/tootle/cmakelists.txt @@ -1,27 +1,49 @@ +link_directories( + ${PROJECT_SOURCE_DIR}/../common/lib/ext/directxtex/DirectXTex-jun2020b/DirectXTex/Bin/Desktop_2017/x64/Release + ${PROJECT_SOURCE_DIR}/../common/lib/ext/directxtex/DirectXTex-jun2020b/DirectXTex/Bin/Desktop_2017/x64/Debug + ) -add_library(Plugin_CMesh_Tootle) - -target_sources(Plugin_CMesh_Tootle PRIVATE +add_library(Mesh_Tootle SHARED) +target_sources(Mesh_Tootle PRIVATE mesh_tootle.cpp mesh_tootle.h option.h timer.cpp timer.h + ${PROJECT_SOURCE_DIR}/applications/_libs/CMP_Mesh/tootlelib.cpp + ${PROJECT_SOURCE_DIR}/applications/_libs/CMP_Mesh/tootlelib.h + ${PROJECT_SOURCE_DIR}/applications/_libs/CMP_Mesh/tootlepch.h + ${PROJECT_SOURCE_DIR}/applications/_libs/CMP_Mesh/tootleraytracer.cpp + ${PROJECT_SOURCE_DIR}/applications/_libs/CMP_Mesh/tootleraytracer.h + ${PROJECT_SOURCE_DIR}/applications/_libs/CMP_Mesh/overdraw.cpp ) -target_include_directories(Plugin_CMesh_Tootle PRIVATE - - ./ -) +target_include_directories(Mesh_Tootle + PRIVATE + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half + ${PROJECT_SOURCE_DIR}/applications/_plugins/common + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_mesh + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_mesh/jrt + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_math + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_meshoptimizer + ${PROJECT_SOURCE_DIR}/applications/_plugins/c3dmodel_loaders/obj + ${PROJECT_SOURCE_DIR}/../common/lib/ext/apitrace/dxsdk/Include + ) -target_link_libraries(Plugin_CMesh_Tootle +target_link_libraries(Mesh_Tootle PRIVATE - CMP_Mesh - CMP_MeshOptimizer - Plugin_C3DModel_Loaders_obj - Plugin_PluginManager - Plugin_TCPluginAPI + CMP_Compressonator + CMP_Framework + CMP_Math + CMP_Mesh ) -set_target_properties(Plugin_CMesh_Tootle PROPERTIES FOLDER ${FOLDER_NAME}) +target_compile_definitions(Mesh_Tootle PRIVATE BUILD_AS_PLUGIN_DLL=1) + +set_target_properties(Mesh_Tootle PROPERTIES + FOLDER "Plugin_Dynamic/Mesh_Compression" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin/debug/plugins" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin/release/plugins" + ) diff --git a/applications/_plugins/cmesh/tootle/mesh_tootle.cpp b/applications/_plugins/cmesh/tootle/mesh_tootle.cpp index 90b35e699..994294435 100644 --- a/applications/_plugins/cmesh/tootle/mesh_tootle.cpp +++ b/applications/_plugins/cmesh/tootle/mesh_tootle.cpp @@ -20,10 +20,11 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // + #include "mesh_tootle.h" -#include "timer.h" +#include "timer.h" #include "modeldata.h" #include "tc_pluginapi.h" #include "tc_plugininternal.h" @@ -33,7 +34,14 @@ #include #include #include + +#ifdef _CMP_CPP17_ // Build code using std::c++17 #include +namespace sfs = std::filesystem; +#else +#include +namespace sfs = std::experimental::filesystem; +#endif #include #ifdef BUILD_AS_PLUGIN_DLL @@ -122,7 +130,7 @@ int Plugin_Mesh_Tootle::CleanUp() { } void getFileName(const char *FilePathName, char *fnameExt, int maxbuffsize) { - std::filesystem::path filePath(FilePathName); + sfs::path filePath(FilePathName); snprintf(fnameExt, maxbuffsize, "%s%s", filePath.filename().c_str(), filePath.extension().c_str()); } diff --git a/applications/_plugins/cmp_gpu/directx/cmakelists.txt b/applications/_plugins/cmp_gpu/directx/cmakelists.txt index 70d192f4e..4bdb156d9 100644 --- a/applications/_plugins/cmp_gpu/directx/cmakelists.txt +++ b/applications/_plugins/cmp_gpu/directx/cmakelists.txt @@ -3,36 +3,35 @@ link_directories( ${PROJECT_SOURCE_DIR}/../common/lib/ext/directxtex/DirectXTex-jun2020b/DirectXTex/Bin/Desktop_2017/x64/Debug ) -add_library(Plugin_CMP_GPU_DXC SHARED ) +add_library(EncodeWith_DXC SHARED ) -target_sources(Plugin_CMP_GPU_DXC PUBLIC +target_sources(EncodeWith_DXC PUBLIC cdirectx.cpp cdirectx.h compute_directx.cpp compute_directx.h ) -target_link_libraries(Plugin_CMP_GPU_DXC +target_link_libraries(EncodeWith_DXC PRIVATE - CompressonatorLIB - GPU_Decode - ExtDirectX - ExtDirectXTex - Plugin_TCPluginAPI + CMP_Compressonator CMP_Framework - Plugin_PluginManager + ExtDirectX ) -target_include_directories(Plugin_CMP_GPU_DXC PRIVATE - ${PROJECT_SOURCE_DIR}/cmp_framework # compute_base.h - ${PROJECT_SOURCE_DIR}/cmp_core/shaders # common_def.h - ${PROJECT_SOURCE_DIR}/cmp_core/source # cmp_math_vec4.h - ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_math #cmp_math_common.h - ${PROJECT_SOURCE_DIR}/../common/lib/ext/directxtex/DirectXTex-jun2020b/DirectXTex +target_include_directories(EncodeWith_DXC PRIVATE + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib # compressonator.h + ${PROJECT_SOURCE_DIR}/cmp_framework # compute_base.h + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half # half.h + ${PROJECT_SOURCE_DIR}/cmp_core/shaders # common_def.h + ${PROJECT_SOURCE_DIR}/cmp_core/source # cmp_math_vec4.h + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_math # cmp_math_common.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common # tc_pluginapi.h + ${PROJECT_SOURCE_DIR}/../common/lib/ext/directxtex/DirectXTex-jun2020b/DirectXTex ) -set_target_properties(Plugin_CMP_GPU_DXC PROPERTIES - FOLDER ${FOLDER_NAME} +set_target_properties(EncodeWith_DXC PROPERTIES + FOLDER "Plugin_Dynamic/GPU_EncodeWith" RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin/debug/plugins" RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin/release/plugins" ) diff --git a/applications/_plugins/cmp_gpu/directx/vs2017/compute_directx.vcxproj b/applications/_plugins/cmp_gpu/directx/vs2017/compute_directx.vcxproj index 4247c3574..a68a8493b 100644 --- a/applications/_plugins/cmp_gpu/directx/vs2017/compute_directx.vcxproj +++ b/applications/_plugins/cmp_gpu/directx/vs2017/compute_directx.vcxproj @@ -53,7 +53,7 @@ {DF04E80A-23A1-4172-89AA-841E4D376745} Win32Proj - CMP_GPU_DXC + EncodeWith_DXC 8.1 diff --git a/applications/_plugins/cmp_gpu/gpuhw/cmakelists.txt b/applications/_plugins/cmp_gpu/gpuhw/cmakelists.txt index d544b6d4f..35c37455f 100644 --- a/applications/_plugins/cmp_gpu/gpuhw/cmakelists.txt +++ b/applications/_plugins/cmp_gpu/gpuhw/cmakelists.txt @@ -1,7 +1,7 @@ -add_library(Plugin_CMP_GPU_HW SHARED ) +add_library(EncodeWith_GPU SHARED ) -target_sources(Plugin_CMP_GPU_HW PUBLIC +target_sources(EncodeWith_GPU PUBLIC compute_gpuhw.cpp compute_gpuhw.h cgpuhw.cpp @@ -10,30 +10,30 @@ target_sources(Plugin_CMP_GPU_HW PUBLIC ${PROJECT_SOURCE_DIR}/external/glad/src/glad.c ) -target_include_directories(Plugin_CMP_GPU_HW PRIVATE - ${PROJECT_SOURCE_DIR}/cmp_framework # compute_base.h - ${PROJECT_SOURCE_DIR}/cmp_core/shaders # common_def.h - ${PROJECT_SOURCE_DIR}/cmp_core/source # cmp_math_vec4.h - ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_math #cmp_math_common.h +target_include_directories(EncodeWith_GPU PRIVATE + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib # compressonator.h + ${PROJECT_SOURCE_DIR}/cmp_framework # compute_base.h + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half # half.h + ${PROJECT_SOURCE_DIR}/cmp_core/shaders # common_def.h + ${PROJECT_SOURCE_DIR}/cmp_core/source # cmp_math_vec4.h + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_math # cmp_math_common.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common # tc_pluginapi.h ${PROJECT_SOURCE_DIR}/external/glad/include/ ${PROJECT_SOURCE_DIR}/external/glad/include/glad ) -target_link_libraries(Plugin_CMP_GPU_HW +target_link_libraries(EncodeWith_GPU PRIVATE ExtGLM ExtGLFW CMP_Framework - CompressonatorLIB - Plugin_PluginManager - Plugin_TCPluginAPI - GPU_Decode + CMP_Compressonator ) set(BUILD_PLUGIN_TARGET ${CMAKE_BINARY_DIR}/bin/debug/plugin) -set_target_properties(Plugin_CMP_GPU_HW PROPERTIES - FOLDER ${FOLDER_NAME} +set_target_properties(EncodeWith_GPU PROPERTIES + FOLDER "Plugin_Dynamic/GPU_EncodeWith" RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin/debug/plugins" RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin/release/plugins" ) diff --git a/applications/_plugins/cmp_gpu/gpuhw/compute_gpuhw.cpp b/applications/_plugins/cmp_gpu/gpuhw/compute_gpuhw.cpp index 195dea34b..6046d296b 100644 --- a/applications/_plugins/cmp_gpu/gpuhw/compute_gpuhw.cpp +++ b/applications/_plugins/cmp_gpu/gpuhw/compute_gpuhw.cpp @@ -299,13 +299,12 @@ const char* CGpuHW::GetVersion() GLuint CGpuHW::processtexture() { CMIPS CMips; - int levels; + int levels = 1; + + // requested miplevels if (m_genGPUMipMaps) { - levels = static_cast(std::log2(m_SourceInfo.m_src_width)) - 1; - if (levels > MAX_MIPLEVEL_SUPPORTED) levels = MAX_MIPLEVEL_SUPPORTED; + levels = m_kernel_options->miplevels; } - else - levels = 1; int imgChannels = 4; // add code to supprt switching to only 3 channel data! @@ -481,8 +480,7 @@ bool CGpuHW::RunKernel() glUniform1i(glGetUniformLocation(w_program, "tex"), 0); glDrawArrays(GL_TRIANGLES, 0, 2 * 3); - // if debugging then do this - glfwSwapBuffers(m_gltfwindow); // show result + glfwSwapBuffers(m_gltfwindow); glfwPollEvents(); if (m_program) @@ -624,6 +622,7 @@ CMP_ERROR CGpuHW::Compress(KernelOptions* KernelOptions, MipSet& srcTexture, Mip m_getPerfStats = KernelOptions->getPerfStats && (destTexture.m_nIterations < 1); m_genGPUMipMaps = KernelOptions->genGPUMipMaps; + m_kernel_options->miplevels = KernelOptions->miplevels; m_kernel_options->data = KernelOptions->data; m_kernel_options->size = KernelOptions->size; m_kernel_options->format = KernelOptions->format; diff --git a/applications/_plugins/cmp_gpu/opencl/cmakelists.txt b/applications/_plugins/cmp_gpu/opencl/cmakelists.txt index ad1a2669c..7612972bb 100644 --- a/applications/_plugins/cmp_gpu/opencl/cmakelists.txt +++ b/applications/_plugins/cmp_gpu/opencl/cmakelists.txt @@ -2,9 +2,9 @@ link_directories( ${PROJECT_SOURCE_DIR}/../common/lib/ext/opencl/lib/x86_64 ) -add_library(Plugin_CMP_GPU_OCL SHARED ) +add_library(EncodeWith_OCL SHARED ) -target_sources(Plugin_CMP_GPU_OCL PUBLIC +target_sources(EncodeWith_OCL PUBLIC compute_opencl.cpp compute_opencl.h @@ -12,28 +12,28 @@ target_sources(Plugin_CMP_GPU_OCL PUBLIC copencl.h ) -target_include_directories(Plugin_CMP_GPU_OCL PRIVATE - ${PROJECT_SOURCE_DIR}/cmp_framework # compute_base.h - ${PROJECT_SOURCE_DIR}/cmp_core/shaders # common_def.h - ${PROJECT_SOURCE_DIR}/cmp_core/source # cmp_math_vec4.h - ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_math #cmp_math_common.h +target_include_directories(EncodeWith_OCL PRIVATE + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib # compressonator.h + ${PROJECT_SOURCE_DIR}/cmp_framework # compute_base.h + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half # half.h + ${PROJECT_SOURCE_DIR}/cmp_core/shaders # common_def.h + ${PROJECT_SOURCE_DIR}/cmp_core/source # cmp_math_vec4.h + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_math #cmp_math_common.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common # tc_pluginapi.h ${PROJECT_SOURCE_DIR}/../common/lib/ext/opencl/include ) -target_link_libraries(Plugin_CMP_GPU_OCL +target_link_libraries(EncodeWith_OCL PRIVATE CMP_Framework - CompressonatorLIB - Plugin_PluginManager - Plugin_TCPluginAPI - GPU_Decode + CMP_Compressonator OpenCL ) set(BUILD_PLUGIN_TARGET ${CMAKE_BINARY_DIR}/bin/debug/plugin) -set_target_properties(Plugin_CMP_GPU_OCL PROPERTIES - FOLDER ${FOLDER_NAME} +set_target_properties(EncodeWith_OCL PROPERTIES + FOLDER "Plugin_Dynamic/GPU_EncodeWith" RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin/debug/plugins" RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin/release/plugins" ) diff --git a/applications/_plugins/cmp_gpu/opencl/compute_opencl.cpp b/applications/_plugins/cmp_gpu/opencl/compute_opencl.cpp index 406f48f20..0a1526e43 100644 --- a/applications/_plugins/cmp_gpu/opencl/compute_opencl.cpp +++ b/applications/_plugins/cmp_gpu/opencl/compute_opencl.cpp @@ -300,7 +300,7 @@ void COpenCL::Init() { m_compile_options[0] = 0; // Make all warnings into errors, use -w to Inhitit all warning messages - strncat_s(m_compile_options, cmp_opt_size, "-Werror ", _TRUNCATE); + // strncat_s(m_compile_options, cmp_opt_size, "-Werror ", _TRUNCATE); // single and double precision denormalized numbers may be flushed to zero strncat_s(m_compile_options, cmp_opt_size, "-cl-denorms-are-zero ", _TRUNCATE); diff --git a/applications/_plugins/cmp_gpu/opencl/vs2017/compute_opencl.vcxproj b/applications/_plugins/cmp_gpu/opencl/vs2017/compute_opencl.vcxproj index 780caad7c..709e6b137 100644 --- a/applications/_plugins/cmp_gpu/opencl/vs2017/compute_opencl.vcxproj +++ b/applications/_plugins/cmp_gpu/opencl/vs2017/compute_opencl.vcxproj @@ -53,7 +53,7 @@ {C5C9003E-9986-431F-B8A0-15BB232ED650} Win32Proj - CMP_GPU_OCL + EncodeWith_OCL 8.1 diff --git a/applications/_plugins/common/atiformats.cpp b/applications/_plugins/common/atiformats.cpp index 98e37174b..a2623adc4 100644 --- a/applications/_plugins/common/atiformats.cpp +++ b/applications/_plugins/common/atiformats.cpp @@ -44,44 +44,72 @@ CMP_TextureTypeDesc g_TextureTypeDesc[] = { }; CMP_FormatDesc g_FormatDesc[] = { + {CMP_FORMAT_Unknown, "Unknown"}, + + // 4 channels {CMP_FORMAT_ARGB_8888, "ARGB_8888"}, - {CMP_FORMAT_RGB_888, "RGB_888"}, + {CMP_FORMAT_RGBA_8888, "RGBA_8888"}, + {CMP_FORMAT_ARGB_16F, "ARGB_16F"}, + {CMP_FORMAT_ARGB_32F, "ARGB_32F"}, + {CMP_FORMAT_RGBA_8888_S, "RGBA_8888_S"}, + +#ifdef CMP_ENABLE_TRANSCODECHANNEL_SUPPORT + + // alternate 4 channels + {CMP_FORMAT_ARGB_16, "ARGB_16"}, + {CMP_FORMAT_RGBA_8888_S, "RGBA_8888_S"}, + {CMP_FORMAT_ARGB_2101010, "ARGB_2101010"}, + + // 3 channels + {CMP_FORMAT_RGB_888, "RGB_888"}, + + // 2 channels {CMP_FORMAT_RG_8, "RG_8"}, - {CMP_FORMAT_R_8, "R_8"}, - {CMP_FORMAT_ARGB_2101010, "ARGB_2101010"}, - {CMP_FORMAT_ARGB_16, "ARGB_16"}, {CMP_FORMAT_RG_16, "RG_16"}, - {CMP_FORMAT_R_16, "R_16"}, - {CMP_FORMAT_ARGB_16F, "ARGB_16F"}, {CMP_FORMAT_RG_16F, "RG_16F"}, - {CMP_FORMAT_R_16F, "R_16F"}, - {CMP_FORMAT_ARGB_32F, "ARGB_32F"}, {CMP_FORMAT_RG_32F, "RG_32F"}, + + // 1 channel + {CMP_FORMAT_R_8, "R_8"}, + {CMP_FORMAT_R_16, "R_16"}, + {CMP_FORMAT_R_16F, "R_16F"}, {CMP_FORMAT_R_32F, "R_32F"}, - {CMP_FORMAT_DXT1, "DXT1"}, - {CMP_FORMAT_DXT3, "DXT3"}, - {CMP_FORMAT_DXT5, "DXT5"}, - {CMP_FORMAT_DXT5_xGBR, "DXT5_XGBR"}, - {CMP_FORMAT_DXT5_RxBG, "DXT5_RXBG"}, - {CMP_FORMAT_DXT5_RBxG, "DXT5_RBXG"}, - {CMP_FORMAT_DXT5_xRBG, "DXT5_XRBG"}, - {CMP_FORMAT_DXT5_RGxB, "DXT5_RGXB"}, - {CMP_FORMAT_DXT5_xGxR, "DXT5_XGXR"}, +#endif + + // Compressed + {CMP_FORMAT_ASTC, "ASTC"}, + {CMP_FORMAT_ATI1N, "ATI1N"}, {CMP_FORMAT_ATI2N, "ATI2N"}, {CMP_FORMAT_ATI2N_XY, "ATI2N_XY"}, {CMP_FORMAT_ATI2N_DXT5, "ATI2N_DXT5"}, + {CMP_FORMAT_ATC_RGB, "ATC_RGB"}, + {CMP_FORMAT_ATC_RGBA_Explicit, "ATC_RGBA_EXPLICIT"}, + {CMP_FORMAT_ATC_RGBA_Interpolated, "ATC_RGBA_INTERPOLATED"}, + {CMP_FORMAT_BC1, "BC1"}, {CMP_FORMAT_BC2, "BC2"}, {CMP_FORMAT_BC3, "BC3"}, {CMP_FORMAT_BC4, "BC4"}, - {CMP_FORMAT_BC4, "BC4_S"}, + {CMP_FORMAT_BC4_S, "BC4_S"}, {CMP_FORMAT_BC5, "BC5"}, - {CMP_FORMAT_BC5, "BC5_S"}, - {CMP_FORMAT_ATC_RGB, "ATC_RGB"}, - {CMP_FORMAT_ATC_RGBA_Explicit, "ATC_RGBA_EXPLICIT"}, - {CMP_FORMAT_ATC_RGBA_Interpolated, "ATC_RGBA_INTERPOLATED"}, + {CMP_FORMAT_BC5_S, "BC5_S"}, + {CMP_FORMAT_BC6H, "BC6H"}, + {CMP_FORMAT_BC6H_SF, "BC6H_SF" }, + {CMP_FORMAT_BC7, "BC7"}, + + + {CMP_FORMAT_DXT1, "DXT1"}, + {CMP_FORMAT_DXT3, "DXT3"}, + {CMP_FORMAT_DXT5, "DXT5"}, + {CMP_FORMAT_DXT5_xGBR, "DXT5_XGBR"}, + {CMP_FORMAT_DXT5_RxBG, "DXT5_RXBG"}, + {CMP_FORMAT_DXT5_RBxG, "DXT5_RBXG"}, + {CMP_FORMAT_DXT5_xRBG, "DXT5_XRBG"}, + {CMP_FORMAT_DXT5_RGxB, "DXT5_RGXB"}, + {CMP_FORMAT_DXT5_xGxR, "DXT5_XGXR"}, + {CMP_FORMAT_ETC_RGB, "ETC_RGB"}, {CMP_FORMAT_ETC2_RGB, "ETC2_RGB" }, {CMP_FORMAT_ETC2_RGBA, "ETC2_RGBA" }, @@ -91,18 +119,14 @@ CMP_FormatDesc g_FormatDesc[] = { {CMP_FORMAT_ETC2_SRGBA, "ETC2_SRGBA" }, {CMP_FORMAT_ETC2_SRGBA1, "ETC2_SRGBA1" }, #endif - {CMP_FORMAT_BC6H, "BC6H"}, - {CMP_FORMAT_BC6H_SF, "BC6H_SF" }, - {CMP_FORMAT_BC7, "BC7"}, - {CMP_FORMAT_ASTC, "ASTC"} #ifdef USE_GTC - ,{CMP_FORMAT_GTC, "GTC" } + {CMP_FORMAT_GTC, "GTC" }, #endif #ifdef USE_APC - ,{CMP_FORMAT_APC, "APC"} + {CMP_FORMAT_APC, "APC"}, #endif #ifdef USE_BASIS - ,{CMP_FORMAT_BASIS, "BASIS" } + {CMP_FORMAT_BASIS, "BASIS" }, #endif }; @@ -133,10 +157,11 @@ CMP_FORMAT CMP_API CMP_ParseFormat(char* pFormat) { } CMP_CHAR* GetFormatDesc(CMP_FORMAT nFormat) { - for(CMP_DWORD i = 0; i < g_dwFormatDescCount; i++) - if(nFormat == g_FormatDesc[i].nFormat) + for (CMP_DWORD i = 0; i < g_dwFormatDescCount; i++) + { + if (nFormat == g_FormatDesc[i].nFormat) return g_FormatDesc[i].pszFormatDesc; - + } return g_FormatDesc[0].pszFormatDesc; } @@ -150,8 +175,12 @@ CMP_CHAR* GetTextureTypeDesc(CMP_TextureType nTextureType) { // depthsupport void CMP_API CMP_Format2FourCC(CMP_FORMAT format, MipSet *pMipSet) { switch(format) { - case CMP_FORMAT_BC4: + case CMP_FORMAT_BC4_S: + pMipSet->m_dwFourCC = CMP_FOURCC_BC4S; + break; + + case CMP_FORMAT_BC4: case CMP_FORMAT_ATI1N: pMipSet->m_dwFourCC = CMP_FOURCC_ATI1N; break; @@ -160,11 +189,14 @@ void CMP_API CMP_Format2FourCC(CMP_FORMAT format, MipSet *pMipSet) { pMipSet->m_dwFourCC = CMP_FOURCC_ATI2N; break; - case CMP_FORMAT_BC5: case CMP_FORMAT_BC5_S: + pMipSet->m_dwFourCC = CMP_FOURCC_BC5S; + break; + + case CMP_FORMAT_BC5: case CMP_FORMAT_ATI2N_XY: pMipSet->m_dwFourCC = CMP_FOURCC_ATI2N; - pMipSet->m_dwFourCC2 = CMP_FOURCC_ATI2N_XY; + pMipSet->m_dwFourCC2 = CMP_FOURCC_ATI2N_XY; // Swizzled format break; case CMP_FORMAT_ATI2N_DXT5: @@ -268,30 +300,169 @@ void CMP_API CMP_Format2FourCC(CMP_FORMAT format, MipSet *pMipSet) { } } -CMP_BOOL CMP_API CMP_IsCompressedFormat(CMP_FORMAT format) { - switch (format) { - case CMP_FORMAT_Unknown: - case CMP_FORMAT_ARGB_8888: - case CMP_FORMAT_RGB_888: - case CMP_FORMAT_RG_8: - case CMP_FORMAT_R_8: - case CMP_FORMAT_ARGB_2101010: - case CMP_FORMAT_ARGB_16: - case CMP_FORMAT_RG_16: - case CMP_FORMAT_R_16: - case CMP_FORMAT_RGBE_32F: - case CMP_FORMAT_ARGB_16F: - case CMP_FORMAT_RG_16F: - case CMP_FORMAT_R_16F: - case CMP_FORMAT_ARGB_32F: - case CMP_FORMAT_RGB_32F: - case CMP_FORMAT_RG_32F: - case CMP_FORMAT_R_32F: - return (false); +CMP_FORMAT CMP_API CMP_FourCC2Format(CMP_DWORD fourCC) +{ + + switch (fourCC) + { + case CMP_FOURCC_BC4S: + return (CMP_FORMAT_BC4_S); + break; + + case CMP_FOURCC_ATI1N: + case CMP_FOURCC_BC4U: + return (CMP_FORMAT_BC4); // CMP_FORMAT_ATI1N: + break; + + case CMP_FOURCC_BC5S: + return (CMP_FORMAT_BC5_S); + break; + + case CMP_FOURCC_ATI2N: + case CMP_FOURCC_BC5: + case CMP_FOURCC_BC5U: + return (CMP_FORMAT_BC5); // CMP_FOURCC_ATI2N; + break; + + case CMP_FOURCC_ATI2N_XY: + return (CMP_FORMAT_ATI2N_XY); break; + + case CMP_FOURCC_ATI2N_DXT5: + return(CMP_FORMAT_ATI2N_DXT5); + break; + + case CMP_FOURCC_DXT1: + return (CMP_FORMAT_BC1); // CMP_FORMAT_DXT1: + break; + + case CMP_FOURCC_DXT3: + return (CMP_FORMAT_BC2); //CMP_FORMAT_DXT3: + break; + + case CMP_FOURCC_DXT5: + return (CMP_FORMAT_BC3); // CMP_FORMAT_DXT5: + break; + case CMP_FOURCC_DXT5_xGBR: + return (CMP_FORMAT_DXT5_xGBR); + break; + case CMP_FOURCC_DXT5_RxBG: + return (CMP_FORMAT_DXT5_RxBG); + break; + case CMP_FOURCC_DXT5_RBxG: + return (CMP_FORMAT_DXT5_RBxG); + break; + case CMP_FOURCC_DXT5_xRBG: + return (CMP_FORMAT_DXT5_xRBG); + break; + case CMP_FOURCC_DXT5_RGxB: + return (CMP_FORMAT_DXT5_RGxB); + break; + case CMP_FOURCC_DXT5_xGxR: + return (CMP_FORMAT_DXT5_xGxR); + break; + case CMP_FOURCC_ATC_RGB: + return (CMP_FORMAT_ATC_RGB); + break; + case CMP_FOURCC_ATC_RGBA_EXPLICIT: + return (CMP_FORMAT_ATC_RGBA_Explicit); + break; + case CMP_FOURCC_ATC_RGBA_INTERP: + return (CMP_FORMAT_ATC_RGBA_Interpolated); + break; + case CMP_FOURCC_ETC_RGB: + return (CMP_FORMAT_ETC_RGB); + break; + case CMP_FOURCC_ETC2_RGB: + return (CMP_FORMAT_ETC2_RGB); + break; + case CMP_FOURCC_ETC2_SRGB: + return (CMP_FORMAT_ETC2_SRGB); + break; + case CMP_FOURCC_ETC2_RGBA: + return (CMP_FORMAT_ETC2_RGBA); + break; + case CMP_FOURCC_ETC2_RGBA1: + return (CMP_FORMAT_ETC2_RGBA1); + break; + case CMP_FOURCC_ETC2_SRGBA: + return (CMP_FORMAT_ETC2_SRGBA); + break; + case CMP_FOURCC_ETC2_SRGBA1: + return (CMP_FORMAT_ETC2_SRGBA1); + break; +#ifdef USE_GTC + case CMP_FOURCC_GTC: + return (CMP_FORMAT_GTC); + break; +#endif +#ifdef USE_APC + case CMP_FOURCC_APC: + return (CMP_FORMAT_APC); + break; +#endif +#ifdef USE_BASIS + case CMP_FOURCC_BASIS: + return (CMP_FORMAT_BASIS); + break; +#endif + } + return (CMP_FORMAT_Unknown); +} + +// Duplicate of IsCompressedFormat() in compress.cpp +CMP_BOOL CMP_API CMP_IsCompressedFormat(CMP_FORMAT format) { + + switch (format) + { + case CMP_FORMAT_ASTC: + case CMP_FORMAT_ATI1N: + case CMP_FORMAT_ATI2N: + case CMP_FORMAT_ATI2N_XY: + case CMP_FORMAT_ATI2N_DXT5: + case CMP_FORMAT_ATC_RGB: + case CMP_FORMAT_ATC_RGBA_Explicit: + case CMP_FORMAT_ATC_RGBA_Interpolated: + case CMP_FORMAT_BC1: + case CMP_FORMAT_BC2: + case CMP_FORMAT_BC3: + case CMP_FORMAT_BC4: + case CMP_FORMAT_BC4_S: + case CMP_FORMAT_BC5: + case CMP_FORMAT_BC5_S: + case CMP_FORMAT_BC6H: + case CMP_FORMAT_BC6H_SF: + case CMP_FORMAT_BC7: + case CMP_FORMAT_DXT1: + case CMP_FORMAT_DXT3: + case CMP_FORMAT_DXT5: + case CMP_FORMAT_DXT5_xGBR: + case CMP_FORMAT_DXT5_RxBG: + case CMP_FORMAT_DXT5_RBxG: + case CMP_FORMAT_DXT5_xRBG: + case CMP_FORMAT_DXT5_RGxB: + case CMP_FORMAT_DXT5_xGxR: + case CMP_FORMAT_ETC_RGB: + case CMP_FORMAT_ETC2_RGB: + case CMP_FORMAT_ETC2_SRGB: + case CMP_FORMAT_ETC2_RGBA: + case CMP_FORMAT_ETC2_RGBA1: + case CMP_FORMAT_ETC2_SRGBA: + case CMP_FORMAT_ETC2_SRGBA1: + case CMP_FORMAT_PVRTC: +#ifdef USE_APC + case CMP_FORMAT_APC: //< APC Texture Compressor +#endif + case CMP_FORMAT_GTC: //< GTC Fast Gradient Texture Compressor + case CMP_FORMAT_BASIS: //< BASIS compression + { + return true; + } + break; default: break; } - return true; + + return false; } diff --git a/applications/_plugins/common/atiformats.h b/applications/_plugins/common/atiformats.h index 56090dfc5..dc34b8957 100644 --- a/applications/_plugins/common/atiformats.h +++ b/applications/_plugins/common/atiformats.h @@ -44,7 +44,8 @@ extern "C" { #endif CMP_FORMAT CMP_API CMP_ParseFormat(char* pFormat); void CMP_API CMP_Format2FourCC(CMP_FORMAT format, MipSet *pMipSet); -CMP_BOOL CMP_API CMP_IsCompressedFormat(CMP_FORMAT format); +CMP_FORMAT CMP_API CMP_FourCC2Format(CMP_DWORD fourCC); +CMP_BOOL CMP_API CMP_IsCompressedFormat(CMP_FORMAT format); #ifdef __cplusplus }; #endif diff --git a/applications/_plugins/common/cexr.h b/applications/_plugins/common/cexr.h index b3128fe86..5e607c80c 100644 --- a/applications/_plugins/common/cexr.h +++ b/applications/_plugins/common/cexr.h @@ -24,7 +24,7 @@ #ifndef cEXR_HEADER #define cEXR_HEADER -#include "namespacealias.h" +#include #pragma warning(push) #pragma warning(disable : 4100) diff --git a/applications/_plugins/common/cmakelists.txt b/applications/_plugins/common/cmakelists.txt index 5fe932c8c..8f2db3d3b 100644 --- a/applications/_plugins/common/cmakelists.txt +++ b/applications/_plugins/common/cmakelists.txt @@ -1,15 +1,73 @@ -# ======================================================================= - -add_subdirectory(c_exr) -add_subdirectory(d3dx12) -add_subdirectory(hpc_compress) -add_subdirectory(imgui) -add_subdirectory(kernel_def) -add_subdirectory(misc) -add_subdirectory(qtimgui) -add_subdirectory(query_performance) -add_subdirectory(stb_image) -add_subdirectory(test_report) -add_subdirectory(util_funcs) -add_subdirectory(vectypes) +set(PLUGIN_COMMON_SRC + atiformats.cpp + cexr.cpp + cmdline.cpp + cmp_fileio.cpp + misc.cpp + modeldata.cpp + mipstoqimage.cpp + pluginmanager.cpp + query_timer.cpp + ssim.cpp + tc_plugininternal.cpp + textureio.cpp + userinterface.cpp + utilfuncs.cpp +) + +set(PLUGIN_COMMON_H + atiformats.h + cexr.h + cmdline.h + cmp_fileio.h + common_kerneldef.h + crc32.h + hpc_compress.h + misc.h + modeldata.h + mipstoqimage.h + namespacealias.h + pluginbase.h + plugininterface.h + pluginmanager.h + query_timer.h + ssim.h + stb_image.h + tc_pluginapi.h + tc_plugininternal.h + testreport.h + texture.h + textureio.h + userinterface.h + utilfuncs.h + vectypes.h +) + +add_library(CMP_Common STATIC ${PLUGIN_COMMON_SRC} ${PLUGIN_COMMON_H}) + +target_include_directories(CMP_Common PRIVATE + . + ${PROJECT_SOURCE_DIR}/cmp_framework + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/gltf + ${PROJECT_SOURCE_DIR}/cmp_core/shaders + ${PROJECT_SOURCE_DIR}/cmp_core/source + ${PROJECT_SOURCE_DIR}/applications/_plugins/cmesh + ${PROJECT_SOURCE_DIR}/applications/_plugins/cmesh/mesh_optimizer + ${PROJECT_SOURCE_DIR}/applications/_libs/gpu_decode + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_math + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_meshoptimizer + ${PROJECT_SOURCE_DIR}/../common/lib/ext/rapidxml + ${PROJECT_SOURCE_DIR}/../common/lib/ext/glm + ${OpenCV_INCLUDE_DIRS} + ${OpenEXR_INCLUDE_DIRS} + ${draco_INCLUDE_DIRS} + ) + +target_link_libraries(CMP_Common Qt5::Widgets) + +set_target_properties(CMP_Common PROPERTIES FOLDER "Libs") + +add_subdirectory(gltf) diff --git a/applications/_plugins/common/cmdline.cpp b/applications/_plugins/common/cmdline.cpp index a55ab835e..54c000eda 100644 --- a/applications/_plugins/common/cmdline.cpp +++ b/applications/_plugins/common/cmdline.cpp @@ -33,7 +33,6 @@ #include "misc.h" #include "cmp_fileio.h" -#include #include #include @@ -43,6 +42,14 @@ #include "modeldata.h" #include "utilfuncs.h" +#ifdef _CMP_CPP17_ // Build code using std::c++17 +#include +namespace sfs = std::filesystem; +#else +#include +namespace sfs = std::experimental::filesystem; +#endif + using namespace tinygltf2; #if !defined(NO_LEGACY_BEHAVIOR) #ifdef _DEBUG @@ -85,40 +92,54 @@ using namespace std; CCmdLineParamaters g_CmdPrams; -static inline void helper_erase_all(std::string& str, const char* pErase) { +static inline void helper_erase_all(std::string& str, const char* pErase) +{ size_t pos = str.find(pErase); - while (pos != std::string::npos) { + while (pos != std::string::npos) + { str.erase(pos, 1); pos = str.find(pErase); } } -static inline void helper_to_upper(std::string& str) { +static inline void helper_to_upper(std::string& str) +{ for (char& i : str) i = toupper(i); } bool fileIsGLTF(string SourceFile); -string DefaultDestination(string SourceFile, CMP_FORMAT DestFormat, string DestFileExt) { - string DestFile = ""; - std::filesystem::path fp(SourceFile); - string file_name = fp.stem().string(); - string file_ext = fp.extension().string(); +string DefaultDestination(string SourceFile, CMP_FORMAT DestFormat, string DestFileExt, CMP_BOOL suffixDestFormat) +{ + string DestFile = ""; + sfs::path fp(SourceFile); + string file_name = fp.stem().string(); + string file_ext = fp.extension().string(); DestFile.append(file_name); - DestFile.append("_"); - file_ext.erase(std::remove(file_ext.begin(), file_ext.end(), '.'), file_ext.end()); - DestFile.append(file_ext); - DestFile.append("_"); - DestFile.append(GetFormatDesc(DestFormat)); - if (DestFileExt.find('.') != std::string::npos) { + if (suffixDestFormat) + { + DestFile.append("_"); + file_ext.erase(std::remove(file_ext.begin(), file_ext.end(), '.'), file_ext.end()); + DestFile.append(file_ext); + DestFile.append("_"); + DestFile.append(GetFormatDesc(DestFormat)); + } + else + file_ext.erase(std::remove(file_ext.begin(), file_ext.end(), '.'), file_ext.end()); + + if (DestFileExt.find('.') != std::string::npos) + { DestFile.append(DestFileExt); - } else { + } + else + { if (DestFormat == CMP_FORMAT_ASTC) DestFile.append(".astc"); - else { + else + { DestFile.append(".dds"); } } @@ -127,64 +148,42 @@ string DefaultDestination(string SourceFile, CMP_FORMAT DestFormat, string DestF } // check if file is glTF format extension -bool fileIsGLTF(string SourceFile) { - std::filesystem::path fp(SourceFile); - string file_ext = fp.extension().string(); +bool fileIsGLTF(string SourceFile) +{ + sfs::path fp(SourceFile); + string file_ext = fp.extension().string(); helper_to_upper(file_ext); return (file_ext.compare(".GLTF") == 0); } // check if file is OBJ format extension -bool fileIsOBJ(string SourceFile) { - std::filesystem::path fp(SourceFile); - string file_ext = fp.extension().string(); +bool fileIsOBJ(string SourceFile) +{ + sfs::path fp(SourceFile); + string file_ext = fp.extension().string(); helper_to_upper(file_ext); return (file_ext.compare(".OBJ") == 0); } // check if file is DRC (draco compressed OBJ file) format extension -bool fileIsDRC(string SourceFile) { - std::filesystem::path fp(SourceFile); - string file_ext = fp.extension().string(); +bool fileIsDRC(string SourceFile) +{ + sfs::path fp(SourceFile); + string file_ext = fp.extension().string(); helper_to_upper(file_ext); return (file_ext.compare(".DRC") == 0); } -bool fileIsModel(string SourceFile) { +bool fileIsModel(string SourceFile) +{ return (fileIsGLTF(SourceFile) || fileIsOBJ(SourceFile) || fileIsDRC(SourceFile)); } -#ifdef USE_WITH_COMMANDLINE_TOOL - -int GetNumberOfCores(wchar_t* envp[]) { - int i, cores = 1; - - for (i = 0; envp[i] != NULL; i++) { - //wprintf(L"var = %ws\n", envp[i]); - cores = wcsncmp(envp[i], L"NUMBER_OF_PROCESSORS", 20); - if (cores == 0) { - wchar_t* p_envp; - size_t equal = wcscspn(envp[i], L"="); - if ((equal > 0) && (equal < wcslen(envp[i]))) { - p_envp = envp[i] + equal + 1; - wchar_t num[16]; - wcsncpy_s(num, p_envp, 16); - cores = _wtoi(num); - return cores > 0 ? cores : 1; - } - break; - } - } - return 1; -} - -#else // Code is shared with GUI -#endif - -CMP_GPUDecode DecodeWith(const char* strParameter) { +CMP_GPUDecode DecodeWith(const char* strParameter) +{ if (strcmp(strParameter, "DirectX") == 0) return GPUDecode_DIRECTX; else if (strcmp(strParameter, "DXC") == 0) @@ -201,7 +200,8 @@ CMP_GPUDecode DecodeWith(const char* strParameter) { return GPUDecode_INVALID; } -CMP_Compute_type EncodeWith(const char* strParameter) { +CMP_Compute_type EncodeWith(const char* strParameter) +{ if (strcmp(strParameter, "CPU") == 0) return CMP_CPU; else if (strcmp(strParameter, "HPC") == 0) @@ -218,79 +218,117 @@ CMP_Compute_type EncodeWith(const char* strParameter) { return CMP_UNKNOWN; } -bool ProcessSingleFlags(const char* strCommand) { +bool ProcessSingleFlags(const char* strCommand) +{ bool isset = false; - if ((strcmp(strCommand, "-nomipmap") == 0)) { + if ((strcmp(strCommand, "-nomipmap") == 0)) + { g_CmdPrams.use_noMipMaps = true; isset = true; + if (g_CmdPrams.MipsLevel > 0) + throw "Both nomipmap and miplevels are set!"; } #ifdef USE_MESH_DRACO_EXTENSION - else if ((strcmp(strCommand, "-draco") == 0)) { + else if ((strcmp(strCommand, "-draco") == 0)) + { g_CmdPrams.use_Draco_Encode = true; isset = true; } #endif - else if ((strcmp(strCommand, "-silent") == 0)) { + else if ((strcmp(strCommand, "-silent") == 0)) + { g_CmdPrams.silent = true; isset = true; - } else if ((strcmp(strCommand, "-performance") == 0)) { + } + else if ((strcmp(strCommand, "-performance") == 0)) + { g_CmdPrams.showperformance = true; isset = true; - } else if ((strcmp(strCommand, "-noprogress") == 0)) { + } + else if ((strcmp(strCommand, "-noprogress") == 0)) + { g_CmdPrams.noprogressinfo = true; isset = true; - } else if ((strcmp(strCommand, "-noswizzle") == 0)) { + } + else if ((strcmp(strCommand, "-noswizzle") == 0)) + { g_CmdPrams.noswizzle = true; isset = true; - } else if ((strcmp(strCommand, "-doswizzle") == 0)) { + } + else if ((strcmp(strCommand, "-doswizzle") == 0)) + { g_CmdPrams.doswizzle = true; isset = true; - } else if ((strcmp(strCommand, "-analysis") == 0) || (strcmp(strCommand, "-Analysis") == 0)) { + } + else if ((strcmp(strCommand, "-analysis") == 0) || (strcmp(strCommand, "-Analysis") == 0)) + { g_CmdPrams.analysis = true; isset = true; - } else if ((strcmp(strCommand, "-diff_image") == 0)) { + } + else if ((strcmp(strCommand, "-diff_image") == 0)) + { g_CmdPrams.diffImage = true; isset = true; - } else if ((strcmp(strCommand, "-imageprops") == 0)) { + } + else if ((strcmp(strCommand, "-imageprops") == 0)) + { g_CmdPrams.imageprops = true; isset = true; } #ifdef USE_3DMESH_OPTIMIZE - else if ((strcmp(strCommand, "-meshopt") == 0)) { + else if ((strcmp(strCommand, "-meshopt") == 0)) + { g_CmdPrams.doMeshOptimize = true; isset = true; } #endif - else if (strcmp(strCommand, "-compress-gltf-images") == 0) { + else if (strcmp(strCommand, "-compress-gltf-images") == 0) + { g_CmdPrams.compressImagesFromGLTF = true; isset = true; - } else if ((strcmp(strCommand, "-log") == 0)) { + } + else if ((strcmp(strCommand, "-log") == 0)) + { g_CmdPrams.logcsvformat = false; g_CmdPrams.logresultsToFile = true; g_CmdPrams.logresults = true; isset = true; g_CmdPrams.LogProcessResultsFile.assign(LOG_PROCESS_RESULTS_FILE_TXT); - } else if ((strcmp(strCommand, "-logcsv") == 0)) { + } + else if ((strcmp(strCommand, "-logcsv") == 0)) + { g_CmdPrams.logcsvformat = true; g_CmdPrams.logresultsToFile = true; g_CmdPrams.logresults = true; isset = true; g_CmdPrams.LogProcessResultsFile.assign(LOG_PROCESS_RESULTS_FILE_CSV); - } else if (strcmp(strCommand, "-UseGPUDecompress") == 0) { + } + else if (strcmp(strCommand, "-UseGPUDecompress") == 0) + { g_CmdPrams.CompressOptions.bUseGPUDecompress = true; isset = true; - } else if ((strcmp(strCommand, "-GenGPUMipMaps") == 0)) { + } +#ifndef __linux__ + else if ((strcmp(strCommand, "-GenGPUMipMaps") == 0)) + { g_CmdPrams.CompressOptions.genGPUMipMaps = true; isset = true; - } else if ((strcmp(strCommand, "-UseSRGBFrames") == 0)) { + } +#endif +#ifndef __linux__ + else if ((strcmp(strCommand, "-UseSRGBFrames") == 0)) + { g_CmdPrams.CompressOptions.useSRGBFrames = true; isset = true; } +#endif return isset; } -bool ProcessCMDLineOptions(const char* strCommand, const char* strParameter) { - try { +bool ProcessCMDLineOptions(const char* strCommand, const char* strParameter) +{ + try + { // ==================================== // Prechecks prior to sending to Codec // ==================================== @@ -298,409 +336,574 @@ bool ProcessCMDLineOptions(const char* strCommand, const char* strParameter) { // Special Case for ASTC - improve this remove in next revision // This command param is passed down to ASTC Codex // We are doing an early capture to get some dimensions for ASTC file Save - if (strcmp(strCommand, "-BlockRate") == 0) { - if (strchr(strParameter, 'x') != NULL) { + if (strcmp(strCommand, "-BlockRate") == 0) + { + if (strchr(strParameter, 'x') != NULL) + { int dimensions = sscanf(strParameter, "%dx%dx", &g_CmdPrams.BlockWidth, &g_CmdPrams.BlockHeight); if (dimensions < 2) throw "Command Parameter is invalid"; - else { + else + { astc_find_closest_blockxy_2d(&g_CmdPrams.BlockWidth, &g_CmdPrams.BlockHeight, 0); } - } else { + } + else + { float m_target_bitrate = static_cast(atof(strParameter)); if (m_target_bitrate > 0) astc_find_closest_blockdim_2d(m_target_bitrate, &g_CmdPrams.BlockWidth, &g_CmdPrams.BlockHeight, 0); else throw "Command Parameter is invalid"; } - } else if (strcmp(strCommand, "-Quality") == 0) { - if (strlen(strParameter) == 0) { + } + else if (strcmp(strCommand, "-Quality") == 0) + { + if (strlen(strParameter) == 0) + { throw "No Quality value specified"; } float value = std::stof(strParameter); - if ((value < 0) || (value > 1.0)) { + if ((value < 0) || (value > 1.0)) + { throw "Quality value should be in range of 0 to 1.0"; } g_CmdPrams.CompressOptions.fquality = value; } #ifdef ENABLE_MAKE_COMPATIBLE_API - else if (strcmp(strCommand, "-InExposure") == 0) { - if (strlen(strParameter) == 0) { + else if (strcmp(strCommand, "-InExposure") == 0) + { + if (strlen(strParameter) == 0) + { throw "No input exposure value specified"; } float value = std::stof(strParameter); - if ((value < -10) || (value > 10)) { + if ((value < -10) || (value > 10)) + { throw "Input Exposure value should be in range of -10 to 10"; } g_CmdPrams.CompressOptions.fInputExposure = value; - } else if (strcmp(strCommand, "-InDefog") == 0) { - if (strlen(strParameter) == 0) { + } + else if (strcmp(strCommand, "-InDefog") == 0) + { + if (strlen(strParameter) == 0) + { throw "No input defog value specified"; } float value = std::stof(strParameter); - if ((value < 0) || (value > 0.01)) { + if ((value < 0) || (value > 0.01)) + { throw "Input Defog value should be in range of 0.0000 to 0.0100"; } g_CmdPrams.CompressOptions.fInputDefog = value; - } else if (strcmp(strCommand, "-InKneeLow") == 0) { - if (strlen(strParameter) == 0) { + } + else if (strcmp(strCommand, "-InKneeLow") == 0) + { + if (strlen(strParameter) == 0) + { throw "No input kneelow value specified"; } float value = std::stof(strParameter); - if ((value < -3) || (value > 3)) { + if ((value < -3) || (value > 3)) + { throw "Input Knee Low value should be in range of -3 to 3"; } g_CmdPrams.CompressOptions.fInputKneeLow = value; - } else if (strcmp(strCommand, "-InKneeHigh") == 0) { - if (strlen(strParameter) == 0) { + } + else if (strcmp(strCommand, "-InKneeHigh") == 0) + { + if (strlen(strParameter) == 0) + { throw "No input kneehigh value specified"; } float value = std::stof(strParameter); - if ((value < 3.5) || (value > 7.5)) { + if ((value < 3.5) || (value > 7.5)) + { throw "Input Knee High value should be in range of 3.5 to 7.5"; } g_CmdPrams.CompressOptions.fInputKneeHigh = value; - } else if (strcmp(strCommand, "-Gamma") == 0) { - if (strlen(strParameter) == 0) { + } + else if (strcmp(strCommand, "-Gamma") == 0) + { + if (strlen(strParameter) == 0) + { throw "No gamma value specified"; } float value = std::stof(strParameter); - if ((value < 1.0) || (value > 2.6)) { + if ((value < 1.0) || (value > 2.6)) + { throw "Gamma supported is in range of 1.0 to 2.6"; } g_CmdPrams.CompressOptions.fInputGamma = value; } #endif - else if (strcmp(strCommand, "-WeightR") == 0) { - if (!g_CmdPrams.CompressOptions.bUseChannelWeighting) { + else if (strcmp(strCommand, "-WeightR") == 0) + { + if (!g_CmdPrams.CompressOptions.bUseChannelWeighting) + { throw "Please enable \"-UseChannelWeighting 1\" first before setting weight for color channel"; } - if (strlen(strParameter) == 0) { + if (strlen(strParameter) == 0) + { throw "No WeightR value specified"; } float value = std::stof(strParameter); - if ((value < 0) || (value > 1.0)) { + if ((value < 0) || (value > 1.0)) + { throw "WeightR value should be in range of 0 to 1.0"; } g_CmdPrams.CompressOptions.fWeightingRed = value; - } else if (strcmp(strCommand, "-WeightG") == 0) { - if (!g_CmdPrams.CompressOptions.bUseChannelWeighting) { + } + else if (strcmp(strCommand, "-WeightG") == 0) + { + if (!g_CmdPrams.CompressOptions.bUseChannelWeighting) + { throw "Please enable \"-UseChannelWeighting 1\" first before setting weight for color channel"; } - if (strlen(strParameter) == 0) { + if (strlen(strParameter) == 0) + { throw "No WeightG value specified"; } float value = std::stof(strParameter); - if ((value < 0) || (value > 1.0)) { + if ((value < 0) || (value > 1.0)) + { throw "WeightG value should be in range of 0 to 1.0"; } g_CmdPrams.CompressOptions.fWeightingGreen = value; - } else if (strcmp(strCommand, "-WeightB") == 0) { - if (!g_CmdPrams.CompressOptions.bUseChannelWeighting) { + } + else if (strcmp(strCommand, "-WeightB") == 0) + { + if (!g_CmdPrams.CompressOptions.bUseChannelWeighting) + { throw "Please enable \"-UseChannelWeighting 1\" first before setting weight for color channel"; } - if (strlen(strParameter) == 0) { + if (strlen(strParameter) == 0) + { throw "No WeightB value specified"; } float value = std::stof(strParameter); - if ((value < 0) || (value > 1.0)) { + if ((value < 0) || (value > 1.0)) + { throw "WeightB value should be in range of 0 to 1.0"; } g_CmdPrams.CompressOptions.fWeightingBlue = value; - } else if (strcmp(strCommand, "-UseChannelWeighting") == 0) { - if (strlen(strParameter) == 0) { + } + else if (strcmp(strCommand, "-UseChannelWeighting") == 0) + { + if (strlen(strParameter) == 0) + { throw "No UseChannelWeighting value specified (default is 0:off; 1:on)"; } int value = std::stof(strParameter); - if ((value < 0) || (value > 1)) { + if ((value < 0) || (value > 1)) + { throw "UseChannelWeighting value should be 1 or 0"; } g_CmdPrams.CompressOptions.bUseChannelWeighting = bool(value); - } else if (strcmp(strCommand, "-AlphaThreshold") == 0) { - if (strlen(strParameter) == 0) { + } + else if (strcmp(strCommand, "-AlphaThreshold") == 0) + { + if (strlen(strParameter) == 0) + { throw "No Alpha threshold value specified"; } int value = std::stof(strParameter); - if ((value < 1) || (value > 255)) { + if ((value < 1) || (value > 255)) + { throw "Alpha threshold value should be in range of 1 to 255"; } g_CmdPrams.CompressOptions.nAlphaThreshold = value; - } else if (strcmp(strCommand, "-DXT1UseAlpha") == 0) { - if (strlen(strParameter) == 0) { + } + else if (strcmp(strCommand, "-DXT1UseAlpha") == 0) + { + if (strlen(strParameter) == 0) + { throw "No DXT1UseAlpha value specified (default is 0:off; 1:on)"; } int value = std::stof(strParameter); - if ((value < 0) || (value > 1)) { + if ((value < 0) || (value > 1)) + { throw "DXT1UseAlpha value should be 1 or 0"; } g_CmdPrams.CompressOptions.bDXT1UseAlpha = bool(value); g_CmdPrams.CompressOptions.nAlphaThreshold = 128; //default to 128 - } else if (strcmp(strCommand, "-DecodeWith") == 0) { - if (strlen(strParameter) == 0) { + } + else if (strcmp(strCommand, "-DecodeWith") == 0) + { + if (strlen(strParameter) == 0) + { throw "No API specified (set either OpenGL or DirectX (Default is OpenGL)."; } g_CmdPrams.CompressOptions.nGPUDecode = DecodeWith((char*)strParameter); - if (g_CmdPrams.CompressOptions.nGPUDecode == GPUDecode_INVALID) { + if (g_CmdPrams.CompressOptions.nGPUDecode == GPUDecode_INVALID) + { throw "Unknown API format specified."; } g_CmdPrams.CompressOptions.bUseGPUDecompress = true; } #ifdef USE_3DMESH_OPTIMIZE - else if (strcmp(strCommand, "-optVCacheSize") == 0) { - if (strlen(strParameter) == 0) { + else if (strcmp(strCommand, "-optVCacheSize") == 0) + { + if (strlen(strParameter) == 0) + { throw "No hardware vertices cache size specified."; } int value = std::stoi(strParameter); - if (value < 1) { + if (value < 1) + { throw "Hardware vertices cache size must be a positive value."; } g_CmdPrams.CompressOptions.iVcacheSize = value; - } else if (strcmp(strCommand, "-optVCacheFIFOSize") == 0) { - if (strlen(strParameter) == 0) { + } + else if (strcmp(strCommand, "-optVCacheFIFOSize") == 0) + { + if (strlen(strParameter) == 0) + { throw "No hardware vertices cache(FIFO replacement policy) size specified."; } int value = std::stoi(strParameter); - if (value < 1) { + if (value < 1) + { throw "Hardware vertices cache(FIFO replacement policy) size should be > 1."; } g_CmdPrams.CompressOptions.iVcacheFIFOSize = value; - } else if (strcmp(strCommand, "-optOverdrawACMRThres") == 0) { - if (strlen(strParameter) == 0) { + } + else if (strcmp(strCommand, "-optOverdrawACMRThres") == 0) + { + if (strlen(strParameter) == 0) + { throw "No ACMR threshold value specified for overdraw optimization."; } float value = std::stof(strParameter); - if ((value < 1) || (value > 3)) { + if ((value < 1) || (value > 3)) + { throw "ACMR (Average Cache Miss Ratio) should be in the range of 1 - 3."; } g_CmdPrams.CompressOptions.fOverdrawACMR = value; - } else if (strcmp(strCommand, "-simplifyMeshLOD") == 0) { - if (strlen(strParameter) == 0) { + } + else if (strcmp(strCommand, "-simplifyMeshLOD") == 0) + { + if (strlen(strParameter) == 0) + { throw "No LOD (Level of Details) specified for mesh simplication."; } int value = std::stoi(strParameter); - if (value < 1) { + if (value < 1) + { throw "LOD (Level of Details) for mesh simplication should be > 1."; } g_CmdPrams.CompressOptions.iSimplifyLOD = value; - } else if (strcmp(strCommand, "-optVFetch") == 0) { - if (strlen(strParameter) == 0) { + } + else if (strcmp(strCommand, "-optVFetch") == 0) + { + if (strlen(strParameter) == 0) + { throw "No boolean value (1 or 0) specified to enable vertex fetch optimization or not."; } int value = std::stof(strParameter); - if ((value < 0) || (value > 1)) { + if ((value < 0) || (value > 1)) + { throw "Optimize vertex fetch value should be 1(enabled) or 0(disabled)."; } g_CmdPrams.CompressOptions.bVertexFetch = bool(value); } #endif - else if (strcmp(strCommand, "-EncodeWith") == 0) { - if (strlen(strParameter) == 0) { + else if (strcmp(strCommand, "-EncodeWith") == 0) + { + if (strlen(strParameter) == 0) + { throw "No API specified."; } g_CmdPrams.CompressOptions.nEncodeWith = EncodeWith((char*)strParameter); - if (g_CmdPrams.CompressOptions.nEncodeWith == CMP_Compute_type::CMP_UNKNOWN) { - throw "Unknown API format specified."; - } - - g_CmdPrams.CompressOptions.bUseCGCompress = true; - } else if (strcmp(strCommand, "-decomp") == 0) { - if (strlen(strParameter) == 0) { + if (g_CmdPrams.CompressOptions.nEncodeWith == CMP_Compute_type::CMP_UNKNOWN) + { + g_CmdPrams.CompressOptions.nEncodeWith = CMP_Compute_type::CMP_CPU; + g_CmdPrams.CompressOptions.bUseCGCompress = false; + PrintInfo("Unknown EncodeWith format specified. CPU will be used!"); + } + else if ((g_CmdPrams.CompressOptions.nEncodeWith == CMP_Compute_type::CMP_CPU) + //|| (g_CmdPrams.CompressOptions.nEncodeWith == CMP_Compute_type::CMP_HPC) + ) + g_CmdPrams.CompressOptions.bUseCGCompress = false; + else + g_CmdPrams.CompressOptions.bUseCGCompress = true; + } + else if (strcmp(strCommand, "-decomp") == 0) + { + if (strlen(strParameter) == 0) + { throw "no destination file specified"; } g_CmdPrams.DecompressFile = (char*)strParameter; - g_CmdPrams.doDecompress = true; + if (g_CmdPrams.DestFile.length() == 0) + g_CmdPrams.DestFile = g_CmdPrams.DecompressFile; + g_CmdPrams.doDecompress = true; } #ifdef USE_MESH_DRACO_EXTENSION #ifdef USE_MESH_DRACO_SETTING - else if (strcmp(strCommand, "-dracolvl") == 0) { //draco compression level - if (strlen(strParameter) == 0) { + else if (strcmp(strCommand, "-dracolvl") == 0) + { //draco compression level + if (strlen(strParameter) == 0) + { throw "No draco compression level specified"; } int value = std::stoi(strParameter); - if ((value < 0) || (value > 10)) { + if ((value < 0) || (value > 10)) + { throw "draco compression level supported is in range of 0 to 10"; } g_CmdPrams.CompressOptions.iCmpLevel = value; - } else if (strcmp(strCommand, "-qpos") == 0) { //quantization bit for positon - if (strlen(strParameter) == 0) { + } + else if (strcmp(strCommand, "-qpos") == 0) + { //quantization bit for positon + if (strlen(strParameter) == 0) + { throw "No quantization bits for position specified"; } int value = std::stoi(strParameter); - if ((value < 1) || (value > 30)) { + if ((value < 1) || (value > 30)) + { throw "quantization bits for position supported is in range of 1 to 30"; } g_CmdPrams.CompressOptions.iPosBits = value; - } else if (strcmp(strCommand, "-qtexc") == 0) { //quantization bit for texture coordinates - if (strlen(strParameter) == 0) { + } + else if (strcmp(strCommand, "-qtexc") == 0) + { //quantization bit for texture coordinates + if (strlen(strParameter) == 0) + { throw "No quantization bits for texture coordinates specified"; } int value = std::stoi(strParameter); - if ((value < 1) || (value > 30)) { + if ((value < 1) || (value > 30)) + { throw "quantization bits for texture coordinates supported is in range of 1 to 30"; } g_CmdPrams.CompressOptions.iTexCBits = value; - } else if (strcmp(strCommand, "-qnorm") == 0) { //quantization bit for normal - if (strlen(strParameter) == 0) { + } + else if (strcmp(strCommand, "-qnorm") == 0) + { //quantization bit for normal + if (strlen(strParameter) == 0) + { throw "No quantization bits for normal specified"; } int value = std::stoi(strParameter); - if ((value < 1) || (value > 30)) { + if ((value < 1) || (value > 30)) + { throw "quantization bits for normal supported is in range of 1 to 30"; } g_CmdPrams.CompressOptions.iNormalBits = value; - } else if (strcmp(strCommand, "-qgen") == 0) { //quantization bit for generic - if (strlen(strParameter) == 0) { + } + else if (strcmp(strCommand, "-qgen") == 0) + { //quantization bit for generic + if (strlen(strParameter) == 0) + { throw "No quantization bits for generic (i.e. tangent) specified"; } int value = std::stoi(strParameter); - if ((value < 1) || (value > 30)) { + if ((value < 1) || (value > 30)) + { throw "quantization bits for generic (i.e. tangent) supported is in range of 1 to 30"; } g_CmdPrams.CompressOptions.iGenericBits = value; } #endif #endif - else if (strcmp(strCommand, "-logfile") == 0) { - if (strlen(strParameter) == 0) { + else if (strcmp(strCommand, "-logfile") == 0) + { + if (strlen(strParameter) == 0) + { throw "no log file specified"; } g_CmdPrams.logcsvformat = false; g_CmdPrams.logresults = true; g_CmdPrams.logresultsToFile = true; g_CmdPrams.LogProcessResultsFile.assign((char*)strParameter); - } else if (strcmp(strCommand, "-logcsvfile") == 0) { - if (strlen(strParameter) == 0) { + } + else if (strcmp(strCommand, "-logcsvfile") == 0) + { + if (strlen(strParameter) == 0) + { throw "no log file specified"; } g_CmdPrams.logcsvformat = true; g_CmdPrams.logresults = true; g_CmdPrams.logresultsToFile = true; g_CmdPrams.LogProcessResultsFile.assign((char*)strParameter); - } else if (strcmp(strCommand, "-fs") == 0) { - if (strlen(strParameter) == 0) { + } +#ifdef CMP_ENABLE_LEGACY_OPTIONS + else if (strcmp(strCommand, "-fs") == 0) + { + if (strlen(strParameter) == 0) + { throw "no format specified"; } g_CmdPrams.CompressOptions.SourceFormat = CMP_ParseFormat((char*)strParameter); - if (g_CmdPrams.CompressOptions.SourceFormat == CMP_FORMAT_Unknown) { + if (g_CmdPrams.CompressOptions.SourceFormat == CMP_FORMAT_Unknown) + { throw "unknown format specified"; } - } else if ((strcmp(strCommand, "-ff") == 0) || // FileFilter used for collecting list of source files in a given source Dir - (strcmp(strCommand, "-fx") == 0)) { //and FileOutExt used for file output extension at given output Dir - if (strlen(strParameter) == 0) { + } +#endif + else if ((strcmp(strCommand, "-ff") == 0) || // FileFilter used for collecting list of source files in a given source Dir + (strcmp(strCommand, "-fx") == 0)) + { //and FileOutExt used for file output extension at given output Dir + if (strlen(strParameter) == 0) + { throw "no file filter specified"; } std::string filterParameter = strParameter; std::transform(filterParameter.begin(), filterParameter.end(), filterParameter.begin(), ::toupper); - string supported_ExtListings = {"DDS,KTX,TGA,EXR,PNG,BMP,HDR,JPG,TIFF,PPM"}; + string supported_ExtListings = {"DDS,KTX,KTX2,TGA,EXR,PNG,BMP,HDR,JPG,TIFF,PPM"}; istringstream ff(filterParameter); string sff; int filter_num = 0; - while (getline(ff, sff, ',')) { + while (getline(ff, sff, ',')) + { filter_num++; - if (supported_ExtListings.find(sff) == std::string::npos) { + if (supported_ExtListings.find(sff) == std::string::npos) + { char err[128]; sprintf(err, "[%s] file filter is not supported", sff.c_str()); throw(err); - } else if (filter_num > 1 && strcmp(strCommand, "-fx") == 0) { + } + else if (filter_num > 1 && strcmp(strCommand, "-fx") == 0) + { char err[128]; sprintf(err, "Only one file extension for output file."); throw(err); } } - if (strcmp(strCommand, "-ff") == 0) { + if (strcmp(strCommand, "-ff") == 0) + { g_CmdPrams.FileFilter = filterParameter; - } else if (strcmp(strCommand, "-fx") == 0) { + } + else if (strcmp(strCommand, "-fx") == 0) + { std::transform(filterParameter.begin(), filterParameter.end(), filterParameter.begin(), ::tolower); //change to lower g_CmdPrams.FileOutExt = "." + filterParameter; //add dot } - } else if (strcmp(strCommand, "-fd") == 0) { - if (strlen(strParameter) == 0) { + } + else if (strcmp(strCommand, "-fd") == 0) + { + if (strlen(strParameter) == 0) + { throw "no format specified"; } g_CmdPrams.CompressOptions.DestFormat = CMP_ParseFormat((char*)strParameter); - if (g_CmdPrams.CompressOptions.DestFormat == CMP_FORMAT_Unknown) { + if (g_CmdPrams.CompressOptions.DestFormat == CMP_FORMAT_Unknown) + { throw "unknown format specified"; } - } else if ((strcmp(strCommand, "-miplevels") == 0)) { - if (strlen(strParameter) == 0) { + } + else if ((strcmp(strCommand, "-miplevels") == 0)) + { + if (strlen(strParameter) == 0) + { throw "no level is specified"; } - try { + if (g_CmdPrams.use_noMipMaps == true) + throw "Both miplevels and nomipmap are set!"; + + try + { g_CmdPrams.MipsLevel = std::stoi(strParameter); if ((g_CmdPrams.MipsLevel <= 0) || ((g_CmdPrams.MipsLevel > 20))) throw "Invalid miplevel value specified, valid range is [1 to 20]"; - } catch (std::exception) { + } + catch (std::exception) + { throw "conversion failed for miplevel value"; } - } else if ((strcmp(strCommand, "-mipsize") == 0)) { - if (strlen(strParameter) == 0) { + } + else if ((strcmp(strCommand, "-mipsize") == 0)) + { + if (strlen(strParameter) == 0) + { throw "no size is specified"; } - try { + try + { g_CmdPrams.nMinSize = std::stoi(strParameter); - } catch (std::exception) { + } + catch (std::exception) + { throw "conversion failed for mipsize value"; } g_CmdPrams.MipsLevel = 2; - } else if (strcmp(strCommand, "-r") == 0) { - if (strlen(strParameter) == 0) { + } + else if (strcmp(strCommand, "-r") == 0) + { + if (strlen(strParameter) == 0) + { throw "no read codec framework is specified"; } if (strcmp(strParameter, "ocv") == 0) g_CmdPrams.use_OCV = true; - else { + else + { throw "unsupported codec framework is specified"; } - } else if (strcmp(strCommand, "-w") == 0) { - if (strlen(strParameter) == 0) { + } + else if (strcmp(strCommand, "-w") == 0) + { + if (strlen(strParameter) == 0) + { throw "no read codec framework is specified"; } if (strcmp(strParameter, "ocv") == 0) g_CmdPrams.use_OCV_out = true; - else { + else + { throw "unsupported codec framework is specified"; } - } else if ((strcmp(strCommand, "-version") == 0) || (strcmp(strCommand, "-v") == 0)) { + } + else if ((strcmp(strCommand, "-version") == 0) || (strcmp(strCommand, "-v") == 0)) + { printf("version %d.%d.%d\n", VERSION_MAJOR_MAJOR, VERSION_MAJOR_MINOR, VERSION_MINOR_MAJOR); - } else { - if ((strlen(strParameter) > 0) || (strCommand[0] == '-')) { + exit(0); + } + else + { + if ((strlen(strParameter) > 0) || (strCommand[0] == '-')) + { // List of command the codecs use. // A call back loop should be used command for all codecs that can validate the seetings // For now we list all of the commands here to check. Prior to passing down to Codecs! if ((strcmp(strCommand, "-NumThreads") == 0) || (strcmp(strCommand, "-Quality") == 0) || (strcmp(strCommand, "-ModeMask") == 0) || - (strcmp(strCommand, "-PatternRec") == 0) || (strcmp(strCommand, "-ColourRestrict") == 0) || (strcmp(strCommand, "-AlphaRestrict") == 0) || - (strcmp(strCommand, "-AlphaThreshold") == 0) || (strcmp(strCommand, "-ImageNeedsAlpha") == 0) || (strcmp(strCommand, "-UseSSE2") == 0) || - (strcmp(strCommand, "-DXT1UseAlpha") == 0) || (strcmp(strCommand, "-WeightR") == 0) || (strcmp(strCommand, "-WeightG") == 0) || - (strcmp(strCommand, "-WeightB") == 0) || (strcmp(strCommand, "-3DRefinement") == 0) || (strcmp(strCommand, "-UseAdaptiveWeighting") == 0) || - (strcmp(strCommand, "-UseChannelWeighting") == 0) || (strcmp(strCommand, "-RefinementSteps") == 0) || - (strcmp(strCommand, "-ForceFloatPath") == 0) || (strcmp(strCommand, "-CompressionSpeed") == 0) || - (strcmp(strCommand, "-SwizzleChannels") == 0) || (strcmp(strCommand, "-CompressionSpeed") == 0) || - (strcmp(strCommand, "-MultiThreading") == 0)) { + (strcmp(strCommand, "-PatternRec") == 0) || (strcmp(strCommand, "-ColourRestrict") == 0) || (strcmp(strCommand, "-AlphaRestrict") == 0) || + (strcmp(strCommand, "-AlphaThreshold") == 0) || (strcmp(strCommand, "-ImageNeedsAlpha") == 0) || (strcmp(strCommand, "-UseSSE2") == 0) || + (strcmp(strCommand, "-DXT1UseAlpha") == 0) || (strcmp(strCommand, "-WeightR") == 0) || (strcmp(strCommand, "-WeightG") == 0) || + (strcmp(strCommand, "-WeightB") == 0) || (strcmp(strCommand, "-3DRefinement") == 0) || (strcmp(strCommand, "-UseAdaptiveWeighting") == 0) || + (strcmp(strCommand, "-UseChannelWeighting") == 0) || (strcmp(strCommand, "-RefinementSteps") == 0) || + (strcmp(strCommand, "-ForceFloatPath") == 0) || (strcmp(strCommand, "-CompressionSpeed") == 0) || + (strcmp(strCommand, "-SwizzleChannels") == 0) || (strcmp(strCommand, "-CompressionSpeed") == 0) || + (strcmp(strCommand, "-MultiThreading") == 0)) + { // Reserved for future dev: command options passed down to codec levels const char* str; // strip leading - or -- @@ -710,11 +913,13 @@ bool ProcessCMDLineOptions(const char* strCommand, const char* strParameter) { if (strCommand[1] == '-') str++; - if (strlen(str) > AMD_MAX_CMD_STR) { + if (strlen(str) > AMD_MAX_CMD_STR) + { throw "Command option is invalid"; } - if (strlen(strParameter) > AMD_MAX_CMD_PARAM) { + if (strlen(strParameter) > AMD_MAX_CMD_PARAM) + { throw "Command Parameter is invalid"; } @@ -722,86 +927,119 @@ bool ProcessCMDLineOptions(const char* strCommand, const char* strParameter) { strcpy(g_CmdPrams.CompressOptions.CmdSet[g_CmdPrams.CompressOptions.NumCmds].strParameter, strParameter); g_CmdPrams.CompressOptions.NumCmds++; - } else + } + else throw "Command option is invalid"; - } else { + } + else + { // Flags or Source and destination files specified - if ((g_CmdPrams.SourceFile.length() == 0) && (g_CmdPrams.SourceFileList.size() == 0)) { + if ((g_CmdPrams.SourceFile.length() == 0) && (g_CmdPrams.SourceFileList.size() == 0)) + { if (CMP_PathType(strCommand) == CMP_PATHTYPES::CMP_PATH_IS_FILE) g_CmdPrams.SourceFile = strCommand; - else { + else + { const std::string directory(CMP_GetFullPath(strCommand)); + CMP_GetDirList(directory, g_CmdPrams.SourceFileList, g_CmdPrams.FileFilter); - if (g_CmdPrams.SourceFileList.size() > 0) { + + // feature for recursive folder scans to get souce list to process, feature for v4.2 + // g_CmdPrams.SourceDir = directory; + // CMP_GetAllDirFilesList(directory, g_CmdPrams.SourceFileList, g_CmdPrams.FileFilter); + + if (g_CmdPrams.SourceFileList.size() > 0) + { // Set the first file in the new list to SourceFile and delete it from the list g_CmdPrams.SourceFile = g_CmdPrams.SourceFileList[0].c_str(); g_CmdPrams.SourceFileList.erase(g_CmdPrams.SourceFileList.begin()); - } else + } + else throw "No files to process in source dir"; } - } else // now check for destination once sourcefile or filelist has been added - if ((g_CmdPrams.DestFile.length() == 0) && (g_CmdPrams.SourceFile.length() || g_CmdPrams.SourceFileList.size())) { - if (CMP_PathType(strCommand) == CMP_PATHTYPES::CMP_PATH_IS_FILE) { - g_CmdPrams.DestFile = strCommand; - - string file_extension = std::filesystem::path(strCommand).extension().string(); - - // User did not supply a destination extension default - if (file_extension.length() == 0) { - if (g_CmdPrams.DestFile.length() == 0) { - g_CmdPrams.DestFile = DefaultDestination(g_CmdPrams.SourceFile, g_CmdPrams.CompressOptions.DestFormat, g_CmdPrams.FileOutExt); - PrintInfo("Destination texture file was not supplied: Defaulting to %s\n", g_CmdPrams.DestFile.c_str()); - } else { - if (g_CmdPrams.CompressOptions.DestFormat == CMP_FORMAT_ASTC) - g_CmdPrams.DestFile.append(".astc"); - else - g_CmdPrams.DestFile.append(".dds"); - } + } + else // now check for destination once sourcefile or filelist has been added + if ((g_CmdPrams.DestFile.length() == 0) && (g_CmdPrams.SourceFile.length() || g_CmdPrams.SourceFileList.size())) + { + if (CMP_PathType(strCommand) == CMP_PATHTYPES::CMP_PATH_IS_FILE) + { + g_CmdPrams.DestFile = strCommand; + + string file_extension = sfs::path(strCommand).extension().string(); + + // User did not supply a destination extension default + if (file_extension.length() == 0) + { + if (g_CmdPrams.DestFile.length() == 0) + { + g_CmdPrams.DestFile = + DefaultDestination(g_CmdPrams.SourceFile, g_CmdPrams.CompressOptions.DestFormat, g_CmdPrams.FileOutExt, true); + PrintInfo("Destination texture file was not supplied: Defaulting to %s\n", g_CmdPrams.DestFile.c_str()); + } + else + { + if (g_CmdPrams.CompressOptions.DestFormat == CMP_FORMAT_ASTC) + g_CmdPrams.DestFile.append(".astc"); + else + g_CmdPrams.DestFile.append(".dds"); } - } else { - const std::string fullPath = CMP_GetFullPath(strCommand); - - if (fullPath.length() > 0) { - const std::string directory(CMP_GetFullPath(strCommand)); - // check if dir exist if not create it! - if (!CMP_DirExists(directory)) { - if (!CMP_CreateDir(directory)) - throw "Unable to create destination dir"; - // check and wait for system to generate a valid dir, - // typically this should not happen on local a dir - int delayloop = 0; - while (!CMP_DirExists(directory) && (delayloop < 5)) { + } + } + else + { + const std::string fullPath = CMP_GetFullPath(strCommand); + + if (fullPath.length() > 0) + { + const std::string directory(CMP_GetFullPath(strCommand)); + // check if dir exist if not create it! + if (!CMP_DirExists(directory)) + { + if (!CMP_CreateDir(directory)) + throw "Unable to create destination dir"; + // check and wait for system to generate a valid dir, + // typically this should not happen on local a dir + int delayloop = 0; + while (!CMP_DirExists(directory) && (delayloop < 5)) + { #ifdef _WIN32 - Sleep(100); + Sleep(100); #else - usleep(100000); + usleep(100000); #endif - delayloop++; - } - if (delayloop == 5) - throw "Unable to create destination dir"; + delayloop++; } - g_CmdPrams.DestDir = directory; - std::string destFileName; - //since DestFile is empty we need to create one from the source file - destFileName = DefaultDestination(g_CmdPrams.SourceFile, g_CmdPrams.CompressOptions.DestFormat, g_CmdPrams.FileOutExt); - std::filesystem::path destFile = std::filesystem::path(directory) / destFileName; - g_CmdPrams.DestFile = destFile.string(); - } else { - throw "Unable to create destination dir or file path is invalid"; + if (delayloop == 5) + throw "Unable to create destination dir"; } + g_CmdPrams.DestDir = directory; + std::string destFileName; + //since DestFile is empty we need to create one from the source file + destFileName = DefaultDestination(g_CmdPrams.SourceFile, g_CmdPrams.CompressOptions.DestFormat, g_CmdPrams.FileOutExt, true); + sfs::path destFile = sfs::path(directory) / destFileName; + g_CmdPrams.DestFile = destFile.string(); + } + else + { + throw "Unable to create destination dir or file path is invalid"; } - } else { - throw "unknown source, destination file or dir path specified"; } + } + else + { + throw "unknown source, destination file or dir path specified"; + } } } } // Try code - catch (const char* str) { + catch (const char* str) + { PrintInfo("Option [%s] : %s\n\n", strCommand, str); return false; - } catch (std::exception& e) { + } + catch (std::exception& e) + { PrintInfo("Option [%s] : %s\n\n", strCommand, e.what()); return false; } @@ -827,12 +1065,15 @@ bool ProcessCMDLineOptions(const char* strCommand, const char* strParameter) { ParseParams(5,argv); */ -bool ParseParams(int argc, CMP_CHAR* argv[]) { - try { +bool ParseParams(int argc, CMP_CHAR* argv[]) +{ + try + { g_CmdPrams.SetDefault(); #ifdef USE_WITH_COMMANDLINE_TOOL1 - if (argc == 1) { + if (argc == 1) + { PrintUsage(); } #endif @@ -841,32 +1082,45 @@ bool ParseParams(int argc, CMP_CHAR* argv[]) { std::string strParameter; std::string strTemp; - for (int i = 1; i < argc; i++) { + for (int i = 1; i < argc; i++) + { strTemp = argv[i]; // // Cmd line options can be -option value // strCommand = strTemp; - if ((strTemp[0] == '-') && (i <= (argc - 1))) { - if (!ProcessSingleFlags(strCommand.c_str())) { + if ((strTemp[0] == '-') && (i <= (argc - 1))) + { + if (!ProcessSingleFlags(strCommand.c_str())) + { i++; if (i < argc) strParameter = argv[i]; else strParameter = ""; - if (!ProcessCMDLineOptions(strCommand.c_str(), strParameter.c_str())) { + if (!ProcessCMDLineOptions(strCommand.c_str(), strParameter.c_str())) + { throw "Invalid Command"; } + + // Check odd cases + if (g_CmdPrams.use_noMipMaps && g_CmdPrams.CompressOptions.genGPUMipMaps) + throw "Invalid commands set for combined nomipmap and GenGPUMipMaps"; } - } else { - if (!ProcessCMDLineOptions(strCommand.c_str(), "")) { + } + else + { + if (!ProcessCMDLineOptions(strCommand.c_str(), "")) + { throw "Invalid Command"; } } } // for loop - } catch (const char* str) { + } + catch (const char* str) + { PrintInfo("%s\n", str); return false; } @@ -874,7 +1128,8 @@ bool ParseParams(int argc, CMP_CHAR* argv[]) { return true; } -bool SouceAndDestCompatible(CCmdLineParamaters CmdPrams) { +bool SouceAndDestCompatible(CCmdLineParamaters CmdPrams) +{ return true; } @@ -909,8 +1164,10 @@ MipSet g_MipSetOut; int g_MipLevel = 1; float g_fProgress = -1; -bool CompressionCallback(float fProgress, CMP_DWORD_PTR pUser1, CMP_DWORD_PTR pUser2) { - if (g_fProgress != fProgress) { +bool CompressionCallback(float fProgress, CMP_DWORD_PTR pUser1, CMP_DWORD_PTR pUser2) +{ + if (g_fProgress != fProgress) + { UNREFERENCED_PARAMETER(pUser1); UNREFERENCED_PARAMETER(pUser2); @@ -928,8 +1185,10 @@ bool CompressionCallback(float fProgress, CMP_DWORD_PTR pUser1, CMP_DWORD_PTR pU return g_bAbortCompression; } -int CalcMinMipSize(int nHeight, int nWidth, int MipsLevel) { - while (MipsLevel > 0) { +int CalcMinMipSize(int nHeight, int nWidth, int MipsLevel) +{ + while (MipsLevel > 0) + { nWidth = max(nWidth >> 1, 1); nHeight = max(nHeight >> 1, 1); MipsLevel--; @@ -940,32 +1199,53 @@ int CalcMinMipSize(int nHeight, int nWidth, int MipsLevel) { return (nWidth); } -bool TC_PluginCodecSupportsFormat(const MipSet* pMipSet) { +bool TC_PluginCodecSupportsFormat(const MipSet* pMipSet) +{ return (pMipSet->m_ChannelFormat == CF_8bit || pMipSet->m_ChannelFormat == CF_16bit || pMipSet->m_ChannelFormat == CF_2101010 || pMipSet->m_ChannelFormat == CF_32bit || pMipSet->m_ChannelFormat == CF_Float16 || pMipSet->m_ChannelFormat == CF_Float32); } -void cleanup(bool Delete_gMipSetIn, bool SwizzleMipSetIn) { +void LogErrorToCSVFile(AnalysisErrorCodeType error) +{ + // Used in test automation and results validation + if (g_CmdPrams.logcsvformat) + { + CMP_ANALYSIS_DATA analysisData = {0}; + analysisData.SSIM = -1; // Set data content is invalid and not processed + analysisData.errCode = error; + ProcessResults(g_CmdPrams, analysisData); + } +} + +void cleanup(bool Delete_gMipSetIn, bool SwizzleMipSetIn) +{ #ifdef _WIN32 SetDllDirectory(NULL); #endif - if (Delete_gMipSetIn) { - if (g_MipSetIn.m_pMipLevelTable) { + if (Delete_gMipSetIn) + { + if (g_MipSetIn.m_pMipLevelTable) + { g_CMIPS->FreeMipSet(&g_MipSetIn); g_MipSetIn.m_pMipLevelTable = NULL; } - } else { - if (SwizzleMipSetIn) { + } + else + { + if (SwizzleMipSetIn) + { SwizzleMipMap(&g_MipSetIn); } } - if (g_MipSetCmp.m_pMipLevelTable) { + if (g_MipSetCmp.m_pMipLevelTable) + { g_CMIPS->FreeMipSet(&g_MipSetCmp); g_MipSetCmp.m_pMipLevelTable = NULL; } - if (g_MipSetOut.m_pMipLevelTable) { + if (g_MipSetOut.m_pMipLevelTable) + { g_CMIPS->FreeMipSet(&g_MipSetOut); g_MipSetOut.m_pMipLevelTable = NULL; } @@ -973,8 +1253,10 @@ void cleanup(bool Delete_gMipSetIn, bool SwizzleMipSetIn) { // mesh optimization process // only support case glTF->glTF, case obj->obj -bool OptimizeMesh(std::string SourceFile, std::string DestFile) { - if (!(CMP_FileExists(SourceFile))) { +bool OptimizeMesh(std::string SourceFile, std::string DestFile) +{ + if (!(CMP_FileExists(SourceFile))) + { PrintInfo("Error: Source Model Mesh File is not found.\n"); return false; } @@ -984,38 +1266,47 @@ bool OptimizeMesh(std::string SourceFile, std::string DestFile) { GLTFCommon* gltfdata = nullptr; // load model - string file_extension = std::filesystem::path(SourceFile).extension().string(); + string file_extension = sfs::path(SourceFile).extension().string(); helper_to_upper(file_extension); helper_erase_all(file_extension, "."); PluginInterface_3DModel_Loader* plugin_loader; plugin_loader = reinterpret_cast(g_pluginManager.GetPlugin("3DMODEL_LOADER", (char*)file_extension.c_str())); - if (plugin_loader) { + if (plugin_loader) + { plugin_loader->TC_PluginSetSharedIO(g_CMIPS); void* msgHandler = NULL; int result; - if (result = plugin_loader->LoadModelData(g_CmdPrams.SourceFile.c_str(), "", &g_pluginManager, msgHandler, &CompressionCallback) != 0) { + if (result = plugin_loader->LoadModelData(g_CmdPrams.SourceFile.c_str(), "", &g_pluginManager, msgHandler, &CompressionCallback) != 0) + { if (result != 0) throw("Error Loading Model Data"); } - if (fileIsGLTF(g_CmdPrams.SourceFile)) { + if (fileIsGLTF(g_CmdPrams.SourceFile)) + { gltfdata = (GLTFCommon*)plugin_loader->GetModelData(); - if (gltfdata) { + if (gltfdata) + { if (gltfdata->m_meshBufferData.m_meshData[0].vertices.size() > 0) modelDataIn = (void*)&(gltfdata->m_meshBufferData); - else { + else + { modelDataIn = nullptr; PrintInfo("[Mesh Optimization] Error in processing mesh. Mesh data format size is not supported."); return false; } } - } else + } + else modelDataIn = plugin_loader->GetModelData(); - } else { + } + else + { PrintInfo("[Mesh Optimization] Loading file error.: %s\n.", SourceFile.c_str()); - if (plugin_loader) { + if (plugin_loader) + { delete plugin_loader; plugin_loader = nullptr; } @@ -1029,8 +1320,10 @@ bool OptimizeMesh(std::string SourceFile, std::string DestFile) { plugin_Mesh = reinterpret_cast(g_pluginManager.GetPlugin("MESH_OPTIMIZER", "TOOTLE_MESH")); - if (plugin_Mesh) { - if (plugin_Mesh->Init() == 0) { + if (plugin_Mesh) + { + if (plugin_Mesh->Init() == 0) + { plugin_Mesh->TC_PluginSetSharedIO(g_CMIPS); MeshSettings meshSettings; meshSettings.bOptimizeOverdraw = (g_CmdPrams.CompressOptions.fOverdrawACMR > 0); @@ -1048,9 +1341,12 @@ bool OptimizeMesh(std::string SourceFile, std::string DestFile) { if (meshSettings.bSimplifyMesh) meshSettings.nlevelofDetails = g_CmdPrams.CompressOptions.iSimplifyLOD; - try { + try + { modelDataOut = plugin_Mesh->ProcessMesh(modelDataIn, (void*)&meshSettings, NULL, &CompressionCallback); - } catch (std::exception& e) { + } + catch (std::exception& e) + { PrintInfo("[Mesh Optimization] Error: %s\n.", e.what()); if (plugin_Mesh) plugin_Mesh->CleanUp(); @@ -1060,21 +1356,25 @@ bool OptimizeMesh(std::string SourceFile, std::string DestFile) { } //save model - if (modelDataOut) { + if (modelDataOut) + { std::vector* optimized = ((std::vector*)modelDataOut); PluginInterface_3DModel_Loader* plugin_save = NULL; - string destfile_extension = std::filesystem::path(DestFile).extension().string(); + string destfile_extension = sfs::path(DestFile).extension().string(); helper_to_upper(destfile_extension); helper_erase_all(destfile_extension, "."); plugin_save = reinterpret_cast(g_pluginManager.GetPlugin("3DMODEL_LOADER", (char*)destfile_extension.c_str())); - if (plugin_save) { + if (plugin_save) + { plugin_save->TC_PluginSetSharedIO(g_CMIPS); int result = 0; - if (fileIsGLTF(g_CmdPrams.DestFile)) { - if (gltfdata) { + if (fileIsGLTF(g_CmdPrams.DestFile)) + { + if (gltfdata) + { GLTFCommon optimizedGltf; optimizedGltf.buffersData = gltfdata->buffersData; optimizedGltf.isBinFile = gltfdata->isBinFile; @@ -1087,31 +1387,42 @@ bool OptimizeMesh(std::string SourceFile, std::string DestFile) { optimizedGltf.m_scenes = gltfdata->m_scenes; optimizedGltf.m_meshBufferData.m_meshData = *optimized; - if (plugin_save->SaveModelData(g_CmdPrams.DestFile.c_str(), &optimizedGltf) == -1) { + if (plugin_save->SaveModelData(g_CmdPrams.DestFile.c_str(), &optimizedGltf) == -1) + { PrintInfo("[Mesh Optimization] Failed to save optimized gltf data.\n"); plugin_Mesh->CleanUp(); - if (plugin_save) { + if (plugin_save) + { delete plugin_save; plugin_save = nullptr; } return false; } - } else { + } + else + { PrintInfo("[Mesh Optimization] Failed to save optimized gltf data.\n"); plugin_Mesh->CleanUp(); - if (plugin_save) { + if (plugin_save) + { delete plugin_save; plugin_save = nullptr; } return false; } - } else { - if (plugin_save->SaveModelData(g_CmdPrams.DestFile.c_str(), &((*optimized)[0])) != -1) { + } + else + { + if (plugin_save->SaveModelData(g_CmdPrams.DestFile.c_str(), &((*optimized)[0])) != -1) + { PrintInfo("[Mesh Optimization] Success in saving optimized obj data.\n"); - } else { + } + else + { PrintInfo("[Mesh Optimization] Failed to save optimized obj data.\n"); plugin_Mesh->CleanUp(); - if (plugin_save) { + if (plugin_save) + { delete plugin_save; plugin_save = nullptr; } @@ -1119,21 +1430,28 @@ bool OptimizeMesh(std::string SourceFile, std::string DestFile) { } } - if (result != 0) { + if (result != 0) + { PrintInfo("[Mesh Optimization] Error in saving mesh file."); plugin_Mesh->CleanUp(); - if (plugin_save) { + if (plugin_save) + { delete plugin_save; plugin_save = nullptr; } return false; - } else { + } + else + { PrintInfo("[Mesh Optimization] Saving %s done.\n", g_CmdPrams.DestFile.c_str()); } - } else { + } + else + { PrintInfo("[Mesh Optimization]Saving: File format not supported.\n"); plugin_Mesh->CleanUp(); - if (plugin_save) { + if (plugin_save) + { delete plugin_save; plugin_save = nullptr; } @@ -1141,7 +1459,8 @@ bool OptimizeMesh(std::string SourceFile, std::string DestFile) { } plugin_Mesh->CleanUp(); - if (plugin_save) { + if (plugin_save) + { delete plugin_save; plugin_save = nullptr; } @@ -1150,16 +1469,55 @@ bool OptimizeMesh(std::string SourceFile, std::string DestFile) { return true; } +CMP_ChannelFormat cmp_getChannelFormat(CMP_FORMAT destFormat) +{ + switch (destFormat) + { + case CMP_FORMAT_ARGB_32F: + case CMP_FORMAT_ABGR_32F: + case CMP_FORMAT_RGBA_32F: + case CMP_FORMAT_BGRA_32F: + case CMP_FORMAT_RGB_32F: + case CMP_FORMAT_BGR_32F: + case CMP_FORMAT_RG_32F: + case CMP_FORMAT_R_32F: + return (CF_Float32); + break; + case CMP_FORMAT_ARGB_16F: + case CMP_FORMAT_ABGR_16F: + case CMP_FORMAT_RGBA_16F: + case CMP_FORMAT_BGRA_16F: + case CMP_FORMAT_RG_16F: + case CMP_FORMAT_R_16F: + return (CF_Float16); + break; + case CMP_FORMAT_ARGB_2101010: + case CMP_FORMAT_ARGB_16: + case CMP_FORMAT_ABGR_16: + case CMP_FORMAT_RGBA_16: + case CMP_FORMAT_BGRA_16: + case CMP_FORMAT_RG_16: + case CMP_FORMAT_R_16: + return (CF_16bit); + break; + } + return (CF_8bit); +} + // mesh draco compression/decompression -bool CompressDecompressMesh(std::string SourceFile, std::string DestFile) { - if (!(CMP_FileExists(SourceFile))) { +bool CompressDecompressMesh(std::string SourceFile, std::string DestFile) +{ + if (!(CMP_FileExists(SourceFile))) + { PrintInfo("Error: Source Model Mesh File is not found.\n"); return false; } // Case: glTF -> glTF handle both compression and decompression - if (fileIsGLTF(SourceFile)) { - if (fileIsGLTF(DestFile)) { + if (fileIsGLTF(SourceFile)) + { + if (fileIsGLTF(DestFile)) + { std::string err; Model model; TinyGLTF loader; @@ -1172,9 +1530,11 @@ bool CompressDecompressMesh(std::string SourceFile, std::string DestFile) { std::string srcFile = SourceFile; std::string dstFile = DestFile; // Check if mesh optimization was done if so then source is optimized file - if (g_CmdPrams.doMeshOptimize) { + if (g_CmdPrams.doMeshOptimize) + { srcFile = DestFile; - if (!CMP_FileExists(srcFile)) { + if (!CMP_FileExists(srcFile)) + { PrintInfo("Error: Source Model Mesh File is not found.\n"); return false; } @@ -1183,39 +1543,49 @@ bool CompressDecompressMesh(std::string SourceFile, std::string DestFile) { } bool ret = loader.LoadASCIIFromFile(&model, &err, srcFile, g_CmdPrams.use_Draco_Encode); - if (!err.empty()) { + if (!err.empty()) + { PrintInfo("Error processing gltf source:[%s] file [%s]\n", srcFile.c_str(), err.c_str()); return false; } - if (!ret) { + if (!ret) + { PrintInfo("Failed in loading glTF file : [%s].\n", srcFile.c_str()); return false; - } else { + } + else + { PrintInfo("Success in loading glTF file : [%s].\n", srcFile.c_str()); } bool is_draco_src = false; #ifdef USE_MESH_DRACO_EXTENSION - if (model.dracomeshes.size() > 0) { + if (model.dracomeshes.size() > 0) + { is_draco_src = true; } #endif err.clear(); - if (g_CmdPrams.compressImagesFromGLTF) { + if (g_CmdPrams.compressImagesFromGLTF) + { std::string dstFolder = dstFile; auto pos = dstFolder.rfind("\\"); - if (pos == std::string::npos) { + if (pos == std::string::npos) + { pos = dstFolder.rfind("/"); } - if (pos != std::string::npos) { + if (pos != std::string::npos) + { dstFolder = dstFolder.substr(0, pos + 1); } size_t originalImages = model.images.size(); - for (unsigned i = 0; i < model.images.size(); ++i) { + for (unsigned i = 0; i < model.images.size(); ++i) + { std::string input = model.images[i].uri; PrintInfo("Processing '%s'\n", input.c_str()); - if (input.empty()) { + if (input.empty()) + { PrintInfo("Error: Compressonator can only compress separate images with glTF!\n"); return false; } @@ -1225,29 +1595,34 @@ bool CompressDecompressMesh(std::string SourceFile, std::string DestFile) { std::string imgSrcDir = ""; auto pos = srcFile.rfind("\\"); - if (pos == std::string::npos) { + if (pos == std::string::npos) + { pos = srcFile.rfind("/"); } - if (pos != std::string::npos) { + if (pos != std::string::npos) + { imgSrcDir = srcFile.substr(0, pos + 1); } std::string imgDestDir = dstFolder; pos = output.rfind("\\"); - if (pos == std::string::npos) { + if (pos == std::string::npos) + { pos = output.rfind("/"); } - if (pos != std::string::npos) { + if (pos != std::string::npos) + { imgDestDir = output.substr(0, pos + 1); } - std::filesystem::create_directories(imgDestDir); + sfs::create_directories(imgDestDir); { MipSet inMips{}; memset(&inMips, 0, sizeof(CMP_MipSet)); int ret = AMDLoadMIPSTextureImage((imgSrcDir + input).c_str(), &inMips, false, &g_pluginManager); - if (inMips.m_nMipLevels < g_CmdPrams.MipsLevel && !g_CmdPrams.use_noMipMaps) { + if (inMips.m_nMipLevels < g_CmdPrams.MipsLevel && !g_CmdPrams.use_noMipMaps) + { CMP_INT requestLevel = g_CmdPrams.MipsLevel; CMP_INT nMinSize = CMP_CalcMinMipSize(inMips.m_nHeight, inMips.m_nWidth, requestLevel); CMP_GenerateMIPLevels(&inMips, nMinSize); @@ -1267,44 +1642,54 @@ bool CompressDecompressMesh(std::string SourceFile, std::string DestFile) { kernel_options.encodeWith = g_CmdPrams.CompressOptions.nEncodeWith; auto cmp_status = CMP_ProcessTexture(&inMips, &mipSetCmp, kernel_options, CompressionCallback); - if (cmp_status == CMP_ERR_FAILED_HOST_SETUP) { + if (cmp_status == CMP_ERR_FAILED_HOST_SETUP) + { g_CmdPrams.CompressOptions.nEncodeWith = CMP_Compute_type::CMP_CPU; kernel_options.encodeWith = g_CmdPrams.CompressOptions.nEncodeWith; memset(&mipSetCmp, 0, sizeof(CMP_MipSet)); cmp_status = CMP_ProcessTexture(&inMips, &mipSetCmp, kernel_options, CompressionCallback); } - if (cmp_status != CMP_OK) { + if (cmp_status != CMP_OK) + { PrintInfo("Error: Something went wrong while compressing image!\n"); return false; } ret = AMDSaveMIPSTextureImage(output.c_str(), &mipSetCmp, false, g_CmdPrams.CompressOptions); - if (ret != 0) { + if (ret != 0) + { PrintInfo("Error: Something went wrong while saving compressed image!\n"); return false; } } - if (!std::filesystem::exists(dstFolder + input)) { - std::filesystem::copy(imgSrcDir + input, dstFolder + input); + if (!sfs::exists(dstFolder + input)) + { + sfs::copy(imgSrcDir + input, dstFolder + input); } } } ret = saver.WriteGltfSceneToFile(&model, &err, dstFile, g_CmdPrams.CompressOptions, is_draco_src, g_CmdPrams.use_Draco_Encode); - if (!err.empty()) { + if (!err.empty()) + { PrintInfo("Error processing gltf destination:[%s] file [%s]\n", dstFile.c_str(), err.c_str()); return false; } - if (!ret) { + if (!ret) + { PrintInfo("Failed to save glTF file %s\n", dstFile.c_str()); return false; - } else { + } + else + { PrintInfo("Success in writting glTF file : [%s].\n", dstFile.c_str()); } - } else { + } + else + { PrintInfo("Destination file must also be a gltf file\n"); return false; } @@ -1318,8 +1703,10 @@ bool CompressDecompressMesh(std::string SourceFile, std::string DestFile) { PluginInterface_Mesh* plugin_MeshComp; plugin_MeshComp = reinterpret_cast(g_pluginManager.GetPlugin("MESH_COMPRESSOR", "DRACO")); - if (plugin_MeshComp) { - if (plugin_MeshComp->Init() == 0) { + if (plugin_MeshComp) + { + if (plugin_MeshComp->Init() == 0) + { //showProgressDialog("Process Mesh Data"); plugin_MeshComp->TC_PluginSetSharedIO(g_CMIPS); @@ -1333,18 +1720,23 @@ bool CompressDecompressMesh(std::string SourceFile, std::string DestFile) { DracoOptions.generic_quantization_bits = g_CmdPrams.CompressOptions.iGenericBits; // Check if mesh optimization was done if so then source is optimized file - if (g_CmdPrams.doMeshOptimize) { + if (g_CmdPrams.doMeshOptimize) + { DracoOptions.input = DestFile; if (fileIsOBJ(DracoOptions.input)) DracoOptions.output = DestFile + ".drc"; - } else { + } + else + { DracoOptions.input = SourceFile; //obj->obj - if (fileIsOBJ(SourceFile) && fileIsOBJ(DestFile)) { + if (fileIsOBJ(SourceFile) && fileIsOBJ(DestFile)) + { //this obj->obj case only support for encode, a new encode.drc will be created if (g_CmdPrams.use_Draco_Encode) DracoOptions.output = DestFile + ".drc"; - else { + else + { PrintInfo("Error: please use -draco option to perform encode on the source obj file.\n"); return false; } @@ -1363,22 +1755,30 @@ bool CompressDecompressMesh(std::string SourceFile, std::string DestFile) { PluginInterface_3DModel_Loader* m_plugin_loader_drc; m_plugin_loader_drc = reinterpret_cast(g_pluginManager.GetPlugin("3DMODEL_LOADER", "DRC")); - if (m_plugin_loader_drc) { + if (m_plugin_loader_drc) + { m_plugin_loader_drc->TC_PluginSetSharedIO(g_CMIPS); int result; - if (fileIsOBJ(DracoOptions.input)) { - if (result = m_plugin_loader_drc->LoadModelData("OBJ", NULL, &g_pluginManager, &DracoOptions, &CompressionCallback) != 0) { - if (result != 0) { + if (fileIsOBJ(DracoOptions.input)) + { + if (result = m_plugin_loader_drc->LoadModelData("OBJ", NULL, &g_pluginManager, &DracoOptions, &CompressionCallback) != 0) + { + if (result != 0) + { PrintInfo("[Mesh Compression] Error Loading Model Data.\n"); plugin_MeshComp->CleanUp(); return false; } } - } else if (fileIsDRC(DracoOptions.input)) { + } + else if (fileIsDRC(DracoOptions.input)) + { if (result = - m_plugin_loader_drc->LoadModelData(DracoOptions.input.c_str(), NULL, &g_pluginManager, &DracoOptions, &CompressionCallback) != 0) { - if (result != 0) { + m_plugin_loader_drc->LoadModelData(DracoOptions.input.c_str(), NULL, &g_pluginManager, &DracoOptions, &CompressionCallback) != 0) + { + if (result != 0) + { PrintInfo("[Mesh Compression] Error Loading Model Data.\n"); plugin_MeshComp->CleanUp(); return false; @@ -1390,27 +1790,35 @@ bool CompressDecompressMesh(std::string SourceFile, std::string DestFile) { modelDataIn = m_plugin_loader_drc->GetModelData(); - try { + try + { if (modelDataIn) modelDataOut = plugin_MeshComp->ProcessMesh(modelDataIn, (void*)&DracoOptions, NULL, &CompressionCallback); - } catch (std::exception& e) { + } + catch (std::exception& e) + { PrintInfo("[Mesh Compression] Error: %s . Please try another setting.\n", e.what()); plugin_MeshComp->CleanUp(); return false; } - if (!modelDataOut) { + if (!modelDataOut) + { PrintInfo("[Mesh Compression] Error in processing mesh.\n"); plugin_MeshComp->CleanUp(); return false; } } - } else { + } + else + { PrintInfo("[Mesh Compression] Error in init mesh plugin.\n"); plugin_MeshComp->CleanUp(); return false; } - } else { + } + else + { PrintInfo("[Mesh Compression] Error in loading mesh compression plugin.\n"); return false; } @@ -1419,13 +1827,16 @@ bool CompressDecompressMesh(std::string SourceFile, std::string DestFile) { } //cmdline only -bool GenerateAnalysis(std::string SourceFile, std::string DestFile) { - if (!(CMP_FileExists(SourceFile))) { +bool GenerateAnalysis(std::string SourceFile, std::string DestFile) +{ + if (!(CMP_FileExists(SourceFile))) + { PrintInfo("Error: Source Image File is not found.\n"); return false; } - if (!(CMP_FileExists(DestFile))) { + if (!(CMP_FileExists(DestFile))) + { PrintInfo("Error: Destination Image File is not found.\n"); return false; } @@ -1434,18 +1845,23 @@ bool GenerateAnalysis(std::string SourceFile, std::string DestFile) { PluginInterface_Analysis* Plugin_Analysis; int testpassed = 0; Plugin_Analysis = reinterpret_cast(g_pluginManager.GetPlugin("IMAGE", "ANALYSIS")); - if (Plugin_Analysis) { - if (g_CmdPrams.diffImage) { + if (Plugin_Analysis) + { + if (g_CmdPrams.diffImage) + { g_CmdPrams.DiffFile = DestFile; int lastindex = (int)g_CmdPrams.DiffFile.find_last_of("."); g_CmdPrams.DiffFile = g_CmdPrams.DiffFile.substr(0, lastindex); g_CmdPrams.DiffFile.append("_diff.bmp"); - } else { + } + else + { g_CmdPrams.DiffFile = ""; } string results_file = ""; - if (g_CmdPrams.analysis) { + if (g_CmdPrams.analysis) + { results_file = DestFile; int index = (int)results_file.find_last_of("/"); results_file = results_file.substr(0, (index + 1)); @@ -1453,12 +1869,15 @@ bool GenerateAnalysis(std::string SourceFile, std::string DestFile) { } testpassed = Plugin_Analysis->TC_ImageDiff( - SourceFile.c_str(), DestFile.c_str(), g_CmdPrams.DiffFile.c_str(), (char*)results_file.c_str(), NULL, &g_pluginManager, NULL); + SourceFile.c_str(), DestFile.c_str(), g_CmdPrams.DiffFile.c_str(), (char*)results_file.c_str(), NULL, &g_pluginManager, NULL); delete Plugin_Analysis; - if (testpassed != 0) { + if (testpassed != 0) + { return false; } - } else { + } + else + { printf("Error: Plugin for image analysis is not loaded\n"); return false; } @@ -1467,8 +1886,10 @@ bool GenerateAnalysis(std::string SourceFile, std::string DestFile) { } //cmdline only: print image properties (i.e. image name, path, file size, image size, image width, height, miplevel and format) -bool GenerateImageProps(std::string ImageFile) { - if (!(CMP_FileExists(ImageFile))) { +bool GenerateImageProps(std::string ImageFile) +{ + if (!(CMP_FileExists(ImageFile))) + { PrintInfo("Error: Image File is not found.\n"); return false; } @@ -1477,43 +1898,55 @@ bool GenerateImageProps(std::string ImageFile) { PrintInfo("File Name: %s\n", ImageFile.c_str()); // full path - std::filesystem::path p(ImageFile); - std::filesystem::path fullpath = std::filesystem::absolute(p); + sfs::path p(ImageFile); + sfs::path fullpath = sfs::absolute(p); PrintInfo("File Full Path: %s\n", fullpath.generic_string().c_str()); // file size - uintmax_t filesize = std::filesystem::file_size(ImageFile); + uintmax_t filesize = sfs::file_size(ImageFile); - if (filesize > 1024000) { + if (filesize > 1024000) + { filesize /= 1024000; PrintInfo("File Size: %ju MB\n", filesize); - } else if (filesize > 1024) { + } + else if (filesize > 1024) + { filesize /= 1024; PrintInfo("File Size: %ju KB\n", filesize); - } else { + } + else + { PrintInfo("File Size: %ju Bytes\n", filesize); } // load image into mipset - if (AMDLoadMIPSTextureImage(ImageFile.c_str(), &g_MipSetIn, g_CmdPrams.use_OCV, &g_pluginManager) != 0) { + if (AMDLoadMIPSTextureImage(ImageFile.c_str(), &g_MipSetIn, g_CmdPrams.use_OCV, &g_pluginManager) != 0) + { PrintInfo("Error: reading image, data type not supported.\n"); return false; } - if (&g_MipSetIn) { + if (&g_MipSetIn) + { //image size CMIPS CMips; MipLevel* pInMipLevel = CMips.GetMipLevel(&g_MipSetIn, 0, 0); uintmax_t imagesize = pInMipLevel->m_dwLinearSize; - if (imagesize > 1024000) { + if (imagesize > 1024000) + { imagesize /= 1024000; PrintInfo("Image Size: %ju MB\n", imagesize); - } else if (imagesize > 1024) { + } + else if (imagesize > 1024) + { imagesize /= 1024; PrintInfo("Image Size: %ju KB\n", imagesize); - } else { + } + else + { PrintInfo("Image Size: %ju Bytes\n", imagesize); } @@ -1528,8 +1961,10 @@ bool GenerateImageProps(std::string ImageFile) { if (g_MipSetIn.m_format != CMP_FORMAT_Unknown) PrintInfo("Format: %s\n", GetFormatDesc(g_MipSetIn.m_format)); - else { - switch (g_MipSetIn.m_ChannelFormat) { + else + { + switch (g_MipSetIn.m_ChannelFormat) + { case CF_8bit: //< 8-bit integer data. PrintInfo("Channel Format: 8-bit integer data\n"); break; @@ -1573,7 +2008,8 @@ bool GenerateImageProps(std::string ImageFile) { return true; } -void LocalPrintF(char* buff) { +void LocalPrintF(char* buff) +{ // Issue #89 Pull Request #ifdef __clang__ #pragma clang diagnostic ignored "-Wformat-security" // warning : warning: format string is not a string literal @@ -1586,13 +2022,16 @@ void LocalPrintF(char* buff) { #include "common_kerneldef.h" #include "compute_base.h" -bool SVMInitCodec(KernelOptions* options) { +bool SVMInitCodec(KernelOptions* options) +{ CMP_FORMAT format = options->format; - switch (format) { + switch (format) + { case CMP_FORMAT_BC1: case CMP_FORMAT_DXT1: case CMP_FORMAT_BC7: - case CMP_FORMAT_ASTC: { + case CMP_FORMAT_ASTC: + { unsigned char* src = (unsigned char*)options->data; unsigned char* dst = (unsigned char*)options->dataSVM; memcpy(dst, src, options->size); @@ -1605,7 +2044,8 @@ bool SVMInitCodec(KernelOptions* options) { //#endif -double timeStampsec() { +double timeStampsec() +{ #ifdef _WIN32 static LARGE_INTEGER frequency; if (frequency.QuadPart == 0) @@ -1621,7 +2061,8 @@ double timeStampsec() { #endif } -bool CMP_GenerateMipLevelData(CMP_FORMAT format, int w, int h, int level, int nFaceOrSlice, MipSet* pMipSetOut) { +bool CMP_GenerateMipLevelData(CMP_FORMAT format, int w, int h, int level, int nFaceOrSlice, MipSet* pMipSetOut) +{ //======================== // Compressed Destination //======================== @@ -1639,14 +2080,16 @@ bool CMP_GenerateMipLevelData(CMP_FORMAT format, int w, int h, int level, int nF pMipSetOut->dwHeight = h; MipLevel* pGPUOutMipLevel = g_CMIPS->GetMipLevel(pMipSetOut, level, nFaceOrSlice); - if (!g_CMIPS->AllocateCompressedMipLevelData(pGPUOutMipLevel, w, h, destGPUMipTexture.dwDataSize)) { + if (!g_CMIPS->AllocateCompressedMipLevelData(pGPUOutMipLevel, w, h, destGPUMipTexture.dwDataSize)) + { return false; } return true; } -CMP_ERROR CMP_ConvertMipTextureCGP(MipSet* pMipSetIn, MipSet* pMipSetOut, CMP_CompressOptions* pCompressOptions, CMP_Feedback_Proc pFeedbackProc) { +CMP_ERROR CMP_ConvertMipTextureCGP(MipSet* pMipSetIn, MipSet* pMipSetOut, CMP_CompressOptions* pCompressOptions, CMP_Feedback_Proc pFeedbackProc) +{ assert(pMipSetIn); assert(pMipSetOut); assert(pCompressOptions); @@ -1673,13 +2116,9 @@ CMP_ERROR CMP_ConvertMipTextureCGP(MipSet* pMipSetIn, MipSet* pMipSetOut, CMP_Co pMipSetOut->m_TextureType = pMipSetIn->m_TextureType; pMipSetOut->m_nIterations = 0; - if (!g_CMIPS->AllocateMipSet(pMipSetOut, - pMipSetOut->m_ChannelFormat, - TDT_ARGB, - pMipSetOut->m_TextureType, - pMipSetIn->m_nWidth, - pMipSetIn->m_nHeight, - pMipSetOut->m_nDepth)) { // depthsupport, what should nDepth be set as here? + if (!g_CMIPS->AllocateMipSet( + pMipSetOut, pMipSetOut->m_ChannelFormat, TDT_ARGB, pMipSetOut->m_TextureType, pMipSetIn->m_nWidth, pMipSetIn->m_nHeight, pMipSetOut->m_nDepth)) + { // depthsupport, what should nDepth be set as here? return CMP_ERR_MEM_ALLOC_FOR_MIPSET; } @@ -1700,12 +2139,15 @@ CMP_ERROR CMP_ConvertMipTextureCGP(MipSet* pMipSetIn, MipSet* pMipSetOut, CMP_Co CGU_BOOL genGPUMipMaps = pCompressOptions->genGPUMipMaps; // If generating GPU mipmaps using HW allocate IO data, do this only once as this loop also processes cubemap faces bool genOntimeDestMipMaps = false; - if (pCompressOptions->nEncodeWith == CMP_Compute_type::CMP_GPU_HW) { + if (pCompressOptions->nEncodeWith == CMP_Compute_type::CMP_GPU_HW) + { genOntimeDestMipMaps = genGPUMipMaps; } - for (int nMipLevel = 0; (nMipLevel < DestMipLevel) && contineProcessing; nMipLevel++) { - for (int nFaceOrSlice = 0; (nFaceOrSlice < CMP_MaxFacesOrSlices(pMipSetIn, nMipLevel)) && contineProcessing; nFaceOrSlice++) { + for (int nMipLevel = 0; (nMipLevel < DestMipLevel) && contineProcessing; nMipLevel++) + { + for (int nFaceOrSlice = 0; (nFaceOrSlice < CMP_MaxFacesOrSlices(pMipSetIn, nMipLevel)) && contineProcessing; nFaceOrSlice++) + { //===================== // Uncompressed source //====================== @@ -1714,12 +2156,16 @@ CMP_ERROR CMP_ConvertMipTextureCGP(MipSet* pMipSetIn, MipSet* pMipSetOut, CMP_Co //======================================= // For GPU skip non-divisable by 4 pixels //======================================= - if (isGPUEncoding && ((pInMipLevel->m_nWidth % 4) || (pInMipLevel->m_nHeight % 4))) { + if (isGPUEncoding && ((pInMipLevel->m_nWidth % 4) || (pInMipLevel->m_nHeight % 4))) + { // adjust final output miplevel - PrintInfo("GPU Process Warning! MipLevels > %d not processed, miplevel width or height not divisible by 4!\n", nMipLevel); - pMipSetOut->m_nMipLevels = nMipLevel; - contineProcessing = false; - continue; + PrintInfo("GPU Process Warning! MIP level %d not processed", nMipLevel); + PrintInfo("MIP level width (%d) or height (%d) not divisible by 4!. CPU will be used\n", pInMipLevel->m_nWidth, pInMipLevel->m_nHeight); + // pMipSetOut->m_nMipLevels = nMipLevel; + // contineProcessing = false; + pCompressOptions->format_support_hostEncoder = false; + isGPUEncoding = false; + // continue; } dataProcessed = true; @@ -1761,7 +2207,8 @@ CMP_ERROR CMP_ConvertMipTextureCGP(MipSet* pMipSetIn, MipSet* pMipSetOut, CMP_Co CMP_INT m_atcubeorface; MipLevel* pOutMipLevel = g_CMIPS->GetMipLevel(pMipSetOut, nMipLevel, nFaceOrSlice); - if (!g_CMIPS->AllocateCompressedMipLevelData(pOutMipLevel, destTexture.dwWidth, destTexture.dwHeight, destTexture.dwDataSize)) { + if (!g_CMIPS->AllocateCompressedMipLevelData(pOutMipLevel, destTexture.dwWidth, destTexture.dwHeight, destTexture.dwDataSize)) + { return CMP_ERR_MEM_ALLOC_FOR_MIPSET; } @@ -1771,7 +2218,8 @@ CMP_ERROR CMP_ConvertMipTextureCGP(MipSet* pMipSetIn, MipSet* pMipSetOut, CMP_Co //======================== // Process ConvertTexture //======================== - if (pCompressOptions->format_support_hostEncoder) { + if (pCompressOptions->format_support_hostEncoder) + { //------------------------------------------------ // Initializing the Host Framework // if it fails revert to CPU version of the codec @@ -1786,40 +2234,55 @@ CMP_ERROR CMP_ConvertMipTextureCGP(MipSet* pMipSetIn, MipSet* pMipSetOut, CMP_Co kernel_options.getDeviceInfo = pCompressOptions->getDeviceInfo; kernel_options.genGPUMipMaps = genGPUMipMaps; kernel_options.useSRGBFrames = pCompressOptions->useSRGBFrames; - do { + do + { ComputeOptions options; options.force_rebuild = false; // set this to true if you want the shader source code to be allways compiled! //=============================================================================== // Initalize the Pipeline that will be used for the codec to run on HPC or GPU //=============================================================================== - if (CMP_CreateComputeLibrary(&g_MipSetIn, &kernel_options, g_CMIPS) != CMP_OK) { + if (CMP_CreateComputeLibrary(&g_MipSetIn, &kernel_options, g_CMIPS) != CMP_OK) + { PrintInfo("Warning! CPU will be used for compression\n"); pCompressOptions->format_support_hostEncoder = false; break; } // Init Compute Codec info IO - if ((g_CMIPS->PrintLine == NULL) && (PrintStatusLine != NULL)) { + if ((g_CMIPS->PrintLine == NULL) && (PrintStatusLine != NULL)) + { g_CMIPS->PrintLine = PrintStatusLine; } // Set any addition feature as needed for the Host - if (CMP_SetComputeOptions(&options) != CMP_OK) { + if (CMP_SetComputeOptions(&options) != CMP_OK) + { PrintInfo("Failed to setup (HPC, SPMD or GPU) host options\n"); return CMP_ERR_FAILED_HOST_SETUP; } - if (genOntimeDestMipMaps) { + if (genOntimeDestMipMaps) + { // Generate MipMap images : Using CPU Box Filter, note however these miplevel images will be overritten by GPU generated images // CMP_GenerateMIPLevels(pMipSetIn, 4); // Now setup destination miplevel storage that will be filled in by GPU based mipmaping - CMP_INT levels = static_cast(std::log2(pMipSetIn->m_nWidth)) - 1; + CMP_INT levels = CMP_CalcMaxMipLevel(pMipSetIn->m_nWidth, pMipSetIn->m_nHeight, true); + if (levels > pMipSetIn->m_nMaxMipLevels) levels = pMipSetIn->m_nMaxMipLevels; + + if ((pCompressOptions->miplevels < levels) && (pCompressOptions->miplevels >= 1)) + { + levels = pCompressOptions->miplevels; + } + + kernel_options.miplevels = levels; pMipSetOut->m_nMipLevels = levels; - for (CMP_INT level = 1; level < levels; level++) { + + for (CMP_INT level = 1; level < levels; level++) + { CMP_INT w = pMipSetIn->m_nWidth >> level; CMP_INT h = pMipSetIn->m_nHeight >> level; if (!CMP_GenerateMipLevelData(pMipSetOut->m_format, w, h, level, nFaceOrSlice, pMipSetOut)) @@ -1829,27 +2292,59 @@ CMP_ERROR CMP_ConvertMipTextureCGP(MipSet* pMipSetIn, MipSet* pMipSetOut, CMP_Co } } + //========================== + // User suppiled Print Info + //========================== + if (pCompressOptions->m_PrintInfoStr) + { + char buff[256]; + snprintf(buff, + sizeof(buff), + "\nSource Texture size = %d Bytes, width = %d px height = %d px\n", + srcTexture.dwDataSize, + srcTexture.dwWidth, + srcTexture.dwHeight); + pCompressOptions->m_PrintInfoStr(buff); + if (destTexture.dwDataSize > 0) + { + snprintf(buff, + sizeof(buff), + "Destination Texture size = %d Bytes Resulting compression ratio = %2.2f:1\n", + destTexture.dwDataSize, + srcTexture.dwDataSize / (float)destTexture.dwDataSize); + pCompressOptions->m_PrintInfoStr(buff); + } + } + // Do the compression - if (CMP_CompressTexture(&kernel_options, *pMipSetIn, *pMipSetOut, pFeedbackProc) != CMP_OK) { + if (CMP_CompressTexture(&kernel_options, *pMipSetIn, *pMipSetOut, pFeedbackProc) != CMP_OK) + { PrintInfo("Warning: Target device or format is not supported or failed to build. CPU will be used\n"); pCompressOptions->format_support_hostEncoder = false; break; - } else + } + else pMipSetOut->m_nIterations++; - if (kernel_options.getPerfStats) { + if (kernel_options.getPerfStats) + { // Get Preformance Stats - if (CMP_GetPerformanceStats(&kernel_options.perfStats) == CMP_OK) { + if (CMP_GetPerformanceStats(&kernel_options.perfStats) == CMP_OK) + { pCompressOptions->perfStats = kernel_options.perfStats; - } else + } + else PrintInfo("Warning: Target device or format is not supported or failed to build. CPU will be used\n"); } - if (kernel_options.getDeviceInfo) { + if (kernel_options.getDeviceInfo) + { // Get Device Info - if (CMP_GetDeviceInfo(&kernel_options.deviceInfo) == CMP_OK) { + if (CMP_GetDeviceInfo(&kernel_options.deviceInfo) == CMP_OK) + { pCompressOptions->deviceInfo = kernel_options.deviceInfo; - } else + } + else PrintInfo("Warning: Target device or format is not supported or failed to build. CPU will be used\n"); } @@ -1862,26 +2357,30 @@ CMP_ERROR CMP_ConvertMipTextureCGP(MipSet* pMipSetIn, MipSet* pMipSetOut, CMP_Co } // SPMD/GPU failed run CPU version - if (!pCompressOptions->format_support_hostEncoder) { + if (!pCompressOptions->format_support_hostEncoder) + { isGPUEncoding = false; - if (CMP_ConvertTexture(&srcTexture, &destTexture, pCompressOptions, pFeedbackProc) != CMP_OK) { + if (CMP_ConvertTexture(&srcTexture, &destTexture, pCompressOptions, pFeedbackProc) != CMP_OK) + { PrintInfo("Error in compressing destination texture\n"); return CMP_ERR_CMP_DESTINATION; - } else + } + else pMipSetOut->m_nIterations++; } } // Check if GPU was used for MipMap Generation, This indicates that All processed compressed miplevels were generated // Need to exit the outter processing loop - if (genGPUMipMaps) + if (genGPUMipMaps && isGPUEncoding) contineProcessing = false; } if (pFeedbackProc) pFeedbackProc(100, NULL, NULL); - if (isGPUEncoding && !dataProcessed) { + if (isGPUEncoding && !dataProcessed) + { PrintInfo("Error source texture is not processed, check source width & height is divisable by 4!\n"); return CMP_ERR_CMP_DESTINATION; } @@ -1892,22 +2391,25 @@ CMP_ERROR CMP_ConvertMipTextureCGP(MipSet* pMipSetIn, MipSet* pMipSetOut, CMP_Co } // ToDo replace with plugin scan, qt checks and src dest format checks. -bool SupportedFileTypes(std::string fileExt) { - char* supportedTypes[19] = { - "DDS", "KTX", "BMP", "PNG", "JPEG", "JPG", "EXR", "TGA", "TIF", "TIFF", "OBJ", "GLTF", "PBM", "PGM", "PPM", "XBM", "XPM", "ASTC", "DRC" - }; - for (int i = 0; i < 19; i++) { +bool SupportedFileTypes(std::string fileExt) +{ + char* supportedTypes[20] = {"DDS", "KTX", "KTX2", "BMP", "PNG", "JPEG", "JPG", "EXR", "TGA", "TIF", + "TIFF", "OBJ", "GLTF", "PBM", "PGM", "PPM", "XBM", "XPM", "ASTC", "DRC"}; + for (int i = 0; i < 20; i++) + { if (fileExt.compare(supportedTypes[i]) == 0) return true; } return false; } -void PrintInfoStr(const char* InfoStr) { +void PrintInfoStr(const char* InfoStr) +{ PrintInfo(InfoStr); } -int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { +int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) +{ int processResult = 0; double frequency, conversion_loopStartTime = {0}, conversion_loopEndTime = {0}, compress_loopStartTime = {0}, compress_loopEndTime = {0}, decompress_loopStartTime = {0}, decompress_loopEndTime = {0}; @@ -1952,43 +2454,56 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { bool MoreSourceFiles = false; PluginInterface_Analysis* Plugin_Analysis = NULL; - if (g_CmdPrams.logresults) { + if (g_CmdPrams.logresults) + { // Check for vaild -log usage - if ((!fileIsModel(g_CmdPrams.SourceFile)) && (!fileIsModel(g_CmdPrams.DestFile))) { + if ((!fileIsModel(g_CmdPrams.SourceFile)) && (!fileIsModel(g_CmdPrams.DestFile))) + { //int testpassed = 0; Plugin_Analysis = reinterpret_cast(g_pluginManager.GetPlugin("IMAGE", "ANALYSIS")); - } else { + } + else + { PrintInfo("Warning: -log is only valid for Images, option is turned off!\n"); g_CmdPrams.logresults = false; } } + // Check if print status line has been assigned + // if not get it a default to printf + if (PrintStatusLine == NULL) + { + PrintStatusLine = &LocalPrintF; + } + // Fix to output view to look the same as v3.1 print info for calls to CMP_ConvertMipTexture g_CmdPrams.CompressOptions.m_PrintInfoStr = PrintInfoStr; - do { + do + { // Initailize stats data and defaults for repeated use in do while()! g_CmdPrams.compress_nIterations = 0; g_CmdPrams.decompress_nIterations = 0; g_CmdPrams.CompressOptions.format_support_hostEncoder = false; - if ((!fileIsModel(g_CmdPrams.SourceFile)) && (!fileIsModel(g_CmdPrams.DestFile))) { - // Check if print status line has been assigned - // if not get it a default to printf - if (PrintStatusLine == NULL) { - PrintStatusLine = &LocalPrintF; - } - - if (g_CmdPrams.analysis || g_CmdPrams.diffImage) { - if (!(GenerateAnalysis(g_CmdPrams.SourceFile, g_CmdPrams.DestFile))) { + if ((!fileIsModel(g_CmdPrams.SourceFile)) && (!fileIsModel(g_CmdPrams.DestFile))) + { + if (g_CmdPrams.analysis || g_CmdPrams.diffImage) + { + if (!(GenerateAnalysis(g_CmdPrams.SourceFile, g_CmdPrams.DestFile))) + { + LogErrorToCSVFile(ANALYSIS_IMAGE_TESTFAILED); PrintInfo("Error: Image Analysis Failed\n"); return -1; } return 0; } - if (g_CmdPrams.imageprops) { - if (!(GenerateImageProps(g_CmdPrams.SourceFile))) { + if (g_CmdPrams.imageprops) + { + if (!(GenerateImageProps(g_CmdPrams.SourceFile))) + { + LogErrorToCSVFile(ANALYSIS_RETRIEVE_IMAGE_PROPERTIES); PrintInfo("Error: Failed to retrieve image properties\n"); return -1; } @@ -2013,28 +2528,31 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { helper_to_upper(DestExt); // Check if destination file is supported, else print warning and continue. - if (!SupportedFileTypes(DestExt)) { + if (!SupportedFileTypes(DestExt)) + { + LogErrorToCSVFile(ANALYSIS_DESTINATION_TYPE_NOT_SUPPORTED); PrintInfo("Error: Destination file type not is supported\n"); processResult = -2; continue; } // Try some known format to attach by supported file ext types - if (g_CmdPrams.CompressOptions.DestFormat == CMP_FORMAT_Unknown) { + if (g_CmdPrams.CompressOptions.DestFormat == CMP_FORMAT_Unknown) + { if (DestExt.compare("ASTC") == 0) g_CmdPrams.CompressOptions.DestFormat = CMP_FORMAT_ASTC; else #ifdef USE_BASIS if (DestExt.compare("BASIS") == 0) - g_CmdPrams.CompressOptions.DestFormat = CMP_FORMAT_BASIS; - else + g_CmdPrams.CompressOptions.DestFormat = CMP_FORMAT_BASIS; + else #endif - if (DestExt.compare("EXR") == 0) - g_CmdPrams.CompressOptions.DestFormat = CMP_FORMAT_ARGB_16F; - else if (DestExt.compare("BMP") == 0) - g_CmdPrams.CompressOptions.DestFormat = CMP_FORMAT_ARGB_8888; - else if (DestExt.compare("PNG") == 0) - g_CmdPrams.CompressOptions.DestFormat = CMP_FORMAT_ARGB_8888; + if (DestExt.compare("EXR") == 0) + g_CmdPrams.CompressOptions.DestFormat = CMP_FORMAT_ARGB_16F; + else if (DestExt.compare("BMP") == 0) + g_CmdPrams.CompressOptions.DestFormat = CMP_FORMAT_ARGB_8888; + else if (DestExt.compare("PNG") == 0) + g_CmdPrams.CompressOptions.DestFormat = CMP_FORMAT_ARGB_8888; } destFormat = g_CmdPrams.CompressOptions.DestFormat; @@ -2042,25 +2560,34 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { // Determin if destinationfile is to be Compressed DestinationFormatIsCompressed = CMP_IsCompressedFormat(destFormat); - if (destFormat != CMP_FORMAT_ASTC) { + if (destFormat != CMP_FORMAT_ASTC) + { // Check for valid format to destination for ASTC - if (DestExt.compare("ASTC") == 0) { + if (DestExt.compare("ASTC") == 0) + { + LogErrorToCSVFile(ANALYSIS_ASTC_DESTINATION_TYPE_NOT_SUPPORTED); PrintInfo("Error: destination file type only supports ASTC compression format\n"); return -1; } - } else { + } + else + { // Check for valid format to destination for ASTC - if (!((DestExt.compare("ASTC") == 0) || (DestExt.compare("KTX") == 0))) { - PrintInfo("Error: destination file type for ASTC must be set to .astc or .ktx\n"); + if (!((DestExt.compare("ASTC") == 0) || (DestExt.compare("KTX") == 0) || (DestExt.compare("KTX2") == 0))) + { + LogErrorToCSVFile(ANALYSIS_ASTC_DESTINATION_FILE_FORMAT_NOTSET); + PrintInfo("Error: destination file type for ASTC must be set to .astc or .ktx, .ktx2\n"); return -1; } //========================================================== // Determine if MIP mapping is set for invalid file formats //========================================================== - if ((g_CmdPrams.MipsLevel > 1) && (DestExt.compare("ASTC") == 0)) { - PrintInfo("Error: destination file type for ASTC must be set to .ktx for miplevel support\n"); + if ((g_CmdPrams.MipsLevel > 1) && (DestExt.compare("ASTC") == 0)) + { + LogErrorToCSVFile(ANALYSIS_ASTC_MIPMAP_DESTINATION_NOT_SUPPORTED); + PrintInfo("Error: destination file type for ASTC must be set to .ktx or .ktx2 for miplevel support\n"); return -1; } } @@ -2071,7 +2598,8 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { //---------------- // Read Input file //---------------- - if (p_userMipSetIn) { //for GUI + if (p_userMipSetIn) + { //for GUI memcpy(&g_MipSetIn, p_userMipSetIn, sizeof(MipSet)); // Data in DXTn Files are expected to be in BGRA as input to CMP_ConvertTexture // Data in ASTC BC6 BC7 etc - expect data to be RGBA as input to CMP_ConvertTexture @@ -2079,7 +2607,9 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { g_MipSetIn.m_pMipLevelTable = p_userMipSetIn->m_pMipLevelTable; Delete_gMipSetIn = false; - } else { //for CLI + } + else + { //for CLI Delete_gMipSetIn = true; // =============================================== @@ -2108,7 +2638,9 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { //--------------------------------------- // Set user specification for Block sizes //---------------------------------------- - if (AMDLoadMIPSTextureImage(g_CmdPrams.SourceFile.c_str(), &g_MipSetIn, g_CmdPrams.use_OCV, &g_pluginManager) != 0) { + if (AMDLoadMIPSTextureImage(g_CmdPrams.SourceFile.c_str(), &g_MipSetIn, g_CmdPrams.use_OCV, &g_pluginManager) != 0) + { + LogErrorToCSVFile(ANALYSIS_FAILED_FILELOAD); cleanup(Delete_gMipSetIn, SwizzledMipSetIn); PrintInfo("Error: loading source image\n"); return -1; @@ -2131,13 +2663,17 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { g_MipSetIn.m_nBlockWidth = g_CmdPrams.BlockWidth; g_MipSetIn.m_nBlockHeight = g_CmdPrams.BlockHeight; g_MipSetIn.m_nBlockDepth = g_CmdPrams.BlockDepth; - if (g_CmdPrams.use_noMipMaps) { + if (g_CmdPrams.use_noMipMaps) + { g_MipSetIn.m_Flags = MS_FLAG_DisableMipMapping; } // check if CubeMap is supported in destination file - if (g_MipSetIn.m_TextureType == TT_CubeMap) { - if (!(DestExt.compare("DDS") == 0 || DestExt.compare("KTX") == 0)) { + if (g_MipSetIn.m_TextureType == TT_CubeMap) + { + if (!(DestExt.compare("DDS") == 0 || DestExt.compare("KTX") == 0 || DestExt.compare("KTX2") == 0)) + { + LogErrorToCSVFile(ANALYSIS_CUBEMAP_NOTSUPPORTED); PrintInfo("Error: Cube Maps is not supported in destination file.\n"); cleanup(Delete_gMipSetIn, SwizzledMipSetIn); return -1; @@ -2154,9 +2690,12 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { srcFormat = g_MipSetIn.m_format; // Load MIPS did not return a format try to set one - if (srcFormat == CMP_FORMAT_Unknown) { + if (srcFormat == CMP_FORMAT_Unknown) + { g_MipSetIn.m_format = GetFormat(&g_MipSetIn); - if (g_MipSetIn.m_format == CMP_FORMAT_Unknown) { + if (g_MipSetIn.m_format == CMP_FORMAT_Unknown) + { + LogErrorToCSVFile(ANALYSIS_UNSUPPORTED_IMAGE_FORMAT); cleanup(Delete_gMipSetIn, SwizzledMipSetIn); PrintInfo("Error: unsupported input image file format\n"); return -1; @@ -2169,7 +2708,8 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { SourceFormatIsCompressed = CMP_IsCompressedFormat(srcFormat); #ifndef ENABLE_MAKE_COMPATIBLE_API - if ((FloatFormat(srcFormat) && !FloatFormat(destFormat)) || (!FloatFormat(srcFormat) && FloatFormat(destFormat))) { + if ((FloatFormat(srcFormat) && !FloatFormat(destFormat)) || (!FloatFormat(srcFormat) && FloatFormat(destFormat))) + { cleanup(Delete_gMipSetIn, SwizzledMipSetIn); PrintInfo("Error: Processing floating point format <-> non-floating point format is not supported\n"); return -1; @@ -2178,76 +2718,101 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { //===================================================== // Check for Transcode else Unsupported conversion // ==================================================== - if (SourceFormatIsCompressed && DestinationFormatIsCompressed) { + if (SourceFormatIsCompressed && DestinationFormatIsCompressed) + { #ifdef USE_CMP_TRANSCODE #ifdef USE_GTC if ((g_MipSetIn.m_format == CMP_FORMAT_GTC) && ((destFormat == CMP_FORMAT_ASTC) || (destFormat == CMP_FORMAT_BC1) || - (destFormat == CMP_FORMAT_BC7) || (destFormat == CMP_FORMAT_ETC2_RGB))) { + (destFormat == CMP_FORMAT_BC7) || (destFormat == CMP_FORMAT_ETC2_RGB))) + { CMP_TranscodeFormat = true; - } else + } + else #endif #ifdef USE_BASIS if ((g_MipSetIn.m_format == CMP_FORMAT_BASIS) && - ((destFormat == CMP_FORMAT_BC1) || (destFormat == CMP_FORMAT_BC7) || (destFormat == CMP_FORMAT_ETC2_RGB))) { - CMP_TranscodeFormat = true; - } else + ((destFormat == CMP_FORMAT_BC1) || (destFormat == CMP_FORMAT_BC7) || (destFormat == CMP_FORMAT_ETC2_RGB))) + { + CMP_TranscodeFormat = true; + } + else #endif #endif - { - cleanup(Delete_gMipSetIn, SwizzledMipSetIn); - PrintInfo("Trascoding Error: Compressed source and compressed destination selection is not supported\n"); - return -1; - } + { + LogErrorToCSVFile(ANALYSIS_TRANSCODE_SRC_TO_DST_NOT_SUPPORTED); + cleanup(Delete_gMipSetIn, SwizzledMipSetIn); + PrintInfo("Trascoding Error: Compressed source and compressed destination selection is not supported\n"); + return -1; + } } //===================================================== // Perform swizzle // =================================================== - if (p_userMipSetIn) { //for GUI + if (p_userMipSetIn) + { //for GUI #ifdef USE_SWIZZLE - if (g_MipSetIn.m_swizzle && !g_CmdPrams.CompressOptions.bUseCGCompress) { + if (g_MipSetIn.m_swizzle && !g_CmdPrams.CompressOptions.bUseCGCompress) + { //SwizzleMipMap(&g_MipSetIn); //SwizzledMipSetIn = true; } #endif - } else { + } + else + { if (g_MipSetIn.m_swizzle) SwizzleMipMap(&g_MipSetIn); } + CMP_BOOL isGPUEncoding = !((g_CmdPrams.CompressOptions.nEncodeWith == CMP_Compute_type::CMP_CPU) || + (g_CmdPrams.CompressOptions.nEncodeWith == CMP_Compute_type::CMP_UNKNOWN) || + (g_CmdPrams.CompressOptions.nEncodeWith == CMP_Compute_type::CMP_HPC)); + + //====================================================== // Determine if MIP mapping is required // if so generate the MIP levels for the source file //======================================================= if (((g_CmdPrams.MipsLevel > 1) && (g_MipSetIn.m_nMipLevels == 1)) && (!g_CmdPrams.use_noMipMaps)) { int nMinSize; + // Precheck user setting against image size - CMP_INT nHeight = g_MipSetIn.m_nHeight; - CMP_INT nWidth = g_MipSetIn.m_nWidth; - CMP_INT maxLevel = 1; // count from top level - CMP_INT MipsLevel = g_CmdPrams.MipsLevel; - while (MipsLevel > 0) { - maxLevel++; - nWidth = CMP_MAX(nWidth >> 1, 1); - nHeight = CMP_MAX(nHeight >> 1, 1); - if ((nWidth <= 1) || (nHeight <= 1)) - break; - MipsLevel--; - } + CMP_INT nHeight = g_MipSetIn.m_nHeight; + CMP_INT nWidth = g_MipSetIn.m_nWidth; + CMP_INT maxLevel = CMP_CalcMaxMipLevel(nWidth, nHeight, (g_CmdPrams.CompressOptions.genGPUMipMaps || isGPUEncoding )); // User has two option to specify MIP levels - if (g_CmdPrams.nMinSize > 0) { + if (g_CmdPrams.nMinSize > 0) + { nMinSize = g_CmdPrams.nMinSize; - } else { + } + else + { if (maxLevel < g_CmdPrams.MipsLevel) + { PrintInfo("Warning miplevels %d is larger then required, value is autoset to use %d\n", g_CmdPrams.MipsLevel, maxLevel); - nMinSize = CMP_CalcMinMipSize(g_MipSetIn.m_nHeight, g_MipSetIn.m_nWidth, g_CmdPrams.MipsLevel); + } + else + maxLevel = g_CmdPrams.MipsLevel; + + nMinSize = CMP_CalcMinMipSize(g_MipSetIn.m_nHeight, g_MipSetIn.m_nWidth, maxLevel); } CMP_GenerateMIPLevels((CMP_MipSet*)&g_MipSetIn, nMinSize); } + else if (g_CmdPrams.CompressOptions.genGPUMipMaps) + { + // check ranges for GPU use + CMP_INT maxlevel = CMP_CalcMaxMipLevel(g_MipSetIn.m_nHeight, g_MipSetIn.m_nWidth, true); + if (maxlevel < g_CmdPrams.MipsLevel) + { + PrintInfo("Warning miplevels %d is larger then required, value is autoset to use %d\n", g_CmdPrams.MipsLevel, maxlevel); + g_CmdPrams.MipsLevel = maxlevel; + } + } // -------------------------------- // Setup Compressed Mip Set @@ -2274,7 +2839,8 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { // Example: BMP -> DDS with -fd Compression flag // //===================================================== - if ((!SourceFormatIsCompressed) && (DestinationFormatIsCompressed)) { + if ((!SourceFormatIsCompressed) && (DestinationFormatIsCompressed)) + { compress_loopStartTime = timeStampsec(); g_CmdPrams.CompressOptions.getPerfStats = true; g_CmdPrams.CompressOptions.getDeviceInfo = true; @@ -2287,9 +2853,10 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { // Use CGP only if it is not a transcoder format if (g_CmdPrams.CompressOptions.bUseCGCompress #ifdef USE_BASIS - && (g_MipSetCmp.m_format != CMP_FORMAT_BASIS) + && (g_MipSetCmp.m_format != CMP_FORMAT_BASIS) #endif - ) { + ) + { // Use HPC, GPU Interfaces // Use this for internal CPU verion code tests @@ -2303,29 +2870,34 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { // cmp_status = CMP_ProcessTexture(&g_MipSetIn,&g_MipSetCmp,kernel_options,pFeedbackProc); // Save info for -log - if (g_CmdPrams.logresultsToFile) { + if (g_CmdPrams.logresultsToFile) + { MipLevel* pInMipLevel = g_CMIPS->GetMipLevel(&g_MipSetIn, 0, 0); g_CmdPrams.dwHeight = pInMipLevel->m_nHeight; g_CmdPrams.dwWidth = pInMipLevel->m_nWidth; g_CmdPrams.dwDataSize = pInMipLevel->m_dwLinearSize; } + // set for kernel options use, incase of GPU processing + g_CmdPrams.CompressOptions.miplevels = g_CmdPrams.MipsLevel; + // Process the texture cmp_status = CMP_ConvertMipTextureCGP(&g_MipSetIn, &g_MipSetCmp, &g_CmdPrams.CompressOptions, pFeedbackProc); g_CmdPrams.compress_nIterations = g_MipSetCmp.m_nIterations; - } else { - // use Compressonator SDK intercace: This is only CPU based. Check if user set GPU, give warning msg - CMP_BOOL isGPUEncoding = !((g_CmdPrams.CompressOptions.nEncodeWith == CMP_Compute_type::CMP_CPU) || - (g_CmdPrams.CompressOptions.nEncodeWith == CMP_Compute_type::CMP_UNKNOWN) || - (g_CmdPrams.CompressOptions.nEncodeWith == CMP_Compute_type::CMP_HPC)); + } + else + { + // use Compressonator SDK : This is only CPU based. Check if user set GPU, give warning msg if (isGPUEncoding) PrintInfo("Warning! GPU Encoding with this codec is not supported. CPU will be used for compression\n"); cmp_status = CMP_ConvertMipTexture((CMP_MipSet*)&g_MipSetIn, (CMP_MipSet*)&g_MipSetCmp, &g_CmdPrams.CompressOptions, pFeedbackProc); g_CmdPrams.compress_nIterations = g_MipSetCmp.m_nIterations; } - if (cmp_status != CMP_OK) { + if (cmp_status != CMP_OK) + { + LogErrorToCSVFile(ANALYSIS_COMPRESSING_TEXTURE); PrintInfo("Error %d: Compressing Texture\n", cmp_status); cleanup(Delete_gMipSetIn, SwizzledMipSetIn); return -1; @@ -2344,7 +2916,8 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { //============================================== if ((!SourceFormatIsCompressed) && (DestinationFormatIsCompressed) && (IsDestinationUnCompressed((const char*)g_CmdPrams.DestFile.c_str()) == false) - ) { + ) + { //------------------------------------------------------------- // Set user specification for ASTC Block sizes that was used! //------------------------------------------------------------- @@ -2355,7 +2928,9 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { #ifdef USE_WITH_COMMANDLINE_TOOL PrintInfo("\n"); #endif - if (AMDSaveMIPSTextureImage(g_CmdPrams.DestFile.c_str(), &g_MipSetCmp, g_CmdPrams.use_OCV_out, g_CmdPrams.CompressOptions) != 0) { + if (AMDSaveMIPSTextureImage(g_CmdPrams.DestFile.c_str(), &g_MipSetCmp, g_CmdPrams.use_OCV_out, g_CmdPrams.CompressOptions) != 0) + { + LogErrorToCSVFile(ANALYSIS_FAILED_FILESAVE); PrintInfo("Error: saving image failed, write permission denied or format is unsupported for the file extension.\n"); cleanup(Delete_gMipSetIn, SwizzledMipSetIn); return -1; @@ -2363,9 +2938,11 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { // User requested a DECOMPRESS file also // Set a new destinate and flag a Midway Decompress - if (g_CmdPrams.doDecompress) { + if (g_CmdPrams.doDecompress) + { // Clean the Mipset if any was set - if (g_MipSetOut.m_pMipLevelTable) { + if (g_MipSetOut.m_pMipLevelTable) + { g_CMIPS->FreeMipSet(&g_MipSetOut); } g_CmdPrams.DestFile = g_CmdPrams.DecompressFile; @@ -2380,10 +2957,13 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { // Case example: BMP -> BMP with -fd uncompression flag // //===================================================== - if ((!SourceFormatIsCompressed) && (!DestinationFormatIsCompressed)) { + if ((!SourceFormatIsCompressed) && (!DestinationFormatIsCompressed)) + { TranscodeBits = true; // Check if source and destinatation types are supported for transcoding - if ((g_MipSetOut.m_TextureType == TT_2D) && (g_MipSetIn.m_TextureType == TT_CubeMap)) { + if ((g_MipSetOut.m_TextureType == TT_2D) && (g_MipSetIn.m_TextureType == TT_CubeMap)) + { + LogErrorToCSVFile(ANALYSIS_CUBEMAP_TRANSCODE_NOTSUPPORTED); PrintInfo("Error: Transcoding Cube Maps is not supported.\n"); cleanup(Delete_gMipSetIn, SwizzledMipSetIn); return -1; @@ -2397,7 +2977,8 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { // Case example: BMP -> BMP with -fd compression flag // //===================================================== - if ((!SourceFormatIsCompressed) && (DestinationFormatIsCompressed) && (IsDestinationUnCompressed((const char*)g_CmdPrams.DestFile.c_str()) == true)) { + if ((!SourceFormatIsCompressed) && (DestinationFormatIsCompressed) && (IsDestinationUnCompressed((const char*)g_CmdPrams.DestFile.c_str()) == true)) + { MidwayDecompress = true; // Prepare for an uncompress request on destination p_MipSetIn = &g_MipSetCmp; @@ -2412,38 +2993,54 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { // BMP - BMP with -fd flag(s) // //===================================================== - if (MidwayDecompress) { + if (MidwayDecompress) + { PrintInfo("\nProcessed image is been decompressed to new target format!\n"); } - if (((SourceFormatIsCompressed) && (!DestinationFormatIsCompressed)) || (TranscodeBits) || (MidwayDecompress)) { + if (((SourceFormatIsCompressed) && (!DestinationFormatIsCompressed)) || (TranscodeBits) || (MidwayDecompress)) + { compress_loopStartTime = timeStampsec(); - g_MipSetOut.m_TextureDataType = TDT_ARGB; - - if (SourceFormatIsCompressed) { + if (SourceFormatIsCompressed) + { // BMP is saved as CMP_FORMAT_ARGB_8888 // EXR is saved as CMP_FORMAT_ARGB_32F - switch (srcFormat) { + switch (srcFormat) + { case CMP_FORMAT_BC6H: case CMP_FORMAT_BC6H_SF: destFormat = CMP_FORMAT_ARGB_16F; g_MipSetOut.m_ChannelFormat = CF_Float16; break; + case CMP_FORMAT_BC4_S: + case CMP_FORMAT_BC5_S: + destFormat = CMP_FORMAT_RGBA_8888_S; + g_MipSetOut.m_TextureDataType = TDT_XRGB; + break; default: destFormat = CMP_FORMAT_ARGB_8888; break; } - } else { - if (MidwayDecompress) { + } + else + { + if (MidwayDecompress) + { // Need to determin a target format. // Based on file extension. - switch (srcFormat) { + switch (srcFormat) + { case CMP_FORMAT_BC6H: case CMP_FORMAT_BC6H_SF: destFormat = CMP_FORMAT_ARGB_16F; g_MipSetOut.m_ChannelFormat = CF_Float16; break; + case CMP_FORMAT_BC4_S: + case CMP_FORMAT_BC5_S: + destFormat = CMP_FORMAT_RGBA_8888_S; + g_MipSetOut.m_TextureDataType = TDT_XRGB; + break; default: destFormat = FormatByFileExtension((const char*)g_CmdPrams.DestFile.c_str(), &g_MipSetOut); break; @@ -2456,14 +3053,24 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { if (destFormat == CMP_FORMAT_Unknown) destFormat = CMP_FORMAT_ARGB_8888; - if (destFormat == CMP_FORMAT_ARGB_32F) - g_MipSetOut.m_ChannelFormat = CF_Float32; - else if (destFormat == CMP_FORMAT_ARGB_16F) - g_MipSetOut.m_ChannelFormat = CF_Float16; - + g_MipSetOut.m_ChannelFormat = cmp_getChannelFormat(destFormat); g_MipSetOut.m_format = destFormat; g_MipSetOut.m_isDeCompressed = srcFormat != CMP_FORMAT_Unknown ? srcFormat : CMP_FORMAT_MAX; + // set m_TextureDataType Correct to Format Type + switch (destFormat) + { + case CMP_FORMAT_RGB_888: + g_MipSetOut.m_TextureDataType = TDT_RGB; + break; + case CMP_FORMAT_RGBA_8888_S: + // skip already set + break; + default: + g_MipSetOut.m_TextureDataType = TDT_ARGB; + break; + } + // Allocate output MipSet if (!g_CMIPS->AllocateMipSet(&g_MipSetOut, g_MipSetOut.m_ChannelFormat, @@ -2471,7 +3078,9 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { p_MipSetIn->m_TextureType, p_MipSetIn->m_nWidth, p_MipSetIn->m_nHeight, - p_MipSetIn->m_nDepth)) { // depthsupport, what should nDepth be set as here? + p_MipSetIn->m_nDepth)) + { // depthsupport, what should nDepth be set as here? + LogErrorToCSVFile(ANALYSIS_MEMORY_ERROR2); PrintInfo("Memory Error(2): allocating MIPSet Output buffer\n"); cleanup(Delete_gMipSetIn, SwizzledMipSetIn); return -1; @@ -2491,14 +3100,18 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { decompress_loopStartTime = timeStampsec(); - for (int nFaceOrSlice = 0; nFaceOrSlice < nMaxFaceOrSlice; nFaceOrSlice++) { + for (int nFaceOrSlice = 0; nFaceOrSlice < nMaxFaceOrSlice; nFaceOrSlice++) + { int nMipWidth = nWidth; int nMipHeight = nHeight; - for (int nMipLevel = 0; nMipLevel < p_MipSetIn->m_nMipLevels; nMipLevel++) { + for (int nMipLevel = 0; nMipLevel < p_MipSetIn->m_nMipLevels; nMipLevel++) + { MipLevel* pInMipLevel = g_CMIPS->GetMipLevel(p_MipSetIn, nMipLevel, nFaceOrSlice); - if (!pInMipLevel) { - PrintInfo("Memory Error(2): allocating MIPSet Output Cmp level buffer\n"); + if (!pInMipLevel) + { + LogErrorToCSVFile(ANALYSIS_MEMORY_ERROR3); + PrintInfo("Memory Error(3): allocating MIPSet Output Cmp level buffer\n"); cleanup(Delete_gMipSetIn, SwizzledMipSetIn); return -1; } @@ -2511,8 +3124,10 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { nMipWidth, nMipHeight, g_MipSetOut.m_ChannelFormat, - g_MipSetOut.m_TextureDataType)) { - PrintInfo("Memory Error(2): allocating MIPSet Output level buffer\n"); + g_MipSetOut.m_TextureDataType)) + { + LogErrorToCSVFile(ANALYSIS_MEMORY_ERROR4); + PrintInfo("Memory Error(4): allocating MIPSet Output level buffer\n"); cleanup(Delete_gMipSetIn, SwizzledMipSetIn); return -1; } @@ -2540,29 +3155,34 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { // Uncompressed Destination //----------------------------- CMP_Texture destTexture; - destTexture.dwSize = sizeof(destTexture); - destTexture.dwWidth = nMipWidth; - destTexture.dwHeight = nMipHeight; - destTexture.dwPitch = 0; - destTexture.format = destFormat; - destTexture.dwDataSize = CMP_CalculateBufferSize(&destTexture); - destTexture.pData = g_CMIPS->GetMipLevel(&g_MipSetOut, nMipLevel, nFaceOrSlice)->m_pbData; - if (!g_CmdPrams.silent) { - if ((nMipLevel > 1) || (nFaceOrSlice > 1)) - PrintInfo("\rProcessing destination MipLevel %2d FaceOrSlice %2d", nMipLevel + 1, nFaceOrSlice); - else - PrintInfo("\rProcessing destination "); + destTexture.dwSize = sizeof(destTexture); + destTexture.dwWidth = nMipWidth; + destTexture.dwHeight = nMipHeight; + destTexture.dwPitch = 0; + destTexture.nBlockHeight = p_MipSetIn->m_nBlockHeight; + destTexture.nBlockWidth = p_MipSetIn->m_nBlockWidth; + destTexture.nBlockDepth = p_MipSetIn->m_nBlockDepth; + destTexture.format = destFormat; + destTexture.dwDataSize = CMP_CalculateBufferSize(&destTexture); + destTexture.pData = g_CMIPS->GetMipLevel(&g_MipSetOut, nMipLevel, nFaceOrSlice)->m_pbData; + if (!g_CmdPrams.silent) + { + PrintInfo("\rProcessing destination MipLevel %2d FaceOrSlice %2d", nMipLevel + 1, nFaceOrSlice + 1); } #ifdef _WIN32 - if (/*(srcTexture.dwDataSize > destTexture.dwDataSize) || */ (IsBadWritePtr(destTexture.pData, destTexture.dwDataSize))) { - PrintInfo("Memory Error(2): Destination image must be compatible with source\n"); + if (/*(srcTexture.dwDataSize > destTexture.dwDataSize) || */ (IsBadWritePtr(destTexture.pData, destTexture.dwDataSize))) + { + LogErrorToCSVFile(ANALYSIS_MEMORY_ERROR5); + PrintInfo("Memory Error(5): Destination image must be compatible with source\n"); cleanup(Delete_gMipSetIn, SwizzledMipSetIn); return -1; } #else int nullfd = open("/dev/random", O_WRONLY); - if (write(nullfd, destTexture.pData, destTexture.dwDataSize) < 0) { - PrintInfo("Memory Error(2): Destination image must be compatible with source\n"); + if (write(nullfd, destTexture.pData, destTexture.dwDataSize) < 0) + { + LogErrorToCSVFile(ANALYSIS_MEMORY_ERROR5); + PrintInfo("Memory Error(5): Destination image must be compatible with source\n"); cleanup(Delete_gMipSetIn, SwizzledMipSetIn); return -1; } @@ -2570,29 +3190,36 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { #endif g_fProgress = -1; - if (use_GPUDecode) { + if (use_GPUDecode) + { #ifdef _WIN32 -#ifndef DISABLE_TESTCODE - if (srcTexture.format == CMP_FORMAT_ASTC) { - PrintInfo("Decompress Error: ASTC decompressed with GPU is not supported. Please view ASTC compressed images using CPU.\n"); + if (srcTexture.format == CMP_FORMAT_ASTC) + { + LogErrorToCSVFile(ANALYSIS_ATSC_TRANCODE_WITH_GPU_NOT_SUPPORTED); + PrintInfo("Destination Error: ASTC decompressed with GPU is not supported. Please view ASTC compressed images using CPU.\n"); cleanup(Delete_gMipSetIn, SwizzledMipSetIn); return -1; } - if (CMP_DecompressTexture(&srcTexture, &destTexture, DecodeWith) != CMP_OK) { - PrintInfo("Error in decompressing source texture\n"); + if (CMP_DecompressTexture(&srcTexture, &destTexture, DecodeWith) != CMP_OK) + { + LogErrorToCSVFile(ANALYSIS_DECOMPRESSING_SOURCE); + PrintInfo("Error in Destination source texture\n"); cleanup(Delete_gMipSetIn, SwizzledMipSetIn); return -1; } -#endif #else PrintInfo("GPU Decompress is not supported in linux.\n"); cleanup(Delete_gMipSetIn, SwizzledMipSetIn); return -1; #endif - } else { - if (CMP_ConvertTexture(&srcTexture, &destTexture, &g_CmdPrams.CompressOptions, pFeedbackProc) != CMP_OK) { - PrintInfo("Error(2) in compressing destination texture\n"); + } + else + { + if (CMP_ConvertTexture(&srcTexture, &destTexture, &g_CmdPrams.CompressOptions, pFeedbackProc) != CMP_OK) + { + LogErrorToCSVFile(ANALYSIS_ERROR_COMPRESSING_DESTINATION_TEXTURE); + PrintInfo("Error in compressing destination texture\n"); cleanup(Delete_gMipSetIn, SwizzledMipSetIn); return -1; } @@ -2644,7 +3271,9 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { #ifdef USE_WITH_COMMANDLINE_TOOL PrintInfo("\n"); #endif - if (AMDSaveMIPSTextureImage(g_CmdPrams.DestFile.c_str(), p_MipSetOut, g_CmdPrams.use_OCV_out, g_CmdPrams.CompressOptions) != 0) { + if (AMDSaveMIPSTextureImage(g_CmdPrams.DestFile.c_str(), p_MipSetOut, g_CmdPrams.use_OCV_out, g_CmdPrams.CompressOptions) != 0) + { + LogErrorToCSVFile(ANALYSIS_FAILED_FILESAVE); PrintInfo("Error: saving image failed, write permission denied or format is unsupported for the file extension.\n"); cleanup(Delete_gMipSetIn, SwizzledMipSetIn); return -1; @@ -2655,9 +3284,11 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { //============================================================ // Process Transcode Compress source to new compressed target //============================================================ - if (SourceFormatIsCompressed && DestinationFormatIsCompressed && CMP_TranscodeFormat) { + if (SourceFormatIsCompressed && DestinationFormatIsCompressed && CMP_TranscodeFormat) + { PrintInfo("\nTranscoding %s to %s\n", GetFormatDesc(g_MipSetIn.m_format), GetFormatDesc(g_MipSetCmp.m_format)); - if (GTCTranscode(&g_MipSetIn, &g_MipSetCmp, g_pluginManager)) { + if (GTCTranscode(&g_MipSetIn, &g_MipSetCmp, g_pluginManager)) + { g_MipSetOut.m_nMipLevels = p_MipSetIn->m_nMipLevels; p_MipSetOut = &g_MipSetCmp; @@ -2668,7 +3299,8 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { p_MipSetOut->m_nBlockHeight = g_CmdPrams.BlockHeight; p_MipSetOut->m_nBlockDepth = g_CmdPrams.BlockDepth; - if (AMDSaveMIPSTextureImage(g_CmdPrams.DestFile.c_str(), &g_MipSetCmp, g_CmdPrams.use_OCV_out, g_CmdPrams.CompressOptions) != 0) { + if (AMDSaveMIPSTextureImage(g_CmdPrams.DestFile.c_str(), &g_MipSetCmp, g_CmdPrams.use_OCV_out, g_CmdPrams.CompressOptions) != 0) + { PrintInfo("Error: saving image failed, write permission denied or format is unsupported for the file extension.\n"); cleanup(Delete_gMipSetIn, SwizzledMipSetIn); return -1; @@ -2691,7 +3323,8 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { if (g_CmdPrams.conversion_fDuration < 0.001) g_CmdPrams.conversion_fDuration = 0.0; - if ((!g_CmdPrams.silent) && (g_CmdPrams.showperformance)) { + if ((!g_CmdPrams.silent) && (g_CmdPrams.showperformance)) + { #ifdef USE_WITH_COMMANDLINE_TOOL PrintInfo("\r"); #endif @@ -2703,25 +3336,34 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { g_CmdPrams.compress_fDuration); if (g_CmdPrams.decompress_nIterations) - PrintInfo("DeCompressed to %s with %i iteration(s) in %.3f seconds\n", + PrintInfo("Processed to %s with %i iteration(s) in %.3f seconds\n", GetFormatDesc(destFormat), g_CmdPrams.decompress_nIterations, g_CmdPrams.decompress_fDuration); PrintInfo("Total time taken (includes file I/O): %.3f seconds\n", g_CmdPrams.conversion_fDuration); } - } else { + } + else + { #ifdef USE_MESH_CLI //Mesh Optimization #ifdef USE_3DMESH_OPTIMIZE - if (g_CmdPrams.doMeshOptimize) { + if (g_CmdPrams.doMeshOptimize) + { if ((fileIsGLTF(g_CmdPrams.SourceFile) && fileIsGLTF(g_CmdPrams.DestFile)) || - (fileIsOBJ(g_CmdPrams.SourceFile) && fileIsOBJ(g_CmdPrams.DestFile))) { - if (!(OptimizeMesh(g_CmdPrams.SourceFile, g_CmdPrams.DestFile))) { + (fileIsOBJ(g_CmdPrams.SourceFile) && fileIsOBJ(g_CmdPrams.DestFile))) + { + if (!(OptimizeMesh(g_CmdPrams.SourceFile, g_CmdPrams.DestFile))) + { + LogErrorToCSVFile(ANALYSIS_MESH_OPTIMIZATION_FAILED); PrintInfo("Error: Mesh Optimization Failed.\n"); return -1; } - } else { + } + else + { + LogErrorToCSVFile(ANALYSIS_MESH_OPTIMIZATION_TYPE_FAILED); PrintInfo("Error: Mesh Optimization Failed. Only glTF->glTF, obj->obj are supported in mesh optimization.\n"); return -1; } @@ -2729,20 +3371,24 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { #endif // Mesh Compression and Decompression - if (!(g_CmdPrams.doMeshOptimize && - !g_CmdPrams - .use_Draco_Encode)) { // skip mesh decompression for case only meshopt turn on: CompressonatorCLI.exe -meshopt source.gltf/obj dest.gltf/obj + if (!(g_CmdPrams.doMeshOptimize && !g_CmdPrams.use_Draco_Encode)) + { // skip mesh decompression for case only meshopt turn on: CompressonatorCLI.exe -meshopt source.gltf/obj dest.gltf/obj if ((fileIsGLTF(g_CmdPrams.SourceFile) && fileIsGLTF(g_CmdPrams.DestFile)) #ifdef _WIN32 - || (fileIsOBJ(g_CmdPrams.SourceFile) && fileIsDRC(g_CmdPrams.DestFile)) || - (fileIsDRC(g_CmdPrams.SourceFile) && fileIsOBJ(g_CmdPrams.DestFile)) || (fileIsOBJ(g_CmdPrams.SourceFile) && fileIsOBJ(g_CmdPrams.DestFile)) + || (fileIsOBJ(g_CmdPrams.SourceFile) && fileIsDRC(g_CmdPrams.DestFile)) || + (fileIsDRC(g_CmdPrams.SourceFile) && fileIsOBJ(g_CmdPrams.DestFile)) || (fileIsOBJ(g_CmdPrams.SourceFile) && fileIsOBJ(g_CmdPrams.DestFile)) #endif - ) { - if (!(CompressDecompressMesh(g_CmdPrams.SourceFile, g_CmdPrams.DestFile))) { + ) + { + if (!(CompressDecompressMesh(g_CmdPrams.SourceFile, g_CmdPrams.DestFile))) + { + LogErrorToCSVFile(ANALYSIS_MESH_COMPRESSION_FAILED); PrintInfo("Error: Mesh Compression Failed.\n"); return -1; } - } else { + } + else + { #ifdef _WIN32 PrintInfo( "Note: Mesh Compression Failed. Only glTF->glTF, obj->drc(compression), drc->obj(decompression), obj->obj(optimize first then " @@ -2750,6 +3396,7 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { #else PrintInfo("Note: Mesh Compression Failed. Only glTF->glTF are supported in mesh compression/decompression.\n"); #endif + LogErrorToCSVFile(ANALYSIS_MESH_COMPRESSION_FAILED); return -1; } } @@ -2762,14 +3409,10 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { bool result2 = GetProcessMemoryInfo(GetCurrentProcess(), &memCounter2, sizeof(memCounter2)); #endif - // Warning for log on when not compressing files - if ((g_CmdPrams.logresults) && (g_CmdPrams.decompress_nIterations > 0)) { - PrintInfo("\nWarning: log is only supported for image compression, option is turned off!\n"); - g_CmdPrams.logresults = false; - } - - if (g_CmdPrams.logresults) { - if (!g_CmdPrams.silent) { + if (g_CmdPrams.logresults) + { + if (!g_CmdPrams.silent) + { #ifdef USE_WITH_COMMANDLINE_TOOL PrintInfo("\rlog results "); #endif @@ -2778,7 +3421,8 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { CMP_ANALYSIS_DATA analysisData = {0}; analysisData.SSIM = -1; // Set data content is invalid and not processed - if (Plugin_Analysis) { + if (Plugin_Analysis) + { // Diff File { g_CmdPrams.DiffFile = g_CmdPrams.DestFile; @@ -2795,7 +3439,8 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { "", &analysisData, &g_pluginManager, - NULL) != 0) { + NULL) != 0) + { analysisData.SSIM = -2; } } // Analysis report @@ -2803,7 +3448,8 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { ProcessResults(g_CmdPrams, analysisData); - if ((analysisData.SSIM != -1) && ((analysisData.SSIM != -2))) { + if ((analysisData.SSIM != -1) && ((analysisData.SSIM != -2))) + { psnr_sum += analysisData.PSNR; ssim_sum += analysisData.SSIM; process_time_sum += g_CmdPrams.compress_fDuration; @@ -2816,23 +3462,28 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { //============================================================== // Get a new set of source and destination files for processing. //============================================================== - if (g_CmdPrams.SourceFileList.size() > 0) { + if (g_CmdPrams.SourceFileList.size() > 0) + { MoreSourceFiles = true; // Set the first file in list to SourceFile and delete it from the list g_CmdPrams.SourceFile = g_CmdPrams.SourceFileList[0].c_str(); g_CmdPrams.SourceFileList.erase(g_CmdPrams.SourceFileList.begin()); std::string destFileName; - destFileName = DefaultDestination(g_CmdPrams.SourceFile, g_CmdPrams.CompressOptions.DestFormat, g_CmdPrams.FileOutExt); - std::filesystem::path destFile = std::filesystem::path(g_CmdPrams.DestDir) / destFileName; - g_CmdPrams.DestFile = destFile.string(); - } else + destFileName = DefaultDestination(g_CmdPrams.SourceFile, g_CmdPrams.CompressOptions.DestFormat, g_CmdPrams.FileOutExt, true); + sfs::path destFile = sfs::path(g_CmdPrams.DestDir) / destFileName; + g_CmdPrams.DestFile = destFile.string(); + } + else MoreSourceFiles = false; - if (!g_CmdPrams.silent) { - if ((g_CmdPrams.logresults) && (!g_CmdPrams.logresultsToFile)) { + if (!g_CmdPrams.silent) + { + if ((g_CmdPrams.logresults) && (!g_CmdPrams.logresultsToFile)) + { // check if any data was calculated - if (g_CmdPrams.SSIM >= 0) { + if (g_CmdPrams.SSIM >= 0) + { PrintInfo("MSE %.2f PSRN %.1f SSIM %.4f\n", g_CmdPrams.MSE, g_CmdPrams.PSNR, g_CmdPrams.SSIM); } } @@ -2842,11 +3493,13 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { } while (MoreSourceFiles); - if (g_CmdPrams.logresults) { + if (g_CmdPrams.logresults) + { if (Plugin_Analysis) delete Plugin_Analysis; - if (total_processed_items > 1) { + if (total_processed_items > 1) + { char buff[128]; snprintf(buff, sizeof(buff), @@ -2862,12 +3515,13 @@ int ProcessCMDLine(CMP_Feedback_Proc pFeedbackProc, MipSet* p_userMipSetIn) { LogToResults(g_CmdPrams, "--------------\n"); } - //printf("--END--\n"); return processResult; } -void LogToResults(CCmdLineParamaters& prams, char* str) { - if (prams.logresultsToFile) { +void LogToResults(CCmdLineParamaters& prams, char* str) +{ + if (prams.logresultsToFile) + { #ifdef _WIN32 FILE* fp; //errno_t err = @@ -2875,15 +3529,18 @@ void LogToResults(CCmdLineParamaters& prams, char* str) { #else FILE* fp = fopen(prams.LogProcessResultsFile.c_str(), "a"); #endif - if (fp) { + if (fp) + { fprintf(fp, "%s", str); fclose(fp); } } } -void ProcessResults(CCmdLineParamaters& prams, CMP_ANALYSIS_DATA& analysisData) { - if (prams.logresultsToFile) { +void ProcessResults(CCmdLineParamaters& prams, CMP_ANALYSIS_DATA& analysisData) +{ + if (prams.logresultsToFile) + { bool newfile = false; // Check for file existance first! if (!CMP_FileExists(prams.LogProcessResultsFile)) @@ -2895,16 +3552,22 @@ void ProcessResults(CCmdLineParamaters& prams, CMP_ANALYSIS_DATA& analysisData) #else FILE* fp = fopen(prams.LogProcessResultsFile.c_str(), "a"); #endif - if (fp) { + if (fp) + { // Write Header info - if (newfile) { - fprintf(fp, "CompressonatorCLI Performance Log v1.1\n\n"); - fprintf(fp, "Negative values are errors in measurement\n"); - fprintf(fp, "For images with no errors PSNR=255 MSE=0 and SSIM=1.0\n\n"); - if (prams.logcsvformat) { + if (newfile) + { + fprintf(fp, "CompressonatorCLI Performance Log v1.2\n\n"); + fprintf(fp, "Negative values are errors in measurement Sets ErrCode > 0 else 0 for none\n"); + fprintf(fp, "For images with no errors MSE= 0 PSNR=255 and SSIM= 1.0\n"); + fprintf(fp, "Transcoded images MSE= 0 PSNR=255 and SSIM=-2.0\n"); + fprintf(fp, "No image data generated MSE=-1 PSNR= -1 and SSIM=-1.0 with ErrCode set\n\n"); + + if (prams.logcsvformat) + { fprintf(fp, "Source,Height,Width,LinearSize(MB),Destination,ProcessedTo,Iteration,Duration(s),Using,Quality,KPerf(ms),MTx/" - "s,MSE,PSNR,SSIM,TotalTime(s)\n"); + "s,MSE,PSNR,SSIM,TotalTime(s),ErrCode\n"); } } @@ -2938,17 +3601,25 @@ void ProcessResults(CCmdLineParamaters& prams, CMP_ANALYSIS_DATA& analysisData) //==================================== // EncodeWith option //==================================== - if (prams.CompressOptions.bUseCGCompress) { - if (prams.CompressOptions.nEncodeWith != CMP_Compute_type::CMP_CPU) { - if (!prams.CompressOptions.format_support_hostEncoder) { + if (prams.CompressOptions.bUseCGCompress) + { + if (prams.CompressOptions.nEncodeWith != CMP_Compute_type::CMP_CPU) + { + if (!prams.CompressOptions.format_support_hostEncoder) + { str_encodewith.append("[CPU] "); } str_encodewith.append(GetEncodeWithDesc(prams.CompressOptions.nEncodeWith)); - } else + } + else str_encodewith.append("[CPU]"); - } else { - if (prams.CompressOptions.bUseGPUDecompress) { - switch (prams.CompressOptions.nGPUDecode) { + } + else + { + if (prams.CompressOptions.bUseGPUDecompress) + { + switch (prams.CompressOptions.nGPUDecode) + { case GPUDecode_DIRECTX: str_encodewith.append("DirectX"); break; @@ -2960,7 +3631,8 @@ void ProcessResults(CCmdLineParamaters& prams, CMP_ANALYSIS_DATA& analysisData) str_encodewith.append("OpenGL"); break; } - } else + } + else str_encodewith.append("CPU"); } @@ -2999,21 +3671,22 @@ void ProcessResults(CCmdLineParamaters& prams, CMP_ANALYSIS_DATA& analysisData) //====================================== // Quality Data //====================================== - if (analysisData.PSNR <= 0) { + if (analysisData.PSNR <= 0) + { analysisData.PSNR = 256; } - if (analysisData.SSIM != -1) { - snprintf(buffer, 1024, "%.2f", analysisData.MSE); + + if (analysisData.SSIM != -1) + { + snprintf(buffer, 1024, "%.4f", analysisData.MSE); str_mse = buffer; snprintf(buffer, 1024, "%.1f", analysisData.PSNR); str_psnr = buffer; snprintf(buffer, 1024, "%.4f", analysisData.SSIM); str_ssim = buffer; - } else if (analysisData.SSIM == -2) { // Failed to process - str_mse = "-2"; - str_psnr = "-2"; - str_ssim = "-2"; - } else { + } + else + { str_mse = "-1"; str_psnr = "-1"; str_ssim = "-1"; @@ -3022,9 +3695,10 @@ void ProcessResults(CCmdLineParamaters& prams, CMP_ANALYSIS_DATA& analysisData) snprintf(buffer, 1024, "%.3f", prams.conversion_fDuration); str_duration = buffer; - if (prams.logcsvformat) { + if (prams.logcsvformat) + { fprintf(fp, - "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n", + "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%d\n", str_source.c_str(), str_height.c_str(), str_width.c_str(), @@ -3040,8 +3714,11 @@ void ProcessResults(CCmdLineParamaters& prams, CMP_ANALYSIS_DATA& analysisData) str_mse.c_str(), str_psnr.c_str(), str_ssim.c_str(), - str_duration.c_str()); - } else { + str_duration.c_str(), + analysisData.errCode); + } + else + { fprintf(fp, "Source : %s, Height %s, Width %s, Linear size %s MB\n", str_source.c_str(), @@ -3065,12 +3742,16 @@ void ProcessResults(CCmdLineParamaters& prams, CMP_ANALYSIS_DATA& analysisData) } fclose(fp); } - } else { - if (analysisData.PSNR <= 0) { + } + else + { + if (analysisData.PSNR <= 0) + { analysisData.PSNR = 256; } - if (analysisData.SSIM != -1) { + if (analysisData.SSIM != -1) + { prams.MSE = analysisData.MSE; prams.PSNR = analysisData.PSNR; prams.SSIM = analysisData.SSIM; diff --git a/applications/_plugins/common/cmdline.h b/applications/_plugins/common/cmdline.h index 3545644a7..958a31a23 100644 --- a/applications/_plugins/common/cmdline.h +++ b/applications/_plugins/common/cmdline.h @@ -115,6 +115,7 @@ class CCmdLineParamaters { std::vector SourceFileList; // std::string SourceFile; // std::string DestFile; // + std::string SourceDir; // std::string DestDir; // std::string DiffFile; // Diff image file name std::string DecompressFile; // diff --git a/applications/_plugins/common/cmp_fileio.cpp b/applications/_plugins/common/cmp_fileio.cpp index e968550c4..42b8f4916 100644 --- a/applications/_plugins/common/cmp_fileio.cpp +++ b/applications/_plugins/common/cmp_fileio.cpp @@ -1,6 +1,6 @@ // AMD AMDUtils code // -// Copyright(c) 2017 Advanced Micro Devices, Inc.All rights reserved. +// Copyright(c) 2020 Advanced Micro Devices, Inc.All rights reserved. // // Major Code based on Header-only tiny glTF 2.0 loader and serializer. // The MIT License (MIT) @@ -31,7 +31,6 @@ #ifdef _WIN32 #include "windows.h" #include - #include #include #include @@ -42,26 +41,37 @@ #include #endif -#include +#ifdef _CMP_CPP17_ // Build code using std::c++17 #include +namespace sfs = std::filesystem; +#else +#include +namespace sfs = std::experimental::filesystem; +#endif -bool CMP_DirExists(const std::string &abs_dir) { - if (std::filesystem::exists(abs_dir)) - return std::filesystem::is_directory(abs_dir); +#include + +bool CMP_DirExists(const std::string& abs_dir) +{ + if (sfs::exists(abs_dir)) + return sfs::is_directory(abs_dir); return (false); } -bool CMP_FileExists(const std::string &abs_filename) { - return std::filesystem::exists(abs_filename); +bool CMP_FileExists(const std::string& abs_filename) +{ + return sfs::exists(abs_filename); } -bool CMP_CreateDir(std::string sPath) { - bool success = std::filesystem::create_directory(std::filesystem::absolute(sPath)); +bool CMP_CreateDir(std::string sPath) +{ + bool success = sfs::create_directory(sfs::absolute(sPath)); return (success); } -std::string CMP_ExpandFilePath(const std::string &filepath) { +std::string CMP_ExpandFilePath(const std::string& filepath) +{ #ifdef _WIN32 DWORD len = ExpandEnvironmentStringsA(filepath.c_str(), NULL, 0); char* str = new char[len]; @@ -81,23 +91,28 @@ std::string CMP_ExpandFilePath(const std::string &filepath) { std::string s; wordexp_t p; - if (filepath.empty()) { + if (filepath.empty()) + { return ""; } // char** w; int ret = wordexp(filepath.c_str(), &p, 0); - if (ret) { + if (ret) + { // err s = filepath; return s; } // Use first element only. - if (p.we_wordv) { + if (p.we_wordv) + { s = std::string(p.we_wordv[0]); wordfree(&p); - } else { + } + else + { s = filepath; } @@ -107,24 +122,34 @@ std::string CMP_ExpandFilePath(const std::string &filepath) { #endif } -std::string CMP_JoinPath(const std::string& path0, const std::string& path1) { - if (path0.empty()) { +std::string CMP_JoinPath(const std::string& path0, const std::string& path1) +{ + if (path0.empty()) + { return path1; - } else { + } + else + { // check '/' char lastChar = *path0.rbegin(); - if (lastChar != '/') { + if (lastChar != '/') + { return path0 + std::string("/") + path1; - } else { + } + else + { return path0 + path1; } } } -std::string CMP_FindFile(const std::vector& paths, const std::string& filepath) { - for (size_t i = 0; i < paths.size(); i++) { +std::string CMP_FindFile(const std::vector& paths, const std::string& filepath) +{ + for (size_t i = 0; i < paths.size(); i++) + { std::string absPath = CMP_ExpandFilePath(CMP_JoinPath(paths[i], filepath)); - if (CMP_FileExists(absPath)) { + if (CMP_FileExists(absPath)) + { return absPath; } } @@ -132,34 +157,39 @@ std::string CMP_FindFile(const std::vector& paths, const std::strin return std::string(); } -std::string CMP_GetFilePathExtension(const std::string& FileName) { +std::string CMP_GetFilePathExtension(const std::string& FileName) +{ if (FileName.find_last_of(".") != std::string::npos) return FileName.substr(FileName.find_last_of(".") + 1); return ""; } -std::string CMP_GetBaseDir(const std::string& srcfileDirpath) { +std::string CMP_GetBaseDir(const std::string& srcfileDirpath) +{ if (srcfileDirpath.find_last_of(FILE_SPLIT_PATH) != std::string::npos) return srcfileDirpath.substr(0, srcfileDirpath.find_last_of(FILE_SPLIT_PATH)); return ""; } -std::string CMP_GetFileName(const std::string& srcfileNamepath) { +std::string CMP_GetFileName(const std::string& srcfileNamepath) +{ int pos = (int)srcfileNamepath.find_last_of(FILE_SPLIT_PATH); return srcfileNamepath.substr(pos + 1); } -bool CMP_IsHidden(const std::string &fullpath) { +bool CMP_IsHidden(const std::string& fullpath) +{ #ifdef _WIN32 - bool IsHidden = false; - DWORD Result = GetFileAttributesA(fullpath.c_str()); - if (Result != 0xFFFFFFFF) { + bool IsHidden = false; + DWORD Result = GetFileAttributesA(fullpath.c_str()); + if (Result != 0xFFFFFFFF) + { IsHidden = !!(Result & FILE_ATTRIBUTE_HIDDEN); } return IsHidden; #else - std::filesystem::path path(fullpath); + sfs::path path(fullpath); if (path.filename().string().find(".") == 0) return true; @@ -167,35 +197,72 @@ bool CMP_IsHidden(const std::string &fullpath) { #endif } -void CMP_GetDirList(const std::string &directory, std::vector &files, std::string filter) { +using recursive_directory_iterator = sfs::recursive_directory_iterator; + +void CMP_GetAllDirFilesList(const std::string& directory, std::vector& files, std::string filter) +{ + std::string path(directory); + for (const auto& dirEntry : recursive_directory_iterator(path)) + { + if (sfs::is_regular_file(dirEntry)) + { + std::string FileNamePath = dirEntry.path().string(); + // Get the file extension if a file filter is suppiled + if (filter.length() > 0) + { + std::string FileName = CMP_GetFileName(FileNamePath); + std::string ext = CMP_GetFilePathExtension(FileName); + std::transform(ext.begin(), ext.end(), ext.begin(), ::toupper); + if (filter.find(ext) != std::string::npos) { + //std::cout << FileNamePath << std::endl; + files.push_back(FileNamePath); + } + } + else { + //std::cout << FileNamePath << std::endl; + files.push_back(FileNamePath); + } + } + } +} + +void CMP_GetDirList(const std::string& directory, std::vector& files, std::string filter) +{ #ifdef _WIN32 WIN32_FIND_DATAA data; - HANDLE hFind; - std::string path(directory); - std::string fullpath; + HANDLE hFind; + std::string path(directory); + std::string fullpath; path.append("\\*"); - if ((hFind = FindFirstFileA(path.c_str(), &data)) != INVALID_HANDLE_VALUE) { - do { + if ((hFind = FindFirstFileA(path.c_str(), &data)) != INVALID_HANDLE_VALUE) + { + do + { fullpath = directory; fullpath.append("\\"); fullpath.append(data.cFileName); - if (CMP_PathType(fullpath.c_str()) == CMP_PATH_IS_FILE) { + if (CMP_PathType(fullpath.c_str()) == CMP_PATH_IS_FILE) + { // check file attribute is not hidden - bool IsHidden = false; - DWORD Result = GetFileAttributesA(fullpath.c_str()); - if (Result != 0xFFFFFFFF) { + bool IsHidden = false; + DWORD Result = GetFileAttributesA(fullpath.c_str()); + if (Result != 0xFFFFFFFF) + { IsHidden = !!(Result & FILE_ATTRIBUTE_HIDDEN); } - if (!IsHidden) { + if (!IsHidden) + { // Get the file extension if a file filter is suppiled - if (filter.length() > 0) { + if (filter.length() > 0) + { std::string FileName(data.cFileName); std::string ext = CMP_GetFilePathExtension(FileName); std::transform(ext.begin(), ext.end(), ext.begin(), ::toupper); if (filter.find(ext) != std::string::npos) files.push_back(fullpath); - } else + } + else files.push_back(fullpath); } } @@ -203,42 +270,54 @@ void CMP_GetDirList(const std::string &directory, std::vector &file FindClose(hFind); } #else - for (const std::filesystem::directory_entry &entry : std::filesystem::directory_iterator(directory)) { + for (const sfs::directory_entry& entry : sfs::directory_iterator(directory)) + { if (CMP_IsHidden(entry.path().string())) continue; - if (filter.length() > 0) { + if (filter.length() > 0) + { std::string ext = entry.path().extension(); std::transform(ext.begin(), ext.end(), ext.begin(), ::toupper); if (filter.find(ext) != std::string::npos) files.push_back(entry.path()); - } else { + } + else + { files.push_back(entry.path()); } } #endif } -std::string CMP_GetFullPath(std::string file) { - return std::filesystem::absolute(file).string(); +std::string CMP_GetFullPath(std::string file) +{ + return sfs::absolute(file).string(); } -CMP_PATHTYPES CMP_PathType(const char *path) { - +CMP_PATHTYPES CMP_PathType(const char* path) +{ #ifdef _WIN32 DWORD attrib = GetFileAttributesA(path); - if (attrib != INVALID_FILE_ATTRIBUTES) { - if (attrib & FILE_ATTRIBUTE_DIRECTORY) { + if (attrib != INVALID_FILE_ATTRIBUTES) + { + if (attrib & FILE_ATTRIBUTE_DIRECTORY) + { return CMP_PATHTYPES::CMP_PATH_IS_DIR; - } else if (attrib & FILE_ATTRIBUTE_ARCHIVE) { + } + else if (attrib & FILE_ATTRIBUTE_ARCHIVE) + { return CMP_PATHTYPES::CMP_PATH_IS_FILE; } } #else // works only if file or dir exists! - if (std::filesystem::is_directory(path)) { + if (sfs::is_directory(path)) + { return CMP_PATHTYPES::CMP_PATH_IS_DIR; - } else if (std::filesystem::is_regular_file(path)) { + } + else if (sfs::is_regular_file(path)) + { return CMP_PATHTYPES::CMP_PATH_IS_FILE; } #endif @@ -251,19 +330,16 @@ CMP_PATHTYPES CMP_PathType(const char *path) { std::string filename = CMP_GetFileName(unkn); // files should have an extension - if (ext.length() > 0) { - if (( // we only support a limited relative path upto 2 levels up! - (basedir.compare(".") == 0) - ||(basedir.compare("..\\..") == 0) - ||(basedir.compare("../..") == 0) - ||(basedir.compare("..") == 0) - ) - && - ((ext.compare(0,1,"\\") == 0) - ||(ext.compare(0,1,"/") == 0))) + if (ext.length() > 0) + { + if (( // we only support a limited relative path upto 2 levels up! + (basedir.compare(".") == 0) || (basedir.compare("..\\..") == 0) || (basedir.compare("../..") == 0) || (basedir.compare("..") == 0)) && + ((ext.compare(0, 1, "\\") == 0) || (ext.compare(0, 1, "/") == 0))) return CMP_PATH_IS_DIR; return CMP_PATH_IS_FILE; - } else { // must be a folder or file with no extension + } + else + { // must be a folder or file with no extension if ((basedir.length() > 0)) return CMP_PATH_IS_DIR; if (filename.length() > 0) @@ -272,4 +348,3 @@ CMP_PATHTYPES CMP_PathType(const char *path) { return CMP_PATHTYPES::CMP_PATH_IS_UNKNOWN; } - diff --git a/applications/_plugins/common/cmp_fileio.h b/applications/_plugins/common/cmp_fileio.h index 0860fa703..d10c04d9e 100644 --- a/applications/_plugins/common/cmp_fileio.h +++ b/applications/_plugins/common/cmp_fileio.h @@ -40,5 +40,6 @@ std::string CMP_GetBaseDir(const std::string& srcfileDirpath); std::string CMP_GetFileName(const std::string& srcfileNamepath); CMP_PATHTYPES CMP_PathType(const char *path); void CMP_GetDirList(const std::string& directory, std::vector& files, std::string filter); -std::string CMP_GetFullPath(std::string file); +void CMP_GetAllDirFilesList(const std::string& directory, std::vector& files, std::string filter); +std::string CMP_GetFullPath(std::string file); #endif diff --git a/applications/_plugins/common/gltf/cmakelists.txt b/applications/_plugins/common/gltf/cmakelists.txt index a0c062699..71622e91f 100644 --- a/applications/_plugins/common/gltf/cmakelists.txt +++ b/applications/_plugins/common/gltf/cmakelists.txt @@ -1,33 +1,35 @@ set(GLTF_SRC - #Error.cpp # Windows API - gltfcommon.cpp - gltfhelpers.cpp - # misc.cpp # same as misc.cpp in top level directory. unsued and windows api. - tiny_gltf2_utils.cpp + gltfcommon.cpp + gltfhelpers.cpp + tiny_gltf2_utils.cpp + error.cpp ) -set(gltf_h - defines.h - #error.h - gltfcommon.h - gltffeatures.h - gltfhelpers.h - gltfstructures.h - # misc.h - stb_image.h - stb_image_write.h - targetver.h - tiny_gltf2.h - tiny_gltf2_utils.h +set(GLTF_H + defines.h + gltfcommon.h + gltffeatures.h + gltfhelpers.h + gltfstructures.h + stb_image.h + stb_image_write.h + tiny_gltf2.h + tiny_gltf2_utils.h + error.h ) -add_library(gltf STATIC ${GLTF_H} ${GLTF_SRC}) +add_library(CMP_GUI_Gltf STATIC ${GLTF_H} ${GLTF_SRC}) -target_include_directories(gltf PRIVATE - ../../../../cmp_compressonatorlib - ../../../../cmp_framework/common/half +target_include_directories(CMP_GUI_Gltf PRIVATE + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_meshcompressor/draco/src + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_meshoptimizer + ${PROJECT_SOURCE_DIR}/applications/_plugins/common + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/json + ${PROJECT_SOURCE_DIR}/../common/lib/ext/glm ${draco_INCLUDE_DIRS} - ../../../_libs/cmp_meshoptimizer - ../json/ - ../ ) + +set_target_properties(CMP_GUI_Gltf PROPERTIES FOLDER "Libs") + diff --git a/applications/_plugins/common/gltf/misc.h b/applications/_plugins/common/gltf/misc.h index 4b179d605..e7a6fbdb7 100644 --- a/applications/_plugins/common/gltf/misc.h +++ b/applications/_plugins/common/gltf/misc.h @@ -20,7 +20,7 @@ #pragma once -#include +#include double MillisecondsNow(); diff --git a/applications/_plugins/common/mipstoqimage.cpp b/applications/_plugins/common/mipstoqimage.cpp index 1d555896f..9568256c3 100644 --- a/applications/_plugins/common/mipstoqimage.cpp +++ b/applications/_plugins/common/mipstoqimage.cpp @@ -20,11 +20,13 @@ #include "mipstoqimage.h" #ifndef USE_QT_IMAGELOAD -int QImage2MIPS(QImage*, CMIPS*, MipSet*) { - return 0; // no-op +int QImage2MIPS(QImage*, CMIPS*, MipSet*) +{ + return 0; // no-op } -QImage* MIPS2QImage(CMIPS*, MipSet*, int, int, CMP_CompressOptions, CMP_Feedback_Proc) { - return nullptr; // no-op +QImage* MIPS2QImage(CMIPS*, MipSet*, int, int, CMP_CompressOptions, CMP_Feedback_Proc) +{ + return nullptr; // no-op } #else #include "textureio.h" @@ -39,11 +41,19 @@ QImage* MIPS2QImage(CMIPS*, MipSet*, int, int, CMP_CompressOptions, CMP_Feedback #pragma comment(lib, "Qt5Gui.lib") #endif +static float cmp_clampf(float value, float min, float max) +{ + if (value < min) return min; + if (value > max) return max; + return value; +} + /* conversion from the ILM Half * format into the normal 32 bit pixel format. Refer to http://www.openexr.com/using.html * on each steps regarding how to display your image */ -unsigned int floatToQrgba(float r, float g, float b, float a) { +unsigned int floatToQrgba(float r, float g, float b, float a) +{ // step 3) Values, which are now 1.0, are called "middle gray". // If defog and exposure are both set to 0.0, then // middle gray corresponds to a raw pixel value of 0.18. @@ -81,50 +91,61 @@ unsigned int floatToQrgba(float r, float g, float b, float a) { // the display's maximum intensity). // // Step 7) Clamp the values to [0, 255]. - return qRgba((unsigned char)(std::clamp(r * 84.66f, 0.f, 255.f)), - (unsigned char)(std::clamp(g * 84.66f, 0.f, 255.f)), - (unsigned char)(std::clamp(b * 84.66f, 0.f, 255.f)), - (unsigned char)(std::clamp(a * 84.66f, 0.f, 255.f))); + return qRgba((unsigned char)(cmp_clampf(r * 84.66f, 0.f, 255.f)), + (unsigned char)(cmp_clampf(g * 84.66f, 0.f, 255.f)), + (unsigned char)(cmp_clampf(b * 84.66f, 0.f, 255.f)), + (unsigned char)(cmp_clampf(a * 84.66f, 0.f, 255.f))); } -QImage::Format MipFormat2QFormat(MipSet *mipset) { +QImage::Format MipFormat2QFormat(MipSet* mipset) +{ QImage::Format format = QImage::Format_Invalid; if (CMP_IsCompressedFormat(mipset->m_format)) return format; - switch (mipset->m_ChannelFormat) { - case CF_8bit: { + switch (mipset->m_ChannelFormat) + { + case CF_8bit: + { format = QImage::Format_ARGB32; break; } - case CF_Float16: { + case CF_Float16: + { format = QImage::Format_ARGB32; break; } - case CF_Float32: { + case CF_Float32: + { format = QImage::Format_ARGB32; break; } - case CF_Float9995E: { + case CF_Float9995E: + { format = QImage::Format_ARGB32; break; } - case CF_Compressed: { + case CF_Compressed: + { break; } - case CF_16bit: { + case CF_16bit: + { format = QImage::Format_ARGB32; break; } - case CF_2101010: { + case CF_2101010: + { break; } - case CF_32bit: { + case CF_32bit: + { format = QImage::Format_ARGB32; break; } - default: { + default: + { break; } } @@ -132,8 +153,10 @@ QImage::Format MipFormat2QFormat(MipSet *mipset) { return format; } -int QImage2MIPS(QImage *qimage, CMIPS *m_CMips, MipSet *pMipSet) { - if (qimage == nullptr) { +int QImage2MIPS(QImage* qimage, CMIPS* m_CMips, MipSet* pMipSet) +{ + if (qimage == nullptr) + { return -1; } @@ -142,22 +165,24 @@ int QImage2MIPS(QImage *qimage, CMIPS *m_CMips, MipSet *pMipSet) { QImage::Format format = qimage->format(); // Check supported format - if (!((format == QImage::Format_ARGB32) || - (format == QImage::Format_ARGB32_Premultiplied) || + if (!( (format == QImage::Format_ARGB32) || + (format == QImage::Format_ARGB32_Premultiplied) || (format == QImage::Format_RGB32) || - (format == QImage::Format_Mono) || - (format == QImage::Format_Indexed8))) { + (format == QImage::Format_Mono) || + (format == QImage::Format_Grayscale8) || + (format == QImage::Format_Indexed8))) + { return -1; } // Set the channel formats and mip levels - pMipSet->m_ChannelFormat = CF_8bit; + pMipSet->m_ChannelFormat = CF_8bit; pMipSet->m_TextureDataType = TDT_ARGB; - pMipSet->m_dwFourCC = 0; - pMipSet->m_dwFourCC2 = 0; - pMipSet->m_TextureType = TT_2D; - pMipSet->m_format = CMP_FORMAT_ARGB_8888; - pMipSet->m_nDepth = 1; // depthsupport + pMipSet->m_dwFourCC = 0; + pMipSet->m_dwFourCC2 = 0; + pMipSet->m_TextureType = TT_2D; + pMipSet->m_format = CMP_FORMAT_ARGB_8888; + pMipSet->m_nDepth = 1; // depthsupport // Allocate default MipSet header m_CMips->AllocateMipSet(pMipSet, @@ -166,22 +191,25 @@ int QImage2MIPS(QImage *qimage, CMIPS *m_CMips, MipSet *pMipSet) { pMipSet->m_TextureType, qimage->width(), qimage->height(), - pMipSet->m_nDepth); // depthsupport, what should nDepth be set as here? + pMipSet->m_nDepth); // depthsupport, what should nDepth be set as here? // Determin buffer size and set Mip Set Levels we want to use for now - MipLevel *mipLevel = m_CMips->GetMipLevel(pMipSet, 0); + MipLevel* mipLevel = m_CMips->GetMipLevel(pMipSet, 0); pMipSet->m_nMipLevels = 1; m_CMips->AllocateMipLevelData(mipLevel, pMipSet->m_nWidth, pMipSet->m_nHeight, pMipSet->m_ChannelFormat, pMipSet->m_TextureDataType); // We have allocated a data buffer to fill get its referance - CMP_BYTE *pData = (CMP_BYTE *)(mipLevel->m_pbData); - QRgb qRGB; - int i = 0; - - if (pMipSet->m_swizzle) { - for (int y = 0; y < qimage->height(); y++) { - for (int x = 0; x < qimage->width(); x++) { - qRGB = qimage->pixel(x, y); + CMP_BYTE* pData = (CMP_BYTE*)(mipLevel->m_pbData); + QRgb qRGB; + int i = 0; + + if (pMipSet->m_swizzle) + { + for (int y = 0; y < qimage->height(); y++) + { + for (int x = 0; x < qimage->width(); x++) + { + qRGB = qimage->pixel(x, y); pData[i] = qBlue(qRGB); i++; pData[i] = qGreen(qRGB); @@ -192,11 +220,15 @@ int QImage2MIPS(QImage *qimage, CMIPS *m_CMips, MipSet *pMipSet) { i++; } } - pMipSet->m_swizzle = false; //already swizzled; reset - } else { - for (int y = 0; y < qimage->height(); y++) { - for (int x = 0; x < qimage->width(); x++) { - qRGB = qimage->pixel(x, y); + pMipSet->m_swizzle = false; //already swizzled; reset + } + else + { + for (int y = 0; y < qimage->height(); y++) + { + for (int x = 0; x < qimage->width(); x++) + { + qRGB = qimage->pixel(x, y); pData[i] = qRed(qRGB); i++; pData[i] = qGreen(qRGB); @@ -213,26 +245,33 @@ int QImage2MIPS(QImage *qimage, CMIPS *m_CMips, MipSet *pMipSet) { } //load data byte in mipset into Qimage ARGB32 format -inline float knee(double x, double f) { +inline float knee(double x, double f) +{ return float(log(x * f + 1.f) / f); } -float findKneeF(float x, float y) { +float findKneeF(float x, float y) +{ float f0 = 0; float f1 = 1.f; - while (knee(x, f1) > y) { + while (knee(x, f1) > y) + { f0 = f1; f1 = f1 * 2.f; } - for (int i = 0; i < 30; ++i) { + for (int i = 0; i < 30; ++i) + { const float f2 = (f0 + f1) / 2.f; const float y2 = knee(x, f2); - if (y2 < y) { + if (y2 < y) + { f1 = f2; - } else { + } + else + { f0 = f2; } } @@ -240,32 +279,37 @@ float findKneeF(float x, float y) { return (f0 + f1) / 2.f; } -CMP_FLOAT F16toF32(CMP_HALFSHORT f) { +CMP_FLOAT F16toF32(CMP_HALFSHORT f) +{ CMP_HALF A; A.setBits(f); return ((CMP_FLOAT)A); } -CMP_HALFSHORT F32toF16(CMP_FLOAT f) { +CMP_HALFSHORT F32toF16(CMP_FLOAT f) +{ return (half(f).bits()); } -void float2Pixel(float kl, float f, float r, float g, float b, float a, int x, int y, QImage *image, CMP_CompressOptions option) { +void float2Pixel(float kl, float f, float r, float g, float b, float a, int x, int y, QImage* image, CMP_CompressOptions option) +{ CMP_BYTE r_b, g_b, b_b, a_b; float invGamma, scale; - if (option.fInputGamma < 1.0f) { + if (option.fInputGamma < 1.0f) + { option.fInputGamma = 2.2f; } - invGamma = 1.0 / option.fInputGamma; //for gamma correction - float luminance3f = powf(2, -3.5); // always assume max intensity is 1 and 3.5f darker for scale later - scale = 255.0 * powf(luminance3f, invGamma); + invGamma = 1.0 / option.fInputGamma; //for gamma correction + float luminance3f = powf(2, -3.5); // always assume max intensity is 1 and 3.5f darker for scale later + scale = 255.0 * powf(luminance3f, invGamma); // 1) Compensate for fogging by subtracting defog // from the raw pixel values. // We assume a defog of 0 - if (option.fInputDefog > 0.0f) { + if (option.fInputDefog > 0.0f) + { r = r - option.fInputDefog; g = g - option.fInputDefog; b = b - option.fInputDefog; @@ -275,10 +319,10 @@ void float2Pixel(float kl, float f, float r, float g, float b, float a, int x, i // 2) Multiply the defogged pixel values by // 2^(exposure + 2.47393). const float exposeScale = pow(2, option.fInputExposure + 2.47393f); - r = r * exposeScale; - g = g * exposeScale; - b = b * exposeScale; - a = a * exposeScale; + r = r * exposeScale; + g = g * exposeScale; + b = b * exposeScale; + a = a * exposeScale; // 3) Values that are now 1.0 are called "middle gray". // If defog and exposure are both set to 0.0, then @@ -295,16 +339,20 @@ void float2Pixel(float kl, float f, float r, float g, float b, float a, int x, i // value 2^kneeHigh is mapped to 2^3.5. (In step 6, // this value will be mapped to the the display's // maximum intensity.) - if (r > kl) { + if (r > kl) + { r = kl + knee(r - kl, f); } - if (g > kl) { + if (g > kl) + { g = kl + knee(g - kl, f); } - if (b > kl) { + if (b > kl) + { b = kl + knee(b - kl, f); } - if (a > kl) { + if (a > kl) + { a = kl + knee(a - kl, f); } @@ -325,10 +373,10 @@ void float2Pixel(float kl, float f, float r, float g, float b, float a, int x, i b *= scale; a *= scale; - r_b = (CMP_BYTE)std::clamp(r, 0.f, 255.f); - g_b = (CMP_BYTE)std::clamp(g, 0.f, 255.f); - b_b = (CMP_BYTE)std::clamp(b, 0.f, 255.f); - a_b = (CMP_BYTE)std::clamp(a, 0.f, 255.f); + r_b = (CMP_BYTE)cmp_clampf(r, 0.f, 255.f); + g_b = (CMP_BYTE)cmp_clampf(g, 0.f, 255.f); + b_b = (CMP_BYTE)cmp_clampf(b, 0.f, 255.f); + a_b = (CMP_BYTE)cmp_clampf(a, 0.f, 255.f); image->setPixel(x, y, qRgba(r_b, g_b, b_b, a_b)); } @@ -337,20 +385,24 @@ void float2Pixel(float kl, float f, float r, float g, float b, float a, int x, i // load Exr Image Properties // -void loadExrProperties(CMIPS *m_CMips, MipSet *mipset, int level, QImage *image, CMP_CompressOptions option) { - MipLevel *mipLevel = m_CMips->GetMipLevel(mipset, level); +void loadExrProperties(CMIPS* m_CMips, MipSet* mipset, int level, QImage* image, CMP_CompressOptions option) +{ + MipLevel* mipLevel = m_CMips->GetMipLevel(mipset, level); if (mipLevel->m_pbData == NULL) return; float kl = pow(2.f, option.fInputKneeLow); - float f = findKneeF(pow(2.f, option.fInputKneeHigh) - kl, pow(2.f, 3.5f) - kl); + float f = findKneeF(pow(2.f, option.fInputKneeHigh) - kl, pow(2.f, 3.5f) - kl); - if (mipset->m_ChannelFormat == CF_Float32) { - float *data = mipLevel->m_pfData; - float r = 0, g = 0, b = 0, a = 0; + if (mipset->m_ChannelFormat == CF_Float32) + { + float* data = mipLevel->m_pfData; + float r = 0, g = 0, b = 0, a = 0; //copy pixels into image - for (int y = 0; y < mipLevel->m_nHeight; y++) { - for (int x = 0; x < mipLevel->m_nWidth; x++) { + for (int y = 0; y < mipLevel->m_nHeight; y++) + { + for (int x = 0; x < mipLevel->m_nWidth; x++) + { r = *data; data++; g = *data; @@ -365,12 +417,16 @@ void loadExrProperties(CMIPS *m_CMips, MipSet *mipset, int level, QImage *image, //if ((y % 10) == 0) // QApplication::processEvents(); } - } else if (mipset->m_ChannelFormat == CF_Float16) { - CMP_HALFSHORT *data = mipLevel->m_phfsData; - CMP_HALFSHORT r, g, b, a; + } + else if (mipset->m_ChannelFormat == CF_Float16) + { + CMP_HALFSHORT* data = mipLevel->m_phfsData; + CMP_HALFSHORT r, g, b, a; //copy pixels into image - for (int y = 0; y < mipLevel->m_nHeight; y++) { - for (int x = 0; x < mipLevel->m_nWidth; x++) { + for (int y = 0; y < mipLevel->m_nHeight; y++) + { + for (int x = 0; x < mipLevel->m_nWidth; x++) + { r = *data; data++; g = *data; @@ -385,31 +441,36 @@ void loadExrProperties(CMIPS *m_CMips, MipSet *mipset, int level, QImage *image, //if ((y % 10) == 0) // QApplication::processEvents(); } - } else if (mipset->m_ChannelFormat == CF_Float9995E) { + } + else if (mipset->m_ChannelFormat == CF_Float9995E) + { //CMP_DWORD dwSize = mipLevel->m_dwLinearSize; - CMP_DWORD *pSrc = mipLevel->m_pdwData; - float r = 0, g = 0, b = 0, a = 0; - union { - float f; + CMP_DWORD* pSrc = mipLevel->m_pdwData; + float r = 0, g = 0, b = 0, a = 0; + union + { + float f; int32_t i; } fi; float Scale = 0.0f; - for (int y = 0; y < mipLevel->m_nHeight; y++) { - for (int x = 0; x < mipLevel->m_nWidth; x++) { + for (int y = 0; y < mipLevel->m_nHeight; y++) + { + for (int x = 0; x < mipLevel->m_nWidth; x++) + { CMP_DWORD dwSrc = *pSrc++; - R9G9B9E5 pTemp; + R9G9B9E5 pTemp; pTemp.rm = (dwSrc & 0x000001ff); pTemp.gm = (dwSrc & 0x0003fe00) >> 9; pTemp.bm = (dwSrc & 0x07fc0000) >> 18; - pTemp.e = (dwSrc & 0xf8000000) >> 27; + pTemp.e = (dwSrc & 0xf8000000) >> 27; - fi.i = 0x33800000 + (pTemp.e << 23); + fi.i = 0x33800000 + (pTemp.e << 23); Scale = fi.f; - r = Scale * float(pTemp.rm); - g = Scale * float(pTemp.gm); - b = Scale * float(pTemp.bm); - a = 1.0f; + r = Scale * float(pTemp.rm); + g = Scale * float(pTemp.gm); + b = Scale * float(pTemp.bm); + a = 1.0f; float2Pixel(kl, f, r, g, b, a, x, y, image, option); } //if ((y % 10) == 0) @@ -418,175 +479,356 @@ void loadExrProperties(CMIPS *m_CMips, MipSet *mipset, int level, QImage *image, } } -//load data byte in mipset into Qimage ARGB32 format -QImage *MIPS2QImage(CMIPS *m_CMips, MipSet *tmpMipSet, int MipMaplevel, int Depthlevel, CMP_CompressOptions option, CMP_Feedback_Proc pFeedbackProc) { - if (tmpMipSet == NULL) { - QImage *image = new QImage(":/CompressonatorGUI/Images/CompressedImageError.png"); +// SNORM int ranges from -128 to 127 +CMP_BYTE CMP_SBYTE_to_UBYTE(CMP_SBYTE value) +{ + if (value < -128) + return 0; + + if (value > 127) + return (255); + + return (value + 128); +} + + //load data byte in mipset into Qimage ARGB32 format +QImage* MIPS2QImage(CMIPS* m_CMips, MipSet* tmpMipSet, int MipMaplevel, int Depthlevel, CMP_CompressOptions option, CMP_Feedback_Proc pFeedbackProc) +{ + if (tmpMipSet == NULL) + { + QImage* image = new QImage(":/compressonatorgui/images/compressedimageerror.png"); return image; } - MipLevel *mipLevel = m_CMips->GetMipLevel(tmpMipSet, MipMaplevel, Depthlevel); - if (!mipLevel) { + MipLevel* mipLevel = m_CMips->GetMipLevel(tmpMipSet, MipMaplevel, Depthlevel); + if (!mipLevel) + { return nullptr; } - QImage *image = NULL; - - if ((tmpMipSet->m_TextureDataType == TDT_ARGB) || - (tmpMipSet->m_TextureDataType == TDT_XRGB)) { + QImage* image = NULL; - if ((tmpMipSet->m_ChannelFormat == CF_Float32) || (tmpMipSet->m_ChannelFormat == CF_Float16)) { - if (tmpMipSet->m_ChannelFormat == CF_Float32) { - float *pData = mipLevel->m_pfData; + if ((tmpMipSet->m_TextureDataType == TDT_ARGB) || (tmpMipSet->m_TextureDataType == TDT_XRGB)) + { + if ((tmpMipSet->m_ChannelFormat == CF_Float32) || (tmpMipSet->m_ChannelFormat == CF_Float16)) + { + if (tmpMipSet->m_ChannelFormat == CF_Float32) + { + float* pData = mipLevel->m_pfData; if (pData == NULL) return nullptr; - } else if (tmpMipSet->m_ChannelFormat == CF_Float16) { - CMP_HALFSHORT *pData = mipLevel->m_phfsData; + } + else if (tmpMipSet->m_ChannelFormat == CF_Float16) + { + CMP_HALFSHORT* pData = mipLevel->m_phfsData; if (pData == NULL) return nullptr; - } else { - CMP_WORD *pData = mipLevel->m_pwData; + } + else + { + CMP_WORD* pData = mipLevel->m_pwData; if (pData == NULL) return nullptr; } image = new QImage(mipLevel->m_nWidth, mipLevel->m_nHeight, MipFormat2QFormat(tmpMipSet)); - if (image == NULL) { - image = new QImage(":/CompressonatorGUI/Images/OutOfMemoryError.png"); + if (image == NULL) + { + image = new QImage(":/compressonatorgui/images/outofmemoryerror.png"); return nullptr; } loadExrProperties(m_CMips, tmpMipSet, MipMaplevel, image, option); - } else if (tmpMipSet->m_ChannelFormat == CF_Float9995E) { - - float *pData = mipLevel->m_pfData; + } + else if (tmpMipSet->m_ChannelFormat == CF_Float9995E) + { + float* pData = mipLevel->m_pfData; if (pData == NULL) return nullptr; image = new QImage(mipLevel->m_nWidth, mipLevel->m_nHeight, MipFormat2QFormat(tmpMipSet)); - if (image == NULL) { - image = new QImage(":/CompressonatorGUI/Images/OutOfMemoryError.png"); + if (image == NULL) + { + image = new QImage(":/compressonatorgui/images/outofmemoryerror.png"); return nullptr; } loadExrProperties(m_CMips, tmpMipSet, MipMaplevel, image, option); - } else if (tmpMipSet->m_ChannelFormat == CF_16bit) { + } + else + if (tmpMipSet->m_ChannelFormat == CF_16bit) + { // We have allocated a data buffer to fill get its referance - CMP_WORD *pData = mipLevel->m_pwData; + CMP_WORD* pData = mipLevel->m_pwData; if (pData == NULL) return nullptr; // We dont support the conversion - if (MipFormat2QFormat(tmpMipSet) == QImage::Format_Invalid) { - PrintInfo("MipSet to Qt Format is not supported!"); + if (MipFormat2QFormat(tmpMipSet) == QImage::Format_Invalid) + { + PrintInfo("Conversion Format is not supported!"); return nullptr; } // Allocates a uninitialized buffer of specified size and format image = new QImage(mipLevel->m_nWidth, mipLevel->m_nHeight, MipFormat2QFormat(tmpMipSet)); - if (image == NULL) { - image = new QImage(":/CompressonatorGUI/Images/OutOfMemoryError.png"); + if (image == NULL) + { + image = new QImage(":/compressonatorgui/images/outofmemoryerror.png"); return nullptr; } //QImageFormatInfo(image); - bool isRGBA = (tmpMipSet->m_TextureDataType == TDT_ARGB) ? true : false; + bool isRGBA = (tmpMipSet->m_TextureDataType == TDT_ARGB) ? true : false; bool isFixedAlpha = false; // BC4 only Red channel is valid. All other channels are set equal to that channels value // Compressonator decoder also set Alpha to the red channel value! // The Alpha should be set to 255 if viewing in GUI! - if (tmpMipSet->m_isDeCompressed == CMP_FORMAT_ATI1N) { + if (tmpMipSet->m_isDeCompressed == CMP_FORMAT_ATI1N) + { isFixedAlpha = true; } // Initialize the buffer CMP_BYTE R, G, B, A; - int i = 0; - for (int y = 0; y < mipLevel->m_nHeight; y++) { - for (int x = 0; x < mipLevel->m_nWidth; x++) { + int i = 0; + for (int y = 0; y < mipLevel->m_nHeight; y++) + { + for (int x = 0; x < mipLevel->m_nWidth; x++) + { R = pData[i] / 257; i++; G = pData[i] / 257; i++; B = pData[i] / 257; i++; - if (isRGBA) { + if (isRGBA) + { if (isFixedAlpha) A = 255; else A = pData[i] / 257; i++; - } else + } + else A = 255; image->setPixel(x, y, qRgba(R, G, B, A)); } - if (pFeedbackProc) { + if (pFeedbackProc) + { float fProgress = 100.f * (y * mipLevel->m_nWidth) / (mipLevel->m_nWidth * mipLevel->m_nHeight); if (pFeedbackProc(fProgress, NULL, NULL)) return NULL; } } - } else { + } + else + { // CF_8bit // We have allocated a data buffer to fill get its referance - CMP_BYTE *pData = mipLevel->m_pbData; - if (pData == NULL) + + if (mipLevel->m_pbData == NULL) return nullptr; // We dont support the conversion - if (MipFormat2QFormat(tmpMipSet) == QImage::Format_Invalid) { - PrintInfo("MipSet to Qt Format is not supported!"); + if (MipFormat2QFormat(tmpMipSet) == QImage::Format_Invalid) + { + PrintInfo("Mipset to Qt format is not supported!\n"); return nullptr; } // Allocates a uninitialized buffer of specified size and format image = new QImage(mipLevel->m_nWidth, mipLevel->m_nHeight, MipFormat2QFormat(tmpMipSet)); - if (image == NULL) { - image = new QImage(":/CompressonatorGUI/Images/OutOfMemoryError.png"); + if (image == NULL) + { + image = new QImage(":/compressonatorgui/images/outofmemoryerror.png"); return nullptr; } //QImageFormatInfo(image); - bool isRGBA = (tmpMipSet->m_TextureDataType == TDT_ARGB) ? true : false; + bool isRGBA = (tmpMipSet->m_TextureDataType == TDT_ARGB) ? true : false; bool isFixedAlpha = false; + CMP_BYTE channels; + // BC4 only Red channel is valid. All other channels are set equal to that channels value // Compressonator decoder also set Alpha to the red channel value! // The Alpha should be set to 255 if viewing in GUI! - if (tmpMipSet->m_isDeCompressed == CMP_FORMAT_ATI1N) { + if ((tmpMipSet->m_isDeCompressed == CMP_FORMAT_ATI1N) || + (tmpMipSet->m_isDeCompressed == CMP_FORMAT_BC4) || + (tmpMipSet->m_isDeCompressed == CMP_FORMAT_BC4_S)) + { + isFixedAlpha = true; + channels = 0b0001; // red only + } + else + if ((tmpMipSet->m_isDeCompressed == CMP_FORMAT_ATI2N) || + (tmpMipSet->m_isDeCompressed == CMP_FORMAT_BC5) || + (tmpMipSet->m_isDeCompressed == CMP_FORMAT_BC5_S)) + { + isFixedAlpha = true; + channels = 0b0011; // red + green only + } + else + channels = 0b1111; + + if ((tmpMipSet->m_TextureDataType == TDT_XRGB) || (tmpMipSet->m_TextureDataType == TDT_RGB)) + { + isRGBA = true; isFixedAlpha = true; + channels = 0b0111; + } + + // Initialize the buffer + if (tmpMipSet->m_format == CMP_FORMAT_RGBA_8888_S) + { + CMP_SBYTE* pData = mipLevel->m_psbData; + CMP_SBYTE R, G, B, A; + CMP_FLOAT nR, nG, nB, nA; + CMP_BYTE bR, bG, bB, bA; + int i = 0; + for (int y = 0; y < mipLevel->m_nHeight; y++) + { + for (int x = 0; x < mipLevel->m_nWidth; x++) + { + R = pData[i]; + i++; + G = pData[i]; + i++; + B = pData[i]; + i++; + + if (isRGBA) + { + if (isFixedAlpha) + A = 127; + else + A = pData[i]; + i++; + } + else + A = 127; + + // Normalize signed int + nR = R / 127.0f; + nG = G / 127.0f; + nB = B / 127.0f; + nA = A / 127.0f; + + // Covert from SNORM -> UINT + bR = ((nR * 0.5) + 0.5) * 255.0f; + bG = ((nG * 0.5) + 0.5) * 255.0f; + bB = ((nB * 0.5) + 0.5) * 255.0f; + bA = 255; + + //CMP_SBYTE_to_UBYTE(A); + image->setPixel(x, y, qRgba(bR, bG, bB, 255)); + } + if (pFeedbackProc) + { + float fProgress = 100.f * (y * mipLevel->m_nWidth) / (mipLevel->m_nWidth * mipLevel->m_nHeight); + if (pFeedbackProc(fProgress, NULL, NULL)) + return NULL; + } + } + } + else + { + CMP_BYTE* pData = mipLevel->m_pbData; + CMP_BYTE R, G, B, A; + int i = 0; + for (int y = 0; y < mipLevel->m_nHeight; y++) + { + for (int x = 0; x < mipLevel->m_nWidth; x++) + { + R = pData[i]; + i++; + G = pData[i]; + i++; + B = pData[i]; + i++; + if (isRGBA) + { + if (isFixedAlpha) + A = 255; + else + A = pData[i]; + i++; + } + else + A = 255; + image->setPixel(x, y, qRgba(R, G, B, A)); + } + if (pFeedbackProc) + { + float fProgress = 100.f * (y * mipLevel->m_nWidth) / (mipLevel->m_nWidth * mipLevel->m_nHeight); + if (pFeedbackProc(fProgress, NULL, NULL)) + return NULL; + } + } + } + } + } + else if (tmpMipSet->m_TextureDataType == TDT_R) + { + if (tmpMipSet->m_ChannelFormat == CF_16bit) + { + // We have allocated a data buffer to fill get its referance + CMP_WORD* pData = mipLevel->m_pwData; + if (pData == NULL) + return nullptr; + + // Allocates a uninitialized buffer of specified size and format + image = new QImage(mipLevel->m_nWidth, mipLevel->m_nHeight, MipFormat2QFormat(tmpMipSet)); + if (image == NULL) + { + image = new QImage(":/compressonatorgui/images/outofmemoryerror.png"); + return nullptr; } - if (tmpMipSet->m_TextureDataType == TDT_XRGB) { - isRGBA = true; + //QImageFormatInfo(image); + + bool isRGBA = (tmpMipSet->m_TextureDataType == TDT_ARGB) ? true : false; + bool isFixedAlpha = false; + + // BC4 only Red channel is valid. All other channels are set equal to that channels value + // Compressonator decoder also set Alpha to the red channel value! + // The Alpha should be set to 255 if viewing in GUI! + if (tmpMipSet->m_isDeCompressed == CMP_FORMAT_ATI1N) + { isFixedAlpha = true; } // Initialize the buffer CMP_BYTE R, G, B, A; - int i = 0; - for (int y = 0; y < mipLevel->m_nHeight; y++) { - for (int x = 0; x < mipLevel->m_nWidth; x++) { - R = pData[i]; + int i = 0; + for (int y = 0; y < mipLevel->m_nHeight; y++) + { + for (int x = 0; x < mipLevel->m_nWidth; x++) + { + R = pData[i] / 257; i++; - G = pData[i]; + G = pData[i] / 257; i++; - B = pData[i]; + B = pData[i] / 257; i++; - if (isRGBA) { + if (isRGBA) + { if (isFixedAlpha) A = 255; else - A = pData[i]; + A = pData[i] / 257; i++; - } else + } + else A = 255; image->setPixel(x, y, qRgba(R, G, B, A)); } - if (pFeedbackProc) { + if (pFeedbackProc) + { float fProgress = 100.f * (y * mipLevel->m_nWidth) / (mipLevel->m_nWidth * mipLevel->m_nHeight); if (pFeedbackProc(fProgress, NULL, NULL)) return NULL; @@ -594,7 +836,6 @@ QImage *MIPS2QImage(CMIPS *m_CMips, MipSet *tmpMipSet, int MipMaplevel, int Dept } } } - return image; } #endif diff --git a/applications/_plugins/common/mipstoqimage.h b/applications/_plugins/common/mipstoqimage.h index 7ca03fc89..668a981fd 100644 --- a/applications/_plugins/common/mipstoqimage.h +++ b/applications/_plugins/common/mipstoqimage.h @@ -28,6 +28,6 @@ class QImage; int QImage2MIPS(QImage *qimage, CMIPS *m_CMips, MipSet *pMipSet); QImage* MIPS2QImage(CMIPS* m_CMips, MipSet* tmpMipSet, int MipMaplevel, int Depthlevel, CMP_CompressOptions option, CMP_Feedback_Proc pFeedbackProc = nullptr); - +void loadExrProperties(CMIPS* m_CMips, MipSet* mipset, int level, QImage* image, CMP_CompressOptions option); #endif diff --git a/applications/_plugins/common/misc.cpp b/applications/_plugins/common/misc.cpp index 0f9b7187b..195e5f5a0 100644 --- a/applications/_plugins/common/misc.cpp +++ b/applications/_plugins/common/misc.cpp @@ -41,7 +41,7 @@ double MillisecondsNow() { return milliseconds; #else std::chrono::duration diff = std::chrono::steady_clock::now() - misc_start; - return diff.count() + return diff.count(); #endif } diff --git a/applications/_plugins/common/pluginmanager.cpp b/applications/_plugins/common/pluginmanager.cpp index ff6b2f178..41cee484b 100644 --- a/applications/_plugins/common/pluginmanager.cpp +++ b/applications/_plugins/common/pluginmanager.cpp @@ -33,9 +33,16 @@ #define USE_NewLoader #endif -#include #include +#ifdef _CMP_CPP17_ // Build code using std::c++17 +#include +namespace sfs = std::filesystem; +#else +#include +namespace sfs = std::experimental::filesystem; +#endif + static bool CMP_FileExists(const std::string &abs_filename) { #ifdef _WIN32 bool ret = false; @@ -52,7 +59,7 @@ static bool CMP_FileExists(const std::string &abs_filename) { return ret; #else - return std::filesystem::exists(abs_filename); + return sfs::exists(abs_filename); #endif } @@ -61,6 +68,7 @@ static bool CMP_FileExists(const std::string &abs_filename) { #pragma comment(lib, "imagehlp.lib") bool GetDLLFileExports(LPCSTR szFileName, std::vector& names) { + //printf("%s\n",__FUNCTION__); _LOADED_IMAGE LoadedImage; if (!MapAndLoad(szFileName, NULL, &LoadedImage, TRUE, TRUE)) @@ -173,16 +181,22 @@ bool GetDLLFileExports(LPCSTR szFileName, std::vector& names) { PluginManager::PluginManager() { + //printf("%s\n", __FUNCTION__); + m_pluginlistset = false; } PluginManager::~PluginManager() { + //printf("%s\n", __FUNCTION__); + clearPluginList(); } void PluginManager::registerStaticPlugin(char *pluginType, char *pluginName, void * makePlugin) { + //printf("%s\n", __FUNCTION__); + PluginDetails * curPlugin = new PluginDetails(); curPlugin->funcHandle = reinterpret_cast(makePlugin); curPlugin->isStatic = true; @@ -205,6 +219,8 @@ void PluginManager::registerStaticPlugin(char *pluginType, char *pluginName, cha void PluginManager::getPluginDetails(PluginDetails *curPlugin) { + //printf("%s\n", __FUNCTION__); + #ifdef _WIN32 HINSTANCE dllHandle; @@ -236,6 +252,8 @@ void PluginManager::getPluginDetails(PluginDetails *curPlugin) { } void PluginManager::clearPluginList() { + //printf("%s\n", __FUNCTION__); + for (unsigned int i = 0; i < pluginRegister.size(); i++) { delete pluginRegister.at(i); pluginRegister.at(i) = NULL; @@ -244,6 +262,8 @@ void PluginManager::clearPluginList() { } void PluginManager::getPluginList(char * SubFolderName, bool append) { + //printf("%s\n", __FUNCTION__); + // Check for prior setting, if set clear for new one if (m_pluginlistset) { if (!append) @@ -364,6 +384,8 @@ void PluginManager::getPluginList(char * SubFolderName, bool append) { snprintf(fname, MAX_PATH,"%s\\%s",dirPath,fd.cFileName); #ifdef USE_NewLoader + + //printf("GetDLL File exports %s\n", fd.cFileName); // Valid only for Windows need one for Linux names.clear(); GetDLLFileExports(fname, names); @@ -478,7 +500,7 @@ void *PluginManager::GetPlugin(char *type, const char *name) { PluginDetails *pPlugin = pluginRegister.at(i); if (!pPlugin->isRegistered) getPluginDetails(pPlugin); - + //printf("%2d getPlugin(Type %s name %s) == [%s,%s] \n", i, getPluginType(i), getPluginName(i), type, name); if ( (strcmp(getPluginType(i),type) == 0) && (strcmp(getPluginName(i),name) == 0)) { return ( (void *)makeNewPluginInstance(i) ); diff --git a/applications/_plugins/common/qtimgui/cmakelists.txt b/applications/_plugins/common/qtimgui/cmakelists.txt index deee4789b..74dd85b48 100644 --- a/applications/_plugins/common/qtimgui/cmakelists.txt +++ b/applications/_plugins/common/qtimgui/cmakelists.txt @@ -1,50 +1,30 @@ -# ======================================================================= - -add_library(Plugin_Common_QtImgui_DX12 INTERFACE) - -target_sources(Plugin_Common_QtImgui_DX12 INTERFACE - - imgui_dx12.h - imgui_dx12renderer.cpp - imgui_dx12renderer.h -) +add_library(CMP_Imgui) -target_link_libraries(Plugin_Common_QtImgui_DX12 INTERFACE - ExtQt5 +file(GLOB_RECURSE IMGUI_SRCS + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/imgui/*.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/imgui/*.h ) -target_include_directories(Plugin_Common_QtImgui_DX12 INTERFACE - ./ +file(GLOB_RECURSE QTIMGUI_SRCS ) -#set_target_properties(Plugin_Common_QtImgui PROPERTIES FOLDER ${FOLDER_NAME}) - -# ======================================================================= - -add_library(Plugin_Common_QtImgui_OpenGL INTERFACE) - -target_sources(Plugin_Common_QtImgui_OpenGL INTERFACE +target_sources(CMP_Imgui PRIVATE + ${IMGUI_SRCS} + ) - imgui_opengl.h - imgui_openglrenderer.cpp - imgui_openglrenderer.h - # demowindow.cpp - # demowindow.h -) +target_include_directories(CMP_Imgui + PRIVATE + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib + ${PROJECT_SOURCE_DIR}/applications/_plugins/common + ) -target_include_directories(Plugin_Common_QtImgui_OpenGL INTERFACE - ./ +target_link_libraries(CMP_Imgui + PRIVATE + ExtQt5 ) -#set_target_properties(Plugin_Common_QtImgui PROPERTIES FOLDER ${FOLDER_NAME}) - +set_target_properties(CMP_Imgui PROPERTIES + FOLDER "Libs" + ) # ======================================================================= -add_library(Plugin_Common_QtImgui INTERFACE) - -target_link_libraries(Plugin_Common_QtImgui INTERFACE - Plugin_Common_QtImgui_DX12 - Plugin_Common_QtImgui_OpenGL -) - -#set_target_properties(Plugin_Common_QtImgui PROPERTIES FOLDER ${FOLDER_NAME}) diff --git a/applications/_plugins/common/qtimgui/imgui_dx12renderer.h b/applications/_plugins/common/qtimgui/imgui_dx12renderer.h index 65a0aadff..434fe934d 100644 --- a/applications/_plugins/common/qtimgui/imgui_dx12renderer.h +++ b/applications/_plugins/common/qtimgui/imgui_dx12renderer.h @@ -27,8 +27,7 @@ #include "resourceviewheapsdx12.h" #include "uploadheapdx12.h" -#include "Error.h" -#include "Defines.h" +#include "defines.h" #include diff --git a/applications/_plugins/common/texture.h b/applications/_plugins/common/texture.h index 79b8f8d9f..b34098809 100644 --- a/applications/_plugins/common/texture.h +++ b/applications/_plugins/common/texture.h @@ -88,12 +88,17 @@ extern "C" { #define CMP_FOURCC_DXT5_BRGX CMP_MAKEFOURCC('B', 'R', 'G', 'X') #define CMP_FOURCC_BC1 CMP_MAKEFOURCC('B', 'C', '1', ' ') +#define CMP_FOURCC_BC1U CMP_MAKEFOURCC('B', 'C', '1', 'U') #define CMP_FOURCC_BC2 CMP_MAKEFOURCC('B', 'C', '2', ' ') +#define CMP_FOURCC_BC2U CMP_MAKEFOURCC('B', 'C', '2', 'U') #define CMP_FOURCC_BC3 CMP_MAKEFOURCC('B', 'C', '3', ' ') +#define CMP_FOURCC_BC3U CMP_MAKEFOURCC('B', 'C', '3', 'U') #define CMP_FOURCC_BC4 CMP_MAKEFOURCC('B', 'C', '4', ' ') +#define CMP_FOURCC_BC4U CMP_MAKEFOURCC('B', 'C', '4', 'U') #define CMP_FOURCC_BC4S CMP_MAKEFOURCC('B', 'C', '4', 'S') #define CMP_FOURCC_BC4U CMP_MAKEFOURCC('B', 'C', '4', 'U') #define CMP_FOURCC_BC5 CMP_MAKEFOURCC('B', 'C', '5', ' ') +#define CMP_FOURCC_BC5U CMP_MAKEFOURCC('B', 'C', '5', 'U') #define CMP_FOURCC_BC5S CMP_MAKEFOURCC('B', 'C', '5', 'S') #define CMP_FOURCC_DX10 CMP_MAKEFOURCC('D', 'X', '1', '0') diff --git a/applications/_plugins/common/textureio.cpp b/applications/_plugins/common/textureio.cpp index e28d6ed35..cbb564b4c 100644 --- a/applications/_plugins/common/textureio.cpp +++ b/applications/_plugins/common/textureio.cpp @@ -57,10 +57,16 @@ #include #include -#include #include #include #include +#ifdef _CMP_CPP17_ // Build code using std::c++17 +#include +namespace sfs = std::filesystem; +#else +#include +namespace sfs = std::experimental::filesystem; +#endif using namespace std; @@ -72,7 +78,7 @@ extern bool g_bAbortCompression; std::string GetFileExtension(const char *file, CMP_BOOL incDot, CMP_BOOL upperCase) { //#ifdef USE_BOOST - string file_extension = std::filesystem::path(file).extension().string(); + string file_extension = sfs::path(file).extension().string(); if (upperCase) { for (char& c : file_extension) c = toupper(c); @@ -165,13 +171,13 @@ bool DeCompressionCallback(float fProgress, CMP_DWORD_PTR pUser1, CMP_DWORD_PTR static float Progress = 0; if (fProgress > Progress) Progress = fProgress; - PrintInfo("\rDeCompression progress = %2.0f", Progress); + PrintInfo("\rDeCompression progress = %3.0f", Progress); return g_bAbortCompression; } bool IsDestinationUnCompressed(const char *fname) { bool isuncompressed = true; - std::string file_extension = std::filesystem::path(fname).extension().string(); + std::string file_extension = sfs::path(fname).extension().string(); // tolower for(char& c : file_extension) @@ -179,11 +185,19 @@ bool IsDestinationUnCompressed(const char *fname) { if (file_extension.compare(".dds") == 0) { isuncompressed = false; - } else if (file_extension.compare(".astc") == 0) { + } + else if (file_extension.compare(".astc") == 0) { + isuncompressed = false; + } + else if (file_extension.compare(".ktx") == 0) { isuncompressed = false; - } else if (file_extension.compare(".ktx") == 0) { + } + else if (file_extension.compare(".ktx2") == 0) + { isuncompressed = false; - } else if (file_extension.compare(".raw") == 0) { + } + else if (file_extension.compare(".raw") == 0) + { isuncompressed = false; } else if (file_extension.compare(".basis") == 0) { isuncompressed = false; @@ -198,7 +212,7 @@ bool IsDestinationUnCompressed(const char *fname) { } CMP_FORMAT FormatByFileExtension(const char *fname, MipSet *pMipSet) { - std::string file_extension = std::filesystem::path(fname).extension().string(); + std::string file_extension = sfs::path(fname).extension().string(); // To upper std::transform(file_extension.begin(), file_extension.end(), file_extension.begin(), @@ -217,87 +231,54 @@ CMP_FORMAT FormatByFileExtension(const char *fname, MipSet *pMipSet) { CMP_FORMAT GetFormat(CMP_DWORD dwFourCC) { switch(dwFourCC) { - case CMP_FOURCC_ATI1N: - return CMP_FORMAT_ATI1N; - case CMP_FOURCC_ATI2N: - return CMP_FORMAT_ATI2N; - case CMP_FOURCC_ATI2N_XY: - return CMP_FORMAT_ATI2N_XY; - case CMP_FOURCC_ATI2N_DXT5: - return CMP_FORMAT_ATI2N_DXT5; - case CMP_FOURCC_DXT1: - return CMP_FORMAT_DXT1; - case CMP_FOURCC_DXT3: - return CMP_FORMAT_DXT3; - case CMP_FOURCC_DXT5: - return CMP_FORMAT_DXT5; - case CMP_FOURCC_DXT5_xGBR: - return CMP_FORMAT_DXT5_xGBR; - case CMP_FOURCC_DXT5_RxBG: - return CMP_FORMAT_DXT5_RxBG; - case CMP_FOURCC_DXT5_RBxG: - return CMP_FORMAT_DXT5_RBxG; - case CMP_FOURCC_DXT5_xRBG: - return CMP_FORMAT_DXT5_xRBG; - case CMP_FOURCC_DXT5_RGxB: - return CMP_FORMAT_DXT5_RGxB; - case CMP_FOURCC_DXT5_xGxR: - return CMP_FORMAT_DXT5_xGxR; + case CMP_FOURCC_ATI1N: return CMP_FORMAT_ATI1N; + case CMP_FOURCC_ATI2N: return CMP_FORMAT_ATI2N; + case CMP_FOURCC_ATI2N_XY: return CMP_FORMAT_ATI2N_XY; + case CMP_FOURCC_ATI2N_DXT5: return CMP_FORMAT_ATI2N_DXT5; + case CMP_FOURCC_DXT1: return CMP_FORMAT_DXT1; + case CMP_FOURCC_DXT3: return CMP_FORMAT_DXT3; + case CMP_FOURCC_DXT5: return CMP_FORMAT_DXT5; + case CMP_FOURCC_DXT5_xGBR: return CMP_FORMAT_DXT5_xGBR; + case CMP_FOURCC_DXT5_RxBG: return CMP_FORMAT_DXT5_RxBG; + case CMP_FOURCC_DXT5_RBxG: return CMP_FORMAT_DXT5_RBxG; + case CMP_FOURCC_DXT5_xRBG: return CMP_FORMAT_DXT5_xRBG; + case CMP_FOURCC_DXT5_RGxB: return CMP_FORMAT_DXT5_RGxB; + case CMP_FOURCC_DXT5_xGxR: return CMP_FORMAT_DXT5_xGxR; // Deprecated but still supported for decompression // Some definition are not valid FOURCC values nut are used as Custom formats // so that DDS files can be used for storage - case CMP_FOURCC_DXT5_GXRB: - return CMP_FORMAT_DXT5_xRBG; - case CMP_FOURCC_DXT5_GRXB: - return CMP_FORMAT_DXT5_RxBG; - case CMP_FOURCC_DXT5_RXGB: - return CMP_FORMAT_DXT5_xGBR; - case CMP_FOURCC_DXT5_BRGX: - return CMP_FORMAT_DXT5_RGxB; - case CMP_FOURCC_BC4S: - return CMP_FORMAT_ATI1N; - case CMP_FOURCC_BC4U: - return CMP_FORMAT_ATI1N; - case CMP_FOURCC_BC5S: - return CMP_FORMAT_ATI2N_XY; - case CMP_FOURCC_ATC_RGB: - return CMP_FORMAT_ATC_RGB; - case CMP_FOURCC_ATC_RGBA_EXPLICIT: - return CMP_FORMAT_ATC_RGBA_Explicit; - case CMP_FOURCC_ATC_RGBA_INTERP: - return CMP_FORMAT_ATC_RGBA_Interpolated; - case CMP_FOURCC_ETC_RGB: - return CMP_FORMAT_ETC_RGB; - case CMP_FOURCC_ETC2_RGB: - return CMP_FORMAT_ETC2_RGB; - case CMP_FOURCC_ETC2_SRGB: - return CMP_FORMAT_ETC2_SRGB; - case CMP_FOURCC_ETC2_RGBA: - return CMP_FORMAT_ETC2_RGBA; - case CMP_FOURCC_ETC2_RGBA1: - return CMP_FORMAT_ETC2_RGBA1; - case CMP_FOURCC_ETC2_SRGBA: - return CMP_FORMAT_ETC2_SRGBA; - case CMP_FOURCC_ETC2_SRGBA1: - return CMP_FORMAT_ETC2_SRGBA1; - case CMP_FOURCC_BC6H: - return CMP_FORMAT_BC6H; - case CMP_FOURCC_BC7: - return CMP_FORMAT_BC7; - case CMP_FOURCC_ASTC: - return CMP_FORMAT_ASTC; + case CMP_FOURCC_DXT5_GXRB: return CMP_FORMAT_DXT5_xRBG; + case CMP_FOURCC_DXT5_GRXB: return CMP_FORMAT_DXT5_RxBG; + case CMP_FOURCC_DXT5_RXGB: return CMP_FORMAT_DXT5_xGBR; + case CMP_FOURCC_DXT5_BRGX: return CMP_FORMAT_DXT5_RGxB; + + case CMP_FOURCC_ATC_RGB: return CMP_FORMAT_ATC_RGB; + case CMP_FOURCC_ATC_RGBA_EXPLICIT: return CMP_FORMAT_ATC_RGBA_Explicit; + case CMP_FOURCC_ATC_RGBA_INTERP: return CMP_FORMAT_ATC_RGBA_Interpolated; + case CMP_FOURCC_ETC_RGB: return CMP_FORMAT_ETC_RGB; + case CMP_FOURCC_ETC2_RGB: return CMP_FORMAT_ETC2_RGB; + case CMP_FOURCC_ETC2_SRGB: return CMP_FORMAT_ETC2_SRGB; + case CMP_FOURCC_ETC2_RGBA: return CMP_FORMAT_ETC2_RGBA; + case CMP_FOURCC_ETC2_RGBA1: return CMP_FORMAT_ETC2_RGBA1; + case CMP_FOURCC_ETC2_SRGBA: return CMP_FORMAT_ETC2_SRGBA; + case CMP_FOURCC_ETC2_SRGBA1: return CMP_FORMAT_ETC2_SRGBA1; + case CMP_FOURCC_BC4S: return CMP_FORMAT_BC4_S; + case CMP_FOURCC_BC4: + case CMP_FOURCC_BC4U: return CMP_FORMAT_ATI1N; + case CMP_FOURCC_BC5: return CMP_FORMAT_BC5; + case CMP_FOURCC_BC5S: return CMP_FORMAT_BC5_S; + case CMP_FOURCC_BC6H: return CMP_FORMAT_BC6H; + case CMP_FOURCC_BC7: return CMP_FORMAT_BC7; + case CMP_FOURCC_ASTC: return CMP_FORMAT_ASTC; #ifdef USE_APC - case CMP_FOURCC_APC: - return CMP_FORMAT_APC; + case CMP_FOURCC_APC: return CMP_FORMAT_APC; #endif #ifdef USE_GTC - case CMP_FOURCC_GTC: - return CMP_FORMAT_GTC; + case CMP_FOURCC_GTC: return CMP_FORMAT_GTC; #endif #ifdef USE_BASIS - case CMP_FOURCC_BASIS: - return CMP_FORMAT_BASIS; + case CMP_FOURCC_BASIS: return CMP_FORMAT_BASIS; #endif default: return CMP_FORMAT_Unknown; @@ -405,7 +386,7 @@ bool FloatFormat(CMP_FORMAT InFormat) { bool CompressedFileFormat(std::string file) { - std::string file_extension = std::filesystem::path(file).extension().string(); + std::string file_extension = sfs::path(file).extension().string(); // To upper std::transform(file_extension.begin(), file_extension.end(), file_extension.begin(), [](unsigned char c) -> unsigned char { return toupper(c); }); @@ -430,12 +411,12 @@ MipSet* DecompressMIPSet(MipSet *MipSetIn, CMP_GPUDecode decodeWith, Config *con return NULL; } if (MipSetIn->m_format == CMP_FORMAT_ASTC && !(configSetting->useCPU) && decodeWith == CMP_GPUDecode::GPUDecode_DIRECTX) { - configSetting->errMessage = "ASTC format does not supported by DirectX API. Please view ASTC compressed images using other options (CPU or Vulkan) (under Settings->Application Options)."; - PrintInfo("Decompress Error: ASTC format does not supported by DirectX API. Please view ASTC compressed images using CPU or GPU_Vulkan (under Settings->Application Options).\n"); + configSetting->errMessage = "ASTC format does not supported by DirectX API. Please view ASTC compressed images using other options (CPU) (under Settings->Application Options)."; + PrintInfo("Decompress Error: ASTC format does not supported by DirectX API. Please view ASTC compressed images using CPU (under Settings->Application Options).\n"); return NULL; } else if (MipSetIn->m_format == CMP_FORMAT_ASTC && !(configSetting->useCPU) && decodeWith == CMP_GPUDecode::GPUDecode_OPENGL) { - configSetting->errMessage = "Decode ASTC with OpenGL is not supported. Please view ASTC compressed images using other options (CPU or Vulkan) (under Settings->Application Options)."; - PrintInfo("Decompress Error: Decode ASTC with OpenGL is not supported. Please view ASTC compressed images using CPU or GPU_Vulkan (under Settings->Application Options).\n"); + configSetting->errMessage = "Decode ASTC with OpenGL is not supported. Please view ASTC compressed images using other options (CPU) (under Settings->Application Options)."; + PrintInfo("Decompress Error: Decode ASTC with OpenGL is not supported. Please view ASTC compressed images using CPU (under Settings->Application Options).\n"); return NULL; } @@ -461,6 +442,10 @@ MipSet* DecompressMIPSet(MipSet *MipSetIn, CMP_GPUDecode decodeWith, Config *con // BMP is saved as CMP_FORMAT_ARGB_8888 // EXR is saved as CMP_FORMAT_ARGB_16F switch (MipSetIn->m_format) { + case CMP_FORMAT_BC4_S: + case CMP_FORMAT_BC5_S: + MipSetOut->m_format = CMP_FORMAT_RGBA_8888_S; + break; case CMP_FORMAT_BC6H: case CMP_FORMAT_BC6H_SF: MipSetOut->m_format = CMP_FORMAT_ARGB_16F; @@ -547,6 +532,7 @@ MipSet* DecompressMIPSet(MipSet *MipSetIn, CMP_GPUDecode decodeWith, Config *con srcTexture.transcodeFormat = MipSetIn->m_transcodeFormat; srcTexture.dwDataSize = CMP_CalculateBufferSize(&srcTexture); srcTexture.pData = pMipData; + srcTexture.pMipSet = MipSetIn; //----------------------------- // Uncompressed Destination @@ -562,6 +548,7 @@ MipSet* DecompressMIPSet(MipSet *MipSetIn, CMP_GPUDecode decodeWith, Config *con destTexture.format = MipSetOut->m_format; destTexture.dwDataSize = CMP_CalculateBufferSize(&destTexture); destTexture.pData = m_CMIPS.GetMipLevel(MipSetOut, nMipLevel, nFaceOrSlice)->m_pbData; + destTexture.pMipSet = MipSetOut; if (!silent) { if ((nMipLevel > 1) || (nFaceOrSlice > 1)) @@ -627,9 +614,18 @@ MipSet* DecompressMIPSet(MipSet *MipSetIn, CMP_GPUDecode decodeWith, Config *con } } else { #ifdef _WIN32 -#ifndef DISABLE_TESTCODE CMP_ERROR res; + CMP_FORMAT hold_destformat = destTexture.format; res = CMP_DecompressTexture(&srcTexture, &destTexture, decodeWith); + + // Did decompress adjust any formats, if so make sure MipSetOut->m_format is updated + // This typically can happen when GPU views are used and the captured framebuffer differs + // from that which was set as a destination texture format. Note all buffer sizes and channel formats + // should match when this happens. Typical case can be moving from SNORM to UNORM bytes + if (hold_destformat != destTexture.format) + MipSetOut->m_format = destTexture.format; + + if (res == CMP_ERR_UNSUPPORTED_GPU_ASTC_DECODE) { configSetting->errMessage = "Error: ASTC compressed texture is not supported by the GPU device.\n"; PrintInfo("Error: ASTC compressed texture is not supported by the GPU device.\n"); @@ -638,21 +634,20 @@ MipSet* DecompressMIPSet(MipSet *MipSetIn, CMP_GPUDecode decodeWith, Config *con MipSetOut = NULL; return NULL; } else if (res == CMP_ERR_UNABLE_TO_INIT_DECOMPRESSLIB) { - configSetting->errMessage = "Error: Failed to decompress with the API selected. Version is not supported. Stay tune for update!\n"; - PrintInfo("Error: Failed to decompress with the API selected. Note for Vulkan, only driver version up to 1.5.0 is supported by this app. Please stay tune for update! Thanks.\n"); + configSetting->errMessage = "Error: Failed to load decompress lib for this image or view.\n"; + PrintInfo("Error: Failed to decompress with the API selected.\n"); m_CMIPS.FreeMipSet(MipSetOut); delete MipSetOut; MipSetOut = NULL; return NULL; } else if (res != CMP_OK) { - configSetting->errMessage = "Decompress Failed. Texture format not supported. Please view the compressed images using other options (CPU) (under Settings->Application Options)."; + configSetting->errMessage = "Decompress Failed. Texture format not supported. Please view the compressed images using other options (CPU) (under Settings->Application Options)"; PrintInfo("Decompress Failed with Error %d\n", res); m_CMIPS.FreeMipSet(MipSetOut); delete MipSetOut; MipSetOut = NULL; return NULL; } -#endif #else PrintInfo("GPU Decompress is not supported in linux.\n"); m_CMIPS.FreeMipSet(MipSetOut); @@ -796,7 +791,6 @@ int AMDLoadMIPSTextureImage(const char *SourceFile, MipSet *MipSetIn, bool use_O int result = -1; QString filename(SourceFile); QImage *qimage = new QImage(filename); - if (qimage) { result = QImage2MIPS(qimage, g_CMIPS, MipSetIn); delete qimage; @@ -816,7 +810,7 @@ int AMDSaveMIPSTextureImage(const char *DestFile, MipSet *MipSetIn, bool use_OCV bool filesaved = false; CMIPS m_CMIPS; - std::string file_extension = std::filesystem::path(DestFile).extension().string(); + std::string file_extension = sfs::path(DestFile).extension().string(); file_extension.erase(std::remove(file_extension.begin(), file_extension.end(), '.'), file_extension.end()); for(char& c : file_extension) c = toupper(c); diff --git a/applications/_plugins/common/utilfuncs.cpp b/applications/_plugins/common/utilfuncs.cpp index 47a7a9bf8..87b332eb4 100644 --- a/applications/_plugins/common/utilfuncs.cpp +++ b/applications/_plugins/common/utilfuncs.cpp @@ -39,7 +39,14 @@ #include #endif +#ifdef _CMP_CPP17_ // Build code using std::c++17 #include +namespace sfs = std::filesystem; +#else +#include +namespace sfs = std::experimental::filesystem; +#endif + void SwizzleBytes(void *src, unsigned long numBytes) { unsigned char tmp[8]; // large enough to hold a double @@ -91,7 +98,7 @@ void getFileNameExt(const char *FilePathName, char *fnameExt, int maxbuffsize) { _splitpath_s(FilePathName, drive, _MAX_DRIVE, dir, _MAX_DIR, fname, _MAX_FNAME, ext, _MAX_EXT); snprintf(fnameExt, maxbuffsize, "%s%s", fname, ext); #else - std::string fname = std::filesystem::path(FilePathName).filename().string(); + std::string fname = sfs::path(FilePathName).filename().string(); snprintf(fnameExt, maxbuffsize, "%s", fname.c_str()); #endif } diff --git a/applications/compressonatorcli/cmakelists.txt b/applications/compressonatorcli/cmakelists.txt index 85bd0b1fc..02c88df6b 100644 --- a/applications/compressonatorcli/cmakelists.txt +++ b/applications/compressonatorcli/cmakelists.txt @@ -1,42 +1,35 @@ -message(STATUS "++++++++++++++++++ CLI link_directories ${CMAKE_CURRENT_LIST_DIR}") - -# this must be before add_executable -link_directories( - ${PROJECT_SOURCE_DIR}/../common/lib/ext/opencv/2.49/x64/VS2015/lib - ${PROJECT_SOURCE_DIR}/../common/lib/ext/zlib/zlib-1.2.10/VS2015/x64/lib - ${PROJECT_SOURCE_DIR}/../common/lib/ext/openexr/ilmbase-2.2.0/VS2015/x64/lib - ${PROJECT_SOURCE_DIR}/../common/lib/ext/openexr/openexr-2.2.0/VS2015/x64/lib - ${PROJECT_SOURCE_DIR}/../common/lib/ext/glew/1.9.0/lib/x64 - ) - - -# this must be before add_executable -if (OPTION_CMP_DIRECTX) -link_directories( - ${PROJECT_SOURCE_DIR}/../common/lib/ext/directxtex/DirectXTex-jun2020b/DirectXTex/Bin/Desktop_2017/x64 - ) -endif() - -add_executable(CompressonatorCLI-bin) +cmake_minimum_required(VERSION 3.10) if(CMP_HOST_WINDOWS) -add_dependencies(CompressonatorCLI-bin extern_ktx) + # this must be before add_executable + link_directories( + ${PROJECT_EXTERNAL_LIBDIR}/opencv/2.49/x64/VS2015/lib + ${PROJECT_EXTERNAL_LIBDIR}/zlib/zlib-1.2.10/VS2015/x64/lib + ${PROJECT_EXTERNAL_LIBDIR}/openexr/ilmbase-2.2.0/VS2015/x64/lib + ${PROJECT_EXTERNAL_LIBDIR}/openexr/openexr-2.2.0/VS2015/x64/lib + ${PROJECT_EXTERNAL_LIBDIR}/glew/1.9.0/lib/x64 + ) + + # this must be before add_executable + if (OPTION_CMP_DIRECTX) + link_directories( + ${PROJECT_EXTERNAL_LIBDIR}/directxtex/DirectXTex-jun2020b/DirectXTex/Bin/Desktop_2017/x64 + ) + endif() endif() -set_target_properties(CompressonatorCLI-bin PROPERTIES - AUTORCC ON - OUTPUT_NAME "compressonatorcli" -) +add_executable(CompressonatorCLI-bin "") -if(CMP_HOST_WINDOWS) - set_target_properties(CompressonatorCLI-bin PROPERTIES - WIN32_EXECUTABLE OFF #Build an executable with a WinMain entry point on windows. - VS_DEBUGGER_WORKING_DIRECTORY $ - ) -elseif(CMP_HOST_APPLE) - set_target_properties(CompressonatorCLI-bin PROPERTIES - LINK_FLAGS "-Wl,-F${CMAKE_OSX_SYSROOT}/System/Library/Frameworks,-v" - ) + +# allow const char* -> char* +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + target_compile_options(CompressonatorCLI-bin + PRIVATE + "-Wno-c++11-compat-deprecated-writable-strings") +elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + target_compile_options(CompressonatorCLI-bin + PRIVATE + "-Wno-write-strings") endif() target_compile_definitions(CompressonatorCLI-bin PUBLIC @@ -46,112 +39,147 @@ target_compile_definitions(CompressonatorCLI-bin PUBLIC ) file(GLOB_RECURSE RESOURCES - resources/* ) -target_sources(CompressonatorCLI-bin PRIVATE +target_sources(CompressonatorCLI-bin + PRIVATE + ${RESOURCES} + source/compressonatorcli.cpp + source/compressonatorcli_documentation.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/mipstoqimage.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/mipstoqimage.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/atiformats.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/atiformats.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/cmdline.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/cmdline.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/cmp_fileio.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/cmp_fileio.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/modeldata.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/modeldata.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/plugininterface.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/pluginmanager.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/pluginmanager.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/query_timer.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/query_timer.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/textureio.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/textureio.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/gltf/tiny_gltf2.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/gltf/tiny_gltf2_utils.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/gltf/tiny_gltf2_utils.cpp + ) + +target_include_directories(CompressonatorCLI-bin + PRIVATE + Source + ${PROJECT_SOURCE_DIR}/applications/_plugins/common + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/json + ${PROJECT_SOURCE_DIR}/applications/_plugins/cmesh/mesh_optimizer + ${PROJECT_SOURCE_DIR}/applications/_libs/gpu_decode + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_meshoptimizer + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_math + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib + ${PROJECT_SOURCE_DIR}/cmp_framework/ + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half + ${PROJECT_SOURCE_DIR}/cmp_core/shaders + ${PROJECT_SOURCE_DIR}/cmp_core/source + ${PROJECT_SOURCE_DIR}/../common/lib/ext/glm + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_meshcompressor/draco/src + ${draco_INCLUDE_DIRS} + ${OpenEXR_INCLUDE_DIRS} + ${Qt5Gui_INCLUDE_DIRS}) + +if(APPLE) +target_include_directories(CompressonatorCLI-bin + PRIVATE + /usr/local/include/OpenEXR/) +endif() - ${RESOURCES} - source/compressonatorcli.cpp - source/compressonatorcli_documentation.h -) +# Qt5 include path - users install required +set(CMAKE_POSITION_INDEPENDENT_CODE ON) -target_include_directories(CompressonatorCLI-bin PRIVATE - ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib - ./source -) - -if (OPTION_CMP_DIRECTX) -target_link_libraries(CompressonatorCLI-bin - PRIVATE - # Static Libs - ExtDirectXTex -) +if (UNIX) +target_compile_definitions(CompressonatorCLI-bin PRIVATE _LINUX) endif() -target_link_libraries(CompressonatorCLI-bin - PRIVATE - # Static Libs - ExtKTX - ExtGLEW - CMP_FileIO - CMP_Core - CMP_FrameworkLIB - CMP_MeshCompressor - CMP_MeshOptimizer - CompressonatorLIB - GPU_Decode - # Static "plugin" Libs - Plugin_C3DModel_Loaders - Plugin_CCMP_Encode - Plugin_CCMP_SDK - Plugin_CGPUDecode - Plugin_CmdLine - Plugin_CMesh_Mesh_Optimizer - Plugin_Common_QueryPerformance - Plugin_GLTF - # Dynamic plugins - Plugin_CImage - Plugin_CAnalysis -) - +set (CMP_LIBS "") +list(APPEND CMP_LIBS CMP_Compressonator + CMP_Framework + CMP_MeshCompressor + + Image_Analysis + Image_ASTC + Image_EXR + Image_KTX + Image_TGA + + ) + +if (UNIX) + if(NOT APPLE) + list(APPEND CMP_LIBS Threads::Threads + ${OpenEXR_LIBRARIES}) + else() + list(APPEND CMP_LIBS "/usr/lib/libz.dylib") + list(APPEND CMP_LIBS "/usr/local/lib/libHalf.dylib") + list(APPEND CMP_LIBS "/usr/local/lib/libImath.dylib") + list(APPEND CMP_LIBS "/usr/local/lib/libIlmThread.dylib") + list(APPEND CMP_LIBS "/usr/local/lib/libIex.dylib") + list(APPEND CMP_LIBS "/usr/local/lib/libIlmImf.dylib") + endif() +else() + list(APPEND + CMP_LIBS + CMP_GpuDecode + Image_KTX2 + ExtQt5 + ExtQt5Widgets) +endif() -target_link_libraries(CompressonatorCLI-bin - PRIVATE - # Static Libs - ExtKTX - ExtGLEW - CMP_FileIO - CMP_Core - CMP_FrameworkLIB - CMP_MeshCompressor - CMP_MeshOptimizer - CompressonatorLIB - GPU_Decode - # Static "plugin" Libs - Plugin_C3DModel_Loaders - Plugin_CCMP_Encode - Plugin_CCMP_SDK - Plugin_CGPUDecode - Plugin_CmdLine - Plugin_CMesh_Mesh_Optimizer - Plugin_Common_QueryPerformance - Plugin_GLTF - # Dynamic plugins - Plugin_CImage - Plugin_CAnalysis - - ExtQt5 - ExtQt5Widgets -) +list(APPEND CMP_LIBS ${OpenCV_LIBRARIES} + ${draco_LIBRARIES}) -if (OPTION_BUILD_TESTING) - add_subdirectory(tests) -endif() +target_link_libraries(CompressonatorCLI-bin + ${CMP_LIBS} + Qt5::Gui + ) -include(copyfiles.cmake) +add_dependencies(CompressonatorCLI-bin + CMP_Compressonator + CMP_Framework + CMP_MeshCompressor -set_target_properties(CompressonatorCLI-bin PROPERTIES FOLDER ${FOLDER_NAME}) + Image_Analysis + Image_ASTC + Image_EXR + Image_KTX + Image_TGA + ) -if (CMAKE_GENERATOR STREQUAL "Unix Makefiles") - install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - DESTINATION . - USE_SOURCE_PERMISSIONS - PATTERN "cmake_install.cmake" EXCLUDE - PATTERN "CMakeFiles*" EXCLUDE - PATTERN "ctesttestfile.cmake" EXCLUDE - PATTERN "makefile" EXCLUDE - PATTERN "tests*" EXCLUDE - ) -elseif (CMAKE_GENERATOR STREQUAL "Visual Studio 16 2019") - install(DIRECTORY "$" - DESTINATION "./compressonatorcli" - USE_SOURCE_PERMISSIONS +if(CMP_HOST_WINDOWS) + include(copyfiles.cmake) + set_target_properties(CompressonatorCLI-bin PROPERTIES + WIN32_EXECUTABLE OFF #Build an executable with a WinMain entry point on windows. + VS_DEBUGGER_WORKING_DIRECTORY $ ) -elseif (CMAKE_GENERATOR STREQUAL "Xcode") - install(DIRECTORY "$" - DESTINATION "./compressonatorcli" - USE_SOURCE_PERMISSIONS +elseif(CMP_HOST_APPLE) + set_target_properties(CompressonatorCLI-bin PROPERTIES + LINK_FLAGS "-Wl,-F${CMAKE_OSX_SYSROOT}/System/Library/Frameworks,-v" ) endif() + +if(CMP_HOST_WINDOWS) +set_target_properties(CompressonatorCLI-bin PROPERTIES + AUTORCC ON + AUTOMOC ON + OUTPUT_NAME "compressonatorcli" + ) +else() +set_target_properties(CompressonatorCLI-bin PROPERTIES + AUTORCC ON + AUTOMOC ON + OUTPUT_NAME "compressonatorcli-bin" + ) +endif() + +# install(TARGETS CompressonatorCLI-bin) diff --git a/applications/compressonatorcli/copyfiles.cmake b/applications/compressonatorcli/copyfiles.cmake index 24759e5af..cad80e547 100644 --- a/applications/compressonatorcli/copyfiles.cmake +++ b/applications/compressonatorcli/copyfiles.cmake @@ -7,10 +7,6 @@ set(ASSETS_PATH $) set(DYLIBS_PATH $) set(PLUGINS_PATH $/plugins) -#copy glew32.dll -#get_property(ExtGLEW_BIN_PATH GLOBAL PROPERTY ExtGLEW_BIN_PATH) -cmp_gui_copy_to_output(${ExtGLEW_BIN_PATH}/glew32.dll ${ASSETS_PATH}/glew32.dll) - cmp_gui_copy_to_output(${PROJECT_SOURCE_DIR}/Applications/_Plugins/CGPUDecode/Vulkan/VK_ComputeShader/texture.vert.spv ${ASSETS_PATH}/texture.vert.spv) cmp_gui_copy_to_output(${PROJECT_SOURCE_DIR}/Applications/_Plugins/CGPUDecode/Vulkan/VK_ComputeShader/texture.frag.spv ${ASSETS_PATH}/texture.frag.spv) @@ -40,12 +36,26 @@ if (OPTION_USE_QT_IMAGELOAD) endif() endif() -# OpenCV dll -cmp_gui_copy_to_output(${PROJECT_SOURCE_DIR}/../common/lib/ext/opencv/2.49/x64/VS2015/bin/$<$:debug>$<$:release>/opencv_core249$<$:d>.dll ${ASSETS_PATH}/opencv_core249$<$:d>.dll) -cmp_gui_copy_to_output(${PROJECT_SOURCE_DIR}/../common/lib/ext/opencv/2.49/x64/VS2015/bin/$<$:debug>$<$:release>/opencv_imgproc249$<$:d>.dll ${ASSETS_PATH}/opencv_imgproc249$<$:d>.dll) -#KTX dll -cmp_gui_copy_to_output(${PROJECT_SOURCE_DIR}/../common/lib/ext/ktx/build/$<$:debug>$<$:release>/ktx.dll ${ASSETS_PATH}/ktx.dll) +if (CMP_HOST_WINDOWS) + #copy glew32.dll + #get_property(ExtGLEW_BIN_PATH GLOBAL PROPERTY ExtGLEW_BIN_PATH) + cmp_gui_copy_to_output(${ExtGLEW_BIN_PATH}/glew32.dll ${ASSETS_PATH}/glew32.dll) + + # OpenCV dll + cmp_gui_copy_to_output(${PROJECT_SOURCE_DIR}/../common/lib/ext/opencv/2.49/x64/VS2015/bin/$<$:debug>$<$:release>/opencv_core249$<$:d>.dll ${ASSETS_PATH}/opencv_core249$<$:d>.dll) + cmp_gui_copy_to_output(${PROJECT_SOURCE_DIR}/../common/lib/ext/opencv/2.49/x64/VS2015/bin/$<$:debug>$<$:release>/opencv_imgproc249$<$:d>.dll ${ASSETS_PATH}/opencv_imgproc249$<$:d>.dll) + + #KTX2 Features dll + if (OPTION_BUILD_KTX2) + cmp_gui_copy_to_output(${PROJECT_SOURCE_DIR}/../common/lib/ext/ktx/build/$<$:debug>$<$:release>/ktx.dll ${ASSETS_PATH}/ktx.dll) + else() + # Use a null dll so that installers can build + cmp_gui_copy_to_output(${PROJECT_SOURCE_DIR}/runtime/ktx.dll ${ASSETS_PATH}/ktx.dll) + endif() + +endif() # end host windows + # GPU Shaders file(GLOB_RECURSE GPUCOMPUTE_SHADERS ${PROJECT_SOURCE_DIR}/CMP_Core/shaders/*) diff --git a/applications/compressonatorcli/source/compressonatorcli.cpp b/applications/compressonatorcli/source/compressonatorcli.cpp index 10682b442..b4fa7d9ad 100644 --- a/applications/compressonatorcli/source/compressonatorcli.cpp +++ b/applications/compressonatorcli/source/compressonatorcli.cpp @@ -43,20 +43,15 @@ #include #endif -// Our Static Plugin Interfaces -#if defined(_WIN32) && !defined(NO_LEGACY_BEHAVIOR) -#pragma comment(lib, "ASTC.lib") -#pragma comment(lib, "EXR.lib") -#pragma comment(lib, "KTX2.lib") -#pragma comment(lib, "TGA.lib") -#pragma comment(lib, "IMGAnalysis.lib") -#else -#pragma comment(lib, "Plugin_CImage_ASTC.lib") -#pragma comment(lib, "Plugin_CImage_EXR.lib") -#pragma comment(lib, "Plugin_CImage_KTX.lib") -#pragma comment(lib, "Plugin_CImage_TGA.lib") -#pragma comment(lib, "Plugin_CAnalysis.lib") +// Standard App Static Plugin Interfaces for minimal support +#pragma comment(lib, "Image_ASTC.lib") +#pragma comment(lib, "Image_EXR.lib") +#pragma comment(lib, "Image_KTX.lib") +#ifdef _WIN32 +#pragma comment(lib, "Image_KTX2.lib") #endif +#pragma comment(lib, "Image_TGA.lib") +#pragma comment(lib, "Image_Analysis.lib") extern void* make_Plugin_ASTC(); extern void* make_Plugin_EXR(); @@ -64,53 +59,72 @@ extern void* make_Plugin_TGA(); extern void* make_Plugin_KTX(); extern void* make_Plugin_CAnalysis(); +#ifdef _WIN32 +extern void* make_Plugin_KTX2(); +#endif + // Setup Static Host Pluging Libs extern void CMP_RegisterHostPlugins(); -extern bool CompressionCallback(float fProgress, CMP_DWORD_PTR pUser1, CMP_DWORD_PTR pUser2); -extern void LocalPrintF(char* buff); -extern std::string DefaultDestination(std::string SourceFile, CMP_FORMAT DestFormat, std::string DestFileExt); +extern bool CompressionCallback(float fProgress, CMP_DWORD_PTR pUser1, CMP_DWORD_PTR pUser2); +extern void LocalPrintF(char* buff); +extern std::string DefaultDestination(std::string SourceFile, CMP_FORMAT DestFormat, std::string DestFileExt, CMP_BOOL useDestFormat); extern PluginManager g_pluginManager; -bool g_bAbortCompression = false; -CMIPS* g_CMIPS; // Global MIPS functions shared between app and all IMAGE plugins +bool g_bAbortCompression = false; +CMIPS* g_CMIPS; // Global MIPS functions shared between app and all IMAGE plugins -void AboutCompressonator() { +void AboutCompressonator() +{ printf("------------------------------------------------\n"); // current build release if (VERSION_MINOR_MAJOR) - printf("CompressonatorCLI V%d.%d.%d Copyright AMD 2020\n", VERSION_MAJOR_MAJOR, VERSION_MAJOR_MINOR, VERSION_MINOR_MAJOR); - else { + printf("compressonatorcli V%d.%d.%d Copyright AMD 2020\n", VERSION_MAJOR_MAJOR, VERSION_MAJOR_MINOR, VERSION_MINOR_MAJOR); + else + { // Keep track of Customer patches from last release to current // This is what is shown when you build the exe outside of the automated Build System (such as Jenkins) - printf("CompressonatorCLI V4.0.0 Copyright AMD 2020\n"); + printf("compressonatorcli V4.1.0 Copyright AMD 2020\n"); } printf("------------------------------------------------\n"); printf("\n"); } -void PrintUsage() { +void PrintUsage() +{ AboutCompressonator(); - printf("Usage: CompressonatorCLI.exe [options] SourceFile DestFile\n\n"); + printf("Usage: compressonatorcli.exe [options] SourceFile DestFile\n\n"); printf("MipMap options:\n\n"); printf("-nomipmap Turns off Mipmap generation\n"); printf("-mipsize The size in pixels used to determine\n"); printf(" how many mip levels to generate\n"); printf("-miplevels Sets Mips Level for output, range is 1 to 20\n"); printf(" (mipSize overides this option): default is 1\n"); +#ifdef _WIN32 + printf("-GenGPUMipMap When encoding with GPU this flag will enable mipmap level generation\n"); + printf(" using GPU HW. Default level is 1 unless miplevels is set\n "); + printf("-UseSRGBFrames When encoding with GPU, GL_FRAMEBUFFER_SRGB will be enabled else it will use GL_FRAMEBUFFER\n"); +#endif printf("Compression options:\n\n"); + +#ifdef CMP_ENABLE_LEGACY_OPTIONS printf("-fs Optionally specifies the source texture format to use\n"); - printf("-fd Specifies the destination texture format to use\n"); +#endif + + printf("-fd Specifies the destination texture format to use\n"); printf("-decomp If the destination file is compressed optionally\n"); printf(" decompress it\n"); printf(" to the specified file. Note the destination must\n"); printf(" be compatible \n"); printf(" with the sources format,decompress formats are typically\n"); printf(" set to ARGB_8888 or ARGB_32F\n"); - printf("-EncodeWith Compression with CPU, HPC, GPU, OCL or DXC\n"); #ifdef _WIN32 + printf("-EncodeWith Compression with CPU, HPC, GPU, OCL or DXC\n"); + printf(" GPU will use GL Compress Extensions, OCL or DXC will use CMP_Core SDK Shaders\n"); printf("-DecodeWith GPU based decompression using OpenGL, DirectX or Vulkan\n"); +#else + printf("-EncodeWith Compression with CPU, HPC\n"); #endif #ifdef USE_MESH_DRACO_EXTENSION printf("-draco Enable draco compression. (only support glTF files)\n"); @@ -125,12 +139,13 @@ void PrintUsage() { printf("-doswizzle Swizzle the source images Red and Blue channels\n"); printf("\n"); printf("The following is a list of channel formats\n"); - printf("ARGB_16 ARGB format with 16-bit fixed channels\n"); - printf("ARGB_16F ARGB format with 16-bit floating-point channels\n"); - printf("ARGB_32F ARGB format with 32-bit floating-point channels\n"); - printf("ARGB_2101010 ARGB format with 10-bit fixed channels for color\n"); + printf("ARGB_8888 format with 8-bit fixed channels\n"); + printf("ARGB_16F format with 16-bit floating-point channels\n"); + printf("ARGB_32F format with 32-bit floating-point channels\n"); +#ifdef CMP_ENABLE_TRANSCODECHANNEL_SUPPORT + printf("ARGB_16 format with 16-bit fixed channels\n"); + printf("ARGB_2101010 format with 10-bit fixed channels for color\n"); printf(" and a 2-bit fixed channel for alpha\n"); - printf("ARGB_8888 ARGB format with 8-bit fixed channels\n"); printf("R_8 Single component format with 8-bit fixed channels\n"); printf("R_16 Single component format with 16-bit fixed channels\n"); printf("R_16F Two component format with 32-bit floating-point channels\n"); @@ -140,6 +155,7 @@ void PrintUsage() { printf("RG_16F Two component format with 16-bit floating-point channels\n"); printf("RG_32F Two component format with 32-bit floating-point channels\n"); printf("RGB_888 RGB format with 8-bit fixed channels\n"); +#endif printf("\n"); printf("The following is a list of compression formats\n"); printf("ASTC Adaptive Scalable Texture Compression\n"); @@ -162,8 +178,10 @@ void PrintUsage() { printf(" alpha. Eight bits per pixel\n"); printf("BC3 Four component compressed texture format with interpolated\n"); printf(" alpha. Eight bits per pixel\n"); - printf("BC4 Single component compressed texture format for Microsoft\n"); - printf("BC5 Two component compressed texture format for Microsoft\n"); + printf("BC4 Single component compressed texture format\n"); + printf("BC4_S Signed Single component compressed texture format\n"); + printf("BC5 Two component compressed texture format\n"); + printf("BC5_S Signed Two component compressed texture format\n"); printf("BC6H High-Dynamic Range compression format\n"); printf("BC7 High-quality compression of RGB and RGBA data\n\n"); printf("DXT1 An opaque (or 1-bit alpha) DXTC compressed texture format.\n"); @@ -269,27 +287,31 @@ void PrintUsage() { printf("-noprogress Disables showing of compression progress messages\n"); printf("\n\n"); printf("Example compression:\n\n"); - printf("CompressonatorCLI.exe -fd ASTC image.bmp result.astc \n"); - printf("CompressonatorCLI.exe -fd ASTC -BlockRate 0.8 image.bmp result.astc\n"); - printf("CompressonatorCLI.exe -fd ASTC -BlockRate 12x12 image.bmp result.astc\n"); - printf("CompressonatorCLI.exe -fd BC7 image.bmp result.dds \n"); - printf("CompressonatorCLI.exe -fd BC7 image.bmp result.bmp\n"); - printf("CompressonatorCLI.exe -fd BC7 -NumTheads 16 image.bmp result.dds\n"); - printf("CompressonatorCLI.exe -fd BC7 -ff PNG -fx KTX ./source_dir/ ./dist_dir/\n"); - printf("CompressonatorCLI.exe -fd BC6H image.exr result.exr\n\n"); + printf("compressonatorcli.exe -fd ASTC image.bmp result.astc \n"); + printf("compressonatorcli.exe -fd ASTC -BlockRate 0.8 image.bmp result.astc\n"); + printf("compressonatorcli.exe -fd ASTC -BlockRate 12x12 image.bmp result.astc\n"); + printf("compressonatorcli.exe -fd BC7 image.bmp result.dds \n"); + printf("compressonatorcli.exe -fd BC7 image.bmp result.bmp\n"); + printf("compressonatorcli.exe -fd BC7 -NumTheads 16 image.bmp result.dds\n"); + printf("compressonatorcli.exe -fd BC7 -ff PNG -fx KTX ./source_dir/ ./dist_dir/\n"); + printf("compressonatorcli.exe -fd BC6H image.exr result.exr\n\n"); +#ifdef _WIN32 printf("Example compression using GPU Hardware or shader code with frameworks like OpenCL or DirectX:\n\n"); - printf("CompressonatorCLI.exe -fd BC1 -EncodeWith GPU image.bmp result.dds \n"); - printf("CompressonatorCLI.exe -fd BC1 -EncodeWith DXC image.bmp result.dds \n"); - printf("CompressonatorCLI.exe -fd BC1 -EncodeWith OCL image.bmp result.dds \n"); + printf("compressonatorcli.exe -fd BC1 -EncodeWith GPU image.bmp result.dds \n"); + printf("compressonatorcli.exe -fd BC1 -EncodeWith DXC image.bmp result.dds \n"); + printf("compressonatorcli.exe -fd BC1 -EncodeWith OCL image.bmp result.dds \n"); +#endif printf("Example decompression from compressed image using CPU:\n\n"); - printf("CompressonatorCLI.exe result.dds image.bmp\n\n"); + printf("compressonatorcli.exe result.dds image.bmp\n\n"); +#ifdef _WIN32 printf("Example decompression from compressed image using GPU:\n\n"); - printf("CompressonatorCLI.exe -UseGPUDecompress result.dds image.bmp\n\n"); + printf("compressonatorcli.exe -UseGPUDecompress result.dds image.bmp\n\n"); +#endif printf("Example compression with decompressed result (Useful for qualitative analysis):\n\n"); #ifdef USE_MESH_DRACO_EXTENSION printf("Example draco compression usage (support glTF and OBJ file only):\n\n"); - printf("CompressonatorCLI.exe -draco source.gltf dest.gltf\n"); - printf("CompressonatorCLI.exe -draco source.obj dest.drc\n"); + printf("compressonatorcli.exe -draco source.gltf dest.gltf\n"); + printf("compressonatorcli.exe -draco source.obj dest.drc\n"); printf("Note: only .obj file produces compressed .drc file. glTF does not produce .drc compressed file.\n"); printf("Note: You can specify .obj as compressed file format as well, but a new .drc file will be created for this case.\n\n"); #ifdef USE_MESH_DRACO_SETTING @@ -297,8 +319,8 @@ void PrintUsage() { printf("CompressonatorCLI.exe -draco -dracolvl 7 -qpos 12 -qtexc 8 -qnorm 8 source.gltf dest.gltf\n\n"); #endif printf("Example draco decompression usage (support glTF and drc file only):\n\n"); - printf("CompressonatorCLI.exe source.gltf dest.gltf\n"); - printf("CompressonatorCLI.exe source.drc dest.obj\n"); + printf("compressonatorcli.exe source.gltf dest.gltf\n"); + printf("compressonatorcli.exe source.drc dest.obj\n"); #endif #ifdef USE_3DMESH_OPTIMIZE printf("\n\n"); @@ -311,31 +333,36 @@ void PrintUsage() { printf("Specifies settings :\n\n"); printf("CompressonatorCLI.exe -meshopt -optVCacheSize 32 -optOverdrawACMRThres 1.03 -optVFetch 0 source.gltf dest.gltf\n"); #endif - printf("For additional help go to Documents folder and type index.htlm\n"); +#ifdef _WIN32 + printf("For additional help go to documents folder and type index.htlm \n"); +#else + printf("For additional help go to documents htlm folder and type index.htlm \n"); +#endif } -bool ProgressCallback(float fProgress, CMP_DWORD_PTR pUser1, CMP_DWORD_PTR pUser2) { +bool ProgressCallback(float fProgress, CMP_DWORD_PTR pUser1, CMP_DWORD_PTR pUser2) +{ return CompressionCallback(fProgress, pUser1, pUser2); } - - //--------------------------------------------- // For future releases::compute codecs //--------------------------------------------- #ifdef USE_GTC -PluginInterface_Encoder *g_plugin_EncoderGTC = NULL; -CMP_Encoder *g_Codec_GTC = NULL; +PluginInterface_Encoder* g_plugin_EncoderGTC = NULL; +CMP_Encoder* g_Codec_GTC = NULL; -extern void(*GTC_DecompressBlock)(void *out, void *in); -extern void(*GTC_CompressBlock)(void * srcblock, void *dest, void *blockoptions); +extern void (*GTC_DecompressBlock)(void* out, void* in); +extern void (*GTC_CompressBlock)(void* srcblock, void* dest, void* blockoptions); -void g_GTC_DecompressBlock(void *in, void *out) { +void g_GTC_DecompressBlock(void* in, void* out) +{ if (g_Codec_GTC) g_Codec_GTC->DecompressBlock(in, out); } -void g_GTC_CompressBlock(void *in, void *out, void *blockoptions) { +void g_GTC_CompressBlock(void* in, void* out, void* blockoptions) +{ if (g_Codec_GTC) g_Codec_GTC->CompressBlock(in, out, blockoptions); } @@ -343,20 +370,24 @@ void g_GTC_CompressBlock(void *in, void *out, void *blockoptions) { //----------------- BASIS: Run Time Encoder ------------------ #ifdef USE_BASIS -PluginInterface_Encoder *g_plugin_EncoderBASIS = NULL; -CMP_Encoder *g_Codec_BASIS = NULL; -extern int(*BASIS_CompressTexture)(void * in, void *out, void *blockoptions); -extern int(*BASIS_DecompressTexture)(void * in, void *out, void *blockoptions); - -int g_BASIS_CompressTexture(void *in, void *out, void *blockoptions) { - if (g_Codec_BASIS) { +PluginInterface_Encoder* g_plugin_EncoderBASIS = NULL; +CMP_Encoder* g_Codec_BASIS = NULL; +extern int (*BASIS_CompressTexture)(void* in, void* out, void* blockoptions); +extern int (*BASIS_DecompressTexture)(void* in, void* out, void* blockoptions); + +int g_BASIS_CompressTexture(void* in, void* out, void* blockoptions) +{ + if (g_Codec_BASIS) + { return g_Codec_BASIS->CompressTexture(in, out, blockoptions); } return 0; } -int g_BASIS_DecompressTexture(void *in, void *out, void *blockoptions) { - if (g_Codec_BASIS) { +int g_BASIS_DecompressTexture(void* in, void* out, void* blockoptions) +{ + if (g_Codec_BASIS) + { return g_Codec_BASIS->DecompressTexture(in, out, blockoptions); } return 0; @@ -364,40 +395,52 @@ int g_BASIS_DecompressTexture(void *in, void *out, void *blockoptions) { #endif - //----------------- APC: Run Time Encoder ------------------ #ifdef USE_APC -PluginInterface_Encoder *g_plugin_EncoderAPC = NULL; -CMP_Encoder *g_Codec_APC = NULL; -extern void(*APC_DecompressBlock)(void *out, void *in); -extern void(*APC_CompressBlock)(void * srcblock, void *dest, void *blockoptions); +PluginInterface_Encoder* g_plugin_EncoderAPC = NULL; +CMP_Encoder* g_Codec_APC = NULL; +extern void (*APC_DecompressBlock)(void* out, void* in); +extern void (*APC_CompressBlock)(void* srcblock, void* dest, void* blockoptions); -void g_APC_DecompressBlock(void *in, void *out) { +void g_APC_DecompressBlock(void* in, void* out) +{ if (g_Codec_APC) g_Codec_APC->DecompressBlock(in, out); } -void g_APC_CompressBlock(void *in, void *out, void *blockoptions) { +void g_APC_CompressBlock(void* in, void* out, void* blockoptions) +{ if (g_Codec_APC) g_Codec_APC->CompressBlock(in, out, blockoptions); } #endif +int main(int argc, char* argv[]) +{ + // Check if print status line has been assigned + // if not get it a default to printf + if (PrintStatusLine == NULL) + PrintStatusLine = &LocalPrintF; -int main(int argc, char* argv[]) { #ifdef USE_QT_IMAGELOAD QCoreApplication app(argc, argv); #endif g_CMIPS = new CMIPS; - g_pluginManager.registerStaticPlugin("IMAGE", "ASTC",(void*)make_Plugin_ASTC); + g_pluginManager.registerStaticPlugin("IMAGE", "ASTC", (void*)make_Plugin_ASTC); g_pluginManager.registerStaticPlugin("IMAGE", "EXR", (void*)make_Plugin_EXR); g_pluginManager.registerStaticPlugin("IMAGE", "TGA", (void*)make_Plugin_TGA); // Use for load only, Qt will be used for Save g_pluginManager.registerStaticPlugin("IMAGE", "KTX", (void*)make_Plugin_KTX); g_pluginManager.registerStaticPlugin("IMAGE", "ANALYSIS", (void*)make_Plugin_CAnalysis); + +#ifdef _WIN32 + g_pluginManager.registerStaticPlugin("IMAGE", "KTX2", (void*)make_Plugin_KTX2); +#endif + g_pluginManager.getPluginList("\\Plugins"); + CMP_RegisterHostPlugins(); #ifdef USE_QT_IMAGELOAD @@ -405,44 +448,59 @@ int main(int argc, char* argv[]) { QCoreApplication::addLibraryPath(dirPath + "./plugins/imageformats"); #endif - // Check if print status line has been assigned - // if not get it a default to printf - if (PrintStatusLine == NULL) - PrintStatusLine = &LocalPrintF; + //---------------------------------- // Process user command line parameters //---------------------------------- - if (argc > 1) { + if (argc > 1) + { bool ParseOk = ParseParams(argc, argv); - if (!ParseOk) { + if (!ParseOk) + { delete g_CMIPS; return -1; } - if (g_CmdPrams.SourceFile.length() == 0) { + if (g_CmdPrams.SourceFile.length() == 0) + { + printf("Source Texture file was not supplied!\n"); delete g_CMIPS; return -2; } - if (!g_CmdPrams.imageprops && (g_CmdPrams.DestFile.length() == 0)) { + // Some checks prior to running + if (!g_CmdPrams.imageprops && (g_CmdPrams.DestFile.length() == 0)) + { // Try to patch the detination file - if ((g_CmdPrams.DestFile.length() == 0) && (g_CmdPrams.SourceFile.length() > 0)) { - g_CmdPrams.DestFile = DefaultDestination(g_CmdPrams.SourceFile, g_CmdPrams.CompressOptions.DestFormat, g_CmdPrams.FileOutExt); + if ((g_CmdPrams.DestFile.length() == 0) && (g_CmdPrams.SourceFile.length() > 0)) + { + g_CmdPrams.DestFile = DefaultDestination(g_CmdPrams.SourceFile, g_CmdPrams.CompressOptions.DestFormat, g_CmdPrams.FileOutExt,true); printf("Destination Texture file was not supplied: Defaulting to %s\n", g_CmdPrams.DestFile.c_str()); - } else { + } + else + { + printf("Image properties not set"); delete g_CMIPS; return (-3); } } + if ((g_CmdPrams.CompressOptions.nEncodeWith != CMP_Compute_type::CMP_GPU_HW) && + (g_CmdPrams.CompressOptions.genGPUMipMaps || g_CmdPrams.CompressOptions.useSRGBFrames)) + { + printf("Setup Error: genGPUMipMaps or useSRGBFrames requires EncodeWith GPU\n"); + return -1; + } + #ifdef USE_GTC //--------------------------------------- // attempt to load GTC Codec //--------------------------------------- - g_plugin_EncoderGTC = reinterpret_cast(g_pluginManager.GetPlugin("ENCODER", "GTC")); + g_plugin_EncoderGTC = reinterpret_cast(g_pluginManager.GetPlugin("ENCODER", "GTC")); // Found GTC Codec - if (g_plugin_EncoderGTC) { + if (g_plugin_EncoderGTC) + { //------------------------------- // create the compression Codec //------------------------------- @@ -451,8 +509,9 @@ int main(int argc, char* argv[]) { //------------------------------------------------------------ // Assign compressonator lib GTC codec to Compute GTC Codec //------------------------------------------------------------ - if (g_Codec_GTC) { - GTC_CompressBlock = g_GTC_CompressBlock; + if (g_Codec_GTC) + { + GTC_CompressBlock = g_GTC_CompressBlock; GTC_DecompressBlock = g_GTC_DecompressBlock; } } @@ -462,16 +521,18 @@ int main(int argc, char* argv[]) { //--------------------------------------- // attempt to load compute BASIS Codec //--------------------------------------- - g_plugin_EncoderBASIS = reinterpret_cast(g_pluginManager.GetPlugin("ENCODER", "BASIS")); + g_plugin_EncoderBASIS = reinterpret_cast(g_pluginManager.GetPlugin("ENCODER", "BASIS")); // Found BASIS Codec - if (g_plugin_EncoderBASIS) { + if (g_plugin_EncoderBASIS) + { //------------------------------- // create the compression Codec //------------------------------- g_Codec_BASIS = (CMP_Encoder*)g_plugin_EncoderBASIS->TC_Create(); // ToDo: Assignment to new encoder interfaces - if (g_Codec_BASIS) { + if (g_Codec_BASIS) + { BASIS_CompressTexture = g_BASIS_CompressTexture; BASIS_DecompressTexture = g_BASIS_DecompressTexture; } @@ -479,8 +540,9 @@ int main(int argc, char* argv[]) { #endif #ifdef USE_APC - g_plugin_EncoderAPC = reinterpret_cast(g_pluginManager.GetPlugin("ENCODER", "APC")); - if (g_plugin_EncoderAPC) { + g_plugin_EncoderAPC = reinterpret_cast(g_pluginManager.GetPlugin("ENCODER", "APC")); + if (g_plugin_EncoderAPC) + { //------------------------------- // create the compression Codec //------------------------------- @@ -489,7 +551,8 @@ int main(int argc, char* argv[]) { //------------------------------------------------------------ // Assign compressonator lib APC codec to Compute APC Codec //------------------------------------------------------------ - if (g_Codec_APC) { + if (g_Codec_APC) + { APC_CompressBlock = g_APC_CompressBlock; APC_DecompressBlock = g_APC_DecompressBlock; } @@ -504,35 +567,40 @@ int main(int argc, char* argv[]) { //------------------------------------------ // Cleanup the compute GTC compression Codec //------------------------------------------ - if (g_plugin_EncoderGTC) { + if (g_plugin_EncoderGTC) + { if (g_Codec_GTC) g_plugin_EncoderGTC->TC_Destroy(g_Codec_GTC); - delete g_plugin_EncoderGTC; + delete g_plugin_EncoderGTC; } #endif #ifdef USE_BASIS //------------------------------------------ // Cleanup the compute GTC compression Codec //------------------------------------------ - if (g_plugin_EncoderBASIS) { + if (g_plugin_EncoderBASIS) + { if (g_Codec_BASIS) g_plugin_EncoderBASIS->TC_Destroy(g_Codec_BASIS); - delete g_plugin_EncoderBASIS; + delete g_plugin_EncoderBASIS; } #endif #ifdef USE_APC //------------------------------------------ // Cleanup the compute GTC compression Codec //------------------------------------------ - if (g_plugin_EncoderAPC) { + if (g_plugin_EncoderAPC) + { if (g_Codec_APC) g_plugin_EncoderAPC->TC_Destroy(g_Codec_APC); - delete g_plugin_EncoderAPC; + delete g_plugin_EncoderAPC; } #endif return ret; - } else { + } + else + { PrintUsage(); delete g_CMIPS; return 0; diff --git a/applications/compressonatorgui/cmakelists.txt b/applications/compressonatorgui/cmakelists.txt index 6cc87f174..59f90528b 100644 --- a/applications/compressonatorgui/cmakelists.txt +++ b/applications/compressonatorgui/cmakelists.txt @@ -1,150 +1,168 @@ -# Compressonator GUI -# this must be before add_executable -if (OPTION_CMP_DIRECTX ) -link_directories( - ${PROJECT_SOURCE_DIR}/../common/lib/ext/directxtex/DirectXTex-jun2020b/DirectXTex/Bin/Desktop_2017/x64 - ) -endif() - -link_directories( - ${PROJECT_SOURCE_DIR}/../common/lib/ext/opencv/2.49/x64/VS2015/lib - ${PROJECT_SOURCE_DIR}/../common/lib/ext/zlib/zlib-1.2.10/VS2015/x64/lib - ${PROJECT_SOURCE_DIR}/../common/lib/ext/openexr/ilmbase-2.2.0/VS2015/x64/lib - ${PROJECT_SOURCE_DIR}/../common/lib/ext/openexr/openexr-2.2.0/VS2015/x64/lib - ) - -# Enable automoc and autorcc -set(CMAKE_INCLUDE_CURRENT_DIR ON) +# Include local GUI required libs add_subdirectory(common) add_subdirectory(components) -add_subdirectory(generatedfiles) add_subdirectory(qpropertypages) -# Compressonator GUI Application -add_executable(CompressonatorGUI-bin) +if(CMP_HOST_WINDOWS) + # this must be before add_executable + link_directories( + ${PROJECT_EXTERNAL_LIBDIR}/opencv/2.49/x64/VS2015/lib + ${PROJECT_EXTERNAL_LIBDIR}/zlib/zlib-1.2.10/VS2015/x64/lib + ${PROJECT_EXTERNAL_LIBDIR}/openexr/ilmbase-2.2.0/VS2015/x64/lib + ${PROJECT_EXTERNAL_LIBDIR}/openexr/openexr-2.2.0/VS2015/x64/lib + ${PROJECT_EXTERNAL_LIBDIR}/glew/1.9.0/lib/x64 + ) + + # this must be before add_executable + if (OPTION_CMP_DIRECTX) + link_directories( + ${PROJECT_EXTERNAL_LIBDIR}/directxtex/DirectXTex-jun2020b/DirectXTex/Bin/Desktop_2017/x64 + ) + endif() -if (CMP_HOST_WINDOWS) - add_dependencies(CompressonatorGUI-bin extern_ktx) endif() -set_target_properties(CompressonatorGUI-bin PROPERTIES - AUTOMOC ON - AUTORCC ON - AUTOMOC_COMPILER_PREDEFINES ON - OUTPUT_NAME "compressonator" +file(GLOB_RECURSE RESOURCES + resources/* ) -# Find the QtWidgets library -find_package(Qt5 REQUIRED COMPONENTS Core Widgets HINTS ${QT_PACKAGE_ROOT}) +file(GLOB_RECURSE APP_SOURCES + source/cpmaincomponents.cpp + source/cpmaincomponents.h + source/main.cpp + source/qtignorecompilerwarnings.h + compressonatorgui.qrc + + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/mipstoqimage.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/mipstoqimage.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/atiformats.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/atiformats.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/cmdline.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/cmdline.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/cmp_fileio.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/cmp_fileio.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/modeldata.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/modeldata.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/plugininterface.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/pluginmanager.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/pluginmanager.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/query_timer.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/query_timer.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/textureio.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/textureio.cpp +) if(CMP_HOST_WINDOWS) - set_target_properties(CompressonatorGUI-bin PROPERTIES - WIN32_EXECUTABLE ON - AUTOMOC_MOC_OPTIONS -D_WIN32 - VS_DEBUGGER_WORKING_DIRECTORY $ - ) -elseif(CMP_HOST_APPLE) - set_target_properties(CompressonatorGUI-bin PROPERTIES - MACOSX_BUNDLE ON - LINK_FLAGS "-Wl,-F${CMAKE_OSX_SYSROOT}/System/Library/Frameworks,-v" - ) +add_executable(CompressonatorGUI-bin + WIN32 + ${RESOURCES} + ${APP_SOURCES} + ) +else() +add_executable(CompressonatorGUI-bin + ${RESOURCES} + ${APP_SOURCES} + ) endif() -file(GLOB_RECURSE SRCS +set (CMP_LIBS "") +list(APPEND CMP_LIBS CMP_Common + CMP_Compressonator + CMP_Framework + CMP_GpuDecode + CMP_MeshCompressor + CMP_MeshOptimizer + + CMP_GUI_Gltf + CMP_GUI_Components + CMP_GUI_Common + CMP_GUI_QPropertyPages + + Image_Analysis + Image_ASTC + Image_EXR + Image_KTX + Image_TGA + + # min links + ${OpenEXR_LIBRARIES} + ${OpenCV_LIBRARIES} + ${Boost_LIBRARIES} + ${draco_LIBRARIES} + Threads::Threads + ) - Source/*.cpp - Source/*.h -) - -file(GLOB_RECURSE IMAGES - - images/* -) - -file(GLOB_RECURSE RESOURCES +if(CMP_HOST_WINDOWS) + list(APPEND CMP_LIBS ExtQt5 + ExtQt5Widgets + Qt5::Xml + Image_KTX2 + ) +else() + list(APPEND CMP_LIBS Qt5::Widgets + Qt5::Qml + Qt5::OpenGL + Qt5::WebEngineWidgets + Qt5::Xml + ) + +# allow const char* -> char* +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + target_compile_options(CompressonatorGUI-bin + PRIVATE + "-Wno-c++11-compat-deprecated-writable-strings") +elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + target_compile_options(CompressonatorGUI-bin + PRIVATE + "-Wno-write-strings") +endif() - resources/* -) -target_sources(CompressonatorGUI-bin PRIVATE +endif() - ${SRCS} - ${RESOURCES} - compressonatorgui.qrc -) +target_link_libraries(CompressonatorGUI-bin ${CMP_LIBS} ) target_include_directories(CompressonatorGUI-bin PRIVATE - ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib - ./resources - ./source -) - -target_link_libraries(CompressonatorGUI-bin + components + qpropertypages + common + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib + ${PROJECT_SOURCE_DIR}/cmp_framework/ + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half + ${PROJECT_SOURCE_DIR}/applications/_plugins/common + ${PROJECT_SOURCE_DIR}/applications/_plugins/canalysis/analysis + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/gltf/ + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/json/ + ${PROJECT_SOURCE_DIR}/applications/_libs/gpu_decode + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_mesh + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_meshoptimizer + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_meshcompressor/draco/src + ${PROJECT_SOURCE_DIR}/applications/_plugins/cmesh/mesh_optimizer + ${PROJECT_SOURCE_DIR}/cmp_core/shaders + ${PROJECT_SOURCE_DIR}/cmp_core/source + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_math + ${PROJECT_SOURCE_DIR}/../common/lib/ext/glm + ${PROJECT_EXTERNAL_LIBDIR}/rapidxml + ${OpenCV_INCLUDE_DIRS} + ${draco_INCLUDE_DIRS} ) - PRIVATE - # Static libs - ExtKTX - ExtGLEW - CMP_GUI - CMP_GUI_BIN_Common - CMP_GUI_BIN_Components - CMP_GUI_BIN_GeneratedFiles - CMP_GUI_BIN_QPropertyPages - CMP_Mesh - CompressonatorLIB - GPU_Decode - - # Static plugin libs - Plugin_CImage - Plugin_Common_UtilFuncs - Plugin_GLTF - Plugin_PluginManager - Plugin_TCPluginAPI - - # Dynamic plugins - CMP_MeshCompressor - Plugin_C3DModel_Loaders - Plugin_C3DModel_Viewers - Plugin_CGPUDecode - Plugin_CMesh -) - -include(copyfiles.cmake) - -set_target_properties(CompressonatorGUI-bin PROPERTIES FOLDER ${FOLDER_NAME}) - -if (CMAKE_GENERATOR STREQUAL "Unix Makefiles") - install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - DESTINATION . - USE_SOURCE_PERMISSIONS - PATTERN "cmake_install.cmake" EXCLUDE - PATTERN "CMakeFiles*" EXCLUDE - PATTERN "common*" EXCLUDE - PATTERN "components*" EXCLUDE - PATTERN "CompressonatorGUI-bin_autogen*" EXCLUDE - PATTERN "ctesttestfile.cmake" EXCLUDE - PATTERN "generatedfiles*" EXCLUDE - PATTERN "makefile" EXCLUDE - PATTERN "qpropertypages*" EXCLUDE - ) -elseif (CMAKE_GENERATOR STREQUAL "Visual Studio 16 2019") - install(DIRECTORY "$" - DESTINATION "./compressonatorgui" - USE_SOURCE_PERMISSIONS - ) -elseif (CMAKE_GENERATOR STREQUAL "Xcode") - install(DIRECTORY "$" - DESTINATION "./compressonatorgui" - USE_SOURCE_PERMISSIONS - ) +if(CMP_HOST_WINDOWS) + include(copyfiles.cmake) + set_target_properties(CompressonatorGUI-bin + PROPERTIES + AUTOMOC ON + AUTORCC ON + AUTOMOC_COMPILER_PREDEFINES ON + OUTPUT_NAME "compressonator" + ) +else() + set_target_properties(CompressonatorGUI-bin + PROPERTIES + AUTOMOC ON + AUTORCC ON + AUTOMOC_COMPILER_PREDEFINES ON + OUTPUT_NAME "compressonator-bin" + ) endif() - - -#============== Helper to show all currently accessable variables in cmake -# get_cmake_property(_variableNames VARIABLES) -# list (SORT _variableNames) -# foreach (_variableName ${_variableNames}) -# message(STATUS "${_variableName}=${${_variableName}}") -# endforeach() diff --git a/applications/compressonatorgui/common/cmakelists.txt b/applications/compressonatorgui/common/cmakelists.txt index e0f95f05a..aaf18c243 100644 --- a/applications/compressonatorgui/common/cmakelists.txt +++ b/applications/compressonatorgui/common/cmakelists.txt @@ -1,25 +1,30 @@ -add_library(CMP_GUI_BIN_Common INTERFACE) - -target_sources(CMP_GUI_BIN_Common INTERFACE - cptreewidget.cpp - geometryengine.cpp - objectcontroller.cpp +set(COMMON_H + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/common/cptreewidget.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/common/cvmatandqimage.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/common/geometryengine.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/common/objectcontroller.cpp ) -target_sources(CMP_GUI_BIN_Common INTERFACE - - cptreewidget.h - geometryengine.h - objectcontroller.h +set(COMMON_SRC + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/common/cptreewidget.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/common/cvmatandqimage.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/common/geometryengine.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/common/objectcontroller.h ) -target_link_libraries(CMP_GUI_BIN_Common INTERFACE +add_library(CMP_GUI_Common STATIC ${COMMON_H} ${COMMON_SRC}) - Plugin_PluginManager - ExtOpenCV -) +target_include_directories(CMP_GUI_Common PRIVATE + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib + ${PROJECT_SOURCE_DIR}/applications/_plugins/common + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/qpropertypages + ${PROJECT_SOURCE_DIR}/../common/lib/ext/glm + ${OpenCV_INCLUDE_DIRS} + ) -target_include_directories(CMP_GUI_BIN_Common INTERFACE +target_link_libraries(CMP_GUI_Common CMP_Compressonator Qt5::Widgets ${OpenCV_LIBS}) - ./ -) \ No newline at end of file +set_target_properties(CMP_GUI_Common PROPERTIES + AUTOMOC ON + FOLDER "Libs") diff --git a/applications/compressonatorgui/components/ac3dmeshanalysis.cpp b/applications/compressonatorgui/components/ac3dmeshanalysis.cpp index 1e3f73c8d..044bca50e 100644 --- a/applications/compressonatorgui/components/ac3dmeshanalysis.cpp +++ b/applications/compressonatorgui/components/ac3dmeshanalysis.cpp @@ -57,7 +57,7 @@ void ac3DMeshAnalysis::Init(const QString & title, const QString & productName) m_textBrowser = new QTextBrowser(this); m_textBrowser->setReadOnly(true); m_textBrowser->setAcceptRichText(true); - m_textBrowser->setMinimumHeight(350); + m_textBrowser->setMinimumHeight(32); // Main Vertical Layout: pMainLayout->addWidget(m_GBAnalyseVCache); diff --git a/applications/compressonatorgui/components/ac3dmeshanalysis.h b/applications/compressonatorgui/components/ac3dmeshanalysis.h index 312fea8e4..c12000738 100644 --- a/applications/compressonatorgui/components/ac3dmeshanalysis.h +++ b/applications/compressonatorgui/components/ac3dmeshanalysis.h @@ -2,7 +2,7 @@ #define AC3DMESHANALYSIS_H #include -#include +#include #include #include #include diff --git a/applications/compressonatorgui/components/acaboutdlg.cpp b/applications/compressonatorgui/components/acaboutdlg.cpp index d8d555a68..ef9be731f 100644 --- a/applications/compressonatorgui/components/acaboutdlg.cpp +++ b/applications/compressonatorgui/components/acaboutdlg.cpp @@ -14,7 +14,7 @@ void CHelpAboutDialog::Init(const QString & title, const QString & productName) // Set the dialog title: setWindowTitle(title); - setWindowIcon(QIcon(":/CompressonatorGUI/Images/acompress-256.png")); + setWindowIcon(QIcon(":/compressonatorgui/images/acompress-256.png")); // Set window flags (minimize / maximize / close buttons): Qt::WindowFlags flags = windowFlags(); diff --git a/applications/compressonatorgui/components/acaboutdlg.h b/applications/compressonatorgui/components/acaboutdlg.h index 7d9a8c9c0..74c467d27 100644 --- a/applications/compressonatorgui/components/acaboutdlg.h +++ b/applications/compressonatorgui/components/acaboutdlg.h @@ -1,12 +1,12 @@ #ifndef CPABOUTDLG_H #define CPABOUTDLG_H -#include +#include #include #include #include #include -#include +#include #include class CHelpAboutDialog : public QDialog { diff --git a/applications/compressonatorgui/components/accustomgraphics.cpp b/applications/compressonatorgui/components/accustomgraphics.cpp index 948fba098..89dafc3ab 100644 --- a/applications/compressonatorgui/components/accustomgraphics.cpp +++ b/applications/compressonatorgui/components/accustomgraphics.cpp @@ -224,7 +224,7 @@ void acCustomGraphicsScene::drawBackground(QPainter* painter, const QRectF& rect switch (m_gridenabled) { case eCustomGraphicsScene_Grids::Block: { - QImage image(":/CompressonatorGUI/Images/GridSolid.png"); + QImage image(":/compressonatorgui/images/gridsolid.png"); QBrush brush(image); painter->fillRect(rect, brush); break; diff --git a/applications/compressonatorgui/components/acdiffimage.cpp b/applications/compressonatorgui/components/acdiffimage.cpp index 829252dcf..fd6c8a4b8 100644 --- a/applications/compressonatorgui/components/acdiffimage.cpp +++ b/applications/compressonatorgui/components/acdiffimage.cpp @@ -1,4 +1,4 @@ -#include "acDiffImage.h" +#include "acdiffimage.h" #include "cpprojectview.h" acDiffImage::acDiffImage(QWidget *parent) : QDialog(parent) { diff --git a/applications/compressonatorgui/components/acdockwidgettitlebar.cpp b/applications/compressonatorgui/components/acdockwidgettitlebar.cpp index e7b7ff20d..2f591b2ee 100644 --- a/applications/compressonatorgui/components/acdockwidgettitlebar.cpp +++ b/applications/compressonatorgui/components/acdockwidgettitlebar.cpp @@ -40,7 +40,7 @@ acDockWidgetTitlebar::acDockWidgetTitlebar(QWidget *parent) : parent(parent) { m_parent = parent; // Create a close button and set its icon to that of the OS - m_ToolBarIcon = new QIcon(":/CompressonatorGUI/Images/settings.png"); + m_ToolBarIcon = new QIcon(":/compressonatorgui/images/settings.png"); m_buttonToolBar = new QPushButton(this); m_buttonToolBar->setIcon(*m_ToolBarIcon); @@ -78,7 +78,7 @@ acDockWidgetTitlebar::acDockWidgetTitlebar(QWidget *parent) : parent(parent) { // Create a close button and set its icon to that of the OS m_buttonClose = new QPushButton(this); - //m_buttonClose->setIcon(QIcon(":/CompressonatorGUI/Images/cxClose.png")); + //m_buttonClose->setIcon(qicon(":/compressonatorgui/images/cxclose.png")); m_buttonClose->setIcon(closeIcon); m_buttonClose->setStyleSheet(PushButtonStyle); m_buttonClose->setToolTip("Close Window"); diff --git a/applications/compressonatorgui/components/acimageview.cpp b/applications/compressonatorgui/components/acimageview.cpp index 8403bc158..e75161ce0 100644 --- a/applications/compressonatorgui/components/acimageview.cpp +++ b/applications/compressonatorgui/components/acimageview.cpp @@ -26,11 +26,254 @@ #include "acimageview.h" -#define CURSOR_SIZE 12 // pixel per cross hair fin +//========================================================== +// Note; acPrefix functions are defined also in mipstoqimage +// remove this duplication +//========================================================== + +CMP_FLOAT acF16toF32(CMP_HALFSHORT f) +{ + CMP_HALF A; + A.setBits(f); + return ((CMP_FLOAT)A); +} + +static float accmp_clampf(float value, float min, float max) +{ + if (value < min) + return min; + if (value > max) + return max; + return value; +} + +//load data byte in mipset into Qimage ARGB32 format +inline float acknee(double x, double f) +{ + return float(log(x * f + 1.f) / f); +} + +float acfindKneeF(float x, float y) +{ + float f0 = 0; + float f1 = 1.f; + + while (acknee(x, f1) > y) + { + f0 = f1; + f1 = f1 * 2.f; + } + + for (int i = 0; i < 30; ++i) + { + const float f2 = (f0 + f1) / 2.f; + const float y2 = acknee(x, f2); + + if (y2 < y) + { + f1 = f2; + } + else + { + f0 = f2; + } + } + + return (f0 + f1) / 2.f; +} + +void acfloat2Pixel(float kl, float f, float r, float g, float b, float a, int x, int y, QImage* image, CMP_CompressOptions option) +{ + CMP_BYTE r_b, g_b, b_b, a_b; + + float invGamma, scale; + if (option.fInputGamma < 1.0f) + { + option.fInputGamma = 2.2f; + } + + invGamma = 1.0 / option.fInputGamma; //for gamma correction + float luminance3f = powf(2, -3.5); // always assume max intensity is 1 and 3.5f darker for scale later + scale = 255.0 * powf(luminance3f, invGamma); + + // 1) Compensate for fogging by subtracting defog + // from the raw pixel values. + // We assume a defog of 0 + if (option.fInputDefog > 0.0f) + { + r = r - option.fInputDefog; + g = g - option.fInputDefog; + b = b - option.fInputDefog; + a = a - option.fInputDefog; + } + + // 2) Multiply the defogged pixel values by + // 2^(exposure + 2.47393). + const float exposeScale = pow(2, option.fInputExposure + 2.47393f); + r = r * exposeScale; + g = g * exposeScale; + b = b * exposeScale; + a = a * exposeScale; + + // 3) Values that are now 1.0 are called "middle gray". + // If defog and exposure are both set to 0.0, then + // middle gray corresponds to a raw pixel value of 0.18. + // In step 6, middle gray values will be mapped to an + // intensity 3.5 f-stops below the display's maximum + // intensity. + + // 4) Apply a knee function. The knee function has two + // parameters, kneeLow and kneeHigh. Pixel values + // below 2^kneeLow are not changed by the knee + // function. Pixel values above kneeLow are lowered + // according to a logarithmic curve, such that the + // value 2^kneeHigh is mapped to 2^3.5. (In step 6, + // this value will be mapped to the the display's + // maximum intensity.) + if (r > kl) + { + r = kl + acknee(r - kl, f); + } + if (g > kl) + { + g = kl + acknee(g - kl, f); + } + if (b > kl) + { + b = kl + acknee(b - kl, f); + } + if (a > kl) + { + a = kl + acknee(a - kl, f); + } + + // 5) Gamma-correct the pixel values, according to the + // screen's gamma. (We assume that the gamma curve + // is a simple power function.) + r = pow(r, invGamma); + g = pow(g, invGamma); + b = pow(b, invGamma); + a = pow(a, option.fInputGamma); + + // 6) Scale the values such that middle gray pixels are + // mapped to a frame buffer value that is 3.5 f-stops + // below the display's maximum intensity. (84.65 if + // the screen's gamma is 2.2) + r *= scale; + g *= scale; + b *= scale; + a *= scale; + + r_b = (CMP_BYTE)accmp_clampf(r, 0.f, 255.f); + g_b = (CMP_BYTE)accmp_clampf(g, 0.f, 255.f); + b_b = (CMP_BYTE)accmp_clampf(b, 0.f, 255.f); + a_b = (CMP_BYTE)accmp_clampf(a, 0.f, 255.f); + + image->setPixel(x, y, qRgba(r_b, g_b, b_b, a_b)); +} + +// +// load Exr Image Properties +// + +void acloadExrProperties(CMIPS* m_CMips, MipSet* mipset, int level, QImage* image, CMP_CompressOptions option) +{ + MipLevel* mipLevel = m_CMips->GetMipLevel(mipset, level); + if (mipLevel->m_pbData == NULL) + return; + + float kl = pow(2.f, option.fInputKneeLow); + float f = acfindKneeF(pow(2.f, option.fInputKneeHigh) - kl, pow(2.f, 3.5f) - kl); + + if (mipset->m_ChannelFormat == CF_Float32) + { + float* data = mipLevel->m_pfData; + float r = 0, g = 0, b = 0, a = 0; + //copy pixels into image + for (int y = 0; y < mipLevel->m_nHeight; y++) + { + for (int x = 0; x < mipLevel->m_nWidth; x++) + { + r = *data; + data++; + g = *data; + data++; + b = *data; + data++; + a = *data; + data++; + acfloat2Pixel(kl, f, r, g, b, a, x, y, image, option); + } + + //if ((y % 10) == 0) + // QApplication::processEvents(); + } + } + else if (mipset->m_ChannelFormat == CF_Float16) + { + CMP_HALFSHORT* data = mipLevel->m_phfsData; + CMP_HALFSHORT r, g, b, a; + //copy pixels into image + for (int y = 0; y < mipLevel->m_nHeight; y++) + { + for (int x = 0; x < mipLevel->m_nWidth; x++) + { + r = *data; + data++; + g = *data; + data++; + b = *data; + data++; + a = *data; + data++; + acfloat2Pixel(kl, f, acF16toF32(r), acF16toF32(g), acF16toF32(b), acF16toF32(a), x, y, image, option); + } + + //if ((y % 10) == 0) + // QApplication::processEvents(); + } + } + else if (mipset->m_ChannelFormat == CF_Float9995E) + { + //CMP_DWORD dwSize = mipLevel->m_dwLinearSize; + CMP_DWORD* pSrc = mipLevel->m_pdwData; + float r = 0, g = 0, b = 0, a = 0; + union + { + float f; + int32_t i; + } fi; + float Scale = 0.0f; + for (int y = 0; y < mipLevel->m_nHeight; y++) + { + for (int x = 0; x < mipLevel->m_nWidth; x++) + { + CMP_DWORD dwSrc = *pSrc++; + R9G9B9E5 pTemp; + + pTemp.rm = (dwSrc & 0x000001ff); + pTemp.gm = (dwSrc & 0x0003fe00) >> 9; + pTemp.bm = (dwSrc & 0x07fc0000) >> 18; + pTemp.e = (dwSrc & 0xf8000000) >> 27; + + fi.i = 0x33800000 + (pTemp.e << 23); + Scale = fi.f; + r = Scale * float(pTemp.rm); + g = Scale * float(pTemp.gm); + b = Scale * float(pTemp.bm); + a = 1.0f; + acfloat2Pixel(kl, f, r, g, b, a, x, y, image, option); + } + //if ((y % 10) == 0) + // QApplication::processEvents(); + } + } +} -extern void loadExrProperties(CMIPS *m_CMips, MipSet* mipset, int level, QImage *image, CMP_CompressOptions option); +#define CURSOR_SIZE 12 // pixel per cross hair fin -acImageView::~acImageView() { +acImageView::~acImageView() +{ if (m_imageloader) delete m_imageloader; } @@ -42,52 +285,57 @@ acImageView::~acImageView() { // a navigation view default (lower right corner) // Date: 13/8/2015 // --------------------------------------------------------------------------- -acImageView::acImageView(const QString filePathName, QWidget *parent, CMipImages *OriginalMipImages, CMipImages *MipImages) { +acImageView::acImageView(const QString filePathName, QWidget* parent, CMipImages* OriginalMipImages, CMipImages* MipImages) +{ Q_UNUSED(parent); m_MipImages = MipImages; m_OriginalMipImages = OriginalMipImages; - m_imageloader = new CImageLoader(); + m_imageloader = new CImageLoader(); m_layout = new QGridLayout(this); m_layout->setSpacing(0); m_layout->setMargin(0); m_layout->setContentsMargins(0, 0, 0, 0); - m_imageGraphicsView = NULL; - m_graphicsScene = NULL; + m_imageGraphicsView = NULL; + m_graphicsScene = NULL; m_imageItem_Processed = NULL; - m_imageItemNav = NULL; - m_errMessage = NULL; + m_imageItemNav = NULL; + m_errMessage = NULL; //Reserved: GPUDecode - m_navVisible = false; - m_isDiffView = false; - m_currentMiplevel = 0; - m_DepthIndex = 0; + m_navVisible = false; + m_isDiffView = false; + m_currentMiplevel = 0; + m_DepthIndex = 0; #ifdef _DEBUG - m_debugMode = false; - m_debugFormat = ""; + m_debugMode = false; + m_debugFormat = ""; #endif - m_imageOrientation = 0; - m_ImageScale = 100; - m_MouseHandDown = false; - m_appBusy = false; // Set to true when ImageView is processing data from a prior event + m_imageOrientation = 0; + m_ImageScale = 100; + m_MouseHandDown = false; + m_appBusy = false; // Set to true when ImageView is processing data from a prior event - for (int ml=0; mlQImage_list[ii].size() > 0) { + if (m_MipImages) + { + if (m_MipImages->QImage_list[0][0] != NULL) + for (int ii = 0; ii < CMP_MIPSET_MAX_DEPTHS; ii++) + { + if (m_MipImages->QImage_list[ii].size() > 0) + { // The scene is at 0,0 and set to the size of this display widget m_graphicsScene = new acCustomGraphicsScene(this); - QSize size = this->size(); + QSize size = this->size(); m_graphicsScene->setSceneRect(0, 0, size.width(), size.height()); m_graphicsScene->setBackgroundBrush(QBrush(Qt::black, Qt::SolidPattern)); @@ -98,48 +346,55 @@ acImageView::acImageView(const QString filePathName, QWidget *parent, CMipImages // An image item this is been used for the main view // Note if a compressed image is loaded ie BCn DDS file // We will still have a default image file that is used as a temp - // The temp file is loaded via resource file ie: ":/CompressonatorGUI/Images/CompressedImageError.png" + // The temp file is loaded via resource file ie: ":/compressonatorgui/images/compressedimageerror.png" //========================== - m_ImageIndex = 0; - QImage *image_original = NULL; + m_ImageIndex = 0; + QImage* image_original = NULL; QPixmap pixmap_original; - if (m_OriginalMipImages) { - image_original = m_OriginalMipImages->QImage_list[m_DepthIndex][m_ImageIndex]; - pixmap_original = QPixmap::fromImage(*image_original); - m_imageItem_Original = new acCustomGraphicsImageItem(pixmap_original, NULL); + if (m_OriginalMipImages) + { + image_original = m_OriginalMipImages->QImage_list[m_DepthIndex][m_ImageIndex]; + pixmap_original = QPixmap::fromImage(*image_original); + m_imageItem_Original = new acCustomGraphicsImageItem(pixmap_original, NULL); m_imageItem_Original->ID = m_graphicsScene->ID; m_imageItem_Original->setFlags(QGraphicsItem::ItemIsSelectable); m_graphicsScene->addItem(m_imageItem_Original); m_imageItem_Original->setVisible(false); - } else + } + else m_imageItem_Original = NULL; + QImage* image_processed = m_MipImages->QImage_list[m_DepthIndex][m_ImageIndex]; - QImage *image_processed = m_MipImages->QImage_list[m_DepthIndex][m_ImageIndex]; - QPixmap pixmap_processed = QPixmap::fromImage(*image_processed); + if (image_processed != NULL) + { + QPixmap pixmap_processed = QPixmap::fromImage(*image_processed); - m_imageItem_Processed = new acCustomGraphicsImageItem(pixmap_processed, image_original); - m_imageItem_Processed->ID = m_graphicsScene->ID; - m_imageItem_Processed->setFlags(QGraphicsItem::ItemIsSelectable); - m_graphicsScene->addItem(m_imageItem_Processed); - m_imageItem_Processed->setVisible(true); + m_imageItem_Processed = new acCustomGraphicsImageItem(pixmap_processed, image_original); + m_imageItem_Processed->ID = m_graphicsScene->ID; + m_imageItem_Processed->setFlags(QGraphicsItem::ItemIsSelectable); + m_graphicsScene->addItem(m_imageItem_Processed); + } + m_imageItem_Processed->setVisible(true); #ifdef ENABLE_NAVIGATION // Copy of the image view item (ToDo->Scale down to fit a smalled size) // it will be positioned to bottom left corner of the graphics view // and hidden by default, the view toggles on or off based on when // a navigateButton is clicked - m_imageItemNav = new acCustomGraphicsNavImageItem(QPixmap::fromImage((*image))); + m_imageItemNav = new acCustomGraphicsNavImageItem(QPixmap::fromImage((*image))); m_imageItemNav->ID = m_graphicsScene->ID; m_imageItemNav->setVisible(false); m_imageItemNav->setFlags(QGraphicsItem::ItemIsSelectable); m_graphicsScene->addItem(m_imageItemNav); m_navigateButton = new QPushButton(); - if (m_MipImages->MIPS2QtFailed) { - QString PushButtonStyle("QPushButton {background: red; border:none; margin: 0px; padding: 0px } QPushButton:hover {border:1px solid black}"); + if (m_MipImages->MIPS2QtFailed) + { + QString PushButtonStyle( + "QPushButton {background: red; border:none; margin: 0px; padding: 0px } QPushButton:hover {border:1px solid black}"); m_navigateButton->setStyleSheet(PushButtonStyle); } @@ -152,8 +407,8 @@ acImageView::acImageView(const QString filePathName, QWidget *parent, CMipImages // 1. when its moved the view of the main image will pan to match the box area // 2. scale the size of the box to that of acImageView widget size // 3. Keep the bounds of the move within the navigation window - acCustomGraphicsNavWindow *m_navWindow = new acCustomGraphicsNavWindow(QRectF(0, 0, 50, 50), m_imageItemNav); - m_navWindow->ID = m_graphicsScene->ID; + acCustomGraphicsNavWindow* m_navWindow = new acCustomGraphicsNavWindow(QRectF(0, 0, 50, 50), m_imageItemNav); + m_navWindow->ID = m_graphicsScene->ID; m_navWindow->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable); m_navWindow->setOpacity(0.5); m_navWindow->setPen(QPen(Qt::black)); @@ -164,9 +419,11 @@ acImageView::acImageView(const QString filePathName, QWidget *parent, CMipImages //========================== // Add Error message to the scene if there is error //========================== - if (m_MipImages->errMsg != "") { + if (m_MipImages->errMsg != "") + { m_errMessage = new QLabel(); - if (m_errMessage != NULL) { + if (m_errMessage != NULL) + { QFont font = m_errMessage->font(); font.setBold(true); font.setItalic(true); @@ -181,7 +438,7 @@ acImageView::acImageView(const QString filePathName, QWidget *parent, CMipImages } // The widget viewer for all of the items - m_imageGraphicsView = new acCustomGraphicsView(); + m_imageGraphicsView = new acCustomGraphicsView(); m_imageGraphicsView->ID = m_graphicsScene->ID; m_imageGraphicsView->setVisible(false); m_imageGraphicsView->setScene(m_graphicsScene); @@ -195,7 +452,8 @@ acImageView::acImageView(const QString filePathName, QWidget *parent, CMipImages m_imageGraphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); m_xPos = m_imageGraphicsView->horizontalScrollBar(); - if (m_xPos) { + if (m_xPos) + { // These values should be refreshed each time the acImageView is resized // for now we are obtaining the constructors defaults m_defaultXPos_minimum = m_xPos->minimum(); @@ -203,7 +461,8 @@ acImageView::acImageView(const QString filePathName, QWidget *parent, CMipImages } m_yPos = m_imageGraphicsView->verticalScrollBar(); - if (m_yPos) { + if (m_yPos) + { // These values should be refreshed each time the acImageView is resized // for now we are obtaining the constructors defaults m_defaultYPos_minimum = m_yPos->minimum(); @@ -211,13 +470,13 @@ acImageView::acImageView(const QString filePathName, QWidget *parent, CMipImages } // Public : Events from acCustomGraphics - connect(m_graphicsScene, SIGNAL(sceneMousePosition(QPointF *, int)), this, SLOT(onacImageViewMousePosition(QPointF *, int))); + connect(m_graphicsScene, SIGNAL(sceneMousePosition(QPointF*, int)), this, SLOT(onacImageViewMousePosition(QPointF*, int))); connect(m_imageGraphicsView, SIGNAL(resetImageView()), this, SLOT(onResetImageView())); connect(m_imageGraphicsView, SIGNAL(OnMouseHandDown()), this, SLOT(onMouseHandDown())); connect(m_imageGraphicsView, SIGNAL(OnMouseHandD()), this, SLOT(onMouseHandD())); - connect(m_imageGraphicsView, SIGNAL(OnWheelScaleUp(QPointF &)), this, SLOT(onWheelScaleUp(QPointF &))); - connect(m_imageGraphicsView, SIGNAL(OnWheelScaleDown(QPointF &)), this, SLOT(onWheelScaleDown(QPointF &))); + connect(m_imageGraphicsView, SIGNAL(OnWheelScaleUp(QPointF&)), this, SLOT(onWheelScaleUp(QPointF&))); + connect(m_imageGraphicsView, SIGNAL(OnWheelScaleDown(QPointF&)), this, SLOT(onWheelScaleDown(QPointF&))); #ifdef ENABLE_NAVIGATION connect(m_navigateButton, SIGNAL(released()), this, SLOT(onNavigateClicked())); @@ -227,13 +486,12 @@ acImageView::acImageView(const QString filePathName, QWidget *parent, CMipImages // Add a OpenGL Widget if needed // to the GraphicsView //=============================== - if ( - m_MipImages - && - (m_MipImages->m_MipImageFormat == MIPIMAGE_FORMAT::Format_OpenGL) - ) { - if (m_MipImages->mipset) { - if (m_MipImages->mipset->m_compressed) { + if (m_MipImages && (m_MipImages->m_MipImageFormat == MIPIMAGE_FORMAT::Format_OpenGL)) + { + if (m_MipImages->mipset) + { + if (m_MipImages->mipset->m_compressed) + { //Reserved: GPUDecode } } @@ -243,12 +501,14 @@ acImageView::acImageView(const QString filePathName, QWidget *parent, CMipImages // Shift the position of the // added error message //========================== - if (m_MipImages->errMsg != "") { - QGraphicsItem * m_errItem = m_imageGraphicsView->itemAt(0, 0); + if (m_MipImages->errMsg != "") + { + QGraphicsItem* m_errItem = m_imageGraphicsView->itemAt(0, 0); if (m_errItem) - m_errItem->moveBy(size.width()/4, size.height()/4); + m_errItem->moveBy(size.width() / 4, size.height() / 4); - if (m_errMessage) { + if (m_errMessage) + { m_imageItem_Processed->setVisible(false); if (m_imageItem_Original) m_imageItem_Original->setVisible(false); @@ -260,28 +520,32 @@ acImageView::acImageView(const QString filePathName, QWidget *parent, CMipImages // to the GraphicsView //========================== - if (m_MipImages->m_Error == MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) { + if ((m_MipImages->m_Error == MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) && image_processed) + { QRect ImageSize = image_processed->rect(); - m_myModel = new acTableImageDataModel(ImageSize.height(), ImageSize.width(), this); + m_myModel = new acTableImageDataModel(ImageSize.height(), ImageSize.width(), this); m_tableView = new QTableView(this); m_tableView->setAutoScroll(true); m_tableView->setModel(m_myModel); - QWidget *newWidget = new QWidget(); - QHBoxLayout *layout = new QHBoxLayout(); + QWidget* newWidget = new QWidget(); + QHBoxLayout* layout = new QHBoxLayout(); layout->addWidget(m_tableView); newWidget->setLayout(layout); m_graphicsScene->addWidget(newWidget); m_tableViewitem = m_imageGraphicsView->itemAt(0, 0); - if (m_tableViewitem) { + if (m_tableViewitem) + { m_tableViewitem->setOpacity(0.75); m_tableViewitem->hide(); } - } else { + } + else + { m_tableViewitem = NULL; - m_tableView = NULL; + m_tableView = NULL; } //=============================== @@ -303,7 +567,7 @@ acImageView::acImageView(const QString filePathName, QWidget *parent, CMipImages m_linex->hide(); m_liney->hide(); - m_rectBlocks = new QGraphicsRectItem(0,0, m_graphicsScene->cursorBlockX, m_graphicsScene->cursorBlockY); + m_rectBlocks = new QGraphicsRectItem(0, 0, m_graphicsScene->cursorBlockX, m_graphicsScene->cursorBlockY); m_graphicsScene->addItem(m_rectBlocks); m_rectBlocks->setPen(pen); m_rectBlocks->hide(); @@ -315,40 +579,49 @@ acImageView::acImageView(const QString filePathName, QWidget *parent, CMipImages ManageScrollBars(); } + } } setLayout(m_layout); } - -int acImageView::getBrightnessLevel() { +int acImageView::getBrightnessLevel() +{ return m_imageItem_Processed->m_iBrightness; } -void acImageView::setBrightnessLevel(int brightness) { +void acImageView::setBrightnessLevel(int brightness) +{ // Check! - if (!m_imageItem_Processed) return; + if (!m_imageItem_Processed) + return; if (m_MipImages) - if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) return; + if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) + return; m_imageItem_Processed->m_iBrightness = brightness; - if (m_appBusy) return; - m_appBusy = true; + if (m_appBusy) + return; + m_appBusy = true; m_imageItem_Processed->m_UseProcessedImage = true; - m_imageItem_Processed->m_ImageBrightness = true; + m_imageItem_Processed->m_ImageBrightness = true; m_imageItem_Processed->UpdateImage(); m_imageItem_Processed->m_UseProcessedImage = false; - m_appBusy = false; + m_appBusy = false; } -void acImageView::enableNavigation(bool enable) { +void acImageView::enableNavigation(bool enable) +{ Q_UNUSED(enable); #ifdef ENABLE_NAVIGATION - if (enable) { + if (enable) + { m_navigateButton->show(); - } else { + } + else + { m_navigateButton->hide(); } #endif @@ -361,13 +634,14 @@ void acImageView::enableNavigation(bool enable) { // Return Val: void // Date: 4/9/2015 // --------------------------------------------------------------------------- -void acImageView::onMouseDoubleClickEvent(QMouseEvent * e) { +void acImageView::onMouseDoubleClickEvent(QMouseEvent* e) +{ //qDebug() << __FUNCTION__ << " MouseEvent "; Q_UNUSED(e); } - -void acImageView::showVirtualCursor() { +void acImageView::showVirtualCursor() +{ m_linex->show(); m_liney->show(); m_rectBlocks->show(); @@ -376,7 +650,8 @@ void acImageView::showVirtualCursor() { #endif } -void acImageView::hideVirtualCursor() { +void acImageView::hideVirtualCursor() +{ m_linex->hide(); m_liney->hide(); m_rectBlocks->hide(); @@ -386,83 +661,100 @@ void acImageView::hideVirtualCursor() { } // ToDo check for m_imageItem_Original -void acImageView::showTableView(bool display) { - if (display) { +void acImageView::showTableView(bool display) +{ + if (display) + { m_imageItem_Processed->hide(); m_tableViewitem->show(); - } else { + } + else + { m_tableViewitem->hide(); m_imageItem_Processed->show(); } } #ifdef _DEBUG -void acImageView::onToggleDebugChanged(int index) { - switch (index) { +void acImageView::onToggleDebugChanged(int index) +{ + switch (index) + { case 0: - m_debugMode = false; + m_debugMode = false; m_debugFormat = ""; - if (m_graphicsScene) { + if (m_graphicsScene) + { m_graphicsScene->isDebug = false; } break; - case 1: //BC6H - m_debugMode = true; + case 1: //BC6H + m_debugMode = true; m_debugFormat = "BC6H"; - if (m_graphicsScene) { + if (m_graphicsScene) + { m_graphicsScene->isDebug = true; } break; - case 2: //BC6H_SF - m_debugMode = true; + case 2: //BC6H_SF + m_debugMode = true; m_debugFormat = "BC6H_SF"; - if (m_graphicsScene) { + if (m_graphicsScene) + { m_graphicsScene->isDebug = true; } break; - case 3: //BC7 - m_debugMode = true; + case 3: //BC7 + m_debugMode = true; m_debugFormat = "BC7"; - if (m_graphicsScene) { + if (m_graphicsScene) + { m_graphicsScene->isDebug = true; } break; default: - m_debugMode = false; + m_debugMode = false; m_debugFormat = ""; - if (m_graphicsScene) { + if (m_graphicsScene) + { m_graphicsScene->isDebug = false; } break; } - - } #endif -void acImageView::onToggleImageViews(int index) { +void acImageView::onToggleImageViews(int index) +{ // View Processed - if (index == 0) { + if (index == 0) + { // Use Current position of Original image to set position of Processed image view MatchImagePosition(1); // if there is error, view is not visible - if (m_errMessage) { + if (m_errMessage) + { m_errMessage->setVisible(true); m_imageItem_Processed->setVisible(false); if (m_imageItem_Original) m_imageItem_Original->setVisible(false); - } else { + } + else + { // Make Visible the Processed Image and Hide the Original m_imageItem_Processed->setVisible(true); if (m_imageItem_Original) m_imageItem_Original->setVisible(false); } - } else { + } + else + { // Use Current position of Processed image to set position of Original image view MatchImagePosition(0); // Hide processed view error when view original - if (m_errMessage) { + if (m_errMessage) + { m_errMessage->setVisible(false); } @@ -473,34 +765,47 @@ void acImageView::onToggleImageViews(int index) { } } -void acImageView::MatchImagePosition(int activeIndex) { +void acImageView::MatchImagePosition(int activeIndex) +{ // Check! - if (!m_graphicsScene) return; - if (!m_imageItem_Processed) return; - if (!m_imageItem_Original) return; - - if (activeIndex == 0) { // Processed Image - QPointF pos = m_imageItem_Processed->pos(); // Current image size and position user is viewing + if (!m_graphicsScene) + return; + if (!m_imageItem_Processed) + return; + if (!m_imageItem_Original) + return; + + if (activeIndex == 0) + { // Processed Image + QPointF pos = m_imageItem_Processed->pos(); // Current image size and position user is viewing m_imageItem_Original->setPos(pos); - } else { // We are viewing the Original Image - QPointF pos = m_imageItem_Original->pos(); // Current image size and position user is viewing + } + else + { // We are viewing the Original Image + QPointF pos = m_imageItem_Original->pos(); // Current image size and position user is viewing m_imageItem_Processed->setPos(pos); } - } -void acImageView::onVirtualMouseMoveEvent(QPointF *scenePos, QPointF *localPos, int onID) { - if (m_MouseHandDown) return; - if (m_MipImages == NULL) return; +void acImageView::onVirtualMouseMoveEvent(QPointF* scenePos, QPointF* localPos, int onID) +{ + if (m_MouseHandDown) + return; + if (m_MipImages == NULL) + return; - if (scenePos) { + if (scenePos) + { //qDebug() << "*** acImageView::onVirtualMouseMoveEvent ID " << m_graphicsScene->ID << " onID " << onID << " sx : " << scenePos->rx() << " sy: " << scenePos->ry(); // Tracking cursor - maps mouse pos from multiple acImageView items // Is the real mouse on the actual image or off the image and out of bounds - if ((onID == m_graphicsScene->ID) || (localPos == NULL)) { + if ((onID == m_graphicsScene->ID) || (localPos == NULL)) + { hideVirtualCursor(); - } else { - QPoint imageloc = localPos->toPoint(); + } + else + { + QPoint imageloc = localPos->toPoint(); QRectF boundImage = m_imageItem_Processed->boundingRect(); QRectF boundScene = m_imageItem_Processed->sceneBoundingRect(); @@ -513,7 +818,7 @@ void acImageView::onVirtualMouseMoveEvent(QPointF *scenePos, QPointF *localPos, scaleY = boundScene.height() / boundImage.height(); QPointF imagePt = boundScene.topLeft(); - QPoint point = imagePt.toPoint(); + QPoint point = imagePt.toPoint(); int X = point.x() + (imageloc.x() * scaleX); int Y = point.y() + (imageloc.y() * scaleY); @@ -523,7 +828,8 @@ void acImageView::onVirtualMouseMoveEvent(QPointF *scenePos, QPointF *localPos, m_linex->setPos(X - CURSOR_SIZE, Y); m_liney->setPos(X, Y - CURSOR_SIZE); - if (m_tableViewitem) { + if (m_tableViewitem) + { m_tableView->rowAt(imageloc.y()); m_tableView->columnAt(imageloc.x()); } @@ -532,8 +838,9 @@ void acImageView::onVirtualMouseMoveEvent(QPointF *scenePos, QPointF *localPos, } #ifdef _DEBUG - if (m_debugMode) { - QPoint imageloc = localPos->toPoint(); + if (m_debugMode) + { + QPoint imageloc = localPos->toPoint(); QRectF boundImage = m_imageItem_Processed->boundingRect(); QRectF boundScene = m_imageItem_Processed->sceneBoundingRect(); @@ -546,7 +853,7 @@ void acImageView::onVirtualMouseMoveEvent(QPointF *scenePos, QPointF *localPos, scaleY = boundScene.height() / boundImage.height(); QPointF imagePt = boundScene.topLeft(); - QPoint point = imagePt.toPoint(); + QPoint point = imagePt.toPoint(); int X = point.x() + (imageloc.x() * scaleX); int Y = point.y() + (imageloc.y() * scaleY); @@ -565,15 +872,14 @@ void acImageView::onVirtualMouseMoveEvent(QPointF *scenePos, QPointF *localPos, m_rectBlocks->setRect(X - (cursorXsize / 2), Y - (cursorYsize / 2), cursorXsize, cursorYsize); } #endif - } - } -bool acImageView::IsImageBoundedToView(QPointF *mousePos) { +bool acImageView::IsImageBoundedToView(QPointF* mousePos) +{ UNREFERENCED_PARAMETER(mousePos); - QPointF DeltaPos{ 0,0 }; + QPointF DeltaPos{0, 0}; // if (mousePos) // { @@ -583,8 +889,8 @@ bool acImageView::IsImageBoundedToView(QPointF *mousePos) { // } // Check Image is in bound of View - QRectF bounds = m_graphicsScene->sceneRect(); // Size of our Scene view - QRectF imageBounds = m_imageItem_Processed->sceneBoundingRect(); // Current image size and position user is viewing + QRectF bounds = m_graphicsScene->sceneRect(); // Size of our Scene view + QRectF imageBounds = m_imageItem_Processed->sceneBoundingRect(); // Current image size and position user is viewing bool bounded = false; @@ -608,28 +914,34 @@ bool acImageView::IsImageBoundedToView(QPointF *mousePos) { // Return Val: void // Date: 4/9/2015 // --------------------------------------------------------------------------- -void acImageView::onacImageViewMousePosition(QPointF *scenePos, int ID) { - if (m_MouseHandDown) return; +void acImageView::onacImageViewMousePosition(QPointF* scenePos, int ID) +{ + if (m_MouseHandDown) + return; //qDebug() << " =============== START OF MOUSE MOVE EVENT =================" << " rx : " << scenePos->rx() << " ry: " << scenePos->ry(); - QGraphicsItem *itemPicked; + QGraphicsItem* itemPicked; QPointF localPt; itemPicked = m_graphicsScene->itemAt(scenePos->rx(), scenePos->ry(), QTransform()); // is mouse inside image view - if (itemPicked) { - localPt = itemPicked->mapFromScene((const QPointF &)*scenePos); + if (itemPicked) + { + localPt = itemPicked->mapFromScene((const QPointF&)*scenePos); //qDebug() << "item at rx : " << localPt.rx() << " ry: " << localPt.ry(); - if (m_graphicsScene->isDebug) { + if (m_graphicsScene->isDebug) + { // napatel int x = qRound(localPt.rx() - 0.5f); - if (x < 0) x = 0; + if (x < 0) + x = 0; int y = qRound(localPt.ry() - 0.5f); - if (y < 0) y = 0; + if (y < 0) + y = 0; x = (x / m_graphicsScene->cursorBlockX); y = (y / m_graphicsScene->cursorBlockY); @@ -638,25 +950,31 @@ void acImageView::onacImageViewMousePosition(QPointF *scenePos, int ID) { localPt.setY(qreal(y)); } emit acImageViewMousePosition(scenePos, &localPt, ID); - } else { + } + else + { // mouse is outside of an image view emit acImageViewMousePosition(scenePos, NULL, ID); } - if (m_MouseHandDown) { - if (m_imageItem_Processed) { - if (!IsImageBoundedToView(scenePos)) { + if (m_MouseHandDown) + { + if (m_imageItem_Processed) + { + if (!IsImageBoundedToView(scenePos)) + { m_imageItem_Processed->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable); if (m_imageItem_Original) m_imageItem_Original->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable); - } else { + } + else + { m_imageItem_Processed->setFlags(QGraphicsItem::ItemIsSelectable); if (m_imageItem_Original) m_imageItem_Original->setFlags(QGraphicsItem::ItemIsSelectable); } } } - } // --------------------------------------------------------------------------- @@ -667,21 +985,24 @@ void acImageView::onacImageViewMousePosition(QPointF *scenePos, int ID) { // Return Val: void // Date: 4/9/2015 // --------------------------------------------------------------------------- -void acImageView::resizeEvent(QResizeEvent *e) { +void acImageView::resizeEvent(QResizeEvent* e) +{ Q_UNUSED(e); //qDebug() << __FUNCTION__; - QSize size = this->size(); + QSize size = this->size(); //qDebug() << "Size H:" << size.height() << " W:" << size.width(); // Update the scene to fit window - if (m_graphicsScene) { + if (m_graphicsScene) + { QRectF bounds = m_graphicsScene->itemsBoundingRect(); m_graphicsScene->setSceneRect(0, 0, size.width(), size.height()); } // get the image bounding size and use it to center our Image to widget windows - if (m_imageItem_Processed) { + if (m_imageItem_Processed) + { centerImage(); } @@ -689,7 +1010,8 @@ void acImageView::resizeEvent(QResizeEvent *e) { #ifdef ENABLE_NAVIGATION // Move the navigate button - if (m_navigateButton) { + if (m_navigateButton) + { // Note we are clipping the button to the bottom left corner of the view // and not showing all of it: only the top left corner of the button // it has a nice visual effect of a small corner box. We can change this to @@ -699,16 +1021,19 @@ void acImageView::resizeEvent(QResizeEvent *e) { // If the Navigation image is been viewed then also // move it tp fix the corner - if (m_imageItemNav) { + if (m_imageItemNav) + { QSizeF sizeF = m_imageItemNav->boundingRect().size(); m_imageItemNav->setPos(size.width() - sizeF.width(), size.height() - sizeF.width()); } } #endif - if (m_tableViewitem) { + if (m_tableViewitem) + { //m_tableViewitem->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable); - if (m_graphicsScene) { + if (m_graphicsScene) + { // Scaling an item to fit view QRectF bound = m_graphicsScene->itemsBoundingRect(); // // QRect itemSize = cpView.m_imageGraphicsView->mapFromScene(itemPicked->sceneBoundingRect()).boundingRect(); @@ -724,9 +1049,6 @@ void acImageView::resizeEvent(QResizeEvent *e) { m_tableView->setMinimumHeight(rec.height()); } } - - - } // --------------------------------------------------------------------------- @@ -737,15 +1059,21 @@ void acImageView::resizeEvent(QResizeEvent *e) { // Return Val: void // Date: 4/9/2015 // --------------------------------------------------------------------------- -void acImageView::onNavigateClicked() { +void acImageView::onNavigateClicked() +{ #ifdef ENABLE_NAVIGATION - if (m_navVisible) { - if (m_imageItemNav) { + if (m_navVisible) + { + if (m_imageItemNav) + { m_imageItemNav->setVisible(false); m_navVisible = false; } - } else { - if (m_imageItemNav) { + } + else + { + if (m_imageItemNav) + { m_imageItemNav->setVisible(true); m_navVisible = true; } @@ -761,21 +1089,24 @@ void acImageView::onNavigateClicked() { // Return Val: void // Date: 4/9/2015 // --------------------------------------------------------------------------- -void acImageView::onResetImageView() { +void acImageView::onResetImageView() +{ if (m_MipImages) - if (m_MipImages->m_Error == MIPIMAGE_FORMAT_ERRORS::Format_InvalidFile) return; + if (m_MipImages->m_Error == MIPIMAGE_FORMAT_ERRORS::Format_InvalidFile) + return; - QSize size = this->size(); + QSize size = this->size(); // Update the scene to fit window - if (m_graphicsScene) { + if (m_graphicsScene) + { QRectF bounds = m_graphicsScene->itemsBoundingRect(); m_graphicsScene->setSceneRect(0, 0, size.width(), size.height()); } - // get the image use it to fit it to widget windows - if (m_imageItem_Processed) { + if (m_imageItem_Processed) + { // Transformation point m_imageItem_Processed->setTransformOriginPoint(0, 0); if (m_imageItem_Original) @@ -784,19 +1115,19 @@ void acImageView::onResetImageView() { // Set image properties back to defaults m_imageItem_Processed->setDefaults(); m_imageItem_Processed->m_UseProcessedImage = true; - m_appBusy = true; + m_appBusy = true; m_imageItem_Processed->UpdateImage(); m_appBusy = false; - if (m_imageItem_Original) { + if (m_imageItem_Original) + { m_imageItem_Original->setDefaults(); m_imageItem_Original->m_UseProcessedImage = true; - m_appBusy = true; + m_appBusy = true; m_imageItem_Original->UpdateImage(); m_appBusy = false; } - onViewImageOriginalSize(); m_imageOrientation = 0; @@ -811,203 +1142,237 @@ void acImageView::onResetImageView() { // ToDo Reset all action checked ToolButtons back to default state } -void acImageView::onExrExposureChanged(double value) { +void acImageView::onExrExposureChanged(double value) +{ // Check! - if (!m_imageItem_Processed) return; + if (!m_imageItem_Processed) + return; if (m_MipImages) - if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) return; - if (!m_imageloader) return; + if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) + return; + if (!m_imageloader) + return; m_imageloader->m_options.fInputExposure = float(value); QImage image((m_imageItem_Processed->pixmap()).toImage()); if (m_MipImages->mipset->m_compressed) - loadExrProperties(m_imageloader->getCMips(), m_MipImages->decompressedMipSet, m_currentMiplevel, &image, m_imageloader->m_options); + acloadExrProperties(m_imageloader->getCMips(), m_MipImages->decompressedMipSet, m_currentMiplevel, &image, m_imageloader->m_options); else - loadExrProperties(m_imageloader->getCMips(), m_MipImages->mipset, m_currentMiplevel, &image, m_imageloader->m_options); + acloadExrProperties(m_imageloader->getCMips(), m_MipImages->mipset, m_currentMiplevel, &image, m_imageloader->m_options); m_imageItem_Processed->setPixmap(QPixmap::fromImage(image)); - } -void acImageView::onExrDefogChanged(double value) { +void acImageView::onExrDefogChanged(double value) +{ // Check! - if (!m_imageItem_Processed) return; + if (!m_imageItem_Processed) + return; if (m_MipImages) - if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) return; - if (!m_imageloader) return; + if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) + return; + if (!m_imageloader) + return; m_imageloader->m_options.fInputDefog = float(value); QImage image((m_imageItem_Processed->pixmap()).toImage()); if (m_MipImages->mipset->m_compressed) - loadExrProperties(m_imageloader->getCMips(), m_MipImages->decompressedMipSet, m_currentMiplevel, &image, m_imageloader->m_options); + acloadExrProperties(m_imageloader->getCMips(), m_MipImages->decompressedMipSet, m_currentMiplevel, &image, m_imageloader->m_options); else - loadExrProperties(m_imageloader->getCMips(), m_MipImages->mipset, m_currentMiplevel, &image, m_imageloader->m_options); + acloadExrProperties(m_imageloader->getCMips(), m_MipImages->mipset, m_currentMiplevel, &image, m_imageloader->m_options); m_imageItem_Processed->setPixmap(QPixmap::fromImage(image)); - } -void acImageView::onExrKneeLowChanged(double value) { +void acImageView::onExrKneeLowChanged(double value) +{ // Check! - if (!m_imageItem_Processed) return; + if (!m_imageItem_Processed) + return; if (m_MipImages) - if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) return; + if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) + return; - if (!m_imageloader) return; + if (!m_imageloader) + return; - m_imageloader->m_options.fInputKneeLow= float(value); + m_imageloader->m_options.fInputKneeLow = float(value); QImage image((m_imageItem_Processed->pixmap()).toImage()); if (m_MipImages->mipset->m_compressed) - loadExrProperties(m_imageloader->getCMips(), m_MipImages->decompressedMipSet, m_currentMiplevel, &image, m_imageloader->m_options); + acloadExrProperties(m_imageloader->getCMips(), m_MipImages->decompressedMipSet, m_currentMiplevel, &image, m_imageloader->m_options); else - loadExrProperties(m_imageloader->getCMips(), m_MipImages->mipset, m_currentMiplevel, &image, m_imageloader->m_options); + acloadExrProperties(m_imageloader->getCMips(), m_MipImages->mipset, m_currentMiplevel, &image, m_imageloader->m_options); m_imageItem_Processed->setPixmap(QPixmap::fromImage(image)); - } -void acImageView::onExrKneeHighChanged(double value) { +void acImageView::onExrKneeHighChanged(double value) +{ // Check! - if (!m_imageItem_Processed) return; + if (!m_imageItem_Processed) + return; if (m_MipImages) - if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) return; - if (!m_imageloader) return; + if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) + return; + if (!m_imageloader) + return; m_imageloader->m_options.fInputKneeHigh = float(value); QImage image((m_imageItem_Processed->pixmap()).toImage()); if (m_MipImages->mipset->m_compressed) - loadExrProperties(m_imageloader->getCMips(), m_MipImages->decompressedMipSet, m_currentMiplevel, &image, m_imageloader->m_options); + acloadExrProperties(m_imageloader->getCMips(), m_MipImages->decompressedMipSet, m_currentMiplevel, &image, m_imageloader->m_options); else - loadExrProperties(m_imageloader->getCMips(), m_MipImages->mipset, m_currentMiplevel, &image, m_imageloader->m_options); + acloadExrProperties(m_imageloader->getCMips(), m_MipImages->mipset, m_currentMiplevel, &image, m_imageloader->m_options); m_imageItem_Processed->setPixmap(QPixmap::fromImage(image)); - } -void acImageView::onExrGammaChanged(double value) { +void acImageView::onExrGammaChanged(double value) +{ // Check! - if (!m_imageItem_Processed) return; + if (!m_imageItem_Processed) + return; if (m_MipImages) - if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) return; + if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) + return; - if (!m_imageloader) return; + if (!m_imageloader) + return; m_imageloader->m_options.fInputGamma = float(value); QImage image((m_imageItem_Processed->pixmap()).toImage()); if (m_MipImages->mipset->m_compressed) - loadExrProperties(m_imageloader->getCMips(), m_MipImages->decompressedMipSet, m_currentMiplevel, &image, m_imageloader->m_options); + acloadExrProperties(m_imageloader->getCMips(), m_MipImages->decompressedMipSet, m_currentMiplevel, &image, m_imageloader->m_options); else - loadExrProperties(m_imageloader->getCMips(), m_MipImages->mipset, m_currentMiplevel, &image, m_imageloader->m_options); + acloadExrProperties(m_imageloader->getCMips(), m_MipImages->mipset, m_currentMiplevel, &image, m_imageloader->m_options); m_imageItem_Processed->setPixmap(QPixmap::fromImage(image)); - } -void acImageView::onToggleChannelR() { +void acImageView::onToggleChannelR() +{ // Check! - if (!m_imageItem_Processed) return; + if (!m_imageItem_Processed) + return; if (m_MipImages) - if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) return; + if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) + return; - if (m_appBusy) return; + if (m_appBusy) + return; m_appBusy = true; m_imageItem_Processed->m_UseProcessedImage = true; - m_imageItem_Processed->m_ChannelR = !m_imageItem_Processed->m_ChannelR; + m_imageItem_Processed->m_ChannelR = !m_imageItem_Processed->m_ChannelR; m_imageItem_Processed->UpdateImage(); m_imageItem_Processed->m_UseProcessedImage = false; m_appBusy = false; - } -void acImageView::onToggleChannelG() { +void acImageView::onToggleChannelG() +{ // Check! - if (!m_imageItem_Processed) return; + if (!m_imageItem_Processed) + return; if (m_MipImages) - if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) return; + if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) + return; - if (m_appBusy) return; + if (m_appBusy) + return; m_appBusy = true; m_imageItem_Processed->m_UseProcessedImage = true; - m_imageItem_Processed->m_ChannelG = !m_imageItem_Processed->m_ChannelG; + m_imageItem_Processed->m_ChannelG = !m_imageItem_Processed->m_ChannelG; m_imageItem_Processed->UpdateImage(); m_imageItem_Processed->m_UseProcessedImage = false; m_appBusy = false; - } -void acImageView::onToggleChannelB() { +void acImageView::onToggleChannelB() +{ // Check! - if (!m_imageItem_Processed) return; + if (!m_imageItem_Processed) + return; if (m_MipImages) - if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) return; + if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) + return; - if (m_appBusy) return; + if (m_appBusy) + return; m_appBusy = true; m_imageItem_Processed->m_UseProcessedImage = true; - m_imageItem_Processed->m_ChannelB = !m_imageItem_Processed->m_ChannelB; + m_imageItem_Processed->m_ChannelB = !m_imageItem_Processed->m_ChannelB; m_imageItem_Processed->UpdateImage(); m_imageItem_Processed->m_UseProcessedImage = false; m_appBusy = false; - } -void acImageView::onToggleChannelA() { +void acImageView::onToggleChannelA() +{ // Check! - if (!m_imageItem_Processed) return; + if (!m_imageItem_Processed) + return; if (m_MipImages) - if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) return; + if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) + return; - if (m_appBusy) return; + if (m_appBusy) + return; m_appBusy = true; m_imageItem_Processed->m_UseProcessedImage = true; - m_imageItem_Processed->m_ChannelA = !m_imageItem_Processed->m_ChannelA; + m_imageItem_Processed->m_ChannelA = !m_imageItem_Processed->m_ChannelA; m_imageItem_Processed->UpdateImage(); m_imageItem_Processed->m_UseProcessedImage = false; m_appBusy = false; - } -void acImageView::onToggleGrayScale() { +void acImageView::onToggleGrayScale() +{ // Check! - if (!m_imageItem_Processed) return; + if (!m_imageItem_Processed) + return; if (m_MipImages) - if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) return; - if (m_appBusy) return; + if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) + return; + if (m_appBusy) + return; m_appBusy = true; m_imageItem_Processed->m_UseProcessedImage = true; - m_imageItem_Processed->m_GrayScale = !m_imageItem_Processed->m_GrayScale; + m_imageItem_Processed->m_GrayScale = !m_imageItem_Processed->m_GrayScale; m_imageItem_Processed->UpdateImage(); m_imageItem_Processed->m_UseProcessedImage = false; - m_appBusy = false; - + m_appBusy = false; } -void acImageView::onInvertImage() { +void acImageView::onInvertImage() +{ // Check! - if (!m_imageItem_Processed) return; + if (!m_imageItem_Processed) + return; if (m_MipImages) - if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) return; + if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) + return; - if (m_appBusy) return; + if (m_appBusy) + return; m_appBusy = true; m_imageItem_Processed->m_InvertImage = true; @@ -1015,25 +1380,27 @@ void acImageView::onInvertImage() { m_imageItem_Processed->m_InvertImage = false; m_appBusy = false; - } -void acImageView::onMirrorHorizontal() { +void acImageView::onMirrorHorizontal() +{ // Check! - if (!m_imageItem_Processed) return; + if (!m_imageItem_Processed) + return; if (m_MipImages) - if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) return; + if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) + return; - if (m_appBusy) return; + if (m_appBusy) + return; m_appBusy = true; - // Transformation point m_imageItem_Processed->setTransformOriginPoint(0, 0); - m_imageItem_Processed->m_Mirrored = true; - m_imageItem_Processed->m_Mirrored_h = true; - m_imageItem_Processed->m_Mirrored_v = false; + m_imageItem_Processed->m_Mirrored = true; + m_imageItem_Processed->m_Mirrored_h = true; + m_imageItem_Processed->m_Mirrored_v = false; m_imageItem_Processed->m_ImageBrightness = false; m_imageItem_Processed->UpdateImage(); m_imageItem_Processed->m_Mirrored = false; @@ -1041,173 +1408,189 @@ void acImageView::onMirrorHorizontal() { m_appBusy = false; // Transformation point - if (m_imageItem_Original) { + if (m_imageItem_Original) + { m_imageItem_Original->setTransformOriginPoint(0, 0); - m_imageItem_Original->m_Mirrored = true; - m_imageItem_Original->m_Mirrored_h = true; - m_imageItem_Original->m_Mirrored_v = false; + m_imageItem_Original->m_Mirrored = true; + m_imageItem_Original->m_Mirrored_h = true; + m_imageItem_Original->m_Mirrored_v = false; m_imageItem_Original->m_ImageBrightness = false; - m_appBusy = true; + m_appBusy = true; m_imageItem_Original->UpdateImage(); - m_appBusy = false; + m_appBusy = false; m_imageItem_Original->m_Mirrored = false; } - - - } -void acImageView::onMirrorVirtical() { +void acImageView::onMirrorVirtical() +{ // Check! - if (!m_imageItem_Processed) return; + if (!m_imageItem_Processed) + return; if (m_MipImages) - if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) return; + if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) + return; - if (m_appBusy) return; + if (m_appBusy) + return; m_appBusy = true; // Transformation point m_imageItem_Processed->setTransformOriginPoint(0, 0); - m_imageItem_Processed->m_Mirrored = true; - m_imageItem_Processed->m_Mirrored_h = false; - m_imageItem_Processed->m_Mirrored_v = true; + m_imageItem_Processed->m_Mirrored = true; + m_imageItem_Processed->m_Mirrored_h = false; + m_imageItem_Processed->m_Mirrored_v = true; m_imageItem_Processed->m_ImageBrightness = false; m_imageItem_Processed->UpdateImage(); m_imageItem_Processed->m_Mirrored = false; - m_appBusy = false; + m_appBusy = false; - if (m_imageItem_Original) { + if (m_imageItem_Original) + { m_imageItem_Original->setTransformOriginPoint(0, 0); - m_imageItem_Original->m_Mirrored = true; - m_imageItem_Original->m_Mirrored_h = false; - m_imageItem_Original->m_Mirrored_v = true; + m_imageItem_Original->m_Mirrored = true; + m_imageItem_Original->m_Mirrored_h = false; + m_imageItem_Original->m_Mirrored_v = true; m_imageItem_Original->m_ImageBrightness = false; - m_appBusy = true; + m_appBusy = true; m_imageItem_Original->UpdateImage(); - m_appBusy = false; + m_appBusy = false; m_imageItem_Original->m_Mirrored = false; } - - - } -void acImageView::onRotateRight() { +void acImageView::onRotateRight() +{ // Check! - if (!m_imageItem_Processed) return; + if (!m_imageItem_Processed) + return; if (m_MipImages) - if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) return; - - QRectF bounds = m_imageItem_Processed->boundingRect(); // Images Size bounded within a rectangle - qreal x = bounds.width() / 2; - qreal y = bounds.height() / 2; - m_imageOrientation +=90; - if (m_imageOrientation >= 360) m_imageOrientation = 0; + if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) + return; + + QRectF bounds = m_imageItem_Processed->boundingRect(); // Images Size bounded within a rectangle + qreal x = bounds.width() / 2; + qreal y = bounds.height() / 2; + m_imageOrientation += 90; + if (m_imageOrientation >= 360) + m_imageOrientation = 0; m_imageItem_Processed->setTransformOriginPoint(x, y); m_imageItem_Processed->setRotation(m_imageOrientation); - if (m_imageItem_Original) { + if (m_imageItem_Original) + { m_imageItem_Original->setTransformOriginPoint(x, y); m_imageItem_Original->setRotation(m_imageOrientation); } centerImage(); - - } -void acImageView::onRotateLeft() { +void acImageView::onRotateLeft() +{ // Check! - if (!m_imageItem_Processed) return; + if (!m_imageItem_Processed) + return; if (m_MipImages) - if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) return; + if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) + return; m_imageOrientation -= 90; - if (m_imageOrientation < 0) m_imageOrientation = 270; + if (m_imageOrientation < 0) + m_imageOrientation = 270; - - QRectF bounds = m_imageItem_Processed->boundingRect(); // Images Size bounded within a rectangle - qreal x = bounds.width() / 2; - qreal y = bounds.height() / 2; + QRectF bounds = m_imageItem_Processed->boundingRect(); // Images Size bounded within a rectangle + qreal x = bounds.width() / 2; + qreal y = bounds.height() / 2; m_imageItem_Processed->setTransformOriginPoint(x, y); m_imageItem_Processed->setRotation(m_imageOrientation); - if (m_imageItem_Original) { + if (m_imageItem_Original) + { m_imageItem_Original->setTransformOriginPoint(x, y); m_imageItem_Original->setRotation(m_imageOrientation); } centerImage(); - } -void acImageView::onViewImageOriginalSize() { +void acImageView::onViewImageOriginalSize() +{ // Check! - if (!m_graphicsScene) return; - if (!m_imageItem_Processed) return; + if (!m_graphicsScene) + return; + if (!m_imageItem_Processed) + return; if (m_MipImages) - if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) return; + if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) + return; - QSize size = this->size(); + QSize size = this->size(); m_imageItem_Processed->setTransformOriginPoint(0, 0); m_imageItem_Processed->m_UseProcessedImage = true; m_imageItem_Processed->setScale(1.0); - - if (m_imageItem_Original) { + if (m_imageItem_Original) + { m_imageItem_Original->setTransformOriginPoint(0, 0); m_imageItem_Original->m_UseProcessedImage = true; m_imageItem_Original->setScale(1.0); } m_ImageScale = 100; - emit acScaleChanged(m_ImageScale); + emit acScaleChanged(m_ImageScale); #ifdef ENABLE_NAVIGATION QSizeF sizeF = m_imageItemNav->boundingRect().size(); m_imageItemNav->setPos(size.width() - sizeF.width(), size.height() - sizeF.width()); #endif - centerImage(); m_imageItem_Processed->m_UseProcessedImage = false; if (m_imageItem_Original) m_imageItem_Original->m_UseProcessedImage = false; - } -void acImageView::onSetPixelDiffView(bool OnOff) { +void acImageView::onSetPixelDiffView(bool OnOff) +{ // Check! - if (!m_graphicsScene) return; - if (!m_imageItem_Processed) return; + if (!m_graphicsScene) + return; + if (!m_imageItem_Processed) + return; - if (m_appBusy) return; + if (m_appBusy) + return; - m_appBusy = true; - m_imageItem_Processed->m_ShowPixelDiff = OnOff; - m_imageItem_Processed->m_UseProcessedImage = true; + m_appBusy = true; + m_imageItem_Processed->m_ShowPixelDiff = OnOff; + m_imageItem_Processed->m_UseProcessedImage = true; m_imageItem_Processed->UpdateImage(); - m_imageItem_Processed->m_UseProcessedImage = false; - m_appBusy = false; + m_imageItem_Processed->m_UseProcessedImage = false; + m_appBusy = false; } -void acImageView::centerImage() { -//#ifdef USE_MOVABLE_IMAGES +void acImageView::centerImage() +{ + //#ifdef USE_MOVABLE_IMAGES // Check! - if (!m_graphicsScene) return; - if (!m_imageItem_Processed) return; + if (!m_graphicsScene) + return; + if (!m_imageItem_Processed) + return; - QRectF boundsView = m_graphicsScene->sceneRect(); // Size of the view user sees the image in - m_imageItem_Processed->setTransformOriginPoint(0, 0); // Transformation point + QRectF boundsView = m_graphicsScene->sceneRect(); // Size of the view user sees the image in + m_imageItem_Processed->setTransformOriginPoint(0, 0); // Transformation point - QRectF bounds = m_imageItem_Processed->boundingRect(); // Original images size - QRectF offset = m_imageItem_Processed->sceneBoundingRect(); // Current image size and position user is viewing + QRectF bounds = m_imageItem_Processed->boundingRect(); // Original images size + QRectF offset = m_imageItem_Processed->sceneBoundingRect(); // Current image size and position user is viewing // if original item exist use original item for bound and offset - if (m_imageItem_Original) { + if (m_imageItem_Original) + { bounds = m_imageItem_Original->boundingRect(); offset = m_imageItem_Original->sceneBoundingRect(); } @@ -1223,47 +1606,52 @@ void acImageView::centerImage() { // so that is viewed in center QTransform translation; translation.translate(dx, dy); - m_imageItem_Processed->setTransform(translation,true); -//#endif + m_imageItem_Processed->setTransform(translation, true); + //#endif - if (m_imageItem_Original) { + if (m_imageItem_Original) + { m_imageItem_Original->setTransformOriginPoint(0, 0); - m_imageItem_Original->setTransform(translation,true); + m_imageItem_Original->setTransform(translation, true); } - } -void acImageView::onFitInWindow() { +void acImageView::onFitInWindow() +{ // Check! - if (!m_graphicsScene) return; - if (!m_imageItem_Processed) return; + if (!m_graphicsScene) + return; + if (!m_imageItem_Processed) + return; if (m_MipImages) - if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) return; - - + if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) + return; // Scale the image to fit scene view - QRectF bounds = m_graphicsScene->sceneRect(); // Size of our Scene view + QRectF bounds = m_graphicsScene->sceneRect(); // Size of our Scene view QSizeF sizeF = m_imageItem_Processed->boundingRect().size(); // Actual size of the image // if original exist, use original actual size - if (m_imageItem_Original) { + if (m_imageItem_Original) + { sizeF = m_imageItem_Original->boundingRect().size(); // Actual size of the image } qreal sceneW = bounds.width(); qreal sceneH = bounds.height(); - qreal itemW = sizeF.width(); - qreal itemH = sizeF.height(); - qreal scale = 1.0; + qreal itemW = sizeF.width(); + qreal itemH = sizeF.height(); + qreal scale = 1.0; qreal scaleW = 1.0; - if (itemW > 0) { + if (itemW > 0) + { scaleW = sceneW / itemW; } qreal scaleH = 1.0; - if (itemH > 0) { + if (itemH > 0) + { scaleH = sceneH / itemH; } @@ -1272,25 +1660,25 @@ void acImageView::onFitInWindow() { else scale = scaleH; - m_ImageScale = (int) (scale * 100); + m_ImageScale = (int)(scale * 100); // Transformation point m_imageItem_Processed->setTransformOriginPoint(0, 0); m_imageItem_Processed->setScale(scale); - if (m_imageItem_Original) { + if (m_imageItem_Original) + { m_imageItem_Original->setTransformOriginPoint(0, 0); m_imageItem_Original->setScale(scale); } - - emit acScaleChanged(m_ImageScale); + emit acScaleChanged(m_ImageScale); centerImage(); } -void acImageView::onGridBackground(int enableGrid) { - +void acImageView::onGridBackground(int enableGrid) +{ eCustomGraphicsScene_Grids enable = (eCustomGraphicsScene_Grids)enableGrid; if (m_graphicsScene->isGridEnabled()) @@ -1298,39 +1686,60 @@ void acImageView::onGridBackground(int enableGrid) { else m_graphicsScene->gridEnabled(enable); m_graphicsScene->update(); - } -void acImageView::processPSNR() { +// This can be called multiple time when used with more then 1 image view, like that used in Image DIff views +// the code should be optimized so that the PSNR calc can be turned on of off during contruct or viewing +void acImageView::processPSNR() +{ // Is the calculation for the first time then process the data - if (m_PSNR[m_currentMiplevel][m_DepthIndex] == 0.0) { - CMP_DOUBLE outMSE; - CMP_DOUBLE outPSNR; - if (CMP_CalcMipSetMSE_PSNR(m_OriginalMipImages->mipset, m_MipImages->decompressedMipSet, m_currentMiplevel, m_DepthIndex, &outMSE, &outPSNR) == CMP_OK) { - m_MSE[m_currentMiplevel][m_DepthIndex] = outMSE; - m_PSNR[m_currentMiplevel][m_DepthIndex] = outPSNR; - } else - return; // should post an error message to user! + if (m_PSNR[m_currentMiplevel][m_DepthIndex] == 0.0) + { + if (m_OriginalMipImages == NULL) + return; + if (m_MipImages == NULL) + return; + if (m_MipImages->decompressedMipSet == NULL) + return; + + CMP_DOUBLE outMSE; + CMP_DOUBLE outPSNR; + CMP_AnalysisData anlysisData = {0}; + anlysisData.channelBitMap = CMP_getFormat_nChannels(m_MipImages->mipset->m_format); // m_MipImages->decompressedMipSet->m_format); + if (CMP_MipSetAnlaysis(m_OriginalMipImages->mipset, m_MipImages->decompressedMipSet, m_currentMiplevel, m_DepthIndex, &anlysisData) == CMP_OK) + { + m_MSE[m_currentMiplevel][m_DepthIndex] = anlysisData.mse; + m_PSNR[m_currentMiplevel][m_DepthIndex] = anlysisData.psnr; + } + else + return; // should post an error message to user! } // signal the data to all slots acPSNRUpdated(m_PSNR[m_currentMiplevel][m_DepthIndex]); } -void acImageView::onImageMipLevelChanged(int MipLevel) { +void acImageView::onImageMipLevelChanged(int MipLevel) +{ m_currentMiplevel = MipLevel; - if (m_MipImages) { - if (m_MipImages->QImage_list[m_DepthIndex].size() > MipLevel) { - m_ImageIndex = MipLevel; - QImage *image = m_MipImages->QImage_list[m_DepthIndex][m_ImageIndex]; - if (image) { + if (m_MipImages) + { + if (m_MipImages->QImage_list[m_DepthIndex].size() > MipLevel) + { + m_ImageIndex = MipLevel; + QImage* image = m_MipImages->QImage_list[m_DepthIndex][m_ImageIndex]; + if (image) + { m_imageItem_Processed->changeImage(*image); } - if (m_OriginalMipImages) { - if (m_OriginalMipImages->QImage_list[m_DepthIndex].size() > MipLevel) { - QImage *image_original = m_OriginalMipImages->QImage_list[m_DepthIndex][m_ImageIndex]; - if (image_original) { + if (m_OriginalMipImages) + { + if (m_OriginalMipImages->QImage_list[m_DepthIndex].size() > MipLevel) + { + QImage* image_original = m_OriginalMipImages->QImage_list[m_DepthIndex][m_ImageIndex]; + if (image_original) + { m_imageItem_Original->changeImage(*image_original); m_imageItem_Processed->changeImageDiffRef(image_original); } @@ -1345,18 +1754,24 @@ void acImageView::onImageMipLevelChanged(int MipLevel) { } } -void acImageView::onImageDepthChanged(int DepthLevel) { - if (DepthLevel >= CMP_MIPSET_MAX_DEPTHS) DepthLevel = CMP_MIPSET_MAX_DEPTHS-1; - if (m_MipImages) { - m_DepthIndex = DepthLevel; - QImage *image = m_MipImages->QImage_list[DepthLevel][m_ImageIndex]; - if (image) { +void acImageView::onImageDepthChanged(int DepthLevel) +{ + if (DepthLevel >= CMP_MIPSET_MAX_DEPTHS) + DepthLevel = CMP_MIPSET_MAX_DEPTHS - 1; + if (m_MipImages) + { + m_DepthIndex = DepthLevel; + QImage* image = m_MipImages->QImage_list[DepthLevel][m_ImageIndex]; + if (image) + { m_imageItem_Processed->changeImage(*image); } - if (m_OriginalMipImages) { - QImage *image_original = m_OriginalMipImages->QImage_list[DepthLevel][m_ImageIndex]; - if (image_original) { + if (m_OriginalMipImages) + { + QImage* image_original = m_OriginalMipImages->QImage_list[DepthLevel][m_ImageIndex]; + if (image_original) + { m_imageItem_Original->changeImage(*image_original); m_imageItem_Processed->changeImageDiffRef(image_original); } @@ -1367,31 +1782,32 @@ void acImageView::onImageDepthChanged(int DepthLevel) { } } - -void acImageView::onMouseHandDown() { +void acImageView::onMouseHandDown() +{ m_MouseHandDown = true; m_lastMousePos.setX(0); m_lastMousePos.setY(0); - if (m_imageItem_Processed) { + if (m_imageItem_Processed) + { if (!IsImageBoundedToView(NULL)) m_imageItem_Processed->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable); else m_imageItem_Processed->setFlags(QGraphicsItem::ItemIsSelectable); } - if (m_imageItem_Original) { + if (m_imageItem_Original) + { if (!IsImageBoundedToView(NULL)) m_imageItem_Original->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable); else m_imageItem_Original->setFlags(QGraphicsItem::ItemIsSelectable); } - ManageScrollBars(); - } -void acImageView::onMouseHandD() { +void acImageView::onMouseHandD() +{ m_MouseHandDown = false; if (m_imageItem_Processed) @@ -1402,44 +1818,50 @@ void acImageView::onMouseHandD() { ManageScrollBars(); } -void acImageView::onWheelScaleUp(QPointF &pos) { +void acImageView::onWheelScaleUp(QPointF& pos) +{ // Check! - if (!m_imageItem_Processed) return; + if (!m_imageItem_Processed) + return; if (m_MipImages) - if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) return; + if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) + return; Qt::KeyboardModifiers keyMod = QApplication::keyboardModifiers(); - m_ImageScale += keyMod.testFlag(Qt::ControlModifier) ? 100: 10; + m_ImageScale += keyMod.testFlag(Qt::ControlModifier) ? 100 : 10; if (m_ImageScale > AC_IMAGE_MAX_ZOOM) m_ImageScale = AC_IMAGE_MAX_ZOOM; - QPointF localPt; + QPointF localPt; - localPt = m_imageItem_Processed->mapFromScene(pos); + localPt = m_imageItem_Processed->mapFromScene(pos); qreal scale = m_ImageScale / 100.0; m_imageItem_Processed->setScale(scale); if (m_imageItem_Original) m_imageItem_Original->setScale(scale); - QPointF localPtNew; + QPointF localPtNew; localPtNew = m_imageItem_Processed->mapFromScene(pos); - QPointF Delta = localPtNew - localPt; + QPointF Delta = localPtNew - localPt; m_imageItem_Processed->moveBy(Delta.x() * scale, Delta.y() * scale); if (m_imageItem_Original) m_imageItem_Original->moveBy(Delta.x() * scale, Delta.y() * scale); - emit acScaleChanged(m_ImageScale); + emit acScaleChanged(m_ImageScale); ManageScrollBars(); } // Zoom Out -void acImageView::onWheelScaleDown(QPointF &pos) { +void acImageView::onWheelScaleDown(QPointF& pos) +{ // Check! - if (!m_imageItem_Processed) return; + if (!m_imageItem_Processed) + return; if (m_MipImages) - if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) return; + if (m_MipImages->m_Error != MIPIMAGE_FORMAT_ERRORS::Format_NoErrors) + return; Qt::KeyboardModifiers keyMod = QApplication::keyboardModifiers(); m_ImageScale += keyMod.testFlag(Qt::ControlModifier) ? -100 : -10; @@ -1447,94 +1869,104 @@ void acImageView::onWheelScaleDown(QPointF &pos) { if (m_ImageScale < AC_IMAGE_MIN_ZOOM) m_ImageScale = AC_IMAGE_MIN_ZOOM; - QPointF localPt; + QPointF localPt; - localPt = m_imageItem_Processed->mapFromScene(pos); + localPt = m_imageItem_Processed->mapFromScene(pos); qreal scale = m_ImageScale / 100.0; m_imageItem_Processed->setScale(scale); if (m_imageItem_Original) m_imageItem_Original->setScale(scale); - QPointF localPtNew; + QPointF localPtNew; localPtNew = m_imageItem_Processed->mapFromScene(pos); - QPointF Delta = localPtNew - localPt; + QPointF Delta = localPtNew - localPt; m_imageItem_Processed->moveBy(Delta.x() * scale, Delta.y() * scale); if (m_imageItem_Original) m_imageItem_Original->moveBy(Delta.x() * scale, Delta.y() * scale); - emit acScaleChanged(m_ImageScale); + emit acScaleChanged(m_ImageScale); ManageScrollBars(); } -void acImageView::onSetScale(int value) { - - if (!m_graphicsScene) return; - if (!m_imageItem_Processed) return; +void acImageView::onSetScale(int value) +{ + if (!m_graphicsScene) + return; + if (!m_imageItem_Processed) + return; // Size of our Scene view - QRectF bounds = m_graphicsScene->sceneRect(); - QPointF pos = bounds.center(); + QRectF bounds = m_graphicsScene->sceneRect(); + QPointF pos = bounds.center(); - QGraphicsItem * itemPicked = m_graphicsScene->itemAt(pos, QTransform()); + QGraphicsItem* itemPicked = m_graphicsScene->itemAt(pos, QTransform()); // Found an item under the cursor - if (itemPicked) { + if (itemPicked) + { // Is the item our custome image - if (itemPicked->type() == (itemPicked->UserType + acCustomGraphicsTypes::IMAGE)) { + if (itemPicked->type() == (itemPicked->UserType + acCustomGraphicsTypes::IMAGE)) + { //======================================================== // Zoom from a image position that is on the scene Center //======================================================== - QPointF localPt; + QPointF localPt; localPt = m_imageItem_Processed->mapFromScene(pos); m_ImageScale = value; - qreal scale = m_ImageScale / 100.0; + qreal scale = m_ImageScale / 100.0; m_imageItem_Processed->setScale(scale); if (m_imageItem_Original) m_imageItem_Original->setScale(scale); - QPointF localPtNew; + QPointF localPtNew; localPtNew = m_imageItem_Processed->mapFromScene(pos); - QPointF Delta = localPtNew - localPt; + QPointF Delta = localPtNew - localPt; m_imageItem_Processed->moveBy(Delta.x() * scale, Delta.y() * scale); if (m_imageItem_Original) m_imageItem_Original->moveBy(Delta.x() * scale, Delta.y() * scale); } - } else { + } + else + { //======================================================== // Zoom from image Center //======================================================== - QRectF offset1 = m_imageItem_Processed->sceneBoundingRect(); // Current image size and position user is viewing + QRectF offset1 = m_imageItem_Processed->sceneBoundingRect(); // Current image size and position user is viewing m_ImageScale = value; m_imageItem_Processed->setScale(m_ImageScale / 100.0); if (m_imageItem_Original) m_imageItem_Original->setScale(m_ImageScale / 100.0); - QRectF offset2 = m_imageItem_Processed->sceneBoundingRect(); // New image size and position user is viewing - QPointF Delta = offset1.center() - offset2.center(); + QRectF offset2 = m_imageItem_Processed->sceneBoundingRect(); // New image size and position user is viewing + QPointF Delta = offset1.center() - offset2.center(); m_imageItem_Processed->moveBy(Delta.x(), Delta.y()); if (m_imageItem_Original) m_imageItem_Original->moveBy(Delta.x(), Delta.y()); - } ManageScrollBars(); - } -void acImageView::ManageScrollBars() { - if (!m_imageGraphicsView) return; - if (!m_graphicsScene) return; - if (!m_imageItem_Processed) return; +void acImageView::ManageScrollBars() +{ + if (!m_imageGraphicsView) + return; + if (!m_graphicsScene) + return; + if (!m_imageItem_Processed) + return; - if (!m_xPos) return; - if (!m_yPos) return; + if (!m_xPos) + return; + if (!m_yPos) + return; // Size of our viewing area QRectF Viewbounds = m_imageGraphicsView->contentsRect(); @@ -1545,7 +1977,6 @@ void acImageView::ManageScrollBars() { // Get the Current location of the Image QRectF imageoffset = m_imageItem_Processed->sceneBoundingRect(); - int value; value = m_xPos->value(); @@ -1561,28 +1992,32 @@ void acImageView::ManageScrollBars() { bool ShowScrollX = false; bool ShowScrollY = false; // Horizontal scroll setting - if (imageoffset.x() < 0) { + if (imageoffset.x() < 0) + { int diff = abs(imageoffset.x()); m_xPos->setMinimum(-diff); ShowScrollX = true; } - if ((imageoffset.x() + imageoffset.width()) > Viewbounds.width()) { + if ((imageoffset.x() + imageoffset.width()) > Viewbounds.width()) + { int diff = abs((imageoffset.x() + imageoffset.width()) - Viewbounds.width()); m_xPos->setMaximum(diff); ShowScrollX = true; } // Verical scroll setting - if (imageoffset.y() < 0) { - int diff = abs(imageoffset.y()+200); + if (imageoffset.y() < 0) + { + int diff = abs(imageoffset.y() + 200); m_yPos->setMinimum(-diff); ShowScrollY = true; } - if ((imageoffset.y() + imageoffset.height()) > Viewbounds.height()) { + if ((imageoffset.y() + imageoffset.height()) > Viewbounds.height()) + { int diff = abs((imageoffset.y() + imageoffset.height()) - Viewbounds.height()); - m_yPos->setMaximum(diff+200); + m_yPos->setMaximum(diff + 200); ShowScrollY = true; } @@ -1595,6 +2030,4 @@ void acImageView::ManageScrollBars() { m_yPos->show(); else m_yPos->hide(); - } - diff --git a/applications/compressonatorgui/components/cmakelists.txt b/applications/compressonatorgui/components/cmakelists.txt index ca5e79b55..4c184545d 100644 --- a/applications/compressonatorgui/components/cmakelists.txt +++ b/applications/compressonatorgui/components/cmakelists.txt @@ -1,86 +1,99 @@ -add_library(CMP_GUI_BIN_Components INTERFACE) - -target_sources(CMP_GUI_BIN_Components INTERFACE - ac3dmeshanalysis.h - acaboutdlg.h - accustomdockwidget.h - accustomgraphics.h - acdiffimage.h - acdockwidgettitlebar.h - acexrtool.h - acimageview.h - acoglwidget.h - acprogressanimationwidget.h - acprogressdlg.h - actableimagedatamodel.h - cp3dmodelcompare.h - cp3dmodelconvert.h - cp3dmodelview.h - cpcompressstatus.h - cpgenmips.h - cpimageanalysis.h - cpimagecompare.h - cpimagefiledata.h - cpimagepropertyview.h - cpimageview.h - cpnewproject.h - cpprojectdata.h - cpprojectview.h - cpsetapplicationoptions.h - cpsetcompressoptions.h - cpsetmeshoptions.h - cpstartuppage.h - cpwelcomepage.h +set(COMPONENTS_H + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/ac3dmeshanalysis.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/acaboutdlg.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/accustomdockwidget.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/accustomgraphics.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/acdiffimage.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/acdockwidgettitlebar.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/acexrtool.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/acimageview.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/acoglwidget.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/acprogressanimationwidget.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/acprogressdlg.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/actableimagedatamodel.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cp3dmodelcompare.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cp3dmodelconvert.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cp3dmodelview.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpcompressstatus.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpgenmips.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpimageanalysis.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpimagecompare.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpimagefiledata.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpimageloader.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpimagepropertyview.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpimageview.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpnewproject.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpprojectdata.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpprojectview.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpsetapplicationoptions.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpsetcompressoptions.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpsetmeshoptions.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpstartuppage.h + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpwelcomepage.h ) -target_sources(CMP_GUI_BIN_Components INTERFACE - ac3dmeshanalysis.cpp - acaboutdlg.cpp - accustomdockwidget.cpp - accustomgraphics.cpp - acdiffimage.cpp - acdockwidgettitlebar.cpp - acexrtool.cpp - acimageview.cpp - acoglwidget.cpp - acprogressanimationwidget.cpp - acprogressdlg.cpp - actableimagedatamodel.cpp - cp3dmodelcompare.cpp - cp3dmodelconvert.cpp - cp3dmodelview.cpp - cpcompressstatus.cpp - cpgenmips.cpp - cpimageanalysis.cpp - cpimagecompare.cpp - cpimagepropertyview.cpp - cpimageview.cpp - cpnewproject.cpp - cpprojectview.cpp - cpsetapplicationoptions.cpp - cpsetcompressoptions.cpp - cpsetmeshoptions.cpp - cpstartuppage.cpp - cpwelcomepage.cpp +set(COMPONENTS_SRC + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/ac3dmeshanalysis.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/acaboutdlg.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/accustomdockwidget.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/accustomgraphics.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/acdiffimage.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/acdockwidgettitlebar.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/acexrtool.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/acimageview.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/acoglwidget.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/acprogressanimationwidget.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/acprogressdlg.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/actableimagedatamodel.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cp3dmodelcompare.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cp3dmodelconvert.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cp3dmodelview.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpcompressstatus.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpgenmips.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpimageanalysis.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpimagecompare.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpimageloader.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpimagepropertyview.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpimageview.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpnewproject.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpprojectview.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpsetapplicationoptions.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpsetcompressoptions.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpsetmeshoptions.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpstartuppage.cpp + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components/cpwelcomepage.cpp ) -target_link_libraries(CMP_GUI_BIN_Components INTERFACE - - CMP_GUI - CMP_Mesh - CMP_MeshOptimizer +add_library(CMP_GUI_Components STATIC ${COMPONENTS_H} ${COMPONENTS_SRC}) - Plugin_CAnalysis - Plugin_Common_ATIFormats - Plugin_Json - - ExtOpenGL - ExtQt5ForGUI - ExtQt5OpenGL - ExtRapidXML -) +target_include_directories(CMP_GUI_Components PRIVATE + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/components + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/common + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/source + ${PROJECT_SOURCE_DIR}/applications/compressonatorgui/qpropertypages + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_meshoptimizer + ${PROJECT_SOURCE_DIR}/applications/_libs/gpu_decode + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_mesh + ${PROJECT_SOURCE_DIR}/applications/_plugins/common + ${PROJECT_SOURCE_DIR}/applications/_plugins/canalysis/analysis + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/gltf/ + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/json/ + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half + ${PROJECT_SOURCE_DIR}/../common/lib/ext/rapidxml + ${PROJECT_SOURCE_DIR}/../common/lib/ext/glm + ${OpenCV_INCLUDE_DIRS} + ${OpenEXR_INCLUDE_DIRS} + ${draco_INCLUDE_DIRS} + ) -target_include_directories(CMP_GUI_BIN_Components INTERFACE +target_link_libraries(CMP_GUI_Components + Qt5::Widgets + Qt5::OpenGL + Qt5::Qml + Qt5::WebEngineWidgets + ) - ./ -) \ No newline at end of file +set_target_properties(CMP_GUI_Components PROPERTIES + AUTOMOC ON + FOLDER "Libs") diff --git a/applications/compressonatorgui/components/cp3dmodelcompare.cpp b/applications/compressonatorgui/components/cp3dmodelcompare.cpp index 8b178bbbb..2c959e27b 100644 --- a/applications/compressonatorgui/components/cp3dmodelcompare.cpp +++ b/applications/compressonatorgui/components/cp3dmodelcompare.cpp @@ -90,8 +90,8 @@ C3DModelCompare::C3DModelCompare(const QString title, QString file1, QString fil m_dockToolBar->addWidget(m_CBManual_renderView); - hlayoutAct = new QAction(QIcon(":/CompressonatorGUI/Images/horizontal.png"), tr("&Change to horizontal view"), this); - orilayoutAct = new QAction(QIcon(":/CompressonatorGUI/Images/orilayout.png"), tr("&Change to default view"), this); + hlayoutAct = new QAction(QIcon(":/compressonatorgui/images/horizontal.png"), tr("&Change to horizontal view"), this); + orilayoutAct = new QAction(QIcon(":/compressonatorgui/images/orilayout.png"), tr("&Change to default view"), this); orilayoutAct->setDisabled(true); m_dockToolBar->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea | Qt::RightToolBarArea | Qt::LeftToolBarArea); diff --git a/applications/compressonatorgui/components/cp3dmodelview.cpp b/applications/compressonatorgui/components/cp3dmodelview.cpp index 4dd75cd8b..1b89c7601 100644 --- a/applications/compressonatorgui/components/cp3dmodelview.cpp +++ b/applications/compressonatorgui/components/cp3dmodelview.cpp @@ -30,6 +30,7 @@ extern C_Application_Options g_Application_Options; extern CMIPS* g_GUI_CMIPS; +extern bool isDX12Supported(); // Shared memory for all views. We keep track of the mouse last pos // to calc deta {-1,0+1} from current mouse pos to last mouse pos @@ -46,7 +47,8 @@ static winMsgHandler static_winMsgHandler; #ifdef _WIN32 // this is the main message handler for the calls that uses createNativeWindowView // it will emit signals to Qt connected classes via the winMsgHandler -LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { +LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ MSG msg; msg.hwnd = hWnd; @@ -58,7 +60,8 @@ LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPara return DefWindowProc(hWnd, message, wParam, lParam); } -WId createNativeWindowView(const char* name, int width, int height) { +WId createNativeWindowView(const char* name, int width, int height) +{ WId result = 0; WNDCLASSEX windowClass; @@ -92,15 +95,23 @@ WId createNativeWindowView(const char* name, int width, int height) { HWND hwnd = CreateWindowEx(NULL, TEXT("WindowClass1"), // name of the window class - TEXT("Native Window"), WS_OVERLAPPEDWINDOW, 100, 100, width, height, + TEXT("Native Window"), + WS_OVERLAPPEDWINDOW, + 100, + 100, + width, + height, NULL, // we have no parent window, NULL NULL, // we aren't using menus, NULL windowClass.hInstance, // application handle NULL); // used with multiple windows, NULL - if (hwnd) { + if (hwnd) + { result = WId(hwnd); - } else { + } + else + { qErrnoWarning("Cannot create window \"%s\"", qPrintable(name)); } @@ -108,93 +119,112 @@ WId createNativeWindowView(const char* name, int width, int height) { } #endif -bool cpRenderWindow::nativeEvent(const QByteArray& eventType, void* message, long* result) { +bool cpRenderWindow::nativeEvent(const QByteArray& eventType, void* message, long* result) +{ Q_UNUSED(result); Q_UNUSED(eventType); #ifdef _WIN32 MSG* msg = static_cast(message); - if (msg->message == WM_ACTIVATE) { + if (msg->message == WM_ACTIVATE) + { } - if (msg->message == WM_CREATE) { + if (msg->message == WM_CREATE) + { } #endif return false; } -void cpRenderWindow::resizeEvent(QResizeEvent* event) { - if (m_plugin && m_viewOpen) { +void cpRenderWindow::resizeEvent(QResizeEvent* event) +{ + if (m_plugin && m_viewOpen) + { m_plugin->OnReSizeView(event->size().width(), event->size().height()); } } -void cpRenderWindow::paintEvent(QPaintEvent* ev) { +void cpRenderWindow::paintEvent(QPaintEvent* ev) +{ (void)ev; - if (m_plugin && m_viewOpen) { + if (m_plugin && m_viewOpen) + { m_viewOpen = m_plugin->OnRenderView(); update(); } } -static void qNormalizeAngle(int& angle) { +static void qNormalizeAngle(int& angle) +{ while (angle < 0) angle += 360 * 16; while (angle > 360 * 16) angle -= 360 * 16; } -void cpRenderWindow::setXRotation(int angle) { +void cpRenderWindow::setXRotation(int angle) +{ qNormalizeAngle(angle); - if (angle != g_Roll) { + if (angle != g_Roll) + { g_Roll = angle; } } -void cpRenderWindow::setYRotation(int angle) { +void cpRenderWindow::setYRotation(int angle) +{ qNormalizeAngle(angle); - if (angle != g_Yaw) { + if (angle != g_Yaw) + { g_Yaw = angle; } } -void cpRenderWindow::setZRotation(int angle) { +void cpRenderWindow::setZRotation(int angle) +{ qNormalizeAngle(angle); - if (angle != g_Pitch) { + if (angle != g_Pitch) + { g_Pitch = angle; } } - -void cpRenderWindow::SetManualRenderFlip(int mode) { +void cpRenderWindow::SetManualRenderFlip(int mode) +{ #ifdef _WIN32 - if (m_plugin) { - MSG msg = { 0 }; - msg.hwnd = m_hwnd; - msg.message = WM_KEYDOWN; + if (m_plugin) + { + MSG msg = {0}; + msg.hwnd = m_hwnd; + msg.message = WM_KEYDOWN; m_manual3DViewFlipMode = mode; - msg.wParam = 1; // Signal that we want to set a Manual Mode setting - msg.lParam = mode; + msg.wParam = 1; // Signal that we want to set a Manual Mode setting + msg.lParam = mode; m_plugin->processMSG(&msg); } #endif } // Keypressed event for the Qt render window -void cpRenderWindow::keyPressEvent(QKeyEvent *event) { +void cpRenderWindow::keyPressEvent(QKeyEvent* event) +{ #ifdef _WIN32 - if (m_plugin) { + if (m_plugin) + { // Process key press if 3D model view is in manual mode - if (m_manual3DViewFlipMode == 1) { + if (m_manual3DViewFlipMode == 1) + { //printf("%x ", event->key()); - MSG msg = { 0 }; - msg.hwnd = m_hwnd; + MSG msg = {0}; + msg.hwnd = m_hwnd; msg.message = WM_KEYDOWN; // Tracks user flip mode, always starts with original then flips // for now we only have two rendered models overlaid in one view. - if (event->key() == QT_KEY_SPACE) { + if (event->key() == QT_KEY_SPACE) + { m_viewingOriginalModel = m_viewingOriginalModel ? false : true; - msg.wParam = 2; // Signal we want to flip view - msg.lParam = m_viewingOriginalModel ? 0 : 1; + msg.wParam = 2; // Signal we want to flip view + msg.lParam = m_viewingOriginalModel ? 0 : 1; m_plugin->processMSG(&msg); emit signalModelKeyPressed(event->key()); } @@ -205,7 +235,8 @@ void cpRenderWindow::keyPressEvent(QKeyEvent *event) { // This event handler has "Global" scope and is triggered throughout the app // Mouse events, paint .. are filtered for the active Rendered Windows -bool cpRenderWindow::eventFilter(QObject* obj, QEvent* ev) { +bool cpRenderWindow::eventFilter(QObject* obj, QEvent* ev) +{ // pass the event on to the parent class if events cannot be handled by this class if (obj != this) return QObject::eventFilter(obj, ev); @@ -218,13 +249,14 @@ bool cpRenderWindow::eventFilter(QObject* obj, QEvent* ev) { static bool LeftButtonPressed = false; static bool RightButtonPressed = false; - - if (ev->type() == QEvent::MouseMove) { + if (ev->type() == QEvent::MouseMove) + { MSG msg = {0}; msg.hwnd = m_hwnd; QMouseEvent* mouseEvent = static_cast(ev); - if (LeftButtonPressed) { + if (LeftButtonPressed) + { msg.message = WM_MOUSEMOVE; if ((g_last_x - mouseEvent->pos().x()) == 0) @@ -258,28 +290,35 @@ bool cpRenderWindow::eventFilter(QObject* obj, QEvent* ev) { g_last_x = mouseEvent->pos().x(); g_last_y = mouseEvent->pos().y(); - } else if (RightButtonPressed) { + } + else if (RightButtonPressed) + { msg.message = WM_MOUSEMOVE; msg.lParam = mouseEvent->pos().x() + (mouseEvent->pos().y() << 16); m_plugin->processMSG(&msg); g_last_x = mouseEvent->pos().x(); g_last_y = mouseEvent->pos().y(); } - } else if (ev->type() == QEvent::MouseButtonPress) { + } + else if (ev->type() == QEvent::MouseButtonPress) + { if (m_usingWindowProc) return QObject::eventFilter(obj, ev); QMouseEvent* mouseEvent = static_cast(ev); g_last_x = mouseEvent->pos().x(); g_last_y = mouseEvent->pos().y(); - if (mouseEvent->button() == Qt::MouseButton::LeftButton) { + if (mouseEvent->button() == Qt::MouseButton::LeftButton) + { MSG msg = {0}; msg.hwnd = m_hwnd; msg.message = WM_LBUTTONDOWN; msg.lParam = g_last_x + (g_last_y << 16); m_plugin->processMSG(&msg); LeftButtonPressed = true; - } else if (mouseEvent->button() == Qt::MouseButton::RightButton) { + } + else if (mouseEvent->button() == Qt::MouseButton::RightButton) + { MSG msg = {0}; msg.hwnd = m_hwnd; msg.message = WM_RBUTTONDOWN; @@ -289,11 +328,14 @@ bool cpRenderWindow::eventFilter(QObject* obj, QEvent* ev) { g_Pitch = 0; RightButtonPressed = true; } - } else if (ev->type() == QEvent::MouseButtonRelease) { + } + else if (ev->type() == QEvent::MouseButtonRelease) + { if (m_usingWindowProc) return QObject::eventFilter(obj, ev); QMouseEvent* mouseEvent = static_cast(ev); - if (mouseEvent->button() == Qt::MouseButton::LeftButton) { + if (mouseEvent->button() == Qt::MouseButton::LeftButton) + { MSG msg = {0}; msg.hwnd = m_hwnd; msg.message = WM_LBUTTONUP; @@ -301,7 +343,9 @@ bool cpRenderWindow::eventFilter(QObject* obj, QEvent* ev) { msg.lParam = g_last_x + (g_last_y << 16); m_plugin->processMSG(&msg); LeftButtonPressed = false; - } else if (mouseEvent->button() == Qt::MouseButton::RightButton) { + } + else if (mouseEvent->button() == Qt::MouseButton::RightButton) + { MSG msg = {0}; msg.hwnd = m_hwnd; msg.message = WM_RBUTTONUP; @@ -309,7 +353,9 @@ bool cpRenderWindow::eventFilter(QObject* obj, QEvent* ev) { m_plugin->processMSG(&msg); RightButtonPressed = false; } - } else if (ev->type() == QEvent::Wheel) { + } + else if (ev->type() == QEvent::Wheel) + { QWheelEvent* wheelEvent = static_cast(ev); MSG msg = {0}; msg.hwnd = m_hwnd; @@ -321,14 +367,17 @@ bool cpRenderWindow::eventFilter(QObject* obj, QEvent* ev) { msg.wParam = -10 << 16; m_plugin->processMSG(&msg); - } else if (ev->type() == QEvent::Paint) { + } + else if (ev->type() == QEvent::Paint) + { } #endif return QObject::eventFilter(obj, ev); } #ifdef _WIN32 -void cpRenderWindow::localMessage(MSG& msg) { +void cpRenderWindow::localMessage(MSG& msg) +{ if (!m_usingWindowProc) return; @@ -337,12 +386,14 @@ void cpRenderWindow::localMessage(MSG& msg) { int xPos; int yPos; - switch (msg.message) { + switch (msg.message) + { case WM_RBUTTONDOWN: case WM_RBUTTONUP: case WM_LBUTTONDOWN: case WM_LBUTTONUP: - switch (msg.message) { + switch (msg.message) + { case WM_RBUTTONDOWN: g_last_x = LOWORD(msg.lParam); g_last_y = HIWORD(msg.lParam); @@ -367,7 +418,8 @@ void cpRenderWindow::localMessage(MSG& msg) { case WM_MOUSEMOVE: xPos = LOWORD(msg.lParam); yPos = HIWORD(msg.lParam); - if (LeftButtonPressed) { + if (LeftButtonPressed) + { if ((g_last_x - xPos) == 0) g_Roll = g_Roll; else if ((g_last_x - xPos) > 0) @@ -399,7 +451,9 @@ void cpRenderWindow::localMessage(MSG& msg) { g_last_x = xPos; g_last_y = yPos; - } else if (RightButtonPressed) { + } + else if (RightButtonPressed) + { msg.lParam = xPos + (yPos << 16); m_plugin->processMSG(&msg); g_last_x = xPos; @@ -411,39 +465,47 @@ void cpRenderWindow::localMessage(MSG& msg) { #endif //=========================================================== -void cp3DModelView::Clean3DModelView() { +void cp3DModelView::Clean3DModelView() +{ // remove the Viewer plugin we used first - if (m_plugin) { + if (m_plugin) + { m_plugin->CloseView(); delete m_plugin; m_plugin = NULL; } // remove the data loaded plugin we used last - if (m_plugin_loader) { + if (m_plugin_loader) + { delete m_plugin_loader; m_plugin_loader = NULL; } } -cp3DModelView::~cp3DModelView() { +cp3DModelView::~cp3DModelView() +{ Clean3DModelView(); } -void cp3DModelView::setManualViewFlip(int mode) { - if (m_renderview) { +void cp3DModelView::setManualViewFlip(int mode) +{ + if (m_renderview) + { m_renderview->SetManualRenderFlip(mode); } } -void cp3DModelView::OnToolBarClicked() { +void cp3DModelView::OnToolBarClicked() +{ if (m_toolBar->isVisible()) m_toolBar->hide(); else m_toolBar->show(); } -void cp3DModelView::OnShowOptions() { +void cp3DModelView::OnShowOptions() +{ m_showViewOptions ^= 1; m_OptionsButton->setText(!m_showViewOptions ? SHOW_VIEW_OPTIONS : HIDE_VIEW_OPTIONS); #ifdef _WIN32 @@ -456,10 +518,14 @@ void cp3DModelView::OnShowOptions() { } // This is only signaled when we are viewing a 3D model diff -void cp3DModelView::OnModelViewKeyPressed(int key) { - if (!m_renderview) return; - if (!custTitleBar) return; - if (key == QT_KEY_SPACE) { +void cp3DModelView::OnModelViewKeyPressed(int key) +{ + if (!m_renderview) + return; + if (!custTitleBar) + return; + if (key == QT_KEY_SPACE) + { if (m_renderview->m_viewingOriginalModel) custTitleBar->setTitle("3D Model Diff View: Original"); else @@ -467,8 +533,9 @@ void cp3DModelView::OnModelViewKeyPressed(int key) { } } -cp3DModelView::cp3DModelView(const QString filePathName, const QString filePathName2, const QString Title, QWidget* parent) : - acCustomDockWidget(filePathName, parent) { +cp3DModelView::cp3DModelView(const QString filePathName, const QString filePathName2, const QString Title, QWidget* parent) + : acCustomDockWidget(filePathName, parent) +{ m_parent = parent; m_fileName = filePathName; m_statusBar = NULL; @@ -488,7 +555,8 @@ cp3DModelView::cp3DModelView(const QString filePathName, const QString filePathN // Main Widget //=============== m_newWidget = new QWidget(parent); - if (!m_newWidget) { + if (!m_newWidget) + { PrintInfo("Error: creating main widget\n"); return; } @@ -504,9 +572,11 @@ cp3DModelView::cp3DModelView(const QString filePathName, const QString filePathN m_toolBar->setMaximumHeight(25); m_showViewOptions = false; - if (g_Application_Options.m_GLTFRenderWith != C_Application_Options::RenderModelsWith::glTF_Vulkan) { - m_OptionsButton = new QPushButton(!m_showViewOptions ? SHOW_VIEW_OPTIONS : HIDE_VIEW_OPTIONS); - if (m_OptionsButton) { + if (g_Application_Options.m_GLTFRenderWith != C_Application_Options::RenderModelsWith::glTF_Vulkan) + { + m_OptionsButton = new QPushButton(!m_showViewOptions ? SHOW_VIEW_OPTIONS : HIDE_VIEW_OPTIONS); + if (m_OptionsButton) + { m_OptionsButton->setStatusTip(tr("Toggle Display Options")); connect(m_OptionsButton, SIGNAL(released()), this, SLOT(OnShowOptions())); } @@ -533,10 +603,12 @@ cp3DModelView::cp3DModelView(const QString filePathName, const QString filePathN void* msgHandler = NULL; cpMainComponents* mainComponents = NULL; - if (m_parent) { + if (m_parent) + { // assign msg handler QString ClassName = m_parent->metaObject()->className(); - if (ClassName.compare("cpMainComponents") == 0) { + if (ClassName.compare("cpMainComponents") == 0) + { mainComponents = (cpMainComponents*)m_parent; msgHandler = (void*)(mainComponents->PrintStatus); } @@ -549,21 +621,25 @@ cp3DModelView::cp3DModelView(const QString filePathName, const QString filePathN m_layout->setContentsMargins(0, 0, 0, 0); m_layout->addLayout(hlayout2, 0, 0); - try { + try + { // Load the Model Data QFileInfo fileInfo(filePathName); QString EXT = fileInfo.suffix(); QByteArray ba = EXT.toUpper().toLatin1(); char* c_ext = ba.data(); - if (((strcmp(c_ext, "GLTF") == 0) || (strcmp(c_ext, "BIN") == 0))) { + if (((strcmp(c_ext, "GLTF") == 0) || (strcmp(c_ext, "BIN") == 0))) + { m_isviewingDX12 = true; if (g_Application_Options.getGLTFRender() == C_Application_Options::RenderModelsWith::glTF_OpenGL) g_Application_Options.setGLTFRender(C_Application_Options::RenderModelsWith::glTF_DX12_EX); - } else { + } + else + { m_isviewingDX12 = false; if (g_Application_Options.getGLTFRender() == C_Application_Options::RenderModelsWith::glTF_DX12_EX || - g_Application_Options.getGLTFRender() == C_Application_Options::RenderModelsWith::glTF_Vulkan) + g_Application_Options.getGLTFRender() == C_Application_Options::RenderModelsWith::glTF_Vulkan) g_Application_Options.setGLTFRender(C_Application_Options::RenderModelsWith::glTF_OpenGL); } @@ -571,19 +647,26 @@ cp3DModelView::cp3DModelView(const QString filePathName, const QString filePathN std::string srcglTFfile2 = filePathName2.toStdString(); // for gltf case: detect if is gltf draco the call decode before loading - if (((strcmp(c_ext, "GLTF") == 0) || (strcmp(c_ext, "BIN") == 0))) { - if ((strcmp(c_ext, "BIN") == 0)) { - if (filePathName2.contains(".gltf") || filePathName2.contains(".GLTF")) { + if (((strcmp(c_ext, "GLTF") == 0) || (strcmp(c_ext, "BIN") == 0))) + { + if ((strcmp(c_ext, "BIN") == 0)) + { + if (filePathName2.contains(".gltf") || filePathName2.contains(".GLTF")) + { srcglTFfile1 = ""; } } - if (isGLTFDracoFile(srcglTFfile1)) { + if (isGLTFDracoFile(srcglTFfile1)) + { std::size_t dotPos = srcglTFfile1.rfind('.'); std::string tempdstFile = srcglTFfile1.substr(0, dotPos) + "_tmpdecoded.glTF"; - if (!decompressglTFfile(srcglTFfile1, tempdstFile, g_CmdPrams.use_Draco_Encode, g_CmdPrams.CompressOptions)) { - if (mainComponents) { - if (mainComponents->m_CompressStatusDialog) { + if (!decompressglTFfile(srcglTFfile1, tempdstFile, g_CmdPrams.use_Draco_Encode, g_CmdPrams.CompressOptions)) + { + if (mainComponents) + { + if (mainComponents->m_CompressStatusDialog) + { mainComponents->m_CompressStatusDialog->showOutput(); mainComponents->m_CompressStatusDialog->raise(); } @@ -595,12 +678,16 @@ cp3DModelView::cp3DModelView(const QString filePathName, const QString filePathN srcglTFfile1 = tempdstFile; } - if (isGLTFDracoFile(srcglTFfile2)) { + if (isGLTFDracoFile(srcglTFfile2)) + { std::size_t dotPos = srcglTFfile2.rfind('.'); std::string tempdstFile = srcglTFfile2.substr(0, dotPos) + "_tmpdecoded.glTF"; - if (!decompressglTFfile(srcglTFfile2, tempdstFile, g_CmdPrams.use_Draco_Encode, g_CmdPrams.CompressOptions)) { - if (mainComponents) { - if (mainComponents->m_CompressStatusDialog) { + if (!decompressglTFfile(srcglTFfile2, tempdstFile, g_CmdPrams.use_Draco_Encode, g_CmdPrams.CompressOptions)) + { + if (mainComponents) + { + if (mainComponents->m_CompressStatusDialog) + { mainComponents->m_CompressStatusDialog->showOutput(); mainComponents->m_CompressStatusDialog->raise(); } @@ -615,23 +702,28 @@ cp3DModelView::cp3DModelView(const QString filePathName, const QString filePathN // We will only use GLTF loader when not using OpenGL if (((strcmp(c_ext, "GLTF") == 0) || (strcmp(c_ext, "BIN") == 0)) && - (g_Application_Options.getGLTFRender() != C_Application_Options::RenderModelsWith::glTF_OpenGL)) + (g_Application_Options.getGLTFRender() != C_Application_Options::RenderModelsWith::glTF_OpenGL)) m_plugin_loader = reinterpret_cast(g_pluginManager.GetPlugin("3DMODEL_LOADER", "GLTF")); else if (strcmp(c_ext, "DRC") == 0) m_plugin_loader = reinterpret_cast(g_pluginManager.GetPlugin("3DMODEL_LOADER", "DRC")); else m_plugin_loader = reinterpret_cast( - g_pluginManager.GetPlugin("3DMODEL_LOADER", g_Application_Options.getUseAssimp() ? "ASSIMP" : "OBJ")); + g_pluginManager.GetPlugin("3DMODEL_LOADER", g_Application_Options.getUseAssimp() ? "ASSIMP" : "OBJ")); - if (m_plugin_loader) { + if (m_plugin_loader) + { m_plugin_loader->TC_PluginSetSharedIO(g_GUI_CMIPS); int result = 0; - if ((strcmp(c_ext, "BIN") == 0)) { + if ((strcmp(c_ext, "BIN") == 0)) + { if (filePathName2.contains(".gltf") || filePathName2.contains(".GLTF")) result = m_plugin_loader->LoadModelData(srcglTFfile2.c_str(), "", &g_pluginManager, msgHandler, &ProgressCallback); - else { - if (mainComponents) { - if (mainComponents->m_CompressStatusDialog) { + else + { + if (mainComponents) + { + if (mainComponents->m_CompressStatusDialog) + { mainComponents->m_CompressStatusDialog->showOutput(); mainComponents->m_CompressStatusDialog->raise(); } @@ -639,25 +731,29 @@ cp3DModelView::cp3DModelView(const QString filePathName, const QString filePathN PrintInfo("Error: Please add related gltf file then view bin file from there. View bin alone is not supported!\n"); return; } - } else { - if (strcmp(c_ext, "DRC") == 0) { + } + else + { + if (strcmp(c_ext, "DRC") == 0) + { CMP_DracoOptions DracoOptions; DracoOptions.output = ""; - result = m_plugin_loader->LoadModelData(srcglTFfile1.c_str(), - srcglTFfile2.size() > 0 ? srcglTFfile2.c_str() : "", &g_pluginManager, - &DracoOptions, - &ProgressCallback); - } else { - result = m_plugin_loader->LoadModelData(srcglTFfile1.c_str(), - srcglTFfile2.size() > 0 ? srcglTFfile2.c_str() : "", &g_pluginManager, - msgHandler, - &ProgressCallback); + result = m_plugin_loader->LoadModelData( + srcglTFfile1.c_str(), srcglTFfile2.size() > 0 ? srcglTFfile2.c_str() : "", &g_pluginManager, &DracoOptions, &ProgressCallback); + } + else + { + result = m_plugin_loader->LoadModelData( + srcglTFfile1.c_str(), srcglTFfile2.size() > 0 ? srcglTFfile2.c_str() : "", &g_pluginManager, msgHandler, &ProgressCallback); } } - if (result != 0) { - if (mainComponents) { - if (mainComponents->m_CompressStatusDialog) { + if (result != 0) + { + if (mainComponents) + { + if (mainComponents->m_CompressStatusDialog) + { mainComponents->m_CompressStatusDialog->showOutput(); mainComponents->m_CompressStatusDialog->raise(); } @@ -668,19 +764,34 @@ cp3DModelView::cp3DModelView(const QString filePathName, const QString filePathN char* viewer_type = nullptr; - switch (g_Application_Options.getGLTFRender()) { + switch (g_Application_Options.getGLTFRender()) + { case C_Application_Options::RenderModelsWith::glTF_DX12_EX: viewer_type = "DX12_EX"; + if (isDX12Supported()) + { + m_plugin = reinterpret_cast(g_pluginManager.GetPlugin("3DMODEL_VIEWER", viewer_type)); + } + else + { + PrintInfo("Error: Selected Viewer type is not supported!\n"); + return; + } break; case C_Application_Options::RenderModelsWith::glTF_OpenGL: viewer_type = "OPENGL"; + m_plugin = reinterpret_cast(g_pluginManager.GetPlugin("3DMODEL_VIEWER", viewer_type)); break; case C_Application_Options::RenderModelsWith::glTF_Vulkan: viewer_type = "VULKAN"; + m_plugin = reinterpret_cast(g_pluginManager.GetPlugin("3DMODEL_VIEWER", viewer_type)); break; - default: { - if (mainComponents) { - if (mainComponents->m_CompressStatusDialog) { + default: + { + if (mainComponents) + { + if (mainComponents->m_CompressStatusDialog) + { mainComponents->m_CompressStatusDialog->showOutput(); mainComponents->m_CompressStatusDialog->raise(); } @@ -691,14 +802,13 @@ cp3DModelView::cp3DModelView(const QString filePathName, const QString filePathN } this->m_CustomTitle = Title; - - m_plugin = reinterpret_cast(g_pluginManager.GetPlugin("3DMODEL_VIEWER", viewer_type)); - - if (m_plugin) { + if (m_plugin) + { m_plugin->TC_PluginSetSharedIO(g_GUI_CMIPS); void* data = m_plugin_loader->GetModelData(); - if (data && m_isviewingDX12) { + if (data && m_isviewingDX12) + { if ((strcmp(c_ext, "BIN") == 0)) ((GLTFCommon*)data)->isBinFile = true; else @@ -706,14 +816,19 @@ cp3DModelView::cp3DModelView(const QString filePathName, const QString filePathN } void* result = NULL; - if (strcmp(viewer_type, "VULKAN") == 0) { + if (strcmp(viewer_type, "VULKAN") == 0) + { result = m_plugin->CreateView(data, parent->width(), parent->height(), NULL, &g_pluginManager, msgHandler, &ProgressCallback); - if (result) { + if (result) + { // Create Empty window m_renderview = new cpRenderWindow(); - if (!m_renderview) { - if (mainComponents) { - if (mainComponents->m_CompressStatusDialog) { + if (!m_renderview) + { + if (mainComponents) + { + if (mainComponents->m_CompressStatusDialog) + { mainComponents->m_CompressStatusDialog->showOutput(); mainComponents->m_CompressStatusDialog->raise(); } @@ -724,13 +839,15 @@ cp3DModelView::cp3DModelView(const QString filePathName, const QString filePathN #ifdef _WIN32 WId wid = createNativeWindowView("Vulkan Viewer", parent->width(), parent->height()); - if (wid) { + if (wid) + { connect(&static_winMsgHandler, SIGNAL(signalMessage(MSG&)), m_renderview, SLOT(localMessage(MSG&))); m_renderview->m_usingWindowProc = true; connect(m_renderview, SIGNAL(signalModelKeyPressed(int)), this, SLOT(OnModelViewKeyPressed(int))); HWND hwnd = reinterpret_cast(wid); - if (m_plugin->ShowView(hwnd) != NULL) { + if (m_plugin->ShowView(hwnd) != NULL) + { QVBoxLayout* widgetLayout = new QVBoxLayout(); QWindow* foreignWindow = QWindow::fromWinId(wid); QWidget* newWindow = QWidget::createWindowContainer(foreignWindow, m_renderview); @@ -739,24 +856,33 @@ cp3DModelView::cp3DModelView(const QString filePathName, const QString filePathN m_renderview->setplugin(m_plugin); m_renderview->setLayout(widgetLayout); m_viewOpen = true; - } else { + } + else + { m_viewOpen = false; } m_renderview->setView(m_viewOpen); - } else { + } + else + { m_renderview->setplugin(m_plugin); m_viewOpen = false; m_renderview->setView(false); } #endif } - } else { + } + else + { m_renderview = new cpRenderWindow(); - if (!m_renderview) { - if (mainComponents) { - if (mainComponents->m_CompressStatusDialog) { + if (!m_renderview) + { + if (mainComponents) + { + if (mainComponents->m_CompressStatusDialog) + { mainComponents->m_CompressStatusDialog->showOutput(); mainComponents->m_CompressStatusDialog->raise(); } @@ -767,22 +893,24 @@ cp3DModelView::cp3DModelView(const QString filePathName, const QString filePathN connect(m_renderview, SIGNAL(signalModelKeyPressed(int)), this, SLOT(OnModelViewKeyPressed(int))); - m_renderview->setplugin(m_plugin); #ifdef _WIN32 m_hwnd = reinterpret_cast(m_renderview->winId()); #endif m_renderview->setHwnd(m_hwnd); - try { - result = - m_plugin->CreateView(data, parent->width(), parent->height(), m_renderview, &g_pluginManager, msgHandler, &ProgressCallback); - - } catch (...) { - if (m_renderview) { + try + { + result = m_plugin->CreateView(data, parent->width(), parent->height(), m_renderview, &g_pluginManager, msgHandler, &ProgressCallback); + } + catch (...) + { + if (m_renderview) + { delete m_renderview; m_renderview = nullptr; } - if (m_plugin) { + if (m_plugin) + { m_plugin->CloseView(); } throw; @@ -792,11 +920,16 @@ cp3DModelView::cp3DModelView(const QString filePathName, const QString filePathN m_renderview->setView(true); } - if (result) { + if (result) + { m_layout->addWidget(m_renderview, 1, 0); - } else { - if (mainComponents) { - if (mainComponents->m_CompressStatusDialog) { + } + else + { + if (mainComponents) + { + if (mainComponents->m_CompressStatusDialog) + { mainComponents->m_CompressStatusDialog->showOutput(); mainComponents->m_CompressStatusDialog->raise(); } @@ -805,9 +938,13 @@ cp3DModelView::cp3DModelView(const QString filePathName, const QString filePathN return; } } - } else { - if (mainComponents) { - if (mainComponents->m_CompressStatusDialog) { + } + else + { + if (mainComponents) + { + if (mainComponents->m_CompressStatusDialog) + { mainComponents->m_CompressStatusDialog->showOutput(); mainComponents->m_CompressStatusDialog->raise(); } @@ -815,9 +952,13 @@ cp3DModelView::cp3DModelView(const QString filePathName, const QString filePathN PrintInfo("Error: File format not supported.\n"); return; } - } catch (const char* error) { - if (mainComponents) { - if (mainComponents->m_CompressStatusDialog) { + } + catch (const char* error) + { + if (mainComponents) + { + if (mainComponents->m_CompressStatusDialog) + { mainComponents->m_CompressStatusDialog->showOutput(); mainComponents->m_CompressStatusDialog->raise(); } diff --git a/applications/compressonatorgui/components/cpcompressstatus.cpp b/applications/compressonatorgui/components/cpcompressstatus.cpp index 9dbfc1a8a..c974c27ce 100644 --- a/applications/compressonatorgui/components/cpcompressstatus.cpp +++ b/applications/compressonatorgui/components/cpcompressstatus.cpp @@ -86,7 +86,7 @@ CompressStatusDialog::CompressStatusDialog(const QString title, QWidget *parent) connect(m_btnClearText, SIGNAL(pressed()), this, SLOT(onClearText())); - m_layout = new QGridLayout(m_newWidget); + QGridLayout* m_layout = new QGridLayout(m_newWidget); m_layout->setSpacing(0); m_layout->setMargin(0); diff --git a/applications/compressonatorgui/components/cpgenmips.cpp b/applications/compressonatorgui/components/cpgenmips.cpp index a81de90a4..9a6b00dde 100644 --- a/applications/compressonatorgui/components/cpgenmips.cpp +++ b/applications/compressonatorgui/components/cpgenmips.cpp @@ -58,6 +58,7 @@ CGenMips::CGenMips(const QString title, QWidget* parent) m_ImageSize_W = 0; m_ImageSize_H = 0; m_MipLevels = 0; + m_minsize[0] = 0; // evalProperties(); use this to debug set properties of a class definition , in this case its CData } @@ -67,8 +68,6 @@ void CGenMips::setMipLevelDisplay(int Width, int Height, bool UsingGPU = false) m_ImageSize_H = Height; m_MipLevelSizes.clear(); - m_MipLevels = 0; - QString level; level.append(QString::number(Width)); level.append("x"); @@ -76,16 +75,25 @@ void CGenMips::setMipLevelDisplay(int Width, int Height, bool UsingGPU = false) m_MipLevelSizes << level; - m_MipLevels++; + m_minsize[0] = Width; + m_MipLevels = 1; + do { Width = (Width > 1) ? (Width >> 1) : 1; Height = (Height > 1) ? (Height >> 1) : 1; if (UsingGPU) { - if ((Width % 4) || (Height % 4)) + int non4DivW = (Width % 4); + int non4DivH = (Height % 4); + if (non4DivW || non4DivH) break; } + if (m_MipLevels > MAX_MIPLEVEL_SUPPORTED) + break; + + m_minsize[m_MipLevels] = Width; + QString level; level.append(QString::number(Width)); level.append("x"); @@ -106,16 +114,14 @@ void CGenMips::setMipLevelDisplay(int Width, int Height, bool UsingGPU = false) m_propertyMipLevels->setAttribute("enumNames", m_MipLevelSizes); m_propertyMipLevels->setValue(0); + m_MipLevels = 0; } void CGenMips::onGenerate() { - int minsize = m_ImageSize_H; - if (m_ImageSize_W > m_ImageSize_H) - minsize = m_ImageSize_W; - int temp = m_MipLevelSizes.count() - m_MipLevels; - m_MipLevels = temp - 1; - minsize = minsize >> m_MipLevels; - m_CFilterParams.nMinSize = minsize; + int levelSize = (m_MipLevelSizes.size() - 1) - m_MipLevels; + if ((levelSize < 0) || (levelSize >= MAX_MIPLEVEL_SUPPORTED)) + levelSize = 0; + m_CFilterParams.nMinSize = m_minsize[levelSize]; emit generateMIPMap(m_CFilterParams, this->m_mipsitem); hide(); } @@ -185,10 +191,28 @@ void CGenMips::valueChanged(QtProperty* property, const QVariant& value) { default: m_GroupProperty->setEnabled(true); m_propertyGamma->setEnabled(false); // Not implemented in D3DX options - m_CFilterParams.nFilterType = 1; // Using D3DX options + m_CFilterParams.nFilterType = 1; // Using D3DX options + int dxFilter = CMP_D3DX_FILTER_POINT; // Default + + // CMP_D3DX_FILTER_NONE is skipped as it cause the mip map image to be enlarged and offset! + + switch (filtertype) { + case 1: + dxFilter = CMP_D3DX_FILTER_POINT; + break; + case 2: + dxFilter = CMP_D3DX_FILTER_LINEAR; + break; + case 3: + dxFilter = CMP_D3DX_FILTER_TRIANGLE; + break; + case 4: + dxFilter = CMP_D3DX_FILTER_BOX; + break; + } + m_CFilterParams.dwMipFilterOptions = - m_CFilterParams.dwMipFilterOptions & - 0xFFFFFFE0 | filtertype; // mapping is 1: CMP_D3DX_FILTER_NONE to 5:CMP_D3DX_FILTER_BOX + m_CFilterParams.dwMipFilterOptions & 0xFFFFFFE0 | dxFilter; break; } } else if (id == QLatin1String("Gamma")) { @@ -233,9 +257,10 @@ void CGenMips::SetGUIItems() { m_propertyFilterType = m_variantPropertyManager->addProperty(QtVariantPropertyManager::enumTypeId(), "Filter Type"); + + // Note: The enum for this should bt a struct type with proper settings to attribtre for D3DX and other options QStringList types; types << "CMP Box" - << "D3DX None" << "D3DX Point" << "D3DX Linear" << "D3DX Triangle" diff --git a/applications/compressonatorgui/components/cpgenmips.h b/applications/compressonatorgui/components/cpgenmips.h index 35428758a..0b34a081e 100644 --- a/applications/compressonatorgui/components/cpgenmips.h +++ b/applications/compressonatorgui/components/cpgenmips.h @@ -27,6 +27,7 @@ #define _GENMIPS_H #include "compressonator.h" +#include "common.h" #include #include @@ -51,10 +52,11 @@ #define CMP_D3DX_FILTER_DITHER (1 << 19) #define CMP_D3DX_FILTER_SRGB (3 << 21) -class CGenMips : public QWidget { +class CGenMips : public QWidget +{ Q_OBJECT - public: +public: CGenMips(const QString title, QWidget* parent); ~CGenMips(); @@ -62,12 +64,13 @@ class CGenMips : public QWidget { void SetGUIItems(); - private: +private: CMP_CFilterParams m_CFilterParams; int m_ImageSize_W; int m_ImageSize_H; int m_MipLevels; + int m_minsize[MAX_MIPLEVEL_SUPPORTED]; QtVariantProperty* m_propertyMipLevels; QtVariantProperty* m_propertyGamma; @@ -77,7 +80,7 @@ class CGenMips : public QWidget { QtVariantProperty* m_propertyMirrorPixels; //QtVariantProperty* m_propertyPerformFiltering; - QStringList m_MipLevelSizes; + QStringList m_MipLevelSizes; void addProperty(QtVariantProperty* property, const QString& id); void addD3DXProperty(QtVariantProperty* property, const QString& id); @@ -98,16 +101,16 @@ class CGenMips : public QWidget { const QString m_title; QWidget* m_parent; - Q_SIGNALS: +Q_SIGNALS: void generateMIPMap(CMP_CFilterParams m_CFilterParams, QTreeWidgetItem* item); - public Q_SLOTS: +public Q_SLOTS: void valueChanged(QtProperty* property, const QVariant& value); void onCancel(); void onGenerate(); - public: +public: QTreeWidgetItem* m_mipsitem; }; #endif diff --git a/applications/compressonatorgui/components/cpimageanalysis.cpp b/applications/compressonatorgui/components/cpimageanalysis.cpp index ca802dfe8..30bb7c79a 100644 --- a/applications/compressonatorgui/components/cpimageanalysis.cpp +++ b/applications/compressonatorgui/components/cpimageanalysis.cpp @@ -25,9 +25,15 @@ #include "cpimageanalysis.h" -#include +#include +#ifdef _CMP_CPP17_ // Build code using std::c++17 #include +namespace sfs = std::filesystem; +#else +#include +namespace sfs = std::experimental::filesystem; +#endif #include extern bool ProgressCallback(float fProgress, CMP_DWORD_PTR pUser1, CMP_DWORD_PTR pUser2); @@ -41,10 +47,10 @@ static inline void helper_toLower(std::string &str) { } bool C_AnalysisData::SourceAndDestFileExtMatch(const char *fsource, const char *fdest) { - std::string fsource_extension = std::filesystem::path(fsource).extension().string(); + std::string fsource_extension = sfs::path(fsource).extension().string(); helper_toLower(fsource_extension); - std::string fdest_extension = std::filesystem::path(fdest).extension().string(); + std::string fdest_extension = sfs::path(fdest).extension().string(); helper_toLower(fdest_extension); return (fsource_extension.compare(fdest_extension) == 0); @@ -54,8 +60,8 @@ CMipImages* C_AnalysisData::GenerateDiffImage(const char *fsource, const char *f int testpassed = 0; std::string src_ext = ""; std::string des_ext = ""; - src_ext = std::filesystem::path(fsource).extension().string(); - des_ext = std::filesystem::path(fdest).extension().string(); + src_ext = sfs::path(fsource).extension().string(); + des_ext = sfs::path(fdest).extension().string(); if (strcmp(src_ext.c_str(), "")==0 || strcmp(des_ext.c_str(), "")==0) { printf("Error: Source or compressed files cannot be found \n"); @@ -67,7 +73,7 @@ CMipImages* C_AnalysisData::GenerateDiffImage(const char *fsource, const char *f helper_toLower(src_ext); helper_toLower(des_ext); - std::filesystem::path file_path = std::filesystem::path(fdest); + sfs::path file_path = sfs::path(fdest); //if (!SourceAndDestFileExtMatch(src_ext.c_str(), des_ext.c_str())) //{ @@ -107,8 +113,8 @@ int C_AnalysisData::GeneratePSNRMSEAnalysis(const char *fsource, const char *fde int testpassed = 0; std::string src_ext = ""; std::string des_ext = ""; - src_ext = std::filesystem::path(fsource).extension().string(); - des_ext = std::filesystem::path(fdest).extension().string(); + src_ext = sfs::path(fsource).extension().string(); + des_ext = sfs::path(fdest).extension().string(); if (strcmp(src_ext.c_str(), "") == 0 || strcmp(des_ext.c_str(), "") == 0) { printf("Error: Source or compressed files cannot be found \n"); @@ -119,7 +125,7 @@ int C_AnalysisData::GeneratePSNRMSEAnalysis(const char *fsource, const char *fde helper_toLower(src_ext); helper_toLower(des_ext); - std::filesystem::path file_path = std::filesystem::path(fdest); + sfs::path file_path = sfs::path(fdest); //if (!SourceAndDestFileExtMatch(src_ext.c_str(), des_ext.c_str())) //{ @@ -152,8 +158,8 @@ int C_AnalysisData::GenerateSSIMAnalysis(const char *fsource, const char *fdest) int testpassed = 0; std::string src_ext = ""; std::string des_ext = ""; - src_ext = std::filesystem::path(fsource).extension().string(); - des_ext = std::filesystem::path(fdest).extension().string(); + src_ext = sfs::path(fsource).extension().string(); + des_ext = sfs::path(fdest).extension().string(); if (strcmp(src_ext.c_str(), "") == 0 || strcmp(des_ext.c_str(), "") == 0) { printf("Error: Source or compressed files cannot be found \n"); @@ -165,7 +171,7 @@ int C_AnalysisData::GenerateSSIMAnalysis(const char *fsource, const char *fdest) helper_toLower(src_ext); helper_toLower(des_ext); - std::filesystem::path file_path = std::filesystem::path(fdest); + sfs::path file_path = sfs::path(fdest); //if (!SourceAndDestFileExtMatch(src_ext.c_str(), des_ext.c_str())) //{ diff --git a/applications/compressonatorgui/components/cpimagecompare.cpp b/applications/compressonatorgui/components/cpimagecompare.cpp index 0589c4f17..5f66e69f5 100644 --- a/applications/compressonatorgui/components/cpimagecompare.cpp +++ b/applications/compressonatorgui/components/cpimagecompare.cpp @@ -85,11 +85,11 @@ CImageCompare::CImageCompare(const QString title, QString file1, QString file2, m_dockToolBar = new QToolBar(tr("dockToolbar")); m_dockToolBar->setObjectName("dockImageViewToolBar"); - ssimAct = new QAction(QIcon(":/CompressonatorGUI/Images/ssim.png"), tr("&Run SSIM Analysis"), this); - psnrmseAct = new QAction(QIcon(":/CompressonatorGUI/Images/psnrmse.png"), tr("&Run PSNR & MSE Analysis"), this); + ssimAct = new QAction(QIcon(":/compressonatorgui/images/ssim.png"), tr("&Run SSIM Analysis"), this); + psnrmseAct = new QAction(QIcon(":/compressonatorgui/images/psnrmse.png"), tr("&Run PSNR & MSE Analysis"), this); - hlayoutAct = new QAction(QIcon(":/CompressonatorGUI/Images/horizontal.png"), tr("&Change to horizontal view"), this); - orilayoutAct = new QAction(QIcon(":/CompressonatorGUI/Images/orilayout.png"), tr("&Change to default view"), this); + hlayoutAct = new QAction(QIcon(":/compressonatorgui/images/horizontal.png"), tr("&Change to horizontal view"), this); + orilayoutAct = new QAction(QIcon(":/compressonatorgui/images/orilayout.png"), tr("&Change to default view"), this); orilayoutAct->setDisabled(true); m_dockToolBar->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea | Qt::RightToolBarArea | Qt::LeftToolBarArea); @@ -386,17 +386,15 @@ bool CImageCompare::setAnalysisResultView() { m_psnrAnalysis->m_PSNR_Green = std::stod(child->first_node("PSNR_GREEN")->value()); m_psnrAnalysis->m_PSNR_Red = std::stod(child->first_node("PSNR_RED")->value()); } else if (ssimAnalysis && psnrAnalysis) { - - m_allAnalysis->m_SSIM = std::stod(child->first_node("SSIM")->value()); - m_allAnalysis->m_SSIM_Blue = std::stod(child->first_node("SSIM_BLUE")->value()); - m_allAnalysis->m_SSIM_Green = std::stod(child->first_node("SSIM_GREEN")->value()); - m_allAnalysis->m_SSIM_Red = std::stod(child->first_node("PSNSSIM_REDR_RED")->value()); - - m_allAnalysis->m_MSE = std::stod(child->first_node("MSE")->value()); - m_allAnalysis->m_PSNR = std::stod(child->first_node("PSNR")->value()); - m_allAnalysis->m_PSNR_Blue = std::stod(child->first_node("PSNR_BLUE")->value()); - m_allAnalysis->m_PSNR_Green = std::stod(child->first_node("PSNR_GREEN")->value()); - m_allAnalysis->m_PSNR_Red = std::stod(child->first_node("PSNR_RED")->value()); + m_allAnalysis->m_SSIM = std::stod(child->first_node("SSIM")->value()); + m_allAnalysis->m_SSIM_Blue = std::stod(child->first_node("SSIM_BLUE")->value()); + m_allAnalysis->m_SSIM_Green = std::stod(child->first_node("SSIM_GREEN")->value()); + m_allAnalysis->m_SSIM_Red = std::stod(child->first_node("SSIM_RED")->value()); + m_allAnalysis->m_MSE = std::stod(child->first_node("MSE")->value()); + m_allAnalysis->m_PSNR = std::stod(child->first_node("PSNR")->value()); + m_allAnalysis->m_PSNR_Blue = std::stod(child->first_node("PSNR_BLUE")->value()); + m_allAnalysis->m_PSNR_Green = std::stod(child->first_node("PSNR_GREEN")->value()); + m_allAnalysis->m_PSNR_Red = std::stod(child->first_node("PSNR_RED")->value()); } } } diff --git a/applications/compressonatorgui/components/cpimageloader.cpp b/applications/compressonatorgui/components/cpimageloader.cpp index 444dd92e4..3bfda54a7 100644 --- a/applications/compressonatorgui/components/cpimageloader.cpp +++ b/applications/compressonatorgui/components/cpimageloader.cpp @@ -511,7 +511,7 @@ CMipImages *CImageLoader::LoadPluginImage(std::string filename, CMP_Feedback_Pro QFile file(qfilename); if (!file.exists()) { MipImages->m_Error = MIPIMAGE_FORMAT_ERRORS::Format_InvalidFile; - image = new QImage(":/CompressonatorGUI/Images/ImageFileDoesNotExist.png"); + image = new QImage(":/compressonatorgui/images/imagefiledoesnotexist.png"); usedQT = true; } @@ -522,6 +522,7 @@ CMipImages *CImageLoader::LoadPluginImage(std::string filename, CMP_Feedback_Pro QFileInfo fi(qfilename); QString ext = fi.suffix().toUpper(); bool useAMD_Plugin = true; + bool isError = false; // ------------------------------------------------------- // Exception on load as DDS for BCn < 6 is not working @@ -545,8 +546,9 @@ CMipImages *CImageLoader::LoadPluginImage(std::string filename, CMP_Feedback_Pro if (tmpMipSet == NULL) { MipImages->errMsg = decompSetting.errMessage; - image = new QImage(":/CompressonatorGUI/Images/DeCompressImageError.png"); + image = new QImage(":/compressonatorgui/images/decompressimageerror.png"); usedQT = true; + isError = true; } else image = MIPS2QImage(m_CMips, tmpMipSet, 0, 0, m_options, pFeedbackProc); } @@ -600,7 +602,7 @@ CMipImages *CImageLoader::LoadPluginImage(std::string filename, CMP_Feedback_Pro //----------------------------------------------- if (MipImages->mipset) { - if (MipImages->mipset->m_nMipLevels > 1 || MipImages->mipset->m_nDepth > 1) + if (((MipImages->mipset->m_nMipLevels > 1 || MipImages->mipset->m_nDepth > 1)) && !isError) UpdateMIPMapImages(MipImages); } @@ -611,7 +613,7 @@ CMipImages *CImageLoader::LoadPluginImage(std::string filename, CMP_Feedback_Pro if ((MipImages->mipset == NULL) && (MipImages->QImage_list[0].size() == 0)) { MipImages->m_Error = MIPIMAGE_FORMAT_ERRORS::Format_NotSupported; // we have a bug to fix!! - QImage *image = new QImage(":/CompressonatorGUI/Images/notsupportedImage.png"); + QImage *image = new QImage(":/compressonatorgui/images/notsupportedimage.png"); if (image) { MipImages->QImage_list[0].push_back(image); MipImages->m_MipImageFormat = MIPIMAGE_FORMAT::Format_QImage; diff --git a/applications/compressonatorgui/components/cpimagepropertyview.cpp b/applications/compressonatorgui/components/cpimagepropertyview.cpp index 1b239ec5f..4f046a69f 100644 --- a/applications/compressonatorgui/components/cpimagepropertyview.cpp +++ b/applications/compressonatorgui/components/cpimagepropertyview.cpp @@ -404,7 +404,7 @@ void CImagePropertyView::compressionValueChanged(QVariant& value) { DXT1_Alpha = true; m_infotext->append("Format Description"); m_infotext->append( - "A four component opaque (or 1-bit alpha) compressed texture format for Microsoft DirectX10. DXT1 identical to BC1. Four bits per " + "A four component opaque (or 1-bit alpha) compressed texture format. DXT1 identical to BC1. Four bits per " "pixel."); break; case C_Destination_Options::BC2: @@ -413,7 +413,7 @@ void CImagePropertyView::compressionValueChanged(QVariant& value) { Channel_Weights = true; m_infotext->append("Format Description"); m_infotext->append( - "A four component compressed texture format with explicit alpha for Microsoft DirectX10. DXT3 identical to BC2. Eight bits per pixel."); + "A four component compressed texture format with explicit alpha. DXT3 identical to BC2. Eight bits per pixel."); break; case C_Destination_Options::BC3: case C_Destination_Options::DXT5: @@ -421,14 +421,14 @@ void CImagePropertyView::compressionValueChanged(QVariant& value) { Channel_Weights = true; m_infotext->append("Format Description"); m_infotext->append( - "A four component compressed texture format with interpolated alpha for Microsoft DirectX10. DXT5 identical to BC3. Eight bits per " + "A four component compressed texture format with interpolated alpha. DXT5 identical to BC3. Eight bits per " "pixel."); break; case C_Destination_Options::BC4: case C_Destination_Options::BC4_S: Quality_Settings = true; m_infotext->append("Format Description"); - m_infotext->append("A single component compressed texture format for Microsoft DirectX10. Identical to ATI1N. Four bits per pixel."); + m_infotext->append("A single component compressed texture. BC4 identical to ATI1N. Four bits per pixel. BC4_S is used for signed components"); break; case C_Destination_Options::BC5: case C_Destination_Options::BC5_S: @@ -437,7 +437,7 @@ void CImagePropertyView::compressionValueChanged(QVariant& value) { case C_Destination_Options::ATI2N_DXT5: Quality_Settings = true; m_infotext->append("Format Description"); - m_infotext->append("A two component compressed texture format for Microsoft DirectX10. BC5 identical to ATI2N. Eight bits per pixel."); + m_infotext->append("A two component compressed texture format. BC5 identical to ATI2N. Eight bits per pixel. BC5_S is used for signed components"); break; case C_Destination_Options::ASTC: Quality_Settings = true; diff --git a/applications/compressonatorgui/components/cpimageview.cpp b/applications/compressonatorgui/components/cpimageview.cpp index 282211578..cbefe6ee2 100644 --- a/applications/compressonatorgui/components/cpimageview.cpp +++ b/applications/compressonatorgui/components/cpimageview.cpp @@ -24,16 +24,19 @@ #include "cpimageview.h" #include "acimageview.h" -void cpImageView::oncpImageViewVirtualMousePosition(QPointF* scenePos, QPointF* localPos, int onID) { +void cpImageView::oncpImageViewVirtualMousePosition(QPointF* scenePos, QPointF* localPos, int onID) +{ oncpImageViewMousePosition(scenePos, localPos, onID); } -void cpImageView::showToobarButton(bool show) { +void cpImageView::showToobarButton(bool show) +{ if (custTitleBar) custTitleBar->setButtonToolBarShow(show); } -void cpImageView::oncpImageViewMousePosition(QPointF* scenePos, QPointF* localPos, int onID) { +void cpImageView::oncpImageViewMousePosition(QPointF* scenePos, QPointF* localPos, int onID) +{ Q_UNUSED(scenePos); Q_UNUSED(onID); @@ -41,7 +44,8 @@ void cpImageView::oncpImageViewMousePosition(QPointF* scenePos, QPointF* localPo return; // is mouse inside image view - if (localPos) { + if (localPos) + { m_localPos = *localPos; // Rounds up pixel co-ordinates // if in debugMode: blocks at 0,0 will start at 1,1 @@ -55,12 +59,17 @@ void cpImageView::oncpImageViewMousePosition(QPointF* scenePos, QPointF* localPo QColor color; QString Txt = ""; - switch (m_ImageViewState) { - case eImageViewState::isOriginal: { + switch (m_ImageViewState) + { + case eImageViewState::isOriginal: + { //m_labelTxtView->setText("Original"); - if (m_acImageView->m_imageItem_Original) { + if (m_acImageView->m_imageItem_Original) + { color = m_acImageView->m_imageItem_Original->pixmap().toImage().pixel(localPos->rx(), localPos->ry()); - } else { + } + else + { // original image is a root on project explorer if (m_acImageView->m_imageItem_Processed) color = m_acImageView->m_imageItem_Processed->pixmap().toImage().pixel(localPos->rx(), localPos->ry()); @@ -69,27 +78,67 @@ void cpImageView::oncpImageViewMousePosition(QPointF* scenePos, QPointF* localPo bool dispDefault = true; // New feature for debugging code that uses alpha channel - if (m_OriginalMipImages && m_CMips) { - if (m_OriginalMipImages->mipset) { - MipLevel* mipLevel = m_CMips->GetMipLevel(m_OriginalMipImages->mipset, m_DepthLevel); - if (mipLevel) { - CMP_BYTE* pData = mipLevel->m_pbData; - if (pData) { - if ((y < mipLevel->m_nHeight) && (x < mipLevel->m_nWidth)) { - // For Channel data RGBA 8:8:8:8 in mipset - if (m_OriginalMipImages->mipset->m_ChannelFormat == CF_8bit) { - CMP_DWORD pixoffset = 0; - if ((x > 0) && (y > 0)) - pixoffset = (y - 1) * mipLevel->m_nWidth * 4 + (x - 1) * 4; - if ((mipLevel->m_dwLinearSize > 3) && (pixoffset < mipLevel->m_dwLinearSize - 3)) { - dispDefault = false; - Txt.sprintf("RGBA Source (%3d,%3d,%3d,%3d) ", - pData[pixoffset], - pData[pixoffset + 1], - pData[pixoffset + 2], - pData[pixoffset + 3]); - // Rendered (%3d,%3d,%3d,%3d) ", - //color.red(), color.green(), color.blue(), color.alpha()); + if (m_OriginalMipImages && m_CMips) + { + if (m_OriginalMipImages->mipset) + { + MipLevel* mipLevel = m_CMips->GetMipLevel(m_OriginalMipImages->mipset, m_acImageView->m_currentMiplevel,m_DepthLevel); + if (mipLevel) + { + if (m_OriginalMipImages->mipset->m_format == CMP_FORMAT_RGBA_8888_S) + { + CMP_SBYTE* pData = mipLevel->m_psbData; + if (pData) + { + if ((y <= mipLevel->m_nHeight) && (x <= mipLevel->m_nWidth)) + { + // For Channel data RGBA 8:8:8:8 in mipset + if (m_OriginalMipImages->mipset->m_ChannelFormat == CF_8bit) + { + CMP_DWORD pixoffset = 0; + if ((x > 0) && (y > 0)) + pixoffset = (y - 1) * mipLevel->m_nWidth * 4 + (x - 1) * 4; + if ((mipLevel->m_dwLinearSize > 3) && (pixoffset < mipLevel->m_dwLinearSize - 3)) + { + dispDefault = false; + Txt.sprintf("RGBA_S Source (%3d,%3d,%3d,%3d) Rendered (%3d,%3d,%3d,%3d)", + pData[pixoffset], + pData[pixoffset + 1], + pData[pixoffset + 2], + pData[pixoffset + 3], + color.red(), + color.green(), + color.blue(), + color.alpha()); + } + } + } + } + } + else + { + CMP_BYTE* pData = mipLevel->m_pbData; + if (pData) + { + if ((y <= mipLevel->m_nHeight) && (x <= mipLevel->m_nWidth)) + { + // For Channel data RGBA 8:8:8:8 in mipset + if (m_OriginalMipImages->mipset->m_ChannelFormat == CF_8bit) + { + CMP_DWORD pixoffset = 0; + if ((x > 0) && (y > 0)) + pixoffset = (y - 1) * mipLevel->m_nWidth * 4 + (x - 1) * 4; + if ((mipLevel->m_dwLinearSize > 3) && (pixoffset < mipLevel->m_dwLinearSize - 3)) + { + dispDefault = false; + Txt.sprintf("RGBA Source (%3d,%3d,%3d,%3d) ", + pData[pixoffset], + pData[pixoffset + 1], + pData[pixoffset + 2], + pData[pixoffset + 3]); + // Rendered (%3d,%3d,%3d,%3d) ", + //color.red(), color.green(), color.blue(), color.alpha()); + } } } } @@ -104,13 +153,15 @@ void cpImageView::oncpImageViewMousePosition(QPointF* scenePos, QPointF* localPo break; case eImageViewState::isDiff: m_labelTxtView->setText(TXT_IMAGE_DIFF); - if (m_acImageView->m_imageItem_Processed) { + if (m_acImageView->m_imageItem_Processed) + { color = m_acImageView->m_imageItem_Processed->pixmap().toImage().pixel(localPos->rx(), localPos->ry()); // normalize color back prior to any brightness or contrast adjustments for the user view int r = color.red(); int g = color.green(); int b = color.blue(); - if (g_Application_Options.m_imagediff_contrast != 0) { + if (g_Application_Options.m_imagediff_contrast != 0) + { r = (r / g_Application_Options.m_imagediff_contrast); g = (g / g_Application_Options.m_imagediff_contrast); b = (b / g_Application_Options.m_imagediff_contrast); @@ -118,14 +169,85 @@ void cpImageView::oncpImageViewMousePosition(QPointF* scenePos, QPointF* localPo color.setRed(r); color.setGreen(g); color.setBlue(b); - Txt.sprintf("RGB [%3d,%3d,%3d]", color.red(), color.green(), color.blue()); + Txt.sprintf("RGB Diff [%3d,%3d,%3d]", color.red(), color.green(), color.blue()); } break; case eImageViewState::isProcessed: m_labelTxtView->setText(TXT_IMAGE_PROCESSED); - if (m_acImageView->m_imageItem_Processed) { - color = m_acImageView->m_imageItem_Processed->pixmap().toImage().pixel(localPos->rx(), localPos->ry()); - Txt.sprintf(" RGBA (%3d,%3d,%3d,%3d)", color.red(), color.green(), color.blue(), color.alpha()); + if (m_acImageView->m_imageItem_Processed) + { + bool dispDefault = true; + + if (m_acImageView->m_MipImages) + { + color = m_acImageView->m_imageItem_Processed->pixmap().toImage().pixel(localPos->rx(), localPos->ry()); + if (m_acImageView->m_MipImages->decompressedMipSet) + { + MipLevel* mipLevel = + m_CMips->GetMipLevel(m_acImageView->m_MipImages->decompressedMipSet, m_acImageView->m_currentMiplevel, m_DepthLevel); + if (mipLevel) + { + if (m_acImageView->m_MipImages->decompressedMipSet->m_format == CMP_FORMAT_RGBA_8888_S) + { + CMP_SBYTE* pData = mipLevel->m_psbData; + if (pData) + { + if ((y <= mipLevel->m_nHeight) && (x <= mipLevel->m_nWidth)) + { + // For Channel data RGBA 8:8:8:8 in mipset + if (m_acImageView->m_MipImages->decompressedMipSet->m_ChannelFormat == CF_8bit) + { + CMP_DWORD pixoffset = 0; + if ((x > 0) && (y > 0)) + pixoffset = (y - 1) * mipLevel->m_nWidth * 4 + (x - 1) * 4; + if ((mipLevel->m_dwLinearSize > 3) && (pixoffset < mipLevel->m_dwLinearSize - 3)) + { + dispDefault = false; + Txt.sprintf("RGBA_S Source (%3d,%3d,%3d,%3d) Rendered (%3d,%3d,%3d,%3d)", + pData[pixoffset], + pData[pixoffset + 1], + pData[pixoffset + 2], + pData[pixoffset + 3], + color.red(), + color.green(), + color.blue(), + color.alpha()); + } + } + } + } + } + else + { + CMP_BYTE* pData = mipLevel->m_pbData; + if (pData) + { + if ((y <= mipLevel->m_nHeight) && (x <= mipLevel->m_nWidth)) + { + // For Channel data RGBA 8:8:8:8 in mipset + if (m_acImageView->m_MipImages->decompressedMipSet->m_ChannelFormat == CF_8bit) + { + CMP_DWORD pixoffset = 0; + if ((x > 0) && (y > 0)) + pixoffset = (y - 1) * mipLevel->m_nWidth * 4 + (x - 1) * 4; + if ((mipLevel->m_dwLinearSize > 3) && (pixoffset < mipLevel->m_dwLinearSize - 3)) + { + dispDefault = false; + Txt.sprintf("RGBA Source (%3d,%3d,%3d,%3d)", + pData[pixoffset], + pData[pixoffset + 1], + pData[pixoffset + 2], + pData[pixoffset + 3]); + } + } + } + } + } + } + } + } + if (dispDefault) + Txt.sprintf(" RGBA Rendered (%3d,%3d,%3d,%3d)", color.red(), color.green(), color.blue(), color.alpha()); } break; default: @@ -139,7 +261,8 @@ void cpImageView::oncpImageViewMousePosition(QPointF* scenePos, QPointF* localPo #ifdef USE_BCN_IMAGE_DEBUG #ifdef _DEBUG - if (m_acImageView->m_debugMode) { + if (m_acImageView->m_debugMode) + { m_labelColorTxt->setText(""); XBlockNum = x; YBlockNum = y; @@ -165,7 +288,9 @@ void cpImageView::oncpImageViewMousePosition(QPointF* scenePos, QPointF* localPo palette.setColor(QPalette::Background, color); palette.setColor(QPalette::WindowText, color); m_labelColorRGBA->setPalette(palette); - } else { + } + else + { m_labelPos->setText(""); m_labelBlockPos->setText(""); m_labelColorTxt->setText(""); @@ -180,7 +305,8 @@ void cpImageView::oncpImageViewMousePosition(QPointF* scenePos, QPointF* localPo #include "ImfArray.h" #include "ImfRgba.h" -void cpImageView::onImageDiff() { +void cpImageView::onImageDiff() +{ if (!m_CBimageview_Toggle) return; @@ -189,10 +315,13 @@ void cpImageView::onImageDiff() { imageview_ImageDiff->setDisabled(true); - if (m_ImageViewState == eImageViewState::isDiff) { + if (m_ImageViewState == eImageViewState::isDiff) + { m_ImageViewState = eImageViewState::isProcessed; m_CBimageview_Toggle->setItemText(0, TXT_IMAGE_PROCESSED); - } else { + } + else + { m_ImageViewState = eImageViewState::isDiff; m_CBimageview_Toggle->setItemText(0, TXT_IMAGE_DIFF); } @@ -200,7 +329,8 @@ void cpImageView::onImageDiff() { activeview = 0; m_CBimageview_Toggle->setCurrentIndex(0); - if (m_acImageView) { + if (m_acImageView) + { m_acImageView->onToggleImageViews(0); m_acImageView->onSetPixelDiffView(m_ImageViewState == eImageViewState::isDiff); } @@ -214,11 +344,12 @@ void cpImageView::onImageDiff() { setActionForImageViewStateChange(); } -void cpImageView::keyPressEvent(QKeyEvent* event) { +void cpImageView::keyPressEvent(QKeyEvent* event) +{ if (m_acImageView == NULL) return; -#ifndef _LINUX +#ifndef __linux__ //------------------------------------------------------------------------------------ // Feature enabled to allow user to copy an image view onto window system clipboard // This is only available through keyboard entry using: @@ -226,15 +357,18 @@ void cpImageView::keyPressEvent(QKeyEvent* event) { // Alt+C to copy using the original image size // in both cases aspect ratio is maintained. //------------------------------------------------------------------------------------ - if (event->key() == Qt::Key_C) { + if (event->key() == Qt::Key_C) + { bool CTRL_key = event->modifiers() & Qt::ControlModifier; bool ALT_key = event->modifiers() & Qt::AltModifier; - if (CTRL_key || ALT_key) { + if (CTRL_key || ALT_key) + { if (!m_acImageView->m_imageItem_Processed) return; // Copy Scaled image size - if (CTRL_key) { + if (CTRL_key) + { int w = m_acImageView->m_imageItem_Processed->pixmap().width(); int h = m_acImageView->m_imageItem_Processed->pixmap().height(); qreal scale = m_acImageView->m_imageItem_Processed->scale(); // same value as m_ZoomLevel->value() / 100.0f @@ -242,7 +376,9 @@ void cpImageView::keyPressEvent(QKeyEvent* event) { h = int(h * scale); QPixmap pixmap = m_acImageView->m_imageItem_Processed->pixmap().scaled(w, h, Qt::KeepAspectRatio); QApplication::clipboard()->setPixmap(pixmap, QClipboard::Clipboard); - } else { // Copy Original Size + } + else + { // Copy Original Size QPixmap pixmap = m_acImageView->m_imageItem_Processed->pixmap(); QApplication::clipboard()->setPixmap(pixmap, QClipboard::Clipboard); } @@ -256,22 +392,29 @@ void cpImageView::keyPressEvent(QKeyEvent* event) { return; // Checks if we are viewing a processed image view (child node in project explorer) - if (m_CBimageview_Toggle) { - if (event->key() == Qt::Key_D) { // Set View to Processed Image (Index 0) and Enable Diff if applicable + if (m_CBimageview_Toggle) + { + if (event->key() == Qt::Key_D) + { // Set View to Processed Image (Index 0) and Enable Diff if applicable onImageDiff(); - } else if (event->key() == Qt::Key_P) { // View Processed Image (Index 0) + } + else if (event->key() == Qt::Key_P) + { // View Processed Image (Index 0) activeview = 0; m_CBimageview_Toggle->setCurrentIndex(0); // switch to a processed image view if (m_acImageView) m_acImageView->onToggleImageViews(0); oncpImageViewMousePosition(0, &m_localPos, 0); - if (m_ImageViewState == eImageViewState::isDiff) { + if (m_ImageViewState == eImageViewState::isDiff) + { // the current processed view is an image diff to reset the view back to processed with no diff onImageDiff(); } m_ImageViewState = eImageViewState::isProcessed; - } else if (event->key() == Qt::Key_O) { // View Original Image (Index 1) + } + else if (event->key() == Qt::Key_O) + { // View Original Image (Index 1) activeview = 1; m_CBimageview_Toggle->setCurrentIndex(1); // switch to a original image view @@ -279,11 +422,16 @@ void cpImageView::keyPressEvent(QKeyEvent* event) { m_acImageView->onToggleImageViews(1); oncpImageViewMousePosition(0, &m_localPos, 0); m_ImageViewState = eImageViewState::isOriginal; - } else if (event->key() == Qt::Key_Space || event->key() == Qt::Key_S) { - if (activeview == 0) { + } + else if (event->key() == Qt::Key_Space || event->key() == Qt::Key_S) + { + if (activeview == 0) + { activeview = 1; // Original m_ImageViewState = eImageViewState::isOriginal; - } else { + } + else + { activeview = 0; // Processed or "Processed Diff" view if (m_DiffOnOff) m_ImageViewState = eImageViewState::isDiff; @@ -301,8 +449,10 @@ void cpImageView::keyPressEvent(QKeyEvent* event) { // codecs at the lowest block level of encoding. #ifdef USE_BCN_IMAGE_DEBUG #ifdef _DEBUG - if (event->key() == Qt::Key_F1) { - if (m_acImageView->m_debugMode) { + if (event->key() == Qt::Key_F1) + { + if (m_acImageView->m_debugMode) + { MipLevel* mipLevel = m_CMips->GetMipLevel(m_OriginalMipImages->mipset, m_DepthLevel); if (!mipLevel) @@ -332,22 +482,26 @@ void cpImageView::keyPressEvent(QKeyEvent* event) { // napatel check format before getting data! // we have two sets Float16 (2 Bytes alligned) and Float32 (4 Bytes alligned) - if (m_acImageView->m_debugFormat == "BC6H" || m_acImageView->m_debugFormat == "BC6H_SF") { + if (m_acImageView->m_debugFormat == "BC6H" || m_acImageView->m_debugFormat == "BC6H_SF") + { // Encoder input to fill with data CMP_FLOAT in[16][4]; float dec_out[16][4]; BYTE cmp_in[16]; - if (m_OriginalMipImages->mipset->m_ChannelFormat == CF_Float16) { + if (m_OriginalMipImages->mipset->m_ChannelFormat == CF_Float16) + { // Get origin data pointer CMP_HALFSHORT* data = mipLevel->m_phfsData; CMP_HALF* temp = (CMP_HALF*)data; Array2D pixels(4, 4); pixels.resizeErase(4, 4); int d = 0; - for (int row = 0; row < 4; row++) { + for (int row = 0; row < 4; row++) + { index = (row * row_stride) + start_index; - for (int col = 0; col < 4; col++) { + for (int col = 0; col < 4; col++) + { in[d][0] = (float)temp[index]; pixels[row][col].r.setBits(data[index]); in[d][1] = (float)temp[index + 1]; @@ -361,19 +515,24 @@ void cpImageView::keyPressEvent(QKeyEvent* event) { } } hasData = true; - if (event->key() == Qt::Key_S) { + if (event->key() == Qt::Key_S) + { string filename = "exr16f_srcblock_x" + to_string(XBlockNum) + "_y" + to_string(YBlockNum) + ".exr"; Exr::writeRgba(filename, pixels, 4, 4); } - } else if (m_OriginalMipImages->mipset->m_ChannelFormat == CF_Float32) { + } + else if (m_OriginalMipImages->mipset->m_ChannelFormat == CF_Float32) + { // Get origin data pointer float* data = mipLevel->m_pfData; Array2D pixels(4, 4); pixels.resizeErase(4, 4); int d = 0; - for (int row = 0; row < 4; row++) { + for (int row = 0; row < 4; row++) + { index = (row * row_stride) + start_index; - for (int col = 0; col < 4; col++) { + for (int col = 0; col < 4; col++) + { in[d][0] = data[index]; pixels[row][col].r = data[index]; in[d][1] = data[index + 1]; @@ -387,11 +546,14 @@ void cpImageView::keyPressEvent(QKeyEvent* event) { } } hasData = true; - if (event->key() == Qt::Key_S) { + if (event->key() == Qt::Key_S) + { string filename = "exr32f_srcblock_" + to_string(XBlockNum) + "_y" + to_string(YBlockNum) + ".exr"; Exr::writeRgba(filename, pixels, 4, 4); } - } else if (m_OriginalMipImages->mipset->m_ChannelFormat == CF_Compressed) { + } + else if (m_OriginalMipImages->mipset->m_ChannelFormat == CF_Compressed) + { // Get origin data pointer BYTE* data = mipLevel->m_pbData; @@ -400,17 +562,20 @@ void cpImageView::keyPressEvent(QKeyEvent* event) { int cmp_block_offset = 4 * m_OriginalMipImages->mipset->m_nWidth; int offset = (((XBlockNum - 1) * 16)) + ((YBlockNum - 1) * cmp_block_offset); data = data + offset; - for (int d = 0; d < 16; d++) { + for (int d = 0; d < 16; d++) + { cmp_in[d] = data[d]; } hasData = true; } - if (hasData) { + if (hasData) + { // Use SDK interface to Encode a Block for debugging with BC6HBlockEncoder* blockEncode; BYTE output[16]; - if (CMP_InitializeBCLibrary() == BC_ERROR_NONE) { + if (CMP_InitializeBCLibrary() == BC_ERROR_NONE) + { CMP_BC6H_BLOCK_PARAMETERS user_settings; user_settings.fQuality = 1.0; user_settings.bUsePatternRec = false; @@ -419,12 +584,16 @@ void cpImageView::keyPressEvent(QKeyEvent* event) { else user_settings.bIsSigned = true; user_settings.fExposure = 1.00; - if (CMP_CreateBC6HEncoder(user_settings, &blockEncode) == BC_ERROR_NONE) { - if (m_OriginalMipImages->mipset->m_ChannelFormat != CF_Compressed) { + if (CMP_CreateBC6HEncoder(user_settings, &blockEncode) == BC_ERROR_NONE) + { + if (m_OriginalMipImages->mipset->m_ChannelFormat != CF_Compressed) + { CMP_EncodeBC6HBlock(blockEncode, in, output); // Feed compressed output to be decoded into dec_out CMP_DecodeBC6HBlock(output, dec_out); - } else { + } + else + { CMP_DecodeBC6HBlock(cmp_in, dec_out); } CMP_DestroyBC6HEncoder(blockEncode); @@ -432,7 +601,9 @@ void cpImageView::keyPressEvent(QKeyEvent* event) { CMP_ShutdownBCLibrary(); } } - } else if (m_acImageView->m_debugFormat == "BC7") { + } + else if (m_acImageView->m_debugFormat == "BC7") + { // Encoder input to fill with data double in[16][4]; double dec_out[16][4]; @@ -440,9 +611,11 @@ void cpImageView::keyPressEvent(QKeyEvent* event) { // Get origin data pointer BYTE* data = mipLevel->m_pbData; int d = 0; - for (int row = 0; row < 4; row++) { + for (int row = 0; row < 4; row++) + { index = (row * row_stride) + start_index; - for (int col = 0; col < 4; col++) { + for (int col = 0; col < 4; col++) + { in[d][0] = data[index]; in[d][1] = data[index + 1]; in[d][2] = data[index + 2]; @@ -453,17 +626,20 @@ void cpImageView::keyPressEvent(QKeyEvent* event) { } hasData = true; - if (hasData) { + if (hasData) + { // Use SDK interface to Encode a Block for debugging with BC7BlockEncoder* blockEncode; BYTE output[16]; - if (CMP_InitializeBCLibrary() == BC_ERROR_NONE) { + if (CMP_InitializeBCLibrary() == BC_ERROR_NONE) + { CMP_BC6H_BLOCK_PARAMETERS user_settings; user_settings.fQuality = 1.0; user_settings.bUsePatternRec = false; user_settings.bIsSigned = false; user_settings.fExposure = 1.00; - if (CMP_CreateBC7Encoder(0.05, false, false, 0xCF, 1.0, &blockEncode) == BC_ERROR_NONE) { + if (CMP_CreateBC7Encoder(0.05, false, false, 0xCF, 1.0, &blockEncode) == BC_ERROR_NONE) + { CMP_EncodeBC7Block(blockEncode, in, output); // Feed compressed output to be decoded into dec_out CMP_DecodeBC7Block(output, dec_out); @@ -473,7 +649,8 @@ void cpImageView::keyPressEvent(QKeyEvent* event) { } } } - } else + } + else return; } #endif @@ -482,7 +659,8 @@ void cpImageView::keyPressEvent(QKeyEvent* event) { return; } -void cpImageView::GetSourceBlock(int BlockX, int BlockY, string filename) { +void cpImageView::GetSourceBlock(int BlockX, int BlockY, string filename) +{ if (!m_CMips) return; if (!m_OriginalMipImages) @@ -514,7 +692,8 @@ void cpImageView::GetSourceBlock(int BlockX, int BlockY, string filename) { // Incrimental start pixel index from (row,col = 0) position of each 4x4 block int index; - if (m_OriginalMipImages->mipset->m_ChannelFormat == CF_Float16) { + if (m_OriginalMipImages->mipset->m_ChannelFormat == CF_Float16) + { // Encoder input to fill with data CMP_FLOAT in[16][4]; // Get origin data pointer @@ -523,9 +702,11 @@ void cpImageView::GetSourceBlock(int BlockX, int BlockY, string filename) { Array2D pixels(4, 4); pixels.resizeErase(4, 4); int d = 0; - for (int row = 0; row < 4; row++) { + for (int row = 0; row < 4; row++) + { index = (row * row_stride) + start_index; - for (int col = 0; col < 4; col++) { + for (int col = 0; col < 4; col++) + { in[d][0] = (float)temp[index]; pixels[row][col].r.setBits(data[index]); in[d][1] = (float)temp[index + 1]; @@ -539,7 +720,9 @@ void cpImageView::GetSourceBlock(int BlockX, int BlockY, string filename) { } } Exr::writeRgba(filename, pixels, 4, 4); - } else if (m_OriginalMipImages->mipset->m_ChannelFormat == CF_Float32) { + } + else if (m_OriginalMipImages->mipset->m_ChannelFormat == CF_Float32) + { // Encoder input to fill with data CMP_FLOAT in[16][4]; // Get origin data pointer @@ -547,9 +730,11 @@ void cpImageView::GetSourceBlock(int BlockX, int BlockY, string filename) { Array2D pixels(4, 4); pixels.resizeErase(4, 4); int d = 0; - for (int row = 0; row < 4; row++) { + for (int row = 0; row < 4; row++) + { index = (row * row_stride) + start_index; - for (int col = 0; col < 4; col++) { + for (int col = 0; col < 4; col++) + { in[d][0] = data[index]; pixels[row][col].r = data[index]; in[d][1] = data[index + 1]; @@ -564,16 +749,19 @@ void cpImageView::GetSourceBlock(int BlockX, int BlockY, string filename) { } Exr::writeRgba(filename, pixels, 4, 4); } // else CF_Float3 - else { + else + { QColor color; QPixmap pixmap(4, 4); QImage image = pixmap.toImage(); CMP_BYTE* data = mipLevel->m_pbData; int d = 0; - for (int h = 0; h < 4; h++) { + for (int h = 0; h < 4; h++) + { index = (h * row_stride) + start_index; - for (int w = 0; w < 4; w++) { + for (int w = 0; w < 4; w++) + { color.setRed(data[index]); color.setGreen(data[index + 1]); color.setBlue(data[index + 2]); @@ -587,58 +775,70 @@ void cpImageView::GetSourceBlock(int BlockX, int BlockY, string filename) { } } -void cpImageView::EnableMipLevelDisplay(int level) { +void cpImageView::EnableMipLevelDisplay(int level) +{ if (level <= 1) return; - for (int num = 0; num < level; num++) { + for (int num = 0; num < level; num++) + { m_CBimageview_MipLevel->addItem("MipLevel : " + QString::number(num)); } m_CBimageview_MipLevel->setEnabled(true); m_MipLevels = level; } -void cpImageView::EnableDepthLevelDisplay(int level) { +void cpImageView::EnableDepthLevelDisplay(int level) +{ if (level < 0) return; - for (int num = 0; num < level; num++) { + for (int num = 0; num < level; num++) + { m_CBimageview_DepthLevel->addItem("Frames : " + QString::number(num)); } m_CBimageview_DepthLevel->setEnabled(true); m_DepthLevel = level; } -cpImageView::~cpImageView() { - if (m_ExrProperties) { +cpImageView::~cpImageView() +{ + if (m_ExrProperties) + { delete m_ExrProperties; } - if (m_localMipImages) { + if (m_localMipImages) + { if (m_processedMipImages) m_imageLoader->clearMipImages(&m_processedMipImages); delete m_imageLoader; m_imageLoader = NULL; } - if (m_imageLoader) { + if (m_imageLoader) + { delete m_imageLoader; m_imageLoader = NULL; } - if (m_CMips) { + if (m_CMips) + { delete m_CMips; m_CMips = NULL; } - if (Plastique_style) { + if (Plastique_style) + { delete Plastique_style; Plastique_style = NULL; } } -void cpImageView::setActionForImageViewStateChange() { - switch (m_ImageViewState) { +void cpImageView::setActionForImageViewStateChange() +{ + switch (m_ImageViewState) + { case eImageViewState::isOriginal: case eImageViewState::isDiff: if (imageview_ResetImageView) @@ -667,7 +867,8 @@ void cpImageView::setActionForImageViewStateChange() { m_BrightnessLevel->setEnabled(false); if (m_CBimageview_ToolList) m_CBimageview_ToolList->setEnabled(false); - if (m_ExrProperties) { + if (m_ExrProperties) + { if (m_ExrProperties->isVisible()) m_ExrProperties->hide(); } @@ -705,7 +906,8 @@ void cpImageView::setActionForImageViewStateChange() { } } -void cpImageView::InitData() { +void cpImageView::InitData() +{ m_imageSize = {0, 0}; ID = 0; m_localMipImages = false; @@ -769,7 +971,8 @@ cpImageView::cpImageView(const QString filePathName, CMipImages* MipImages, Setting* setting, CMipImages* CompressedMipImages) - : acCustomDockWidget(filePathName, parent) { + : acCustomDockWidget(filePathName, parent) +{ if (!setting) return; InitData(); @@ -784,20 +987,28 @@ cpImageView::cpImageView(const QString filePathName, getSupportedImageFormats(); - if (MipImages) { - if (setting->reloadImage && !setting->generateDiff && !setting->generateMips) { + if (MipImages) + { + if (setting->reloadImage && !setting->generateDiff && !setting->generateMips) + { m_imageLoader = new CImageLoader(); - if (m_imageLoader) { + if (m_imageLoader) + { m_processedMipImages = m_imageLoader->LoadPluginImage(filePathName.toStdString()); } - } else + } + else m_processedMipImages = MipImages; - } else { + } + else + { m_imageLoader = new CImageLoader(); - if (m_imageLoader) { + if (m_imageLoader) + { m_processedMipImages = m_imageLoader->LoadPluginImage(filePathName.toStdString()); m_localMipImages = true; - } else + } + else m_processedMipImages = NULL; } @@ -805,15 +1016,21 @@ cpImageView::cpImageView(const QString filePathName, // else we just use the current miplevels, this pointer can be either compressed or uncompressed data // check its MIPIMAGE_FORMAT property to determine which one it is - if (CompressedMipImages) { - if (CompressedMipImages->mipset) { + if (CompressedMipImages) + { + if (CompressedMipImages->mipset) + { m_CompressedMipImages = CompressedMipImages->mipset->m_compressed; - } else { + } + else + { m_CompressedMipImages = true; } m_OriginalMipImages = CompressedMipImages; m_ImageViewState = eImageViewState::isProcessed; - } else { + } + else + { m_CompressedMipImages = false; m_OriginalMipImages = m_processedMipImages; m_ImageViewState = eImageViewState::isOriginal; @@ -833,7 +1050,8 @@ cpImageView::cpImageView(const QString filePathName, // Center Widget //=============== m_newWidget = new QWidget(parent); - if (!m_newWidget) { + if (!m_newWidget) + { // ToDo::Need to process error! return; } @@ -841,20 +1059,25 @@ cpImageView::cpImageView(const QString filePathName, //=================== // Get MipLevels //=================== - if (m_processedMipImages) { - if (m_processedMipImages->mipset) { + if (m_processedMipImages) + { + if (m_processedMipImages->mipset) + { m_MipLevels = m_processedMipImages->mipset->m_nMipLevels; m_MaxDepthLevel = m_processedMipImages->mipset->m_nDepth; // check levels with number of images to view //if (m_processedMipImages->m_MipImageFormat == MIPIMAGE_FORMAT::Format_QImage) //{ int count = m_processedMipImages->QImage_list[0].size(); - if (count <= 1) { + if (count <= 1) + { m_MipLevels = 0; } //} } - } else { + } + else + { m_MipLevels = 0; m_DepthLevel = 0; } @@ -881,14 +1104,19 @@ cpImageView::cpImageView(const QString filePathName, connect(actSaveBlockView, SIGNAL(triggered()), this, SLOT(onSaveBlockView())); m_viewContextMenu->addAction(actSaveBlockView); - if (Title.contains("File#")) { + if (Title.contains("File#")) + { custTitleBar->setTitle(Title + ": " + filePathName); - } else { + } + else + { // Need to check if MipImages is valid here!! - if (m_processedMipImages) { + if (m_processedMipImages) + { QString gpuView = ""; bool useGPUView = false; - switch (m_processedMipImages->m_DecompressedFormat) { + switch (m_processedMipImages->m_DecompressedFormat) + { case MIPIMAGE_FORMAT_DECOMPRESSED::Format_CPU: custTitleBar->setTitle("Compressed Image: CPU View"); break; @@ -901,8 +1129,10 @@ cpImageView::cpImageView(const QString filePathName, break; } - if (useGPUView) { - switch (m_processedMipImages->m_MipImageFormat) { + if (useGPUView) + { + switch (m_processedMipImages->m_MipImageFormat) + { case MIPIMAGE_FORMAT::Format_OpenGL: gpuView += "using OpenGL"; custTitleBar->setTitle(gpuView); @@ -923,7 +1153,8 @@ cpImageView::cpImageView(const QString filePathName, } } - if (m_acImageView->m_graphicsScene) { + if (m_acImageView->m_graphicsScene) + { ID = m_acImageView->m_graphicsScene->ID; m_imageSize = m_acImageView->m_MipImages->QImage_list[0].front()->size(); m_acImageView->enableNavigation(true); @@ -980,7 +1211,8 @@ cpImageView::cpImageView(const QString filePathName, // Zoom level m_ZoomLevel = new QSpinBox(this); - if (m_ZoomLevel) { + if (m_ZoomLevel) + { QLabel* ZoomLabel = new QLabel(this); ZoomLabel->setText("Zoom:"); m_toolBar->addWidget(ZoomLabel); @@ -999,7 +1231,8 @@ cpImageView::cpImageView(const QString filePathName, this->m_useOriginalImageCursor = true; setting->onBrightness = true; m_BrightnessLevel = new QSpinBox(this); - if (m_BrightnessLevel) { + if (m_BrightnessLevel) + { QLabel* ZoomLabel = new QLabel(this); ZoomLabel->setText(" Brightness:"); m_toolBar->addWidget(ZoomLabel); @@ -1010,20 +1243,23 @@ cpImageView::cpImageView(const QString filePathName, m_toolBar->addWidget(m_BrightnessLevel); } - imageview_ViewImageOriginalSize = new QAction(QIcon(":/CompressonatorGUI/Images/cx100.png"), tr("Original Size"), this); - if (imageview_ViewImageOriginalSize) { + imageview_ViewImageOriginalSize = new QAction(QIcon(":/compressonatorgui/images/cx100.png"), tr("Original Size"), this); + if (imageview_ViewImageOriginalSize) + { m_toolBar->addAction(imageview_ViewImageOriginalSize); connect(imageview_ViewImageOriginalSize, SIGNAL(triggered()), m_acImageView, SLOT(onViewImageOriginalSize())); } - imageview_FitInWindow = new QAction(QIcon(":/CompressonatorGUI/Images/cxFit.png"), tr("&Fit in Window"), this); - if (imageview_FitInWindow) { + imageview_FitInWindow = new QAction(QIcon(":/compressonatorgui/images/cxfit.png"), tr("&Fit in Window"), this); + if (imageview_FitInWindow) + { m_toolBar->addAction(imageview_FitInWindow); connect(imageview_FitInWindow, SIGNAL(triggered()), m_acImageView, SLOT(onFitInWindow())); } - imageview_ResetImageView = new QAction(QIcon(":/CompressonatorGUI/Images/OriginalImage.png"), tr("Reset Image View"), this); - if (imageview_ResetImageView) { + imageview_ResetImageView = new QAction(QIcon(":/compressonatorgui/images/originalimage.png"), tr("Reset Image View"), this); + if (imageview_ResetImageView) + { m_toolBar->addAction(imageview_ResetImageView); connect(imageview_ResetImageView, SIGNAL(triggered()), m_acImageView, SLOT(onResetImageView())); connect(imageview_ResetImageView, SIGNAL(triggered()), this, SLOT(oncpResetImageView())); @@ -1031,29 +1267,33 @@ cpImageView::cpImageView(const QString filePathName, m_toolBar->addSeparator(); - imageview_ToggleChannelR = new QAction(QIcon(":/CompressonatorGUI/Images/cxRed.png"), tr("Show or Hide Red channel"), this); - if (imageview_ToggleChannelR) { + imageview_ToggleChannelR = new QAction(QIcon(":/compressonatorgui/images/cxred.png"), tr("Show or Hide Red channel"), this); + if (imageview_ToggleChannelR) + { imageview_ToggleChannelR->setCheckable(true); m_toolBar->addAction(imageview_ToggleChannelR); connect(imageview_ToggleChannelR, SIGNAL(triggered()), m_acImageView, SLOT(onToggleChannelR())); } - imageview_ToggleChannelG = new QAction(QIcon(":/CompressonatorGUI/Images/cxGreen.png"), tr("Show or Hide Green channel"), this); - if (imageview_ToggleChannelG) { + imageview_ToggleChannelG = new QAction(QIcon(":/compressonatorgui/images/cxgreen.png"), tr("Show or Hide Green channel"), this); + if (imageview_ToggleChannelG) + { imageview_ToggleChannelG->setCheckable(true); m_toolBar->addAction(imageview_ToggleChannelG); connect(imageview_ToggleChannelG, SIGNAL(triggered()), m_acImageView, SLOT(onToggleChannelG())); } - imageview_ToggleChannelB = new QAction(QIcon(":/CompressonatorGUI/Images/cxBlue.png"), tr("Show or Hide Blue channel"), this); - if (imageview_ToggleChannelB) { + imageview_ToggleChannelB = new QAction(QIcon(":/compressonatorgui/images/cxblue.png"), tr("Show or Hide Blue channel"), this); + if (imageview_ToggleChannelB) + { imageview_ToggleChannelB->setCheckable(true); m_toolBar->addAction(imageview_ToggleChannelB); connect(imageview_ToggleChannelB, SIGNAL(triggered()), m_acImageView, SLOT(onToggleChannelB())); } - imageview_ToggleChannelA = new QAction(QIcon(":/CompressonatorGUI/Images/cxAlpha.png"), tr("Show or Hide Alpha channel"), this); - if (imageview_ToggleChannelA) { + imageview_ToggleChannelA = new QAction(QIcon(":/compressonatorgui/images/cxalpha.png"), tr("Show or Hide Alpha channel"), this); + if (imageview_ToggleChannelA) + { imageview_ToggleChannelA->setCheckable(true); m_toolBar->addAction(imageview_ToggleChannelA); connect(imageview_ToggleChannelA, SIGNAL(triggered()), m_acImageView, SLOT(onToggleChannelA())); @@ -1061,51 +1301,59 @@ cpImageView::cpImageView(const QString filePathName, m_toolBar->addSeparator(); - imageview_ToggleGrayScale = new QAction(QIcon(":/CompressonatorGUI/Images/cxgrayscale.png"), tr("Gray Scale"), this); - if (imageview_ToggleGrayScale) { + imageview_ToggleGrayScale = new QAction(QIcon(":/compressonatorgui/images/cxgrayscale.png"), tr("Gray Scale"), this); + if (imageview_ToggleGrayScale) + { imageview_ToggleGrayScale->setCheckable(true); m_toolBar->addAction(imageview_ToggleGrayScale); connect(imageview_ToggleGrayScale, SIGNAL(triggered()), m_acImageView, SLOT(onToggleGrayScale())); } - imageview_InvertImage = new QAction(QIcon(":/CompressonatorGUI/Images/cxInvert.png"), tr("Invert Image"), this); - if (imageview_InvertImage) { + imageview_InvertImage = new QAction(QIcon(":/compressonatorgui/images/cxinvert.png"), tr("Invert Image"), this); + if (imageview_InvertImage) + { m_toolBar->addAction(imageview_InvertImage); connect(imageview_InvertImage, SIGNAL(triggered()), m_acImageView, SLOT(onInvertImage())); } - imageview_MirrorHorizontal = new QAction(QIcon(":/CompressonatorGUI/Images/MirrorHorizonal.png"), tr("Mirror Image Horizontally"), this); - if (imageview_MirrorHorizontal) { + imageview_MirrorHorizontal = new QAction(QIcon(":/compressonatorgui/images/mirrorhorizonal.png"), tr("Mirror Image Horizontally"), this); + if (imageview_MirrorHorizontal) + { m_toolBar->addAction(imageview_MirrorHorizontal); connect(imageview_MirrorHorizontal, SIGNAL(triggered()), m_acImageView, SLOT(onMirrorHorizontal())); } - imageview_MirrorVirtical = new QAction(QIcon(":/CompressonatorGUI/Images/MirrorVertical.png"), tr("Mirror Image Vertically"), this); - if (imageview_MirrorVirtical) { + imageview_MirrorVirtical = new QAction(QIcon(":/compressonatorgui/images/mirrorvertical.png"), tr("Mirror Image Vertically"), this); + if (imageview_MirrorVirtical) + { m_toolBar->addAction(imageview_MirrorVirtical); connect(imageview_MirrorVirtical, SIGNAL(triggered()), m_acImageView, SLOT(onMirrorVirtical())); } - imageview_RotateRight = new QAction(QIcon(":/CompressonatorGUI/Images/cxRotationR.png"), tr("Rotate Image 90 Degrees"), this); - if (imageview_RotateRight) { + imageview_RotateRight = new QAction(QIcon(":/compressonatorgui/images/cxrotationr.png"), tr("Rotate Image 90 Degrees"), this); + if (imageview_RotateRight) + { m_toolBar->addAction(imageview_RotateRight); connect(imageview_RotateRight, SIGNAL(triggered()), m_acImageView, SLOT(onRotateRight())); } - imageview_RotateLeft = new QAction(QIcon(":/CompressonatorGUI/Images/cxRotationL.png"), tr("Rotate Image -90 Degrees"), this); - if (imageview_RotateLeft) { + imageview_RotateLeft = new QAction(QIcon(":/compressonatorgui/images/cxrotationl.png"), tr("Rotate Image -90 Degrees"), this); + if (imageview_RotateLeft) + { m_toolBar->addAction(imageview_RotateLeft); connect(imageview_RotateLeft, SIGNAL(triggered()), m_acImageView, SLOT(onRotateLeft())); } - if (m_MaxDepthLevel > 1) { + if (m_MaxDepthLevel > 1) + { m_toolBar->addSeparator(); QLabel* MDepthLevelLabel = new QLabel(this); MDepthLevelLabel->setText("Frame:"); m_toolBar->addWidget(MDepthLevelLabel); m_CBimageview_DepthLevel = new QComboBox(this); - for (int num = 0; num < m_MaxDepthLevel; num++) { + for (int num = 0; num < m_MaxDepthLevel; num++) + { QString depthLevelList = QString::number(num + 1); m_CBimageview_DepthLevel->addItem(depthLevelList); } @@ -1114,7 +1362,8 @@ cpImageView::cpImageView(const QString filePathName, m_toolBar->addWidget(m_CBimageview_DepthLevel); } - if (m_MipLevels > 0) { + if (m_MipLevels > 0) + { m_toolBar->addSeparator(); QLabel* MipLevelLabel = new QLabel(this); MipLevelLabel->setText("MipLevel:"); @@ -1125,21 +1374,27 @@ cpImageView::cpImageView(const QString filePathName, int processedImage_miplevel_max = m_processedMipImages->QImage_list[0].size(); // check if we have miplevels in Original Image if its available match its level with the processed - if (m_OriginalMipImages && (setting->input_image == eImageViewState::isProcessed)) { - if (m_OriginalMipImages->QImage_list[0].size() < processedImage_miplevel_max) { - QMessageBox msgBox; - QMessageBox::warning(this, - "MipLevel", - "Original image MipMap Levels do not match the Processed image levels.\nLevels will be limited, retry by regenerating " - "original image mip levels", - QMessageBox::Ok); + if (m_OriginalMipImages && (setting->input_image == eImageViewState::isProcessed)) + { + if (m_OriginalMipImages->QImage_list[0].size() < processedImage_miplevel_max) + { + // Remove this + // QMessageBox msgBox; + // QMessageBox::warning(this, + // "MipLevel", + // "Original image MipMap Levels do not match the Processed image levels.\nLevels will be limited, retry by regenerating " + // "original image mip levels", + // QMessageBox::Ok); processedImage_miplevel_max = m_OriginalMipImages->QImage_list[0].size(); } } - for (int num = 0; num < m_MipLevels; num++) { - if (m_processedMipImages) { - if (processedImage_miplevel_max > num) { + for (int num = 0; num < m_MipLevels; num++) + { + if (m_processedMipImages) + { + if (processedImage_miplevel_max > num) + { QString mipLevelList = QString::number(num + 1); QImage* image = m_processedMipImages->QImage_list[0][num]; mipLevelList.append(QString(" (")); @@ -1160,10 +1415,12 @@ cpImageView::cpImageView(const QString filePathName, m_toolBar->addSeparator(); - if (setting->input_image == eImageViewState::isProcessed) { + if (setting->input_image == eImageViewState::isProcessed) + { // This combo box is used as one of two methods to changes image view states, the other is user keypress m_CBimageview_Toggle = new QComboBox(this); - if (m_CBimageview_Toggle) { + if (m_CBimageview_Toggle) + { m_CBimageview_Toggle->addItem(tr(TXT_IMAGE_PROCESSED)); m_CBimageview_Toggle->addItem(tr(TXT_IMAGE_ORIGINAL)); connect(m_CBimageview_Toggle, SIGNAL(currentIndexChanged(int)), this, SLOT(onToggleViewChanged(int))); @@ -1172,13 +1429,15 @@ cpImageView::cpImageView(const QString filePathName, } imageview_ImageDiff = - new QAction(QIcon(":/CompressonatorGUI/Images/imagediff.png"), tr("&View Image Diff [can use 'd' key to toggle on or off]"), this); - if (imageview_ImageDiff) { + new QAction(QIcon(":/compressonatorgui/images/imagediff.png"), tr("&View Image Diff [can use 'd' key to toggle on or off]"), this); + if (imageview_ImageDiff) + { m_toolBar->addAction(imageview_ImageDiff); connect(imageview_ImageDiff, SIGNAL(triggered()), this, SLOT(onImageDiff())); } m_toolBar->addSeparator(); - } else + } + else m_CBimageview_Toggle = NULL; #ifdef USE_BCN_IMAGE_DEBUG @@ -1197,10 +1456,12 @@ cpImageView::cpImageView(const QString filePathName, #endif #endif - if (m_processedMipImages && (m_processedMipImages->mipset != NULL)) { + if (m_processedMipImages && (m_processedMipImages->mipset != NULL)) + { if (m_processedMipImages->mipset->m_format == CMP_FORMAT_ARGB_32F || (m_processedMipImages->mipset->m_format == CMP_FORMAT_ARGB_16F) || - (m_processedMipImages->mipset->m_format == CMP_FORMAT_RGBE_32F) || (m_processedMipImages->mipset->m_format == CMP_FORMAT_BC6H) || - (m_processedMipImages->mipset->m_format == CMP_FORMAT_BC6H_SF)) { + (m_processedMipImages->mipset->m_format == CMP_FORMAT_RGBE_32F) || (m_processedMipImages->mipset->m_format == CMP_FORMAT_BC6H) || + (m_processedMipImages->mipset->m_format == CMP_FORMAT_BC6H_SF)) + { m_ExrProperties = new acEXRTool(); // Tool list QLabel* GridLabel = new QLabel(this); @@ -1213,7 +1474,8 @@ cpImageView::cpImageView(const QString filePathName, m_CBimageview_ToolList->setStyle(Plastique_style); m_toolBar->addWidget(m_CBimageview_ToolList); connect(m_CBimageview_ToolList, SIGNAL(activated(int)), this, SLOT(onToolListChanged(int))); - if (m_ExrProperties) { + if (m_ExrProperties) + { connect(m_ExrProperties->exrExposureBox, SIGNAL(valueChanged(double)), m_acImageView, SLOT(onExrExposureChanged(double))); connect(m_ExrProperties->exrDefogBox, SIGNAL(valueChanged(double)), m_acImageView, SLOT(onExrDefogChanged(double))); connect(m_ExrProperties->exrKneeLowBox, SIGNAL(valueChanged(double)), m_acImageView, SLOT(onExrKneeLowChanged(double))); @@ -1238,13 +1500,16 @@ cpImageView::cpImageView(const QString filePathName, #endif // IF we have processed images then enable PSNR view to user - if (m_processedMipImages->decompressedMipSet != NULL) { + if (m_processedMipImages->decompressedMipSet != NULL) + { m_PSNRLabel = new QLabel(this); - if (m_PSNRLabel) { + if (m_PSNRLabel) + { m_PSNRLabel->setText("PSNR: Not Available"); m_toolBar->addWidget(m_PSNRLabel); } - } else + } + else m_PSNRLabel = NULL; m_toolBar->setMaximumHeight(25); @@ -1257,7 +1522,7 @@ cpImageView::cpImageView(const QString filePathName, hlayout2->setContentsMargins(0, 0, 0, 0); hlayout2->addWidget(m_toolBar, 0); - QString Navigation = QStringLiteral(":/CompressonatorGUI/Images/navigate.png"); + QString Navigation = QStringLiteral(":/compressonatorgui/images/navigate.png"); m_statusBar = new QStatusBar(this); m_statusBar->setStyleSheet("QStatusBar{border-top: 1px outset grey; border-bottom: 1px outset grey;}"); @@ -1285,7 +1550,8 @@ cpImageView::cpImageView(const QString filePathName, m_labelBlockPos->setAlignment(Qt::AlignLeft); m_labelTxtView = new QLabel(this); - switch (setting->input_image) { + switch (setting->input_image) + { case eImageViewState::isOriginal: m_ImageViewState = eImageViewState::isOriginal; m_labelTxtView->setText(TXT_IMAGE_ORIGINAL); @@ -1293,8 +1559,10 @@ cpImageView::cpImageView(const QString filePathName, case eImageViewState::isDiff: m_ImageViewState = eImageViewState::isDiff; // Set a default Contrast for image diff views - if (m_acImageView) { - if (m_acImageView->m_imageItem_Processed) { + if (m_acImageView) + { + if (m_acImageView->m_imageItem_Processed) + { m_acImageView->m_imageItem_Processed->m_fContrast = g_Application_Options.m_imagediff_contrast; m_acImageView->setBrightnessLevel(0); } @@ -1334,22 +1602,30 @@ cpImageView::cpImageView(const QString filePathName, } // User selected a view from drop down combo box or called from a key event -void cpImageView::onToggleViewChanged(int view) { +void cpImageView::onToggleViewChanged(int view) +{ if (!m_CBimageview_Toggle) return; QString itemView = m_CBimageview_Toggle->itemText(view); - if (itemView.compare(TXT_IMAGE_PROCESSED) == 0) { + if (itemView.compare(TXT_IMAGE_PROCESSED) == 0) + { m_ImageViewState = eImageViewState::isProcessed; m_labelTxtView->setText(TXT_IMAGE_PROCESSED); - } else if (itemView.compare(TXT_IMAGE_DIFF) == 0) { + } + else if (itemView.compare(TXT_IMAGE_DIFF) == 0) + { m_ImageViewState = eImageViewState::isDiff; m_labelTxtView->setText(TXT_IMAGE_DIFF); - } else if (itemView.compare(TXT_IMAGE_ORIGINAL) == 0) { + } + else if (itemView.compare(TXT_IMAGE_ORIGINAL) == 0) + { m_ImageViewState = eImageViewState::isOriginal; m_labelTxtView->setText(TXT_IMAGE_ORIGINAL); - } else { + } + else + { // send an error message ... return; } @@ -1361,7 +1637,8 @@ void cpImageView::onToggleViewChanged(int view) { m_acImageView->onToggleImageViews(view); } -void cpImageView::oncpResetImageView() { +void cpImageView::oncpResetImageView() +{ imageview_ToggleChannelR->setChecked(false); imageview_ToggleChannelG->setChecked(false); imageview_ToggleChannelB->setChecked(false); @@ -1372,22 +1649,28 @@ void cpImageView::oncpResetImageView() { m_BrightnessLevel->setValue(0); } -void cpImageView::onDecompressUsing(int useDecomp) { +void cpImageView::onDecompressUsing(int useDecomp) +{ Q_UNUSED(useDecomp); } -void cpImageView::onResetHDRandDiff(int MipLevel) { +void cpImageView::onResetHDRandDiff(int MipLevel) +{ // on a MipLevel Change Diff views are reset back to processed views // Makesure the text in the image view ComboBox lable is "Processed" - if (MipLevel > 0) { - if (m_CBimageview_Toggle) { - if (m_ImageViewState == eImageViewState::isDiff) { + if (MipLevel > 0) + { + if (m_CBimageview_Toggle) + { + if (m_ImageViewState == eImageViewState::isDiff) + { onImageDiff(); } } } - if (m_ExrProperties) { + if (m_ExrProperties) + { if (m_ExrProperties->isVisible()) m_ExrProperties->hide(); m_ExrProperties->exrExposureBox->setValue(DEFAULT_EXPOSURE); @@ -1398,10 +1681,13 @@ void cpImageView::onResetHDRandDiff(int MipLevel) { } } -void cpImageView::onToolListChanged(int index) { - switch (index) { +void cpImageView::onToolListChanged(int index) +{ + switch (index) + { case 1: //EXR - if (m_ExrProperties) { + if (m_ExrProperties) + { if (m_ExrProperties->isVisible()) m_ExrProperties->raise(); else @@ -1415,8 +1701,10 @@ void cpImageView::onToolListChanged(int index) { } } -void cpImageView::onViewCustomContextMenu(const QPoint& point) { - if (actSaveBlockView) { +void cpImageView::onViewCustomContextMenu(const QPoint& point) +{ + if (actSaveBlockView) + { QString strSaveBlock; strSaveBlock.sprintf("Save Source Block (%d,%d) as", m_source_BlockXPos, m_source_BlockYPos); actSaveBlockView->setText(strSaveBlock); @@ -1424,15 +1712,19 @@ void cpImageView::onViewCustomContextMenu(const QPoint& point) { m_viewContextMenu->exec(m_acImageView->mapToGlobal(point)); } -void cpImageView::onSaveViewAs() { - if (m_acImageView && actSaveBlockView) { +void cpImageView::onSaveViewAs() +{ + if (m_acImageView && actSaveBlockView) + { QString ImageFilter; ImageFilter = m_QtImageFilter; bool hasFloatData = false; // Add EXR if source is HDR - if (m_processedMipImages) { - if ((m_processedMipImages->mipset->m_ChannelFormat == CF_Float16) || (m_processedMipImages->mipset->m_ChannelFormat == CF_Float32)) { + if (m_processedMipImages) + { + if ((m_processedMipImages->mipset->m_ChannelFormat == CF_Float16) || (m_processedMipImages->mipset->m_ChannelFormat == CF_Float32)) + { ImageFilter.insert(ImageFilter.length() - 1, "*.exr;"); } } @@ -1448,48 +1740,60 @@ void cpImageView::onSaveViewAs() { SuggetedFileNamePath.append(fileInfo.baseName()); // Set suggested file target type - if (hasFloatData) { + if (hasFloatData) + { SuggetedFileNamePath.append("_view.dds"); - } else + } + else SuggetedFileNamePath.append("_view.bmp"); std::string ext; QString filePathName; bool done = false; - do { + do + { filePathName = QFileDialog::getSaveFileName(this, tr("Save Image View as"), SuggetedFileNamePath, ImageFilter); if (filePathName.length() == 0) return; ext = CMP_GetFilePathExtension(filePathName.toStdString()); transform(ext.begin(), ext.end(), ext.begin(), ::tolower); string supported_ExtListings = ImageFilter.toStdString(); - if (supported_ExtListings.find(ext) != std::string::npos) { + if (supported_ExtListings.find(ext) != std::string::npos) + { done = true; - } else { + } + else + { if (QMessageBox::question(this, "Save Image View", "File extension is not supported try again?", QMessageBox::Yes | QMessageBox::No) == - QMessageBox::No) + QMessageBox::No) return; } } while (!done); // get file extension to choose for Qt file or Compressonator File save using .dds,.ktx or .exr plugins - if ((ext.compare("exr") == 0) || (ext.compare("dds") == 0) || (ext.compare("ktx") == 0)) { + if ((ext.compare("exr") == 0) || (ext.compare("dds") == 0) || (ext.compare("ktx") == 0) || (ext.compare("ktx2") == 0)) + { int mipLevel = 0; - if (m_CBimageview_MipLevel) { + if (m_CBimageview_MipLevel) + { mipLevel = m_CBimageview_MipLevel->currentIndex(); } int depthLevel = 0; - if (m_CBimageview_DepthLevel) { + if (m_CBimageview_DepthLevel) + { depthLevel = m_CBimageview_DepthLevel->currentIndex(); } - if (m_processedMipImages && hasFloatData) { - if (mipLevel > 0) { + if (m_processedMipImages && hasFloatData) + { + if (mipLevel > 0) + { MipLevel* pInMipLevel = m_CMips->GetMipLevel(m_processedMipImages->mipset, mipLevel, depthLevel); - if (pInMipLevel == NULL) { + if (pInMipLevel == NULL) + { PrintInfo("Error: MipLevel Data failed to retrieved."); return; } @@ -1497,7 +1801,8 @@ void cpImageView::onSaveViewAs() { // Create a temporary mipset for saving the miplevel data MipSet* pMipLevelMipSet; pMipLevelMipSet = new MipSet(); - if (pMipLevelMipSet == NULL) { + if (pMipLevelMipSet == NULL) + { PrintInfo("Error: Failed to allocate mipset for saving."); return; } @@ -1539,14 +1844,20 @@ void cpImageView::onSaveViewAs() { // delete the temporary mipset used for saving delete pMipLevelMipSet; - } else + } + else AMDSaveMIPSTextureImage(filePathName.toStdString().c_str(), m_processedMipImages->mipset, false, g_CmdPrams.CompressOptions); - } else { - if (m_OriginalMipImages) { + } + else + { + if (m_OriginalMipImages) + { AMDSaveMIPSTextureImage(filePathName.toStdString().c_str(), m_OriginalMipImages->mipset, false, g_CmdPrams.CompressOptions); } } - } else { + } + else + { QPixmap pixmap = m_acImageView->m_imageItem_Processed->pixmap(); QImage img = pixmap.toImage(); img.save(filePathName); @@ -1554,7 +1865,8 @@ void cpImageView::onSaveViewAs() { } } -void cpImageView::getSupportedImageFormats() { +void cpImageView::getSupportedImageFormats() +{ m_QtImageFilter = "Images ("; #ifdef USE_SaveViewAs_ALL_FILE_FORMATS @@ -1563,7 +1875,8 @@ void cpImageView::getSupportedImageFormats() { // Upppercase List QList::Iterator i; - for (i = QtFormats.begin(); i != QtFormats.end(); ++i) { + for (i = QtFormats.begin(); i != QtFormats.end(); ++i) + { QByteArray fformat = (*i); fformat = fformat.toUpper(); m_QtImageFilter.append("*."); @@ -1579,8 +1892,10 @@ void cpImageView::getSupportedImageFormats() { m_QtImageFilter.append(")"); } -void cpImageView::onSaveBlockView() { - if (m_acImageView && m_OriginalMipImages) { +void cpImageView::onSaveBlockView() +{ + if (m_acImageView && m_OriginalMipImages) + { QString ImageFilter; if ((m_OriginalMipImages->mipset->m_ChannelFormat == CF_Float16) || (m_OriginalMipImages->mipset->m_ChannelFormat == CF_Float32)) @@ -1600,18 +1915,22 @@ void cpImageView::onSaveBlockView() { std::string ext; QString filePathName; bool done = false; - do { + do + { filePathName = QFileDialog::getSaveFileName(this, tr("Save Block Image as"), SuggetedFileNamePath, ImageFilter); if (filePathName.length() == 0) return; ext = CMP_GetFilePathExtension(filePathName.toStdString()); transform(ext.begin(), ext.end(), ext.begin(), ::tolower); string supported_ExtListings = ImageFilter.toStdString(); - if (supported_ExtListings.find(ext) != std::string::npos) { + if (supported_ExtListings.find(ext) != std::string::npos) + { done = true; - } else { + } + else + { if (QMessageBox::question(this, "Save Block Image", "File extension is not supported try again?", QMessageBox::Yes | QMessageBox::No) == - QMessageBox::No) + QMessageBox::No) return; } } while (!done); @@ -1621,44 +1940,56 @@ void cpImageView::onSaveBlockView() { } } -void cpImageView::showToobar(bool show) { +void cpImageView::showToobar(bool show) +{ if (show) m_toolBar->show(); else m_toolBar->hide(); } -void cpImageView::OnToolBarClicked() { +void cpImageView::OnToolBarClicked() +{ if (m_toolBar->isVisible()) m_toolBar->hide(); else m_toolBar->show(); } -void cpImageView::paintEvent(QPaintEvent* event) { +void cpImageView::paintEvent(QPaintEvent* event) +{ Q_UNUSED(event); - if (m_FitOnShow) { + if (m_FitOnShow) + { imageview_FitInWindow->trigger(); m_FitOnShow = false; } - if (m_CBimageview_MipLevel) { - if ((m_processedMipImages) && (m_CBimageview_MipLevel->isEnabled() == false)) { + if (m_CBimageview_MipLevel) + { + if ((m_processedMipImages) && (m_CBimageview_MipLevel->isEnabled() == false)) + { // need to find root cause of 0xFEEEFEEE - if ((m_processedMipImages->mipset) && (m_processedMipImages->mipset != (void*)0xFEEEFEEE)) { - if (m_MipLevels != m_processedMipImages->mipset->m_nMipLevels) { + if ((m_processedMipImages->mipset) && (m_processedMipImages->mipset != (void*)0xFEEEFEEE)) + { + if (m_MipLevels != m_processedMipImages->mipset->m_nMipLevels) + { EnableMipLevelDisplay(m_processedMipImages->mipset->m_nMipLevels); } } } } - if (m_CBimageview_DepthLevel) { - if ((m_processedMipImages) && (m_CBimageview_DepthLevel->isEnabled() == false)) { + if (m_CBimageview_DepthLevel) + { + if ((m_processedMipImages) && (m_CBimageview_DepthLevel->isEnabled() == false)) + { // need to find root cause of 0xFEEEFEEE - if ((m_processedMipImages->mipset) && (m_processedMipImages->mipset != (void*)0xFEEEFEEE)) { - if (m_MaxDepthLevel != m_processedMipImages->mipset->m_nDepth) { + if ((m_processedMipImages->mipset) && (m_processedMipImages->mipset != (void*)0xFEEEFEEE)) + { + if (m_MaxDepthLevel != m_processedMipImages->mipset->m_nDepth) + { EnableDepthLevelDisplay(m_processedMipImages->mipset->m_nDepth); } } @@ -1666,11 +1997,14 @@ void cpImageView::paintEvent(QPaintEvent* event) { } } -void cpImageView::showEvent(QShowEvent*) { +void cpImageView::showEvent(QShowEvent*) +{ } -void cpImageView::closeEvent(QCloseEvent*) { - if (m_ExrProperties) { +void cpImageView::closeEvent(QCloseEvent*) +{ + if (m_ExrProperties) + { if (m_ExrProperties->isVisible()) m_ExrProperties->hide(); } @@ -1678,21 +2012,25 @@ void cpImageView::closeEvent(QCloseEvent*) { // This slot is received when user changes zoom using tool bar zoom -void cpImageView::onZoomLevelChanged(int value) { +void cpImageView::onZoomLevelChanged(int value) +{ if (!m_bOnacScaleChange) emit OnSetScale(value); } // This slot is received when user changes zoom using mouse wheel event in acImageView -void cpImageView::onacScaleChange(int value) { +void cpImageView::onacScaleChange(int value) +{ m_bOnacScaleChange = true; m_ZoomLevel->setValue(value); m_bOnacScaleChange = false; } -void cpImageView::onacPSNRUpdated(double value) { - if (m_PSNRLabel && value > 0) { +void cpImageView::onacPSNRUpdated(double value) +{ + if (m_PSNRLabel && value > 0) + { char buff[16]; snprintf(buff, sizeof(buff), "PSNR: %3.1f dB", value); m_PSNRLabel->setText(buff); @@ -1701,8 +2039,10 @@ void cpImageView::onacPSNRUpdated(double value) { // This slot is received when user changes brightness level using tool bar zoom -void cpImageView::onBrightnessLevelChanged(int value) { - if (m_acImageView->m_imageItem_Processed) { +void cpImageView::onBrightnessLevelChanged(int value) +{ + if (m_acImageView->m_imageItem_Processed) + { m_acImageView->setBrightnessLevel(value); } } diff --git a/applications/compressonatorgui/components/cpprojectdata.h b/applications/compressonatorgui/components/cpprojectdata.h index 32b9eecb7..5ce1b9ccb 100644 --- a/applications/compressonatorgui/components/cpprojectdata.h +++ b/applications/compressonatorgui/components/cpprojectdata.h @@ -1221,24 +1221,28 @@ class C_Destination_Options : public C_Destination_Image { DXT5_RGxB, DXT5_xGxR, ARGB_8888, + // ARGB_8888_S, ARGB_16F, ARGB_32F, + + //ARGB_16, //RGB_888, //RG_8, //R_8, //ARGB_2101010, - //ARGB_16, //RG_16, //R_16, //RG_16F, //R_16F, //RG_32F, //R_32F, + MESH_DATA }; C_Destination_Options() { init(); + m_Compression = C_Destination_Options::BC7; } @@ -1249,7 +1253,6 @@ class C_Destination_Options : public C_Destination_Image { m_FileSize = 0; m_DstWidth = 0; m_DstHeight = 0; - m_Compression = C_Destination_Options::BC7; //m_Encoding = No_Encoding; m_Quality = AMD_CODEC_QUALITY_DEFAULT; m_isselected = false; // Flag to force Project View to use the datas child item in compression diff --git a/applications/compressonatorgui/components/cpprojectview.cpp b/applications/compressonatorgui/components/cpprojectview.cpp index c47abd373..283a55f1f 100644 --- a/applications/compressonatorgui/components/cpprojectview.cpp +++ b/applications/compressonatorgui/components/cpprojectview.cpp @@ -26,9 +26,9 @@ #include "cmdline.h" #ifdef USE_MESHOPTIMIZER -#include "../../_Plugins/CMesh/mesh_optimizer/mesh_optimizer.h" +#include "../../_plugins/cmesh/mesh_optimizer/mesh_optimizer.h" #else -#include "../../_Plugins/CMesh/Tootle/mesh_tootle.h" +#include "../../_plugins/cmesh/tootle/mesh_tootle.h" #endif #include "cpmaincomponents.h" @@ -44,6 +44,10 @@ #include #include +#ifndef _WIN32 +#include // for usleep() +#endif + #define STATUS_SUCCESS (0x00000000) // ToDo(s) // Clean up allocated memory for each tree nodes m_data @@ -62,18 +66,22 @@ extern C_Application_Options g_Application_Options; extern CMIPS* g_GUI_CMIPS; extern double timeStampsec(); -int levelType(QTreeWidgetItem* it) { +int levelType(QTreeWidgetItem* it) +{ QVariant v = it->data(TREE_LevelType, Qt::UserRole); int levelType = v.toInt(); return levelType; } -void UpdateDestglTFWithFile(QString modelSource, QString modelDest, QString file, QString newfile, bool userdeleted) { +void UpdateDestglTFWithFile(QString modelSource, QString modelDest, QString file, QString newfile, bool userdeleted) +{ std::ifstream fstreamsrc(modelSource.toStdString()); - if (!fstreamsrc) { + if (!fstreamsrc) + { QString error = "Error:Reading " + modelSource + " failed.\n"; PrintInfo(error.toStdString().c_str()); - if (!(QFile::exists(modelSource))) { + if (!(QFile::exists(modelSource))) + { error = "Error: " + modelSource + " does not exist.\n"; PrintInfo(error.toStdString().c_str()); } @@ -86,10 +94,12 @@ void UpdateDestglTFWithFile(QString modelSource, QString modelDest, QString file fstreamsrc.close(); std::ifstream fstreamdest(modelDest.toStdString()); - if (!fstreamdest) { + if (!fstreamdest) + { QString error = "Error:Reading " + modelDest + " failed. glTF failed to be updated.\n"; PrintInfo(error.toStdString().c_str()); - if (!(QFile::exists(modelDest))) { + if (!(QFile::exists(modelDest))) + { error = "Error: " + modelDest + " does not exist.\n"; PrintInfo(error.toStdString().c_str()); } @@ -108,7 +118,8 @@ void UpdateDestglTFWithFile(QString modelSource, QString modelDest, QString file unsigned int i = 0; bool updated = false; - while (i < srcimages.size()) { + while (i < srcimages.size()) + { std::string srcname = srcimages[i]["uri"].get(); // extract source texture filename + path @@ -118,11 +129,13 @@ void UpdateDestglTFWithFile(QString modelSource, QString modelDest, QString file QFileInfo fileInfo(file); QString filename(fileInfo.fileName()); - if (srcfilename == filename) { + if (srcfilename == filename) + { updated = true; if (newfile == "") gltfdest["images"][i]["uri"] = qsrcname.toStdString(); - else { + else + { // texture filename with path QString path = fileInfosrc.path(); gltfdest["images"][i]["uri"] = (path + "/" + newfile).toStdString(); @@ -132,9 +145,11 @@ void UpdateDestglTFWithFile(QString modelSource, QString modelDest, QString file i++; } - if (!updated) { + if (!updated) + { auto srcmesh = gltfsrc["buffers"]; - for (unsigned int i = 0; i < srcmesh.size(); i++) { + for (unsigned int i = 0; i < srcmesh.size(); i++) + { std::string srcname = srcmesh[i]["uri"].get(); // extract source mesh filename + path @@ -144,11 +159,13 @@ void UpdateDestglTFWithFile(QString modelSource, QString modelDest, QString file QFileInfo fileInfo(file); QString filename(fileInfo.fileName()); - if (srcfilename == filename) { + if (srcfilename == filename) + { updated = true; if (newfile == "") gltfdest["buffers"][i]["uri"] = qsrcname.toStdString(); - else { + else + { // mesh filename with path QString path = fileInfosrc.path(); gltfdest["buffers"][i]["uri"] = (path + "/" + newfile).toStdString(); @@ -160,11 +177,15 @@ void UpdateDestglTFWithFile(QString modelSource, QString modelDest, QString file // Remove the old file first then stream in a new one std::ofstream ofstreamdest(modelDest.toStdString(), std::ios_base::out); - if (!ofstreamdest) { + if (!ofstreamdest) + { QString error = "Error: opening gltf compressed file for update: " + modelDest + "[" + strerror(errno) + "]"; - if (!userdeleted) { + if (!userdeleted) + { PrintInfo(error.toStdString().c_str()); - } else { + } + else + { QMessageBox msgBox; msgBox.setText(error); msgBox.exec(); @@ -177,17 +198,22 @@ void UpdateDestglTFWithFile(QString modelSource, QString modelDest, QString file // Checks the nodes parent, if its a 3DModel the items destination FileName path // is replaced in the gltf file using the items source FileName Path as referance -void UpdateDestglTFAfterProcess(QTreeWidgetItem* item) { - if (item) { +void UpdateDestglTFAfterProcess(QTreeWidgetItem* item) +{ + if (item) + { QTreeWidgetItem* parent = item->parent(); - if (parent) { + if (parent) + { // Verify its root QVariant v = parent->data(TREE_LevelType, Qt::UserRole); int ParentlevelType = v.toInt(); - if (ParentlevelType == TREETYPE_3DSUBMODEL_DATA || ParentlevelType == TREETYPE_3DMODEL_DATA) { + if (ParentlevelType == TREETYPE_3DSUBMODEL_DATA || ParentlevelType == TREETYPE_3DMODEL_DATA) + { QVariant data = item->data(TREE_SourceInfo, Qt::UserRole); C_Destination_Options* m_data = data.value(); - if (m_data) { + if (m_data) + { QFileInfo destfileInfo(m_data->m_destFileNamePath); QString destfilename(destfileInfo.fileName()); UpdateDestglTFWithFile(m_data->m_modelSource, m_data->m_modelDest, m_data->m_sourceFileNamePath, destfilename, false); @@ -197,7 +223,8 @@ void UpdateDestglTFAfterProcess(QTreeWidgetItem* item) { } } -ProjectView::ProjectView(const QString title, CompressStatusDialog* StatusDialog, QWidget* parent) { +ProjectView::ProjectView(const QString title, CompressStatusDialog* StatusDialog, QWidget* parent) +{ setWindowTitle(title); m_parent = parent; m_CompressStatusDialog = StatusDialog; @@ -253,17 +280,17 @@ ProjectView::ProjectView(const QString title, CompressStatusDialog* StatusDialog QString imageList = ""; - QList::Iterator i; - for (i = m_supportedFormats.begin(); i != m_supportedFormats.end(); ++i) { - QByteArray fformat = (*i); - m_ImageFileFilter.append("*."); - m_ImageFileFilter.append(fformat); - m_ImageFileFilter.append(" "); - - imageList.append("*."); - imageList.append(fformat); - imageList.append(";;"); - } + // QList::Iterator i; + // for (i = m_supportedFormats.begin(); i != m_supportedFormats.end(); ++i) { + // QByteArray fformat = (*i); + // m_ImageFileFilter.append("*."); + // m_ImageFileFilter.append(fformat); + // m_ImageFileFilter.append(" "); + // + // imageList.append("*."); + // imageList.append(fformat); + // imageList.append(";;"); + // } m_ImageFileFilter.append(");;"); m_ImageFileFilter.append(imageList); @@ -284,12 +311,14 @@ ProjectView::ProjectView(const QString title, CompressStatusDialog* StatusDialog connect(m_newProjectwindow, SIGNAL(OnSetNewProject(QString&)), this, SLOT(onSetNewProject(QString&))); } -void ProjectView::setCurrentProjectName(QString filePathName) { +void ProjectView::setCurrentProjectName(QString filePathName) +{ QFileInfo fileInfo(filePathName); QString filename; if (filePathName.contains(".cprj")) filename = fileInfo.completeBaseName(); - else { + else + { QString fitempo = QString(filePathName + ".cprj"); QFileInfo fileInfo2(fitempo); filename = fileInfo2.completeBaseName(); @@ -299,7 +328,8 @@ void ProjectView::setCurrentProjectName(QString filePathName) { file.setFileName(filePathName); bool isWritable = file.open(QIODevice::ReadWrite); - if (!isWritable) { + if (!isWritable) + { QFileInfo fileInfo(m_curProjectFilePathName); m_curProjectFilePathName = fileInfo.dir().path(); m_curProjectFilePathName.append(QDir::separator()); @@ -307,7 +337,9 @@ void ProjectView::setCurrentProjectName(QString filePathName) { #ifdef _WIN32 m_curProjectFilePathName.replace("/", "\\"); #endif - } else { + } + else + { m_curProjectFilePathName = filePathName; } @@ -319,55 +351,69 @@ void ProjectView::setCurrentProjectName(QString filePathName) { m_parent->setWindowTitle(filename); } -void ProjectView::SignalUpdateData(QTreeWidgetItem* item, int levelType) { +void ProjectView::SignalUpdateData(QTreeWidgetItem* item, int levelType) +{ if (!item) return; QVariant v = item->data(TREE_SourceInfo, Qt::UserRole); - switch (levelType) { + switch (levelType) + { case TREETYPE_Double_Click_here_to_add_files: emit UpdateData(&m_globalProcessSetting); break; case TREETYPE_MESH_DATA: - case TREETYPE_COMPRESSION_DATA: { + case TREETYPE_COMPRESSION_DATA: + { C_Destination_Options* m_data = v.value(); - if (m_data) { - if ((m_globalProcessSetting.m_Quality > 0.0f) && (g_Application_Options.m_ImageEncode != C_Application_Options::ImageEncodeWith::GPU_HW)) { + if (m_data) + { + if ((m_globalProcessSetting.m_Quality > 0.0f) && (g_Application_Options.m_ImageEncode != C_Application_Options::ImageEncodeWith::GPU_HW)) + { m_data->m_globalSetting_quality = m_globalProcessSetting.m_Quality; m_data->m_globalSetting_qualityEnabled = true; - } else + } + else m_data->m_globalSetting_qualityEnabled = false; emit UpdateData(m_data); } } break; - case TREETYPE_3DMODEL_DATA: { + case TREETYPE_3DMODEL_DATA: + { C_3DModel_Info* m_data = v.value(); if (m_data) emit UpdateData(m_data); } break; - case TREETYPE_3DSUBMODEL_DATA: { + case TREETYPE_3DSUBMODEL_DATA: + { C_3DSubModel_Info* m_data = v.value(); if (m_data) emit UpdateData(m_data); } break; - case TREETYPE_VIEWMESH_ONLY_NODE: { + case TREETYPE_VIEWMESH_ONLY_NODE: + { C_Mesh_Buffer_Info* data = v.value(); - if (data) { + if (data) + { emit UpdateData(data); } } case TREETYPE_VIEWIMAGE_ONLY_NODE: case TREETYPE_IMAGEFILE_DATA: - default: { + default: + { C_Source_Info* imagedata = v.value(); - if (imagedata) { + if (imagedata) + { // Need a better way to do this via Objects DATA Properties - if (imagedata->m_MipImages) { - if (imagedata->m_MipImages->mipset) { + if (imagedata->m_MipImages) + { + if (imagedata->m_MipImages->mipset) + { if (imagedata->m_MipImages->mipset->m_nMipLevels >= 1) imagedata->m_Mip_Levels = imagedata->m_MipImages->mipset->m_nMipLevels; imagedata->m_Depth = imagedata->m_MipImages->mipset->m_nDepth; // depthsupport @@ -381,40 +427,48 @@ void ProjectView::SignalUpdateData(QTreeWidgetItem* item, int levelType) { } } -bool ProjectView::AnySelectedItems() { +bool ProjectView::AnySelectedItems() +{ bool userSelected = false; int ItemsCount; int NumCompressItems; int numSelected = Tree_numCompresstemsSelected(ItemsCount, NumCompressItems); - if (numSelected > 0) { + if (numSelected > 0) + { userSelected = true; } - if (m_CompressStatusDialog) { + if (m_CompressStatusDialog) + { m_CompressStatusDialog->onClearText(); m_CompressStatusDialog->showOutput(); } - if (!userSelected) { + if (!userSelected) + { // We did not find any selected compress images // Try the current selected item QTreeWidgetItem* item = m_projectTreeView->currentItem(); // Try project view items - if (!item) { + if (!item) + { // Parse the Project view tree QTreeWidgetItemIterator it(m_projectTreeView); - while (*it) { + while (*it) + { QString name = (*it)->text(0); QVariant v = (*it)->data(TREE_LevelType, Qt::UserRole); int levelType = v.toInt(); int childcount = (*it)->childCount(); - if (levelType == TREETYPE_IMAGEFILE_DATA) { + if (levelType == TREETYPE_IMAGEFILE_DATA) + { // "Source File" = FilePathName); - if (childcount >= 1) { + if (childcount >= 1) + { item = (*it); break; } @@ -429,15 +483,18 @@ bool ProjectView::AnySelectedItems() { } } - if (item) { + if (item) + { Tree_setAllItemsSetected(); m_AllItemsSelected = true; return false; } } - if (NumCompressItems == 0) { - if (m_CompressStatusDialog) { + if (NumCompressItems == 0) + { + if (m_CompressStatusDialog) + { m_CompressStatusDialog->appendText("Please open or create a project file and add/select destination images to process!"); } return false; @@ -446,29 +503,37 @@ bool ProjectView::AnySelectedItems() { return true; } -bool ProjectView::saveImageAs() { +bool ProjectView::saveImageAs() +{ QTreeWidgetItem* item = m_projectTreeView->currentItem(); - if (!item) { - if (m_CompressStatusDialog) { + if (!item) + { + if (m_CompressStatusDialog) + { m_CompressStatusDialog->appendText("Please load or click to select the image that you would like to save."); } return false; - } else { + } + else + { QVariant v = item->data(TREE_SourceInfo, Qt::UserRole); QString tempName = item->text(0); QFileInfo fileInfo(tempName); QString imgFileName = fileInfo.completeBaseName(); imgFileName.append("_saved"); C_Source_Info* data = v.value(); - if (data == nullptr) { - if (m_CompressStatusDialog) { + if (data == nullptr) + { + if (m_CompressStatusDialog) + { m_CompressStatusDialog->appendText("Please load or click to select a valid image (only 2D texture) that you would like to save."); m_CompressStatusDialog->showOutput(); } return false; } if (data->m_MipImages) - if (data->m_MipImages->mipset) { + if (data->m_MipImages->mipset) + { QString filePathName = QFileDialog::getSaveFileName(this, tr("Save image as"), imgFileName, tr("Image files (*.dds)")); if (filePathName.length() == 0) return false; @@ -480,21 +545,27 @@ bool ProjectView::saveImageAs() { plugin_Image = reinterpret_cast(g_pluginManager.GetPlugin("IMAGE", "DDS")); imgFileName.append(".dds"); - if (AMDSaveMIPSTextureImage(filePathName.toStdString().c_str(), data->m_MipImages->mipset, false, g_CmdPrams.CompressOptions) != 0) { - if (m_CompressStatusDialog) { + if (AMDSaveMIPSTextureImage(filePathName.toStdString().c_str(), data->m_MipImages->mipset, false, g_CmdPrams.CompressOptions) != 0) + { + if (m_CompressStatusDialog) + { m_CompressStatusDialog->onClearText(); m_CompressStatusDialog->showOutput(); } PrintInfo("Error: saving image fail.\n"); return false; } - if (m_CompressStatusDialog) { + if (m_CompressStatusDialog) + { m_CompressStatusDialog->onClearText(); m_CompressStatusDialog->showOutput(); } PrintInfo("Image file: %s saved successfully.\n", imgFileName.toStdString().c_str()); - } else { - if (m_CompressStatusDialog) { + } + else + { + if (m_CompressStatusDialog) + { m_CompressStatusDialog->onClearText(); m_CompressStatusDialog->showOutput(); } @@ -505,8 +576,10 @@ bool ProjectView::saveImageAs() { return true; } -void ProjectView::OnStartCompression() { - if (!AnySelectedItems()) { +void ProjectView::OnStartCompression() +{ + if (!AnySelectedItems()) + { AddSettingtoEmptyTree(); if (g_bAbortCompression) return; @@ -514,10 +587,12 @@ void ProjectView::OnStartCompression() { m_AllItemsSelected = true; } - if (m_CompressStatusDialog) { + if (m_CompressStatusDialog) + { saveProjectFile(); compressProjectFiles(NULL); - if (m_AllItemsSelected) { + if (m_AllItemsSelected) + { // Reset the list of selections Tree_clearAllItemsSetected(); m_AllItemsSelected = false; @@ -525,39 +600,49 @@ void ProjectView::OnStartCompression() { } } -ProjectView::~ProjectView() { +ProjectView::~ProjectView() +{ g_bAbortCompression = true; - if (m_imageloader) { + if (m_imageloader) + { delete m_imageloader; m_imageloader = NULL; } - if (m_newProjectwindow) { + if (m_newProjectwindow) + { delete m_newProjectwindow; m_newProjectwindow = NULL; } - if (m_diffImageDialog) { + if (m_diffImageDialog) + { delete m_diffImageDialog; m_diffImageDialog = NULL; } - if (m_3DMeshAnalysisDlg) { + if (m_3DMeshAnalysisDlg) + { delete m_3DMeshAnalysisDlg; m_3DMeshAnalysisDlg = NULL; } } -void ProjectView::run3DMeshAnalysis(CMODEL_DATA* meshData, CMODEL_DATA* meshDataOri) { +void ProjectView::run3DMeshAnalysis(CMODEL_DATA* meshData, CMODEL_DATA* meshDataOri) +{ if (!meshData) return; - if (m_3DMeshAnalysisDlg) { + if (m_3DMeshAnalysisDlg) + { m_3DMeshAnalysisDlg->m_meshData = meshData->m_meshData; - if (meshDataOri) { + if (meshDataOri) + { m_3DMeshAnalysisDlg->m_meshDataCompare = meshDataOri->m_meshData; - } else { + } + else + { m_3DMeshAnalysisDlg->m_fileNameCompare = ""; } m_3DMeshAnalysisDlg->cleanText(); @@ -565,8 +650,10 @@ void ProjectView::run3DMeshAnalysis(CMODEL_DATA* meshData, CMODEL_DATA* meshData } } -void ProjectView::diffImageFiles() { - if (m_diffImageDialog) { +void ProjectView::diffImageFiles() +{ + if (m_diffImageDialog) + { m_diffImageDialog->m_RecentImageDir = m_RecentImageDirOpen; m_diffImageDialog->m_SupportedImageFormat = m_ImageFileFilter; @@ -576,18 +663,24 @@ void ProjectView::diffImageFiles() { // find the index of the selected diff file from the drop down list int index1 = m_diffImageDialog->m_file1Name->findText(m_curDiffSourceFile); - if (index1 != -1) { + if (index1 != -1) + { m_diffImageDialog->m_file1Name->setCurrentIndex(index1); - } else { + } + else + { m_diffImageDialog->m_file1Name->setCurrentText(m_curDiffSourceFile); } m_diffImageDialog->m_file2Name->clear(); m_diffImageDialog->m_file2Name->addItem(""); m_diffImageDialog->m_file2Name->addItems(m_ImagesinProjectTrees); int index2 = m_diffImageDialog->m_file1Name->findText(m_curDiffDestFile); - if (index2 != -1) { + if (index2 != -1) + { m_diffImageDialog->m_file2Name->setCurrentIndex(index2); - } else { + } + else + { m_diffImageDialog->m_file2Name->setCurrentText(m_curDiffDestFile); } @@ -599,13 +692,17 @@ void ProjectView::diffImageFiles() { } } -bool ProjectView::OpenImageFile() { +bool ProjectView::OpenImageFile() +{ // Add new file QStringList ls = QFileDialog::getOpenFileNames(this, tr("Open image file(s)"), m_RecentImageDirOpen, m_ImageFileFilter); - if (!ls.isEmpty()) { - for (int i = 0; i < ls.size(); i++) { + if (!ls.isEmpty()) + { + for (int i = 0; i < ls.size(); i++) + { C_Source_Info* m_dataout = NULL; - if (Tree_AddImageFile(ls[i], 0, &m_dataout)) { + if (Tree_AddImageFile(ls[i], 0, &m_dataout)) + { QFileInfo FileInfo(ls[i]); emit AddedImageFile(ls[i]); m_RecentImageDirOpen = FileInfo.path(); @@ -619,7 +716,8 @@ bool ProjectView::OpenImageFile() { //======================================================= -int ProjectView::PromptSaveChanges() { +int ProjectView::PromptSaveChanges() +{ QMessageBox msgBox; QString msg; msg.append("The project \""); @@ -632,7 +730,8 @@ int ProjectView::PromptSaveChanges() { return msgBox.exec(); } -void ProjectView::SelectImageItem(QString filePathName) { +void ProjectView::SelectImageItem(QString filePathName) +{ static QString lastfilePathName = ""; // Already Selected on Treeview then exit @@ -641,7 +740,8 @@ void ProjectView::SelectImageItem(QString filePathName) { QTreeWidgetItem* it = Tree_FindImageItem(filePathName, true); - if (it) { + if (it) + { m_projectTreeView->setCurrentItem(it); // Update the poperty view for the item selected QVariant v = it->data(TREE_LevelType, Qt::UserRole); @@ -660,10 +760,12 @@ void ProjectView::SelectImageItem(QString filePathName) { } } -void ProjectView::clearProjectTreeView() { +void ProjectView::clearProjectTreeView() +{ // Find the item and set it as selected QTreeWidgetItemIterator it(m_treeRootItem); - while (*it) { + while (*it) + { DeleteItemData(*it, false); ++it; } @@ -675,15 +777,18 @@ void ProjectView::clearProjectTreeView() { Tree_AddRootNode(); } -void ProjectView::keyPressEvent(QKeyEvent* event) { - if ((event->key() == Qt::Key_Delete) && (!g_bCompressing)) { +void ProjectView::keyPressEvent(QKeyEvent* event) +{ + if ((event->key() == Qt::Key_Delete) && (!g_bCompressing)) + { // handle the key press, perhaps giving the item text a default value UserDeleteItems(); event->accept(); } } -QString ProjectView::GetSourceFileNamePath(QTreeWidgetItem* item) { +QString ProjectView::GetSourceFileNamePath(QTreeWidgetItem* item) +{ QString filePathName = ""; if (!item) return filePathName; @@ -691,15 +796,18 @@ QString ProjectView::GetSourceFileNamePath(QTreeWidgetItem* item) { QVariant v = item->data(TREE_LevelType, Qt::UserRole); int levelType = v.toInt(); - switch (levelType) { - case TREETYPE_3DMODEL_DATA: { + switch (levelType) + { + case TREETYPE_3DMODEL_DATA: + { v = item->data(TREE_SourceInfo, Qt::UserRole); C_3DModel_Info* data = v.value(); if (data) filePathName = data->m_Full_Path; } break; - case TREETYPE_3DSUBMODEL_DATA: { + case TREETYPE_3DSUBMODEL_DATA: + { v = item->data(TREE_SourceInfo, Qt::UserRole); C_3DSubModel_Info* data = v.value(); if (data) @@ -707,14 +815,19 @@ QString ProjectView::GetSourceFileNamePath(QTreeWidgetItem* item) { } break; case TREETYPE_IMAGEFILE_DATA: - case TREETYPE_VIEWIMAGE_ONLY_NODE: { - v = item->data(TREE_SourceInfo, Qt::UserRole); - C_Source_Info* data = v.value(); - if (data) - filePathName = data->m_Full_Path; + case TREETYPE_VIEWIMAGE_ONLY_NODE: + { + v = item->data(TREE_SourceInfo, Qt::UserRole); + if (!v.Invalid) + { + C_Source_Info* data = v.value(); + if (data) + filePathName = data->m_Full_Path; + } } break; - case TREETYPE_VIEWMESH_ONLY_NODE: { + case TREETYPE_VIEWMESH_ONLY_NODE: + { v = item->data(TREE_SourceInfo, Qt::UserRole); C_Mesh_Buffer_Info* data = v.value(); if (data) @@ -722,7 +835,8 @@ QString ProjectView::GetSourceFileNamePath(QTreeWidgetItem* item) { } break; case TREETYPE_MESH_DATA: - case TREETYPE_COMPRESSION_DATA: { + case TREETYPE_COMPRESSION_DATA: + { v = item->data(TREE_SourceInfo, Qt::UserRole); C_Destination_Options* data = v.value(); if (data) @@ -736,7 +850,8 @@ QString ProjectView::GetSourceFileNamePath(QTreeWidgetItem* item) { return filePathName; } -QString ProjectView::GetDestinationFileNamePath(QTreeWidgetItem* item) { +QString ProjectView::GetDestinationFileNamePath(QTreeWidgetItem* item) +{ QString filePathName = ""; if (!item) return filePathName; @@ -744,9 +859,11 @@ QString ProjectView::GetDestinationFileNamePath(QTreeWidgetItem* item) { QVariant v = item->data(TREE_LevelType, Qt::UserRole); int levelType = v.toInt(); - switch (levelType) { + switch (levelType) + { case TREETYPE_MESH_DATA: - case TREETYPE_COMPRESSION_DATA: { + case TREETYPE_COMPRESSION_DATA: + { v = item->data(TREE_SourceInfo, Qt::UserRole); C_Destination_Options* data = v.value(); if (data) @@ -764,24 +881,29 @@ QString ProjectView::GetDestinationFileNamePath(QTreeWidgetItem* item) { // 2: User manually removes a node and its content from the project // In this case we will also add addtional procesing for cases where // Image nodes attached to Models may need further processing -void ProjectView::DeleteItemData(QTreeWidgetItem* item, bool userdeleted) { +void ProjectView::DeleteItemData(QTreeWidgetItem* item, bool userdeleted) +{ if (!item) return; QVariant v = item->data(TREE_LevelType, Qt::UserRole); int levelType = v.toInt(); - switch (levelType) { - case TREETYPE_3DMODEL_DATA: { // Project Explorer Root tree item + switch (levelType) + { + case TREETYPE_3DMODEL_DATA: + { // Project Explorer Root tree item // Get the Image Data linked to this node v = item->data(TREE_SourceInfo, Qt::UserRole); C_3DModel_Info* data = v.value(); // Delete the Data Class - if (data) { + if (data) + { QString filePathName = data->m_Full_Path; // Remove any views of the file - if (filePathName.length() > 0) { + if (filePathName.length() > 0) + { // Remove the item from the diff image drop down list int temp = m_ImagesinProjectTrees.indexOf(filePathName); if (temp != -1) @@ -795,16 +917,19 @@ void ProjectView::DeleteItemData(QTreeWidgetItem* item, bool userdeleted) { } break; } - case TREETYPE_3DSUBMODEL_DATA: { // Project Explorer sub tree item + case TREETYPE_3DSUBMODEL_DATA: + { // Project Explorer sub tree item // Get the Image Data linked to this node v = item->data(TREE_SourceInfo, Qt::UserRole); C_3DSubModel_Info* data = v.value(); // Delete the Data Class - if (data) { + if (data) + { QString filePathName = data->m_Full_Path; // Remove any views of the file - if (filePathName.length() > 0) { + if (filePathName.length() > 0) + { // Remove the item from the diff image drop down list int temp = m_ImagesinProjectTrees.indexOf(filePathName); if (temp != -1) @@ -819,23 +944,27 @@ void ProjectView::DeleteItemData(QTreeWidgetItem* item, bool userdeleted) { break; } case TREETYPE_VIEWIMAGE_ONLY_NODE: - case TREETYPE_IMAGEFILE_DATA: { // Project Explorer Root Item + case TREETYPE_IMAGEFILE_DATA: + { // Project Explorer Root Item // Get the Image Data linked to this node QString filePathName = {}; v = item->data(TREE_SourceInfo, Qt::UserRole); C_Source_Info* data = v.value(); - if (data) { + if (data) + { filePathName = data->m_Full_Path; } // Remove the MIP Image data - if (m_imageloader) { + if (m_imageloader) + { if (data) m_imageloader->clearMipImages(&data->m_MipImages); } // Remove any docked views of the file - if (filePathName.length() > 0) { + if (filePathName.length() > 0) + { // Remove the item from the diff image drop down list int temp = m_ImagesinProjectTrees.indexOf(filePathName); if (temp != -1) @@ -845,23 +974,27 @@ void ProjectView::DeleteItemData(QTreeWidgetItem* item, bool userdeleted) { } // Delete the Data Class - if (data) { + if (data) + { delete data; data = nullptr; } } break; - case TREETYPE_VIEWMESH_ONLY_NODE: { + case TREETYPE_VIEWMESH_ONLY_NODE: + { // Get the Image Data linked to this node QString filePathName = {}; v = item->data(TREE_SourceInfo, Qt::UserRole); C_Mesh_Buffer_Info* data = v.value(); - if (data) { + if (data) + { filePathName = data->m_Full_Path; } // Remove any docked views of the file - if (filePathName.length() > 0) { + if (filePathName.length() > 0) + { // Remove the item from the diff image drop down list int temp = m_ImagesinProjectTrees.indexOf(filePathName); if (temp != -1) @@ -871,27 +1004,32 @@ void ProjectView::DeleteItemData(QTreeWidgetItem* item, bool userdeleted) { } // Delete the Data Class - if (data) { + if (data) + { delete data; data = nullptr; } } break; - case TREETYPE_MESH_DATA: { // Project Explorer sub tree item + case TREETYPE_MESH_DATA: + { // Project Explorer sub tree item v = item->data(TREE_SourceInfo, Qt::UserRole); C_Destination_Options* data = v.value(); if (data == NULL) return; QString filePathName = data->m_destFileNamePath; - if (data) { + if (data) + { // Check if image belongs to a Model parent QTreeWidgetItem* parent = item->parent(); - if (parent && userdeleted) { + if (parent && userdeleted) + { // Verify its root QVariant parentv = parent->data(TREE_LevelType, Qt::UserRole); int ParentlevelType = parentv.toInt(); - if (ParentlevelType == TREETYPE_3DSUBMODEL_DATA) { + if (ParentlevelType == TREETYPE_3DSUBMODEL_DATA) + { QFileInfo replacementfileInfo(data->m_sourceFileNamePath); QString replacementfilename(replacementfileInfo.fileName()); @@ -909,7 +1047,8 @@ void ProjectView::DeleteItemData(QTreeWidgetItem* item, bool userdeleted) { } // Remove any docked views of the file - if (filePathName.length() > 0) { + if (filePathName.length() > 0) + { // Remove the item from the diff image drop down list int temp = m_ImagesinProjectTrees.indexOf(filePathName); if (temp != -1) @@ -919,7 +1058,8 @@ void ProjectView::DeleteItemData(QTreeWidgetItem* item, bool userdeleted) { } } break; - case TREETYPE_COMPRESSION_DATA: { // Project Explorer sub tree item + case TREETYPE_COMPRESSION_DATA: + { // Project Explorer sub tree item v = item->data(TREE_SourceInfo, Qt::UserRole); C_Destination_Options* data = v.value(); if (data == NULL) @@ -930,14 +1070,17 @@ void ProjectView::DeleteItemData(QTreeWidgetItem* item, bool userdeleted) { if (m_imageloader) m_imageloader->clearMipImages(&data->m_MipImages); - if (data) { + if (data) + { // Check if image belongs to a Model parent QTreeWidgetItem* parent = item->parent(); - if (parent && userdeleted) { + if (parent && userdeleted) + { // Verify its root QVariant parentv = parent->data(TREE_LevelType, Qt::UserRole); int ParentlevelType = parentv.toInt(); - if (ParentlevelType == TREETYPE_3DSUBMODEL_DATA || ParentlevelType == TREETYPE_3DMODEL_DATA) { + if (ParentlevelType == TREETYPE_3DSUBMODEL_DATA || ParentlevelType == TREETYPE_3DMODEL_DATA) + { QFileInfo replacementfileInfo(data->m_sourceFileNamePath); QString replacementfilename(replacementfileInfo.fileName()); @@ -955,7 +1098,8 @@ void ProjectView::DeleteItemData(QTreeWidgetItem* item, bool userdeleted) { } // Remove any docked views of the file - if (filePathName.length() > 0) { + if (filePathName.length() > 0) + { // Remove the item from the diff image drop down list int temp = m_ImagesinProjectTrees.indexOf(filePathName); if (temp != -1) @@ -972,7 +1116,8 @@ void ProjectView::DeleteItemData(QTreeWidgetItem* item, bool userdeleted) { // Return a valid image item -QTreeWidgetItem* ProjectView::GetCurrentItem(int inLevelType) { +QTreeWidgetItem* ProjectView::GetCurrentItem(int inLevelType) +{ // check if new items exit to process int topCount = m_projectTreeView->topLevelItemCount(); if (topCount == 1) @@ -987,20 +1132,24 @@ QTreeWidgetItem* ProjectView::GetCurrentItem(int inLevelType) { int levelType = v.toInt(); if ((levelType != TREETYPE_Double_Click_here_to_add_files) && (levelType != TREETYPE_Add_destination_setting) && - (levelType != TREETYPE_Add_Model_destination_settings) && (levelType == inLevelType)) { + (levelType != TREETYPE_Add_Model_destination_settings) && (levelType == inLevelType)) + { return (item); } - if (m_CurrentItem) { + if (m_CurrentItem) + { return m_CurrentItem; } return (NULL); } -QTreeWidgetItem* ProjectView::GetCurrentItem() { +QTreeWidgetItem* ProjectView::GetCurrentItem() +{ // use known current - if (m_CurrentItem) { + if (m_CurrentItem) + { return m_CurrentItem; } @@ -1013,7 +1162,8 @@ QTreeWidgetItem* ProjectView::GetCurrentItem() { int levelType = v.toInt(); if ((levelType != TREETYPE_Double_Click_here_to_add_files) && (levelType != TREETYPE_Add_destination_setting) && - (levelType != TREETYPE_Add_Model_destination_settings)) { + (levelType != TREETYPE_Add_Model_destination_settings)) + { return (item); } return (NULL); @@ -1022,7 +1172,8 @@ QTreeWidgetItem* ProjectView::GetCurrentItem() { // Delete an Image Item from the project view // User is prompted to confirm the delete -void ProjectView::UserDeleteItems() { +void ProjectView::UserDeleteItems() +{ QMessageBox msgBox; msgBox.setText(trUtf8("Delete Selected Images")); msgBox.setInformativeText( @@ -1034,15 +1185,19 @@ void ProjectView::UserDeleteItems() { msgBox.setDefaultButton(myCancelButton); msgBox.setIcon(QMessageBox::Question); msgBox.exec(); - if (msgBox.clickedButton() == myRemoveButton) { + if (msgBox.clickedButton() == myRemoveButton) + { DeleteAllSeletedItems(false); - } else if (msgBox.clickedButton() == myDeleteButton) { + } + else if (msgBox.clickedButton() == myDeleteButton) + { DeleteAllSeletedItems(true); } emit UpdateData(NULL); } -QTreeWidgetItem* ProjectView::DeleteSelectedItemData(QTreeWidgetItem* item, bool RemoveFromDisk) { +QTreeWidgetItem* ProjectView::DeleteSelectedItemData(QTreeWidgetItem* item, bool RemoveFromDisk) +{ if (!item) return (NULL); @@ -1053,10 +1208,12 @@ QTreeWidgetItem* ProjectView::DeleteSelectedItemData(QTreeWidgetItem* item, bool itemName = item->text(0); //printf("Delete [%d]: %s \n", levelType,itemName.toStdString().c_str()); - if (levelType == TREETYPE_IMAGEFILE_DATA || levelType == TREETYPE_3DSUBMODEL_DATA) { + if (levelType == TREETYPE_IMAGEFILE_DATA || levelType == TREETYPE_3DSUBMODEL_DATA) + { // Does this item have childern int childcount = item->childCount(); - if (childcount > 0) { + if (childcount > 0) + { // index starts from 0 to childcound-1 childcount--; @@ -1067,25 +1224,32 @@ QTreeWidgetItem* ProjectView::DeleteSelectedItemData(QTreeWidgetItem* item, bool int ChildlevelType; // child 0 = Root node (+) all other indexes will be Compression settings - while (childcount >= 0) { + while (childcount >= 0) + { child = item->child(childcount); - if (child) { + if (child) + { v = child->data(TREE_LevelType, Qt::UserRole); ChildlevelType = v.toInt(); // This should always be true: We are not flagging errors if false - if ((ChildlevelType == TREETYPE_COMPRESSION_DATA) || ((ChildlevelType == TREETYPE_MESH_DATA))) { + if ((ChildlevelType == TREETYPE_COMPRESSION_DATA) || ((ChildlevelType == TREETYPE_MESH_DATA))) + { v = child->data(TREE_SourceInfo, Qt::UserRole); C_Destination_Options* data = v.value(); - if (data == NULL) { + if (data == NULL) + { --childcount; --m_NumItems; continue; } // Remove the file - if (RemoveFromDisk) { + if (RemoveFromDisk) + { bool isRemoved = QFile::remove(data->m_destFileNamePath); - if (!isRemoved) { - if (QFile::exists(data->m_destFileNamePath)) { + if (!isRemoved) + { + if (QFile::exists(data->m_destFileNamePath)) + { QString error = "Error: Delete " + data->m_destFileNamePath + " from disk failed. \n"; PrintInfo(error.toStdString().c_str()); } @@ -1095,7 +1259,9 @@ QTreeWidgetItem* ProjectView::DeleteSelectedItemData(QTreeWidgetItem* item, bool // Remove the item from the treeview DeleteItemData(child, true); m_NumItems--; - } else { + } + else + { // Remove the (+) item node DeleteItemData(child, true); m_NumItems--; @@ -1106,15 +1272,20 @@ QTreeWidgetItem* ProjectView::DeleteSelectedItemData(QTreeWidgetItem* item, bool m_NumItems--; } - if (levelType == TREETYPE_3DSUBMODEL_DATA) { + if (levelType == TREETYPE_3DSUBMODEL_DATA) + { // Remove the gltf compressed file v = item->data(TREE_SourceInfo, Qt::UserRole); C_3DSubModel_Info* data = v.value(); - if (data) { - if (RemoveFromDisk) { + if (data) + { + if (RemoveFromDisk) + { bool isRemoved = QFile::remove(data->m_Full_Path); - if (!isRemoved) { - if (QFile::exists(data->m_Full_Path)) { + if (!isRemoved) + { + if (QFile::exists(data->m_Full_Path)) + { QString error = "Error: Delete " + data->m_Full_Path + "from disk failed. \n"; PrintInfo(error.toStdString().c_str()); } @@ -1123,10 +1294,13 @@ QTreeWidgetItem* ProjectView::DeleteSelectedItemData(QTreeWidgetItem* item, bool } m_NumItems--; } - } else if (levelType == TREETYPE_3DMODEL_DATA) { + } + else if (levelType == TREETYPE_3DMODEL_DATA) + { // Does this item have childern int childcount = item->childCount(); - if (childcount > 0) { + if (childcount > 0) + { // index starts from 0 to childcound-1 childcount--; @@ -1137,17 +1311,21 @@ QTreeWidgetItem* ProjectView::DeleteSelectedItemData(QTreeWidgetItem* item, bool int ChildlevelType; // child 0 = Root node (+) all other indexes will be Compression settings - while (childcount >= 0) { + while (childcount >= 0) + { child = item->child(childcount); - if (child) { + if (child) + { v = child->data(TREE_LevelType, Qt::UserRole); ChildlevelType = v.toInt(); //printf("childlevel[%d]\n", ChildlevelType); // This should always be true: We are not flagging errors if false - if (ChildlevelType == TREETYPE_3DSUBMODEL_DATA) { + if (ChildlevelType == TREETYPE_3DSUBMODEL_DATA) + { // Does this child item have childern int grandchildcount = child->childCount(); - if (grandchildcount > 0) { + if (grandchildcount > 0) + { //printf("grandchild[%d]\n", grandchildcount); // index starts from 0 to childcound-1 grandchildcount--; @@ -1159,23 +1337,30 @@ QTreeWidgetItem* ProjectView::DeleteSelectedItemData(QTreeWidgetItem* item, bool int grandChildlevelType; // child 0 = Root node (+) all other indexes will be Compression settings - while (grandchildcount >= 0) { + while (grandchildcount >= 0) + { grandchild = child->child(grandchildcount); - if (grandchild) { + if (grandchild) + { v = grandchild->data(TREE_LevelType, Qt::UserRole); grandChildlevelType = v.toInt(); // This should always be true: We are not flagging errors if false - if ((grandChildlevelType == TREETYPE_COMPRESSION_DATA) || (grandChildlevelType == TREETYPE_MESH_DATA)) { + if ((grandChildlevelType == TREETYPE_COMPRESSION_DATA) || (grandChildlevelType == TREETYPE_MESH_DATA)) + { v = grandchild->data(TREE_SourceInfo, Qt::UserRole); C_Destination_Options* data = v.value(); // Remove the file - if (data) { + if (data) + { // remove subitem from treeview //printf("grandchilditem [%s]", data->m_destFileNamePath.toStdString().c_str()); - if (RemoveFromDisk) { + if (RemoveFromDisk) + { bool isRemoved = QFile::remove(data->m_destFileNamePath); - if (!isRemoved) { - if (QFile::exists(data->m_destFileNamePath)) { + if (!isRemoved) + { + if (QFile::exists(data->m_destFileNamePath)) + { QString error = "Error: Delete " + data->m_destFileNamePath + "from disk failed. \n"; PrintInfo(error.toStdString().c_str()); } @@ -1186,7 +1371,9 @@ QTreeWidgetItem* ProjectView::DeleteSelectedItemData(QTreeWidgetItem* item, bool // Remove the item from the treeview DeleteItemData(grandchild, true); m_NumItems--; - } else { + } + else + { // Remove the (+) item node DeleteItemData(grandchild, true); m_NumItems--; @@ -1200,13 +1387,17 @@ QTreeWidgetItem* ProjectView::DeleteSelectedItemData(QTreeWidgetItem* item, bool // Remove the gltf compressed (subtree item) file v = child->data(TREE_SourceInfo, Qt::UserRole); C_3DSubModel_Info* data = v.value(); - if (data) { + if (data) + { // remove subitem from treeview //printf("Subitem [%s]\n", data->m_Full_Path.toStdString().c_str()); - if (RemoveFromDisk) { + if (RemoveFromDisk) + { bool isRemoved = QFile::remove(data->m_Full_Path); - if (!isRemoved) { - if (QFile::exists(data->m_Full_Path)) { + if (!isRemoved) + { + if (QFile::exists(data->m_Full_Path)) + { QString error = "Error: Delete " + data->m_Full_Path + "from disk failed. \n"; PrintInfo(error.toStdString().c_str()); } @@ -1215,7 +1406,9 @@ QTreeWidgetItem* ProjectView::DeleteSelectedItemData(QTreeWidgetItem* item, bool } DeleteItemData(child, true); m_NumItems--; - } else { + } + else + { // Remove the (+) item node DeleteItemData(child, true); m_NumItems--; @@ -1225,15 +1418,21 @@ QTreeWidgetItem* ProjectView::DeleteSelectedItemData(QTreeWidgetItem* item, bool } m_NumItems--; } - } else if ((levelType == TREETYPE_COMPRESSION_DATA) || (levelType == TREETYPE_MESH_DATA)) { + } + else if ((levelType == TREETYPE_COMPRESSION_DATA) || (levelType == TREETYPE_MESH_DATA)) + { // Remove the file v = item->data(TREE_SourceInfo, Qt::UserRole); C_Destination_Options* data = v.value(); - if (data) { - if (RemoveFromDisk) { + if (data) + { + if (RemoveFromDisk) + { bool isRemoved = QFile::remove(data->m_destFileNamePath); - if (!isRemoved) { - if (QFile::exists(data->m_destFileNamePath)) { + if (!isRemoved) + { + if (QFile::exists(data->m_destFileNamePath)) + { QString error = "Error: Delete " + data->m_destFileNamePath + "from disk failed. \n"; PrintInfo(error.toStdString().c_str()); } @@ -1243,28 +1442,40 @@ QTreeWidgetItem* ProjectView::DeleteSelectedItemData(QTreeWidgetItem* item, bool // Find for parent which is 3D sub model or image level type to update the src file combo list QTreeWidgetItem* parent = item->parent(); - if (parent) { + if (parent) + { QVariant parentv = parent->data(TREE_LevelType, Qt::UserRole); int parentlevelType = parentv.toInt(); - if (parentlevelType == TREETYPE_IMAGEFILE_DATA) { + if (parentlevelType == TREETYPE_IMAGEFILE_DATA) + { parentv = parent->data(TREE_SourceInfo, Qt::UserRole); C_3DSubModel_Info* parentdata = parentv.value(); - if (data) { - for (int i = 0; i < data->m_Model_Images.size(); i++) { - if (parentdata) { - if (data->m_sourceFileNamePath == parentdata->m_Model_Images[i].m_FilePathName) { + if (data) + { + for (int i = 0; i < data->m_Model_Images.size(); i++) + { + if (parentdata) + { + if (data->m_sourceFileNamePath == parentdata->m_Model_Images[i].m_FilePathName) + { parentdata->m_SubModel_Images[i].m_srcDelFlag = false; } } } } - } else if (parentlevelType == TREETYPE_3DSUBMODEL_DATA) { + } + else if (parentlevelType == TREETYPE_3DSUBMODEL_DATA) + { parentv = parent->data(TREE_SourceInfo, Qt::UserRole); C_3DSubModel_Info* parentdata = parentv.value(); - if (data) { - for (int i = 0; i < data->m_Model_Images.size(); i++) { - if (parentdata) { - if (data->m_sourceFileNamePath == parentdata->m_Model_Images[i].m_FilePathName) { + if (data) + { + for (int i = 0; i < data->m_Model_Images.size(); i++) + { + if (parentdata) + { + if (data->m_sourceFileNamePath == parentdata->m_Model_Images[i].m_FilePathName) + { parentdata->m_SubModel_Images[i].m_srcDelFlag = false; if (parentdata->ModelType == eModelType::GLTF) UpdateDestglTFWithFile( @@ -1277,16 +1488,20 @@ QTreeWidgetItem* ProjectView::DeleteSelectedItemData(QTreeWidgetItem* item, bool } m_NumItems--; - } else { + } + else + { item->setSelected(false); } // remove the node and its data for which the user selected the delete on int i = m_projectTreeView->indexOfTopLevelItem(item); QTreeWidgetItem* TopLevelitem = m_projectTreeView->takeTopLevelItem(i); if ((levelType != TREETYPE_Double_Click_here_to_add_files) && (levelType != TREETYPE_Add_destination_setting) && - (levelType != TREETYPE_Add_Model_destination_settings)) { + (levelType != TREETYPE_Add_Model_destination_settings)) + { DeleteItemData(item, true); - if (item) { + if (item) + { delete item; item = NULL; } @@ -1296,7 +1511,8 @@ QTreeWidgetItem* ProjectView::DeleteSelectedItemData(QTreeWidgetItem* item, bool return TopLevelitem; } -void ProjectView::DeleteAllSeletedItems(bool RemoveFromDisk) { +void ProjectView::DeleteAllSeletedItems(bool RemoveFromDisk) +{ // Find the item and set it as selected QTreeWidgetItemIterator it(m_treeRootItem); QVariant v; @@ -1306,7 +1522,8 @@ void ProjectView::DeleteAllSeletedItems(bool RemoveFromDisk) { // Use TotalItems as a bug check to prevent infinate loops! // The itterator should loop only once through all current items in the TreeView - while ((*it) && (TotalItems > 0)) { + while ((*it) && (TotalItems > 0)) + { v = (*it)->data(TREE_LevelType, Qt::UserRole); levelType = v.toInt(); @@ -1314,22 +1531,27 @@ void ProjectView::DeleteAllSeletedItems(bool RemoveFromDisk) { itemName = (*it)->text(0); //qDebug() << "Delete: " << itemName; - if ((*it)->isSelected() && (levelType != TREETYPE_Double_Click_here_to_add_files) && (levelType != TREETYPE_VIEWIMAGE_ONLY_NODE)) { + if ((*it)->isSelected() && (levelType != TREETYPE_Double_Click_here_to_add_files) && (levelType != TREETYPE_VIEWIMAGE_ONLY_NODE)) + { DeleteSelectedItemData(*it, RemoveFromDisk); - } else + } + else ++it; TotalItems--; } } -QTreeWidgetItem* ProjectView::Tree_SetCurrentItem(QString FilePathName) { +QTreeWidgetItem* ProjectView::Tree_SetCurrentItem(QString FilePathName) +{ QTreeWidgetItem* item = Tree_FindImageItem(FilePathName, true); - if (item) { + if (item) + { m_CurrentItem = item; m_projectTreeView->setCurrentItem(item); item->setSelected(true); - if (m_EnableCheckedItemsView) { + if (m_EnableCheckedItemsView) + { item->setCheckState(0, Qt::Checked); } return item; @@ -1339,31 +1561,36 @@ QTreeWidgetItem* ProjectView::Tree_SetCurrentItem(QString FilePathName) { return NULL; } -void ProjectView::saveToBatchFile() { +void ProjectView::saveToBatchFile() +{ if (!AnySelectedItems()) return; QFileInfo fileInfo(m_curProjectFilePathName); QString suggestedName = fileInfo.completeBaseName(); QString filePathName = QFileDialog::getSaveFileName(this, tr("Save selected images to batch file"), suggestedName, tr("Command Line Batch Files (*.bat)")); - if (filePathName.length() > 0) { + if (filePathName.length() > 0) + { saveProjectFile(); // Writing to a file QFile file(filePathName); - if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) + { //qDebug() << "Open file for writing failed"; return; } - if (m_CompressStatusDialog) { + if (m_CompressStatusDialog) + { m_CompressStatusDialog->onClearText(); } compressProjectFiles(&file); file.close(); - if (m_AllItemsSelected) { + if (m_AllItemsSelected) + { // Reset the list of selections Tree_clearAllItemsSetected(); m_AllItemsSelected = false; @@ -1371,15 +1598,20 @@ void ProjectView::saveToBatchFile() { } } -void ProjectView::openContainingFolder() { - if (ContextMenu_ImageItem) { +void ProjectView::openContainingFolder() +{ + if (ContextMenu_ImageItem) + { QVariant v = ContextMenu_ImageItem->data(TREE_LevelType, Qt::UserRole); int levelType = v.toInt(); - switch (levelType) { - case TREETYPE_3DMODEL_DATA: { + switch (levelType) + { + case TREETYPE_3DMODEL_DATA: + { QVariant fv = ContextMenu_ImageItem->data(TREE_SourceInfo, Qt::UserRole); C_3DModel_Info* m_data = fv.value(); - if (m_data) { + if (m_data) + { QDir dir(QCoreApplication::applicationDirPath()); QString absolute_file_pathName = dir.absoluteFilePath(m_data->m_Full_Path); QFileInfo FilePath(absolute_file_pathName); @@ -1387,10 +1619,12 @@ void ProjectView::openContainingFolder() { } break; } - case TREETYPE_3DSUBMODEL_DATA: { + case TREETYPE_3DSUBMODEL_DATA: + { QVariant fv = ContextMenu_ImageItem->data(TREE_SourceInfo, Qt::UserRole); C_3DSubModel_Info* m_data = fv.value(); - if (m_data) { + if (m_data) + { QDir dir(QCoreApplication::applicationDirPath()); QString absolute_file_pathName = dir.absoluteFilePath(m_data->m_Full_Path); QFileInfo FilePath(absolute_file_pathName); @@ -1398,10 +1632,12 @@ void ProjectView::openContainingFolder() { } break; } - case TREETYPE_IMAGEFILE_DATA: { // Original Image item + case TREETYPE_IMAGEFILE_DATA: + { // Original Image item QVariant fv = ContextMenu_ImageItem->data(TREE_SourceInfo, Qt::UserRole); C_Source_Info* m_data = fv.value(); - if (m_data) { + if (m_data) + { QDir dir(QCoreApplication::applicationDirPath()); QString absolute_file_pathName = dir.absoluteFilePath(m_data->m_Full_Path); QFileInfo FilePath(absolute_file_pathName); @@ -1410,10 +1646,12 @@ void ProjectView::openContainingFolder() { break; } case TREETYPE_MESH_DATA: - case TREETYPE_COMPRESSION_DATA: { // Compressed Image item + case TREETYPE_COMPRESSION_DATA: + { // Compressed Image item QVariant fv = ContextMenu_ImageItem->data(TREE_SourceInfo, Qt::UserRole); C_Destination_Options* m_data = fv.value(); - if (m_data) { + if (m_data) + { QDir dir(QCoreApplication::applicationDirPath()); QString absolute_file_pathName = dir.absoluteFilePath(m_data->m_destFileNamePath); QFileInfo FilePath(absolute_file_pathName); @@ -1428,17 +1666,23 @@ void ProjectView::openContainingFolder() { } } -void ProjectView::copyFullPath() { - if (ContextMenu_ImageItem) { +void ProjectView::copyFullPath() +{ + if (ContextMenu_ImageItem) + { QVariant v = ContextMenu_ImageItem->data(0, Qt::UserRole); int levelType = v.toInt(); - switch (levelType) { - case TREETYPE_3DMODEL_DATA: { + switch (levelType) + { + case TREETYPE_3DMODEL_DATA: + { QVariant fv = ContextMenu_ImageItem->data(TREE_SourceInfo, Qt::UserRole); C_3DModel_Info* m_data = fv.value(); - if (m_data) { + if (m_data) + { QClipboard* clipboard = QApplication::clipboard(); - if (clipboard) { + if (clipboard) + { QDir dir(QCoreApplication::applicationDirPath()); QString absolute_file_pathName = dir.absoluteFilePath(m_data->m_Full_Path); // Use this if Path Only @@ -1452,12 +1696,15 @@ void ProjectView::copyFullPath() { } break; } - case TREETYPE_3DSUBMODEL_DATA: { + case TREETYPE_3DSUBMODEL_DATA: + { QVariant fv = ContextMenu_ImageItem->data(TREE_SourceInfo, Qt::UserRole); C_3DSubModel_Info* m_data = fv.value(); - if (m_data) { + if (m_data) + { QClipboard* clipboard = QApplication::clipboard(); - if (clipboard) { + if (clipboard) + { QDir dir(QCoreApplication::applicationDirPath()); QString absolute_file_pathName = dir.absoluteFilePath(m_data->m_Full_Path); // Use this if Path Only @@ -1471,12 +1718,15 @@ void ProjectView::copyFullPath() { } break; } - case TREETYPE_IMAGEFILE_DATA: { // Original Image item + case TREETYPE_IMAGEFILE_DATA: + { // Original Image item QVariant fv = ContextMenu_ImageItem->data(TREE_SourceInfo, Qt::UserRole); C_Source_Info* m_data = fv.value(); - if (m_data) { + if (m_data) + { QClipboard* clipboard = QApplication::clipboard(); - if (clipboard) { + if (clipboard) + { QDir dir(QCoreApplication::applicationDirPath()); QString absolute_file_pathName = dir.absoluteFilePath(m_data->m_Full_Path); // Use this if Path Only @@ -1491,12 +1741,15 @@ void ProjectView::copyFullPath() { break; } case TREETYPE_MESH_DATA: - case TREETYPE_COMPRESSION_DATA: { // Compressed Image item + case TREETYPE_COMPRESSION_DATA: + { // Compressed Image item QVariant fv = ContextMenu_ImageItem->data(TREE_SourceInfo, Qt::UserRole); C_Destination_Options* m_data = fv.value(); - if (m_data) { + if (m_data) + { QClipboard* clipboard = QApplication::clipboard(); - if (clipboard) { + if (clipboard) + { QDir dir(QCoreApplication::applicationDirPath()); QString absolute_file_pathName = dir.absoluteFilePath(m_data->m_destFileNamePath); // Use this if Path Only @@ -1517,29 +1770,35 @@ void ProjectView::copyFullPath() { } } -void makeFormatExtCompatible(C_Destination_Options* C_Destination_Options) { +void makeFormatExtCompatible(C_Destination_Options* C_Destination_Options) +{ C_Destination_Options::eCompression format = C_Destination_Options->m_Compression; QString destfilePath = C_Destination_Options->m_destFileNamePath; QFileInfo info(destfilePath); QString newDestPath; - if (format == C_Destination_Options::eCompression::ASTC && info.suffix() == "DDS") { + if (format == C_Destination_Options::eCompression::ASTC && info.suffix() == "DDS") + { newDestPath = info.path() + "/" + info.completeBaseName() + ".KTX"; C_Destination_Options->m_destFileNamePath = newDestPath; C_Destination_Options->m_FileInfoDestinationName = C_Destination_Options->m_compname + ".KTX"; - } else if (format != C_Destination_Options::eCompression::ASTC && info.suffix() == "ASTC") { + } + else if (format != C_Destination_Options::eCompression::ASTC && info.suffix() == "ASTC") + { newDestPath = info.path() + "/" + info.completeBaseName() + ".DDS"; C_Destination_Options->m_destFileNamePath = newDestPath; C_Destination_Options->m_FileInfoDestinationName = C_Destination_Options->m_compname + ".DDS"; } } -void ProjectView::saveProjectFile() { +void ProjectView::saveProjectFile() +{ // Writing to a file if (!(m_curProjectFilePathName.contains(".cprj", Qt::CaseInsensitive))) m_curProjectFilePathName.append(QString(".cprj")); QFile file(m_curProjectFilePathName); - if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) + { //qDebug() << "Open file for writing failed"; return; } @@ -1553,18 +1812,21 @@ void ProjectView::saveProjectFile() { // Parse the Project view tree QTreeWidgetItemIterator it(m_projectTreeView); - while (*it) { + while (*it) + { //======= // Node //======= QVariant v = (*it)->data(TREE_LevelType, Qt::UserRole); int levelType = v.toInt(); - if (levelType == TREETYPE_IMAGEFILE_DATA) { + if (levelType == TREETYPE_IMAGEFILE_DATA) + { int childcount = (*it)->childCount(); QVariant v = (*it)->data(TREE_SourceInfo, Qt::UserRole); C_Source_Info* m_data = v.value(); - if (m_data == NULL) { + if (m_data == NULL) + { ++it; continue; } @@ -1575,20 +1837,24 @@ void ProjectView::saveProjectFile() { // xmlWriter.writeStartElement("Image"); xmlWriter.writeAttribute("File", FilePathName); - if (childcount >= 1) { + if (childcount >= 1) + { // now save the child elements - for (int i = 0; i < childcount; i++) { + for (int i = 0; i < childcount; i++) + { //======= // Node //======= ++it; - if (*it) { + if (*it) + { QString Setting = (*it)->text(0); QVariant v = (*it)->data(TREE_LevelType, Qt::UserRole); int sublevelType = v.toInt(); // save the settings item - if ((sublevelType == TREETYPE_COMPRESSION_DATA) || (sublevelType == TREETYPE_MESH_DATA)) { + if ((sublevelType == TREETYPE_COMPRESSION_DATA) || (sublevelType == TREETYPE_MESH_DATA)) + { v = (*it)->data(TREE_SourceInfo, Qt::UserRole); C_Destination_Options* data = v.value(); @@ -1601,13 +1867,15 @@ void ProjectView::saveProjectFile() { xmlWriter.writeAttribute("Setting", Setting); xmlWriter.writeAttribute("Enabled", (*it)->checkState(0) == Qt::Checked ? "True" : "False"); - if (data) { + if (data) + { makeFormatExtCompatible(data); xmlWriter.writeTextElement("Source", data->m_sourceFileNamePath); xmlWriter.writeTextElement("Destination", data->m_destFileNamePath); - if (sublevelType == TREETYPE_COMPRESSION_DATA) { + if (sublevelType == TREETYPE_COMPRESSION_DATA) + { QMetaObject meta = C_Destination_Options::staticMetaObject; int indexCompression = meta.indexOfEnumerator("eCompression"); QMetaEnum metaEnumCompression = meta.enumerator(indexCompression); @@ -1622,7 +1890,9 @@ void ProjectView::saveProjectFile() { xmlWriter.writeTextElement("AlphaThreshold", QString::number(data->Threshold)); xmlWriter.writeTextElement("BlockRate", data->m_Bitrate); - } else { + } + else + { // This should not be called check!! QMetaObject meta = C_Destination_Options::staticMetaObject; int indexCompression = meta.indexOfEnumerator("eCompression"); @@ -1640,10 +1910,13 @@ void ProjectView::saveProjectFile() { // xmlWriter.writeEndElement(); - } else if (levelType == TREETYPE_3DMODEL_DATA) { + } + else if (levelType == TREETYPE_3DMODEL_DATA) + { QVariant v = (*it)->data(TREE_SourceInfo, Qt::UserRole); C_3DModel_Info* m_data = v.value(); - if (m_data == NULL) { + if (m_data == NULL) + { ++it; continue; } @@ -1656,21 +1929,25 @@ void ProjectView::saveProjectFile() { xmlWriter.writeAttribute("File", FilePathName); int childcount = (*it)->childCount(); - if (childcount >= 1) { + if (childcount >= 1) + { // now save the child elements - for (int i = 0; i < childcount; i++) { + for (int i = 0; i < childcount; i++) + { //======= // Node //======= ++it; - if (*it) { + if (*it) + { QString Setting = (*it)->text(0); QVariant v = (*it)->data(TREE_LevelType, Qt::UserRole); int sublevelType = v.toInt(); // save the glTF detination settings ... node - if (sublevelType == TREETYPE_3DSUBMODEL_DATA) { + if (sublevelType == TREETYPE_3DSUBMODEL_DATA) + { v = (*it)->data(TREE_SourceInfo, Qt::UserRole); C_3DSubModel_Info* data = v.value(); @@ -1678,30 +1955,38 @@ void ProjectView::saveProjectFile() { xmlWriter.writeStartElement("SubModel"); xmlWriter.writeAttribute("Setting", Setting); xmlWriter.writeAttribute("Enabled", (*it)->checkState(0) == Qt::Checked ? "True" : "False"); - if (data) { + if (data) + { xmlWriter.writeTextElement("FileName", data->m_Full_Path); - for (int i = 0; i < data->m_Model_Images.size(); i++) { + for (int i = 0; i < data->m_Model_Images.size(); i++) + { xmlWriter.writeStartElement("DelFlags"); xmlWriter.writeAttribute("TexName", data->m_Model_Images[i].m_FilePathName); - if (data->m_SubModel_Images[i].m_srcDelFlag) { + if (data->m_SubModel_Images[i].m_srcDelFlag) + { xmlWriter.writeAttribute("Deleted", "true"); - } else + } + else xmlWriter.writeAttribute("Deleted", "false"); xmlWriter.writeEndElement(); } int subchildcount = (*it)->childCount(); - if (subchildcount >= 1) { - for (int i = 0; i < subchildcount; i++) { + if (subchildcount >= 1) + { + for (int i = 0; i < subchildcount; i++) + { //======= // Node //======= ++it; - if (*it) { + if (*it) + { QString Setting = (*it)->text(0); QVariant v = (*it)->data(TREE_LevelType, Qt::UserRole); int sublevelType = v.toInt(); - if ((sublevelType == TREETYPE_COMPRESSION_DATA) || (sublevelType == TREETYPE_MESH_DATA)) { + if ((sublevelType == TREETYPE_COMPRESSION_DATA) || (sublevelType == TREETYPE_MESH_DATA)) + { v = (*it)->data(TREE_SourceInfo, Qt::UserRole); C_Destination_Options* data = v.value(); @@ -1714,7 +1999,8 @@ void ProjectView::saveProjectFile() { xmlWriter.writeAttribute("Setting", Setting); xmlWriter.writeAttribute("Enabled", (*it)->checkState(0) == Qt::Checked ? "True" : "False"); - if (data) { + if (data) + { makeFormatExtCompatible(data); xmlWriter.writeTextElement("Source", data->m_sourceFileNamePath); @@ -1723,7 +2009,8 @@ void ProjectView::saveProjectFile() { xmlWriter.writeTextElement("ThreeDSource", data->m_modelSource); xmlWriter.writeTextElement("ThreeDDestination", data->m_modelDest); - if (sublevelType == TREETYPE_COMPRESSION_DATA) { + if (sublevelType == TREETYPE_COMPRESSION_DATA) + { QMetaObject meta = C_Destination_Options::staticMetaObject; int indexCompression = meta.indexOfEnumerator("eCompression"); QMetaEnum metaEnumCompression = meta.enumerator(indexCompression); @@ -1739,7 +2026,9 @@ void ProjectView::saveProjectFile() { xmlWriter.writeTextElement("AlphaThreshold", QString::number(data->Threshold)); xmlWriter.writeTextElement("BlockRate", data->m_Bitrate); - } else { + } + else + { QMetaObject meta = C_Destination_Options::staticMetaObject; int indexCompression = meta.indexOfEnumerator("eCompression"); QMetaEnum metaEnumCompression = meta.enumerator(indexCompression); @@ -1780,7 +2069,8 @@ void ProjectView::saveProjectFile() { m_saveProjectChanges = false; } -void ProjectView::saveAsProjectFile() { +void ProjectView::saveAsProjectFile() +{ QString filePathName = QFileDialog::getSaveFileName(this, tr("Save Project"), m_curProjectFilePathName, tr("Compress Project files (*.cprj)")); if (filePathName.length() == 0) return; @@ -1788,19 +2078,24 @@ void ProjectView::saveAsProjectFile() { saveProjectFile(); } -bool ProjectView::loadProjectFile(QString fileToLoad) { +bool ProjectView::loadProjectFile(QString fileToLoad) +{ int numCompressedItems = 0; // Create a document to write XML QDomDocument document; // Open a file for reading QFile file(fileToLoad); - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + { //qDebug() << "Failed to open the file for reading."; return false; - } else { + } + else + { // loading - if (!document.setContent(&file)) { + if (!document.setContent(&file)) + { //qDebug() << "Failed to load the file for reading."; return false; } @@ -1818,9 +2113,11 @@ bool ProjectView::loadProjectFile(QString fileToLoad) { // The number of Image nodes in the file int countImages = domImages.count(); - for (int i = 0; i < countImages; i++) { + for (int i = 0; i < countImages; i++) + { QDomNode domImage = domImages.at(i); - if (domImage.isElement()) { + if (domImage.isElement()) + { //====== // Node //====== @@ -1829,21 +2126,26 @@ bool ProjectView::loadProjectFile(QString fileToLoad) { QString FilePathName = eleImage.attribute("File"); // Check if we have a file specified else igonore the node - if (FilePathName.length() > 0) { + if (FilePathName.length() > 0) + { // Add the file to our Project View C_Source_Info* m_dataout = NULL; QTreeWidgetItem* Imageitem = Tree_AddImageFile(FilePathName, 0, &m_dataout); - if (Imageitem) { + if (Imageitem) + { QVariant v = Imageitem->data(TREE_LevelType, Qt::UserRole); int levelType = v.toInt(); - if (levelType == TREETYPE_IMAGEFILE_DATA) { + if (levelType == TREETYPE_IMAGEFILE_DATA) + { // Loop Image settings QDomNodeList domCompressions = eleImage.elementsByTagName("Compression"); int countCompressions = domCompressions.count(); - for (int i = 0; i < countCompressions; i++) { + for (int i = 0; i < countCompressions; i++) + { QDomNode domCompress = domCompressions.at(i); - if (domCompress.isElement()) { + if (domCompress.isElement()) + { numCompressedItems++; QDomElement eleCompress = domCompress.toElement(); @@ -1852,12 +2154,14 @@ bool ProjectView::loadProjectFile(QString fileToLoad) { // See also cpMainComponents - for new C_Destination_Options usage C_Destination_Options* m_data = new C_Destination_Options(); - if (m_data) { + if (m_data) + { m_data->m_compname = Setting; m_data->m_FileInfoDestinationName = Setting; m_data->m_sourceFileNamePath = FilePathName; - if (m_dataout) { + if (m_dataout) + { m_data->m_DstWidth = m_dataout->m_Width; m_data->m_DstHeight = m_dataout->m_Height; m_data->m_HeightStr = QString().number(m_data->m_DstHeight) + " px"; @@ -1866,16 +2170,21 @@ bool ProjectView::loadProjectFile(QString fileToLoad) { } QDomNode child = eleCompress.firstChild(); - while (!child.isNull()) { - if (child.toElement().tagName() == "Source") { + while (!child.isNull()) + { + if (child.toElement().tagName() == "Source") + { QDomElement eleDestination = child.toElement(); m_data->m_sourceFileNamePath = eleDestination.text(); - } else if (child.toElement().tagName() == "Destination") { + } + else if (child.toElement().tagName() == "Destination") + { QDomElement eleDestination = child.toElement(); m_data->m_destFileNamePath = eleDestination.text(); QFileInfo fileInfo(m_data->m_destFileNamePath); - if (!fileInfo.isWritable()) { + if (!fileInfo.isWritable()) + { QFileInfo fileInfo2(m_curProjectFilePathName); m_data->m_destFileNamePath = fileInfo2.dir().path(); m_data->m_destFileNamePath.append(QDir::separator()); @@ -1883,15 +2192,19 @@ bool ProjectView::loadProjectFile(QString fileToLoad) { m_data->m_destFileNamePath.replace("/", "\\"); //if current project file path is still not writable then change to app local path QFileInfo fileInfo3(m_data->m_destFileNamePath); - if (!fileInfo3.isWritable()) { + if (!fileInfo3.isWritable()) + { m_data->m_destFileNamePath = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation); m_data->m_destFileNamePath.append(QDir::separator()); m_data->m_destFileNamePath.append(fileInfo.fileName()); m_data->m_destFileNamePath.replace("/", "\\"); } - } else + } + else m_data->m_FileInfoDestinationName = Setting + "." + fileInfo.suffix(); - } else if (child.toElement().tagName() == "fd") { + } + else if (child.toElement().tagName() == "fd") + { QDomElement eleFD = child.toElement(); QString format = eleFD.text(); QMetaObject meta = C_Destination_Options::staticMetaObject; @@ -1900,42 +2213,54 @@ bool ProjectView::loadProjectFile(QString fileToLoad) { QMetaEnum metaEnumCompression = meta.enumerator(indexCompression); m_data->m_Compression = (C_Destination_Options::eCompression)metaEnumCompression.keysToValue(format.toLatin1().data()); - } else if (child.toElement().tagName() == "Quality") { + } + else if (child.toElement().tagName() == "Quality") + { QDomElement eleFD = child.toElement(); QString Quality = eleFD.text(); bool ok; m_data->m_Quality = Quality.toFloat(&ok); if (!ok) m_data->m_Quality = AMD_CODEC_QUALITY_DEFAULT; - } else if (child.toElement().tagName() == "WeightR") { + } + else if (child.toElement().tagName() == "WeightR") + { QDomElement eleFD = child.toElement(); QString WeightR = eleFD.text(); bool ok; m_data->X_RED = WeightR.toFloat(&ok); if (!ok) m_data->X_RED = 0.3086; - } else if (child.toElement().tagName() == "WeightG") { + } + else if (child.toElement().tagName() == "WeightG") + { QDomElement eleFD = child.toElement(); QString WeightG = eleFD.text(); bool ok; m_data->Y_GREEN = WeightG.toFloat(&ok); if (!ok) m_data->Y_GREEN = 0.6094; - } else if (child.toElement().tagName() == "WeightB") { + } + else if (child.toElement().tagName() == "WeightB") + { QDomElement eleFD = child.toElement(); QString WeightB = eleFD.text(); bool ok; m_data->Z_BLUE = WeightB.toFloat(&ok); if (!ok) m_data->Z_BLUE = 0.0820; - } else if (child.toElement().tagName() == "AlphaThreshold") { + } + else if (child.toElement().tagName() == "AlphaThreshold") + { QDomElement eleFD = child.toElement(); QString AlphaThreshold = eleFD.text(); bool ok; m_data->Threshold = AlphaThreshold.toInt(&ok); if (!ok) m_data->Threshold = 0; - } else if (child.toElement().tagName() == "BlockRate") { + } + else if (child.toElement().tagName() == "BlockRate") + { QDomElement eleFD = child.toElement(); QString BlockRate = eleFD.text(); m_data->m_Bitrate = BlockRate; @@ -1952,24 +2277,32 @@ bool ProjectView::loadProjectFile(QString fileToLoad) { } } } - } else if (levelType == TREETYPE_3DMODEL_DATA) { + } + else if (levelType == TREETYPE_3DMODEL_DATA) + { // Loop Image settings QDomNodeList domSubModel = eleImage.elementsByTagName("SubModel"); int countFiles = domSubModel.count(); - for (int i = 0; i < countFiles; i++) { + for (int i = 0; i < countFiles; i++) + { QDomNode dom3DFile = domSubModel.at(i); - if (dom3DFile.isElement()) { + if (dom3DFile.isElement()) + { QDomElement ele3Dest = dom3DFile.toElement(); QDomNode child = ele3Dest.firstChild(); - while (!child.isNull()) { - if (child.toElement().tagName() == "FileName") { + while (!child.isNull()) + { + if (child.toElement().tagName() == "FileName") + { QDomElement eleDestination = child.toElement(); QDomNodeList domDelFlags = ele3Dest.elementsByTagName("DelFlags"); int countDel = domDelFlags.count(); QList loadedDelFlags; - for (int i = 0; i < countDel; i++) { + for (int i = 0; i < countDel; i++) + { QDomNode domDel = domDelFlags.at(i); - if (domDel.isElement()) { + if (domDel.isElement()) + { QString delFlag = domDel.toElement().attribute("Deleted"); if (delFlag == "true") loadedDelFlags.append(true); @@ -1995,9 +2328,11 @@ bool ProjectView::loadProjectFile(QString fileToLoad) { // Loop Compression Mesh QDomNodeList domCompressions_mesh = ele3Dest.elementsByTagName("Compression_mesh"); int countCompressions_mesh = domCompressions_mesh.count(); - for (int i = 0; i < countCompressions_mesh; i++) { + for (int i = 0; i < countCompressions_mesh; i++) + { QDomNode domCompress = domCompressions_mesh.at(i); - if (domCompress.isElement()) { + if (domCompress.isElement()) + { numCompressedItems++; QDomElement eleCompress = domCompress.toElement(); @@ -2006,13 +2341,15 @@ bool ProjectView::loadProjectFile(QString fileToLoad) { // See also cpMainComponents - for new C_Destination_Options usage C_Destination_Options* m_data = new C_Destination_Options(); - if (m_data) { + if (m_data) + { m_data->m_isModelData = true; m_data->m_compname = Setting; m_data->m_FileInfoDestinationName = Setting; m_data->m_sourceFileNamePath = FilePathName; - if (m_dataout) { + if (m_dataout) + { m_data->m_DstWidth = m_dataout->m_Width; m_data->m_DstHeight = m_dataout->m_Height; m_data->m_HeightStr = QString().number(m_data->m_DstHeight) + " px"; @@ -2021,30 +2358,42 @@ bool ProjectView::loadProjectFile(QString fileToLoad) { } QDomNode child = eleCompress.firstChild(); - while (!child.isNull()) { - if (child.toElement().tagName() == "Source") { + while (!child.isNull()) + { + if (child.toElement().tagName() == "Source") + { QDomElement eleDestination = child.toElement(); m_data->m_sourceFileNamePath = eleDestination.text(); - } else if (child.toElement().tagName() == "Destination") { + } + else if (child.toElement().tagName() == "Destination") + { QDomElement eleDestination = child.toElement(); m_data->m_destFileNamePath = eleDestination.text(); QFileInfo fileInfo(m_data->m_destFileNamePath); - if (!fileInfo.isWritable()) { + if (!fileInfo.isWritable()) + { QFileInfo fileInfo2(m_curProjectFilePathName); m_data->m_destFileNamePath = fileInfo2.dir().path(); m_data->m_destFileNamePath.append(QDir::separator()); m_data->m_destFileNamePath.append(fileInfo.fileName()); m_data->m_destFileNamePath.replace("/", "\\"); - } else + } + else m_data->m_FileInfoDestinationName = Setting + "." + fileInfo.suffix(); - } else if (child.toElement().tagName() == "ThreeDSource") { + } + else if (child.toElement().tagName() == "ThreeDSource") + { QDomElement eleDestination = child.toElement(); m_data->m_modelSource = eleDestination.text(); - } else if (child.toElement().tagName() == "ThreeDDestination") { + } + else if (child.toElement().tagName() == "ThreeDDestination") + { QDomElement eleDestination = child.toElement(); m_data->m_modelDest = eleDestination.text(); - } else if (child.toElement().tagName() == "fd") { + } + else if (child.toElement().tagName() == "fd") + { // This is inavid for Compression_mesh type and should be ignored! QDomElement eleFD = child.toElement(); QString format = eleFD.text(); @@ -2070,9 +2419,11 @@ bool ProjectView::loadProjectFile(QString fileToLoad) { // Loop Compression Image Settings QDomNodeList domCompressions = ele3Dest.elementsByTagName("Compression"); int countCompressions = domCompressions.count(); - for (int i = 0; i < countCompressions; i++) { + for (int i = 0; i < countCompressions; i++) + { QDomNode domCompress = domCompressions.at(i); - if (domCompress.isElement()) { + if (domCompress.isElement()) + { numCompressedItems++; QDomElement eleCompress = domCompress.toElement(); @@ -2081,13 +2432,15 @@ bool ProjectView::loadProjectFile(QString fileToLoad) { // See also cpMainComponents - for new C_Destination_Options usage C_Destination_Options* m_data = new C_Destination_Options(); - if (m_data) { + if (m_data) + { m_data->m_isModelData = false; m_data->m_compname = Setting; m_data->m_FileInfoDestinationName = Setting; m_data->m_sourceFileNamePath = FilePathName; - if (m_dataout) { + if (m_dataout) + { m_data->m_DstWidth = m_dataout->m_Width; m_data->m_DstHeight = m_dataout->m_Height; m_data->m_HeightStr = QString().number(m_data->m_DstHeight) + " px"; @@ -2096,30 +2449,42 @@ bool ProjectView::loadProjectFile(QString fileToLoad) { } QDomNode child = eleCompress.firstChild(); - while (!child.isNull()) { - if (child.toElement().tagName() == "Source") { + while (!child.isNull()) + { + if (child.toElement().tagName() == "Source") + { QDomElement eleDestination = child.toElement(); m_data->m_sourceFileNamePath = eleDestination.text(); - } else if (child.toElement().tagName() == "Destination") { + } + else if (child.toElement().tagName() == "Destination") + { QDomElement eleDestination = child.toElement(); m_data->m_destFileNamePath = eleDestination.text(); QFileInfo fileInfo(m_data->m_destFileNamePath); - if (!fileInfo.isWritable()) { + if (!fileInfo.isWritable()) + { QFileInfo fileInfo2(m_curProjectFilePathName); m_data->m_destFileNamePath = fileInfo2.dir().path(); m_data->m_destFileNamePath.append(QDir::separator()); m_data->m_destFileNamePath.append(fileInfo.fileName()); m_data->m_destFileNamePath.replace("/", "\\"); - } else + } + else m_data->m_FileInfoDestinationName = Setting + "." + fileInfo.suffix(); - } else if (child.toElement().tagName() == "ThreeDSource") { + } + else if (child.toElement().tagName() == "ThreeDSource") + { QDomElement eleDestination = child.toElement(); m_data->m_modelSource = eleDestination.text(); - } else if (child.toElement().tagName() == "ThreeDDestination") { + } + else if (child.toElement().tagName() == "ThreeDDestination") + { QDomElement eleDestination = child.toElement(); m_data->m_modelDest = eleDestination.text(); - } else if (child.toElement().tagName() == "fd") { + } + else if (child.toElement().tagName() == "fd") + { QDomElement eleFD = child.toElement(); QString format = eleFD.text(); QMetaObject meta = C_Destination_Options::staticMetaObject; @@ -2128,42 +2493,54 @@ bool ProjectView::loadProjectFile(QString fileToLoad) { QMetaEnum metaEnumCompression = meta.enumerator(indexCompression); m_data->m_Compression = (C_Destination_Options::eCompression)metaEnumCompression.keysToValue(format.toLatin1().data()); - } else if (child.toElement().tagName() == "Quality") { + } + else if (child.toElement().tagName() == "Quality") + { QDomElement eleFD = child.toElement(); QString Quality = eleFD.text(); bool ok; m_data->m_Quality = Quality.toFloat(&ok); if (!ok) m_data->m_Quality = AMD_CODEC_QUALITY_DEFAULT; - } else if (child.toElement().tagName() == "WeightR") { + } + else if (child.toElement().tagName() == "WeightR") + { QDomElement eleFD = child.toElement(); QString WeightR = eleFD.text(); bool ok; m_data->X_RED = WeightR.toFloat(&ok); if (!ok) m_data->X_RED = 0.3086; - } else if (child.toElement().tagName() == "WeightG") { + } + else if (child.toElement().tagName() == "WeightG") + { QDomElement eleFD = child.toElement(); QString WeightG = eleFD.text(); bool ok; m_data->Y_GREEN = WeightG.toFloat(&ok); if (!ok) m_data->Y_GREEN = 0.6094; - } else if (child.toElement().tagName() == "WeightB") { + } + else if (child.toElement().tagName() == "WeightB") + { QDomElement eleFD = child.toElement(); QString WeightB = eleFD.text(); bool ok; m_data->Z_BLUE = WeightB.toFloat(&ok); if (!ok) m_data->Z_BLUE = 0.0820; - } else if (child.toElement().tagName() == "AlphaThreshold") { + } + else if (child.toElement().tagName() == "AlphaThreshold") + { QDomElement eleFD = child.toElement(); QString AlphaThreshold = eleFD.text(); bool ok; m_data->Threshold = AlphaThreshold.toInt(&ok); if (!ok) m_data->Threshold = 0; - } else if (child.toElement().tagName() == "BlockRate") { + } + else if (child.toElement().tagName() == "BlockRate") + { QDomElement eleFD = child.toElement(); QString BlockRate = eleFD.text(); m_data->m_Bitrate = BlockRate; @@ -2200,7 +2577,8 @@ bool ProjectView::loadProjectFile(QString fileToLoad) { return true; } -void ProjectView::openProjectFile() { +void ProjectView::openProjectFile() +{ if (!userSaveProjectAndContinue()) return; @@ -2209,7 +2587,8 @@ void ProjectView::openProjectFile() { loadProjectFile(filename); } -void ProjectView::removeSelectedImage() { +void ProjectView::removeSelectedImage() +{ if (!g_bCompressing) UserDeleteItems(); } @@ -2217,11 +2596,14 @@ void ProjectView::removeSelectedImage() { // Prompt the user to save prio project and continue or // cancel the current task -bool ProjectView::userSaveProjectAndContinue() { +bool ProjectView::userSaveProjectAndContinue() +{ // Check if user wants t osave prior changes - if (m_saveProjectChanges) { + if (m_saveProjectChanges) + { int action = PromptSaveChanges(); - switch (action) { + switch (action) + { case QMessageBox::AcceptRole: saveAsProjectFile(); break; @@ -2234,7 +2616,8 @@ bool ProjectView::userSaveProjectAndContinue() { return true; } -void ProjectView::openNewProjectFile() { +void ProjectView::openNewProjectFile() +{ // Check if user wants to save prior changes if (!userSaveProjectAndContinue()) return; @@ -2242,7 +2625,8 @@ void ProjectView::openNewProjectFile() { emit OnProjectLoaded(0); } -void ProjectView::onSetNewProject(QString& FilePathName) { +void ProjectView::onSetNewProject(QString& FilePathName) +{ // Remove the old view clearProjectTreeView(); setCurrentProjectName(FilePathName); @@ -2254,13 +2638,15 @@ void ProjectView::onSetNewProject(QString& FilePathName) { emit UpdateData(NULL); } -bool ProgressCallback(float fProgress, CMP_DWORD_PTR pUser1, CMP_DWORD_PTR pUser2) { +bool ProgressCallback(float fProgress, CMP_DWORD_PTR pUser1, CMP_DWORD_PTR pUser2) +{ // Keep Qt responsive QApplication::processEvents(); // Process the CmdLine Messages - if (g_fProgress != fProgress) { + if (g_fProgress != fProgress) + { UNREFERENCED_PARAMETER(pUser1); UNREFERENCED_PARAMETER(pUser2); @@ -2274,34 +2660,44 @@ bool ProgressCallback(float fProgress, CMP_DWORD_PTR pUser1, CMP_DWORD_PTR pUser return g_bAbortCompression; } -void ProjectView::onSignalProcessMessage() { - if (g_pProgressDlg) { +void ProjectView::onSignalProcessMessage() +{ + if (g_pProgressDlg) + { g_pProgressDlg->SetValue(g_fProgress); } } -void ProjectView::AddSettingtoEmptyTree() { +void ProjectView::AddSettingtoEmptyTree() +{ int childcount = 0; // Parse the Project view tree QTreeWidgetItemIterator it(m_projectTreeView); cpMainComponents* temp = (cpMainComponents*)(m_parent); - while (*it) { + while (*it) + { QString name = (*it)->text(0); QVariant v = (*it)->data(TREE_LevelType, Qt::UserRole); int levelType = v.toInt(); childcount = (*it)->childCount(); - if (levelType == TREETYPE_IMAGEFILE_DATA) { - if (childcount == 1) { - if (*it) { + if (levelType == TREETYPE_IMAGEFILE_DATA) + { + if (childcount == 1) + { + if (*it) + { QTreeWidgetItem* Imageitem = (*it); QString Setting = Imageitem->text(0); QVariant v = (*it)->data(TREE_LevelType, Qt::UserRole); int sublevelType = v.toInt(); - if (sublevelType == TREETYPE_IMAGEFILE_DATA) { - if (temp) { - if (temp->m_setcompressoptions) { + if (sublevelType == TREETYPE_IMAGEFILE_DATA) + { + if (temp) + { + if (temp->m_setcompressoptions) + { temp->m_setcompressoptions->m_DestinationData.init(); QVariant v = Imageitem->data(TREE_SourceInfo, Qt::UserRole); @@ -2332,9 +2728,12 @@ void ProjectView::AddSettingtoEmptyTree() { break; } - if (temp && temp->m_setcompressoptions->isNoSetting && !(m_processFromContext)) { - if (temp->m_setcompressoptions->updateDisplayContent()) { - if (!temp->m_setcompressoptions->isVisible()) { + if (temp && temp->m_setcompressoptions->isNoSetting && !(m_processFromContext)) + { + if (temp->m_setcompressoptions->updateDisplayContent()) + { + if (!temp->m_setcompressoptions->isVisible()) + { QPoint pos = QCursor::pos(); temp->m_setcompressoptions->move(pos); temp->m_setcompressoptions->m_LEName->setEnabled(false); @@ -2344,27 +2743,35 @@ void ProjectView::AddSettingtoEmptyTree() { } } - if (!temp->m_setcompressoptions->isNoSetting) { + if (!temp->m_setcompressoptions->isNoSetting) + { g_bAbortCompression = true; return; } - while (*it) { + while (*it) + { QString name = (*it)->text(0); QVariant v = (*it)->data(TREE_LevelType, Qt::UserRole); int levelType = v.toInt(); childcount = (*it)->childCount(); - if (levelType == TREETYPE_IMAGEFILE_DATA) { - if (childcount == 1) { - if (*it) { + if (levelType == TREETYPE_IMAGEFILE_DATA) + { + if (childcount == 1) + { + if (*it) + { QTreeWidgetItem* Imageitem = (*it); QString Setting = Imageitem->text(0); QVariant v = (*it)->data(TREE_LevelType, Qt::UserRole); int sublevelType = v.toInt(); - if (sublevelType == TREETYPE_IMAGEFILE_DATA) { - if (temp) { - if (temp->m_setcompressoptions) { + if (sublevelType == TREETYPE_IMAGEFILE_DATA) + { + if (temp) + { + if (temp->m_setcompressoptions) + { QVariant v = Imageitem->data(TREE_SourceInfo, Qt::UserRole); C_Source_Info* m_imagefile = v.value(); QFileInfo fileinfo(m_imagefile->m_Name); @@ -2412,15 +2819,20 @@ void ProjectView::AddSettingtoEmptyTree() { } } -void ProjectView::analyseMeshData() { - if (ContextMenu_ImageItem) { +void ProjectView::analyseMeshData() +{ + if (ContextMenu_ImageItem) + { QVariant v = ContextMenu_ImageItem->data(TREE_LevelType, Qt::UserRole); int levelType = v.toInt(); - switch (levelType) { - case TREETYPE_3DMODEL_DATA: { + switch (levelType) + { + case TREETYPE_3DMODEL_DATA: + { QVariant fv = ContextMenu_ImageItem->data(TREE_SourceInfo, Qt::UserRole); C_3DModel_Info* m_data = fv.value(); - if (m_data) { + if (m_data) + { QFileInfo fileInfo(m_data->m_Full_Path); QString EXT = fileInfo.suffix(); QByteArray ba = EXT.toUpper().toLatin1(); @@ -2435,17 +2847,21 @@ void ProjectView::analyseMeshData() { if (mainComponents) msgHandler = (void*)mainComponents->PrintStatus; - if (m_plugin_loader) { + if (m_plugin_loader) + { m_plugin_loader->TC_PluginSetSharedIO(g_GUI_CMIPS); int result; std::string filename = m_data->m_Full_Path.toStdString(); - if (strcmp(c_ext, "GLTF") == 0) { - if (isGLTFDracoFile(filename)) { + if (strcmp(c_ext, "GLTF") == 0) + { + if (isGLTFDracoFile(filename)) + { std::size_t dotPos = filename.rfind('.'); std::string tempdstFile = filename.substr(0, dotPos) + "_tmpdecoded.glTF"; - if (!decompressglTFfile(filename, tempdstFile, g_CmdPrams.use_Draco_Encode, g_CmdPrams.CompressOptions)) { + if (!decompressglTFfile(filename, tempdstFile, g_CmdPrams.use_Draco_Encode, g_CmdPrams.CompressOptions)) + { PrintInfo("Error: Decoding glTF file [%s] failed.\n", filename.c_str()); return; } @@ -2454,9 +2870,12 @@ void ProjectView::analyseMeshData() { } } - if (result = m_plugin_loader->LoadModelData(filename.c_str(), "", &g_pluginManager, msgHandler, &ProgressCallback) != 0) { - if (result != 0) { - if (m_CompressStatusDialog) { + if (result = m_plugin_loader->LoadModelData(filename.c_str(), "", &g_pluginManager, msgHandler, &ProgressCallback) != 0) + { + if (result != 0) + { + if (m_CompressStatusDialog) + { m_CompressStatusDialog->onClearText(); m_CompressStatusDialog->showOutput(); } @@ -2465,27 +2884,33 @@ void ProjectView::analyseMeshData() { } } - if (strcmp(c_ext, "GLTF") == 0) { + if (strcmp(c_ext, "GLTF") == 0) + { m_data->m_ModelData = (*(GLTFCommon*)m_plugin_loader->GetModelData()).m_meshBufferData; m_3DMeshAnalysisDlg->m_fileName = m_data->m_Full_Path; - } else { + } + else + { m_data->m_ModelData = (*(CMODEL_DATA*)m_plugin_loader->GetModelData()); m_3DMeshAnalysisDlg->m_fileName = QString::fromStdString(m_data->m_ModelData.m_model_name); } } hideProgressDialog(); run3DMeshAnalysis(&(m_data->m_ModelData), NULL); - if (m_plugin_loader) { + if (m_plugin_loader) + { delete m_plugin_loader; m_plugin_loader = nullptr; } } break; } - case TREETYPE_3DSUBMODEL_DATA: { + case TREETYPE_3DSUBMODEL_DATA: + { QVariant fv = ContextMenu_ImageItem->data(TREE_SourceInfo, Qt::UserRole); C_3DSubModel_Info* m_data = fv.value(); - if (m_data) { + if (m_data) + { QFileInfo fileInfo(m_data->m_Full_Path); QString EXT = fileInfo.suffix(); QByteArray ba = EXT.toUpper().toLatin1(); @@ -2500,15 +2925,19 @@ void ProjectView::analyseMeshData() { if (mainComponents) msgHandler = (void*)mainComponents->PrintStatus; - if (m_subplugin_loader) { + if (m_subplugin_loader) + { m_subplugin_loader->TC_PluginSetSharedIO(g_GUI_CMIPS); std::string filename = m_data->m_Full_Path.toStdString(); - if (strcmp(c_ext, "GLTF") == 0) { - if (isGLTFDracoFile(filename)) { + if (strcmp(c_ext, "GLTF") == 0) + { + if (isGLTFDracoFile(filename)) + { std::size_t dotPos = filename.rfind('.'); std::string tempdstFile = filename.substr(0, dotPos) + "_tmpdecoded.glTF"; - if (!decompressglTFfile(filename, tempdstFile, g_CmdPrams.use_Draco_Encode, g_CmdPrams.CompressOptions)) { + if (!decompressglTFfile(filename, tempdstFile, g_CmdPrams.use_Draco_Encode, g_CmdPrams.CompressOptions)) + { PrintInfo("Error: Decoding glTF file [%s] failed.\n", filename.c_str()); return; } @@ -2518,9 +2947,12 @@ void ProjectView::analyseMeshData() { } int result; - if (result = m_subplugin_loader->LoadModelData(filename.c_str(), "", &g_pluginManager, msgHandler, &ProgressCallback) != 0) { - if (result != 0) { - if (m_CompressStatusDialog) { + if (result = m_subplugin_loader->LoadModelData(filename.c_str(), "", &g_pluginManager, msgHandler, &ProgressCallback) != 0) + { + if (result != 0) + { + if (m_CompressStatusDialog) + { m_CompressStatusDialog->onClearText(); m_CompressStatusDialog->showOutput(); } @@ -2529,20 +2961,25 @@ void ProjectView::analyseMeshData() { } } - if (strcmp(c_ext, "GLTF") == 0) { + if (strcmp(c_ext, "GLTF") == 0) + { m_data->m_ModelData = (*(GLTFCommon*)m_subplugin_loader->GetModelData()).m_meshBufferData; m_3DMeshAnalysisDlg->m_fileName = m_data->m_Full_Path; - } else { + } + else + { m_data->m_ModelData = (*(CMODEL_DATA*)m_subplugin_loader->GetModelData()); m_3DMeshAnalysisDlg->m_fileName = QString::fromStdString(m_data->m_ModelData.m_model_name); } } QTreeWidgetItem* ParentItem = ContextMenu_ImageItem->parent(); - if (ParentItem) { + if (ParentItem) + { QVariant parentv = ParentItem->data(TREE_SourceInfo, Qt::UserRole); C_3DModel_Info* parentdata = parentv.value(); - if (parentdata) { + if (parentdata) + { QFileInfo fileInfo(parentdata->m_Full_Path); QString EXT = fileInfo.suffix(); QByteArray ba = EXT.toUpper().toLatin1(); @@ -2557,15 +2994,19 @@ void ProjectView::analyseMeshData() { if (mainComponents) msgHandler = (void*)mainComponents->PrintStatus; - if (m_plugin_loader) { + if (m_plugin_loader) + { m_plugin_loader->TC_PluginSetSharedIO(g_GUI_CMIPS); std::string filename = parentdata->m_Full_Path.toStdString(); - if (strcmp(c_ext, "GLTF") == 0) { - if (isGLTFDracoFile(filename)) { + if (strcmp(c_ext, "GLTF") == 0) + { + if (isGLTFDracoFile(filename)) + { std::size_t dotPos = filename.rfind('.'); std::string tempdstFile = filename.substr(0, dotPos) + "_tmpdecoded.glTF"; - if (!decompressglTFfile(filename, tempdstFile, g_CmdPrams.use_Draco_Encode, g_CmdPrams.CompressOptions)) { + if (!decompressglTFfile(filename, tempdstFile, g_CmdPrams.use_Draco_Encode, g_CmdPrams.CompressOptions)) + { PrintInfo("Error: Decoding glTF file [%s] failed.\n", filename.c_str()); return; } @@ -2575,9 +3016,12 @@ void ProjectView::analyseMeshData() { } int result; - if (result = m_plugin_loader->LoadModelData(filename.c_str(), "", &g_pluginManager, msgHandler, &ProgressCallback) != 0) { - if (result != 0) { - if (m_CompressStatusDialog) { + if (result = m_plugin_loader->LoadModelData(filename.c_str(), "", &g_pluginManager, msgHandler, &ProgressCallback) != 0) + { + if (result != 0) + { + if (m_CompressStatusDialog) + { m_CompressStatusDialog->onClearText(); m_CompressStatusDialog->showOutput(); } @@ -2586,10 +3030,13 @@ void ProjectView::analyseMeshData() { } } - if (strcmp(c_ext, "GLTF") == 0) { + if (strcmp(c_ext, "GLTF") == 0) + { parentdata->m_ModelData = (*(GLTFCommon*)m_plugin_loader->GetModelData()).m_meshBufferData; m_3DMeshAnalysisDlg->m_fileNameCompare = parentdata->m_Full_Path; - } else { + } + else + { parentdata->m_ModelData = (*(CMODEL_DATA*)m_plugin_loader->GetModelData()); m_3DMeshAnalysisDlg->m_fileNameCompare = QString::fromStdString(parentdata->m_ModelData.m_model_name); } @@ -2598,17 +3045,22 @@ void ProjectView::analyseMeshData() { hideProgressDialog(); run3DMeshAnalysis(&(m_data->m_ModelData), &(parentdata->m_ModelData)); - if (m_plugin_loader) { + if (m_plugin_loader) + { delete m_plugin_loader; m_plugin_loader = nullptr; } - } else + } + else run3DMeshAnalysis(&(m_data->m_ModelData), NULL); - } else { + } + else + { run3DMeshAnalysis(&(m_data->m_ModelData), NULL); } - if (m_subplugin_loader) { + if (m_subplugin_loader) + { delete m_subplugin_loader; m_subplugin_loader = nullptr; } @@ -2625,22 +3077,27 @@ void ProjectView::analyseMeshData() { } } -void ProjectView::viewDiffImageFromChild() { +void ProjectView::viewDiffImageFromChild() +{ // Get the active Image view node - if (m_CurrentCompressedImageItem) { + if (m_CurrentCompressedImageItem) + { // view image QVariant v = m_CurrentCompressedImageItem->data(TREE_SourceInfo, Qt::UserRole); C_Destination_Options* m_data = v.value(); - if (m_data) { + if (m_data) + { QFileInfo fileinfo(m_data->m_destFileNamePath); QFile file(m_data->m_destFileNamePath); - if (file.exists() && (fileinfo.suffix().length() > 0)) { + if (file.exists() && (fileinfo.suffix().length() > 0)) + { QTreeWidgetItem* parent = m_CurrentCompressedImageItem->parent(); QString sourcefile = GetSourceFileNamePath(parent); QFileInfo fileinfo(sourcefile); QFile file(sourcefile); - if (file.exists() && (fileinfo.suffix().length() > 0)) { + if (file.exists() && (fileinfo.suffix().length() > 0)) + { m_curDiffSourceFile = m_data->m_sourceFileNamePath; m_curDiffDestFile = m_data->m_destFileNamePath; @@ -2651,34 +3108,45 @@ void ProjectView::viewDiffImageFromChild() { } } -void ProjectView::viewDiff3DModelFromChild() { +void ProjectView::viewDiff3DModelFromChild() +{ // Get the active Image view node - if (m_CurrentCompressedImageItem) { + if (m_CurrentCompressedImageItem) + { // view image QVariant v = m_CurrentCompressedImageItem->data(TREE_SourceInfo, Qt::UserRole); C_3DSubModel_Info* m_data = v.value(); - if (m_data) { + if (m_data) + { QFileInfo fileinfodest(m_data->m_Full_Path); ; - if (QFile::exists(m_data->m_Full_Path) && (fileinfodest.suffix().length() > 0)) { + if (QFile::exists(m_data->m_Full_Path) && (fileinfodest.suffix().length() > 0)) + { QTreeWidgetItem* parent = m_CurrentCompressedImageItem->parent(); QString sourcefile = GetSourceFileNamePath(parent); QFileInfo fileinfosrc(sourcefile); - if (QFile::exists(sourcefile) && (fileinfosrc.suffix().length() > 0)) { + if (QFile::exists(sourcefile) && (fileinfosrc.suffix().length() > 0)) + { m_curDiffSourceFile = sourcefile; m_curDiffDestFile = m_data->m_Full_Path; emit View3DModelFileDiff(m_data, sourcefile, m_data->m_Full_Path); - } else if (!(QFile::exists(sourcefile))) { - if (m_CompressStatusDialog) { + } + else if (!(QFile::exists(sourcefile))) + { + if (m_CompressStatusDialog) + { m_CompressStatusDialog->onClearText(); m_CompressStatusDialog->showOutput(); } PrintInfo("Error: File %s does not exist.\n", sourcefile.toStdString().c_str()); return; } - } else if (!(QFile::exists(m_data->m_Full_Path))) { - if (m_CompressStatusDialog) { + } + else if (!(QFile::exists(m_data->m_Full_Path))) + { + if (m_CompressStatusDialog) + { m_CompressStatusDialog->onClearText(); m_CompressStatusDialog->showOutput(); } @@ -2689,22 +3157,27 @@ void ProjectView::viewDiff3DModelFromChild() { } } -void ProjectView::viewImageDiff() { +void ProjectView::viewImageDiff() +{ // Get the active Image view node - if (m_CurrentCompressedImageItem) { + if (m_CurrentCompressedImageItem) + { // view image QVariant v = m_CurrentCompressedImageItem->data(1, Qt::UserRole); C_Destination_Options* m_data = v.value(); - if (m_data) { + if (m_data) + { QFileInfo fileinfo(m_data->m_destFileNamePath); QFile file(m_data->m_destFileNamePath); - if (file.exists() && (fileinfo.suffix().length() > 0)) { + if (file.exists() && (fileinfo.suffix().length() > 0)) + { QTreeWidgetItem* parent = m_CurrentCompressedImageItem->parent(); QString sourcefile = GetSourceFileNamePath(parent); QFileInfo fileinfo(sourcefile); QFile file(sourcefile); - if (file.exists() && (fileinfo.suffix().length() > 0)) { + if (file.exists() && (fileinfo.suffix().length() > 0)) + { emit ViewImageFileDiff(m_data, sourcefile, m_data->m_destFileNamePath); } } @@ -2712,7 +3185,8 @@ void ProjectView::viewImageDiff() { } } -void ProjectView::SetupTreeView() { +void ProjectView::SetupTreeView() +{ m_projectTreeView = new cpTreeWidget(this); #ifdef USE_DELEGATE @@ -2813,14 +3287,15 @@ void ProjectView::SetupTreeView() { m_contextMenu->addAction(actAnalyseMeshData); } -void ProjectView::Tree_AddRootNode() { +void ProjectView::Tree_AddRootNode() +{ m_treeRootItem = new QTreeWidgetItem(m_projectTreeView); m_treeRootItem->setFlags(Qt::ItemIsEnabled); m_NumItems++; // Keep first char as space so when treeview is sorted by file path its aways on top of view m_treeRootItem->setText(0, "Double Click here to add files..."); - m_treeRootItem->setIcon(0, QIcon(":/CompressonatorGUI/Images/plus.png")); + m_treeRootItem->setIcon(0, QIcon(":/compressonatorgui/images/plus.png")); // This item has gray color QFont font("Default", 9, QFont::Bold); QBrush b(Qt::gray); @@ -2830,8 +3305,10 @@ void ProjectView::Tree_AddRootNode() { m_treeRootItem->setData(TREE_LevelType, Qt::UserRole, QVariant::fromValue(TREETYPE_Double_Click_here_to_add_files)); } -void ProjectView::showProgressDialog(QString header) { - if (g_pProgressDlg) { +void ProjectView::showProgressDialog(QString header) +{ + if (g_pProgressDlg) + { g_pProgressDlg->SetValue(0); g_pProgressDlg->SetHeader(header); g_pProgressDlg->SetLabelText(""); @@ -2839,12 +3316,14 @@ void ProjectView::showProgressDialog(QString header) { } } -void ProjectView::hideProgressDialog() { +void ProjectView::hideProgressDialog() +{ if (g_pProgressDlg) g_pProgressDlg->hide(); } -QTreeWidgetItem* ProjectView::Tree_AddImageFile(QString filePathName, int index, C_Source_Info** m_dataout) { +QTreeWidgetItem* ProjectView::Tree_AddImageFile(QString filePathName, int index, C_Source_Info** m_dataout) +{ Q_UNUSED(index); QTreeWidgetItem* treeItem = NULL; @@ -2852,7 +3331,8 @@ QTreeWidgetItem* ProjectView::Tree_AddImageFile(QString filePathName, int index, QFile SourceFile(filePathName); // file not found! - if (!SourceFile.exists()) { + if (!SourceFile.exists()) + { if (filePathName.length() > 3) PrintInfo("Error: SourceFile %s not found.\n", filePathName.toStdString().c_str()); else @@ -2861,7 +3341,8 @@ QTreeWidgetItem* ProjectView::Tree_AddImageFile(QString filePathName, int index, } // Check if item already exist if so just exit - if (Tree_FindImageItem(filePathName, false)) { + if (Tree_FindImageItem(filePathName, false)) + { PrintInfo("Error: item already exist in project tree (%s).\n", filePathName.toStdString().c_str()); return NULL; } @@ -2877,16 +3358,19 @@ QTreeWidgetItem* ProjectView::Tree_AddImageFile(QString filePathName, int index, bool isImage = true; QImageReader imageFormat(filePathName); - if (!(imageFormat.canRead()) && !(g_pluginManager.PluginSupported("IMAGE", (char*)Ext))) { + if (!(imageFormat.canRead()) && !(g_pluginManager.PluginSupported("IMAGE", (char*)Ext))) + { // The file is not an image checking other supported formats - if (!g_pluginManager.PluginSupported("3DMODEL_LOADER", (char*)Ext)) { + if (!g_pluginManager.PluginSupported("3DMODEL_LOADER", (char*)Ext)) + { return NULL; } isImage = false; } //.cprj is detected as .svg file by qt support format so alway can be drop, below is the fix - if (strcmp(Ext, "CPRJ") == 0) { + if (strcmp(Ext, "CPRJ") == 0) + { PrintInfo("Error:.cprj is detected as .svg file by qt support format "); return NULL; } @@ -2902,8 +3386,9 @@ QTreeWidgetItem* ProjectView::Tree_AddImageFile(QString filePathName, int index, // for the original image on the Project View //--------------------------------------------- QPixmap filepixmap(filePathName); - if (filepixmap.size().height() == 0) { - filepixmap.load(":/CompressonatorGUI/Images/file.png"); + if (filepixmap.size().height() == 0) + { + filepixmap.load(":/compressonatorgui/images/file.png"); } QPixmap newPixmap = filepixmap.scaled(QSize(32, 32), Qt::IgnoreAspectRatio); @@ -2916,7 +3401,7 @@ QTreeWidgetItem* ProjectView::Tree_AddImageFile(QString filePathName, int index, if (isImage) treeItem->setIcon(0, treeicon); // QIcon(filePathName)); else - treeItem->setIcon(0, QIcon(":/CompressonatorGUI/Images/3Dfile.png")); + treeItem->setIcon(0, QIcon(":/compressonatorgui/images/3dfile.png")); treeItem->setToolTip(0, filePathName); @@ -2926,9 +3411,11 @@ QTreeWidgetItem* ProjectView::Tree_AddImageFile(QString filePathName, int index, treeItem->setData(TREE_LevelType, Qt::UserRole, QVariant::fromValue(isImage ? TREETYPE_IMAGEFILE_DATA : TREETYPE_3DMODEL_DATA)); // Create Data for this file and set current know values - if (isImage) { + if (isImage) + { C_Source_Info* m_data = new C_Source_Info(); - if (m_data == NULL) { + if (m_data == NULL) + { PrintInfo("Error: Tree_AddImageFile m_data is null"); return NULL; } @@ -2948,16 +3435,20 @@ QTreeWidgetItem* ProjectView::Tree_AddImageFile(QString filePathName, int index, m_data->m_MipImages = m_imageloader->LoadPluginImage(filePathName.toStdString()); // Mip levels - if (m_data->m_MipImages) { - if (m_data->m_MipImages->errMsg != "") { - if (m_CompressStatusDialog) { + if (m_data->m_MipImages) + { + if (m_data->m_MipImages->errMsg != "") + { + if (m_CompressStatusDialog) + { m_CompressStatusDialog->onClearText(); m_CompressStatusDialog->showOutput(); } PrintInfo("Add Image Error: %s.\n", (m_data->m_MipImages->errMsg).c_str()); return NULL; } - if (m_data->m_MipImages->mipset) { + if (m_data->m_MipImages->mipset) + { if (m_data->m_MipImages->mipset->m_nMipLevels > 1) m_data->m_Mip_Levels = m_data->m_MipImages->mipset->m_nMipLevels - 1; @@ -2977,7 +3468,8 @@ QTreeWidgetItem* ProjectView::Tree_AddImageFile(QString filePathName, int index, CMIPS CMips; MipLevel* pInMipLevel = CMips.GetMipLevel(m_data->m_MipImages->mipset, 0, 0); - if (pInMipLevel) { + if (pInMipLevel) + { m_data->m_ImageSize = pInMipLevel->m_dwLinearSize; if (m_data->m_ImageSize > 1024000) @@ -2986,7 +3478,8 @@ QTreeWidgetItem* ProjectView::Tree_AddImageFile(QString filePathName, int index, m_data->m_ImageSizeStr = QString().number((double)m_data->m_ImageSize / 1024, 'f', 1) + " KB"; else m_data->m_ImageSizeStr = QString().number(m_data->m_ImageSize) + " Bytes"; - } else + } + else m_data->m_ImageSizeStr = UNKNOWN_IMAGE; } } @@ -2996,28 +3489,36 @@ QTreeWidgetItem* ProjectView::Tree_AddImageFile(QString filePathName, int index, Tree_AddCompressFile(treeItem, STR_AddDestinationSetting, false, false, TREETYPE_Add_destination_setting, NULL); emit OnAddedImageSourceNode(); - } else { + } + else + { #ifdef _WIN32 - if (g_Application_Options.getGLTFRender() == C_Application_Options::RenderModelsWith::glTF_DX12_EX) { + if (g_Application_Options.getGLTFRender() == C_Application_Options::RenderModelsWith::glTF_DX12_EX) + { typedef LONG NTSTATUS, *PNTSTATUS; typedef NTSTATUS(WINAPI * RtlGetVersionPtr)(PRTL_OSVERSIONINFOW); RTL_OSVERSIONINFOW win10OSver = {0}; HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll"); - if (hMod) { + if (hMod) + { RtlGetVersionPtr fxPtr = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion"); - if (fxPtr != nullptr) { + if (fxPtr != nullptr) + { RTL_OSVERSIONINFOW rovi = {0}; rovi.dwOSVersionInfoSize = sizeof(rovi); - if (STATUS_SUCCESS == fxPtr(&rovi)) { + if (STATUS_SUCCESS == fxPtr(&rovi)) + { win10OSver = rovi; } } } - if ((win10OSver.dwBuildNumber != 0) && (win10OSver.dwBuildNumber < 15063)) { - if (m_CompressStatusDialog) { + if ((win10OSver.dwBuildNumber != 0) && (win10OSver.dwBuildNumber < 15063)) + { + if (m_CompressStatusDialog) + { m_CompressStatusDialog->onClearText(); m_CompressStatusDialog->showOutput(); } @@ -3032,7 +3533,8 @@ QTreeWidgetItem* ProjectView::Tree_AddImageFile(QString filePathName, int index, treeItem->setToolTip(2, "show model image files"); C_3DModel_Info* m_data = new C_3DModel_Info(); - if (m_data == NULL) { + if (m_data == NULL) + { return NULL; } @@ -3054,13 +3556,15 @@ QTreeWidgetItem* ProjectView::Tree_AddImageFile(QString filePathName, int index, QString filePath = finfo.absolutePath(); std::ifstream fstream(filePathName.toStdString()); - if (!fstream) { + if (!fstream) + { return nullptr; } showProgressDialog("Loading Model Data"); - if (strcmp(Ext, "GLTF") == 0) { + if (strcmp(Ext, "GLTF") == 0) + { m_data->ModelType = eModelType::GLTF; // Load the glTF json text file fstream >> m_data->m_gltf; @@ -3070,7 +3574,8 @@ QTreeWidgetItem* ProjectView::Tree_AddImageFile(QString filePathName, int index, // Vertex // ************/ auto buffers = m_data->m_gltf["buffers"]; - for (unsigned int i = 0; i < buffers.size(); i++) { + for (unsigned int i = 0; i < buffers.size(); i++) + { std::string name = buffers[i]["uri"].get(); QString str = filePath + "/" + name.c_str(); @@ -3081,18 +3586,20 @@ QTreeWidgetItem* ProjectView::Tree_AddImageFile(QString filePathName, int index, if (str.contains("bin")) child = Tree_Add3DModelMeshFile( - treeItem, filePath + "/" + name.c_str(), m_data->m_Full_Path, false, false, TREETYPE_VIEWMESH_ONLY_NODE, &ProgressCallback); + treeItem, filePath + "/" + name.c_str(), m_data->m_Full_Path, false, false, TREETYPE_VIEWMESH_ONLY_NODE, &ProgressCallback); else PrintInfo("Note: embedded glTF mesh process is not supported yet. only .bin mesh is supported now."); int image_filesize = 0; // get the child node we just added and its data - if (child) { + if (child) + { QVariant v = child->data(TREE_SourceInfo, Qt::UserRole); C_Source_Info* m_ChildData = v.value(); - if (m_ChildData) { + if (m_ChildData) + { image_filesize = m_ChildData->m_ImageSize; } } @@ -3111,7 +3618,8 @@ QTreeWidgetItem* ProjectView::Tree_AddImageFile(QString filePathName, int index, Images ************/ auto Images = m_data->m_gltf["images"]; - for (unsigned int i = 0; i < Images.size(); i++) { + for (unsigned int i = 0; i < Images.size(); i++) + { std::string name = Images[i]["uri"].get(); QString str = filePath + "/" + name.c_str(); if (g_pProgressDlg) @@ -3124,11 +3632,13 @@ QTreeWidgetItem* ProjectView::Tree_AddImageFile(QString filePathName, int index, int image_width = 0; int image_height = 0; - if (child) { + if (child) + { QVariant v = child->data(TREE_SourceInfo, Qt::UserRole); C_Source_Info* m_ChildData = v.value(); - if (m_ChildData) { + if (m_ChildData) + { image_filesize = m_ChildData->m_ImageSize; image_width = m_ChildData->m_Width; image_height = m_ChildData->m_Height; @@ -3147,7 +3657,8 @@ QTreeWidgetItem* ProjectView::Tree_AddImageFile(QString filePathName, int index, } auto asset = m_data->m_gltf["asset"]; - if (asset.size() > 0) { + if (asset.size() > 0) + { auto gen = asset["generator"]; if (gen.size() > 0) m_data->m_GeneratorStr = gen.get().c_str(); @@ -3163,7 +3674,9 @@ QTreeWidgetItem* ProjectView::Tree_AddImageFile(QString filePathName, int index, // Add the image to the diff image list if it is not in the list if ((!(m_ImagesinProjectTrees.contains(filePathName))) && (!(filePathName.contains(".gltf"))) && (!(filePathName.contains(".obj")))) m_ImagesinProjectTrees.append(filePathName); - } else if (strcmp(Ext, "OBJ") == 0) { + } + else if (strcmp(Ext, "OBJ") == 0) + { m_data->ModelType = eModelType::OBJ; // Add compression setting option under the new item treeItem->setData(TREE_SourceInfo, Qt::UserRole, QVariant::fromValue(m_data)); @@ -3185,26 +3698,35 @@ QTreeWidgetItem* ProjectView::Tree_AddImageFile(QString filePathName, int index, PluginInterface_3DModel_Loader* plugin_loader = NULL; plugin_loader = reinterpret_cast(g_pluginManager.GetPlugin("3DMODEL_LOADER", "OBJ")); - if (plugin_loader) { + if (plugin_loader) + { plugin_loader->TC_PluginSetSharedIO(g_GUI_CMIPS); int result = plugin_loader->LoadModelData(m_data->m_Full_Path.toStdString().data(), "", &g_pluginManager, NULL, &ProgressCallback); - if (result != 0) { + if (result != 0) + { if (m_CompressStatusDialog) m_CompressStatusDialog->appendText("Error in loading mesh file."); - } else { + } + else + { CMODEL_DATA* temp = (CMODEL_DATA*)(plugin_loader->GetModelData()); m_data->m_ModelData = temp[0]; } - } else { + } + else + { if (m_CompressStatusDialog) m_CompressStatusDialog->appendText("File format not supported."); } - if (plugin_loader) { + if (plugin_loader) + { delete plugin_loader; plugin_loader = NULL; } #endif - } else if (strcmp(Ext, "DRC") == 0) { + } + else if (strcmp(Ext, "DRC") == 0) + { m_data->ModelType = eModelType::DRC; treeItem->setData(TREE_SourceInfo, Qt::UserRole, QVariant::fromValue(m_data)); } @@ -3217,7 +3739,8 @@ QTreeWidgetItem* ProjectView::Tree_AddImageFile(QString filePathName, int index, return treeItem; } -void ProjectView::Tree_Add3DSubModelFile(QTreeWidgetItem* ParentItem, QString filePathName, QList* srcDelFlags) { +void ProjectView::Tree_Add3DSubModelFile(QTreeWidgetItem* ParentItem, QString filePathName, QList* srcDelFlags) +{ QTreeWidgetItem* treeItem = NULL; QString filename; QFile SourceFile(filePathName); @@ -3238,8 +3761,9 @@ void ProjectView::Tree_Add3DSubModelFile(QTreeWidgetItem* ParentItem, QString fi // for the original image on the Project View //--------------------------------------------- QPixmap filepixmap(filePathName); - if (filepixmap.size().height() == 0) { - filepixmap.load(":/CompressonatorGUI/Images/file.png"); + if (filepixmap.size().height() == 0) + { + filepixmap.load(":/compressonatorgui/images/file.png"); } QPixmap newPixmap = filepixmap.scaled(QSize(32, 32), Qt::IgnoreAspectRatio); @@ -3247,7 +3771,7 @@ void ProjectView::Tree_Add3DSubModelFile(QTreeWidgetItem* ParentItem, QString fi treeicon.addPixmap(newPixmap, QIcon::Normal, QIcon::On); treeItem->setSizeHint(0, QSize(33, 33)); - treeItem->setIcon(0, QIcon(":/CompressonatorGUI/Images/3Dfile.png")); + treeItem->setIcon(0, QIcon(":/compressonatorgui/images/3dfile.png")); treeItem->setToolTip(0, filePathName); @@ -3258,7 +3782,8 @@ void ProjectView::Tree_Add3DSubModelFile(QTreeWidgetItem* ParentItem, QString fi // Create Data for this file and set current know values C_3DSubModel_Info* m_data = new C_3DSubModel_Info(); - if (m_data == NULL) { + if (m_data == NULL) + { return; } @@ -3283,7 +3808,8 @@ void ProjectView::Tree_Add3DSubModelFile(QTreeWidgetItem* ParentItem, QString fi // Add parent item data to child : Should Check its type! QVariant parentv = ParentItem->data(TREE_SourceInfo, Qt::UserRole); C_3DModel_Info* m_parentdata = parentv.value(); - if (m_parentdata) { + if (m_parentdata) + { m_data->m_ModelSource_gltf = m_parentdata->m_Full_Path; m_data->m_Model_Images = m_parentdata->m_Model_Images; m_data->m_ModelData = m_parentdata->m_ModelData; @@ -3292,9 +3818,11 @@ void ProjectView::Tree_Add3DSubModelFile(QTreeWidgetItem* ParentItem, QString fi m_data->ModelType = ModelType; } - if (ModelType == eModelType::GLTF) { + if (ModelType == eModelType::GLTF) + { std::ifstream fstream(filePathName.toStdString()); - if (!fstream) { + if (!fstream) + { return; } @@ -3311,7 +3839,8 @@ void ProjectView::Tree_Add3DSubModelFile(QTreeWidgetItem* ParentItem, QString fi Buffers ************/ auto buffers = j3["buffers"]; - for (unsigned int i = 0; i < buffers.size(); i++) { + for (unsigned int i = 0; i < buffers.size(); i++) + { std::string name = buffers[i]["uri"].get(); QString str = filePath + "/" + name.c_str(); Model_Image model_image; @@ -3325,7 +3854,8 @@ void ProjectView::Tree_Add3DSubModelFile(QTreeWidgetItem* ParentItem, QString fi Images ************/ auto Images = j3["images"]; - for (unsigned int i = 0; i < Images.size(); i++) { + for (unsigned int i = 0; i < Images.size(); i++) + { std::string name = Images[i]["uri"].get(); QString str = filePath + "/" + name.c_str(); Model_Image model_image; @@ -3335,16 +3865,19 @@ void ProjectView::Tree_Add3DSubModelFile(QTreeWidgetItem* ParentItem, QString fi m_data->m_SubModel_Images.append(model_image); // This should be replaced by m_SubModel_Images } - if (srcDelFlags != NULL) { + if (srcDelFlags != NULL) + { int indexStart = (int)buffers.size(); - if (srcDelFlags->size() <= (m_data->m_Model_Images.size() - indexStart)) { + if (srcDelFlags->size() <= (m_data->m_Model_Images.size() - indexStart)) + { for (int i = 0; i < srcDelFlags->size(); i++) m_data->m_SubModel_Images[i + indexStart].m_srcDelFlag = srcDelFlags->at(i); } } auto asset = j3["asset"]; - if (asset.size() > 0) { + if (asset.size() > 0) + { std::string name = asset["generator"].get(); if (name.length() > 0) m_data->m_GeneratorStr = name.c_str(); @@ -3352,7 +3885,9 @@ void ProjectView::Tree_Add3DSubModelFile(QTreeWidgetItem* ParentItem, QString fi if (name.length() > 0) m_data->m_VersionStr = name.c_str(); } - } else if (ModelType == eModelType::OBJ) { + } + else if (ModelType == eModelType::OBJ) + { // For Obj file the source is the file itself..! std::string name = m_data->m_Name.toStdString(); QString str = filePath + "/" + name.c_str(); @@ -3373,11 +3908,12 @@ void ProjectView::Tree_Add3DSubModelFile(QTreeWidgetItem* ParentItem, QString fi } QTreeWidgetItem* ProjectView::Tree_Add3DModelImageFiles(QTreeWidgetItem* ParentItem, - QString filePathName, - bool checkable, - bool checked, - int levelType, - CMP_Feedback_Proc pFeedbackProc) { + QString filePathName, + bool checkable, + bool checked, + int levelType, + CMP_Feedback_Proc pFeedbackProc) +{ Q_UNUSED(checkable) Q_UNUSED(checked) Q_UNUSED(levelType) @@ -3391,7 +3927,8 @@ QTreeWidgetItem* ProjectView::Tree_Add3DModelImageFiles(QTreeWidgetItem* Parent QFile f(filePathName); // file not found! - if (!f.exists()) { + if (!f.exists()) + { return NULL; } @@ -3407,7 +3944,8 @@ QTreeWidgetItem* ProjectView::Tree_Add3DModelImageFiles(QTreeWidgetItem* Parent const char* Ext = ba.data(); QImageReader imageFormat(filePathName); - if (!(imageFormat.canRead()) && !(g_pluginManager.PluginSupported("IMAGE", (char*)Ext))) { + if (!(imageFormat.canRead()) && !(g_pluginManager.PluginSupported("IMAGE", (char*)Ext))) + { return NULL; } @@ -3422,8 +3960,9 @@ QTreeWidgetItem* ProjectView::Tree_Add3DModelImageFiles(QTreeWidgetItem* Parent // for the original image on the Project View //--------------------------------------------- QPixmap filepixmap(filePathName); - if (filepixmap.size().height() == 0) { - filepixmap.load(":/CompressonatorGUI/Images/file.png"); + if (filepixmap.size().height() == 0) + { + filepixmap.load(":/compressonatorgui/images/file.png"); } QPixmap newPixmap = filepixmap.scaled(QSize(32, 32), Qt::IgnoreAspectRatio); @@ -3445,7 +3984,8 @@ QTreeWidgetItem* ProjectView::Tree_Add3DModelImageFiles(QTreeWidgetItem* Parent Create Data for this file and set current know values *********************************************************/ C_Source_Info* m_data = new C_Source_Info(); - if (m_data == NULL) { + if (m_data == NULL) + { return NULL; } @@ -3465,8 +4005,10 @@ QTreeWidgetItem* ProjectView::Tree_Add3DModelImageFiles(QTreeWidgetItem* Parent m_data->m_MipImages = m_imageloader->LoadPluginImage(filePathName.toStdString(), pFeedbackProc); // Mip levels - if (m_data->m_MipImages) { - if (m_data->m_MipImages->mipset) { + if (m_data->m_MipImages) + { + if (m_data->m_MipImages->mipset) + { if (m_data->m_MipImages->mipset->m_nMipLevels > 1) m_data->m_Mip_Levels = m_data->m_MipImages->mipset->m_nMipLevels - 1; @@ -3479,7 +4021,8 @@ QTreeWidgetItem* ProjectView::Tree_Add3DModelImageFiles(QTreeWidgetItem* Parent CMIPS CMips; MipLevel* pInMipLevel = CMips.GetMipLevel(m_data->m_MipImages->mipset, 0, 0); - if (pInMipLevel) { + if (pInMipLevel) + { m_data->m_ImageSize = pInMipLevel->m_dwLinearSize; if (m_data->m_ImageSize > 1024000) @@ -3488,7 +4031,8 @@ QTreeWidgetItem* ProjectView::Tree_Add3DModelImageFiles(QTreeWidgetItem* Parent m_data->m_ImageSizeStr = QString().number((double)m_data->m_ImageSize / 1024, 'f', 1) + " KB"; else m_data->m_ImageSizeStr = QString().number(m_data->m_ImageSize) + " Bytes"; - } else + } + else m_data->m_ImageSizeStr = UNKNOWN_IMAGE; } } @@ -3504,12 +4048,13 @@ QTreeWidgetItem* ProjectView::Tree_Add3DModelImageFiles(QTreeWidgetItem* Parent } QTreeWidgetItem* ProjectView::Tree_Add3DModelMeshFile(QTreeWidgetItem* ParentItem, - QString filePathName, - QString pfilePathName, - bool checkable, - bool checked, - int levelType, - CMP_Feedback_Proc pFeedbackProc) { + QString filePathName, + QString pfilePathName, + bool checkable, + bool checked, + int levelType, + CMP_Feedback_Proc pFeedbackProc) +{ Q_UNUSED(checkable) Q_UNUSED(checked) Q_UNUSED(levelType) @@ -3525,7 +4070,8 @@ QTreeWidgetItem* ProjectView::Tree_Add3DModelMeshFile(QTreeWidgetItem* ParentIt QFile f(filePathName); // file not found! - if (!f.exists()) { + if (!f.exists()) + { return NULL; } @@ -3539,8 +4085,9 @@ QTreeWidgetItem* ProjectView::Tree_Add3DModelMeshFile(QTreeWidgetItem* ParentIt // Add icon file mesh file //--------------------------------------------- QPixmap filepixmap(filePathName); - if (filepixmap.size().height() == 0) { - filepixmap.load(":/CompressonatorGUI/Images/file.png"); + if (filepixmap.size().height() == 0) + { + filepixmap.load(":/compressonatorgui/images/file.png"); } QPixmap newPixmap = filepixmap.scaled(QSize(32, 32), Qt::IgnoreAspectRatio); @@ -3560,7 +4107,8 @@ QTreeWidgetItem* ProjectView::Tree_Add3DModelMeshFile(QTreeWidgetItem* ParentIt Create Data for this file and set current know values *********************************************************/ C_Mesh_Buffer_Info* m_data = new C_Mesh_Buffer_Info(); - if (m_data == NULL) { + if (m_data == NULL) + { return NULL; } @@ -3591,7 +4139,8 @@ void ProjectView::Tree_AddCompressFile(QTreeWidgetItem* ParentItem, bool checkable, bool checked, int levelType, - C_Destination_Options* m_data) { + C_Destination_Options* m_data) +{ if (ParentItem == NULL) return; @@ -3601,9 +4150,11 @@ void ProjectView::Tree_AddCompressFile(QTreeWidgetItem* ParentItem, treeItem->setText(0, description); treeItem->setData(TREE_LevelType, Qt::UserRole, QVariant::fromValue(levelType)); - switch (levelType) { + switch (levelType) + { case TREETYPE_Add_destination_setting: - case TREETYPE_Add_Model_destination_settings: { + case TREETYPE_Add_Model_destination_settings: + { // This item has gray color QFont font("", 9, QFont::Bold); QBrush b(Qt::gray); @@ -3611,19 +4162,23 @@ void ProjectView::Tree_AddCompressFile(QTreeWidgetItem* ParentItem, treeItem->setFont(0, font); ParentItem->addChild(treeItem); treeItem->setFlags(Qt::ItemIsEnabled); - treeItem->setIcon(0, QIcon(":/CompressonatorGUI/Images/plusSettings.png")); + treeItem->setIcon(0, QIcon(":/compressonatorgui/images/plussettings.png")); } break; - case TREETYPE_MESH_DATA: { - if (m_data) { - if (checkable && m_EnableCheckedItemsView) { + case TREETYPE_MESH_DATA: + { + if (m_data) + { + if (checkable && m_EnableCheckedItemsView) + { treeItem->setFlags(treeItem->flags() | Qt::ItemIsUserCheckable); if (checked) treeItem->setCheckState(0, Qt::Checked); else treeItem->setCheckState(0, Qt::Unchecked); - } else + } + else treeItem->setFlags(treeItem->flags() | Qt::ItemIsSelectable); // treeItem->setFlags(treeItem->flags() | Qt::ItemIsEditable); @@ -3636,36 +4191,45 @@ void ProjectView::Tree_AddCompressFile(QTreeWidgetItem* ParentItem, QTreeWidgetItem* parent = ParentItem->parent(); // if parent is null, return! - if (!parent) { + if (!parent) + { return; } - if (parent) { + if (parent) + { QVariant v = parent->data(TREE_LevelType, Qt::UserRole); int levelType = v.toInt(); m_data->m_SourceType = levelType; - if (levelType == TREETYPE_3DSUBMODEL_DATA) { + if (levelType == TREETYPE_3DSUBMODEL_DATA) + { QVariant v = parent->data(TREE_SourceInfo, Qt::UserRole); C_3DSubModel_Info* imagedata = v.value(); - if (imagedata) { + if (imagedata) + { m_data->m_OriginalMipImages = NULL; int index = 0; - while (index < imagedata->m_Model_Images.size()) { - if (m_data->m_sourceFileNamePath == imagedata->m_Model_Images[index].m_FilePathName) { + while (index < imagedata->m_Model_Images.size()) + { + if (m_data->m_sourceFileNamePath == imagedata->m_Model_Images[index].m_FilePathName) + { imagedata->m_SubModel_Images[index].m_srcDelFlag = true; break; } index++; } // use the image index to get more info on the source images sizes - if (index < imagedata->m_Model_Images.size()) { + if (index < imagedata->m_Model_Images.size()) + { m_data->m_DstWidth = imagedata->m_Model_Images[index].m_Width; m_data->m_DstHeight = imagedata->m_Model_Images[index].m_Height; m_data->m_HeightStr = QString().number(m_data->m_DstHeight) + " px"; m_data->m_WidthStr = QString().number(m_data->m_DstWidth) + " px"; m_data->m_SourceImageSize = imagedata->m_Model_Images[index].m_FileSize; - } else { + } + else + { m_data->m_DstWidth = 0; m_data->m_DstHeight = 0; m_data->m_HeightStr = ""; @@ -3675,9 +4239,11 @@ void ProjectView::Tree_AddCompressFile(QTreeWidgetItem* ParentItem, m_data->m_Model_Images = imagedata->m_Model_Images; m_data->setMeshData(imagedata->m_ModelData); parent->addChild(treeItem); - } else + } + else return; - } else + } + else return; // Some error occured as Mesh Data node should have a parent! } @@ -3690,15 +4256,19 @@ void ProjectView::Tree_AddCompressFile(QTreeWidgetItem* ParentItem, } } break; - case TREETYPE_COMPRESSION_DATA: { - if (m_data) { - if (checkable && m_EnableCheckedItemsView) { + case TREETYPE_COMPRESSION_DATA: + { + if (m_data) + { + if (checkable && m_EnableCheckedItemsView) + { treeItem->setFlags(treeItem->flags() | Qt::ItemIsUserCheckable); if (checked) treeItem->setCheckState(0, Qt::Checked); else treeItem->setCheckState(0, Qt::Unchecked); - } else + } + else treeItem->setFlags(treeItem->flags() | Qt::ItemIsSelectable); // treeItem->setFlags(treeItem->flags() | Qt::ItemIsEditable); @@ -3710,21 +4280,25 @@ void ProjectView::Tree_AddCompressFile(QTreeWidgetItem* ParentItem, QTreeWidgetItem* parent = ParentItem->parent(); // if parent is null, Node itself is parent - if (!parent) { + if (!parent) + { parent = ParentItem; } - if (parent) { + if (parent) + { parent->addChild(treeItem); QVariant v = parent->data(TREE_LevelType, Qt::UserRole); int levelType = v.toInt(); m_data->m_SourceType = levelType; - if (levelType == TREETYPE_IMAGEFILE_DATA) { + if (levelType == TREETYPE_IMAGEFILE_DATA) + { QVariant v = parent->data(TREE_SourceInfo, Qt::UserRole); C_Source_Info* imagedata = v.value(); - if (imagedata) { + if (imagedata) + { m_data->m_DstWidth = imagedata->m_Width; m_data->m_DstHeight = imagedata->m_Height; m_data->m_HeightStr = QString().number(m_data->m_DstHeight) + " px"; @@ -3732,12 +4306,16 @@ void ProjectView::Tree_AddCompressFile(QTreeWidgetItem* ParentItem, m_data->m_SourceIscompressedFormat = CMP_IsCompressedFormat(imagedata->m_Format); m_data->m_SourceIsFloatFormat = FloatFormat(imagedata->m_Format); m_data->m_OriginalMipImages = imagedata->m_MipImages; - } else + } + else return; - } else if (levelType == TREETYPE_3DMODEL_DATA) { // This case should not occur! + } + else if (levelType == TREETYPE_3DMODEL_DATA) + { // This case should not occur! QVariant v = parent->data(TREE_SourceInfo, Qt::UserRole); C_3DModel_Info* imagedata = v.value(); - if (imagedata) { + if (imagedata) + { m_data->m_DstWidth = 0; m_data->m_DstHeight = 0; m_data->m_HeightStr = ""; @@ -3745,29 +4323,38 @@ void ProjectView::Tree_AddCompressFile(QTreeWidgetItem* ParentItem, m_data->m_OriginalMipImages = NULL; m_data->m_modelSource = imagedata->m_Full_Path; m_data->m_Model_Images = imagedata->m_Model_Images; - } else + } + else return; - } else if (levelType == TREETYPE_3DSUBMODEL_DATA) { + } + else if (levelType == TREETYPE_3DSUBMODEL_DATA) + { QVariant v = parent->data(TREE_SourceInfo, Qt::UserRole); C_3DSubModel_Info* imagedata = v.value(); - if (imagedata) { + if (imagedata) + { m_data->m_OriginalMipImages = NULL; int index = 0; - while (index < imagedata->m_Model_Images.size()) { - if (m_data->m_sourceFileNamePath == imagedata->m_Model_Images[index].m_FilePathName) { + while (index < imagedata->m_Model_Images.size()) + { + if (m_data->m_sourceFileNamePath == imagedata->m_Model_Images[index].m_FilePathName) + { imagedata->m_SubModel_Images[index].m_srcDelFlag = true; break; } index++; } // use the image index to get more info on the source images sizes - if (index < imagedata->m_Model_Images.size()) { + if (index < imagedata->m_Model_Images.size()) + { m_data->m_DstWidth = imagedata->m_Model_Images[index].m_Width; m_data->m_DstHeight = imagedata->m_Model_Images[index].m_Height; m_data->m_HeightStr = QString().number(m_data->m_DstHeight) + " px"; m_data->m_WidthStr = QString().number(m_data->m_DstWidth) + " px"; m_data->m_SourceImageSize = imagedata->m_Model_Images[index].m_FileSize; - } else { + } + else + { m_data->m_DstWidth = 0; m_data->m_DstHeight = 0; m_data->m_HeightStr = ""; @@ -3775,7 +4362,8 @@ void ProjectView::Tree_AddCompressFile(QTreeWidgetItem* ParentItem, } m_data->m_Model_Images = imagedata->m_Model_Images; - } else + } + else return; } } @@ -3789,7 +4377,8 @@ void ProjectView::Tree_AddCompressFile(QTreeWidgetItem* ParentItem, } break; default: - if (treeItem) { + if (treeItem) + { delete treeItem; treeItem = NULL; } @@ -3797,7 +4386,8 @@ void ProjectView::Tree_AddCompressFile(QTreeWidgetItem* ParentItem, } } -bool ProjectView::Tree_updateCompressIcon(QTreeWidgetItem* item, QString FileNamePath, bool RedIcon) { +bool ProjectView::Tree_updateCompressIcon(QTreeWidgetItem* item, QString FileNamePath, bool RedIcon) +{ if (!item) return false; @@ -3807,7 +4397,8 @@ bool ProjectView::Tree_updateCompressIcon(QTreeWidgetItem* item, QString FileNam QFile file(FileNamePath); //for unprocessed obj setting node - if (FileNamePath.contains(".obj") || FileNamePath.contains(".OBJ")) { + if (FileNamePath.contains(".obj") || FileNamePath.contains(".OBJ")) + { #ifdef _WIN32 state = readObjFileState(FileNamePath.toStdString()); if (state == CMP_FILE_ERROR) @@ -3815,26 +4406,34 @@ bool ProjectView::Tree_updateCompressIcon(QTreeWidgetItem* item, QString FileNam #endif } - if (file.exists() && (fileinfo.suffix().length() > 0) && (state != CMP_COPY)) { - item->setIcon(0, QIcon(QStringLiteral(":/CompressonatorGUI/Images/smallGreenStone.png"))); + if (file.exists() && (fileinfo.suffix().length() > 0) && (state != CMP_COPY)) + { + item->setIcon(0, QIcon(QStringLiteral(":/compressonatorgui/images/smallgreenstone.png"))); result = true; - } else { + } + else + { if (RedIcon) - item->setIcon(0, QIcon(QStringLiteral(":/CompressonatorGUI/Images/smallRedStone.png"))); + item->setIcon(0, QIcon(QStringLiteral(":/compressonatorgui/images/smallredstone.png"))); else - item->setIcon(0, QIcon(QStringLiteral(":/CompressonatorGUI/Images/smallWhiteBlank.png"))); + item->setIcon(0, QIcon(QStringLiteral(":/compressonatorgui/images/smallwhiteblank.png"))); } return result; } -void ProjectView::Tree_selectAllChildItems(QTreeWidgetItem* item) { - if (item) { - if (item->childCount() > 0) { - for (int r = 0; r < item->childCount(); r++) { +void ProjectView::Tree_selectAllChildItems(QTreeWidgetItem* item) +{ + if (item) + { + if (item->childCount() > 0) + { + for (int r = 0; r < item->childCount(); r++) + { QTreeWidgetItem* child = item->child(r); child->setSelected(true); - for (int j = 0; j < child->childCount(); j++) { + for (int j = 0; j < child->childCount(); j++) + { QTreeWidgetItem* child2 = child->child(j); child2->setSelected(true); } @@ -3843,7 +4442,8 @@ void ProjectView::Tree_selectAllChildItems(QTreeWidgetItem* item) { } } -int ProjectView::Tree_numSelectedtems(int& ItemsCount) { +int ProjectView::Tree_numSelectedtems(int& ItemsCount) +{ ItemsCount = 0; int numSelected = 0; // Find all items and count the number selected Compression Formats @@ -3851,21 +4451,27 @@ int ProjectView::Tree_numSelectedtems(int& ItemsCount) { QVariant v; int levelType; - while (*it) { + while (*it) + { v = (*it)->data(TREE_LevelType, Qt::UserRole); levelType = v.toInt(); // exclude the add item count - if ((*it)->childCount() > 1) { - for (int r = 0; r < (*it)->childCount(); r++) { + if ((*it)->childCount() > 1) + { + for (int r = 0; r < (*it)->childCount(); r++) + { QTreeWidgetItem* child = (*it)->child(r); - if (child) { + if (child) + { v = child->data(TREE_LevelType, Qt::UserRole); levelType = v.toInt(); - if (child->isSelected()) { - if ((levelType == TREETYPE_COMPRESSION_DATA) || (levelType == TREETYPE_MESH_DATA)) { + if (child->isSelected()) + { + if ((levelType == TREETYPE_COMPRESSION_DATA) || (levelType == TREETYPE_MESH_DATA)) + { numSelected++; } } @@ -3879,7 +4485,8 @@ int ProjectView::Tree_numSelectedtems(int& ItemsCount) { return numSelected; } -int ProjectView::Tree_numCompresstemsSelected(int& ItemsCount, int& NumCompressedItems) { +int ProjectView::Tree_numCompresstemsSelected(int& ItemsCount, int& NumCompressedItems) +{ ItemsCount = 0; NumCompressedItems = 0; @@ -3889,21 +4496,26 @@ int ProjectView::Tree_numCompresstemsSelected(int& ItemsCount, int& NumCompresse QVariant v; int levelType; - while (*it) { + while (*it) + { v = (*it)->data(TREE_LevelType, Qt::UserRole); levelType = v.toInt(); // exclude the add item count - if ((*it)->childCount() > 1) { + if ((*it)->childCount() > 1) + { ItemsCount += (*it)->childCount() - 1; - for (int r = 0; r < (*it)->childCount(); r++) { + for (int r = 0; r < (*it)->childCount(); r++) + { QTreeWidgetItem* child = (*it)->child(r); - if (child) { + if (child) + { v = child->data(TREE_LevelType, Qt::UserRole); levelType = v.toInt(); - if (levelType == TREETYPE_COMPRESSION_DATA || levelType == TREETYPE_MESH_DATA) { + if (levelType == TREETYPE_COMPRESSION_DATA || levelType == TREETYPE_MESH_DATA) + { NumCompressedItems++; if (child->isSelected()) numSelected++; @@ -3917,11 +4529,14 @@ int ProjectView::Tree_numCompresstemsSelected(int& ItemsCount, int& NumCompresse return numSelected; } -void ProjectView::Tree_clearAllItemsSetected() { +void ProjectView::Tree_clearAllItemsSetected() +{ // Find the item and set it as selected QTreeWidgetItemIterator it(m_treeRootItem); - while (*it) { - if (m_EnableCheckedItemsView) { + while (*it) + { + if (m_EnableCheckedItemsView) + { (*it)->setCheckState(0, Qt::Unchecked); } (*it)->setSelected(false); @@ -3929,11 +4544,14 @@ void ProjectView::Tree_clearAllItemsSetected() { } } -void ProjectView::Tree_setAllItemsSetected() { +void ProjectView::Tree_setAllItemsSetected() +{ // Find the item and set it as selected QTreeWidgetItemIterator it(m_treeRootItem); - while (*it) { - if (m_EnableCheckedItemsView) { + while (*it) + { + if (m_EnableCheckedItemsView) + { (*it)->setCheckState(0, Qt::Checked); } (*it)->setSelected(true); @@ -3941,28 +4559,34 @@ void ProjectView::Tree_setAllItemsSetected() { } } -QTreeWidgetItem* ProjectView::Tree_FindImageItem(QString filePathName, bool includeDestination) { +QTreeWidgetItem* ProjectView::Tree_FindImageItem(QString filePathName, bool includeDestination) +{ // Make sure FileName does not contain // any specialized prefixes // Currently Differance us used for image compare // - if (filePathName.contains(DIFFERENCE_IMAGE_TXT)) { + if (filePathName.contains(DIFFERENCE_IMAGE_TXT)) + { filePathName.remove(0, sizeof(DIFFERENCE_IMAGE_TXT) - 1); } // Find the item and set it as selected QTreeWidgetItemIterator it(m_treeRootItem); - while (*it) { + while (*it) + { QString Source_FilePathName = GetSourceFileNamePath(*it); // Match any Source file - if (filePathName.compare(Source_FilePathName, Qt::CaseInsensitive) == 0) { + if (filePathName.compare(Source_FilePathName, Qt::CaseInsensitive) == 0) + { return (*it); } - if (includeDestination) { + if (includeDestination) + { QString Destination_FilePathName = GetDestinationFileNamePath(*it); // Match any Destination file - if (filePathName.compare(Destination_FilePathName, Qt::CaseInsensitive) == 0) { + if (filePathName.compare(Destination_FilePathName, Qt::CaseInsensitive) == 0) + { return (*it); } } @@ -3973,7 +4597,8 @@ QTreeWidgetItem* ProjectView::Tree_FindImageItem(QString filePathName, bool incl return NULL; } -void ProjectView::onTree_ItemClicked(QTreeWidgetItem* item, int column) { +void ProjectView::onTree_ItemClicked(QTreeWidgetItem* item, int column) +{ if (!item) return; if (m_processBusy) @@ -3991,24 +4616,30 @@ void ProjectView::onTree_ItemClicked(QTreeWidgetItem* item, int column) { m_curDiffSourceFile = ""; m_curDiffDestFile = ""; - if (islevelType == TREETYPE_Double_Click_here_to_add_files) { + if (islevelType == TREETYPE_Double_Click_here_to_add_files) + { // Clears of selected items when user clicks on this node Tree_clearAllItemsSetected(); SignalUpdateData(item, islevelType); - } else if (islevelType == TREETYPE_VIEWMESH_ONLY_NODE) { + } + else if (islevelType == TREETYPE_VIEWMESH_ONLY_NODE) + { m_CurrentCompressedImageItem = NULL; // view gltf bin QVariant v = item->data(TREE_SourceInfo, Qt::UserRole); C_Mesh_Buffer_Info* m_data = v.value(); - if (m_data && (column == 0) && (ViewImage)) { + if (m_data && (column == 0) && (ViewImage)) + { QString text = m_data->m_Full_Path; emit ViewImageFile(text, item); } // Update the bin poperty view for the item clicked SignalUpdateData(item, islevelType); - } else if ((islevelType == TREETYPE_IMAGEFILE_DATA) || (islevelType == TREETYPE_VIEWIMAGE_ONLY_NODE)) { + } + else if ((islevelType == TREETYPE_IMAGEFILE_DATA) || (islevelType == TREETYPE_VIEWIMAGE_ONLY_NODE)) + { m_CurrentCompressedImageItem = NULL; emit OnSourceImage(item->childCount()); @@ -4016,7 +4647,8 @@ void ProjectView::onTree_ItemClicked(QTreeWidgetItem* item, int column) { // view image QVariant v = item->data(TREE_SourceInfo, Qt::UserRole); C_Source_Info* m_data = v.value(); - if (m_data && (column == 0) && (ViewImage)) { + if (m_data && (column == 0) && (ViewImage)) + { QString text = m_data->m_Full_Path; emit ViewImageFile(text, item); } @@ -4025,17 +4657,21 @@ void ProjectView::onTree_ItemClicked(QTreeWidgetItem* item, int column) { // Update the image poperty view for the item clicked SignalUpdateData(item, islevelType); - } else if (islevelType == TREETYPE_COMPRESSION_DATA) { + } + else if (islevelType == TREETYPE_COMPRESSION_DATA) + { // view image QVariant v = item->data(TREE_SourceInfo, Qt::UserRole); C_Destination_Options* m_data = v.value(); // Since user has clicked on this item : prepair image diff file incase user want to do a diff // using the main apps tool bar - if (m_data) { + if (m_data) + { m_curDiffSourceFile = m_data->m_sourceFileNamePath; m_curDiffDestFile = m_data->m_destFileNamePath; } - if (m_data && (column == 0) && (ViewImage)) { + if (m_data && (column == 0) && (ViewImage)) + { QFileInfo fileinfo(m_data->m_destFileNamePath); QFile file(m_data->m_destFileNamePath); actViewImageDiff->setEnabled(file.exists()); @@ -4053,54 +4689,76 @@ void ProjectView::onTree_ItemClicked(QTreeWidgetItem* item, int column) { QFile SourcefileInfo(m_data->m_sourceFileNamePath); qint64 SourceImageSize = m_data->m_SourceImageSize; - if ((m_data->m_FileSize > 0) && (SourceImageSize > 0)) { + if ((m_data->m_FileSize > 0) && (SourceImageSize > 0)) + { double CompressionRatio = SourceImageSize / (double)m_data->m_FileSize; char buffer[128]; sprintf(buffer, "%2.2f", CompressionRatio); m_data->m_CompressionRatio = QString("%1 to 1").arg(buffer); } - if (file.exists() && (column == 0) && (m_clicked_onIcon)) { + if (file.exists() && (column == 0) && (m_clicked_onIcon)) + { m_CurrentCompressedImageItem = item; emit OnDecompressImage(); +#ifdef _WIN32 + Sleep(100); +#else + usleep(100000); +#endif emit ViewImageFile(m_data->m_destFileNamePath, item); } } // Update the compression data poperty view for the item clicked SignalUpdateData(item, islevelType); - } else if (islevelType == TREETYPE_3DMODEL_DATA) { + } + else if (islevelType == TREETYPE_3DMODEL_DATA) + { m_CurrentCompressedImageItem = NULL; - if (column == 2) { + if (column == 2) + { item->setExpanded(true); - if (item->text(2).compare("...") == 0) { + if (item->text(2).compare("...") == 0) + { item->setText(2, "(-)"); item->setToolTip(2, "Hide model image files"); - if (item->childCount() > 0) { - for (int r = 0; r < item->childCount(); r++) { + if (item->childCount() > 0) + { + for (int r = 0; r < item->childCount(); r++) + { QTreeWidgetItem* child = item->child(r); - if ((levelType(child) == TREETYPE_VIEWIMAGE_ONLY_NODE) || (levelType(child) == TREETYPE_VIEWMESH_ONLY_NODE)) { + if ((levelType(child) == TREETYPE_VIEWIMAGE_ONLY_NODE) || (levelType(child) == TREETYPE_VIEWMESH_ONLY_NODE)) + { child->setHidden(false); } } } - } else { + } + else + { item->setText(2, "..."); item->setToolTip(2, "Show model image files"); - if (item->childCount() > 0) { - for (int r = 0; r < item->childCount(); r++) { + if (item->childCount() > 0) + { + for (int r = 0; r < item->childCount(); r++) + { QTreeWidgetItem* child = item->child(r); - if ((levelType(child) == TREETYPE_VIEWIMAGE_ONLY_NODE) || (levelType(child) == TREETYPE_VIEWMESH_ONLY_NODE)) { + if ((levelType(child) == TREETYPE_VIEWIMAGE_ONLY_NODE) || (levelType(child) == TREETYPE_VIEWMESH_ONLY_NODE)) + { child->setHidden(true); } } } } - } else { + } + else + { m_CurrentCompressedImageItem = item; QVariant v = item->data(TREE_SourceInfo, Qt::UserRole); C_3DModel_Info* m_data = v.value(); - if (m_data && (column == 0) && (ViewImage)) { + if (m_data && (column == 0) && (ViewImage)) + { QString text = m_data->m_Full_Path; emit ViewImageFile(text, item); } @@ -4108,17 +4766,22 @@ void ProjectView::onTree_ItemClicked(QTreeWidgetItem* item, int column) { // Update the image poperty view for the item clicked SignalUpdateData(item, islevelType); } - } else if (islevelType == TREETYPE_3DSUBMODEL_DATA) { + } + else if (islevelType == TREETYPE_3DSUBMODEL_DATA) + { m_CurrentCompressedImageItem = item; QVariant v = item->data(TREE_SourceInfo, Qt::UserRole); C_3DSubModel_Info* m_data = v.value(); - if (m_data && (column == 0) && (ViewImage)) { + if (m_data && (column == 0) && (ViewImage)) + { QString text = m_data->m_Full_Path; emit ViewImageFile(text, item); } // Update the image poperty view for the item clicked SignalUpdateData(item, islevelType); - } else if (islevelType == TREETYPE_MESH_DATA) { + } + else if (islevelType == TREETYPE_MESH_DATA) + { m_CurrentCompressedImageItem = item; // view image @@ -4126,10 +4789,12 @@ void ProjectView::onTree_ItemClicked(QTreeWidgetItem* item, int column) { C_Destination_Options* m_data = v.value(); // Since user has clicked on this item : prepair image diff file incase user want to do a diff // using the main apps tool bar - if (m_data) { + if (m_data) + { m_curDiffSourceFile = m_data->m_sourceFileNamePath; m_curDiffDestFile = m_data->m_destFileNamePath; - if (column == 0) { + if (column == 0) + { QFileInfo fileinfo(m_data->m_destFileNamePath); QFile file(m_data->m_destFileNamePath); actViewImageDiff->setEnabled(file.exists()); @@ -4147,19 +4812,24 @@ void ProjectView::onTree_ItemClicked(QTreeWidgetItem* item, int column) { QFile SourcefileInfo(m_data->m_sourceFileNamePath); qint64 SourceImageSize = m_data->m_SourceImageSize; - if ((m_data->m_FileSize > 0) && (SourceImageSize > 0)) { + if ((m_data->m_FileSize > 0) && (SourceImageSize > 0)) + { double CompressionRatio = SourceImageSize / (double)m_data->m_FileSize; char buffer[128]; sprintf(buffer, "%2.2f", CompressionRatio); m_data->m_CompressionRatio = QString("%1 to 1").arg(buffer); } - if ((column == 0) && (m_clicked_onIcon)) { + if ((column == 0) && (m_clicked_onIcon)) + { QFile compfile(m_data->m_destFileNamePath + ".drc"); - if ((m_data->getDo_Mesh_Compression() == m_data->Draco) && (compfile.exists())) { + if ((m_data->getDo_Mesh_Compression() == m_data->Draco) && (compfile.exists())) + { QString fileName = compfile.fileName(); emit ViewImageFile(fileName, item); - } else if (file.exists()) { + } + else if (file.exists()) + { emit ViewImageFile(m_data->m_destFileNamePath, item); } } @@ -4168,54 +4838,66 @@ void ProjectView::onTree_ItemClicked(QTreeWidgetItem* item, int column) { // Update the image poperty view for the item clicked SignalUpdateData(item, islevelType); - } else { + } + else + { m_CurrentCompressedImageItem = NULL; emit UpdateData(NULL); } } -void ProjectView::onTree_ItemDoubleClicked(QTreeWidgetItem* item, int column) { +void ProjectView::onTree_ItemDoubleClicked(QTreeWidgetItem* item, int column) +{ if (!item) return; Q_UNUSED(column); QVariant v = item->data(TREE_LevelType, Qt::UserRole); - switch (v.toInt()) { - case TREETYPE_Double_Click_here_to_add_files: { // [+] Double Click here to add files + switch (v.toInt()) + { + case TREETYPE_Double_Click_here_to_add_files: + { // [+] Double Click here to add files // Clears of selected items when user clicks on this node Tree_clearAllItemsSetected(); // Add new file // then Update the poperty view for the item clicked - if (OpenImageFile()) { + if (OpenImageFile()) + { SignalUpdateData(item, TREETYPE_IMAGEFILE_DATA); } } break; - case TREETYPE_Add_destination_setting: // [+] Add Destination Setting - case TREETYPE_Add_Model_destination_settings: { // STR_AddDestinationSetting + case TREETYPE_Add_destination_setting: // [+] Add Destination Setting + case TREETYPE_Add_Model_destination_settings: + { // STR_AddDestinationSetting emit AddCompressSettings(item); } break; } // switch case } -void ProjectView::onTreeMousePress(QMouseEvent* event, bool onIcon) { +void ProjectView::onTreeMousePress(QMouseEvent* event, bool onIcon) +{ m_CurrentItem = m_projectTreeView->m_currentItem; m_clicked_onIcon = onIcon; - if (m_CurrentItem) { + if (m_CurrentItem) + { //bool SHIFT_Key = event->modifiers() & Qt::ShiftModifier; bool CTRL_key = event->modifiers() & Qt::ControlModifier; //bool MouseLeft = event->button() & Qt::LeftButton; - if (CTRL_key) { + if (CTRL_key) + { bool selected = m_CurrentItem->isSelected(); int childCount = m_CurrentItem->childCount(); - if (childCount > 1) { + if (childCount > 1) + { QTreeWidgetItem* childItem; // Vaild Index start from 0 to childCount - while (childCount > 1) { + while (childCount > 1) + { childItem = m_CurrentItem->child(childCount - 1); if (childItem) childItem->setSelected(selected); @@ -4226,12 +4908,14 @@ void ProjectView::onTreeMousePress(QMouseEvent* event, bool onIcon) { } } -void ProjectView::onTreeKeyPress(QKeyEvent* event) { +void ProjectView::onTreeKeyPress(QKeyEvent* event) +{ Q_UNUSED(event); m_CurrentItem = m_projectTreeView->m_currentItem; } -void ProjectView::onImageLoadStart() { +void ProjectView::onImageLoadStart() +{ if (actAnalyseMeshData) actAnalyseMeshData->setEnabled(false); if (actCompressProjectFiles) @@ -4240,7 +4924,8 @@ void ProjectView::onImageLoadStart() { actRemoveImage->setEnabled(false); } -void ProjectView::onImageLoadDone() { +void ProjectView::onImageLoadDone() +{ if (actAnalyseMeshData) actAnalyseMeshData->setEnabled(true); if (actCompressProjectFiles) @@ -4249,11 +4934,13 @@ void ProjectView::onImageLoadDone() { actRemoveImage->setEnabled(true); } -void ProjectView::onSetCurrentItem(QString& FilePathName) { +void ProjectView::onSetCurrentItem(QString& FilePathName) +{ SelectImageItem(FilePathName); } -void ProjectView::onCustomContextMenu(const QPoint& point) { +void ProjectView::onCustomContextMenu(const QPoint& point) +{ QModelIndex index = m_projectTreeView->indexAt(point); // Show or Hide thes Context menu items based on location in the Project View @@ -4270,13 +4957,15 @@ void ProjectView::onCustomContextMenu(const QPoint& point) { actRemoveImage->setVisible(false); ContextMenu_ImageItem = NULL; - if (index.isValid()) { + if (index.isValid()) + { QString text; text = "Process "; // Get the item user right clicked on QTreeWidgetItem* item = m_projectTreeView->itemAt(point); - if (item) { + if (item) + { actViewImageDiff->setEnabled(false); actView3DModelDiff->setEnabled(false); @@ -4292,7 +4981,8 @@ void ProjectView::onCustomContextMenu(const QPoint& point) { else actCompressProjectFiles->setVisible(true); - if (numSelected > 1) { + if (numSelected > 1) + { text.append(QString::number(numSelected)); text.append(" selected image(s)"); actCompressProjectFiles->setText(text); @@ -4300,18 +4990,23 @@ void ProjectView::onCustomContextMenu(const QPoint& point) { QVariant v = item->data(TREE_LevelType, Qt::UserRole); int levelType = v.toInt(); - switch (levelType) { + switch (levelType) + { case TREETYPE_Double_Click_here_to_add_files: // [+] Add Image item case TREETYPE_Add_destination_setting: // [+] Add destination setting - case TREETYPE_Add_Model_destination_settings: { // [+] Add glTF destination settings - if (numSelected > 1) { + case TREETYPE_Add_Model_destination_settings: + { // [+] Add glTF destination settings + if (numSelected > 1) + { actCompressProjectFiles->setText("Process selected images"); - } else + } + else actCompressProjectFiles->setVisible(false); break; } - case TREETYPE_3DMODEL_DATA: { + case TREETYPE_3DMODEL_DATA: + { #ifdef USE_CONTEXT_PROJECT actsaveProjectFile->setVisible(true); actopenProjectFile->setVisible(true); @@ -4323,8 +5018,10 @@ void ProjectView::onCustomContextMenu(const QPoint& point) { QVariant fv = item->data(TREE_SourceInfo, Qt::UserRole); C_3DModel_Info* m_data = fv.value(); - if ((NumCompressItems > 0) && (item->childCount() > 1)) { - if (m_data && (numSelected < (item->childCount() - 1))) { + if ((NumCompressItems > 0) && (item->childCount() > 1)) + { + if (m_data && (numSelected < (item->childCount() - 1))) + { text = "Process all setting for "; text.append(m_data->m_Name); actCompressProjectFiles->setText(text); @@ -4334,7 +5031,8 @@ void ProjectView::onCustomContextMenu(const QPoint& point) { } break; } - case TREETYPE_3DSUBMODEL_DATA: { + case TREETYPE_3DSUBMODEL_DATA: + { #ifdef USE_CONTEXT_PROJECT actsaveProjectFile->setVisible(true); actopenProjectFile->setVisible(true); @@ -4345,23 +5043,29 @@ void ProjectView::onCustomContextMenu(const QPoint& point) { QVariant fv = item->data(TREE_SourceInfo, Qt::UserRole); C_3DSubModel_Info* m_data = fv.value(); - if (m_data) { + if (m_data) + { QFileInfo fileinfo(m_data->m_Full_Path); QFile file(m_data->m_Full_Path); bool fileexist = file.exists(); if ((m_data->ModelType == eModelType::GLTF) && - (g_Application_Options.m_GLTFRenderWith != C_Application_Options::RenderModelsWith::glTF_Vulkan)) { + (g_Application_Options.m_GLTFRenderWith != C_Application_Options::RenderModelsWith::glTF_Vulkan)) + { actView3DModelDiff->setVisible(fileexist); actView3DModelDiff->setEnabled(fileexist); - } else { + } + else + { actView3DModelDiff->setVisible(false); } actAnalyseMeshData->setVisible(fileexist); m_CurrentCompressedImageItem = item; } - if ((NumCompressItems > 0) && (item->childCount() > 1)) { - if (m_data && (numSelected < (item->childCount() - 1))) { + if ((NumCompressItems > 0) && (item->childCount() > 1)) + { + if (m_data && (numSelected < (item->childCount() - 1))) + { text = "Process all setting for "; text.append(m_data->m_Name); actCompressProjectFiles->setText(text); @@ -4371,7 +5075,8 @@ void ProjectView::onCustomContextMenu(const QPoint& point) { } break; } - case TREETYPE_IMAGEFILE_DATA: { // Original Image item + case TREETYPE_IMAGEFILE_DATA: + { // Original Image item #ifdef USE_CONTEXT_PROJECT actsaveProjectFile->setVisible(true); actopenProjectFile->setVisible(true); @@ -4382,8 +5087,10 @@ void ProjectView::onCustomContextMenu(const QPoint& point) { QVariant fv = item->data(TREE_SourceInfo, Qt::UserRole); C_Source_Info* m_data = fv.value(); - if ((NumCompressItems > 0) && (item->childCount() > 1)) { - if (m_data && (numSelected < (item->childCount() - 1))) { + if ((NumCompressItems > 0) && (item->childCount() > 1)) + { + if (m_data && (numSelected < (item->childCount() - 1))) + { text = "Process all setting for "; text.append(m_data->m_Name); actCompressProjectFiles->setText(text); @@ -4394,17 +5101,21 @@ void ProjectView::onCustomContextMenu(const QPoint& point) { break; } case TREETYPE_MESH_DATA: - case TREETYPE_COMPRESSION_DATA: { // Compress Image item - if (NumCompressItems > 0) { + case TREETYPE_COMPRESSION_DATA: + { // Compress Image item + if (NumCompressItems > 0) + { actOpenContainingFolder->setVisible(true); actCopyFullPath->setVisible(true); m_CurrentCompressedImageItem = item; ContextMenu_ImageItem = item; QVariant cv = item->data(TREE_SourceInfo, Qt::UserRole); C_Destination_Options* m_data = cv.value(); - if (m_data) { + if (m_data) + { // Enable 2D image diff for images - if (levelType == TREETYPE_COMPRESSION_DATA) { + if (levelType == TREETYPE_COMPRESSION_DATA) + { QFileInfo fileinfo(m_data->m_destFileNamePath); QFile file(m_data->m_destFileNamePath); bool fileexist = file.exists(); @@ -4413,7 +5124,8 @@ void ProjectView::onCustomContextMenu(const QPoint& point) { actViewImageDiff->setEnabled(fileexist); } } - if (m_data) { + if (m_data) + { text.append(m_data->m_compname); actCompressProjectFiles->setText(text); } @@ -4430,15 +5142,19 @@ void ProjectView::onCustomContextMenu(const QPoint& point) { //===================================================== // This gets call many times (for all qDebug messages and printf's) -void ProjectView::OnGlobalMessage(const char* msg) { - if (m_CompressStatusDialog && g_bCompressing) { +void ProjectView::OnGlobalMessage(const char* msg) +{ + if (m_CompressStatusDialog && g_bCompressing) + { m_CompressStatusDialog->appendText(msg); } } -void ProjectView::onShowCompressStatus() { +void ProjectView::onShowCompressStatus() +{ m_processFromContext = true; - if (m_CompressStatusDialog) { + if (m_CompressStatusDialog) + { g_bAbortCompression = false; m_CompressStatusDialog->onClearText(); m_CompressStatusDialog->showOutput(); @@ -4446,29 +5162,36 @@ void ProjectView::onShowCompressStatus() { } } -void ProjectView::OnCloseCompression() { - if (m_CompressStatusDialog) { +void ProjectView::OnCloseCompression() +{ + if (m_CompressStatusDialog) + { g_bAbortCompression = true; m_CompressStatusDialog->hideOutput(); } } -void ProjectView::onEntered(const QModelIndex& index) { +void ProjectView::onEntered(const QModelIndex& index) +{ Q_UNUSED(index); } -void ProjectView::onDroppedImageItem(QString& filePathName, int index) { +void ProjectView::onDroppedImageItem(QString& filePathName, int index) +{ C_Source_Info* m_dataout = NULL; Tree_AddImageFile(filePathName, index, &m_dataout); m_saveProjectChanges = true; } -void ProjectView::onGlobalPropertyChanged(int& setting) { +void ProjectView::onGlobalPropertyChanged(int& setting) +{ QTreeWidgetItem* item = this->m_CurrentItem; - if (item) { + if (item) + { QVariant v = item->data(TREE_LevelType, Qt::UserRole); int levelType = v.toInt(); - if (levelType == TREETYPE_Double_Click_here_to_add_files) { + if (levelType == TREETYPE_Double_Click_here_to_add_files) + { QColor ColorEnabled(128, 128, 0); // Light Yellow QColor ColorDisabled(255, 255, 255); // White if (setting > 0) @@ -4494,7 +5217,8 @@ void ProjectView::onGlobalPropertyChanged(int& setting) { // TREETYPE_COMPRESSION_DATA /*=====================================================*/ -struct TAnalysisData { +struct TAnalysisData +{ int processCount; double processTime; double SSIM_Total; @@ -4510,7 +5234,8 @@ bool processItem(QFile* file, int& NumberOfItemCompressedFailed, int& NumberOfItemsSkipped, TAnalysisData& m_AnalaysisData, - QTreeWidgetItemIterator it) { + QTreeWidgetItemIterator it) +{ // Use STD vectors to hold argv ** and keep the data in scope typedef std::vector CharArray; typedef std::vector ArgumentVector; @@ -4518,12 +5243,13 @@ bool processItem(QFile* file, std::vector argv; C_Destination_Options setDefaultOptions; - if (*it) { + if (*it) + { argvVec.clear(); argv.clear(); // Push App name string - std::string app = "CompressonatorCLI.exe"; + std::string app = "compressonatorcli.exe"; argvVec.push_back(CharArray(app.begin(), app.end())); argvVec.back().push_back(0); // Terminate String argv.push_back(argvVec.back().data()); @@ -4540,27 +5266,33 @@ bool processItem(QFile* file, QVariant v = (*it)->data(TREE_LevelType, Qt::UserRole); int sublevelType = v.toInt(); // save the settings item - if (sublevelType == TREETYPE_COMPRESSION_DATA) { + if (sublevelType == TREETYPE_COMPRESSION_DATA) + { v = Imageitem->data(TREE_SourceInfo, Qt::UserRole); C_Destination_Options* data = v.value(); bool testitem = false; //Compression setting and item is checked - if (ProjectView->m_EnableCheckedItemsView) { + if (ProjectView->m_EnableCheckedItemsView) + { //qDebug() << "override checked item using m_EnableCheckedItemsView"; testitem = (Imageitem->checkState(0) == Qt::Checked); - } else { + } + else + { //qDebug() << "override checked item is selected"; testitem = Imageitem->isSelected(); } - if (data) { + if (data) + { if (!testitem) testitem = data->m_isselected; } //qDebug() << "testitem " << testitem; - if (data && testitem) { + if (data && testitem) + { //qDebug() << " valid Data"; // Reset force compression use flag data->m_isselected = false; @@ -4583,7 +5315,8 @@ bool processItem(QFile* file, QFileInfo fileInfo(data->m_destFileNamePath); QDir dir(fileInfo.absoluteDir()); QString DestPath = dir.absolutePath(); - if (!dir.exists()) { + if (!dir.exists()) + { dir.mkpath("."); } @@ -4593,16 +5326,20 @@ bool processItem(QFile* file, g_pProgressDlg->SetLabelText(msgCommandLine); g_pProgressDlg->SetValue(0); - if (ProjectView->m_CompressStatusDialog) { + if (ProjectView->m_CompressStatusDialog) + { ProjectView->m_CompressStatusDialog->appendText(msgCommandLine); } // Saving paramaters to Batch file - if (file != NULL) { + if (file != NULL) + { msgCommandLine = ""; - } else { + } + else + { // Compressing - msgCommandLine = "CompressonatorCLI.exe "; + msgCommandLine = "compressonatorcli.exe "; } // Check that the paths dont contain spaces else we need to add quotes @@ -4635,7 +5372,8 @@ bool processItem(QFile* file, CMP_FORMAT cmp_format = CMP_ParseFormat((char*)key); //"fd" = key - if (key != NULL) { + if (key != NULL) + { std::string format = "-fd"; argvVec.push_back(CharArray(format.begin(), format.end())); argvVec.back().push_back(0); // Terminate String @@ -4649,12 +5387,15 @@ bool processItem(QFile* file, argvVec.push_back(CharArray(formatValue.begin(), formatValue.end())); argvVec.back().push_back(0); // Terminate String argv.push_back(argvVec.back().data()); - } else { + } + else + { // do some check! } ////using GPU to compress - if ((!g_useCPUEncode) && (key)) { + if ((!g_useCPUEncode) && (key)) + { std::string format = key; // if ( // #ifdef USE_GTC @@ -4681,19 +5422,24 @@ bool processItem(QFile* file, argvVec.push_back(CharArray(usegpu.begin(), usegpu.end())); argvVec.back().push_back(0); // Terminate String argv.push_back(argvVec.back().data()); - if (g_Application_Options.m_ImageEncode == C_Application_Options::ImageEncodeWith::HPC) { + if (g_Application_Options.m_ImageEncode == C_Application_Options::ImageEncodeWith::HPC) + { msgCommandLine.append(" HPC "); usegpu = "HPC"; argvVec.push_back(CharArray(usegpu.begin(), usegpu.end())); argvVec.back().push_back(0); // Terminate String argv.push_back(argvVec.back().data()); - } else if (g_Application_Options.m_ImageEncode == C_Application_Options::ImageEncodeWith::GPU_OpenCL) { + } + else if (g_Application_Options.m_ImageEncode == C_Application_Options::ImageEncodeWith::GPU_OpenCL) + { msgCommandLine.append(" OCL "); usegpu = "OCL"; argvVec.push_back(CharArray(usegpu.begin(), usegpu.end())); argvVec.back().push_back(0); // Terminate String argv.push_back(argvVec.back().data()); - } else if (g_Application_Options.m_ImageEncode == C_Application_Options::ImageEncodeWith::GPU_HW) { + } + else if (g_Application_Options.m_ImageEncode == C_Application_Options::ImageEncodeWith::GPU_HW) + { msgCommandLine.append(" GPU "); usegpu = "GPU"; argvVec.push_back(CharArray(usegpu.begin(), usegpu.end())); @@ -4701,7 +5447,8 @@ bool processItem(QFile* file, argv.push_back(argvVec.back().data()); } #ifdef USE_GPU_PIPELINE_VULKAN - else if (g_Application_Options.m_ImageEncode == C_Application_Options::ImageEncodeWith::VLK) { + else if (g_Application_Options.m_ImageEncode == C_Application_Options::ImageEncodeWith::VLK) + { msgCommandLine.append(" VLK "); usegpu = "VLK"; argvVec.push_back(CharArray(usegpu.begin(), usegpu.end())); @@ -4709,7 +5456,8 @@ bool processItem(QFile* file, argv.push_back(argvVec.back().data()); } #endif - else if (g_Application_Options.m_ImageEncode == C_Application_Options::ImageEncodeWith::GPU_DirectX) { + else if (g_Application_Options.m_ImageEncode == C_Application_Options::ImageEncodeWith::GPU_DirectX) + { msgCommandLine.append(" DXC "); usegpu = "DXC"; argvVec.push_back(CharArray(usegpu.begin(), usegpu.end())); @@ -4726,7 +5474,8 @@ bool processItem(QFile* file, //===================================================== // User set generate MipMap using GPU HW //===================================================== - if (g_Application_Options.m_useGPUMipMaps && (g_Application_Options.m_ImageEncode == C_Application_Options::ImageEncodeWith::GPU_HW)) { + if (g_Application_Options.m_useGPUMipMaps && (g_Application_Options.m_ImageEncode == C_Application_Options::ImageEncodeWith::GPU_HW)) + { std::string usepram; msgCommandLine.append(" -GenGPUMipMaps "); usepram = "-GenGPUMipMaps"; @@ -4734,7 +5483,8 @@ bool processItem(QFile* file, argvVec.back().push_back(0); // Terminate String argv.push_back(argvVec.back().data()); } - if (g_Application_Options.m_useSRGBFrames && (g_Application_Options.m_ImageEncode == C_Application_Options::ImageEncodeWith::GPU_HW)) { + if (g_Application_Options.m_useSRGBFrames && (g_Application_Options.m_ImageEncode == C_Application_Options::ImageEncodeWith::GPU_HW)) + { std::string usepram; msgCommandLine.append(" -UseSRGBFrames "); usepram = "-UseSRGBFrames"; @@ -4744,9 +5494,10 @@ bool processItem(QFile* file, } // MipLevels - if (miplevels > 1) { + if (miplevels > 1) + { msgCommandLine.append(" -miplevels "); - msgCommandLine.append(QString::number(miplevels - 1)); + msgCommandLine.append(QString::number(miplevels)); msgCommandLine.append(" "); std::string smiplevel = "-miplevels"; @@ -4754,7 +5505,7 @@ bool processItem(QFile* file, argvVec.back().push_back(0); // Terminate String argv.push_back(argvVec.back().data()); - smiplevel = std::to_string(miplevels - 1); + smiplevel = std::to_string(miplevels); argvVec.push_back(CharArray(smiplevel.begin(), smiplevel.end())); argvVec.back().push_back(0); // Terminate String argv.push_back(argvVec.back().data()); @@ -4763,8 +5514,10 @@ bool processItem(QFile* file, //============================= // Quality Settings //============================= - if (FormatSupportsQualitySetting(cmp_format)) { - if ((data->m_Quality != setDefaultOptions.m_Quality) || (ProjectView->m_globalProcessSetting.m_Quality > 0)) { + if (FormatSupportsQualitySetting(cmp_format)) + { + if ((data->m_Quality != setDefaultOptions.m_Quality) || (ProjectView->m_globalProcessSetting.m_Quality > 0)) + { // Override the setting if (ProjectView->m_globalProcessSetting.m_Quality > 0) fqualty_setting = ProjectView->m_globalProcessSetting.m_Quality; @@ -4793,7 +5546,8 @@ bool processItem(QFile* file, //===================================================== // User set Number of threads that is not default 8 //===================================================== - if (g_Application_Options.m_threads != 0) { + if (g_Application_Options.m_threads != 0) + { // Display Msg to user on the process message pannel QString value = QString::number(g_Application_Options.m_threads); msgCommandLine.append(" -NumThreads "); @@ -4801,7 +5555,8 @@ bool processItem(QFile* file, msgCommandLine.append(" "); } - if (FormatSupportsDXTCBase(cmp_format)) { + if (FormatSupportsDXTCBase(cmp_format)) + { //============================= // Channel Weighting //============================= @@ -4812,7 +5567,8 @@ bool processItem(QFile* file, int CHBlue = ceil(data->Z_BLUE * 100); int DefCHBlue = ceil(setDefaultOptions.Z_BLUE * 100); - if ((!useWeightChannel) && ((CHRed != DefCHRed) || (CHGreen != DefCHGreen) || (CHBlue != DefCHBlue))) { + if ((!useWeightChannel) && ((CHRed != DefCHRed) || (CHGreen != DefCHGreen) || (CHBlue != DefCHBlue))) + { msgCommandLine.append(" -UseChannelWeighting 1 "); useWeightChannel = true; @@ -4828,7 +5584,8 @@ bool processItem(QFile* file, argv.push_back(argvVec.back().data()); } - if (CHRed != DefCHRed) { + if (CHRed != DefCHRed) + { // User Msg QString value = QString::number(data->X_RED, 'f', 4); @@ -4849,7 +5606,8 @@ bool processItem(QFile* file, argv.push_back(argvVec.back().data()); } - if (CHGreen != DefCHGreen) { + if (CHGreen != DefCHGreen) + { // User Msg QString value = QString::number(data->Y_GREEN, 'f', 4); @@ -4870,7 +5628,8 @@ bool processItem(QFile* file, argv.push_back(argvVec.back().data()); } - if (CHBlue != DefCHBlue) { + if (CHBlue != DefCHBlue) + { // User Msg QString value = QString::number(data->Z_BLUE, 'f', 4); @@ -4895,11 +5654,14 @@ bool processItem(QFile* file, // ==================================== // DXTC1 settings only // ==================================== - if (cmp_format == CMP_FORMAT_DXT1) { - if (data->Threshold != setDefaultOptions.Threshold) { + if (cmp_format == CMP_FORMAT_DXT1) + { + if (data->Threshold != setDefaultOptions.Threshold) + { // User Msg QString value = QString::number(data->Threshold); - if (!useAlphaChannel) { + if (!useAlphaChannel) + { msgCommandLine.append(" -DXT1UseAlpha 1 "); useAlphaChannel = true; @@ -4941,13 +5703,15 @@ bool processItem(QFile* file, // ==================================== if ((cmp_format == CMP_FORMAT_ASTC) #ifdef USE_GTC - || (cmp_format == CMP_FORMAT_GTC) + || (cmp_format == CMP_FORMAT_GTC) #endif #ifdef USE_APC - || (cmp_format == CMP_FORMAT_APC) + || (cmp_format == CMP_FORMAT_APC) #endif - ) { - if (data->m_Bitrate != setDefaultOptions.m_Bitrate) { + ) + { + if (data->m_Bitrate != setDefaultOptions.m_Bitrate) + { // User Msg //QString value = data->m_correctBitrate; QString value = data->m_Bitrate; @@ -4972,8 +5736,10 @@ bool processItem(QFile* file, // ========================================== // Input HDR Settings for Float->Byte process // ========================================== - if (data->m_SourceIsFloatFormat && !(FloatFormat(cmp_format))) { - if (data->m_Defog != setDefaultOptions.m_Defog) { + if (data->m_SourceIsFloatFormat && !(FloatFormat(cmp_format))) + { + if (data->m_Defog != setDefaultOptions.m_Defog) + { // User Msg QString value = QString::number(data->m_Defog, 'f', 4); msgCommandLine.append(" -InDefog "); @@ -4993,7 +5759,8 @@ bool processItem(QFile* file, argv.push_back(argvVec.back().data()); } - if (data->m_Exposure != setDefaultOptions.m_Exposure) { + if (data->m_Exposure != setDefaultOptions.m_Exposure) + { // User Msg QString value = QString::number(data->m_Exposure, 'f', 4); msgCommandLine.append(" -InExposure "); @@ -5013,7 +5780,8 @@ bool processItem(QFile* file, argv.push_back(argvVec.back().data()); } - if (data->m_KneeLow != setDefaultOptions.m_KneeLow) { + if (data->m_KneeLow != setDefaultOptions.m_KneeLow) + { // User Msg QString value = QString::number(data->m_KneeLow, 'f', 4); msgCommandLine.append(" -InKneeLow "); @@ -5033,7 +5801,8 @@ bool processItem(QFile* file, argv.push_back(argvVec.back().data()); } - if (data->m_KneeHigh != setDefaultOptions.m_KneeHigh) { + if (data->m_KneeHigh != setDefaultOptions.m_KneeHigh) + { // User Msg QString value = QString::number(data->m_KneeHigh, 'f', 4); msgCommandLine.append(" -InKneeHigh "); @@ -5053,7 +5822,8 @@ bool processItem(QFile* file, argv.push_back(argvVec.back().data()); } - if (data->m_Gamma != setDefaultOptions.m_Gamma) { + if (data->m_Gamma != setDefaultOptions.m_Gamma) + { // User Msg QString value = QString::number(data->m_Gamma, 'f', 4); msgCommandLine.append(" -Gamma "); @@ -5077,17 +5847,22 @@ bool processItem(QFile* file, //=========================== // Exporting to Batch file //=========================== - if (file != NULL) { - if (msgCommandLine.length() > 0) { - file->write("CompressonatorCLI.exe "); + if (file != NULL) + { + if (msgCommandLine.length() > 0) + { + file->write("compressonatorcli.exe "); file->write(msgCommandLine.toLatin1()); file->write("\n"); } - if (ProjectView->m_CompressStatusDialog) { + if (ProjectView->m_CompressStatusDialog) + { ProjectView->m_CompressStatusDialog->appendText(" Done"); } - } else { + } + else + { //====================== // Compression Data //====================== @@ -5100,7 +5875,8 @@ bool processItem(QFile* file, emit ProjectView->OnProcessing(data->m_destFileNamePath); // Pass over the command line params - if (ParseParams((int)argv.size(), (CMP_CHAR**)argv.data())) { + if (ParseParams((int)argv.size(), (CMP_CHAR**)argv.data())) + { // Overriding Some Command Line Features for GUI app! g_CmdPrams.showperformance = true; g_CmdPrams.conversion_fDuration = 0; @@ -5109,26 +5885,34 @@ bool processItem(QFile* file, g_CmdPrams.CompressOptions.getPerfStats = true; g_CmdPrams.CompressOptions.getDeviceInfo = true; - if (g_Application_Options.m_logresults) { + if (g_Application_Options.m_logresults) + { g_CmdPrams.logresults = true; g_CmdPrams.logresultsToFile = false; g_CmdPrams.SSIM = 0; - } else { + } + else + { g_CmdPrams.logresults = false; g_CmdPrams.logresultsToFile = false; } // Do the Compression by loading a new MIP set - if (ProcessCMDLine(&ProgressCallback, sourceImageMipSet) == 0) { - if (g_bAbortCompression) { - Imageitem->setIcon(0, QIcon(QStringLiteral(":/CompressonatorGUI/Images/smallGrayStone.png"))); + if (ProcessCMDLine(&ProgressCallback, sourceImageMipSet) == 0) + { + if (g_bAbortCompression) + { + Imageitem->setIcon(0, QIcon(QStringLiteral(":/compressonatorgui/images/smallgraystone.png"))); g_pProgressDlg->SetValue(0); argvVec.clear(); argv.clear(); return false; - } else { + } + else + { // Success in compression - if (ProjectView->Tree_updateCompressIcon(Imageitem, data->m_destFileNamePath, true)) { + if (ProjectView->Tree_updateCompressIcon(Imageitem, data->m_destFileNamePath, true)) + { // Destination File Size QFile fileInfo(data->m_destFileNamePath); data->m_FileSize = fileInfo.size(); @@ -5139,7 +5923,8 @@ bool processItem(QFile* file, else data->m_FileSizeStr = QString().number(data->m_FileSize) + " Bytes"; // Add Compressoin Time - if (g_CmdPrams.conversion_fDuration > 0) { + if (g_CmdPrams.conversion_fDuration > 0) + { data->m_CompressionTime = g_CmdPrams.conversion_fDuration; double CompressionRatio = data->m_SourceImageSize / (double)data->m_FileSize; char buffer[128]; @@ -5152,7 +5937,9 @@ bool processItem(QFile* file, data->m_CompressionTimeStr = QString().number((double)data->m_CompressionTime / 60, 'f', 2) + " Min"; else data->m_CompressionTimeStr = QString().number((double)data->m_CompressionTime / 3600, 'f', 2) + " Hrs"; - } else { + } + else + { data->m_CompressionTime = 0; data->m_CompressionTimeStr = DESTINATION_IMAGE_NOTPROCESSED; } @@ -5160,27 +5947,31 @@ bool processItem(QFile* file, NumberOfItemCompressed++; g_pProgressDlg->SetValue(0); - if (g_Application_Options.m_logresults && (g_CmdPrams.SSIM > 0.0F)) { + if (g_Application_Options.m_logresults && (g_CmdPrams.SSIM > 0.0F)) + { m_AnalaysisData.processCount++; m_AnalaysisData.SSIM_Total += g_CmdPrams.SSIM; m_AnalaysisData.PSNR_Total += g_CmdPrams.PSNR; m_AnalaysisData.processTime += g_CmdPrams.compress_fDuration; - if (g_Application_Options.m_analysisResultTable) { + if (g_Application_Options.m_analysisResultTable) + { ProjectView->m_analysisTable.AddTestResults(g_CmdPrams.DestFile, - data->m_compname, - fqualty_setting, - g_CmdPrams.CompressOptions.perfStats.m_computeShaderElapsedMS, - g_CmdPrams.CompressOptions.perfStats.m_CmpMTxPerSec, - g_CmdPrams.compress_fDuration, - g_CmdPrams.PSNR, - g_CmdPrams.SSIM); + data->m_compname, + fqualty_setting, + g_CmdPrams.CompressOptions.perfStats.m_computeShaderElapsedMS, + g_CmdPrams.CompressOptions.perfStats.m_CmpMTxPerSec, + g_CmdPrams.compress_fDuration, + g_CmdPrams.PSNR, + g_CmdPrams.SSIM); } } argvVec.clear(); argv.clear(); return true; - } else { + } + else + { NumberOfItemCompressedFailed++; g_pProgressDlg->SetValue(0); @@ -5189,9 +5980,11 @@ bool processItem(QFile* file, return false; } } - } else { + } + else + { NumberOfItemCompressedFailed++; - Imageitem->setIcon(0, QIcon(QStringLiteral(":/CompressonatorGUI/Images/smallRedStone.png"))); + Imageitem->setIcon(0, QIcon(QStringLiteral(":/compressonatorgui/images/smallredstone.png"))); g_pProgressDlg->SetValue(0); argvVec.clear(); @@ -5206,26 +5999,33 @@ bool processItem(QFile* file, argv.clear(); } } - } else { + } + else + { if (Imageitem->isSelected()) NumberOfItemsSkipped++; } return true; - } else { + } + else + { return false; } } -void replaceExt(std::string& s, const std::string& newExt) { +void replaceExt(std::string& s, const std::string& newExt) +{ std::string::size_type i = s.rfind('.', s.length()); - if (i != std::string::npos) { + if (i != std::string::npos) + { s.replace(i + 1, newExt.length(), newExt); } } -AnalysisTableWidget::AnalysisTableWidget() { +AnalysisTableWidget::AnalysisTableWidget() +{ Qt::WindowFlags flags = windowFlags(); setWindowFlags(flags | Qt::WindowStaysOnTopHint); // can use Qt::WindowTitleHint to remove close button setWindowTitle("Analysis"); @@ -5252,13 +6052,17 @@ AnalysisTableWidget::AnalysisTableWidget() { setMinimumWidth(696); } -void AnalysisTableWidget::keyPressEvent(QKeyEvent* event) { +void AnalysisTableWidget::keyPressEvent(QKeyEvent* event) +{ // selected cells - if (!selectedIndexes().isEmpty()) { - if (event->matches(QKeySequence::Copy)) { + if (!selectedIndexes().isEmpty()) + { + if (event->matches(QKeySequence::Copy)) + { QString text; QItemSelectionRange range = selectionModel()->selection().first(); - for (auto i = range.top(); i <= range.bottom(); ++i) { + for (auto i = range.top(); i <= range.bottom(); ++i) + { QStringList rowContents; for (auto j = range.left(); j <= range.right(); ++j) rowContents << model()->index(i, j).data().toString(); @@ -5266,12 +6070,14 @@ void AnalysisTableWidget::keyPressEvent(QKeyEvent* event) { text += "\n"; } QApplication::clipboard()->setText(text); - } else + } + else QTableView::keyPressEvent(event); } } -void AnalysisTableWidget::AddAverageResults(QString deviceName, QString processName, QString Time, QString psnr, QString ssim) { +void AnalysisTableWidget::AddAverageResults(QString deviceName, QString processName, QString Time, QString psnr, QString ssim) +{ int rowCount = this->rowCount(); this->insertRow(rowCount); setWindowTitle("Analysis Encode with " + processName + " " + deviceName); @@ -5287,13 +6093,14 @@ void AnalysisTableWidget::AddAverageResults(QString deviceName, QString processN } void AnalysisTableWidget::AddTestResults(std::string processPath, - QString processName, - float Quality, - double PerfTime, - double MPxPerSec, - double Time, - double psnr, - double ssim) { + QString processName, + float Quality, + double PerfTime, + double MPxPerSec, + double Time, + double psnr, + double ssim) +{ QString str_time; QString str_perftime; QString str_Mpx; @@ -5320,25 +6127,31 @@ void AnalysisTableWidget::AddTestResults(std::string processPath, setItem(rowCount, 7, new QTableWidgetItem(str_ssim)); } -void AnalysisTableWidget::ClearResults() { +void AnalysisTableWidget::ClearResults() +{ this->setRowCount(0); } -void CompressFiles(QFile* file, ProjectView* ProjectView) { +void CompressFiles(QFile* file, ProjectView* ProjectView) +{ // if ((file == NULL) || (ProjectView == NULL)) // return; - struct Image_Data { + struct Image_Data + { QString FilePathName; QTreeWidgetItemIterator it; }; TAnalysisData m_AnalaysisData = {0}; - if (g_Application_Options.m_analysisResultTable && g_Application_Options.m_logresults) { + if (g_Application_Options.m_analysisResultTable && g_Application_Options.m_logresults) + { ProjectView->m_analysisTable.ClearResults(); ProjectView->m_analysisTable.show(); - } else { + } + else + { ProjectView->m_analysisTable.hide(); } @@ -5375,14 +6188,17 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { double conversion_loopStartTime = timeStampsec(); - while (*it) { + while (*it) + { QString name = (*it)->text(0); childcount = (*it)->childCount(); - switch (levelType(*it)) { + switch (levelType(*it)) + { case TREETYPE_Double_Click_here_to_add_files: break; - case TREETYPE_IMAGEFILE_DATA: { + case TREETYPE_IMAGEFILE_DATA: + { g_pProgressDlg->SetHeader("Processing: Image"); //========================================== // Get Image Info and data @@ -5390,7 +6206,8 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { QVariant v = (*it)->data(TREE_SourceInfo, Qt::UserRole); C_Source_Info* m_data = v.value(); - if (m_data == NULL) { + if (m_data == NULL) + { ++it; continue; } @@ -5403,27 +6220,34 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { // TREETYPE_Add_destination_setting: // TREETYPE_COMPRESSION_DATA: //========================================== - while (childcount > 0) { + while (childcount > 0) + { if (g_bAbortCompression) break; it++; childcount--; - if ((levelType(*it) == TREETYPE_COMPRESSION_DATA) && (*it)->isSelected()) { - if (m_data->m_MipImages) { - if (m_data->m_MipImages->mipset) { + if ((levelType(*it) == TREETYPE_COMPRESSION_DATA) && (*it)->isSelected()) + { + if (m_data->m_MipImages) + { + if (m_data->m_MipImages->mipset) + { miplevels = m_data->m_MipImages->mipset->m_nMipLevels; - sourceImageMipSet = m_data->m_MipImages->mipset; - + // + // Feature disbled causes confusion when src has not mip levels and is then has them after processing // Do auto MipMap generation for the source if GPU HW is used to generate compressed textures - // use case condition is : 1 Source Mip Map levels are not assigned, and GPU HW Mipmap generation is enabled - if ((g_Application_Options.m_ImageEncode == C_Application_Options::ImageEncodeWith::GPU_HW) && - g_Application_Options.m_useGPUMipMaps && (miplevels <= 1)) { - if (CMP_GenerateMIPLevels(sourceImageMipSet, 4) == CMP_OK) { - // Create Image views for the levels - CImageLoader ImageLoader; - ImageLoader.UpdateMIPMapImages(m_data->m_MipImages); - } - } + // use case condition is : 1 Source Mip Map levels are assigned, and GPU HW Mipmap generation is enabled + // sourceImageMipSet = m_data->m_MipImages->mipset; + // if ((g_Application_Options.m_ImageEncode == C_Application_Options::ImageEncodeWith::GPU_HW) && + // g_Application_Options.m_useGPUMipMaps && (miplevels > 1)) + // { + // if (CMP_GenerateMIPLevels(sourceImageMipSet, 4) == CMP_OK) + // { + // // Create Image views for the levels + // CImageLoader ImageLoader; + // ImageLoader.UpdateMIPMapImages(m_data->m_MipImages); + // } + // } } } processItem(file, @@ -5441,10 +6265,12 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { } break; - case TREETYPE_3DMODEL_DATA: { + case TREETYPE_3DMODEL_DATA: + { QVariant v = (*it)->data(TREE_SourceInfo, Qt::UserRole); C_3DModel_Info* m_data = v.value(); - if (m_data == NULL) { + if (m_data == NULL) + { ++it; continue; } @@ -5454,16 +6280,20 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { std::list image_list; // we have data in the model to process - while (childcount > 0) { + while (childcount > 0) + { if (g_bAbortCompression) break; it++; childcount--; - switch (levelType(*it)) { - case TREETYPE_VIEWIMAGE_ONLY_NODE: { + switch (levelType(*it)) + { + case TREETYPE_VIEWIMAGE_ONLY_NODE: + { QVariant v = (*it)->data(TREE_SourceInfo, Qt::UserRole); C_Source_Info* m_data = v.value(); - if (m_data) { + if (m_data) + { QString SourceImage = m_data->m_Full_Path; image_list.push_back({SourceImage, it}); } @@ -5475,36 +6305,44 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { int subchildcount = (*it)->childCount(); // TREETYPE_Add_destination_setting: // TREETYPE_COMPRESSION_DATA: - while (subchildcount > 0) { + while (subchildcount > 0) + { if (g_bAbortCompression) break; it++; subchildcount--; - if (levelType(*it) == TREETYPE_COMPRESSION_DATA) { + if (levelType(*it) == TREETYPE_COMPRESSION_DATA) + { v = (*it)->data(TREE_SourceInfo, Qt::UserRole); C_Destination_Options* data = v.value(); - if (data) { + if (data) + { QString sourceImage = data->m_sourceFileNamePath; // find the source image data std::list::iterator items = image_list.begin(); - while (items != image_list.end()) { - if (items->FilePathName.compare(sourceImage, Qt::CaseInsensitive) == 0) { + while (items != image_list.end()) + { + if (items->FilePathName.compare(sourceImage, Qt::CaseInsensitive) == 0) + { //========================================== // Get Image Info and data //========================================== QVariant v = (*(items->it))->data(TREE_SourceInfo, Qt::UserRole); C_Source_Info* m_data = v.value(); - if (m_data) { + if (m_data) + { QString FilePathName; FilePathName = m_data->m_Full_Path; int miplevels = 0; sourceImageMipSet = NULL; - if (m_data->m_MipImages) { - if (m_data->m_MipImages->mipset) { + if (m_data->m_MipImages) + { + if (m_data->m_MipImages->mipset) + { miplevels = m_data->m_MipImages->mipset->m_nMipLevels; sourceImageMipSet = m_data->m_MipImages->mipset; } @@ -5519,7 +6357,8 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { NumberOfItemCompressedFailed, NumberOfItemsSkipped, m_AnalaysisData, - it)) { + it)) + { UpdateDestglTFAfterProcess((*it)); } } @@ -5528,24 +6367,31 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { items++; } } - } else if (levelType(*it) == TREETYPE_MESH_DATA) { + } + else if (levelType(*it) == TREETYPE_MESH_DATA) + { v = (*it)->data(TREE_SourceInfo, Qt::UserRole); C_Destination_Options* data = v.value(); bool testitem = false; //process setting and item is checked - if (ProjectView->m_EnableCheckedItemsView) { + if (ProjectView->m_EnableCheckedItemsView) + { //override checked item using m_EnableCheckedItemsView testitem = ((*it)->checkState(0) == Qt::Checked); - } else { + } + else + { //override checked item is selected testitem = (*it)->isSelected(); } - if (data) { + if (data) + { if (!testitem) testitem = data->m_isselected; } - if (data && testitem) { + if (data && testitem) + { // Reset force processing use flag data->m_isselected = false; /*********************************************************/ @@ -5567,7 +6413,8 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { g_pProgressDlg->SetValue(0); g_pProgressDlg->show(); - if (ProjectView->m_CompressStatusDialog) { + if (ProjectView->m_CompressStatusDialog) + { ProjectView->m_CompressStatusDialog->appendText(msgCommandLine); } @@ -5575,7 +6422,8 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { /* MESH OPZTIMIZATION */ /********************************/ - if (data->getDo_Mesh_Optimization() != data->NoOpt) { + if (data->getDo_Mesh_Optimization() != data->NoOpt) + { msgCommandLine = "[Mesh Optimization] Src: " + ModelSource + " Dst: " + ModelDestination; g_pProgressDlg->SetHeader("Processing: Mesh Optimization"); g_pProgressDlg->SetLabelText(msgCommandLine); @@ -5586,8 +6434,10 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { plugin_Mesh = reinterpret_cast(g_pluginManager.GetPlugin("MESH_OPTIMIZER", "TOOTLE_MESH")); - if (plugin_Mesh) { - if (plugin_Mesh->Init() == 0) { + if (plugin_Mesh) + { + if (plugin_Mesh->Init() == 0) + { plugin_Mesh->TC_PluginSetSharedIO(g_GUI_CMIPS); MeshSettings uimeshsettings; @@ -5625,10 +6475,12 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { m_plugin_loader = reinterpret_cast(g_pluginManager.GetPlugin("3DMODEL_LOADER", c_ext)); - if (m_plugin_loader) { + if (m_plugin_loader) + { m_plugin_loader->TC_PluginSetSharedIO(g_GUI_CMIPS); void* msgHandler = NULL; - if (ProjectView) { + if (ProjectView) + { cpMainComponents* mainComponents = NULL; mainComponents = (cpMainComponents*)ProjectView->m_parent; if (mainComponents) @@ -5637,17 +6489,21 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { int result; if (result = m_plugin_loader->LoadModelData( - SourceModelFileNamePath.c_str(), "", &g_pluginManager, msgHandler, &ProgressCallback) != 0) { + SourceModelFileNamePath.c_str(), "", &g_pluginManager, msgHandler, &ProgressCallback) != 0) + { if (result != 0) throw("Error Loading Model Data"); } - if (strcmp(c_ext, "GLTF") == 0) { + if (strcmp(c_ext, "GLTF") == 0) + { gltfdata = (GLTFCommon*)m_plugin_loader->GetModelData(); - if (gltfdata) { + if (gltfdata) + { if (gltfdata->m_meshBufferData.m_meshData[0].vertices.size() > 0) modelDataIn = (void*)&(gltfdata->m_meshBufferData); - else { + else + { modelDataIn = nullptr; if (ProjectView->m_CompressStatusDialog) ProjectView->m_CompressStatusDialog->appendText( @@ -5655,45 +6511,58 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { "supported."); } } - } else + } + else modelDataIn = m_plugin_loader->GetModelData(); } - try { + try + { modelDataOut = plugin_Mesh->ProcessMesh(modelDataIn, (void*)&uimeshsettings, NULL, &ProgressCallback); - } catch (std::exception& e) { + } + catch (std::exception& e) + { if (ProjectView->m_CompressStatusDialog) ProjectView->m_CompressStatusDialog->appendText("[Mesh Optimization] Error: " + - QString::fromStdString(e.what())); + QString::fromStdString(e.what())); } - if (modelDataOut) { - if (g_bAbortCompression) { - Imageitem->setIcon(0, QIcon(QStringLiteral(":/CompressonatorGUI/Images/smallGrayStone.png"))); + if (modelDataOut) + { + if (g_bAbortCompression) + { + Imageitem->setIcon(0, QIcon(QStringLiteral(":/compressonatorgui/images/smallgraystone.png"))); g_pProgressDlg->SetValue(0); - } else { + } + else + { NumberOfItemCompressed++; if (ProjectView->m_CompressStatusDialog) ProjectView->m_CompressStatusDialog->appendText("[Mesh Optimization] Done."); std::vector* optimized = ((std::vector*)modelDataOut); QTreeWidgetItem* subModel_parent = (*it)->parent(); - if (subModel_parent && (levelType(subModel_parent) == TREETYPE_3DSUBMODEL_DATA)) { + if (subModel_parent && (levelType(subModel_parent) == TREETYPE_3DSUBMODEL_DATA)) + { QVariant v = subModel_parent->data(TREE_SourceInfo, Qt::UserRole); C_3DSubModel_Info* subModel_data = v.value(); - if (subModel_data) { + if (subModel_data) + { subModel_data->m_ModelData.m_meshData.resize(optimized->size()); - for (unsigned int i = 0; i < optimized->size(); i++) { + for (unsigned int i = 0; i < optimized->size(); i++) + { subModel_data->m_ModelData.m_meshData[i].vertices = (*optimized)[i].vertices; subModel_data->m_ModelData.m_meshData[i].indices = (*optimized)[i].indices; } } //create a new copy of bin (with user specified name) and update the new .bin file to submodel (gltf case only) - if (strcmp(c_ext, "GLTF") == 0) { + if (strcmp(c_ext, "GLTF") == 0) + { std::ifstream f(ModelSource.toStdString().data()); - if (!f) { + if (!f) + { if (ProjectView->m_CompressStatusDialog) ProjectView->m_CompressStatusDialog->appendText( "[Mesh Optimization] Error in update destination gltf file."); @@ -5706,16 +6575,19 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { auto buffers = j3["buffers"]; - for (unsigned int i = 0; i < buffers.size(); - i++) { //for now only support one buffer (one bin file only) + for (unsigned int i = 0; i < buffers.size(); i++) + { //for now only support one buffer (one bin file only) std::string name = buffers[i]["uri"].get(); - if (name.find(".bin") != std::string::npos) { + if (name.find(".bin") != std::string::npos) + { //retrieve original bin file and create a new copy of bin file QFileInfo srcFile(ModelSource); QString oriBinFile = srcFile.absolutePath() + "/" + QString::fromStdString(name); - if (QFile::exists(data->m_destFileNamePath)) { + if (QFile::exists(data->m_destFileNamePath)) + { bool removeOldcopy = QFile::remove(data->m_destFileNamePath); - if (!removeOldcopy) { + if (!removeOldcopy) + { if (ProjectView->m_CompressStatusDialog) ProjectView->m_CompressStatusDialog->appendText( "[Mesh Optimization] Error in update destination gltf bin file. " @@ -5723,7 +6595,8 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { } } bool copied = QFile::copy(oriBinFile, data->m_destFileNamePath); - if (!copied) { + if (!copied) + { if (ProjectView->m_CompressStatusDialog) ProjectView->m_CompressStatusDialog->appendText( "[Mesh Optimization] Error in update destination gltf bin file."); @@ -5732,7 +6605,9 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { QFileInfo binFile(data->m_destFileNamePath); QString binFileName = binFile.fileName(); j3["buffers"][i]["uri"] = binFileName.toStdString(); - } else { + } + else + { if (ProjectView->m_CompressStatusDialog) ProjectView->m_CompressStatusDialog->appendText( "[Mesh Optimization] Error in update destination gltf file. embedded is " @@ -5741,7 +6616,8 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { } std::ofstream fDest(ModelDestination.toStdString().data(), std::ios_base::out); - if (!fDest) { + if (!fDest) + { if (ProjectView->m_CompressStatusDialog) ProjectView->m_CompressStatusDialog->appendText( "[Mesh Optimization] Error in update destination gltf file."); @@ -5755,12 +6631,15 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { PluginInterface_3DModel_Loader* plugin_save = NULL; plugin_save = reinterpret_cast(g_pluginManager.GetPlugin("3DMODEL_LOADER", c_ext)); - if (plugin_save) { + if (plugin_save) + { plugin_save->TC_PluginSetSharedIO(g_GUI_CMIPS); int result = 0; - if (strcmp(c_ext, "GLTF") == 0) { - if (gltfdata) { + if (strcmp(c_ext, "GLTF") == 0) + { + if (gltfdata) + { GLTFCommon optimizedGltf; optimizedGltf.buffersData = gltfdata->buffersData; optimizedGltf.isBinFile = gltfdata->isBinFile; @@ -5773,53 +6652,69 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { optimizedGltf.m_scenes = gltfdata->m_scenes; optimizedGltf.m_meshBufferData.m_meshData = *optimized; - if (plugin_save->SaveModelData(ModelDestination.toStdString().data(), &optimizedGltf) == -1) { + if (plugin_save->SaveModelData(ModelDestination.toStdString().data(), &optimizedGltf) == -1) + { if (ProjectView->m_CompressStatusDialog) ProjectView->m_CompressStatusDialog->appendText( "[Mesh Optimization] Failed to save optimized gltf data."); } - } else { + } + else + { if (ProjectView->m_CompressStatusDialog) ProjectView->m_CompressStatusDialog->appendText( "[Mesh Optimization] Failed to save optimized gltf data."); } - } else { - if (plugin_save->SaveModelData(ModelDestination.toStdString().data(), &((*optimized)[0])) != -1) { + } + else + { + if (plugin_save->SaveModelData(ModelDestination.toStdString().data(), &((*optimized)[0])) != -1) + { #ifdef _WIN32 - if (!(writeObjFileState(ModelDestination.toStdString().data(), CMP_PROCESSED))) { + if (!(writeObjFileState(ModelDestination.toStdString().data(), CMP_PROCESSED))) + { if (ProjectView->m_CompressStatusDialog) ProjectView->m_CompressStatusDialog->appendText( "[Mesh Optimization] Failed to save optimized obj data."); } #endif - } else { + } + else + { if (ProjectView->m_CompressStatusDialog) ProjectView->m_CompressStatusDialog->appendText( "[Mesh Optimization] Failed to save optimized obj data."); } } - if (result != 0) { + if (result != 0) + { if (ProjectView->m_CompressStatusDialog) ProjectView->m_CompressStatusDialog->appendText( "[Mesh Optimization] Error in saving mesh file."); - if (plugin_save) { + if (plugin_save) + { delete plugin_save; plugin_save = nullptr; } - } else { + } + else + { bMeshOptimized = true; if (ProjectView->m_CompressStatusDialog) ProjectView->m_CompressStatusDialog->appendText("[Mesh Optimization] Saving " + - ModelDestination + " done."); + ModelDestination + " done."); } - } else { + } + else + { if (ProjectView->m_CompressStatusDialog) ProjectView->m_CompressStatusDialog->appendText("[Mesh Optimization] File format not supported."); } plugin_Mesh->CleanUp(); - if (plugin_save) { + if (plugin_save) + { delete plugin_save; plugin_save = nullptr; } @@ -5827,34 +6722,43 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { } // Update Icon if new file exists - if (!ProjectView->Tree_updateCompressIcon(Imageitem, ModelDestination, true)) { + if (!ProjectView->Tree_updateCompressIcon(Imageitem, ModelDestination, true)) + { NumberOfItemCompressedFailed++; g_pProgressDlg->SetValue(0); } - if (m_plugin_loader) { + if (m_plugin_loader) + { delete m_plugin_loader; m_plugin_loader = nullptr; } - } else { + } + else + { if (ProjectView->m_CompressStatusDialog) ProjectView->m_CompressStatusDialog->appendText("[Mesh Optimization] Error in processing mesh."); NumberOfItemCompressedFailed++; - Imageitem->setIcon(0, QIcon(QStringLiteral(":/CompressonatorGUI/Images/smallRedStone.png"))); + Imageitem->setIcon(0, QIcon(QStringLiteral(":/compressonatorgui/images/smallredstone.png"))); g_pProgressDlg->SetValue(0); plugin_Mesh->CleanUp(); } //hideProgressDialog(); - } else { + } + else + { if (ProjectView->m_CompressStatusDialog) ProjectView->m_CompressStatusDialog->appendText("[Mesh Optimization] Error in init mesh plugin."); } - } else { + } + else + { if (ProjectView->m_CompressStatusDialog) ProjectView->m_CompressStatusDialog->appendText("[Mesh Optimization] Error in loading mesh plugin."); } - if (plugin_Mesh) { + if (plugin_Mesh) + { delete plugin_Mesh; plugin_Mesh = NULL; } @@ -5881,9 +6785,11 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { // data->setDo_Mesh_Compression(data->NoComp); //} - if (data->getDo_Mesh_Compression() != data->NoComp) { + if (data->getDo_Mesh_Compression() != data->NoComp) + { // Case: glTF -> glTF draco compression - if (strcmp(c_ext, "GLTF") == 0 && strcmp(c_extdst, "GLTF") == 0) { + if (strcmp(c_ext, "GLTF") == 0 && strcmp(c_extdst, "GLTF") == 0) + { std::string err; tinygltf2::Model model; tinygltf2::TinyGLTF loader; @@ -5896,13 +6802,15 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { std::string srcFile = ModelSource.toStdString(); std::string dstFile = ModelDestination.toStdString(); // Check if mesh optimization was done if so then source is optimized file - if (bMeshOptimized) { + if (bMeshOptimized) + { srcFile = ModelDestination.toStdString(); - if (!(CMP_FileExists(srcFile))) { + if (!(CMP_FileExists(srcFile))) + { if (ProjectView->m_CompressStatusDialog) ProjectView->m_CompressStatusDialog->appendText("Error: Source Model Mesh File is not found.\n"); NumberOfItemCompressedFailed++; - Imageitem->setIcon(0, QIcon(QStringLiteral(":/CompressonatorGUI/Images/smallRedStone.png"))); + Imageitem->setIcon(0, QIcon(QStringLiteral(":/compressonatorgui/images/smallredstone.png"))); g_pProgressDlg->SetValue(0); return; } @@ -5911,33 +6819,38 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { } bool ret = loader.LoadASCIIFromFile(&model, &err, srcFile, true); - if (!err.empty()) { + if (!err.empty()) + { if (ProjectView->m_CompressStatusDialog) ProjectView->m_CompressStatusDialog->appendText( "Error processing gltf source:" + QString::fromStdString(srcFile) + " file. " + QString::fromStdString(err) + "\n"); NumberOfItemCompressedFailed++; - Imageitem->setIcon(0, QIcon(QStringLiteral(":/CompressonatorGUI/Images/smallRedStone.png"))); + Imageitem->setIcon(0, QIcon(QStringLiteral(":/compressonatorgui/images/smallredstone.png"))); g_pProgressDlg->SetValue(0); return; } - if (!ret) { + if (!ret) + { if (ProjectView->m_CompressStatusDialog) ProjectView->m_CompressStatusDialog->appendText("Failed in loading glTF file : " + - QString::fromStdString(srcFile)); + QString::fromStdString(srcFile)); NumberOfItemCompressedFailed++; - Imageitem->setIcon(0, QIcon(QStringLiteral(":/CompressonatorGUI/Images/smallRedStone.png"))); + Imageitem->setIcon(0, QIcon(QStringLiteral(":/compressonatorgui/images/smallredstone.png"))); g_pProgressDlg->SetValue(0); return; - } else { + } + else + { if (ProjectView->m_CompressStatusDialog) ProjectView->m_CompressStatusDialog->appendText("Success in loading glTF file : " + - QString::fromStdString(srcFile)); + QString::fromStdString(srcFile)); } bool is_draco_src = false; #ifdef USE_MESH_DRACO_EXTENSION - if (model.dracomeshes.size() > 0) { + if (model.dracomeshes.size() > 0) + { is_draco_src = true; } #endif @@ -5953,32 +6866,39 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { ret = saver.WriteGltfSceneToFile(&model, &err, dstFile, CompressOptions, is_draco_src, true); - if (!err.empty()) { + if (!err.empty()) + { if (ProjectView->m_CompressStatusDialog) ProjectView->m_CompressStatusDialog->appendText( "Error saving gltf destination:" + QString::fromStdString(dstFile) + " file. " + QString::fromStdString(err) + "\n"); NumberOfItemCompressedFailed++; - Imageitem->setIcon(0, QIcon(QStringLiteral(":/CompressonatorGUI/Images/smallRedStone.png"))); + Imageitem->setIcon(0, QIcon(QStringLiteral(":/compressonatorgui/images/smallredstone.png"))); g_pProgressDlg->SetValue(0); return; } - if (!ret) { + if (!ret) + { if (ProjectView->m_CompressStatusDialog) ProjectView->m_CompressStatusDialog->appendText("Failed to save glTF file " + QString::fromStdString(dstFile) + - "\n"); + "\n"); NumberOfItemCompressedFailed++; - Imageitem->setIcon(0, QIcon(QStringLiteral(":/CompressonatorGUI/Images/smallRedStone.png"))); + Imageitem->setIcon(0, QIcon(QStringLiteral(":/compressonatorgui/images/smallredstone.png"))); g_pProgressDlg->SetValue(0); return; - } else { + } + else + { PrintInfo("Success in writting glTF file : [%s].\n", dstFile.c_str()); } - if (g_bAbortCompression) { - Imageitem->setIcon(0, QIcon(QStringLiteral(":/CompressonatorGUI/Images/smallGrayStone.png"))); + if (g_bAbortCompression) + { + Imageitem->setIcon(0, QIcon(QStringLiteral(":/compressonatorgui/images/smallgraystone.png"))); g_pProgressDlg->SetValue(0); - } else { + } + else + { NumberOfItemCompressed++; if (ProjectView->m_CompressStatusDialog) ProjectView->m_CompressStatusDialog->appendText("[Mesh Compression] Done."); @@ -5986,20 +6906,24 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { } // Update Icon if new file exists - if (!ProjectView->Tree_updateCompressIcon(Imageitem, ModelDestination, true)) { + if (!ProjectView->Tree_updateCompressIcon(Imageitem, ModelDestination, true)) + { NumberOfItemCompressedFailed++; g_pProgressDlg->SetValue(0); } } //case obj->obj will produce obj->drc draco compression - else { + else + { PluginInterface_3DModel_Loader* m_plugin_loader_drc = NULL; PluginInterface_Mesh* plugin_MeshComp; plugin_MeshComp = reinterpret_cast(g_pluginManager.GetPlugin("MESH_COMPRESSOR", "DRACO")); - if (plugin_MeshComp) { - if (plugin_MeshComp->Init() == 0) { + if (plugin_MeshComp) + { + if (plugin_MeshComp->Init() == 0) + { //showProgressDialog("Process Mesh Data"); plugin_MeshComp->TC_PluginSetSharedIO(g_GUI_CMIPS); @@ -6034,10 +6958,12 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { m_plugin_loader_drc = reinterpret_cast(g_pluginManager.GetPlugin("3DMODEL_LOADER", "DRC")); - if (m_plugin_loader_drc) { + if (m_plugin_loader_drc) + { m_plugin_loader_drc->TC_PluginSetSharedIO(g_GUI_CMIPS); void* msgHandler = NULL; - if (ProjectView) { + if (ProjectView) + { cpMainComponents* mainComponents = NULL; mainComponents = (cpMainComponents*)ProjectView->m_parent; if (mainComponents) @@ -6046,8 +6972,10 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { int result; if (result = m_plugin_loader_drc->LoadModelData( - "OBJ", NULL, &g_pluginManager, &DracoOptions, &ProgressCallback) != 0) { - if (result != 0) { + "OBJ", NULL, &g_pluginManager, &DracoOptions, &ProgressCallback) != 0) + { + if (result != 0) + { if (ProjectView->m_CompressStatusDialog) ProjectView->m_CompressStatusDialog->appendText("[Mesh Compression] Error Loading Model Data"); return; @@ -6056,22 +6984,29 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { modelDataIn = m_plugin_loader_drc->GetModelData(); - try { + try + { if (modelDataIn) modelDataOut = plugin_MeshComp->ProcessMesh(modelDataIn, (void*)&DracoOptions, NULL, &ProgressCallback); - } catch (std::exception& e) { + } + catch (std::exception& e) + { if (ProjectView->m_CompressStatusDialog) ProjectView->m_CompressStatusDialog->appendText( "[Mesh Compression] Error: " + QString::fromStdString(e.what()) + ". Please try another setting."); } - if (modelDataOut) { - if (g_bAbortCompression) { - Imageitem->setIcon(0, QIcon(QStringLiteral(":/CompressonatorGUI/Images/smallGrayStone.png"))); + if (modelDataOut) + { + if (g_bAbortCompression) + { + Imageitem->setIcon(0, QIcon(QStringLiteral(":/compressonatorgui/images/smallgraystone.png"))); g_pProgressDlg->SetValue(0); - } else { + } + else + { NumberOfItemCompressed++; if (ProjectView->m_CompressStatusDialog) ProjectView->m_CompressStatusDialog->appendText("[Mesh Compression] Done."); @@ -6079,35 +7014,44 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { } // Update Icon if new file exists - if (!ProjectView->Tree_updateCompressIcon(Imageitem, QString(DracoOptions.output.c_str()), true)) { + if (!ProjectView->Tree_updateCompressIcon(Imageitem, QString(DracoOptions.output.c_str()), true)) + { NumberOfItemCompressedFailed++; g_pProgressDlg->SetValue(0); } - } else { + } + else + { if (ProjectView->m_CompressStatusDialog) ProjectView->m_CompressStatusDialog->appendText("[Mesh Compression] Error in processing mesh."); NumberOfItemCompressedFailed++; - Imageitem->setIcon(0, QIcon(QStringLiteral(":/CompressonatorGUI/Images/smallRedStone.png"))); + Imageitem->setIcon(0, QIcon(QStringLiteral(":/compressonatorgui/images/smallredstone.png"))); g_pProgressDlg->SetValue(0); } } - } else { + } + else + { if (ProjectView->m_CompressStatusDialog) ProjectView->m_CompressStatusDialog->appendText("[Mesh Compression] Error in init mesh plugin."); } plugin_MeshComp->CleanUp(); - } else { + } + else + { if (ProjectView->m_CompressStatusDialog) ProjectView->m_CompressStatusDialog->appendText("[Mesh Compression] Error in loading mesh compression plugin."); } - if (plugin_MeshComp) { + if (plugin_MeshComp) + { delete plugin_MeshComp; plugin_MeshComp = nullptr; } - if (m_plugin_loader_drc) { + if (m_plugin_loader_drc) + { delete m_plugin_loader_drc; m_plugin_loader_drc = nullptr; } @@ -6136,8 +7080,10 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { g_bCompressing = false; ProjectView->m_processFromContext = false; - if (ProjectView->m_CompressStatusDialog && (file == NULL) && (!g_bAbortCompression)) { - if ((NumberOfItemCompressed == 0) && (NumberOfItemCompressedFailed == 0)) { + if (ProjectView->m_CompressStatusDialog && (file == NULL) && (!g_bAbortCompression)) + { + if ((NumberOfItemCompressed == 0) && (NumberOfItemCompressedFailed == 0)) + { QMessageBox msgBox; msgBox.setText("No valid image compression setting(s) are selected to process. Please refer to output window for details."); msgBox.exec(); @@ -6151,10 +7097,13 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { ProjectView->m_CompressStatusDialog->appendText(" and double click on \"(+) Add destination settings...\""); ProjectView->m_CompressStatusDialog->appendText(" - Selecting images to process with Mouse click"); ProjectView->m_CompressStatusDialog->appendText(" on destination images"); - } else { + } + else + { if (g_bAbortCompression) ProjectView->m_CompressStatusDialog->appendText("Compression was canceled!"); - else { + else + { QString MsgDuration; MsgDuration = "Processed in "; double duration = timeStampsec() - conversion_loopStartTime; @@ -6174,7 +7123,8 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { // Msg.append(" skipped "); //} - if (g_Application_Options.m_logresults && (m_AnalaysisData.processCount > 0)) { + if (g_Application_Options.m_logresults && (m_AnalaysisData.processCount > 0)) + { QString time; QString psnr; QString ssim; @@ -6193,7 +7143,8 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { QString EncodeWith; QString DeviceName; - switch (g_Application_Options.m_ImageEncode) { + switch (g_Application_Options.m_ImageEncode) + { case C_Application_Options::ImageEncodeWith::HPC: // Check if last encodewith worked on GPU! if (g_CmdPrams.CompressOptions.format_support_hostEncoder) @@ -6227,7 +7178,8 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { break; } - if (g_Application_Options.m_analysisResultTable) { + if (g_Application_Options.m_analysisResultTable) + { ProjectView->m_analysisTable.AddAverageResults(DeviceName, EncodeWith, time, psnr, ssim); ProjectView->m_analysisTable.show(); } @@ -6245,7 +7197,8 @@ void CompressFiles(QFile* file, ProjectView* ProjectView) { g_bAbortCompression = false; } -void ProjectView::compressProjectFiles(QFile* file) { +void ProjectView::compressProjectFiles(QFile* file) +{ if (m_CompressStatusDialog == NULL) return; diff --git a/applications/compressonatorgui/components/cpsetcompressoptions.cpp b/applications/compressonatorgui/components/cpsetcompressoptions.cpp index 83e0be0c0..22f1bae1b 100644 --- a/applications/compressonatorgui/components/cpsetcompressoptions.cpp +++ b/applications/compressonatorgui/components/cpsetcompressoptions.cpp @@ -136,6 +136,7 @@ CSetCompressOptions::CSetCompressOptions(const QString title, QWidget* parent) m_fileFormats->addItem("DDS"); m_fileFormats->addItem("KTX"); + // ================================================= // Destination File // ================================================= @@ -460,6 +461,9 @@ void CSetCompressOptions::compressionValueChanged(QVariant& value) { codecBlockOptions = false; m_fileFormats->addItem("DDS"); m_fileFormats->addItem("KTX"); +#ifdef _WIN32 + m_fileFormats->addItem("KTX2"); +#endif m_fileFormats->setCurrentIndex(0); m_infotext->clear(); m_infotext->append("Format Description"); @@ -477,6 +481,9 @@ void CSetCompressOptions::compressionValueChanged(QVariant& value) { } m_fileFormats->addItem("DDS"); m_fileFormats->addItem("KTX"); +#ifdef _WIN32 + m_fileFormats->addItem("KTX2"); +#endif #ifdef USE_CRN m_fileFormats->addItem("CRN"); #endif @@ -496,6 +503,9 @@ void CSetCompressOptions::compressionValueChanged(QVariant& value) { } m_fileFormats->addItem("DDS"); m_fileFormats->addItem("KTX"); +#ifdef _WIN32 + m_fileFormats->addItem("KTX2"); +#endif #ifdef USE_CRN m_fileFormats->addItem("CRN"); #endif @@ -516,12 +526,18 @@ void CSetCompressOptions::compressionValueChanged(QVariant& value) { } m_fileFormats->addItem("DDS"); m_fileFormats->addItem("KTX"); +#ifdef _WIN32 + m_fileFormats->addItem("KTX2"); +#endif m_infotext->clear(); m_infotext->append("Format Description"); m_infotext->append( "A four component compressed texture format with explicit alpha for Microsoft DirectX10. DXT3 identical to BC2. Eight bits per pixel."); break; + + case C_Destination_Options::ATI1N: case C_Destination_Options::BC4: + case C_Destination_Options::BC4_S: compressedOptions = true; colorWeightOptions = false; alphaChannelOptions = false; @@ -531,15 +547,20 @@ void CSetCompressOptions::compressionValueChanged(QVariant& value) { } m_fileFormats->addItem("DDS"); m_fileFormats->addItem("KTX"); +#ifdef _WIN32 + if (comp != C_Destination_Options::ATI1N) + m_fileFormats->addItem("KTX2"); +#endif #ifdef USE_CRN m_fileFormats->addItem("CRN"); #endif m_infotext->clear(); m_infotext->append("Format Description"); - m_infotext->append("A single component compressed texture format for Microsoft DirectX10. Identical to ATI1N. Four bits per pixel."); + m_infotext->append("A single component compressed texture format for Microsoft DirectX10. BC4 identical to ATI1N. Four bits per pixel. BC4_S is used for signed components"); break; case C_Destination_Options::BC5: + case C_Destination_Options::BC5_S: case C_Destination_Options::ATI2N: case C_Destination_Options::ATI2N_XY: case C_Destination_Options::ATI2N_DXT5: @@ -552,13 +573,18 @@ void CSetCompressOptions::compressionValueChanged(QVariant& value) { } m_fileFormats->addItem("DDS"); m_fileFormats->addItem("KTX"); +#ifdef _WIN32 + if ((comp == C_Destination_Options::BC5) || + (comp == C_Destination_Options::BC5_S)) + m_fileFormats->addItem("KTX2"); +#endif #ifdef USE_CRN m_fileFormats->addItem("CRN"); #endif m_infotext->clear(); m_infotext->append("Format Description"); - m_infotext->append("A two component compressed texture format for Microsoft DirectX10. BC5 identical to ATI2N. Eight bits per pixel."); + m_infotext->append("A two component compressed texture format for Microsoft DirectX10. BC5 identical to ATI2N. Eight bits per pixel. BC5_S is used for signed components"); break; case C_Destination_Options::ATC_RGB: compressedOptions = true; @@ -702,6 +728,9 @@ void CSetCompressOptions::compressionValueChanged(QVariant& value) { } m_fileFormats->addItem("DDS"); m_fileFormats->addItem("KTX"); +#ifdef _WIN32 + m_fileFormats->addItem("KTX2"); +#endif m_infotext->clear(); m_infotext->append("Format Description"); m_infotext->append("The latest block Compression (BC) format designed to support high-quality compression of RGB and RGBA bytes color spaces."); @@ -748,6 +777,9 @@ void CSetCompressOptions::compressionValueChanged(QVariant& value) { extension = "BASIS"; m_fileFormats->addItem("BASIS"); m_fileFormats->addItem("KTX"); +#ifdef _WIN32 + m_fileFormats->addItem("KTX2"); +#endif m_infotext->clear(); m_infotext->append("Format Description"); m_infotext->append("The latest block Compression (BASIS) format designed to support CTTF and Transcoding."); @@ -771,6 +803,9 @@ void CSetCompressOptions::compressionValueChanged(QVariant& value) { } m_fileFormats->addItem("DDS"); m_fileFormats->addItem("KTX"); +#ifdef _WIN32 + m_fileFormats->addItem("KTX2"); +#endif m_infotext->clear(); m_infotext->append("Format Description"); m_infotext->append("ETC (Ericsson Texture Compression, lossy texture compression developed by Ericsson Research.)"); @@ -786,6 +821,9 @@ void CSetCompressOptions::compressionValueChanged(QVariant& value) { extension = "KTX"; m_fileFormats->addItem("ASTC"); m_fileFormats->addItem("KTX"); +#ifdef _WIN32 + m_fileFormats->addItem("KTX2"); +#endif m_infotext->clear(); m_infotext->append("Format Description"); m_infotext->append("ASTC (Adaptive Scalable Texture Compression),lossy block-based texture compression developed with ARM."); diff --git a/applications/compressonatorgui/components/cpsetmeshoptions.cpp b/applications/compressonatorgui/components/cpsetmeshoptions.cpp index 3753eda1d..0cf46d93c 100644 --- a/applications/compressonatorgui/components/cpsetmeshoptions.cpp +++ b/applications/compressonatorgui/components/cpsetmeshoptions.cpp @@ -21,7 +21,7 @@ // //===================================================================== -#include "cpSetMeshOptions.h" +#include "cpsetmeshoptions.h" #include #include @@ -454,6 +454,9 @@ void CSetMeshOptions::compressionValueChanged(QVariant &value) { astcbitrateOptions = false; m_fileFormats->addItem("DDS"); m_fileFormats->addItem("KTX"); +#ifdef _WIN32 + m_fileFormats->addItem("KTX2"); +#endif m_fileFormats->setCurrentIndex(0); m_infotext->clear(); m_infotext->append("Format Description"); @@ -470,6 +473,9 @@ void CSetMeshOptions::compressionValueChanged(QVariant &value) { } m_fileFormats->addItem("DDS"); m_fileFormats->addItem("KTX"); +#ifdef _WIN32 + m_fileFormats->addItem("KTX2"); +#endif #ifdef USE_CRN m_fileFormats->addItem("CRN"); #endif @@ -488,6 +494,9 @@ void CSetMeshOptions::compressionValueChanged(QVariant &value) { } m_fileFormats->addItem("DDS"); m_fileFormats->addItem("KTX"); +#ifdef _WIN32 + m_fileFormats->addItem("KTX2"); +#endif #ifdef USE_CRN m_fileFormats->addItem("CRN"); #endif @@ -507,10 +516,14 @@ void CSetMeshOptions::compressionValueChanged(QVariant &value) { } m_fileFormats->addItem("DDS"); m_fileFormats->addItem("KTX"); +#ifdef _WIN32 + m_fileFormats->addItem("KTX2"); +#endif m_infotext->clear(); m_infotext->append("Format Description"); m_infotext->append("A four component compressed texture format with explicit alpha for Microsoft DirectX10. DXT3 identical to BC2. Eight bits per pixel."); break; + case C_Destination_Options::ATI1N: case C_Destination_Options::BC4: compressedOptions = true; colorWeightOptions = false; @@ -521,6 +534,10 @@ void CSetMeshOptions::compressionValueChanged(QVariant &value) { } m_fileFormats->addItem("DDS"); m_fileFormats->addItem("KTX"); +#ifdef _WIN32 + if (comp != C_Destination_Options::ATI1N) + m_fileFormats->addItem("KTX2"); +#endif #ifdef USE_CRN m_fileFormats->addItem("CRN"); #endif @@ -542,6 +559,10 @@ void CSetMeshOptions::compressionValueChanged(QVariant &value) { } m_fileFormats->addItem("DDS"); m_fileFormats->addItem("KTX"); +#ifdef _WIN32 + if ((comp == C_Destination_Options::BC5) || (comp == C_Destination_Options::BC5_S)) + m_fileFormats->addItem("KTX2"); +#endif #ifdef USE_CRN m_fileFormats->addItem("CRN"); #endif @@ -686,6 +707,9 @@ void CSetMeshOptions::compressionValueChanged(QVariant &value) { } m_fileFormats->addItem("DDS"); m_fileFormats->addItem("KTX"); +#ifdef _WIN32 + m_fileFormats->addItem("KTX2"); +#endif m_infotext->clear(); m_infotext->append("Format Description"); m_infotext->append("The latest block Compression (BC) format designed to support high-quality compression of RGB and RGBA bytes color spaces."); @@ -701,6 +725,9 @@ void CSetMeshOptions::compressionValueChanged(QVariant &value) { } m_fileFormats->addItem("DDS"); m_fileFormats->addItem("KTX"); +#ifdef _WIN32 + m_fileFormats->addItem("KTX2"); +#endif m_infotext->clear(); m_infotext->append("Format Description"); m_infotext->append("ETC (Ericsson Texture Compression, lossy texture compression developed with Ericsson Research.)"); @@ -716,6 +743,9 @@ void CSetMeshOptions::compressionValueChanged(QVariant &value) { extension = "KTX"; m_fileFormats->addItem("ASTC"); m_fileFormats->addItem("KTX"); +#ifdef _WIN32 + m_fileFormats->addItem("KTX2"); +#endif m_infotext->clear(); m_infotext->append("Format Description"); m_infotext->append("ASTC (Adaptive Scalable Texture Compression),lossy block-based texture compression developed with ARM."); diff --git a/applications/compressonatorgui/components/cpstartuppage.cpp b/applications/compressonatorgui/components/cpstartuppage.cpp index fe55c4c71..4239ce77c 100644 --- a/applications/compressonatorgui/components/cpstartuppage.cpp +++ b/applications/compressonatorgui/components/cpstartuppage.cpp @@ -20,23 +20,39 @@ // THE SOFTWARE. // //===================================================================== - +#include "version.h" #include + +#ifndef USE_QTWEBENGINE #include +#endif + #include + +#ifdef _CMP_CPP17_ // Build code using std::c++17 #include +namespace sfs = std::filesystem; +#else +#include +namespace sfs = std::experimental::filesystem; +#endif + // Local #include "cpstartuppage.h" -#define CP_STR_startup_page_openProjectLink "open_project" -#define CP_STR_startup_page_openProjectLinkPrefix CP_STR_startup_page_openProjectLink "_" -#define CP_STR_startup_page_noRecentProjects "No recent projects" +#define CP_STR_startup_page_openProjectLink "open_project" +#define CP_STR_startup_page_openProjectLinkPrefix CP_STR_startup_page_openProjectLink "_" +#define CP_STR_startup_page_noRecentProjects "No recent projects" -afWebPage::afWebPage(QObject* pParent) : QWebEnginePage(pParent) { +#ifdef USE_QTWEBENGINE +afWebPage::afWebPage(QObject* pParent) + : QWebEnginePage(pParent) +{ } -void afWebPage::javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString& message, int lineNumber, const QString& sourceID) { +void afWebPage::javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString& message, int lineNumber, const QString& sourceID) +{ (void)(level); (void)(sourceID); (void)(lineNumber); @@ -47,73 +63,286 @@ void afWebPage::javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, co QString Request; QString Msg = message; - if (message.contains(CP_STR_startup_page_openProjectLinkPrefix)) { + if (message.contains(CP_STR_startup_page_openProjectLinkPrefix)) + { Request = CP_STR_startup_page_openProjectLink; Msg.remove(CP_STR_startup_page_openProjectLinkPrefix); - } else + } + else Request = message; emit PageButtonClick(Request, Msg); - } -bool afWebPage::acceptNavigationRequest(const QUrl &url, NavigationType type, bool isMainFrame) { +bool afWebPage::acceptNavigationRequest(const QUrl& url, NavigationType type, bool isMainFrame) +{ (void)url; (void)isMainFrame; bool retVal = true; // Do not allow navigation clicks: - if (type == QWebEnginePage::NavigationTypeLinkClicked) { + if (type == QWebEnginePage::NavigationTypeLinkClicked) + { retVal = false; } return retVal; } +#endif -// --------------------------------------------------------------------------- -// Name: afStartupPage::afStartupPage -// Description: Constructor -// Arguments: QWidget* pParent -// Author: Sigal Algranaty -// Date: 21/2/2012 -// --------------------------------------------------------------------------- -cpStartupPage::cpStartupPage(QWidget *parent) - : QWebEngineView(parent) { +cpStartupPage::cpStartupPage(QWidget* parent) +#ifdef USE_QTWEBENGINE + : QWebEngineView(parent) +#endif +{ +#ifdef USE_QTWEBENGINE // Allow focus in this widget: setFocusPolicy(Qt::ClickFocus); afWebPage* pPage = new afWebPage(this); setPage(pPage); - connect(pPage, SIGNAL(PageButtonClick(QString &, QString &)), this, SLOT(onPageButtonClick(QString &, QString &))); + connect(pPage, SIGNAL(PageButtonClick(QString&, QString&)), this, SLOT(onPageButtonClick(QString&, QString&))); // Hide context menu: setContextMenuPolicy(Qt::NoContextMenu); +#else + QString FontFamily = "helvetica"; // "helvetica, arial, sans-serif"; + QString PageListStyle = { + "border-style: none;" + "background-color:#F0F0F0;" + "color:#551a8b;" + "QListWidget::item {" + "border-style: none;" + "background-color:#F0F0F0;" + "color:#551a8b;" + "}" + "QListWidget::item:selected {" + "background-color:#F0F0F0;" + "color:#551a8b;" + "}" + }; + + + QFont HeaderFont(FontFamily, 14, QFont::Bold, true); + QFont Titlefont(FontFamily, 14, QFont::Bold, true); + QFont Selectfont(FontFamily, 10, QFont::Bold, false); + QFont Messagefont(FontFamily, 10); + + QVBoxLayout* m_Vlayout = new QVBoxLayout(); + QHBoxLayout* m_HlayoutLinks = new QHBoxLayout(); + QHBoxLayout* m_HComp = new QHBoxLayout(); + QHBoxLayout* m_HRecentProjects = new QHBoxLayout(); + + QLabel* m_LBlankSpace = new QLabel("", this); + m_LBlankSpace->setFixedHeight(20); + + QLabel* m_LComp = new QLabel("Compressonator", this); + m_LComp->setFont(HeaderFont); + m_LComp->setStyleSheet("color:white; background:qlineargradient(x1 : 0, y1 : 0, x2 : 1, y2 : 1, stop : 0 #1F708F, stop : 1 #000000);"); + + m_LComp->setFixedHeight(40); + m_HComp->addWidget(m_LComp, 0, Qt::AlignTop); + + QHBoxLayout* m_HSampleProjectsAndVersion = new QHBoxLayout(); + Selectfont.setUnderline(true); + { + QVBoxLayout* tInnerVB = new QVBoxLayout; + { + QLabel* m_LSampleProjects = new QLabel("Sample Projects", this); + m_LSampleProjects->setFont(Titlefont); + tInnerVB->addWidget(m_LSampleProjects); + + m_Projectlist.append("bc7_compression"); + m_Projectlist.append("bc6h_compression"); + m_Projectlistview = new QListWidget(); + m_Projectlistview->addItems(m_Projectlist); + m_Projectlistview->setFont(Selectfont); + m_Projectlistview->setStyleSheet(PageListStyle); + m_Projectlistview->setEditTriggers(QAbstractItemView::NoEditTriggers); + m_Projectlistview->setFixedHeight(100); + tInnerVB->addWidget(m_Projectlistview); + connect(m_Projectlistview, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(onProjectlistviewClicked(QListWidgetItem*))); + + QLabel* m_LRecentProjects = new QLabel("Recent Projects", this); + m_LRecentProjects->setFont(Titlefont); + tInnerVB->addWidget(m_LRecentProjects); + + m_LNoRecentProjects = new QLabel("No recent projects", this); + m_LNoRecentProjects->setFont(Messagefont); + tInnerVB->addWidget(m_LNoRecentProjects); + m_LNoRecentProjects->hide(); + + m_RecentProjectlistFullPath.clear(); + m_RecentProjectlist.clear(); + m_RecentProjectsview = new QListWidget(); + m_RecentProjectsview->addItems(m_RecentProjectlist); + m_RecentProjectsview->setStyleSheet(PageListStyle); + m_RecentProjectsview->setFont(Selectfont); + m_RecentProjectsview->setEditTriggers(QAbstractItemView::NoEditTriggers); + tInnerVB->addWidget(m_RecentProjectsview); + connect(m_RecentProjectsview, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(onRecentProjectsviewClicked(QListWidgetItem*))); + } + + m_HSampleProjectsAndVersion->addLayout(tInnerVB); + + tInnerVB = new QVBoxLayout; + { + QString version = "Version "; + version.append(VERSION_TEXT_SHORT); + QLabel* m_LVersion = new QLabel(version, this); + m_LVersion->setFont(Titlefont); + tInnerVB->addWidget(m_LVersion); + + m_NewFeatureslist << "New Features"; + m_NewFeaturesview = new QListWidget(); + m_NewFeaturesview->addItems(m_NewFeatureslist); + m_NewFeaturesview->setFont(Selectfont); + m_NewFeaturesview->setStyleSheet(PageListStyle); + m_NewFeaturesview->setEditTriggers(QAbstractItemView::NoEditTriggers); + tInnerVB->addWidget(m_NewFeaturesview); + connect(m_NewFeaturesview, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(onNewFeaturesviewClicked(QListWidgetItem*))); + } + m_HSampleProjectsAndVersion->addLayout(tInnerVB); + } + + m_PButtonUserGuide = new QPushButton("User Guide", this); + QLabel* m_LBar1 = new QLabel("|", this); + m_PButtonGettingStarted = new QPushButton("Getting Started", this); + QLabel* m_LBar2 = new QLabel("|", this); + m_PButtonCompressonatorWeb = new QPushButton("Compressonator Web Site", this); + + m_LBar1->setFixedWidth(10); + m_LBar2->setFixedWidth(10); + + m_PButtonUserGuide->setMouseTracking(true); + + m_PButtonUserGuide->setFont(Selectfont); + m_PButtonGettingStarted->setFont(Selectfont); + m_PButtonCompressonatorWeb->setFont(Selectfont); + m_PButtonUserGuide->setStyleSheet("border-style: none;color:#551a8b;"); + m_PButtonGettingStarted->setStyleSheet("border-style: none;color:#551a8b;"); + m_PButtonCompressonatorWeb->setStyleSheet("border-style: none;color:#551a8b;"); + + m_HlayoutLinks->addWidget(m_PButtonUserGuide); + m_HlayoutLinks->addWidget(m_LBar1); + m_HlayoutLinks->addWidget(m_PButtonGettingStarted); + m_HlayoutLinks->addWidget(m_LBar2); + m_HlayoutLinks->addWidget(m_PButtonCompressonatorWeb); + + m_HlayoutLinks->setAlignment(Qt::AlignLeft); + + connect(m_PButtonUserGuide, SIGNAL(pressed()), this, SLOT(onPButtonUserGuide())); + connect(m_PButtonGettingStarted, SIGNAL(pressed()), this, SLOT(onPButtonGettingStarted())); + connect(m_PButtonCompressonatorWeb, SIGNAL(pressed()), this, SLOT(onPButtonCompressonatorWeb())); + + m_Vlayout->addLayout(m_HComp); + + m_Vlayout->addWidget(m_LBlankSpace); + m_Vlayout->addLayout(m_HSampleProjectsAndVersion); + m_Vlayout->addLayout(m_HlayoutLinks); + m_Vlayout->addWidget(m_LBlankSpace); + + setLayout(m_Vlayout); + + // parent->setStyleSheet("background-color:rgb(250, 250, 250);border:none;"); +#endif } -void cpStartupPage::onPageButtonClick(QString &Request, QString &Msg) { + +#ifndef USE_QTWEBENGINE + +void cpStartupPage::onPageButtonClick(QString& Request, QString& Msg) +{ emit PageButtonClick(Request, Msg); } -// --------------------------------------------------------------------------- -// Name: afStartupPage::~afStartupPage -// Description: Destructor -// Author: Sigal Algranaty -// Date: 22/2/2012 -// --------------------------------------------------------------------------- -cpStartupPage::~cpStartupPage() { +void cpStartupPage::onPButtonUserGuide() +{ + QString Request = "show_help"; + QString Msg = ""; + emit PageButtonClick(Request, Msg); +} + +void cpStartupPage::onPButtonGettingStarted() +{ + QString Request = "show_quick_start"; + QString Msg = ""; + emit PageButtonClick(Request, Msg); +} + +void cpStartupPage::onPButtonCompressonatorWeb() +{ + QString Request = "show_website"; + QString Msg = ""; + emit PageButtonClick(Request, Msg); +} + +void cpStartupPage::onProjectlistviewClicked(QListWidgetItem* item) +{ + QModelIndex index = m_Projectlistview->currentIndex(); + if (index.row() < m_Projectlist.size()) + { + QString Request = "open_project"; + QString Msg = m_Projectlist.at(index.row()); + emit PageButtonClick(Request, Msg); + } +} + +void cpStartupPage::onNewFeaturesviewClicked(QListWidgetItem* item) +{ + QModelIndex index = m_NewFeaturesview->currentIndex(); + if (index.row() < m_NewFeatureslist.size()) + { + QString Request = "show_newfeatures"; + QString Msg = m_NewFeatureslist.at(index.row()); + emit PageButtonClick(Request, Msg); + } +} + +void cpStartupPage::onRecentProjectsviewClicked(QListWidgetItem* item) +{ + QModelIndex index = m_RecentProjectsview->currentIndex(); + if (index.row() < m_RecentProjectlist.size()) + { + QString Request = "open_project"; + QString Msg = m_RecentProjectlist.at(index.row()); + emit PageButtonClick(Request, Msg); + } +} +#endif +cpStartupPage::~cpStartupPage() +{ } -// --------------------------------------------------------------------------- -// Name: afStartupPage::updateHTML -// Description: Update the HTML text with the current recently used projects -// Return Val: bool - Success / failure. -// Author: Sigal Algranaty -// Date: 21/2/2012 -// --------------------------------------------------------------------------- -bool cpStartupPage::UpdateHTML(QVector& projectsNames) { + +bool cpStartupPage::UpdateHTML(QVector& projectsNames) +{ bool retVal = true; +#ifndef USE_QTWEBENGINE + m_RecentProjectlist.clear(); + for (int i = 0; i < projectsNames.size(); i++) + { + QString rproject = projectsNames.at(i); + m_RecentProjectlistFullPath.append(rproject); + QFileInfo filePath(rproject); + // Get the project name: + QString wsName = filePath.baseName(); + QString str = wsName.trimmed(); + if (wsName.size() > 0) + m_RecentProjectlist.append(wsName); + } + m_RecentProjectsview->clear(); + if (m_RecentProjectlist.size() < 1) + m_LNoRecentProjects->show(); + else + { + m_LNoRecentProjects->hide(); + m_RecentProjectsview->addItems(m_RecentProjectlist); + } + +#else // Find the right HTML file name: QString fileName = "Welcome.html"; @@ -135,11 +364,13 @@ bool cpStartupPage::UpdateHTML(QVector& projectsNames) { // Load the file into a QString: appDir.append(compWelcomePagePath); std::string current_locale_text = appDir.toLocal8Bit().constData(); - QFile file(appDir); - bool rc = file.open(QIODevice::ReadOnly | QIODevice::Text); - if (rc) { + QFile file(appDir); + bool rc = file.open(QIODevice::ReadOnly | QIODevice::Text); + if (rc) + { QTextStream in(&file); - while (!in.atEnd()) { + while (!in.atEnd()) + { QString line = in.readLine(); htmlText.append(line); } @@ -151,94 +382,83 @@ bool cpStartupPage::UpdateHTML(QVector& projectsNames) { // Find the base URL (to enable links): QUrl baseUrl = QUrl::fromLocalFile(appDir); +#ifdef USE_QTWEBENGINE // Set the html text: setHtml(htmlText, baseUrl); +#endif } - +#endif return retVal; } -// --------------------------------------------------------------------------- -// Name: afStartupPage::setSource -// Description: Prevent Qt from activating links -// Arguments: const QUrl & name -// Author: Sigal Algranaty -// Date: 28/2/2012 -// --------------------------------------------------------------------------- -void cpStartupPage::setSource(const QUrl& name) { +void cpStartupPage::setSource(const QUrl& name) +{ (name); //qDebug() << "cpStartupPage::setSource"; } - -// --------------------------------------------------------------------------- -// Name: afStartupPage::BuildRecentlyOpenedProjectsTable -// Description: Append a table with the recently opened projects -// Arguments: QString& tableStr -// Author: Sigal Algranaty -// Date: 28/2/2012 -// --------------------------------------------------------------------------- -bool cpStartupPage::BuildRecentlyOpenedProjectsTable(QString& htmlText, QVector& recentlyUsedProjectsNames) { +bool cpStartupPage::BuildRecentlyOpenedProjectsTable(QString& htmlText, QVector& recentlyUsedProjectsNames) +{ bool retVal = false; - +#ifndef USE_QTWEBENGINE QString recentProjectsTableStr; QString appName; - int numberOfRecentProjects = (int)recentlyUsedProjectsNames.size(); + int numberOfRecentProjects = (int)recentlyUsedProjectsNames.size(); int projectsForDisplayNumber = (std::min)(numberOfRecentProjects, 10); - if (0 < projectsForDisplayNumber) { - for (int i = 0; i < projectsForDisplayNumber; i++) { + if (0 < projectsForDisplayNumber) + { + for (int i = 0; i < projectsForDisplayNumber; i++) + { // Get the current recent project path: QString currentProjectPath = recentlyUsedProjectsNames[i]; - if (QFile(recentlyUsedProjectsNames[i]).exists()) { + if (QFile(recentlyUsedProjectsNames[i]).exists()) + { // Build the file path from the project path: QFileInfo filePath(currentProjectPath); // Get the project name: - QString wsName = filePath.fileName(); + QString wsName = filePath.fileName(); QString tooltip1 = QString("%2")).arg(wsName).arg(wsName); + QString currentProjectCell = + tooltip1.append(QString("href=\"\" onclick = 'clickButton(\"" CP_STR_startup_page_openProjectLinkPrefix "%1\")'>%2")) + .arg(wsName) + .arg(wsName); recentProjectsTableStr.append(currentProjectCell); } } - } else { // 0 >= numberOfRecentProjects + } + else + { // 0 >= numberOfRecentProjects recentProjectsTableStr.append("

" CP_STR_startup_page_noRecentProjects "

"); } // Find the position of the recent projects table in the original HTML text: int tableHeadPos = htmlText.indexOf("", 0); - if (tableHeadPos > 0) { + if (tableHeadPos > 0) + { // Find the position of the table body: int tableBodyStartPos = htmlText.indexOf("", tableHeadPos + 1); - int tableBodyEndPos = htmlText.indexOf("", tableBodyStartPos + 1); + int tableBodyEndPos = htmlText.indexOf("", tableBodyStartPos + 1); - if((tableBodyStartPos > 0) && (tableBodyEndPos > 0) && (tableBodyEndPos > tableBodyStartPos)) { + if ((tableBodyStartPos > 0) && (tableBodyEndPos > 0) && (tableBodyEndPos > tableBodyStartPos)) + { // Get the length of the replaced string: - int len = tableBodyEndPos - tableBodyStartPos; + int len = tableBodyEndPos - tableBodyStartPos; htmlText = htmlText.replace(tableBodyStartPos + 8, len, recentProjectsTableStr); - retVal = true; + retVal = true; } } - +#endif return retVal; } -// --------------------------------------------------------------------------- -// Name: afStartupPage::canLinkBeClicked -// Description: If the operation requested by url requires stop debugging, ask -// the user how to behave -// Arguments: const QUrl& url -// Return Val: bool - true if operation can continue (debug/profile is stopped or wasn't on), false otherwise -// Author: Sigal Algranaty -// Date: 27/3/2012 -// --------------------------------------------------------------------------- -bool cpStartupPage::CanLinkBeClicked(const QUrl& url) { +bool cpStartupPage::CanLinkBeClicked(const QUrl& url) +{ Q_UNUSED(url); bool retVal = true; return retVal; } - - diff --git a/applications/compressonatorgui/components/cpstartuppage.h b/applications/compressonatorgui/components/cpstartuppage.h index dfa9af579..3eb4c6b5a 100644 --- a/applications/compressonatorgui/components/cpstartuppage.h +++ b/applications/compressonatorgui/components/cpstartuppage.h @@ -24,73 +24,92 @@ #ifndef __CPSTARTUPPAGE_H #define __CPSTARTUPPAGE_H +//#define USE_QTWEBENGINE + +#ifdef USE_QTWEBENGINE #include #include +#endif + #include #include -// ---------------------------------------------------------------------------------- -// Class Name: afWebPage: public QWebPage -// General Description: Inherit QWebPage. We need this class to override the JavaScript -// events since in the current HTML design, we need to use JS to -// execute CodeXL actions -// Author: Sigal Algranaty -// Creation Date: 23/9/2014 -// ---------------------------------------------------------------------------------- -class afWebPage : public QWebEnginePage { +#ifdef USE_QTWEBENGINE +class afWebPage : public QWebEnginePage +{ Q_OBJECT - public: - +public: // Constructor: afWebPage(QObject* pParent = nullptr); - Q_SIGNALS: - void PageButtonClick(QString &Request, QString &Msg); - +Q_SIGNALS: + void PageButtonClick(QString& Request, QString& Msg); - - protected: +protected: // Overrides QWebEnginePage: is used for catching requests from welcome page, and implement in Qt: virtual void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString& message, int lineNumber, const QString& sourceID); - virtual bool acceptNavigationRequest(const QUrl &url, NavigationType type, bool isMainFrame); + virtual bool acceptNavigationRequest(const QUrl& url, NavigationType type, bool isMainFrame); }; - -// ---------------------------------------------------------------------------------- -// Class Name: afStartupPage: public QWebView -// General Description: Inherit QWebView and is used for displaying the CodeXL HTML welcome page -// Author: Sigal Algranaty -// Creation Date: 23/9/2014 -// ---------------------------------------------------------------------------------- -class cpStartupPage : public QWebEngineView { +#endif + +class cpStartupPage +#ifdef USE_QTWEBENGINE + : public QWebEngineView +#else + : public QWidget +#endif +{ Q_OBJECT - public: - - cpStartupPage(QWidget *parent); +public: + cpStartupPage(QWidget* parent); virtual ~cpStartupPage(); bool UpdateHTML(QVector& projectsNames); - Q_SIGNALS: - void PageButtonClick(QString &Request, QString &Msg); +#ifndef USE_QTWEBENGINE + QPushButton* m_PButtonUserGuide; + QPushButton* m_PButtonGettingStarted; + QPushButton* m_PButtonCompressonatorWeb; - public slots: + QStringList m_Projectlist; + QListWidget* m_Projectlistview; + + QStringList m_RecentProjectlist; + QStringList m_RecentProjectlistFullPath; + QListWidget* m_RecentProjectsview; + QLabel* m_LNoRecentProjects; + QStringList m_NewFeatureslist; + QListWidget* m_NewFeaturesview; +#endif + +Q_SIGNALS: + void PageButtonClick(QString& Request, QString& Msg); + +public slots: virtual void setSource(const QUrl& name); - void onPageButtonClick(QString &Request, QString &Msg); - protected: +#ifndef USE_QTWEBENGINE + void onPageButtonClick(QString& Request, QString& Msg); + void onPButtonUserGuide(); + void onPButtonGettingStarted(); + void onPButtonCompressonatorWeb(); + void onProjectlistviewClicked(QListWidgetItem* item); + void onRecentProjectsviewClicked(QListWidgetItem* item); + void onNewFeaturesviewClicked(QListWidgetItem* item); +#endif + +protected: bool CanLinkBeClicked(const QUrl& url); - protected slots: +protected slots: /// Build the recently opened projects table, and replace it in the HTML text: /// \param htmlText the loaded HTML text (should contain dummy table for replacement) /// \return true for success (the text contain the expected table) bool BuildRecentlyOpenedProjectsTable(QString& htmlText, QVector& recentlyUsedProjectsNames); - }; -#endif //__CPSTARTUPPAGE_H - +#endif //__CPSTARTUPPAGE_H diff --git a/applications/compressonatorgui/components/cpwelcomepage.cpp b/applications/compressonatorgui/components/cpwelcomepage.cpp index acb27edfb..2bfea26d0 100644 --- a/applications/compressonatorgui/components/cpwelcomepage.cpp +++ b/applications/compressonatorgui/components/cpwelcomepage.cpp @@ -64,7 +64,9 @@ CWelcomePage::CWelcomePage(const QString title, QWidget *parent) void CWelcomePage::GoToAMDHomePage(QVector& projectsNames) { if (m_startUpPage) + { m_startUpPage->UpdateHTML(projectsNames); + } } CWelcomePage::~CWelcomePage() { diff --git a/applications/compressonatorgui/components/cpwelcomepage.h b/applications/compressonatorgui/components/cpwelcomepage.h index 3072c5438..9a34529b3 100644 --- a/applications/compressonatorgui/components/cpwelcomepage.h +++ b/applications/compressonatorgui/components/cpwelcomepage.h @@ -25,45 +25,48 @@ #define _WELCOMEPAGE_H #include + +#ifdef USE_QTWEBENGINE #include +#endif + #include "cpstartuppage.h" #include "accustomdockwidget.h" -class CWelcomePage : public acCustomDockWidget { +class CWelcomePage : public acCustomDockWidget +{ Q_OBJECT - public: - CWelcomePage(const QString title, QWidget *parent); +public: + CWelcomePage(const QString title, QWidget* parent); ~CWelcomePage(); void GoToAMDHomePage(QVector& projectsNames); - cpStartupPage *m_startUpPage; - acDockWidgetTitlebar *custTitleBar; + cpStartupPage* m_startUpPage; + acDockWidgetTitlebar* custTitleBar; - signals: +signals: - void WebPageButtonClick(QString &Request, QString &Msg); + void WebPageButtonClick(QString& Request, QString& Msg); - public slots: - void onWebPageButtonClick(QString &Request, QString &Msg); +public slots: + void onWebPageButtonClick(QString& Request, QString& Msg); - private: - - QString m_homePage; +private: + QString m_homePage; // Common for all - QWidget *m_newWidget; - QGridLayout *m_layout; - const QString m_title; - QWidget *m_parent; - + QWidget* m_newWidget; + QGridLayout* m_layout; + const QString m_title; + QWidget* m_parent; }; #define COMPRESSONATOR_HOME "http://gpuopen.com/gaming-product/compressonator/" -#define URL_FILE "file:///" -#define COMPRESSONATOR_GETTING_STARTED "Documents/gui_tool/getting_started/index.html" -#define COMPRESSONATOR_USER_GUIDE "Documents/gui_tool/user_guide/index.html" -#define COMPRESSONATOR_NEWFEATURES_GUIDE "Documents/revisions.html" +#define URL_FILE "file:///" +#define COMPRESSONATOR_GETTING_STARTED "Documents/gui_tool/getting_started/index.html" +#define COMPRESSONATOR_USER_GUIDE "Documents/gui_tool/user_guide/index.html" +#define COMPRESSONATOR_NEWFEATURES_GUIDE "Documents/revisions.html" #endif diff --git a/applications/compressonatorgui/compressonatorgui.qrc b/applications/compressonatorgui/compressonatorgui.qrc index 42187da05..905669838 100644 --- a/applications/compressonatorgui/compressonatorgui.qrc +++ b/applications/compressonatorgui/compressonatorgui.qrc @@ -1,58 +1,58 @@ - - Images/circle.png - Images/compress.png - Images/expand.png - Images/navigate.png - Images/open.png - Images/save.png - Images/blueStone.png - Images/greenStone.png - Images/redStone.png - Images/smallGreenStone.png - Images/smallRedStone.png - Images/settings.png - Images/filenew.png - Images/ZoomOut.png - Images/ZoomIn.png - Images/horizontal.png - Images/orilayout.png - Images/GridSolid.png - Images/MirrorHorizonal.png - Images/MirrorVertical.png - Images/file.png - Images/MIP.png - Images/delete.png - Images/CompressedImageError.png - Images/notsupportedImage.png - Images/imagediff.png - Images/plus.png - Images/ImageFileDoesNotExist.png - Images/brightnessup.png - Images/brightnessdown.png - Images/Gear.png - Images/OriginalImage.png - Images/cx100.png - Images/cxAlpha.png - Images/cxBlue.png - Images/cxFit.png - Images/cxgrayscale.png - Images/cxGreen.png - Images/cxRed.png - Images/cxInvert.png - Images/cxRotationL.png - Images/cxRotationR.png - Images/cxClose.png - Images/acompress-256.png - Images/DeCompressImageError.png - Images/OutOfMemoryError.png - Images/smallGrayStone.png - Images/smallWhiteBlank.png - Images/psnrmse.png - Images/ssim.png - Images/plusSettings.png - Resources/appicon.ico - Images/3Dfile.png - Images/3DModelConvert.png + + images/circle.png + images/compress.png + images/expand.png + images/navigate.png + images/open.png + images/save.png + images/bluestone.png + images/greenstone.png + images/redstone.png + images/smallgreenstone.png + images/smallredstone.png + images/settings.png + images/filenew.png + images/zoomout.png + images/zoomin.png + images/horizontal.png + images/orilayout.png + images/gridsolid.png + images/mirrorhorizonal.png + images/mirrorvertical.png + images/file.png + images/mip.png + images/delete.png + images/compressedimageerror.png + images/notsupportedimage.png + images/imagediff.png + images/plus.png + images/imagefiledoesnotexist.png + images/brightnessup.png + images/brightnessdown.png + images/gear.png + images/originalimage.png + images/cx100.png + images/cxalpha.png + images/cxblue.png + images/cxfit.png + images/cxgrayscale.png + images/cxgreen.png + images/cxred.png + images/cxinvert.png + images/cxrotationl.png + images/cxrotationr.png + images/cxclose.png + images/acompress-256.png + images/decompressimageerror.png + images/outofmemoryerror.png + images/smallgraystone.png + images/smallwhiteblank.png + images/psnrmse.png + images/ssim.png + images/plussettings.png + resources/appicon.ico + images/3dfile.png + images/3dmodelconvert.png diff --git a/applications/compressonatorgui/copyfiles.cmake b/applications/compressonatorgui/copyfiles.cmake index 159ca3fb6..9cca4f9f9 100644 --- a/applications/compressonatorgui/copyfiles.cmake +++ b/applications/compressonatorgui/copyfiles.cmake @@ -51,6 +51,7 @@ if (CMP_HOST_WINDOWS) cmp_gui_copy_to_output(${QT_LIB_DIR}/plugins/imageformats/qtiff$<$:d>.dll ${PLUGINS_PATH}/imagefomrats/qtiff$<$:d>.dll) cmp_gui_copy_to_output(${QT_LIB_DIR}/plugins/imageformats/qjpeg$<$:d>.dll ${PLUGINS_PATH}/imagefomrats/qjpeg$<$:d>.dll) cmp_gui_copy_to_output(${QT_LIB_DIR}/bin/QtWebEngineProcess$<$:d>.exe ${DYLIBS_PATH}/QtWebEngineProcess$<$:d>.exe) + cmp_gui_copy_to_output(${QT_LIB_DIR}/bin/Qt5QuickWidgets$<$:d>.dll ${DYLIBS_PATH}/Qt5QuickWidgets$<$:d>.dll) else () # Copy the platform libraries into the bundle file(GLOB_RECURSE QT_PLATFORM_LIBS ${QT_LIB_DIR}/plugins/platforms/*) @@ -105,8 +106,13 @@ cmp_gui_copy_to_output(${ExtGLEW_BIN_PATH}/glew32.dll ${ASSETS_PATH}/glew32.dll) cmp_gui_copy_to_output(${PROJECT_SOURCE_DIR}/../common/lib/ext/opencv/2.49/x64/VS2015/bin/$<$:debug>$<$:release>/opencv_core249$<$:d>.dll ${ASSETS_PATH}/opencv_core249$<$:d>.dll) cmp_gui_copy_to_output(${PROJECT_SOURCE_DIR}/../common/lib/ext/opencv/2.49/x64/VS2015/bin/$<$:debug>$<$:release>/opencv_imgproc249$<$:d>.dll ${ASSETS_PATH}/opencv_imgproc249$<$:d>.dll) -#KTX dll +#KTX2 Features dll +if (OPTION_BUILD_KTX2) cmp_gui_copy_to_output(${PROJECT_SOURCE_DIR}/../common/lib/ext/ktx/build/$<$:debug>$<$:release>/ktx.dll ${ASSETS_PATH}/ktx.dll) +else() +# Use a null dll so that installers can build +cmp_gui_copy_to_output(${PROJECT_SOURCE_DIR}/runtime/ktx.dll ${ASSETS_PATH}/ktx.dll) +endif() # GPU Shaders file(GLOB_RECURSE GPUCOMPUTE_SHADERS ${PROJECT_SOURCE_DIR}/cmp_core/shaders/*) diff --git a/applications/compressonatorgui/qpropertypages/cmakelists.txt b/applications/compressonatorgui/qpropertypages/cmakelists.txt index 59ca9d829..ed43d3e54 100644 --- a/applications/compressonatorgui/qpropertypages/cmakelists.txt +++ b/applications/compressonatorgui/qpropertypages/cmakelists.txt @@ -1,6 +1,4 @@ -add_library(CMP_GUI_BIN_QPropertyPages INTERFACE) - -target_sources(CMP_GUI_BIN_QPropertyPages INTERFACE +set(Q_PROPERTY_PAGES_SRC qtbuttonpropertybrowser.cpp qteditorfactory.cpp qtgroupboxpropertybrowser.cpp @@ -11,7 +9,7 @@ target_sources(CMP_GUI_BIN_QPropertyPages INTERFACE qtvariantproperty.cpp ) -target_sources(CMP_GUI_BIN_QPropertyPages INTERFACE +set(Q_PROPERTY_PAGES_H qtbuttonpropertybrowser.h qteditorfactory.h qtgroupboxpropertybrowser.h @@ -22,8 +20,10 @@ target_sources(CMP_GUI_BIN_QPropertyPages INTERFACE qtvariantproperty.h ) -target_include_directories(CMP_GUI_BIN_QPropertyPages INTERFACE +add_library(CMP_GUI_QPropertyPages STATIC ${Q_PROPERTY_PAGES_H} ${Q_PROPERTY_PAGES_SRC}) - ./ -) +target_link_libraries(CMP_GUI_QPropertyPages Qt5::Widgets) +set_target_properties(CMP_GUI_QPropertyPages PROPERTIES + AUTOMOC ON + FOLDER "Libs") diff --git a/applications/compressonatorgui/source/cpmaincomponents.cpp b/applications/compressonatorgui/source/cpmaincomponents.cpp index bed3b1a24..25e86b45b 100644 --- a/applications/compressonatorgui/source/cpmaincomponents.cpp +++ b/applications/compressonatorgui/source/cpmaincomponents.cpp @@ -46,13 +46,16 @@ bool g_bCompressing = false; // Set true when we are compressi extern CMIPS* g_GUI_CMIPS; // Hooked onto Progress Dialog. -void OnCancel() { +void OnCancel() +{ g_bAbortCompression = true; } // Hooked into g_GUI_CMIPS -void Print_onProgressDialog(char* str) { - if (g_pProgressDlg) { +void Print_onProgressDialog(char* str) +{ + if (g_pProgressDlg) + { if (!g_pProgressDlg->isVisible()) g_pProgressDlg->show(); @@ -61,12 +64,15 @@ void Print_onProgressDialog(char* str) { QCoreApplication::processEvents(); } -void Set_onProgressValue(unsigned int value, bool* canceled) { - if (g_pProgressDlg) { +void Set_onProgressValue(unsigned int value, bool* canceled) +{ + if (g_pProgressDlg) + { if (!g_pProgressDlg->isVisible()) g_pProgressDlg->show(); g_pProgressDlg->SetValue(value); - if (g_pProgressDlg->WasCanceled()) { + if (g_pProgressDlg->WasCanceled()) + { g_pProgressDlg->reset(); *canceled = true; } @@ -74,7 +80,8 @@ void Set_onProgressValue(unsigned int value, bool* canceled) { QCoreApplication::processEvents(); } -QString getFileExt(QString filePathName) { +QString getFileExt(QString filePathName) +{ QFileInfo fileInfo(filePathName); QString EXT = fileInfo.completeSuffix(); return EXT; @@ -82,7 +89,8 @@ QString getFileExt(QString filePathName) { //========================================================= cpMainComponents::cpMainComponents(QDockWidget* root_dock, QMainWindow* parent) - : QMainWindow(parent) { + : QMainWindow(parent) +{ if (parent == NULL) m_parent = this; else @@ -166,7 +174,8 @@ cpMainComponents::cpMainComponents(QDockWidget* root_dock, QMainWindow* parent) m_viewDiff = false; m_sSettingsFile = "CompressSettings.ini"; - if (m_showAppSettingsDialog) { + if (m_showAppSettingsDialog) + { m_setapplicationoptions = new CSetApplicationOptions("Application Settings", this); m_setapplicationoptions->resize(600, 500); connect(m_setapplicationoptions, SIGNAL(OnAppSettingHide()), this, SLOT(onWriteSettings())); @@ -184,8 +193,10 @@ cpMainComponents::cpMainComponents(QDockWidget* root_dock, QMainWindow* parent) m_blankpage->m_fileName = ""; setCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea); m_blankpage->setFeatures(QDockWidget::NoDockWidgetFeatures); + m_blankpage->setMinimumHeight(600); m_parent->addDockWidget(Qt::RightDockWidgetArea, m_blankpage); - if (m_blankpage->custTitleBar) { + if (m_blankpage->custTitleBar) + { m_blankpage->custTitleBar->setButtonCloseEnabled(false); m_blankpage->custTitleBar->setButtonToolBarShow(false); m_blankpage->lower(); @@ -196,7 +207,7 @@ cpMainComponents::cpMainComponents(QDockWidget* root_dock, QMainWindow* parent) // Status View when compressing m_CompressStatusDialog = new CompressStatusDialog("Output", this); setCorner(Qt::BottomRightCorner, Qt::BottomDockWidgetArea); - m_CompressStatusDialog->resize(300, 290); + m_CompressStatusDialog->resize(300, 90); m_parent->addDockWidget(Qt::BottomDockWidgetArea, m_CompressStatusDialog); m_CompressStatusDialog->hideOutput(); @@ -211,7 +222,8 @@ cpMainComponents::cpMainComponents(QDockWidget* root_dock, QMainWindow* parent) QString tempSetting = m_sSettingsFile; QFileInfo fileInfo(tempSetting); - if (!fileInfo.isWritable()) { + if (!fileInfo.isWritable()) + { QFileInfo fileInfo2(m_projectview->m_curProjectFilePathName); m_sSettingsFile = fileInfo2.dir().path(); m_sSettingsFile.append(QDir::separator()); @@ -228,6 +240,8 @@ cpMainComponents::cpMainComponents(QDockWidget* root_dock, QMainWindow* parent) m_welcomePage->m_fileName = ""; m_parent->tabifyDockWidget(m_blankpage, m_welcomePage); m_welcomePage->resize(600, 400); + m_welcomePage->setMinimumHeight(600); + if (m_welcomePage->custTitleBar) m_welcomePage->custTitleBar->setTitle(STR_WELCOME_PAGE); m_welcomePage->setAllowedAreas(Qt::RightDockWidgetArea); @@ -235,7 +249,8 @@ cpMainComponents::cpMainComponents(QDockWidget* root_dock, QMainWindow* parent) // Get the blank page tab and set disable property to hide it QTabBar* tabBar = this->findChild(); - if (tabBar) { + if (tabBar) + { tabBar->setStyleSheet("QTabBar::tab:disabled { width: 0; height: 0; margin: 0; padding: 0; border: none; }"); tabBar->setTabEnabled(0, false); } @@ -307,19 +322,22 @@ cpMainComponents::cpMainComponents(QDockWidget* root_dock, QMainWindow* parent) readSettings(); -// Set some global setting -#ifdef ENABLED_USER_GPUVIEW + // Set some global setting + //#ifdef ENABLED_USER_GPUVIEW g_useCPUDecode = (g_Application_Options.m_ImageViewDecode == C_Application_Options::ImageDecodeWith::CPU); -#else - g_useCPUDecode = true; -#endif + //#else + // g_useCPUDecode = true; + //#endif g_useCPUEncode = g_Application_Options.m_ImageEncode == C_Application_Options::ImageEncodeWith::CPU; setUnifiedTitleAndToolBarOnMac(true); - if (m_showAppSettingsDialog) { + if (m_showAppSettingsDialog) + { // Act on read settings for application startup - if (g_Application_Options.m_loadRecentFile) { - if (m_numRecentFiles > 0) { + if (g_Application_Options.m_loadRecentFile) + { + if (m_numRecentFiles > 0) + { recentFileActs[0]->trigger(); } } @@ -339,7 +357,7 @@ cpMainComponents::cpMainComponents(QDockWidget* root_dock, QMainWindow* parent) QString ver = QString("%1.%2.%3.%4") - .arg(QString::number(VERSION_MAJOR_MAJOR), QString::number(VERSION_MAJOR_MINOR), QString::number(VERSION_MINOR_MAJOR), QString::number(0)); + .arg(QString::number(VERSION_MAJOR_MAJOR), QString::number(VERSION_MAJOR_MINOR), QString::number(VERSION_MINOR_MAJOR), QString::number(0)); // Compression Connections @@ -360,7 +378,8 @@ cpMainComponents::cpMainComponents(QDockWidget* root_dock, QMainWindow* parent) // This replaces the one defined in ProjectView // Progress Dialog During Compression g_pProgressDlg = new acProgressDlg(this); - if (g_pProgressDlg) { + if (g_pProgressDlg) + { g_pProgressDlg->setWindowFlags(Qt::FramelessWindowHint | Qt::Window); g_pProgressDlg->ShowCancelButton(true, &OnCancel); g_pProgressDlg->SetHeader(""); @@ -369,7 +388,8 @@ cpMainComponents::cpMainComponents(QDockWidget* root_dock, QMainWindow* parent) g_pProgressDlg->hide(); // Configure printline to global shared CMIPS - if (g_GUI_CMIPS) { + if (g_GUI_CMIPS) + { g_GUI_CMIPS->PrintLine = Print_onProgressDialog; g_GUI_CMIPS->SetProgressValue = Set_onProgressValue; } @@ -377,12 +397,16 @@ cpMainComponents::cpMainComponents(QDockWidget* root_dock, QMainWindow* parent) } // Called when user clicks on a project view item -void cpMainComponents::onSetToolBarActions(int itemType) { +void cpMainComponents::onSetToolBarActions(int itemType) +{ // Enable delete button for any project view item if ((itemType == TREETYPE_3DMODEL_DATA) || (itemType == TREETYPE_3DSUBMODEL_DATA) || (itemType == TREETYPE_COMPRESSION_DATA) || - (itemType == TREETYPE_MESH_DATA) || (itemType == TREETYPE_IMAGEFILE_DATA)) { + (itemType == TREETYPE_MESH_DATA) || (itemType == TREETYPE_IMAGEFILE_DATA)) + { deleteImageAct->setEnabled(true); - } else { + } + else + { deleteImageAct->setEnabled(false); } @@ -393,112 +417,141 @@ void cpMainComponents::onSetToolBarActions(int itemType) { MIPGenAct->setEnabled(false); } -void cpMainComponents::SetProjectWindowTitle() { - if (m_projectview) { +void cpMainComponents::SetProjectWindowTitle() +{ + if (m_projectview) + { setWindowTitle(m_projectview->m_curProjectName); } } -void cpMainComponents::OnWelcomePageButtonClick(QString& Request, QString& Msg) { -#define PROJECT_DIR "/Projects/" +void cpMainComponents::OnWelcomePageButtonClick(QString& Request, QString& Msg) +{ +#define PROJECT_DIR "/projects/" - if (Request.compare("new_project") == 0) { + if (Request.compare("new_project") == 0) + { openNewProject(); - } else + } + else // qDebug() << Request << " Msg: " << Msg; - if (Request.compare("open_project") == 0) { - if (!m_projectview) - return; - if (!m_projectview->userSaveProjectAndContinue()) - return; + if (Request.compare("open_project") == 0) + { + if (!m_projectview) + return; + if (!m_projectview->userSaveProjectAndContinue()) + return; - bool found = false; - if (Msg.indexOf(".cprj") == -1) - Msg.append(PROJECT_EXTENSION); - - // Try the Current Path - if (!found) { - QString ProjectFile = QDir::currentPath(); - ProjectFile.append(PROJECT_DIR); - ProjectFile.append(Msg); - QFile Fout(ProjectFile); - if (Fout.exists()) { - m_projectview->loadProjectFile(ProjectFile); - found = true; - } + bool found = false; + if (Msg.indexOf(".cprj") == -1) + Msg.append(PROJECT_EXTENSION); + + // Try the Current Path + if (!found) + { + QString ProjectFile = QDir::currentPath(); + ProjectFile.append(PROJECT_DIR); + ProjectFile.append(Msg); + QFile Fout(ProjectFile); + if (Fout.exists()) + { + m_projectview->loadProjectFile(ProjectFile); + found = true; } + } - // Try the Application Dir - if (!found) { - // First Try looking for the project in sample folder - QString ProjectFile = qApp->applicationDirPath(); - ProjectFile.append(PROJECT_DIR); - ProjectFile.append(Msg); - QFile Fout(ProjectFile); - if (Fout.exists()) { - m_projectview->loadProjectFile(ProjectFile); - found = true; - } + // Try the Application Dir + if (!found) + { + // First Try looking for the project in sample folder + QString ProjectFile = qApp->applicationDirPath(); + ProjectFile.append(PROJECT_DIR); + ProjectFile.append(Msg); + QFile Fout(ProjectFile); + if (Fout.exists()) + { + m_projectview->loadProjectFile(ProjectFile); + found = true; } + } - // Try the Working Dir - if (!found) { - // This is windows specific! - QString pwd(""); - char* ENV; - // Check if user set our envniornment var - ENV = getenv(ENV_COMPRESSONATOR_ROOT); - if (ENV) - pwd.append(ENV); - else { - // check Pathname of the current working dir - ENV = getenv("PWD"); - } - if (ENV) { - pwd.append(PROJECT_DIR); - pwd.append(Msg); - QFile Fout(pwd); - if (Fout.exists()) { - m_projectview->loadProjectFile(pwd); - found = true; - } + // Try the Working Dir + if (!found) + { + // This is windows specific! + QString pwd(""); + char* ENV; + // Check if user set our envniornment var + ENV = getenv(ENV_COMPRESSONATOR_ROOT); + if (ENV) + pwd.append(ENV); + else + { + // check Pathname of the current working dir + ENV = getenv("PWD"); + } + if (ENV) + { + pwd.append(PROJECT_DIR); + pwd.append(Msg); + QFile Fout(pwd); + if (Fout.exists()) + { + m_projectview->loadProjectFile(pwd); + found = true; } } + } - // Try Recent Files - if (!found) { - for (int i = 0; i < m_numRecentFiles; i++) { - // The list is in &%1 %2 format so the first 3 char are removed - // Our max recents is 5 which is 1 char size - QString proj = recentFileActs[i]->text(); - proj.remove(0, 3); - if (Msg.compare(proj) == 0) { - recentFileActs[i]->trigger(); - found = true; - break; - } + // Try Recent Files + if (!found) + { + for (int i = 0; i < m_numRecentFiles; i++) + { + // The list is in &%1 %2 format so the first 3 char are removed + // Our max recents is 5 which is 1 char size + QString proj = recentFileActs[i]->text(); + proj.remove(0, 3); + if (Msg.compare(proj) == 0) + { + recentFileActs[i]->trigger(); + found = true; + break; } } + } - if (found) - setCurrentFile(m_projectview->m_curProjectFilePathName); - } else if (Request.compare("show_quick_start") == 0) { - OpenCHMFile(COMPRESSONATOR_GETTING_STARTED); - } else if (Request.compare("show_help") == 0) { - OpenCHMFile(COMPRESSONATOR_USER_GUIDE); - } else if (Request.compare("show_newfeatures") == 0) { - OpenCHMFile(COMPRESSONATOR_NEWFEATURES_GUIDE); + if (found) + { + statusBar()->showMessage("Project Location: "+m_projectview->m_curProjectFilePathName); + setCurrentFile(m_projectview->m_curProjectFilePathName); } + } + else if (Request.compare("show_quick_start") == 0) + { + OpenCHMFile(COMPRESSONATOR_GETTING_STARTED); + } + else if (Request.compare("show_help") == 0) + { + OpenCHMFile(COMPRESSONATOR_USER_GUIDE); + } + else if (Request.compare("show_newfeatures") == 0) + { + OpenCHMFile(COMPRESSONATOR_NEWFEATURES_GUIDE); + } } -void cpMainComponents::closeEvent(QCloseEvent* event) { +void cpMainComponents::closeEvent(QCloseEvent* event) +{ m_appclosing = true; - if (g_bCompressing) { + if (g_bCompressing) + { g_bAbortCompression = true; // loop until all compression codecs abort int maxwait = 3000; // > 3 seconds - while (g_bCompressing) { + while (g_bCompressing) + { std::this_thread::sleep_for(std::chrono::milliseconds(1)); maxwait--; if (maxwait == 0) @@ -511,8 +564,10 @@ void cpMainComponents::closeEvent(QCloseEvent* event) { CMP_ShutdownDecompessLibrary(); #endif - if (m_projectview) { - if (!m_projectview->userSaveProjectAndContinue()) { + if (m_projectview) + { + if (!m_projectview->userSaveProjectAndContinue()) + { event->ignore(); m_appclosing = false; return; @@ -527,72 +582,93 @@ void cpMainComponents::closeEvent(QCloseEvent* event) { event->accept(); } -void cpMainComponents::openProjectFile() { - if (m_projectview) { +void cpMainComponents::openProjectFile() +{ + if (m_projectview) + { m_projectview->openProjectFile(); setCurrentFile(m_projectview->m_curProjectFilePathName); } } -void cpMainComponents::openNewProject() { - if (m_projectview) { +void cpMainComponents::openNewProject() +{ + if (m_projectview) + { m_projectview->openNewProjectFile(); setCurrentFile(m_projectview->m_curProjectFilePathName); } } -bool cpMainComponents::saveProjectToBatchFile() { - if (m_projectview) { +bool cpMainComponents::saveProjectToBatchFile() +{ + if (m_projectview) + { m_projectview->saveToBatchFile(); } return true; } -void cpMainComponents::openImageFile() { - if (m_projectview) { +void cpMainComponents::openImageFile() +{ + if (m_projectview) + { m_projectview->OpenImageFile(); } } -void cpMainComponents::imageDiff() { - if (m_projectview) { +void cpMainComponents::imageDiff() +{ + if (m_projectview) + { m_projectview->diffImageFiles(); } } -void cpMainComponents::deleteImageFile() { - if (m_projectview) { +void cpMainComponents::deleteImageFile() +{ + if (m_projectview) + { m_projectview->UserDeleteItems(); } } -bool cpMainComponents::saveProjectFile() { - if (m_projectview) { +bool cpMainComponents::saveProjectFile() +{ + if (m_projectview) + { m_projectview->saveProjectFile(); setCurrentFile(m_projectview->m_curProjectFilePathName); } return true; } -bool cpMainComponents::saveAsProjectFile() { - if (m_projectview) { +bool cpMainComponents::saveAsProjectFile() +{ + if (m_projectview) + { m_projectview->saveAsProjectFile(); setCurrentFile(m_projectview->m_curProjectFilePathName); } return true; } -bool cpMainComponents::saveImageFile() { - if (m_projectview) { +bool cpMainComponents::saveImageFile() +{ + if (m_projectview) + { m_projectview->saveImageAs(); setCurrentFile(m_projectview->m_curProjectFilePathName); } return true; } -void cpMainComponents::settings() { - if (m_showAppSettingsDialog) { - if (m_setapplicationoptions) { +void cpMainComponents::settings() +{ + if (m_showAppSettingsDialog) + { + if (m_setapplicationoptions) + { m_setapplicationoptions->UpdateViewData(); m_setapplicationoptions->show(); m_setapplicationoptions->raise(); @@ -600,26 +676,32 @@ void cpMainComponents::settings() { } } -void cpMainComponents::about() { +void cpMainComponents::about() +{ if (m_pacHelpAboutDialog) m_pacHelpAboutDialog->show(); } -void cpMainComponents::onShowWelcomePage() { - if (m_welcomePage) { +void cpMainComponents::onShowWelcomePage() +{ + if (m_welcomePage) + { m_welcomePage->show(); m_welcomePage->raise(); } } -void cpMainComponents::onShowOutput() { - if (m_CompressStatusDialog) { +void cpMainComponents::onShowOutput() +{ + if (m_CompressStatusDialog) + { m_CompressStatusDialog->showOutput(); m_CompressStatusDialog->raise(); } } -void cpMainComponents::DeleteDock(acCustomDockWidget** dock) { +void cpMainComponents::DeleteDock(acCustomDockWidget** dock) +{ if (!(*dock)) return; (*dock)->close(); @@ -628,17 +710,22 @@ void cpMainComponents::DeleteDock(acCustomDockWidget** dock) { } // CLoses all Docked Views that have a file_name attached to them -void cpMainComponents::onCloseAllDocuments() { +void cpMainComponents::onCloseAllDocuments() +{ QList dockWidgets = m_parent->findChildren(); acCustomDockWidget* dock; - for (int i = 0; i < dockWidgets.size(); i++) { + for (int i = 0; i < dockWidgets.size(); i++) + { dock = dockWidgets[i]; - if (dock) { + if (dock) + { QString FileName = dock->m_fileName; - if (FileName.size() > 0) { + if (FileName.size() > 0) + { //skip diff subwindow which are already deleted - if (dock->m_type == TREETYPE_DIFFVIEW) { + if (dock->m_type == TREETYPE_DIFFVIEW) + { i += 3; } DeleteDock(&dock); @@ -647,104 +734,122 @@ void cpMainComponents::onCloseAllDocuments() { } } -void cpMainComponents::openRecentFile() { +void cpMainComponents::openRecentFile() +{ QAction* action = qobject_cast(sender()); - if (action) { - if (m_projectview) { - if (m_projectview->loadProjectFile(action->data().toString())) { + if (action) + { + if (m_projectview) + { + if (m_projectview->loadProjectFile(action->data().toString())) + { curFile = m_projectview->m_curProjectFilePathName; } } } } -void cpMainComponents::createActions() { - newProjectAct = new QAction(QIcon(":/CompressonatorGUI/Images/filenew.png"), tr("&New Project..."), this); - if (newProjectAct) { +void cpMainComponents::createActions() +{ + newProjectAct = new QAction(QIcon(":/compressonatorgui/images/filenew.png"), tr("&New Project..."), this); + if (newProjectAct) + { newProjectAct->setShortcuts(QKeySequence::New); newProjectAct->setStatusTip(tr("Create a new project file")); connect(newProjectAct, SIGNAL(triggered()), this, SLOT(openNewProject())); } - openAct = new QAction(QIcon(":/CompressonatorGUI/Images/open.png"), tr("&Open project..."), this); - if (openAct) { + openAct = new QAction(QIcon(":/compressonatorgui/images/open.png"), tr("&Open project..."), this); + if (openAct) + { openAct->setShortcuts(QKeySequence::Open); openAct->setStatusTip(tr("Open an existing project file")); connect(openAct, SIGNAL(triggered()), this, SLOT(openProjectFile())); } - saveAct = new QAction(QIcon(":/CompressonatorGUI/Images/save.png"), tr("&Save project"), this); - if (saveAct) { + saveAct = new QAction(QIcon(":/compressonatorgui/images/save.png"), tr("&Save project"), this); + if (saveAct) + { saveAct->setShortcuts(QKeySequence::Save); saveAct->setStatusTip(tr("Save project file")); connect(saveAct, SIGNAL(triggered()), this, SLOT(saveProjectFile())); } saveAsAct = new QAction(QIcon(""), tr("&Save project as..."), this); - if (saveAsAct) { + if (saveAsAct) + { saveAsAct->setShortcuts(QKeySequence::SaveAs); saveAsAct->setStatusTip(tr("Save project as ...")); connect(saveAsAct, SIGNAL(triggered()), this, SLOT(saveAsProjectFile())); } saveImageAct = new QAction(QIcon(""), tr("&Save image as..."), this); - if (saveImageAct) { + if (saveImageAct) + { saveImageAct->setStatusTip(tr("Save image as ...")); connect(saveImageAct, SIGNAL(triggered()), this, SLOT(saveImageFile())); saveImageAct->setEnabled(false); } - for (int i = 0; i < MaxRecentFiles; ++i) { + for (int i = 0; i < MaxRecentFiles; ++i) + { recentFileActs[i] = new QAction(this); recentFileActs[i]->setVisible(false); connect(recentFileActs[i], SIGNAL(triggered()), this, SLOT(openRecentFile())); } saveToBatchFileAct = new QAction(QIcon(""), tr("&Export to batch file..."), this); - if (saveToBatchFileAct) { + if (saveToBatchFileAct) + { saveToBatchFileAct->setStatusTip(tr("Export the project file to a command line batch file")); connect(saveToBatchFileAct, SIGNAL(triggered()), this, SLOT(saveProjectToBatchFile())); saveToBatchFileAct->setEnabled(false); } - openImageFileAct = new QAction(QIcon(":/CompressonatorGUI/Images/file.png"), tr("&Open Image File..."), this); - if (openImageFileAct) { + openImageFileAct = new QAction(QIcon(":/compressonatorgui/images/file.png"), tr("&Open Image File..."), this); + if (openImageFileAct) + { //ToDo :: openImageFileAct->setShortcuts(); openImageFileAct->setStatusTip(tr("Open an image file")); connect(openImageFileAct, SIGNAL(triggered()), this, SLOT(openImageFile())); } - deleteImageAct = new QAction(QIcon(":/CompressonatorGUI/Images/delete.png"), tr("&Delete current image"), this); - if (deleteImageAct) { + deleteImageAct = new QAction(QIcon(":/compressonatorgui/images/delete.png"), tr("&Delete current image"), this); + if (deleteImageAct) + { //ToDo :: deleteImageAct->setShortcuts(); deleteImageAct->setStatusTip(tr("Delete selected image file")); connect(deleteImageAct, SIGNAL(triggered()), this, SLOT(deleteImageFile())); deleteImageAct->setEnabled(false); } - compressAct = new QAction(QIcon(":/CompressonatorGUI/Images/compress.png"), tr("&Process selected images"), this); - if (compressAct) { + compressAct = new QAction(QIcon(":/compressonatorgui/images/compress.png"), tr("&Process selected images"), this); + if (compressAct) + { compressAct->setStatusTip(tr("Compress all selected items")); connect(compressAct, SIGNAL(triggered()), m_projectview, SLOT(OnStartCompression())); compressAct->setEnabled(false); } - imagediffAct = new QAction(QIcon(":/CompressonatorGUI/Images/imagediff.png"), tr("&View Image Diff"), this); - if (imagediffAct) { + imagediffAct = new QAction(QIcon(":/compressonatorgui/images/imagediff.png"), tr("&View Image Diff"), this); + if (imagediffAct) + { imagediffAct->setStatusTip(tr("View Image Diff")); connect(imagediffAct, SIGNAL(triggered()), this, SLOT(imageDiff())); imagediffAct->setEnabled(true); } - MIPGenAct = new QAction(QIcon(":/CompressonatorGUI/Images/MIP.png"), tr("&Generate MIP maps for current source image"), this); - if (MIPGenAct) { + MIPGenAct = new QAction(QIcon(":/compressonatorgui/images/mip.png"), tr("&Generate MIP maps for current source image"), this); + if (MIPGenAct) + { MIPGenAct->setStatusTip(tr("Generate MIP maps on current source image")); connect(MIPGenAct, SIGNAL(triggered()), this, SLOT(genMIPMaps())); MIPGenAct->setEnabled(false); } #ifdef USE_3DCONVERT - ConvertModelAct = new QAction(QIcon(":/CompressonatorGUI/Images/3DModelConvert.png"), tr("&Convert 3D Models to another format"), this); - if (ConvertModelAct) { + ConvertModelAct = new QAction(QIcon(":/compressonatorgui/images/3dmodelconvert.png"), tr("&Convert 3D Models to another format"), this); + if (ConvertModelAct) + { ConvertModelAct->setStatusTip(tr("Convert Model to another file format")); connect(ConvertModelAct, SIGNAL(triggered()), this, SLOT(convertModels())); ConvertModelAct->setEnabled(true); @@ -752,84 +857,97 @@ void cpMainComponents::createActions() { #endif #ifdef ENABLE_AGS_SUPPORT onHDRButton = new QPushButton("Full Screen", this); - if (onHDRButton) { + if (onHDRButton) + { onHDRButton->setStatusTip(tr("Sets Full screen on or off , If available HDR is turned on in Full Screen")); connect(onHDRButton, SIGNAL(released()), this, SLOT(handleHDRon())); } #endif #ifdef USE_MAIN_IMAVEVIEW_TOOLBAR - imageview_zoomInAct = new QAction(QIcon(":/CompressonatorGUI/Images/ZoomIn.png"), tr("&Zoom into Image "), this); - imageview_zoomOutAct = new QAction(QIcon(":/CompressonatorGUI/Images/ZoomOut.png"), tr("&Zoom out of Image"), this); - imageview_RedAct = new QAction(QIcon(":/CompressonatorGUI/Images/redStone.png"), tr("Show or Hide Red channel"), this); - imageview_GreenAct = new QAction(QIcon(":/CompressonatorGUI/Images/greenStone.png"), tr("Show or Hide Green channel"), this); - imageview_BlueAct = new QAction(QIcon(":/CompressonatorGUI/Images/blueStone.png"), tr("Show or Hide Blue channel"), this); - imageview_AlphaAct = new QAction(QIcon(":/CompressonatorGUI/Images/circle.png"), tr("Show or Hide Alpha channel"), this); - imageview_FitScreenAct = new QAction(QIcon(":/CompressonatorGUI/Images/expand.png"), tr("&Fit in Window"), this); + imageview_zoomInAct = new QAction(QIcon(":/compressonatorgui/images/zoomin.png"), tr("&Zoom into Image "), this); + imageview_zoomOutAct = new QAction(QIcon(":/compressonatorgui/images/zoomout.png"), tr("&Zoom out of Image"), this); + imageview_RedAct = new QAction(QIcon(":/compressonatorgui/images/redstone.png"), tr("Show or Hide Red channel"), this); + imageview_GreenAct = new QAction(QIcon(":/compressonatorgui/images/greenstone.png"), tr("Show or Hide Green channel"), this); + imageview_BlueAct = new QAction(QIcon(":/compressonatorgui/images/bluestone.png"), tr("Show or Hide Blue channel"), this); + imageview_AlphaAct = new QAction(QIcon(":/compressonatorgui/images/circle.png"), tr("Show or Hide Alpha channel"), this); + imageview_FitScreenAct = new QAction(QIcon(":/compressonatorgui/images/expand.png"), tr("&Fit in Window"), this); #endif exitAct = new QAction(QIcon(""), tr("&Exit"), this); - if (exitAct) { + if (exitAct) + { exitAct->setStatusTip(tr("Exit Application")); connect(exitAct, SIGNAL(triggered()), this, SLOT(close())); } - if (m_showAppSettingsDialog) { - settingsAct = new QAction(QIcon(":/CompressonatorGUI/Images/Gear.png"), tr("&Set Application Options"), this); - if (settingsAct) { + if (m_showAppSettingsDialog) + { + settingsAct = new QAction(QIcon(":/compressonatorgui/images/gear.png"), tr("&Set Application Options"), this); + if (settingsAct) + { settingsAct->setStatusTip(tr("Set Application Options")); connect(settingsAct, SIGNAL(triggered()), this, SLOT(settings())); } } showWelcomePageAct = new QAction(tr("Welcome Page"), this); - if (showWelcomePageAct) { + if (showWelcomePageAct) + { showWelcomePageAct->setStatusTip(tr("View Welcome Page")); connect(showWelcomePageAct, SIGNAL(triggered()), this, SLOT(onShowWelcomePage())); } showOutputAct = new QAction(tr("Output"), this); - if (showOutputAct) { + if (showOutputAct) + { showOutputAct->setStatusTip(tr("View Output Window")); connect(showOutputAct, SIGNAL(triggered()), this, SLOT(onShowOutput())); } closeAllDocuments = new QAction(tr("Close all Image Views"), this); - if (closeAllDocuments) { + if (closeAllDocuments) + { closeAllDocuments->setStatusTip(tr("Close all opened image views")); connect(closeAllDocuments, SIGNAL(triggered()), this, SLOT(onCloseAllDocuments())); } gettingStartedAct = new QAction(tr("Getting Started ..."), this); - if (gettingStartedAct) { + if (gettingStartedAct) + { gettingStartedAct->setStatusTip(tr("Getting Started")); connect(gettingStartedAct, SIGNAL(triggered()), this, SLOT(gettingStarted())); } userGuideAct = new QAction(tr("User Guide ..."), this); - if (userGuideAct) { + if (userGuideAct) + { userGuideAct->setStatusTip(tr("User Guide")); connect(userGuideAct, SIGNAL(triggered()), this, SLOT(userGuide())); } aboutAct = new QAction(tr("About Compressonator"), this); - if (aboutAct) { + if (aboutAct) + { aboutAct->setStatusTip(tr("About Compressonator")); connect(aboutAct, SIGNAL(triggered()), this, SLOT(about())); } newFeaturesAct = new QAction(tr("New Features ..."), this); - if (newFeaturesAct) { + if (newFeaturesAct) + { newFeaturesAct->setStatusTip(tr("New Features Guide")); connect(newFeaturesAct, SIGNAL(triggered()), this, SLOT(newFeaturesGuide())); } } -void cpMainComponents::onGenerateMIPMap(CMP_CFilterParams CFilterParams, QTreeWidgetItem* item) { +void cpMainComponents::onGenerateMIPMap(CMP_CFilterParams CFilterParams, QTreeWidgetItem* item) +{ if (!m_projectview) return; - if (item) { + if (item) + { QVariant v = item->data(TREE_SourceInfo, Qt::UserRole); C_Source_Info* data = v.value(); if (data == NULL) @@ -837,8 +955,10 @@ void cpMainComponents::onGenerateMIPMap(CMP_CFilterParams CFilterParams, QTreeWi if (data->m_MipImages) if (data->m_MipImages->mipset) - if (data->m_MipImages->mipset->m_compressed) { - if (m_CompressStatusDialog) { + if (data->m_MipImages->mipset->m_compressed) + { + if (m_CompressStatusDialog) + { m_CompressStatusDialog->onClearText(); m_CompressStatusDialog->showOutput(); } @@ -850,11 +970,14 @@ void cpMainComponents::onGenerateMIPMap(CMP_CFilterParams CFilterParams, QTreeWi if (CFilterParams.nMinSize <= 0) CFilterParams.nMinSize = 1; - if (item) { + if (item) + { QVariant v = item->data(TREE_SourceInfo, Qt::UserRole); C_Source_Info* data = v.value(); - if (data) { - if (m_CompressStatusDialog) { + if (data) + { + if (m_CompressStatusDialog) + { m_CompressStatusDialog->onClearText(); m_CompressStatusDialog->showOutput(); } @@ -863,10 +986,13 @@ void cpMainComponents::onGenerateMIPMap(CMP_CFilterParams CFilterParams, QTreeWi int min = data->m_Width; if (min > data->m_Height) min = data->m_Height; - if (CFilterParams.nMinSize < min) { + if (CFilterParams.nMinSize <= min) + { if (data->m_MipImages) - if (data->m_MipImages->mipset) { - if (m_CompressStatusDialog) { + if (data->m_MipImages->mipset) + { + if (m_CompressStatusDialog) + { m_CompressStatusDialog->onClearText(); m_CompressStatusDialog->show(); } @@ -877,7 +1003,9 @@ void cpMainComponents::onGenerateMIPMap(CMP_CFilterParams CFilterParams, QTreeWi // It also takes care of freeing any old mip levels that the use has not request to be kept // ie moving from a lower mip level to a higher one CMP_FreeMipSet((CMP_MipSet*)data->m_MipImages->mipset); - if (AMDLoadMIPSTextureImage(data->m_Full_Path.toStdString().c_str(), (CMP_MipSet*)data->m_MipImages->mipset, false, &g_pluginManager) != CMP_OK) { + if (AMDLoadMIPSTextureImage(data->m_Full_Path.toStdString().c_str(), (CMP_MipSet*)data->m_MipImages->mipset, false, &g_pluginManager) != + CMP_OK) + { // Something went wronge on reload exit! m_CompressStatusDialog->appendText("Error in reloading the impage for MIP Map processing! "); m_CompressStatusDialog->appendText("Please restart the application to restore the source image"); @@ -890,22 +1018,35 @@ void cpMainComponents::onGenerateMIPMap(CMP_CFilterParams CFilterParams, QTreeWi PluginInterface_Filters* g_plugin_MipMapFilter = reinterpret_cast(g_pluginManager.GetPlugin("FILTERS", "MIPMAP")); - if (g_plugin_MipMapFilter) { - // Init CMips & all devices - if (g_plugin_MipMapFilter->TC_PluginSetSharedIO(g_CMIPS) == CMP_OK) { - result = g_plugin_MipMapFilter->TC_CFilter((CMP_MipSet*)data->m_MipImages->mipset,NULL, &CFilterParams); - } else + if (g_plugin_MipMapFilter) + { + // Init Codec info IO + if ((g_CMIPS->PrintLine == NULL) && (PrintStatusLine != NULL)) + { + g_CMIPS->PrintLine = PrintStatusLine; + } + + if (g_plugin_MipMapFilter->TC_PluginSetSharedIO(g_CMIPS) == CMP_OK) + { + result = g_plugin_MipMapFilter->TC_CFilter((CMP_MipSet*)data->m_MipImages->mipset, NULL, &CFilterParams); + } + else result = CMP_ERR_GENERIC; - } else + + delete g_plugin_MipMapFilter; + } + else // Generate the MIP levels using Compressonator SDK result = CMP_GenerateMIPLevelsEx((CMP_MipSet*)data->m_MipImages->mipset, &CFilterParams); // Create Image views for the levels - if (result == CMP_OK) { + if (result == CMP_OK) + { CImageLoader ImageLoader; ImageLoader.UpdateMIPMapImages(data->m_MipImages); - if (m_CompressStatusDialog) { + if (m_CompressStatusDialog) + { QString msg = "Generated : "; msg.append(QString::number(data->m_MipImages->mipset->m_nMipLevels)); msg.append(" MIP level(s)"); @@ -915,7 +1056,9 @@ void cpMainComponents::onGenerateMIPMap(CMP_CFilterParams CFilterParams, QTreeWi m_CompressStatusDialog->appendText(msg); m_ForceImageRefresh = true; } - } else if (m_CompressStatusDialog) { + } + else if (m_CompressStatusDialog) + { m_CompressStatusDialog->appendText("Error in processing MipMap!"); } @@ -923,27 +1066,36 @@ void cpMainComponents::onGenerateMIPMap(CMP_CFilterParams CFilterParams, QTreeWi m_projectview->m_clicked_onIcon = true; m_projectview->onTree_ItemClicked(item, 0); } - } else { - if (m_CompressStatusDialog) { - m_CompressStatusDialog->appendText("No MIP levels generated: Please select a level lower than current image size"); + } + else + { + if (m_CompressStatusDialog) + { + m_CompressStatusDialog->appendText("No new MIP levels generated: if using GPU encoding make sure image sizes are divisible by 4"); } } } } } -void cpMainComponents::genMIPMaps() { - if (m_projectview) { +void cpMainComponents::genMIPMaps() +{ + if (m_projectview) + { QTreeWidgetItem* item = m_projectview->GetCurrentItem(TREETYPE_IMAGEFILE_DATA); - if (item) { + if (item) + { QString Setting = item->text(0); QVariant v = item->data(TREE_SourceInfo, Qt::UserRole); C_Source_Info* data = v.value(); - if (data) { + if (data) + { // regenrate mip map - if (data->m_MipImages->mipset->m_nMipLevels > 1 || data->m_MipImages->QImage_list[0].size() > 1) { + if (data->m_MipImages->mipset->m_nMipLevels > 1 || data->m_MipImages->QImage_list[0].size() > 1) + { int n = data->m_MipImages->QImage_list[0].size(); - for (int i = 1; i < n; i++) { + for (int i = 1; i < n; i++) + { data->m_MipImages->QImage_list[0].pop_back(); } @@ -952,42 +1104,42 @@ void cpMainComponents::genMIPMaps() { m_genmips->m_mipsitem = item; m_genmips->setMipLevelDisplay(data->m_Width, data->m_Height, g_Application_Options.isGPUEncode()); + QString title = "Generate MIP Maps for "; + if (g_Application_Options.isGPUEncode()) + title.append("GPU"); + else + title.append("CPU"); + m_genmips->setWindowTitle(title); m_genmips->show(); - - // Generate mipmap only once- no regenerate then uncomment code block below - //if (data->m_MipImages->mipset->m_nMipLevels <= 1) - //{ - // m_genmips->setMipLevelDisplay(data->m_Width, data->m_Height); - // m_genmips->show(); - //} - //else - //{ - // QMessageBox msgBox; - // msgBox.setText("The image already has MIP levels!"); - // msgBox.setStandardButtons(QMessageBox::Ok); - // msgBox.exec(); - //} - } else { - if (m_projectview) { + } + else + { + if (m_projectview) + { QTreeWidgetItemIterator it(m_projectview->m_projectTreeView); QString Setting = (*it)->text(0); ++it; //skip add image node - if (!(*it)) { + if (!(*it)) + { m_projectview->m_CompressStatusDialog->appendText("Please add the image file that you would like to generate mip map with."); m_projectview->m_CompressStatusDialog->show(); return; } - while (*it) { + while (*it) + { QVariant v = (*it)->data(TREE_SourceInfo, Qt::UserRole); QString Setting = (*it)->text(0); //if (levelType == TREETYPE_IMAGEFILE_DATA) //{ C_Source_Info* data = v.value(); - if (data) { + if (data) + { // regenrate mip map - if (data->m_MipImages->mipset->m_nMipLevels > 1 || data->m_MipImages->QImage_list[0].size() > 1) { + if (data->m_MipImages->mipset->m_nMipLevels > 1 || data->m_MipImages->QImage_list[0].size() > 1) + { int n = data->m_MipImages->QImage_list[0].size(); - for (int i = 1; i < n; i++) { + for (int i = 1; i < n; i++) + { data->m_MipImages->QImage_list[0].pop_back(); } @@ -1006,8 +1158,11 @@ void cpMainComponents::genMIPMaps() { } } } - } else { - if (m_projectview) { + } + else + { + if (m_projectview) + { m_projectview->m_CompressStatusDialog->appendText("Please add the image file that you would like to generate mip map with."); m_projectview->m_CompressStatusDialog->show(); } @@ -1015,25 +1170,33 @@ void cpMainComponents::genMIPMaps() { } } -void cpMainComponents::convertModels() { - if (m_projectview) { +void cpMainComponents::convertModels() +{ + if (m_projectview) + { QString sourceFileName = ""; QTreeWidgetItem* item = m_projectview->GetCurrentItem(); - if (item) { + if (item) + { QVariant v = item->data(TREE_LevelType, Qt::UserRole); int levelType = v.toInt(); v = item->data(TREE_SourceInfo, Qt::UserRole); - switch (levelType) { - case TREETYPE_3DMODEL_DATA: { + switch (levelType) + { + case TREETYPE_3DMODEL_DATA: + { C_3DModel_Info* m_data = v.value(); - if (m_data) { + if (m_data) + { sourceFileName = m_data->m_Full_Path; } } break; - case TREETYPE_3DSUBMODEL_DATA: { + case TREETYPE_3DSUBMODEL_DATA: + { C_3DSubModel_Info* m_data = v.value(); - if (m_data) { + if (m_data) + { sourceFileName = m_data->m_Full_Path; } } @@ -1050,11 +1213,13 @@ void cpMainComponents::convertModels() { #ifdef ENABLE_AGS_SUPPORT //-------------------------------------------------------------------------------------- -void cpMainComponents::AGSGetDisplayInfo(AGSDisplaySettings* settings) { +void cpMainComponents::AGSGetDisplayInfo(AGSDisplaySettings* settings) +{ int displayIndex = 0; DISPLAY_DEVICEA displayDevice; displayDevice.cb = sizeof(displayDevice); - while (EnumDisplayDevicesA(0, displayIndex, &displayDevice, 0)) { + while (EnumDisplayDevicesA(0, displayIndex, &displayDevice, 0)) + { displayIndex++; } @@ -1063,12 +1228,16 @@ void cpMainComponents::AGSGetDisplayInfo(AGSDisplaySettings* settings) { AGSConfiguration config = {}; config.crossfireMode = AGS_CROSSFIRE_MODE_EXPLICIT_AFR; - if (agsInit(&m_agsContext, &config, &gpuInfo) == AGS_SUCCESS) { - for (int gpuIndex = 0; gpuIndex < gpuInfo.numDevices; gpuIndex++) { + if (agsInit(&m_agsContext, &config, &gpuInfo) == AGS_SUCCESS) + { + for (int gpuIndex = 0; gpuIndex < gpuInfo.numDevices; gpuIndex++) + { const AGSDeviceInfo& device = gpuInfo.devices[gpuIndex]; - for (int i = 0; i < device.numDisplays; i++) { + for (int i = 0; i < device.numDisplays; i++) + { const AGSDisplayInfo& display = device.displays[i]; - if (display.displayFlags & AGS_DISPLAYFLAG_PRIMARY_DISPLAY && display.displayFlags & AGS_DISPLAYFLAG_HDR10) { + if (display.displayFlags & AGS_DISPLAYFLAG_PRIMARY_DISPLAY && display.displayFlags & AGS_DISPLAYFLAG_HDR10) + { settings->chromaticityRedX = display.chromaticityRedX; ///< Red display primary X coord settings->chromaticityRedY = display.chromaticityRedY; ///< Red display primary Y coord @@ -1095,23 +1264,31 @@ void cpMainComponents::AGSGetDisplayInfo(AGSDisplaySettings* settings) { } } -bool cpMainComponents::AGSSetDisplay(AGSDisplaySettings* settings) { - if (AGS_SUCCESS == agsSetDisplayMode(m_agsContext, m_DeviceIndex, m_DisplayIndex, settings)) { +bool cpMainComponents::AGSSetDisplay(AGSDisplaySettings* settings) +{ + if (AGS_SUCCESS == agsSetDisplayMode(m_agsContext, m_DeviceIndex, m_DisplayIndex, settings)) + { statusBar()->showMessage(tr("HDR enabled.")); return true; - } else { + } + else + { statusBar()->showMessage(tr("Set HDR fail.")); return false; } } //--------------------------------------------------------------------------------- -void cpMainComponents::handleHDRon() { - if (!m_bIsFullScreenModeOn) { +void cpMainComponents::handleHDRon() +{ + if (!m_bIsFullScreenModeOn) + { this->showFullScreen(); m_bIsFullScreenModeOn = true; onHDRButton->setText("Normal Screen"); - } else { + } + else + { this->showNormal(); if (m_bIsHDRAvailableOnPrimary) onHDRButton->setText("Full Screen with HDR"); @@ -1122,23 +1299,30 @@ void cpMainComponents::handleHDRon() { // This part of the code could also be another button! // that is enabled if HDR is available on the primary display - if (m_bIsHDRAvailableOnPrimary) { - if (m_bIsFullScreenModeOn) { + if (m_bIsHDRAvailableOnPrimary) + { + if (m_bIsFullScreenModeOn) + { // HDR On m_settings.mode = AGSDisplaySettings::Mode_scRGB; - if (AGSSetDisplay(&m_settings)) { + if (AGSSetDisplay(&m_settings)) + { } - } else { + } + else + { // HDR Off m_settings.mode = AGSDisplaySettings::Mode_SDR; - if (AGSSetDisplay(&m_settings)) { + if (AGSSetDisplay(&m_settings)) + { } } } } #endif -void cpMainComponents::setCurrentFile(const QString& fileName) { +void cpMainComponents::setCurrentFile(const QString& fileName) +{ curFile = fileName; setWindowFilePath(curFile); @@ -1154,7 +1338,8 @@ void cpMainComponents::setCurrentFile(const QString& fileName) { SetProjectWindowTitle(); } -void cpMainComponents::updateRecentFileActions() { +void cpMainComponents::updateRecentFileActions() +{ QSettings settings(m_sSettingsFile, QSettings::IniFormat); QStringList files = settings.value("recentFileList").toStringList(); QStringList UpdatedList; @@ -1163,18 +1348,21 @@ void cpMainComponents::updateRecentFileActions() { int scan_numRecentFiles = qMin(files.size(), (int)MaxRecentFiles); UpdatedList.clear(); - for (int i = 0; i < scan_numRecentFiles; ++i) { + for (int i = 0; i < scan_numRecentFiles; ++i) + { // Avoid adding duplicates // and fix up file paths to use "/" notation! files[i].replace("\\", "/"); - if (m_projectsRecentFiles.contains(files[i])) { + if (m_projectsRecentFiles.contains(files[i])) + { continue; } m_projectsRecentFiles.push_back(files[i]); QFile file(files[i]); - if (file.exists()) { + if (file.exists()) + { QString text = tr("&%1 %2").arg(numRecentFile + 1).arg(strippedName(files[i])); recentFileActs[numRecentFile]->setText(text); recentFileActs[numRecentFile]->setData(files[i]); @@ -1195,22 +1383,29 @@ void cpMainComponents::updateRecentFileActions() { separatorAct->setVisible(m_numRecentFiles > 0); } -QString cpMainComponents::strippedName(const QString& fullFileName) { +QString cpMainComponents::strippedName(const QString& fullFileName) +{ return QFileInfo(fullFileName).fileName(); } -void cpMainComponents::onAboutToShowFileMenu() { - if (m_projectview && saveToBatchFileAct) { - if ((m_projectview->m_NumItems > 2) && m_projectview->AnySelectedItems()) { +void cpMainComponents::onAboutToShowFileMenu() +{ + if (m_projectview && saveToBatchFileAct) + { + if ((m_projectview->m_NumItems > 2) && m_projectview->AnySelectedItems()) + { saveToBatchFileAct->setEnabled(true); - } else + } + else saveToBatchFileAct->setEnabled(false); } } -void cpMainComponents::createMenus() { +void cpMainComponents::createMenus() +{ fileMenu = menuBar()->addMenu(tr("&File")); - if (fileMenu) { + if (fileMenu) + { if (newProjectAct) fileMenu->addAction(newProjectAct); if (openAct) @@ -1238,16 +1433,19 @@ void cpMainComponents::createMenus() { connect(fileMenu, SIGNAL(aboutToShow()), this, SLOT(onAboutToShowFileMenu())); } - if (m_showAppSettingsDialog) { + if (m_showAppSettingsDialog) + { settingsMenu = menuBar()->addMenu(tr("&Settings")); - if (settingsMenu) { + if (settingsMenu) + { if (settingsAct) settingsMenu->addAction(settingsAct); } } windowMenu = menuBar()->addMenu(tr("&Window")); - if (windowMenu) { + if (windowMenu) + { if (showWelcomePageAct) windowMenu->addAction(showWelcomePageAct); if (showOutputAct) @@ -1257,7 +1455,8 @@ void cpMainComponents::createMenus() { } helpMenu = menuBar()->addMenu(tr("&Help")); - if (helpMenu) { + if (helpMenu) + { if (gettingStartedAct) helpMenu->addAction(gettingStartedAct); if (userGuideAct) @@ -1267,15 +1466,18 @@ void cpMainComponents::createMenus() { } } -void cpMainComponents::menuItemClicked(QAction* triggeredAction) { +void cpMainComponents::menuItemClicked(QAction* triggeredAction) +{ // use either the action itself... or an offset int value = triggeredAction->data().toInt(); Q_UNUSED(value); } -void cpMainComponents::createToolBars() { +void cpMainComponents::createToolBars() +{ fileToolBar = addToolBar(tr("File")); - if (fileToolBar) { + if (fileToolBar) + { if (newProjectAct) fileToolBar->addAction(newProjectAct); if (openAct) @@ -1293,7 +1495,8 @@ void cpMainComponents::createToolBars() { } CompressionToolBar = addToolBar(tr("Compression")); - if (CompressionToolBar) { + if (CompressionToolBar) + { if (compressAct) CompressionToolBar->addAction(compressAct); if (imagediffAct) @@ -1308,7 +1511,8 @@ void cpMainComponents::createToolBars() { #ifdef USE_3DCONVERT CompressionToolBar = addToolBar(tr("3D Models")); - if (CompressionToolBar) { + if (CompressionToolBar) + { if (ConvertModelAct) CompressionToolBar->addAction(ConvertModelAct); } @@ -1316,7 +1520,8 @@ void cpMainComponents::createToolBars() { #ifdef USE_MAIN_IMAVEVIEW_TOOLBAR ImageViewToolBar = addToolBar(tr("Image View")); - if (ImageViewToolBar) { + if (ImageViewToolBar) + { if (imageview_zoomInAct) ImageViewToolBar->addAction(imageview_zoomInAct); if (imageview_zoomOutAct) @@ -1337,12 +1542,14 @@ void cpMainComponents::createToolBars() { #endif } -void cpMainComponents::createStatusBar() { +void cpMainComponents::createStatusBar() +{ statusBar()->setStyleSheet("QStatusBar{border-top: 1px outset grey;}"); statusBar()->showMessage(tr("Ready")); } -void cpMainComponents::showProgressBusy(QString Message) { +void cpMainComponents::showProgressBusy(QString Message) +{ if (m_appclosing) return; @@ -1353,7 +1560,8 @@ void cpMainComponents::showProgressBusy(QString Message) { m_projectview->m_processBusy = true; } -void cpMainComponents::hideProgressBusy(QString Message) { +void cpMainComponents::hideProgressBusy(QString Message) +{ if (m_appclosing) return; @@ -1364,29 +1572,34 @@ void cpMainComponents::hideProgressBusy(QString Message) { m_projectview->m_processBusy = false; } -void cpMainComponents::readSettings() { +void cpMainComponents::readSettings() +{ QSettings settings(m_sSettingsFile, QSettings::IniFormat); QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint(); QSize size = settings.value("size", QSize(800, 600)).toSize(); resize(size); move(pos); - if (m_showAppSettingsDialog) { + if (m_showAppSettingsDialog) + { m_setapplicationoptions->LoadSettings(m_sSettingsFile, QSettings::IniFormat); } m_projectview->m_curProjectFilePathName = settings.value("ProjectPath").toString(); } -void cpMainComponents::onWriteSettings() { +void cpMainComponents::onWriteSettings() +{ QSettings settings(m_sSettingsFile, QSettings::IniFormat); settings.setValue("pos", pos()); settings.setValue("size", size()); - if (m_showAppSettingsDialog) { + if (m_showAppSettingsDialog) + { m_setapplicationoptions->SaveSettings(m_sSettingsFile, QSettings::IniFormat); } settings.setValue("ProjectPath", m_projectview->m_curProjectFilePathName); } -acCustomDockWidget* cpMainComponents::FindImageView(QString& file, bool findDiffs) { +acCustomDockWidget* cpMainComponents::FindImageView(QString& file, bool findDiffs) +{ QList dockWidgets = m_parent->findChildren(); if (dockWidgets.size() == 0) @@ -1406,15 +1619,21 @@ acCustomDockWidget* cpMainComponents::FindImageView(QString& file, bool findDiff ***********************/ QListIterator iter(dockWidgets); - while (iter.hasNext()) { + while (iter.hasNext()) + { dock = iter.next(); QString dock_fileName = dock->m_fileName; int res = file.compare(dock_fileName); - if ((res == 0) && (!findDiffs)) { + if ((res == 0) && (!findDiffs)) + { return dock; - } else { - if (findDiffs) { - if (dock_fileName.contains(DIFFERENCE_IMAGE_VS_TXT)) { + } + else + { + if (findDiffs) + { + if (dock_fileName.contains(DIFFERENCE_IMAGE_VS_TXT)) + { return dock; } } @@ -1425,17 +1644,21 @@ acCustomDockWidget* cpMainComponents::FindImageView(QString& file, bool findDiff } // Active when user selects any Docked Tabbed Item -void cpMainComponents::onDockImageVisibilityChanged(bool visible) { +void cpMainComponents::onDockImageVisibilityChanged(bool visible) +{ Q_UNUSED(visible); QTabBar* tabBar = this->findChild(); - if (tabBar) { + if (tabBar) + { int currentIndex = tabBar->currentIndex(); QString tabText = tabBar->tabText(currentIndex); int numTab = tabBar->count(); - for (int i = 0; i < numTab; i++) { - if (tabBar->tabText(i).compare("") == 0) { + for (int i = 0; i < numTab; i++) + { + if (tabBar->tabText(i).compare("") == 0) + { tabBar->setStyleSheet("QTabBar::tab:disabled { width: 0; height: 0; margin: 0; padding: 0; border: none; }"); tabBar->setTabEnabled(i, false); } @@ -1443,16 +1666,21 @@ void cpMainComponents::onDockImageVisibilityChanged(bool visible) { const QList tabedWidgets = tabifiedDockWidgets(m_blankpage); QDockWidget* item; - foreach (item, tabedWidgets) { + foreach (item, tabedWidgets) + { // Get our custom Dock Widget - if (item->titleBarWidget()) { + if (item->titleBarWidget()) + { acCustomDockWidget* imageItem = reinterpret_cast(item); - if (imageItem && !m_viewDiff) { - if (imageItem->m_tabName.compare("") == 0) { + if (imageItem && !m_viewDiff) + { + if (imageItem->m_tabName.compare("") == 0) + { imageItem->lower(); } - if (imageItem->m_tabName.compare(tabText) == 0) { + if (imageItem->m_tabName.compare(tabText) == 0) + { emit SetCurrentItem(imageItem->m_fileName); } } @@ -1461,12 +1689,15 @@ void cpMainComponents::onDockImageVisibilityChanged(bool visible) { } } -void cpMainComponents::AddImageCompSettings(QTreeWidgetItem* item, C_Destination_Options& data) { +void cpMainComponents::AddImageCompSettings(QTreeWidgetItem* item, C_Destination_Options& data) +{ if (!item) return; - try { - if (data.m_editing) { + try + { + if (data.m_editing) + { QVariant v = item->data(TREE_SourceInfo, Qt::UserRole); C_Destination_Options* m_data = v.value(); *m_data << data; @@ -1484,9 +1715,9 @@ void cpMainComponents::AddImageCompSettings(QTreeWidgetItem* item, C_Destination else m_data->m_FileSizeStr = QString().number(m_data->m_FileSize) + " Bytes"; if (file.exists() && (fileinfo.suffix().length() > 0)) - item->setIcon(0, QIcon(":/CompressonatorGUI/Images/smallGrayStone.png")); + item->setIcon(0, QIcon(":/compressonatorgui/images/smallgraystone.png")); else - item->setIcon(0, QIcon(":/CompressonatorGUI/Images/smallWhiteBlank.png")); + item->setIcon(0, QIcon(":/compressonatorgui/images/smallwhiteblank.png")); // refresh the current Image property view (It may or maynot be pointing to The compression data // That was edited. @@ -1495,23 +1726,30 @@ void cpMainComponents::AddImageCompSettings(QTreeWidgetItem* item, C_Destination // Refresh the image m_projectview->m_clicked_onIcon = true; m_projectview->onTree_ItemClicked(item, 0); - } else { + } + else + { // Check who called the [+] add setting QString itemName; itemName = item->text(0); QVariant v = item->data(TREE_LevelType, Qt::UserRole); int levelType = v.toInt(); - switch (levelType) { - case TREETYPE_Add_destination_setting: { - if (data.m_isModelData) { // Adding a Mesh Buffer Data Node + switch (levelType) + { + case TREETYPE_Add_destination_setting: + { + if (data.m_isModelData) + { // Adding a Mesh Buffer Data Node C_Destination_Options* m_data = new C_Destination_Options; // copy the new data from compress settings dialog data *m_data << data; if (!m_data) return; m_projectview->Tree_AddCompressFile(item, m_data->m_compname, true, true, TREETYPE_MESH_DATA, m_data); - } else { // Adding a Image Data Node to either transcode or compress + } + else + { // Adding a Image Data Node to either transcode or compress C_Destination_Options* m_data = new C_Destination_Options; // copy the new data from compress settings dialog data *m_data << data; @@ -1521,7 +1759,8 @@ void cpMainComponents::AddImageCompSettings(QTreeWidgetItem* item, C_Destination } break; } - case TREETYPE_Add_Model_destination_settings: { + case TREETYPE_Add_Model_destination_settings: + { C_Destination_Options* m_data = new C_Destination_Options; // copythe new data from comsettings dialog data *m_data << data; @@ -1529,38 +1768,47 @@ void cpMainComponents::AddImageCompSettings(QTreeWidgetItem* item, C_Destination return; QString DestfilePathName = m_data->m_destFileNamePath; - if (QFile::exists(m_data->m_destFileNamePath)) { + if (QFile::exists(m_data->m_destFileNamePath)) + { QFile::remove(m_data->m_destFileNamePath); } bool isCopy = QFile::copy(m_data->m_sourceFileNamePath, m_data->m_destFileNamePath); - if (!isCopy) { + if (!isCopy) + { QString error = "Error: Create " + (m_data->m_destFileNamePath) + " failed. Please make sure you have write permission to the destination path.\n"; PrintInfo(error.toStdString().c_str()); - if (m_CompressStatusDialog) { + if (m_CompressStatusDialog) + { m_CompressStatusDialog->showOutput(); } return; } #ifdef _WIN32 - if (m_data->m_destFileNamePath.contains(".obj") || m_data->m_destFileNamePath.contains(".OBJ")) { + if (m_data->m_destFileNamePath.contains(".obj") || m_data->m_destFileNamePath.contains(".OBJ")) + { //write to indicate the state of the file writeObjFileState(m_data->m_destFileNamePath.toStdString(), CMP_COPY); } #endif QTreeWidgetItem* ParentItem = item->parent(); - if (ParentItem) { + if (ParentItem) + { QString itemName = ParentItem->text(0); m_projectview->Tree_Add3DSubModelFile(ParentItem, DestfilePathName, NULL); } break; } - case TREETYPE_IMAGEFILE_DATA: { //for case no image item selected,only work for imagefile item - if (data.m_isModelData) { + case TREETYPE_IMAGEFILE_DATA: + { //for case no image item selected,only work for imagefile item + if (data.m_isModelData) + { return; - } else { // Adding a Image Data Node for compress + } + else + { // Adding a Image Data Node for compress C_Destination_Options* m_data = new C_Destination_Options; // copy the new data from compress settings dialog data *m_data << data; @@ -1574,30 +1822,42 @@ void cpMainComponents::AddImageCompSettings(QTreeWidgetItem* item, C_Destination break; } } - } catch (...) { + } + catch (...) + { DisplayException(""); } } -void cpMainComponents::DisplayException(QString msgTitle) { - if (m_CompressStatusDialog) { +void cpMainComponents::DisplayException(QString msgTitle) +{ + if (m_CompressStatusDialog) + { m_CompressStatusDialog->showOutput(); m_CompressStatusDialog->raise(); //QTimer::singleShot(10, this, SLOT(SetRaised())); } // do some message - try { + try + { throw; - } catch (const std::exception& e) { + } + catch (const std::exception& e) + { PrintInfo("%s Error: %s (feature may not be supported!)", msgTitle.toStdString().c_str(), e.what()); - } catch (...) { + } + catch (...) + { PrintInfo("Error: Occured while processing image or model (feature may not be supported!)"); } } -void cpMainComponents::AddImageView(QString& fileName, QTreeWidgetItem* item) { - if (!(QFile::exists(fileName))) { - if (m_CompressStatusDialog) { +void cpMainComponents::AddImageView(QString& fileName, QTreeWidgetItem* item) +{ + if (!(QFile::exists(fileName))) + { + if (m_CompressStatusDialog) + { m_CompressStatusDialog->showOutput(); } QString error = "Error: File " + fileName + " does not exist. Please try remove from Project and add again"; @@ -1614,7 +1874,8 @@ void cpMainComponents::AddImageView(QString& fileName, QTreeWidgetItem* item) { cpImageView* m_imageview = NULL; QString ImageType = ""; - try { + try + { if (isCompressInProgress) return; @@ -1623,7 +1884,8 @@ void cpMainComponents::AddImageView(QString& fileName, QTreeWidgetItem* item) { emit OnImageLoadStart(); - if (deleteImageAct) { + if (deleteImageAct) + { isDel = deleteImageAct->isEnabled(); deleteImageAct->setEnabled(false); } @@ -1637,13 +1899,15 @@ void cpMainComponents::AddImageView(QString& fileName, QTreeWidgetItem* item) { bool DockItemDeleted = false; - if (levelType == TREETYPE_COMPRESSION_DATA) { + if (levelType == TREETYPE_COMPRESSION_DATA) + { QVariant v = item->data(TREE_SourceInfo, Qt::UserRole); m_compressdata = v.value(); if (m_compressdata == NULL) return; - if (m_compressdata->m_data_has_been_changed) { + if (m_compressdata->m_data_has_been_changed) + { // Find the old image and remove it OnDeleteImageView(fileName); DockItemDeleted = true; @@ -1653,44 +1917,54 @@ void cpMainComponents::AddImageView(QString& fileName, QTreeWidgetItem* item) { } if (((g_Application_Options.m_useNewImageViews || doRefreshCompressView || g_Application_Options.m_refreshCurrentView || m_ForceImageRefresh)) && - (levelType != TREETYPE_3DMODEL_DATA) && (levelType != TREETYPE_3DSUBMODEL_DATA)) { + (levelType != TREETYPE_3DMODEL_DATA) && (levelType != TREETYPE_3DSUBMODEL_DATA)) + { // Find the old image and remove it // flag (DockItemDeleted) is used make sure a Compressed dock item is not scanned twice for delete if (!DockItemDeleted) OnDeleteImageView(fileName); - if (doRefreshCompressView) { + if (doRefreshCompressView) + { if (m_compressdata) m_compressdata->m_data_has_been_changed = false; } m_ForceImageRefresh = false; - } else { + } + else + { // Make sure we are not not already viewing this image file dock = FindImageView(fileName, false); } - if (dock) { + if (dock) + { // We found the image in our list of existing views m_activeImageTab = dock; } showProgressBusy("Loading ... Please wait"); - if (!g_Application_Options.m_useNewImageViews) { - if (dock) { + if (!g_Application_Options.m_useNewImageViews) + { + if (dock) + { // check if the view is uptodate // or needs to be refreshed m_activeImageTab = dock; - if (levelType == TREETYPE_COMPRESSION_DATA) { - if (doRefreshCompressView) { + if (levelType == TREETYPE_COMPRESSION_DATA) + { + if (doRefreshCompressView) + { // Flag project we have new project settings to save on exit! if (m_projectview) m_projectview->m_saveProjectChanges = true; // Remove the old tab - if (dock) { + if (dock) + { DeleteDock(&dock); } } @@ -1698,16 +1972,19 @@ void cpMainComponents::AddImageView(QString& fileName, QTreeWidgetItem* item) { } } - if (dock == NULL) { + if (dock == NULL) + { Setting* setting = new Setting(); setting->onBrightness = false; - if (levelType == TREETYPE_COMPRESSION_DATA) { + if (levelType == TREETYPE_COMPRESSION_DATA) + { // Get ImageFile Data QVariant v = item->data(TREE_SourceInfo, Qt::UserRole); C_Destination_Options* m_filedata = v.value(); - if (m_filedata) { + if (m_filedata) + { QFile file(fileName); m_filedata->m_FileSize = file.size(); if (m_filedata->m_FileSize > 1024000) @@ -1721,43 +1998,56 @@ void cpMainComponents::AddImageView(QString& fileName, QTreeWidgetItem* item) { // for this compressed image view CMipImages* OrigImages = NULL; QTreeWidgetItem* currItem = item->parent(); - if (currItem) { + if (currItem) + { v = (currItem)->data(TREE_LevelType, Qt::UserRole); int ParentlevelType = v.toInt(); // we need to move up a branch to models parent to get the original images - if (ParentlevelType == TREETYPE_3DSUBMODEL_DATA) { + if (ParentlevelType == TREETYPE_3DSUBMODEL_DATA) + { QString srcImage = m_filedata->m_sourceFileNamePath; currItem = currItem->parent(); - if (currItem) { + if (currItem) + { v = (currItem)->data(TREE_LevelType, Qt::UserRole); ParentlevelType = v.toInt(); - if (ParentlevelType == TREETYPE_3DMODEL_DATA) { + if (ParentlevelType == TREETYPE_3DMODEL_DATA) + { v = currItem->data(TREE_SourceInfo, Qt::UserRole); C_3DModel_Info* data = v.value(); - if (data) { - for (int i = 0; i < data->m_Model_Images.size(); i++) { - if (data->m_Model_Images[i].m_isImage && (data->m_Model_Images[i].m_FilePathName.compare(srcImage) == 0)) { + if (data) + { + for (int i = 0; i < data->m_Model_Images.size(); i++) + { + if (data->m_Model_Images[i].m_isImage && (data->m_Model_Images[i].m_FilePathName.compare(srcImage) == 0)) + { currItem = data->m_Model_Images[i].child; break; } } - } else + } + else currItem = NULL; - } else + } + else currItem = NULL; } - if (currItem) { + if (currItem) + { // check its type ParentlevelType = currItem->data(TREE_LevelType, Qt::UserRole).toInt(); - if (ParentlevelType == TREETYPE_VIEWIMAGE_ONLY_NODE) { + if (ParentlevelType == TREETYPE_VIEWIMAGE_ONLY_NODE) + { v = currItem->data(TREE_SourceInfo, Qt::UserRole); C_Source_Info* imagedata = v.value(); if (imagedata) OrigImages = imagedata->m_MipImages; } } - } else { + } + else + { QVariant v = currItem->data(TREE_SourceInfo, Qt::UserRole); C_Source_Info* imagedata = v.value(); if (imagedata) @@ -1769,14 +2059,23 @@ void cpMainComponents::AddImageView(QString& fileName, QTreeWidgetItem* item) { ImageType = "Image file "; setting->input_image = eImageViewState::isProcessed; m_imageview = new cpImageView(fileName, ImageType, m_parent, m_filedata->m_MipImages, setting, OrigImages); - m_imageview->m_type = TREETYPE_IMAGEFILE_DATA; + if (!m_imageview) + { + QMessageBox::warning(this, "Image View", "Image is not compatible with the current decode view setting!", QMessageBox::Ok); + throw ""; + } + else + m_imageview->m_type = TREETYPE_IMAGEFILE_DATA; } - } else if ((levelType == TREETYPE_IMAGEFILE_DATA) || (levelType == TREETYPE_VIEWIMAGE_ONLY_NODE)) { + } + else if ((levelType == TREETYPE_IMAGEFILE_DATA) || (levelType == TREETYPE_VIEWIMAGE_ONLY_NODE)) + { // Get ImageFile Data QVariant v = item->data(TREE_SourceInfo, Qt::UserRole); C_Source_Info* m_filedata = v.value(); - if (m_filedata) { + if (m_filedata) + { // Create a new view image ImageType = "Original Image file "; setting->reloadImage = g_Application_Options.m_useNewImageViews; @@ -1787,10 +2086,14 @@ void cpMainComponents::AddImageView(QString& fileName, QTreeWidgetItem* item) { m_imageview->m_type = TREETYPE_IMAGEFILE_DATA; setting->generateMips = false; } - } else if ((levelType == TREETYPE_3DMODEL_DATA)) { - if (g_Application_Options.getGLTFRender() == C_Application_Options::RenderModelsWith::glTF_Vulkan) { + } + else if ((levelType == TREETYPE_3DMODEL_DATA)) + { + if (g_Application_Options.getGLTFRender() == C_Application_Options::RenderModelsWith::glTF_Vulkan) + { // V3.1 does not support models with no textures - if (!hasGLTFTextures(fileName.toStdString())) { + if (!hasGLTFTextures(fileName.toStdString())) + { QMessageBox::warning(this, "Vulkan Model Render", "Preview version : Support models with textures only", QMessageBox::Ok); throw ""; } @@ -1801,7 +2104,8 @@ void cpMainComponents::AddImageView(QString& fileName, QTreeWidgetItem* item) { QVariant v = item->data(TREE_SourceInfo, Qt::UserRole); C_3DModel_Info* m_filedata = v.value(); - if (m_filedata) { + if (m_filedata) + { // Create a new view image ImageType = "3D Model Render "; setting->reloadImage = g_Application_Options.m_useNewImageViews; @@ -1810,15 +2114,20 @@ void cpMainComponents::AddImageView(QString& fileName, QTreeWidgetItem* item) { m_3Dmodelview->m_type = TREETYPE_3DMODEL_DATA; setting->generateMips = false; - if (m_3Dmodelview->custTitleBar) { + if (m_3Dmodelview->custTitleBar) + { m_3Dmodelview->custTitleBar->m_close = true; connect(m_3Dmodelview->custTitleBar, SIGNAL(OnAboutToClose(QString&)), this, SLOT(onAboutToClose(QString&))); } } - } else if (levelType == TREETYPE_3DSUBMODEL_DATA) { - if (g_Application_Options.getGLTFRender() == C_Application_Options::RenderModelsWith::glTF_Vulkan) { + } + else if (levelType == TREETYPE_3DSUBMODEL_DATA) + { + if (g_Application_Options.getGLTFRender() == C_Application_Options::RenderModelsWith::glTF_Vulkan) + { // V3.1 does not support models with no textures - if (!hasGLTFTextures(fileName.toStdString())) { + if (!hasGLTFTextures(fileName.toStdString())) + { QMessageBox::warning(this, "Vulkan Model Render", "Preview version : Support models with textures only", QMessageBox::Ok); throw ""; } @@ -1829,7 +2138,8 @@ void cpMainComponents::AddImageView(QString& fileName, QTreeWidgetItem* item) { QVariant v = item->data(TREE_SourceInfo, Qt::UserRole); C_3DSubModel_Info* m_filedata = v.value(); - if (m_filedata) { + if (m_filedata) + { // Create a new view image ImageType = "3D Model Render "; setting->reloadImage = g_Application_Options.m_useNewImageViews; @@ -1838,15 +2148,20 @@ void cpMainComponents::AddImageView(QString& fileName, QTreeWidgetItem* item) { m_3Dmodelview->m_type = TREETYPE_3DSUBMODEL_DATA; setting->generateMips = false; - if (m_3Dmodelview->custTitleBar) { + if (m_3Dmodelview->custTitleBar) + { m_3Dmodelview->custTitleBar->m_close = true; connect(m_3Dmodelview->custTitleBar, SIGNAL(OnAboutToClose(QString&)), this, SLOT(onAboutToClose(QString&))); } } - } else if (levelType == TREETYPE_VIEWMESH_ONLY_NODE) { - if (g_Application_Options.getGLTFRender() == C_Application_Options::RenderModelsWith::glTF_Vulkan) { + } + else if (levelType == TREETYPE_VIEWMESH_ONLY_NODE) + { + if (g_Application_Options.getGLTFRender() == C_Application_Options::RenderModelsWith::glTF_Vulkan) + { // V3.1 does not support models with no textures - if (!hasGLTFTextures(fileName.toStdString())) { + if (!hasGLTFTextures(fileName.toStdString())) + { QMessageBox::warning(this, "Vulkan Model Render", "Preview version : Support models with textures only", QMessageBox::Ok); throw ""; } @@ -1857,7 +2172,8 @@ void cpMainComponents::AddImageView(QString& fileName, QTreeWidgetItem* item) { QVariant v = item->data(TREE_SourceInfo, Qt::UserRole); C_Mesh_Buffer_Info* m_filedata = v.value(); - if (m_filedata) { + if (m_filedata) + { // Create a new view image ImageType = "3D Mesh "; setting->reloadImage = g_Application_Options.m_useNewImageViews; @@ -1866,15 +2182,20 @@ void cpMainComponents::AddImageView(QString& fileName, QTreeWidgetItem* item) { m_3Dmodelview->m_type = TREETYPE_MESH_DATA; setting->generateMips = false; - if (m_3Dmodelview->custTitleBar) { + if (m_3Dmodelview->custTitleBar) + { m_3Dmodelview->custTitleBar->m_close = true; connect(m_3Dmodelview->custTitleBar, SIGNAL(OnAboutToClose(QString&)), this, SLOT(onAboutToClose(QString&))); } } - } else if (levelType == TREETYPE_MESH_DATA) { - if (g_Application_Options.getGLTFRender() == C_Application_Options::RenderModelsWith::glTF_Vulkan) { + } + else if (levelType == TREETYPE_MESH_DATA) + { + if (g_Application_Options.getGLTFRender() == C_Application_Options::RenderModelsWith::glTF_Vulkan) + { // V3.1 does not support models with no textures - if (!hasGLTFTextures(fileName.toStdString())) { + if (!hasGLTFTextures(fileName.toStdString())) + { QMessageBox::warning(this, "Vulkan Model Render", "This version of the render support models with textures only", QMessageBox::Ok); throw ""; } @@ -1885,7 +2206,8 @@ void cpMainComponents::AddImageView(QString& fileName, QTreeWidgetItem* item) { QVariant v = item->data(TREE_SourceInfo, Qt::UserRole); C_Destination_Options* m_destmeshdata = v.value(); - if (m_destmeshdata) { + if (m_destmeshdata) + { // Create a new view image ImageType = "3D Mesh "; setting->reloadImage = g_Application_Options.m_useNewImageViews; @@ -1893,22 +2215,28 @@ void cpMainComponents::AddImageView(QString& fileName, QTreeWidgetItem* item) { QTreeWidgetItem* subModel_parent = item->parent(); - if (subModel_parent) { + if (subModel_parent) + { QVariant v = subModel_parent->data(TREE_LevelType, Qt::UserRole); int parentLevelType = v.toInt(); - if (parentLevelType == TREETYPE_3DSUBMODEL_DATA) { + if (parentLevelType == TREETYPE_3DSUBMODEL_DATA) + { QVariant v = subModel_parent->data(TREE_SourceInfo, Qt::UserRole); C_3DSubModel_Info* subModel_data = v.value(); - if (m_destmeshdata->m_destFileNamePath.contains(".bin") || m_destmeshdata->m_destFileNamePath.contains(".BIN")) { + if (m_destmeshdata->m_destFileNamePath.contains(".bin") || m_destmeshdata->m_destFileNamePath.contains(".BIN")) + { m_3Dmodelview = new cp3DModelView(fileName, subModel_data->m_Full_Path, ImageType, m_parent); - } else { + } + else + { m_3Dmodelview = new cp3DModelView(fileName, "", ImageType, m_parent); } m_3Dmodelview->m_type = TREETYPE_MESH_DATA; setting->generateMips = false; - if (m_3Dmodelview->custTitleBar) { + if (m_3Dmodelview->custTitleBar) + { m_3Dmodelview->custTitleBar->m_close = true; connect(m_3Dmodelview->custTitleBar, SIGNAL(OnAboutToClose(QString&)), this, SLOT(onAboutToClose(QString&))); } @@ -1917,7 +2245,8 @@ void cpMainComponents::AddImageView(QString& fileName, QTreeWidgetItem* item) { } } - if (m_imageview) { + if (m_imageview) + { m_imageview->showToobar(true); m_imageview->showToobarButton(true); m_imageview->setAllowedAreas(Qt::RightDockWidgetArea); @@ -1929,7 +2258,8 @@ void cpMainComponents::AddImageView(QString& fileName, QTreeWidgetItem* item) { m_activeImageTab = m_imageview; } - if (m_3Dmodelview) { + if (m_3Dmodelview) + { m_3Dmodelview->setAllowedAreas(Qt::RightDockWidgetArea); m_parent->addDockWidget(Qt::RightDockWidgetArea, m_3Dmodelview); m_parent->tabifyDockWidget(m_blankpage, m_3Dmodelview); @@ -1943,22 +2273,28 @@ void cpMainComponents::AddImageView(QString& fileName, QTreeWidgetItem* item) { // ================================= // Place the TAB on view if hidden! // ================================= - if (m_activeImageTab) { + if (m_activeImageTab) + { // check its visability: User is requesting view - if (!m_activeImageTab->isVisible()) { + if (!m_activeImageTab->isVisible()) + { m_activeImageTab->setVisible(true); } m_activeImageTab->raise(); //QTimer::singleShot(10, this, SLOT(SetRaised())); } - } catch (...) { - if (m_imageview) { + } + catch (...) + { + if (m_imageview) + { delete m_imageview; m_imageview = nullptr; } - if (m_3Dmodelview) { + if (m_3Dmodelview) + { delete m_3Dmodelview; m_3Dmodelview = nullptr; } @@ -1978,8 +2314,10 @@ void cpMainComponents::AddImageView(QString& fileName, QTreeWidgetItem* item) { hideProgressBusy("Ready"); } -void cpMainComponents::AddImageDiff(C_Destination_Options* destination, QString& fileName1, QString& fileName2) { - try { +void cpMainComponents::AddImageDiff(C_Destination_Options* destination, QString& fileName1, QString& fileName2) +{ + try + { if (isCompressInProgress) return; @@ -1992,16 +2330,19 @@ void cpMainComponents::AddImageDiff(C_Destination_Options* destination, QString& // compressAct->setEnabled(false); //} - if (imagediffAct) { + if (imagediffAct) + { imagediffAct->setEnabled(false); } - if (deleteImageAct) { + if (deleteImageAct) + { isDel = deleteImageAct->isEnabled(); deleteImageAct->setEnabled(false); } - if (m_CompressStatusDialog) { + if (m_CompressStatusDialog) + { m_CompressStatusDialog->onClearText(); m_CompressStatusDialog->showOutput(); } @@ -2012,12 +2353,16 @@ void cpMainComponents::AddImageDiff(C_Destination_Options* destination, QString& QString destFile = ""; QString title = ""; - if (destination == NULL) { + if (destination == NULL) + { QFileInfo fileinfo1(fileName1); QFile file1(fileName1); - if (file1.exists() && (fileinfo1.suffix().length() > 0)) { + if (file1.exists() && (fileinfo1.suffix().length() > 0)) + { originalFileName = fileName1; - } else { + } + else + { PrintInfo("Image Diff Error: Image File #1 cannot be found\n"); onShowOutput(); return; @@ -2025,9 +2370,12 @@ void cpMainComponents::AddImageDiff(C_Destination_Options* destination, QString& QFileInfo fileinfo2(fileName2); QFile file2(fileName2); - if (file2.exists() && (fileinfo2.suffix().length() > 0)) { + if (file2.exists() && (fileinfo2.suffix().length() > 0)) + { destFile = fileName2; - } else { + } + else + { PrintInfo("Image Diff Error: Image File #2 cannot be found\n"); onShowOutput(); return; @@ -2039,7 +2387,9 @@ void cpMainComponents::AddImageDiff(C_Destination_Options* destination, QString& // Create a new view image m_imageCompare = new CImageCompare(title, originalFileName, destFile, false, this); m_imageCompare->m_type = TREETYPE_DIFFVIEW; - } else { + } + else + { originalFileName = destination->m_sourceFileNamePath; // Find the old image diff and remove it // User may have selected a newer Decompression Option @@ -2047,9 +2397,12 @@ void cpMainComponents::AddImageDiff(C_Destination_Options* destination, QString& title = DIFFERENCE_IMAGE_TXT + destination->m_destFileNamePath; OnDeleteImageDiffView(destination->m_destFileNamePath); - if (QDir(destination->m_decompressedFileNamePath).exists() && (QFile(destination->m_decompressedFileNamePath).size()) > 0) { + if (QDir(destination->m_decompressedFileNamePath).exists() && (QFile(destination->m_decompressedFileNamePath).size()) > 0) + { destFile = destination->m_decompressedFileNamePath; - } else { + } + else + { destFile = destination->m_destFileNamePath; } @@ -2059,15 +2412,19 @@ void cpMainComponents::AddImageDiff(C_Destination_Options* destination, QString& } CMipImages* m_diffMips = m_imageCompare->getMdiffMips(); - if (m_diffMips == NULL) { + if (m_diffMips == NULL) + { delete m_imageCompare; PrintInfo("Image Diff Error: Diff Image cannot be found\n"); return; - } else { + } + else + { m_imageCompare->setAllowedAreas(Qt::RightDockWidgetArea); m_parent->addDockWidget(Qt::RightDockWidgetArea, m_imageCompare); m_parent->tabifyDockWidget(m_blankpage, m_imageCompare); - if (m_imageCompare->custTitleBar) { + if (m_imageCompare->custTitleBar) + { m_imageCompare->custTitleBar->m_close = true; connect(m_imageCompare->custTitleBar, SIGNAL(OnAboutToClose(QString&)), this, SLOT(onAboutToClose(QString&))); } @@ -2077,7 +2434,8 @@ void cpMainComponents::AddImageDiff(C_Destination_Options* destination, QString& m_activeImageTab = m_imageCompare; // check its visability: User is requesting view - if (!m_activeImageTab->isVisible()) { + if (!m_activeImageTab->isVisible()) + { m_activeImageTab->setVisible(true); } @@ -2097,15 +2455,19 @@ void cpMainComponents::AddImageDiff(C_Destination_Options* destination, QString& #endif if (deleteImageAct) deleteImageAct->setEnabled(isDel); - } catch (...) { + } + catch (...) + { DisplayException("ImageDiff"); } } -void cpMainComponents::Add3DModelDiff(C_3DSubModel_Info* destination, QString& fileName1, QString& fileName2) { +void cpMainComponents::Add3DModelDiff(C_3DSubModel_Info* destination, QString& fileName1, QString& fileName2) +{ Q_UNUSED(destination); - try { + try + { if (isCompressInProgress) return; @@ -2113,20 +2475,24 @@ void cpMainComponents::Add3DModelDiff(C_3DSubModel_Info* destination, QString& f emit OnImageLoadStart(); - if (imagediffAct) { + if (imagediffAct) + { imagediffAct->setEnabled(false); } #ifdef USE_3DCONVERT - if (ConvertModelAct) { + if (ConvertModelAct) + { ConvertModelAct->setEnabled(false); } #endif - if (deleteImageAct) { + if (deleteImageAct) + { isDel = deleteImageAct->isEnabled(); deleteImageAct->setEnabled(false); } - if (m_CompressStatusDialog) { + if (m_CompressStatusDialog) + { m_CompressStatusDialog->onClearText(); m_CompressStatusDialog->showOutput(); } @@ -2138,18 +2504,24 @@ void cpMainComponents::Add3DModelDiff(C_3DSubModel_Info* destination, QString& f QString title = ""; QFileInfo fileinfo1(fileName1); - if (QFile::exists(fileName1) && (fileinfo1.suffix().length() > 0)) { + if (QFile::exists(fileName1) && (fileinfo1.suffix().length() > 0)) + { originalFileName = fileName1; - } else { + } + else + { PrintInfo("Image Diff Error: Image File #1 cannot be found\n"); onShowOutput(); return; } QFileInfo fileinfo2(fileName2); - if (QFile::exists(fileName2) && (fileinfo2.suffix().length() > 0)) { + if (QFile::exists(fileName2) && (fileinfo2.suffix().length() > 0)) + { destFile = fileName2; - } else { + } + else + { PrintInfo("Image Diff Error: Image File #2 cannot be found\n"); onShowOutput(); return; @@ -2173,7 +2545,8 @@ void cpMainComponents::Add3DModelDiff(C_3DSubModel_Info* destination, QString& f m_3dModelCompare->setAllowedAreas(Qt::RightDockWidgetArea); m_parent->addDockWidget(Qt::RightDockWidgetArea, m_3dModelCompare); m_parent->tabifyDockWidget(m_blankpage, m_3dModelCompare); - if (m_3dModelCompare->custTitleBar) { + if (m_3dModelCompare->custTitleBar) + { m_3dModelCompare->custTitleBar->m_close = true; connect(m_3dModelCompare->custTitleBar, SIGNAL(OnAboutToClose(QString&)), this, SLOT(onAboutToClose(QString&))); } @@ -2183,7 +2556,8 @@ void cpMainComponents::Add3DModelDiff(C_3DSubModel_Info* destination, QString& f m_activeImageTab = m_3dModelCompare; // check its visability: User is requesting view - if (!m_activeImageTab->isVisible()) { + if (!m_activeImageTab->isVisible()) + { m_activeImageTab->setVisible(true); } @@ -2203,17 +2577,21 @@ void cpMainComponents::Add3DModelDiff(C_3DSubModel_Info* destination, QString& f #endif if (deleteImageAct) deleteImageAct->setEnabled(isDel); - } catch (...) { + } + catch (...) + { DisplayException("3D Model ImageDiff"); } } -void cpMainComponents::OnDeleteImageView(QString& fileName) { +void cpMainComponents::OnDeleteImageView(QString& fileName) +{ showProgressBusy("Removing Image view ... Please wait"); // Make sure we are not not already viewing this image file acCustomDockWidget* dock = (acCustomDockWidget*)FindImageView(fileName, false); - if (dock) { + if (dock) + { DeleteDock(&dock); } @@ -2221,24 +2599,28 @@ void cpMainComponents::OnDeleteImageView(QString& fileName) { OnDeleteImageDiffView(fileName); // Check if we need to disable any buttons from the main app! - if (m_projectview) { + if (m_projectview) + { int ItemsCount; m_projectview->Tree_numSelectedtems(ItemsCount); - if (deleteImageAct) { + if (deleteImageAct) + { deleteImageAct->setEnabled(ItemsCount > 0); } } hideProgressBusy("Ready"); } -void cpMainComponents::OnDeleteImageDiffView(QString& fileName) { +void cpMainComponents::OnDeleteImageDiffView(QString& fileName) +{ showProgressBusy("Removing Image Differance view ... Please wait"); // Make sure we are not not already viewing this image file acCustomDockWidget* dock = (acCustomDockWidget*)FindImageView(fileName, true); // Vaid pointer else NULL - if (dock) { + if (dock) + { // Only delete a valid assigned DiffView type //printf("Delete Image Diff Dock %d\n",dock->m_type); if (dock->m_type == TREETYPE_DIFFVIEW) @@ -2248,19 +2630,22 @@ void cpMainComponents::OnDeleteImageDiffView(QString& fileName) { hideProgressBusy("Ready"); } -void cpMainComponents::SetRaised() { +void cpMainComponents::SetRaised() +{ if (m_activeImageTab) m_activeImageTab->raise(); } -cpMainComponents::~cpMainComponents() { +cpMainComponents::~cpMainComponents() +{ g_bAbortCompression = true; #ifdef _WIN32 CMP_ShutdownDecompessLibrary(); #endif } -void cpMainComponents::OnAddCompressSettings(QTreeWidgetItem* item) { +void cpMainComponents::OnAddCompressSettings(QTreeWidgetItem* item) +{ if (!item) return; @@ -2277,7 +2662,8 @@ void cpMainComponents::OnAddCompressSettings(QTreeWidgetItem* item) { if (!parent) parent = item; - if (parent) { + if (parent) + { // Verify its root QVariant v = parent->data(TREE_LevelType, Qt::UserRole); int itemlevelType = v.toInt(); @@ -2285,7 +2671,8 @@ void cpMainComponents::OnAddCompressSettings(QTreeWidgetItem* item) { m_setcompressoptions->m_PBDestFileFolder->setEnabled(true); m_setcompressoptions->m_DestinationData.m_SourceType = itemlevelType; - if (itemlevelType == TREETYPE_IMAGEFILE_DATA) { + if (itemlevelType == TREETYPE_IMAGEFILE_DATA) + { QVariant v = parent->data(TREE_SourceInfo, Qt::UserRole); C_Source_Info* m_ImageSourceFile = v.value(); @@ -2328,7 +2715,9 @@ void cpMainComponents::OnAddCompressSettings(QTreeWidgetItem* item) { QFileInfo fi(m_setcompressoptions->m_DestinationData.m_sourceFileNamePath); QString name = fi.fileName(); m_setcompressoptions->m_CBSourceFile->addItem(name); - } else if (itemlevelType == TREETYPE_3DMODEL_DATA) { + } + else if (itemlevelType == TREETYPE_3DMODEL_DATA) + { m_setcompressoptions->m_DestinationData.m_SourceType = itemlevelType; QVariant v = parent->data(TREE_SourceInfo, Qt::UserRole); C_3DModel_Info* m_3DModelSourceFile = v.value(); @@ -2348,7 +2737,8 @@ void cpMainComponents::OnAddCompressSettings(QTreeWidgetItem* item) { m_setcompressoptions->m_extnum = m_3DModelSourceFile->m_extnum++; // List of source files - if (levelType == TREETYPE_Add_Model_destination_settings) { + if (levelType == TREETYPE_Add_Model_destination_settings) + { // Set Compression Widgets to enable m_setcompressoptions->m_showDestinationEXTSetting = false; m_setcompressoptions->m_showTheControllerSetting = false; @@ -2358,10 +2748,14 @@ void cpMainComponents::OnAddCompressSettings(QTreeWidgetItem* item) { QString name = fi.fileName(); m_setcompressoptions->m_CBSourceFile->addItem(name); m_setcompressoptions->m_DestinationData.m_modelSource = m_3DModelSourceFile->m_Full_Path; - } else { + } + else + { m_setcompressoptions->m_CBSourceFile->clear(); } - } else if (itemlevelType == TREETYPE_3DSUBMODEL_DATA) { + } + else if (itemlevelType == TREETYPE_3DSUBMODEL_DATA) + { if (levelType != TREETYPE_Add_destination_setting) return; // noting to do with compression settings! @@ -2371,7 +2765,8 @@ void cpMainComponents::OnAddCompressSettings(QTreeWidgetItem* item) { if (!m_3DSubModelSourceFile) return; - if (m_3DSubModelSourceFile->m_Model_Images.isEmpty()) { + if (m_3DSubModelSourceFile->m_Model_Images.isEmpty()) + { PrintInfo("Error: There is no texture found in the glTF file."); onShowOutput(); return; @@ -2380,15 +2775,18 @@ void cpMainComponents::OnAddCompressSettings(QTreeWidgetItem* item) { // Is 3d src del flag set to true bool canSetValues = false; int defaultIndex = 0; - while (defaultIndex < m_3DSubModelSourceFile->m_Model_Images.size()) { - if (m_3DSubModelSourceFile->m_SubModel_Images[defaultIndex].m_srcDelFlag == false) { + while (defaultIndex < m_3DSubModelSourceFile->m_Model_Images.size()) + { + if (m_3DSubModelSourceFile->m_SubModel_Images[defaultIndex].m_srcDelFlag == false) + { canSetValues = true; break; } defaultIndex++; } - if (canSetValues == false) { + if (canSetValues == false) + { PrintInfo( "Error: All setting for the file has been added. \n Note: To add/ modify setting, please add new setting or modify the current setting " "through properties window."); @@ -2402,7 +2800,8 @@ void cpMainComponents::OnAddCompressSettings(QTreeWidgetItem* item) { m_setcompressoptions->m_DestinationData.m_SubModel_Images = m_3DSubModelSourceFile->m_SubModel_Images; // Let assume for now that we are using PBR images which all have the same sizes - if (m_setcompressoptions->m_DestinationData.m_Model_Images.size() > 0) { + if (m_setcompressoptions->m_DestinationData.m_Model_Images.size() > 0) + { m_setcompressoptions->m_DestinationData.m_DstWidth = m_3DSubModelSourceFile->m_Model_Images[defaultIndex].m_Width; m_setcompressoptions->m_DestinationData.m_DstHeight = m_3DSubModelSourceFile->m_Model_Images[defaultIndex].m_Height; } @@ -2413,9 +2812,11 @@ void cpMainComponents::OnAddCompressSettings(QTreeWidgetItem* item) { m_setcompressoptions->m_showTheInfoTextSetting = true; m_setcompressoptions->m_CBSourceFile->clear(); - for (int i = 0; i < m_setcompressoptions->m_DestinationData.m_Model_Images.size(); ++i) { + for (int i = 0; i < m_setcompressoptions->m_DestinationData.m_Model_Images.size(); ++i) + { // check for delete flags - if (m_setcompressoptions->m_DestinationData.m_SubModel_Images[i].m_srcDelFlag == false) { + if (m_setcompressoptions->m_DestinationData.m_SubModel_Images[i].m_srcDelFlag == false) + { QFileInfo fi(m_setcompressoptions->m_DestinationData.m_Model_Images[i].m_FilePathName); QString name = fi.fileName(); m_setcompressoptions->m_CBSourceFile->addItem(name, QVariant(m_setcompressoptions->m_DestinationData.m_SubModel_Images[i].m_isImage)); @@ -2427,10 +2828,13 @@ void cpMainComponents::OnAddCompressSettings(QTreeWidgetItem* item) { // Default to first entry in the source combolist QString srcPath = ""; - if (m_setcompressoptions->m_DestinationData.m_Model_Images.isEmpty()) { + if (m_setcompressoptions->m_DestinationData.m_Model_Images.isEmpty()) + { QFileInfo srcfileInfo(m_setcompressoptions->m_DestinationData.m_modelSource); srcPath = srcfileInfo.absolutePath(); - } else { + } + else + { QFileInfo srcfileInfo(m_setcompressoptions->m_DestinationData.m_Model_Images[defaultIndex].m_FilePathName); srcPath = srcfileInfo.absolutePath(); } @@ -2448,9 +2852,12 @@ void cpMainComponents::OnAddCompressSettings(QTreeWidgetItem* item) { // Extension Counter Number incriment m_setcompressoptions->m_extnum = m_3DSubModelSourceFile->m_extnum++; - if ((m_3DSubModelSourceFile->ModelType == eModelType::OBJ) && (parent != item)) { + if ((m_3DSubModelSourceFile->ModelType == eModelType::OBJ) && (parent != item)) + { m_setcompressoptions->GBDestinationFile->setVisible(false); - } else { + } + else + { // we are adding a OBJ or GLTF destination file m_setcompressoptions->GBDestinationFile->setVisible(true); } @@ -2465,8 +2872,10 @@ void cpMainComponents::OnAddCompressSettings(QTreeWidgetItem* item) { emit m_setcompressoptions->m_DestinationData.compressionChanged((QVariant&)m_setcompressoptions->m_DestinationData.m_Compression); - if (m_setcompressoptions->updateDisplayContent()) { - if (!m_setcompressoptions->isVisible()) { + if (m_setcompressoptions->updateDisplayContent()) + { + if (!m_setcompressoptions->isVisible()) + { QPoint pos = QCursor::pos(); m_setcompressoptions->move(pos); m_setcompressoptions->show(); @@ -2474,26 +2883,32 @@ void cpMainComponents::OnAddCompressSettings(QTreeWidgetItem* item) { } } -void cpMainComponents::onAddedImageSourceNode() { +void cpMainComponents::onAddedImageSourceNode() +{ if (compressAct) compressAct->setEnabled(true); } -void cpMainComponents::onAddedCompressSettingNode() { +void cpMainComponents::onAddedCompressSettingNode() +{ if (compressAct) compressAct->setEnabled(true); m_setcompressoptions->m_destFilePath = m_setcompressoptions->m_DestinationFolder->text(); } -void cpMainComponents::onEditCompressSettings(QTreeWidgetItem* item) { +void cpMainComponents::onEditCompressSettings(QTreeWidgetItem* item) +{ QVariant v = item->data(TREE_SourceInfo, Qt::UserRole); C_Destination_Options* m_data = v.value(); - if (m_data) { + if (m_data) + { m_setcompressoptions->m_item = item; m_setcompressoptions->m_DestinationData << (const C_Destination_Options&)*m_data; - if (m_setcompressoptions->updateDisplayContent()) { - if (!m_setcompressoptions->isVisible()) { + if (m_setcompressoptions->updateDisplayContent()) + { + if (!m_setcompressoptions->isVisible()) + { QPoint pos = QCursor::pos(); m_setcompressoptions->move(pos); m_setcompressoptions->show(); @@ -2513,7 +2928,8 @@ QString comError = "Failed to initialize COM"; // Messages from command line prints // ---------------------------------- -void cpMainComponents::PrintStatus(char* buff) { +void cpMainComponents::PrintStatus(char* buff) +{ //qDebug() << buff; isCompressMSG = true; QString msg = buff; @@ -2527,15 +2943,18 @@ void cpMainComponents::PrintStatus(char* buff) { // Messages from qDebug() // ---------------------------------- -void cpMainComponents::msgHandler(QtMsgType type, const char* msg) { +void cpMainComponents::msgHandler(QtMsgType type, const char* msg) +{ Q_UNUSED(type); Q_UNUSED(msg); //emit static_msghandler.signalMessage(msg); } -void cpMainComponents::browserMsg(const char* msg) { +void cpMainComponents::browserMsg(const char* msg) +{ //statusBar()->showMessage(msg); - if (m_CompressStatusDialog && isCompressMSG) { + if (m_CompressStatusDialog && isCompressMSG) + { QString qmsg = msg; qmsg.remove(QRegExp("[\\n\\r]")); m_CompressStatusDialog->appendText(qmsg); @@ -2545,19 +2964,24 @@ void cpMainComponents::browserMsg(const char* msg) { //================================================ -void cpMainComponents::removeItemTabs(QString* FilePathName) { +void cpMainComponents::removeItemTabs(QString* FilePathName) +{ QTreeWidgetItem* item = m_projectview->GetCurrentItem(TREETYPE_COMPRESSION_DATA); - if (item) { + if (item) + { // qDebug() << "Delete this Tab: " << *FilePathName; // view image QVariant v = item->data(TREE_SourceInfo, Qt::UserRole); C_Destination_Options* m_data = v.value(); - if (m_data) { - if ((m_data->m_destFileNamePath.compare(*FilePathName) == 0) || (m_data->m_destFileNamePath.compare(DIFFERENCE_IMAGE_TXT + *FilePathName) == 0)) { + if (m_data) + { + if ((m_data->m_destFileNamePath.compare(*FilePathName) == 0) || (m_data->m_destFileNamePath.compare(DIFFERENCE_IMAGE_TXT + *FilePathName) == 0)) + { OnDeleteImageView(*FilePathName); OnDeleteImageDiffView(*FilePathName); m_activeImageTab = NULL; - if (m_projectview) { + if (m_projectview) + { m_projectview->Tree_updateCompressIcon(item, *FilePathName, false); m_projectview->m_saveProjectChanges = true; } @@ -2566,36 +2990,48 @@ void cpMainComponents::removeItemTabs(QString* FilePathName) { } } -void cpMainComponents::onPropertyViewSaveSetting(QString* FilePathName) { - if (m_projectview) { +void cpMainComponents::onPropertyViewSaveSetting(QString* FilePathName) +{ + if (m_projectview) + { QFile::remove(*FilePathName); removeItemTabs(FilePathName); m_projectview->Tree_SetCurrentItem(*FilePathName); } } -void cpMainComponents::onPropertyViewCompressImage(QString* FilePathName) { - if (m_projectview) { +void cpMainComponents::onPropertyViewCompressImage(QString* FilePathName) +{ + if (m_projectview) + { removeItemTabs(FilePathName); m_projectview->Tree_clearAllItemsSetected(); QTreeWidgetItem* item = m_projectview->Tree_SetCurrentItem(*FilePathName); - if (item != NULL) { + if (item != NULL) + { QVariant v = item->data(TREE_SourceInfo, Qt::UserRole); C_Destination_Options* m_data = v.value(); - if (m_data) { + if (m_data) + { m_data->m_isselected = true; m_projectview->OnStartCompression(); - } else { + } + else + { C_3DSubModel_Info* m_modeldata = v.value(); - if (m_modeldata) { + if (m_modeldata) + { QTreeWidgetItemIterator it(item); - while (*it) { + while (*it) + { QVariant vc = (*it)->data(TREE_LevelType, Qt::UserRole); int levelType = vc.toInt(); - if (levelType == TREETYPE_COMPRESSION_DATA || levelType == TREETYPE_MESH_DATA) { + if (levelType == TREETYPE_COMPRESSION_DATA || levelType == TREETYPE_MESH_DATA) + { v = (*it)->data(TREE_SourceInfo, Qt::UserRole); C_Destination_Options* data = v.value(); - if (data) { + if (data) + { data->m_isselected = true; (*it)->setSelected(true); break; @@ -2612,7 +3048,8 @@ void cpMainComponents::onPropertyViewCompressImage(QString* FilePathName) { } } -void cpMainComponents::onCompressionStart() { +void cpMainComponents::onCompressionStart() +{ // Disable relavent tool bar options if (compressAct) compressAct->setEnabled(false); @@ -2635,31 +3072,37 @@ void cpMainComponents::onCompressionStart() { isCompressInProgress = true; } -void cpMainComponents::onCompressionDone() { +void cpMainComponents::onCompressionDone() +{ isCompressInProgress = false; - if (m_projectview) { + if (m_projectview) + { // check if any items prior to compressing! int ItemsCount; m_projectview->Tree_numSelectedtems(ItemsCount); - if (ItemsCount > 0) { + if (ItemsCount > 0) + { // Called when compression from Project view is completed. QTreeWidgetItem* item = m_projectview->GetCurrentItem(TREETYPE_COMPRESSION_DATA); - if (item) { + if (item) + { // view image QVariant v = item->data(TREE_SourceInfo, Qt::UserRole); C_Destination_Options* m_data = v.value(); - if (m_data) { + if (m_data) + { // Refresh any changes in current items data // like compression time if (m_imagePropertyView) m_imagePropertyView->OnUpdateData(m_data); // Add the image to the diff image list - if (m_projectview) { + if (m_projectview) + { // Add the image to the diff image list if it is not in the list if ((!(m_projectview->m_ImagesinProjectTrees.contains(m_data->m_destFileNamePath))) && - (!(m_data->m_destFileNamePath.contains(".gltf")))) + (!(m_data->m_destFileNamePath.contains(".gltf")))) m_projectview->m_ImagesinProjectTrees.append(m_data->m_destFileNamePath); } @@ -2683,10 +3126,12 @@ void cpMainComponents::onCompressionDone() { #endif if (deleteImageAct) deleteImageAct->setEnabled(true); + } // in future revisions: calls to onSourceImage() should be replaced newer code onSetToolBarActions() -void cpMainComponents::onSourceImage(int childCount) { +void cpMainComponents::onSourceImage(int childCount) +{ if (MIPGenAct) MIPGenAct->setEnabled(true); if (deleteImageAct) @@ -2695,14 +3140,17 @@ void cpMainComponents::onSourceImage(int childCount) { if (ConvertModelAct) ConvertModelAct->setEnabled(false); #endif - if (saveToBatchFileAct) { + if (saveToBatchFileAct) + { if (!saveToBatchFileAct->isEnabled()) saveToBatchFileAct->setEnabled(childCount > 1); } } -void cpMainComponents::onProjectLoaded(int childCount) { - if (saveToBatchFileAct) { +void cpMainComponents::onProjectLoaded(int childCount) +{ + if (saveToBatchFileAct) + { saveToBatchFileAct->setEnabled(childCount > 1); } @@ -2710,7 +3158,8 @@ void cpMainComponents::onProjectLoaded(int childCount) { m_imagePropertyView->OnUpdateData(NULL); } -void cpMainComponents::onDecompressImage() { +void cpMainComponents::onDecompressImage() +{ if (imagediffAct) imagediffAct->setEnabled(true); #ifdef USE_3DCONVERT @@ -2722,24 +3171,28 @@ void cpMainComponents::onDecompressImage() { if (deleteImageAct) deleteImageAct->setEnabled(true); - if (saveToBatchFileAct) { + if (saveToBatchFileAct) + { if (!saveToBatchFileAct->isEnabled()) saveToBatchFileAct->setEnabled(true); } } -void cpMainComponents::OpenCHMFile(QString fileName) { +void cpMainComponents::OpenCHMFile(QString fileName) +{ QString str; str.append(URL_FILE); str.append(qApp->applicationDirPath()); str.append("/"); str.append(fileName); - if (!QDesktopServices::openUrl(QUrl(str))) { + if (!QDesktopServices::openUrl(QUrl(str))) + { char* ENV; str = ""; ENV = getenv("COMPRESSONATOR_ROOT"); - if (ENV) { + if (ENV) + { str.append(URL_FILE); str.append(ENV); str.append("/"); @@ -2749,19 +3202,23 @@ void cpMainComponents::OpenCHMFile(QString fileName) { } } -void cpMainComponents::userGuide() { +void cpMainComponents::userGuide() +{ OpenCHMFile(COMPRESSONATOR_USER_GUIDE); } -void cpMainComponents::newFeaturesGuide() { +void cpMainComponents::newFeaturesGuide() +{ OpenCHMFile(COMPRESSONATOR_NEWFEATURES_GUIDE); } -void cpMainComponents::gettingStarted() { +void cpMainComponents::gettingStarted() +{ OpenCHMFile(COMPRESSONATOR_GETTING_STARTED); } -void cpMainComponents::onProcessing(QString& FilePathName) { +void cpMainComponents::onProcessing(QString& FilePathName) +{ Q_UNUSED(FilePathName); // Reserved for future use // to handle any action just before a file is processed @@ -2769,17 +3226,22 @@ void cpMainComponents::onProcessing(QString& FilePathName) { } // Removes all Docked Views that contains Title -void cpMainComponents::onAboutToClose(QString& Title) { +void cpMainComponents::onAboutToClose(QString& Title) +{ QList dockWidgets = m_parent->findChildren(); acCustomDockWidget* dock; - for (int i = 0; i < dockWidgets.size(); i++) { + for (int i = 0; i < dockWidgets.size(); i++) + { dock = dockWidgets[i]; - if (dock) { + if (dock) + { QString DockTitle = dock->custTitleBar->getTitle(); - if (DockTitle.compare(Title) == 0) { + if (DockTitle.compare(Title) == 0) + { //skip diff subwindow which are already deleted - if (dock->m_type == TREETYPE_DIFFVIEW) { + if (dock->m_type == TREETYPE_DIFFVIEW) + { i += 3; } DeleteDock(&dock); diff --git a/applications/compressonatorgui/source/main.cpp b/applications/compressonatorgui/source/main.cpp index 9fdf53ca3..07b218c0e 100644 --- a/applications/compressonatorgui/source/main.cpp +++ b/applications/compressonatorgui/source/main.cpp @@ -29,26 +29,26 @@ #include +#ifdef _CMP_CPP17_ // Build code using std::c++17 #include +namespace sfs = std::filesystem; +#else +#include +namespace sfs = std::experimental::filesystem; +#endif #define MSG_HANDLER -// Our Static Plugin Interfaces -#if defined(_WIN32) && !defined(NO_LEGACY_BEHAVIOR) -#pragma comment(lib, "ASTC.lib") -#pragma comment(lib, "EXR.lib") -#pragma comment(lib, "KTX2.lib") -#pragma comment(lib, "TGA.lib") -#pragma comment(lib, "IMGAnalysis.lib") - -//#pragma comment(lib, "common_app_lib.lib") -#else -#pragma comment(lib, "Plugin_CImage_ASTC.lib") -#pragma comment(lib, "Plugin_CImage_EXR.lib") -#pragma comment(lib, "Plugin_CImage_KTX.lib") -#pragma comment(lib, "Plugin_CImage_TGA.lib") -#pragma comment(lib, "Plugin_CAnalysis.lib") +// Standard App Static Plugin Interfaces for minimal support +#pragma comment(lib, "Image_ASTC.lib") +#pragma comment(lib, "Image_EXR.lib") +#pragma comment(lib, "Image_KTX.lib") +#ifdef _WIN32 +#pragma comment(lib, "Image_KTX2.lib") #endif +#pragma comment(lib, "Image_TGA.lib") +#pragma comment(lib, "Image_Analysis.lib") + #ifdef USE_CRN #pragma comment(lib, "CRN.lib") @@ -57,6 +57,9 @@ extern void* make_Plugin_ASTC(); extern void* make_Plugin_EXR(); extern void* make_Plugin_KTX(); +#ifdef _WIN32 +extern void* make_Plugin_KTX2(); +#endif extern void* make_Plugin_TGA(); extern void* make_Plugin_CAnalysis(); @@ -67,36 +70,6 @@ extern void* make_Plugin_CRN(); // Setup Static Host Pluging Libs extern void CMP_RegisterHostPlugins(); -#ifdef OPTION_BUILD_SHARED_LIBS -#if !OPTION_BUILD_SHARED_LIBS -// Plugins that may optionally be dynamic libraries -#if OPTION_CMP_DIRECTX -extern void* make_Plugin_glTF_DX12_EX(); -extern void* make_Plugin_GPUDecode_DirectX(); -extern void* make_Plugin_Mesh_Tootle(); -#endif - -#if OPTION_CMP_OPENGL -extern void* make_Plugin_glTF_OpenGL(); -extern void* make_Plugin_GPUDecode_OpenGL(); -#endif - -#if OPTION_CMP_VULKAN -extern void* make_Plugin_GPUDecode_Vulkan(); -extern void* make_Plugin_3DModelViewer_Vulkan(); -#endif - -extern void* make_Plugin_Mesh_Compressor(); -extern void* make_Plugin_Mesh_Optimizer(); - -#if PLUGIN_MODEL_LOADERS -extern void* make_Plugin_glTF_Loader(); -extern void* make_Plugin_ModelLoader_drc(); -extern void* make_Plugin_obj_Loader(); -#endif -#endif -#endif - #define SEPERATOR_STYLE "QMainWindow::separator { background-color: #d7d6d5; width: 3px; height: 3px; border:none; }" #define PERCENTAGE_OF_MONITOR_WIDTH_FOR_SCREEN 0.65 #define PERCENTAGE_OF_MONITOR_HEIGHT_FOR_SCREEN 0.8 @@ -226,7 +199,7 @@ int main(int argc, char** argv) { QString dirPath = QApplication::applicationDirPath(); #if __APPLE__ - std::string contentPath = std::filesystem::path(dirPath.toStdString()) / "../"; + std::string contentPath = sfs::path(dirPath.toStdString()) / "../"; dirPath = QString(contentPath.c_str()); QApplication::addLibraryPath(dirPath + "./PlugIns/platforms/"); QApplication::addLibraryPath(dirPath + "./PlugIns/"); @@ -236,7 +209,7 @@ int main(int argc, char** argv) { QApplication::addLibraryPath(dirPath + "./plugins/"); #endif - app.setWindowIcon(QIcon(":/CompressonatorGUI/Images/acompress-256.png")); + app.setWindowIcon(QIcon(":/compressonatorgui/images/acompress-256.png")); // ========================== // Mip Settings Class @@ -244,7 +217,7 @@ int main(int argc, char** argv) { g_GUI_CMIPS = new CMIPS; g_CMIPS = new CMIPS; - const QIcon iconPixMap(":/CompressonatorGUI/Images/compress.png"); + const QIcon iconPixMap(":/compressonatorgui/images/compress.png"); const QString ProductName = "Compressonator"; //---------------------------------- @@ -253,47 +226,21 @@ int main(int argc, char** argv) { g_pluginManager.registerStaticPlugin("IMAGE", "ASTC", (void*)make_Plugin_ASTC); g_pluginManager.registerStaticPlugin("IMAGE", "EXR", (void*)make_Plugin_EXR); g_pluginManager.registerStaticPlugin("IMAGE", "KTX", (void*)make_Plugin_KTX); - -#ifdef USE_CRN - g_pluginManager.registerStaticPlugin("IMAGE", "CRN", (void*)make_Plugin_CRN); -#endif - - // TGA is supported by Qt to some extent if it fails we will try to load it using our custom code g_pluginManager.registerStaticPlugin("IMAGE", "TGA", (void*)make_Plugin_TGA); g_pluginManager.registerStaticPlugin("IMAGE", "ANALYSIS", (void*)make_Plugin_CAnalysis); - g_pluginManager.getPluginList("/plugins", true); - - CMP_RegisterHostPlugins(); - -#ifdef OPTION_BUILD_SHARED_LIBS -#if !OPTION_BUILD_SHARED_LIBS -#if OPTION_CMP_DIRECTX - g_pluginManager.registerStaticPlugin("3DMODEL_VIEWER", "DX12_EX", (void*)make_Plugin_glTF_DX12_EX); - g_pluginManager.registerStaticPlugin("GPUDECODE", "DIRECTX", (void*)make_Plugin_GPUDecode_DirectX); - g_pluginManager.registerStaticPlugin("MESH_OPTIMIZER", "TOOTLE", (void*)make_Plugin_Mesh_Tootle); +#ifdef _WIN32 + g_pluginManager.registerStaticPlugin("IMAGE", "KTX2", (void*)make_Plugin_KTX2); #endif -#if OPTION_CMP_OPENGL - g_pluginManager.registerStaticPlugin("3DMODEL_VIEWER", "OPENGL", (void*)make_Plugin_glTF_OpenGL); - g_pluginManager.registerStaticPlugin("GPUDECODE", "OPENGL", (void*)make_Plugin_GPUDecode_OpenGL); +#ifdef USE_CRN + g_pluginManager.registerStaticPlugin("IMAGE", "CRN", (void*)make_Plugin_CRN); #endif -#if OPTION_CMP_VULKAN - g_pluginManager.registerStaticPlugin("3DMODEL_VIEWER", "VULKAN", (void*)make_Plugin_3DModelViewer_Vulkan); - g_pluginManager.registerStaticPlugin("GPUDECODE", "VULKAN", (void*)make_Plugin_GPUDecode_Vulkan); -#endif -#if PLUGIN_MODEL_LOADERS - g_pluginManager.registerStaticPlugin("3DMODEL_LOADER", "DRC", (void*)make_Plugin_ModelLoader_drc); - g_pluginManager.registerStaticPlugin("3DMODEL_LOADER", "GLTF", (void*)make_Plugin_glTF_Loader); - g_pluginManager.registerStaticPlugin("3DMODEL_LOADER", "OBJ", (void*)make_Plugin_obj_Loader); -#endif - g_pluginManager.registerStaticPlugin("MESH_COMPRESSOR", "DRACO", (void*)make_Plugin_Mesh_Compressor); - g_pluginManager.registerStaticPlugin("MESH_OPTIMIZER", "TOOTLE_MESH", (void*)make_Plugin_Mesh_Optimizer); + g_pluginManager.getPluginList("/plugins", true); -#endif -#endif + CMP_RegisterHostPlugins(); g_bAbortCompression = false; diff --git a/clang-tidy b/clang-tidy new file mode 100644 index 000000000..7500d9cc9 --- /dev/null +++ b/clang-tidy @@ -0,0 +1,52 @@ +Checks: bugprone-*,clang-analyzer-*,clang-diagnostic-*,google-*,misc-*,modernize-*,-modernize-use-trailing-return-type,-modernize-concat-nested-namespaces,performance-*,readability-*,-modernize-use-auto +WarningsAsErrors: bugprone-*,clang-analyzer-*,clang-diagnostic-*,google-*,misc-*,modernize-*,performance-*,readability-* +FormatStyle: file +CheckOptions: + - key: readability-identifier-naming.ClassCase + value: CamelCase + - key: readability-identifier-naming.ClassConstantCase + value: CamelCase + - key: readability-identifier-naming.ClassConstantPrefix + value: k + - key: readability-identifier-naming.EnumCase + value: CamelCase + - key: readability-identifier-naming.EnumConstantCase + value: CamelCase + - key: readability-identifier-naming.EnumConstantPrefix + value: k + - key: readability-identifier-naming.FunctionCase + value: CamelCase + - key: readability-identifier-naming.GlobalConstantCase + value: CamelCase + - key: readability-identifier-naming.GlobalConstantPrefix + value: k + - key: readability-identifier-naming.GlobalConstantPointerCase + value: CamelCase + - key: readability-identifier-naming.GlobalConstantPointerPrefix + value: k + - key: readability-identifier-naming.MethodCase + value: CamelCase + - key: readability-identifier-naming.NamespaceCase + value: lower_case + - key: readability-identifier-naming.ParameterCase + value: lower_case + - key: readability-identifier-naming.PrivateMemberCase + value: lower_case + - key: readability-identifier-naming.PrivateMemberSuffix + value: _ + - key: readability-identifier-naming.PublicMemberCase + value: lower_case + - key: readability-identifier-naming.StaticConstantCase + value: CamelCase + - key: readability-identifier-naming.StaticConstantPrefix + value: k + - key: readability-identifier-naming.TemplateParameterCase + value: CamelCase + - key: readability-identifier-naming.TypeAliasCase + value: CamelCase + - key: readability-identifier-naming.TypedefCase + value: CamelCase + - key: readability-identifier-naming.UnionCase + value: CamelCase + - key: readability-identifier-naming.VariableCase + value: lower_case diff --git a/cmakelists.txt b/cmakelists.txt index e0f62712b..bffb5bc8f 100644 --- a/cmakelists.txt +++ b/cmakelists.txt @@ -1,87 +1,58 @@ -cmake_minimum_required(VERSION 3.13) +cmake_minimum_required(VERSION 3.10) -# if(POLICY CMP0091) -# cmake_policy(SET CMP0091 NEW) -# endif() - -project(Compressonator) +if(POLICY CMP0091) + cmake_policy(SET CMP0091 NEW) +endif() -enable_testing() +if(POLICY CMP0076) + cmake_policy(SET CMP0076 NEW) +endif() -set(CMAKE_CXX_STANDARD 17) +project(Compressonator) +# ------------------------------ +# Helper function for the build +# ------------------------------ include(cmake/helperfunctions.cmake) set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "Supported configuration options" FORCE) set(CMAKE_INSTALL_PREFIX "${PROJECT_SOURCE_DIR}/build-out") -cmp_option(OPTION_BUILD_GUI "Build the GUI Application" (CMP_HOST_WINDOWS)) -cmp_option(OPTION_BUILD_EXAMPLES "Build examples" (CMP_HOST_WINDOWS)) -cmp_option(OPTION_BUILD_TESTING "Build tests" (CMP_HOST_WINDOWS)) -cmp_option(OPTION_BUILD_SHARED_LIBS "Build SHARED, if disabled STATIC is built" (BUILD_SHARED_LIBS)) +set(PROJECT_EXTERNAL_LIBDIR ${PROJECT_SOURCE_DIR}/../common/lib/ext) + +# ------------------------------ +# User options for the build +# ------------------------------ +if(${CMAKE_VERSION} VERSION_GREATER "3.14.0") +cmp_option(OPTION_BUILD_KTX2 "Build CLI KTX2 Support" ON) +else() +cmp_option(OPTION_BUILD_KTX2 "Build CLI KTX2 Support" OFF) +endif() + +cmp_option(OPTION_BUILD_EXR "Build CLI EXR Support" CMP_HOST_WINDOWS) +cmp_option(OPTION_BUILD_DRACO "Build CLI Draco Support" CMP_HOST_WINDOWS) -cmp_option(OPTION_CMP_DIRECTX "Use Directx" (CMP_HOST_WINDOWS)) # set for windows only +cmp_option(OPTION_BUILD_GUI "Build the GUI Application" CMP_HOST_WINDOWS) +cmp_option(OPTION_BUILD_TESTING "Build tests" CMP_HOST_WINDOWS) +cmp_option(OPTION_CMP_DIRECTX "Use Directx" CMP_HOST_WINDOWS) # set for windows only cmp_option(OPTION_CMP_VULKAN "Use Vulkan" ON) # set for windows, can be on other platforms when current code validated! cmp_option(OPTION_CMP_OPENGL "Use OpenGL" ON) # available on all platforms cmp_option(OPTION_USE_QT_IMAGELOAD "Use Qt for Image Loading" ON) -cmp_option(OPTION_DISABLE_TESTCODE "Remove test code" OFF) cmp_option(NO_LEGACY_BEHAVIOR "Disables any legacy behavior (before CMake migration)" ON) # this needs to be removed -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) - -if (OPTION_BUILD_TESTING) - enable_testing() -endif() -if (OPTION_DISABLE_TESTCODE) - add_compile_definitions(DISABLE_TESTCODE=1) -endif () -if (OPTION_USE_QT_IMAGELOAD) - add_compile_definitions(USE_QT_IMAGELOAD=1) -endif() - -if (OPTION_BUILD_SHARED_LIBS) - add_compile_definitions( - BUILD_AS_PLUGIN_DLL=1 - CMP_BUILD_SHARED_LIBS=1 - ) -else() - add_compile_definitions( - CMP_BUILD_SHARED_LIBS=0 - ) -endif() - -if (APPLE OR UNIX) - add_compile_definitions(_LINUX=1) - add_definitions(-std=c++17) -endif () - -if (NO_LEGACY_BEHAVIOR) - add_compile_definitions(NO_LEGACY_BEHAVIOR=1) -endif() - -if (CMP_HOST_WINDOWS) - # add_compile_definitions(USE_NewLoader=1) - add_compile_definitions(WIN32_LEAN_AND_MEAN=1) - set(CMAKE_EXE_LINKER_FLAGS "/INCREMENTAL:NO") -endif () - -# '_DEBUG' is only defined by Visual Studio. Make sure it's definied everywhere -add_compile_definitions($<$:_DEBUG>) - -# Additional options +# ----------------------------------- +# Code build definitions and features +# ----------------------------------- add_compile_definitions( USE_QTWEBKIT # Enable Qt Webengine interfaces for GUI welcome page USE_ETCPACK # Use ETCPack for ETC2 else use CModel code! USE_MESH_CLI # CLI Process Mesh (only support glTF and OBJ files) ENABLE_MAKE_COMPATIBLE_API # Byte<->Float to make all source and dest compatible USE_OLD_SWIZZLE # Remove swizzle flag and abide by CMP_Formats - USE_MESH_DRACO_EXTENSION # Mesh Compression with Draco support in glTF and OBJ files only USE_QTWEBKIT # Enable Qt Webengine interfaces for GUI welcome page # USE_CONVECTION_KERNELS # Test External Lib : ConvectionKernels for quality& performance, to enable use q=0.1 in HPC @@ -118,56 +89,145 @@ add_compile_definitions( #ENABLE_USER_ETC2S_FORMATS # Enable users to set these formats in CLI and GUI applications ) -# Disable code signing -set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO") -set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "") +# Additional options +if (OPTION_BUILD_DRACO) + add_compile_definitions( + USE_MESH_DRACO_EXTENSION # Mesh Compression with Draco support in glTF and OBJ files only + USE_KTX_EXTENSION # Add KTX file support + ) +endif() + + +if (OPTION_USE_QT_IMAGELOAD) + add_compile_definitions(USE_QT_IMAGELOAD=1) +endif() + +set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "Supported configuration options" FORCE) +set(CMAKE_INSTALL_PREFIX "${PROJECT_SOURCE_DIR}/build-out") +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +# '_DEBUG' is only defined by Visual Studio. Make sure it's definied everywhere +add_compile_definitions($<$:_DEBUG>) + + +# ----------------------------------- +# Set or get all package requirements +# ----------------------------------- if (CMP_HOST_WINDOWS) + set(CMAKE_CXX_STANDARD 17) + add_compile_definitions(WIN32_LEAN_AND_MEAN=1 _CMP_CPP17_=1 _WIN32) + set(CMAKE_EXE_LINKER_FLAGS "/INCREMENTAL:NO") + # Configure Externals, they must be added via 'include' before the applications include(${CMAKE_ROOT}/modules/externalproject.cmake) set_property(GLOBAL PROPERTY USE_FOLDERS ON) -endif() - -if (NOT CMP_HOST_APPLE) - set(CMAKE_INSTALL_RPATH "$ORIGIN") - set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) -endif() + include(external/cmake/dependencyinfo.cmake) + include(external/cmake/CMakeLists.txt) + include(external/CMakeLists.txt) +else() + set(CMAKE_CXX_STANDARD 14) + #gltf code needs _LINUX define + add_compile_definitions(_LINUX=1) -# find_package(OpenCV) -# if (OpenCV_FOUND) -# target_include_directories(Analysis -# PRIVATE -# ${OpenCV_INCLUDE_DIRS}) -# else() -# message(FATAL_ERROR "Package OpenCV are required, but not found. In Unix, run initsetup_unix.sh or sudo apt-get install libopencv-dev to install the libs.") -# endif() -# -# # Qt5 include path - users install required -# find_package(Qt5Gui HINTS ${QT_PACKAGE_ROOT}) -# -# if(Qt5Gui_FOUND) -# target_include_directories(Plugin_CAnalysis -# PRIVATE -# "${Qt5Gui_INCLUDE_DIRS}") -# else() -# message(FATAL_ERROR "Package Qt5 (Qt5Gui) are required, but not found. In Unix, run initsetup_unix.sh or sudo apt-get install qtdeclarative5-dev. If is window, please make sure install qt and add the bin path (i.e. C:\Qt\5.7\msvc2015_64\bin) to environment PATH.") -# endif() - - -# Configure Externals, they must be added via 'include' before the applications -include(external/cmake/dependencyinfo.cmake) -include(external/cmake/CMakeLists.txt) -include(external/CMakeLists.txt) - -# add_subdirectory(Compressonator ${CMAKE_BINARY_DIR}/Compressonator) + add_definitions(-std=c++14 -fPIC) + # experimental filesystem support for c++14 + link_libraries(stdc++fs) + + # ---------------------------------- + # Package requirements for the Build + # ---------------------------------- + find_package(Threads REQUIRED) + set(OpenGL_GL_PREFERENCE "GLVND") + + find_package(Qt5 COMPONENTS Gui Widgets OpenGL Qml WebEngineWidgets Xml REQUIRED HINTS ${QT_PACKAGE_ROOT}) + if (Qt5Gui_FOUND) + else() + message(FATAL_ERROR "Package Qt5 (Qt5Gui) are required, but not found. " + "In Unix, run initsetup_unix.sh or sudo apt-get install " + "qtdeclarative5-dev. If is window, please make sure install qt and " + "add the bin path (i.e. C:\Qt\5.7\msvc2015_64\bin) to environment PATH.") + endif() + + if (UNIX) + find_package(PkgConfig REQUIRED) + + # OpenEXRConfig.cmake is broken on Linux, use PkgConfig + # find_package(OpenEXR REQUIRED) + pkg_check_modules(OpenEXR REQUIRED OpenEXR) + + # Draco cmake find scripts are also broken (looks for src/ path in install directory) + # find_package(Draco REQUIRED) + pkg_check_modules(draco draco) + if(draco_FOUND) + else() + # TODO: To do this properly we require a generated config header from CMake + # Which can be done with configure_file. + # See: https://cmake.org/cmake/help/latest/command/configure_file.html + add_definitions(-DCMAKE_DRACO_NOT_FOUND) + endif() + + find_package(OpenCV REQUIRED) + if (OpenCV_FOUND) + else() + message(FATAL_ERROR "Package OpenCV are required, but not found. " + "In Unix, run initsetup_unix.sh or sudo apt-get install " + "libopencv-dev to install the libs.") + endif() + + endif() +endif() +# These are the minimum build includes for both Windows / Linux / Mac +# Libs add_subdirectory(cmp_core) -add_subdirectory(cmp_framework) +add_subdirectory(cmp_framework) add_subdirectory(cmp_compressonatorlib) -add_subdirectory(applications/_libs) -add_subdirectory(applications/_plugins) -add_subdirectory(applications) +add_subdirectory(applications/_plugins/cimage/astc) +add_subdirectory(applications/_plugins/cimage/dds) +add_subdirectory(applications/_plugins/cimage/exr) +add_subdirectory(applications/_plugins/cimage/ktx) +add_subdirectory(applications/_plugins/cimage/tga) +add_subdirectory(applications/_plugins/canalysis) +add_subdirectory(applications/_plugins/common) +add_subdirectory(applications/_libs/cmp_meshcompressor) # Currently only draco based +add_subdirectory(applications/_libs/cmp_meshoptimizer) +add_subdirectory(applications/_libs/gpu_decode) +add_subdirectory(applications/_plugins/common/qtimgui) + +# Applications +add_subdirectory(applications/compressonatorcli) +add_subdirectory(applications/compressonatorgui) + +# Extended Plugins and required libs on Windows +if (CMP_HOST_WINDOWS) -if (OPTION_BUILD_EXAMPLES) + add_subdirectory(applications/_plugins/cimage/ktx2) + add_subdirectory(applications/_plugins/cgpudecode) add_subdirectory(examples) + + if (OPTION_BUILD_TESTING) + add_subdirectory(cmp_core/test) + # add_subdirectory(applications/compressonatorcli/tests) + endif() + + add_subdirectory(applications/_libs/cmp_mesh) + add_subdirectory(applications/_libs/cmp_math) + add_subdirectory(applications/_plugins/cmesh/tootle) + add_subdirectory(applications/_plugins/cmesh/mesh_optimizer) + add_subdirectory(applications/_plugins/cmesh/mesh_compressor) + + # Features for GUI only + add_subdirectory(applications/_plugins/c3dmodel_loaders) + add_subdirectory(applications/_plugins/c3dmodel_viewers) + + # EncodeWith plugins (DXC, OCL and GPU) + add_subdirectory(applications/_plugins/cmp_gpu) + + # mipmap filters and effects + add_subdirectory(applications/_plugins/cfilter) + add_subdirectory(applications/_plugins/cfilter_fx) + endif() diff --git a/cmp_compressonatorlib/astc/codec_astc.cpp b/cmp_compressonatorlib/astc/codec_astc.cpp index d1091ba50..6093327a7 100644 --- a/cmp_compressonatorlib/astc/codec_astc.cpp +++ b/cmp_compressonatorlib/astc/codec_astc.cpp @@ -578,14 +578,20 @@ CodecError CCodec_ASTC::Compress(CCodecBuffer& bufferIn, CCodecBuffer& bufferOut int bitness = 0; //todo: replace astc_codec_image with bufferIn and rewrite fetch_imageblock() switch (bufferIn.GetBufferType()) { - case CBT_RGBA8888: case CBT_BGRA8888: case CBT_ARGB8888: + case CBT_RGBA8888: case CBT_RGB888: case CBT_RG8: case CBT_R8: bitness = 8; break; + case CBT_RGBA8888S: + case CBT_RGB888S: + case CBT_RG8S: + case CBT_R8S: + bitness = 8; + break; case CBT_RGBA2101010: break; case CBT_RGBA16: diff --git a/cmp_compressonatorlib/ati/codec_ati1n.cpp b/cmp_compressonatorlib/ati/codec_ati1n.cpp index 81477adf7..de0e15813 100644 --- a/cmp_compressonatorlib/ati/codec_ati1n.cpp +++ b/cmp_compressonatorlib/ati/codec_ati1n.cpp @@ -201,8 +201,6 @@ CodecError CCodec_ATI1N::Decompress(CCodecBuffer& bufferIn, CCodecBuffer& buffer //------------------------------------------------------------------- - - ////////////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////////////// @@ -230,9 +228,6 @@ CodecError CCodec_ATI1N_S::Compress(CCodecBuffer& bufferIn, Codec_Feedback_Proc pFeedbackProc, CMP_DWORD_PTR pUser1, CMP_DWORD_PTR pUser2) { - if ((m_nCompressionSpeed == CMP_Speed_Fast || m_nCompressionSpeed == CMP_Speed_SuperFast) && m_bUseSSE) - return Compress_Fast(bufferIn, bufferOut, pFeedbackProc, pUser1, pUser2); - assert(bufferIn.GetWidth() == bufferOut.GetWidth()); assert(bufferIn.GetHeight() == bufferOut.GetHeight()); @@ -242,63 +237,40 @@ CodecError CCodec_ATI1N_S::Compress(CCodecBuffer& bufferIn, const CMP_DWORD dwBlocksX = ((bufferIn.GetWidth() + 3) >> 2); const CMP_DWORD dwBlocksY = ((bufferIn.GetHeight() + 3) >> 2); - bool bUseFixed = (!bufferIn.IsFloat() && bufferIn.GetChannelDepth() == 8 && !m_bUseFloat); - for (CMP_DWORD j = 0; j < dwBlocksY; j++) { for (CMP_DWORD i = 0; i < dwBlocksX; i++) { CMP_DWORD compressedBlock[2]; - if (bUseFixed) { + float AlphaBlockSNorm[BLOCK_SIZE_4X4]; + + if (bufferIn.GetFormat() == CMP_FORMAT_RGBA_8888_S) + { + CMP_SBYTE cAlphaBlock[BLOCK_SIZE_4X4]; + + // Our input red is in the correct offset position of the source buffer + bufferIn.ReadBlockR(i * 4, j * 4, 4, 4, cAlphaBlock); + + for (int i = 0; i < BLOCK_SIZE_4X4; i++) + { + AlphaBlockSNorm[i] = cAlphaBlock[i] / 127.0f; + } + } + else + { CMP_BYTE cAlphaBlock[BLOCK_SIZE_4X4]; + if (bufferIn.m_bSwizzle) // Our input red is in the blue offset position of the source buffer bufferIn.ReadBlockB(i * 4, j * 4, 4, 4, cAlphaBlock); else bufferIn.ReadBlockR(i * 4, j * 4, 4, 4, cAlphaBlock); - CompressAlphaBlock(cAlphaBlock, compressedBlock); - } else { - float fAlphaBlock[BLOCK_SIZE_4X4]; - if (bufferIn.m_bSwizzle) - bufferIn.ReadBlockB(i * 4, j * 4, 4, 4, fAlphaBlock); - else - bufferIn.ReadBlockR(i * 4, j * 4, 4, 4, fAlphaBlock); - CompressAlphaBlock(fAlphaBlock, compressedBlock); - } - bufferOut.WriteBlock(i * 4, j * 4, compressedBlock, 2); - } - - if (pFeedbackProc) { - float fProgress = 100.f * (j * dwBlocksX) / (dwBlocksX * dwBlocksY); - if (pFeedbackProc(fProgress, pUser1, pUser2)) - return CE_Aborted; - } - } - - return CE_OK; -} - -CodecError CCodec_ATI1N_S::Compress_Fast(CCodecBuffer& bufferIn, - CCodecBuffer& bufferOut, - Codec_Feedback_Proc pFeedbackProc, - CMP_DWORD_PTR pUser1, - CMP_DWORD_PTR pUser2) { - assert(bufferIn.GetWidth() == bufferOut.GetWidth()); - assert(bufferIn.GetHeight() == bufferOut.GetHeight()); - if (bufferIn.GetWidth() != bufferOut.GetWidth() || bufferIn.GetHeight() != bufferOut.GetHeight()) - return CE_Unknown; - - const CMP_DWORD dwBlocksX = ((bufferIn.GetWidth() + 3) >> 2); - const CMP_DWORD dwBlocksY = ((bufferIn.GetHeight() + 3) >> 2); - - for (CMP_DWORD j = 0; j < dwBlocksY; j++) { - for (CMP_DWORD i = 0; i < dwBlocksX; i++) { - CMP_DWORD compressedBlock[2]; - CMP_BYTE cAlphaBlock[BLOCK_SIZE_4X4]; + // Convert UINT -> SNORM + for (int i = 0; i < BLOCK_SIZE_4X4; i++) + { + AlphaBlockSNorm[i] = ((cAlphaBlock[i]/255.0f)*2.0f - 1.0f); + } + } - if (bufferIn.m_bSwizzle) - bufferIn.ReadBlockB(i * 4, j * 4, 4, 4, cAlphaBlock); - else - bufferIn.ReadBlockR(i * 4, j * 4, 4, 4, cAlphaBlock); - CompressAlphaBlock_Fast(cAlphaBlock, compressedBlock); + CompressAlphaBlockSNorm(AlphaBlockSNorm, compressedBlock); bufferOut.WriteBlock(i * 4, j * 4, compressedBlock, 2); } @@ -327,45 +299,33 @@ CodecError CCodec_ATI1N_S::Decompress(CCodecBuffer& bufferIn, const CMP_DWORD dwBlocksY = ((bufferIn.GetHeight() + 3) >> 2); const CMP_DWORD dwBlocksXY = dwBlocksX * dwBlocksY; - bool bUseFixed = (!bufferOut.IsFloat() && bufferOut.GetChannelDepth() == 8 && !m_bUseFloat); - // Init alpha channel - CMP_BYTE alpha[BLOCK_SIZE_4X4]; - CMP_FLOAT alphaF[BLOCK_SIZE_4X4]; + CMP_SBYTE alpha[BLOCK_SIZE_4X4]; for (CMP_BYTE i = 0; i < BLOCK_SIZE_4X4; i++) { - alpha[i] = 0xFF; - alphaF[i] = FLT_MAX; + alpha[i] = 127; } for (CMP_DWORD j = 0; j < dwBlocksY; j++) { for (CMP_DWORD i = 0; i < dwBlocksX; i++) { CMP_DWORD compressedBlock[2]; bufferIn.ReadBlock(i * 4, j * 4, compressedBlock, 2); - - if (bUseFixed) { #ifdef TEST_CMP_CORE_DECODER CMP_BYTE ATI1NBlock[BLOCK_SIZE_4X4]; - DecompressBlockBC4((CMP_BYTE*)compressedBlock, ATI1NBlock); + DecompressAlphaBlockInt8((const int8_t*)compressedBlock, (int8_t*)ATI1NBlock, NULL); + bufferOut.WriteBlockR(i * 4, j * 4, 4, 4, ATI1NBlock); bufferOut.WriteBlockG(i * 4, j * 4, 4, 4, ATI1NBlock); bufferOut.WriteBlockB(i * 4, j * 4, 4, 4, ATI1NBlock); bufferOut.WriteBlockA(i * 4, j * 4, 4, 4, alpha); #else - CMP_BYTE ATI1NBlock[BLOCK_SIZE_4X4]; - DecompressAlphaBlock(ATI1NBlock, compressedBlock); + CMP_SBYTE ATI1NBlock[BLOCK_SIZE_4X4]; + DecompressAlphaBlockInt8(ATI1NBlock, compressedBlock); + bufferOut.WriteBlockR(i * 4, j * 4, 4, 4, ATI1NBlock); bufferOut.WriteBlockG(i * 4, j * 4, 4, 4, ATI1NBlock); bufferOut.WriteBlockB(i * 4, j * 4, 4, 4, ATI1NBlock); bufferOut.WriteBlockA(i * 4, j * 4, 4, 4, alpha); #endif - } else { - float ATI1NBlock[BLOCK_SIZE_4X4]; - DecompressAlphaBlock(ATI1NBlock, compressedBlock); - bufferOut.WriteBlockR(i * 4, j * 4, 4, 4, ATI1NBlock); - bufferOut.WriteBlockG(i * 4, j * 4, 4, 4, ATI1NBlock); - bufferOut.WriteBlockB(i * 4, j * 4, 4, 4, ATI1NBlock); - bufferOut.WriteBlockA(i * 4, j * 4, 4, 4, alphaF); - } } if (pFeedbackProc) { diff --git a/cmp_compressonatorlib/ati/codec_ati1n.h b/cmp_compressonatorlib/ati/codec_ati1n.h index a30207092..ce812f31d 100644 --- a/cmp_compressonatorlib/ati/codec_ati1n.h +++ b/cmp_compressonatorlib/ati/codec_ati1n.h @@ -31,6 +31,7 @@ #include "codec_dxtc.h" +// Also used as BC4 class CCodec_ATI1N : public CCodec_DXTC { public: CCodec_ATI1N(CodecType codecType = CT_ATI1N); @@ -45,8 +46,7 @@ class CCodec_ATI1N : public CCodec_DXTC { CMP_DWORD dwWidth, CMP_DWORD dwHeight, CMP_DWORD dwPitch = 0, CMP_BYTE* pData = 0,CMP_DWORD dwDataSize = 0) const; }; - - +// Also used as BC4_S class CCodec_ATI1N_S : public CCodec_DXTC { public: CCodec_ATI1N_S(CodecType codecType = CT_ATI1N_S); @@ -57,11 +57,7 @@ class CCodec_ATI1N_S : public CCodec_DXTC { Codec_Feedback_Proc pFeedbackProc = NULL, CMP_DWORD_PTR pUser1 = NULL, CMP_DWORD_PTR pUser2 = NULL); - virtual CodecError Compress_Fast(CCodecBuffer& bufferIn, - CCodecBuffer& bufferOut, - Codec_Feedback_Proc pFeedbackProc = NULL, - CMP_DWORD_PTR pUser1 = NULL, - CMP_DWORD_PTR pUser2 = NULL); + virtual CodecError Decompress(CCodecBuffer& bufferIn, CCodecBuffer& bufferOut, Codec_Feedback_Proc pFeedbackProc = NULL, diff --git a/cmp_compressonatorlib/ati/codec_ati2n.cpp b/cmp_compressonatorlib/ati/codec_ati2n.cpp index ed456137f..572eb72b7 100644 --- a/cmp_compressonatorlib/ati/codec_ati2n.cpp +++ b/cmp_compressonatorlib/ati/codec_ati2n.cpp @@ -61,8 +61,8 @@ CodecError CCodec_ATI2N::Compress(CCodecBuffer& bufferIn, CCodecBuffer& bufferOu const CMP_DWORD dwBlocksX = ((bufferIn.GetWidth() + 3) >> 2); const CMP_DWORD dwBlocksY = ((bufferIn.GetHeight() + 3) >> 2); - const CMP_DWORD dwXOffset = (GetType() == CT_ATI2N) ? 2 : 0; - const CMP_DWORD dwYOffset = (GetType() == CT_ATI2N) ? 0 : 2; + const CMP_DWORD dwXOffset = 0; //(GetType() == CT_ATI2N) ? 2 : 0; + const CMP_DWORD dwYOffset = 2; //(GetType() == CT_ATI2N) ? 0 : 2; bool bUseFixed = (!bufferIn.IsFloat() && bufferIn.GetChannelDepth() == 8 && !m_bUseFloat); @@ -116,8 +116,8 @@ CodecError CCodec_ATI2N::Compress_Fast(CCodecBuffer& bufferIn, CCodecBuffer& buf const CMP_DWORD dwBlocksX = ((bufferIn.GetWidth() + 3) >> 2); const CMP_DWORD dwBlocksY = ((bufferIn.GetHeight() + 3) >> 2); - const CMP_DWORD dwXOffset = (GetType() == CT_ATI2N) ? 2 : 0; - const CMP_DWORD dwYOffset = (GetType() == CT_ATI2N) ? 0 : 2; + const CMP_DWORD dwXOffset = 0; // (GetType() == CT_ATI2N) ? 2 : 0; + const CMP_DWORD dwYOffset = 2; // (GetType() == CT_ATI2N) ? 0 : 2; for(CMP_DWORD j = 0; j < dwBlocksY; j++) { CMP_DWORD compressedBlock[4]; @@ -222,3 +222,181 @@ CodecError CCodec_ATI2N::Decompress(CCodecBuffer& bufferIn, CCodecBuffer& buffer return CE_OK; } +//====================== BC5 Signed Encoder + +CCodec_ATI2N_S::CCodec_ATI2N_S(CodecType codecType) + : CCodec_DXTC(codecType) +{ + m_codecType = codecType; +} + +CCodec_ATI2N_S::~CCodec_ATI2N_S() +{ +} + +CodecError CCodec_ATI2N_S::Compress(CCodecBuffer& bufferIn, + CCodecBuffer& bufferOut, + Codec_Feedback_Proc pFeedbackProc, + CMP_DWORD_PTR pUser1, + CMP_DWORD_PTR pUser2) +{ + + assert(bufferIn.GetWidth() == bufferOut.GetWidth()); + assert(bufferIn.GetHeight() == bufferOut.GetHeight()); + + if (bufferIn.GetWidth() != bufferOut.GetWidth() || bufferIn.GetHeight() != bufferOut.GetHeight()) + return CE_Unknown; + + const CMP_DWORD dwBlocksX = ((bufferIn.GetWidth() + 3) >> 2); + const CMP_DWORD dwBlocksY = ((bufferIn.GetHeight() + 3) >> 2); + + const CMP_DWORD dwXOffset = 0; // (GetType() == CT_ATI2N) ? 2 : 0; + const CMP_DWORD dwYOffset = 2; // (GetType() == CT_ATI2N) ? 0 : 2; + + bool bUseFixed = (!bufferIn.IsFloat() && bufferIn.GetChannelDepth() == 8 && !m_bUseFloat); + + for (CMP_DWORD j = 0; j < dwBlocksY; j++) + { + CMP_DWORD compressedBlock[4]; + for (CMP_DWORD i = 0; i < dwBlocksX; i++) + { + if (bufferIn.GetFormat() == CMP_FORMAT_RGBA_8888_S) + { + + CMP_SBYTE cAlphaBlock[BLOCK_SIZE_4X4]; + + bufferIn.ReadBlockR(i * 4, j * 4, 4, 4, cAlphaBlock); + + // Convert to sbyte to float + float AlphaBlockSNorm[BLOCK_SIZE_4X4]; + + for (int i = 0; i < BLOCK_SIZE_4X4; i++) + { + AlphaBlockSNorm[i] = (cAlphaBlock[i] / 127.0f); + } + + CompressAlphaBlockSNorm(AlphaBlockSNorm, &compressedBlock[dwXOffset]); + + bufferIn.ReadBlockG(i * 4, j * 4, 4, 4, cAlphaBlock); + + // conversion of ubyte to signed normalized /(2^7 -1) + for (int i = 0; i < BLOCK_SIZE_4X4; i++) + { + AlphaBlockSNorm[i] = (cAlphaBlock[i] / 127.0f); + } + + CompressAlphaBlockSNorm(AlphaBlockSNorm, &compressedBlock[dwYOffset]); + } + else + { + CMP_BYTE cAlphaBlock[BLOCK_SIZE_4X4]; + + if (bufferIn.m_bSwizzle) + bufferIn.ReadBlockB(i * 4, j * 4, 4, 4, cAlphaBlock); // <=?? this is actually reading the red channel + else + bufferIn.ReadBlockR(i * 4, j * 4, 4, 4, cAlphaBlock); + + // Convert to sbyte to float + float AlphaBlockSNorm[BLOCK_SIZE_4X4]; + + // Convert UINT -> SNORM + for (int i = 0; i < BLOCK_SIZE_4X4; i++) + { + AlphaBlockSNorm[i] = ((cAlphaBlock[i] / 255.0f) * 2.0f - 1.0f); + } + + + CompressAlphaBlockSNorm(AlphaBlockSNorm, &compressedBlock[dwXOffset]); + + bufferIn.ReadBlockG(i * 4, j * 4, 4, 4, cAlphaBlock); + + // Convert UINT -> SNORM + for (int i = 0; i < BLOCK_SIZE_4X4; i++) + { + AlphaBlockSNorm[i] = ((cAlphaBlock[i] / 255.0f) * 2.0f - 1.0f); + } + + CompressAlphaBlockSNorm(AlphaBlockSNorm, &compressedBlock[dwYOffset]); + } + + bufferOut.WriteBlock(i * 4, j * 4, compressedBlock, 4); + } + if (pFeedbackProc) + { + float fProgress = 100.f * (j * dwBlocksX) / (dwBlocksX * dwBlocksY); + if (pFeedbackProc(fProgress, pUser1, pUser2)) + return CE_Aborted; + } + } + + return CE_OK; +} + +CodecError CCodec_ATI2N_S::Decompress(CCodecBuffer& bufferIn, + CCodecBuffer& bufferOut, + Codec_Feedback_Proc pFeedbackProc, + CMP_DWORD_PTR pUser1, + CMP_DWORD_PTR pUser2) +{ + assert(bufferIn.GetWidth() == bufferOut.GetWidth()); + assert(bufferIn.GetHeight() == bufferOut.GetHeight()); + + if (bufferIn.GetWidth() != bufferOut.GetWidth() || bufferIn.GetHeight() != bufferOut.GetHeight()) + return CE_Unknown; + + const CMP_DWORD dwBlocksX = ((bufferIn.GetWidth() + 3) >> 2); + const CMP_DWORD dwBlocksY = ((bufferIn.GetHeight() + 3) >> 2); + const CMP_DWORD dwBlocksXY = dwBlocksX * dwBlocksY; + + const CMP_DWORD dwXOffset = 0; // (GetType() == CT_ATI2N) ? 2 : 0; + const CMP_DWORD dwYOffset = 2; // (GetType() == CT_ATI2N) ? 0 : 2; + + bool bUseFixed = (!bufferOut.IsFloat() && bufferOut.GetChannelDepth() == 8 && !m_bUseFloat); + + CMP_SBYTE alphaBlockA[BLOCK_SIZE_4X4]; + CMP_SBYTE alphaBlockB[BLOCK_SIZE_4X4]; + + memset(alphaBlockA, 127, sizeof(alphaBlockA)); + memset(alphaBlockB, 0, sizeof(alphaBlockB)); + + CMP_SBYTE alphaBlockR[BLOCK_SIZE_4X4]; + CMP_SBYTE alphaBlockG[BLOCK_SIZE_4X4]; + CMP_DWORD compressedBlock[4]; + + for (CMP_DWORD j = 0; j < dwBlocksY; j++) + { + for (CMP_DWORD i = 0; i < dwBlocksX; i++) + { + bufferIn.ReadBlock(i * 4, j * 4, compressedBlock, 4); + +#ifdef TEST_CMP_CORE_DECODER + DecompressBlockBC5((CMP_BYTE*)&compressedBlock[dwXOffset], alphaBlockR, alphaBlockG); + bufferOut.WriteBlockB(i * 4, j * 4, 4, 4, alphaBlockR); + bufferOut.WriteBlockG(i * 4, j * 4, 4, 4, alphaBlockG); + bufferOut.WriteBlockR(i * 4, j * 4, 4, 4, alphaBlockB); + bufferOut.WriteBlockA(i * 4, j * 4, 4, 4, alphaBlockA); +#else + DecompressAlphaBlockInt8(alphaBlockR, &compressedBlock[dwXOffset]); + DecompressAlphaBlockInt8(alphaBlockG, &compressedBlock[dwYOffset]); + bufferOut.WriteBlockR(i * 4, j * 4, 4, 4, alphaBlockR); + bufferOut.WriteBlockG(i * 4, j * 4, 4, 4, alphaBlockG); + bufferOut.WriteBlockB(i * 4, j * 4, 4, 4, alphaBlockB); + bufferOut.WriteBlockA(i * 4, j * 4, 4, 4, alphaBlockA); +#endif + + } + + if (pFeedbackProc) + { + float fProgress = 100.f * (j * dwBlocksX) / dwBlocksXY; + if (pFeedbackProc(fProgress, pUser1, pUser2)) + { + return CE_Aborted; + } + } + } + + return CE_OK; +} + + diff --git a/cmp_compressonatorlib/ati/codec_ati2n.h b/cmp_compressonatorlib/ati/codec_ati2n.h index f321b8af8..303b903c6 100644 --- a/cmp_compressonatorlib/ati/codec_ati2n.h +++ b/cmp_compressonatorlib/ati/codec_ati2n.h @@ -44,4 +44,27 @@ class CCodec_ATI2N : public CCodec_DXTC { CodecType m_codecType; }; +class CCodec_ATI2N_S : public CCodec_DXTC +{ +public: + CCodec_ATI2N_S(CodecType codecType = CT_ATI2N); + virtual ~CCodec_ATI2N_S(); + + virtual CodecError Compress(CCodecBuffer& bufferIn, + CCodecBuffer& bufferOut, + Codec_Feedback_Proc pFeedbackProc = NULL, + CMP_DWORD_PTR pUser1 = NULL, + CMP_DWORD_PTR pUser2 = NULL); + + virtual CodecError Decompress(CCodecBuffer& bufferIn, + CCodecBuffer& bufferOut, + Codec_Feedback_Proc pFeedbackProc = NULL, + CMP_DWORD_PTR pUser1 = NULL, + CMP_DWORD_PTR pUser2 = NULL); + +protected: + CodecType m_codecType; +}; + + #endif // !defined(_CODEC_ATI2N_H_INCLUDED_) diff --git a/cmp_compressonatorlib/ati/compressonatorxcodec.cpp b/cmp_compressonatorlib/ati/compressonatorxcodec.cpp index f1a0146bd..9ef9125a5 100644 --- a/cmp_compressonatorlib/ati/compressonatorxcodec.cpp +++ b/cmp_compressonatorlib/ati/compressonatorxcodec.cpp @@ -2524,7 +2524,9 @@ static CODECFLOAT CompBlock1(CODECFLOAT _RmpPnts[NUM_ENDPOINTS], [OUT] Min amd M static CODECFLOAT CompBlock1(CODECFLOAT _RmpPnts[NUM_ENDPOINTS], CODECFLOAT _Blk[MAX_BLOCK], int _Nmbr, CMP_BYTE dwNumPoints, bool bFixedRampPoints, - int _IntPrc, int _FracPrc, bool _bFixedRamp, bool _bUseSSE2) { + int _IntPrc, int _FracPrc, bool _bFixedRamp, bool _bUseSSE2, + bool isSigned + ) { CODECFLOAT fMaxError = 0.f; CODECFLOAT Ramp[NUM_ENDPOINTS]; @@ -2727,7 +2729,7 @@ CODECFLOAT CompBlock1X(CODECFLOAT* _Blk, CMP_WORD dwBlockSize, CMP_BYTE nEndpoin // this one makes the bulk of the work CODECFLOAT Ramp[NUM_ENDPOINTS]; - CompBlock1(Ramp, _Blk, dwBlockSize, dwNumPoints, bFixedRampPoints, _intPrec, _fracPrec, _bFixedRamp, _bUseSSE2); + CompBlock1(Ramp, _Blk, dwBlockSize, dwNumPoints, bFixedRampPoints, _intPrec, _fracPrec, _bFixedRamp, _bUseSSE2, false); // final clusterization applied CODECFLOAT fError = Clstr1(pcIndices, _Blk, Ramp, dwBlockSize, dwNumPoints, bFixedRampPoints, _intPrec, _fracPrec, _bFixedRamp); @@ -2737,6 +2739,36 @@ CODECFLOAT CompBlock1X(CODECFLOAT* _Blk, CMP_WORD dwBlockSize, CMP_BYTE nEndpoin return fError; } +CODECFLOAT CompBlock1XS(CODECFLOAT* _Blk, + CMP_WORD dwBlockSize, + CMP_BYTE nEndpoints[2], + CMP_BYTE* pcIndices, + CMP_BYTE dwNumPoints, + bool bFixedRampPoints, + bool _bUseSSE2, + int _intPrec, + int _fracPrec, + bool _bFixedRamp) +{ + // just to make them initialized + if (!_bFixedRamp) + { + _intPrec = 8; + _fracPrec = 0; + } + + // this one makes the bulk of the work + CODECFLOAT Ramp[NUM_ENDPOINTS]; + CompBlock1(Ramp, _Blk, dwBlockSize, dwNumPoints, bFixedRampPoints, _intPrec, _fracPrec, _bFixedRamp, _bUseSSE2, true); + + // final clusterization applied + CODECFLOAT fError = Clstr1(pcIndices, _Blk, Ramp, dwBlockSize, dwNumPoints, bFixedRampPoints, _intPrec, _fracPrec, _bFixedRamp); + nEndpoints[0] = (BYTE)Ramp[0]; + nEndpoints[1] = (BYTE)Ramp[1]; + + return fError; +} + /*-------------------------------------------------------------------------------------------- // input [0,255] void CompBlock1X(CMP_BYTE* _Blk, [IN] scalar data block (alphas or normals) in 8 bits format @@ -2759,3 +2791,23 @@ CODECFLOAT CompBlock1X(CMP_BYTE* _Blk, CMP_WORD dwBlockSize, CMP_BYTE nEndpoints return CompBlock1X(fBlk, dwBlockSize, nEndpoints, pcIndices, dwNumPoints, bFixedRampPoints, _bUseSSE2, _intPrec, _fracPrec, _bFixedRamp); } + + +CODECFLOAT CompBlock1XS(CMP_SBYTE* _Blk, + CMP_WORD dwBlockSize, + CMP_BYTE nEndpoints[2], + CMP_BYTE* pcIndices, + CMP_BYTE dwNumPoints, + bool bFixedRampPoints, + bool _bUseSSE2, + int _intPrec, + int _fracPrec, + bool _bFixedRamp) +{ + // convert the input and call the float equivalent. + CODECFLOAT fBlk[MAX_BLOCK]; + for (int i = 0; i < dwBlockSize; i++) + fBlk[i] = (CODECFLOAT)_Blk[i] / 128.f; + + return CompBlock1X(fBlk, dwBlockSize, nEndpoints, pcIndices, dwNumPoints, bFixedRampPoints, _bUseSSE2, _intPrec, _fracPrec, _bFixedRamp); +} diff --git a/cmp_compressonatorlib/ati/compressonatorxcodec.h b/cmp_compressonatorlib/ati/compressonatorxcodec.h index d6f3e5094..cb7506649 100644 --- a/cmp_compressonatorlib/ati/compressonatorxcodec.h +++ b/cmp_compressonatorlib/ati/compressonatorxcodec.h @@ -97,6 +97,18 @@ CODECFLOAT CompBlock1X(CODECFLOAT* _Blk, int _fracPrec = 0, bool _bFixedRamp = true); +CODECFLOAT CompBlock1XS(CODECFLOAT* _Blk, + CMP_WORD dwBlockSize, + CMP_BYTE nEndpoints[2], + CMP_BYTE* pcIndices, + CMP_BYTE dwNumPoints, + bool bFixedRampPoints, + bool _bUseSSE2 = true, + int _intPrec = 8, + int _fracPrec = 0, + bool _bFixedRamp = true); + + /*-------------------------------------------------------------------------------------------- // input [0,255] void CompBlock1X(CMP_BYTE* _Blk, [IN] scalar data block (alphas or normals) in 8 bits format @@ -121,4 +133,15 @@ CODECFLOAT CompBlock1X(CMP_BYTE* _Blk, int _fracPrec = 0, bool _bFixedRamp = true); +CODECFLOAT CompBlock1XS(CMP_SBYTE* _Blk, + CMP_WORD dwBlockSize, + CMP_BYTE nEndpoints[2], + CMP_BYTE* pcIndices, + CMP_BYTE dwNumPoints, + bool bFixedRampPoints, + bool _bUseSSE2 = true, + int _intPrec = 8, + int _fracPrec = 0, + bool _bFixedRamp = true); + #endif \ No newline at end of file diff --git a/cmp_compressonatorlib/bc6h/bc6h_encode.cpp b/cmp_compressonatorlib/bc6h/bc6h_encode.cpp index d63178387..8efa294c2 100644 --- a/cmp_compressonatorlib/bc6h/bc6h_encode.cpp +++ b/cmp_compressonatorlib/bc6h/bc6h_encode.cpp @@ -481,11 +481,11 @@ void BC6HBlockEncoder::SwapIndices(int iEndPoints[MAX_SUBSETS][MAX_END_POINTS][M size_t i = subset?g_Region2FixUp[shape_pattern]:0; if(iIndices[subset][i] & uHighIndexBit) { + // high bit is set, swap the aEndPts and indices for this region std::swap(iEndPoints[subset][0][0], iEndPoints[subset][1][0]); std::swap(iEndPoints[subset][0][1], iEndPoints[subset][1][1]); std::swap(iEndPoints[subset][0][2], iEndPoints[subset][1][2]); - for(size_t j = 0; j < (size_t)entryCount[subset]; ++j) { iIndices[subset][j] = uNumIndices - 1 - iIndices[subset][j] ; } @@ -1379,9 +1379,9 @@ float BC6HBlockEncoder::CompressBlock(float in[MAX_SUBSET_SIZE][MAX_DIMENSION_BI // so use 0..0x7BFF and sign bit for the floats // using if ( < 0.00001) to avoid case of values been -0.0 which is not processed when using if ( < 0) - if (in[i][0] < 0.00001 || isnan(in[i][0])) { + if (in[i][0] < 0.00001 || cmp_isnan(in[i][0])) { if (m_isSigned) { - BC6H_data.din[i][0] = (isnan(in[i][0]))? F16NEGPREC_LIMIT_VAL : -CMP_HALF(abs(in[i][0] / normalization)).bits(); + BC6H_data.din[i][0] = (cmp_isnan(in[i][0]))? F16NEGPREC_LIMIT_VAL : -CMP_HALF(abs(in[i][0] / normalization)).bits(); if (BC6H_data.din[i][0] < F16NEGPREC_LIMIT_VAL) { BC6H_data.din[i][0] = F16NEGPREC_LIMIT_VAL; } @@ -1390,9 +1390,9 @@ float BC6HBlockEncoder::CompressBlock(float in[MAX_SUBSET_SIZE][MAX_DIMENSION_BI } else BC6H_data.din[i][0] = CMP_HALF(in[i][0] / normalization).bits(); - if (in[i][1] < 0.00001 || isnan(in[i][1])) { + if (in[i][1] < 0.00001 || cmp_isnan(in[i][1])) { if (m_isSigned) { - BC6H_data.din[i][1] = (isnan(in[i][1])) ? F16NEGPREC_LIMIT_VAL : -CMP_HALF(abs(in[i][1] / normalization)).bits(); + BC6H_data.din[i][1] = (cmp_isnan(in[i][1])) ? F16NEGPREC_LIMIT_VAL : -CMP_HALF(abs(in[i][1] / normalization)).bits(); if (BC6H_data.din[i][1] < F16NEGPREC_LIMIT_VAL) { BC6H_data.din[i][1] = F16NEGPREC_LIMIT_VAL; } @@ -1401,9 +1401,9 @@ float BC6HBlockEncoder::CompressBlock(float in[MAX_SUBSET_SIZE][MAX_DIMENSION_BI } else BC6H_data.din[i][1] = CMP_HALF(in[i][1] / normalization).bits(); - if (in[i][2] < 0.00001 || isnan(in[i][2])) { + if (in[i][2] < 0.00001 || cmp_isnan(in[i][2])) { if (m_isSigned) { - BC6H_data.din[i][2] = (isnan(in[i][2])) ? F16NEGPREC_LIMIT_VAL : -CMP_HALF(abs(in[i][2] / normalization)).bits(); + BC6H_data.din[i][2] = (cmp_isnan(in[i][2])) ? F16NEGPREC_LIMIT_VAL : -CMP_HALF(abs(in[i][2] / normalization)).bits(); if (BC6H_data.din[i][2] < F16NEGPREC_LIMIT_VAL) { BC6H_data.din[i][2] = F16NEGPREC_LIMIT_VAL; } diff --git a/cmp_compressonatorlib/buffer/codecbuffer.cpp b/cmp_compressonatorlib/buffer/codecbuffer.cpp index 119f9ab20..b07e3d95b 100644 --- a/cmp_compressonatorlib/buffer/codecbuffer.cpp +++ b/cmp_compressonatorlib/buffer/codecbuffer.cpp @@ -33,6 +33,10 @@ #include "codecbuffer_rgb888.h" #include "codecbuffer_rg8.h" #include "codecbuffer_r8.h" +#include "codecbuffer_rgba8888s.h" +#include "codecbuffer_rgb888s.h" +#include "codecbuffer_rg8s.h" +#include "codecbuffer_r8s.h" #include "codecbuffer_rgba2101010.h" #include "codecbuffer_rgba16.h" #include "codecbuffer_rg16.h" @@ -68,6 +72,14 @@ CCodecBuffer* CreateCodecBuffer(CodecBufferType nCodecBufferType, return new CCodecBuffer_RG8(nBlockWidth, nBlockHeight, nBlockDepth, dwWidth, dwHeight, dwPitch, pData,dwDataSize); case CBT_R8: return new CCodecBuffer_R8(nBlockWidth, nBlockHeight, nBlockDepth, dwWidth, dwHeight, dwPitch, pData,dwDataSize); + case CBT_RGBA8888S: + return new CCodecBuffer_RGBA8888S(nBlockWidth, nBlockHeight, nBlockDepth, dwWidth, dwHeight, dwPitch,(CMP_SBYTE *)pData, dwDataSize); + case CBT_RGB888S: + return new CCodecBuffer_RGB888S(nBlockWidth, nBlockHeight, nBlockDepth, dwWidth, dwHeight, dwPitch, pData, dwDataSize); + case CBT_RG8S: + return new CCodecBuffer_RG8S(nBlockWidth, nBlockHeight, nBlockDepth, dwWidth, dwHeight, dwPitch, pData, dwDataSize); + case CBT_R8S: + return new CCodecBuffer_R8S(nBlockWidth, nBlockHeight, nBlockDepth, dwWidth, dwHeight, dwPitch, pData, dwDataSize); case CBT_RGBA2101010: return new CCodecBuffer_RGBA2101010(nBlockWidth, nBlockHeight, nBlockDepth, dwWidth, dwHeight, dwPitch, pData,dwDataSize); case CBT_RGBA16: @@ -175,6 +187,9 @@ CodecBufferType GetCodecBufferType(CMP_FORMAT format) { case CMP_FORMAT_ARGB_2101010: CBT_type = CBT_RGBA2101010; break; + case CMP_FORMAT_RGBA_8888_S: // Need to expand on this format + CBT_type = CBT_RGBA8888S; + break; case CMP_FORMAT_ARGB_8888: // Need to expand on this format case CMP_FORMAT_BGRA_8888: // Need to expand on this format case CMP_FORMAT_RGBA_8888: @@ -184,12 +199,21 @@ CodecBufferType GetCodecBufferType(CMP_FORMAT format) { case CMP_FORMAT_RGB_888: CBT_type = CBT_RGB888; break; + case CMP_FORMAT_RGB_888_S: + CBT_type = CBT_RGB888S; + break; case CMP_FORMAT_RG_8: CBT_type = CBT_RG8; break; + case CMP_FORMAT_RG_8_S: + CBT_type = CBT_RG8S; + break; case CMP_FORMAT_R_8: CBT_type = CBT_R8; break; + case CMP_FORMAT_R_8_S: + CBT_type = CBT_R8S; + break; default: CBT_type = CBT_Unknown; break; @@ -366,6 +390,119 @@ bool CCodecBuffer::ReadBlockA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, } } +bool CCodecBuffer::ReadBlockR(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE cBlock[]) +{ +#ifdef USE_DBGTRACE + DbgTrace(()); +#endif + // Ok, so we don't support this format + // So we try other formats to find one that is supported + + if (m_bPerformingConversion) + { + return false; + } + else + { + m_bPerformingConversion = true; + + ATTEMPT_BLOCK_READ(cBlock, R, double); + ATTEMPT_BLOCK_READ(cBlock, R, float); + ATTEMPT_BLOCK_READ(cBlock, R, CMP_HALF); + ATTEMPT_BLOCK_READ(cBlock, R, CMP_DWORD); + ATTEMPT_BLOCK_READ(cBlock, R, CMP_WORD); + + assert(0); + m_bPerformingConversion = false; + return false; + } +} + +bool CCodecBuffer::ReadBlockG(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE cBlock[]) +{ +#ifdef USE_DBGTRACE + DbgTrace(()); +#endif + // Ok, so we don't support this format + // So we try other formats to find one that is supported + + if (m_bPerformingConversion) + { + return false; + } + else + { + m_bPerformingConversion = true; + + ATTEMPT_BLOCK_READ(cBlock, G, double); + ATTEMPT_BLOCK_READ(cBlock, G, float); + ATTEMPT_BLOCK_READ(cBlock, G, CMP_HALF); + ATTEMPT_BLOCK_READ(cBlock, G, CMP_DWORD); + ATTEMPT_BLOCK_READ(cBlock, G, CMP_WORD); + + assert(0); + m_bPerformingConversion = false; + return false; + } +} + +bool CCodecBuffer::ReadBlockB(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE cBlock[]) +{ +#ifdef USE_DBGTRACE + DbgTrace(()); +#endif + // Ok, so we don't support this format + // So we try other formats to find one that is supported + + if (m_bPerformingConversion) + { + return false; + } + else + { + m_bPerformingConversion = true; + + ATTEMPT_BLOCK_READ(cBlock, B, double); + ATTEMPT_BLOCK_READ(cBlock, B, float); + ATTEMPT_BLOCK_READ(cBlock, B, CMP_HALF); + ATTEMPT_BLOCK_READ(cBlock, B, CMP_DWORD); + ATTEMPT_BLOCK_READ(cBlock, B, CMP_WORD); + + assert(0); + m_bPerformingConversion = false; + return false; + } +} + +bool CCodecBuffer::ReadBlockA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE cBlock[]) +{ +#ifdef USE_DBGTRACE + DbgTrace(()); +#endif + // Ok, so we don't support this format + // So we try other formats to find one that is supported + + if (m_bPerformingConversion) + { + return false; + } + else + { + m_bPerformingConversion = true; + + ATTEMPT_BLOCK_READ(cBlock, A, double); + ATTEMPT_BLOCK_READ(cBlock, A, float); + ATTEMPT_BLOCK_READ(cBlock, A, CMP_HALF); + ATTEMPT_BLOCK_READ(cBlock, A, CMP_DWORD); + ATTEMPT_BLOCK_READ(cBlock, A, CMP_WORD); + + assert(0); + m_bPerformingConversion = false; + return false; + } +} + + bool CCodecBuffer::ReadBlockR(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_WORD wBlock[]) { #ifdef USE_DBGTRACE DbgTrace(()); @@ -953,6 +1090,123 @@ bool CCodecBuffer::WriteBlockA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, } } +bool CCodecBuffer::WriteBlockR(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE cBlock[]) +{ +#ifdef USE_DBGTRACE + DbgTrace(()); +#endif + // Ok, so we don't support this format + // So we try other formats to find one that is supported + + if (m_bPerformingConversion) + { + return false; + } + else + { + m_bPerformingConversion = true; + + ATTEMPT_BLOCK_WRITE(cBlock, R, CMP_BYTE); + ATTEMPT_BLOCK_WRITE(cBlock, R, CMP_DWORD); + ATTEMPT_BLOCK_WRITE(cBlock, R, CMP_WORD); + ATTEMPT_BLOCK_WRITE(cBlock, R, double); + ATTEMPT_BLOCK_WRITE(cBlock, R, float); + ATTEMPT_BLOCK_WRITE(cBlock, R, CMP_HALF); + + assert(0); + m_bPerformingConversion = false; + return false; + } +} + +bool CCodecBuffer::WriteBlockG(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE cBlock[]) +{ +#ifdef USE_DBGTRACE + DbgTrace(()); +#endif + // Ok, so we don't support this format + // So we try other formats to find one that is supported + + if (m_bPerformingConversion) + { + return false; + } + else + { + m_bPerformingConversion = true; + + ATTEMPT_BLOCK_WRITE(cBlock, G, CMP_BYTE); + ATTEMPT_BLOCK_WRITE(cBlock, G, CMP_DWORD); + ATTEMPT_BLOCK_WRITE(cBlock, G, CMP_WORD); + ATTEMPT_BLOCK_WRITE(cBlock, G, double); + ATTEMPT_BLOCK_WRITE(cBlock, G, float); + ATTEMPT_BLOCK_WRITE(cBlock, G, CMP_HALF); + + assert(0); + m_bPerformingConversion = false; + return false; + } +} + +bool CCodecBuffer::WriteBlockB(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE cBlock[]) +{ +#ifdef USE_DBGTRACE + DbgTrace(()); +#endif + // Ok, so we don't support this format + // So we try other formats to find one that is supported + + if (m_bPerformingConversion) + { + return false; + } + else + { + m_bPerformingConversion = true; + + ATTEMPT_BLOCK_WRITE(cBlock, B, CMP_BYTE); + ATTEMPT_BLOCK_WRITE(cBlock, B, CMP_DWORD); + ATTEMPT_BLOCK_WRITE(cBlock, B, CMP_WORD); + ATTEMPT_BLOCK_WRITE(cBlock, B, double); + ATTEMPT_BLOCK_WRITE(cBlock, B, float); + ATTEMPT_BLOCK_WRITE(cBlock, B, CMP_HALF); + + assert(0); + m_bPerformingConversion = false; + return false; + } +} + +bool CCodecBuffer::WriteBlockA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE cBlock[]) +{ +#ifdef USE_DBGTRACE + DbgTrace(()); +#endif + // Ok, so we don't support this format + // So we try other formats to find one that is supported + + if (m_bPerformingConversion) + { + return false; + } + else + { + m_bPerformingConversion = true; + + ATTEMPT_BLOCK_WRITE(cBlock, A, CMP_BYTE); + ATTEMPT_BLOCK_WRITE(cBlock, A, CMP_DWORD); + ATTEMPT_BLOCK_WRITE(cBlock, A, CMP_WORD); + ATTEMPT_BLOCK_WRITE(cBlock, A, double); + ATTEMPT_BLOCK_WRITE(cBlock, A, float); + ATTEMPT_BLOCK_WRITE(cBlock, A, CMP_HALF); + + assert(0); + m_bPerformingConversion = false; + return false; + } +} + + bool CCodecBuffer::WriteBlockR(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_WORD wBlock[]) { #ifdef USE_DBGTRACE DbgTrace(()); @@ -1433,6 +1687,141 @@ bool CCodecBuffer::WriteBlockA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, } } +bool CCodecBuffer::WriteBlockRGBA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE cBlock[]) +{ +#ifdef USE_DBGTRACE + DbgTrace(()); +#endif + // Ok, so we don't support this format + // So we try other formats to find one that is supported + + if (m_bPerformingConversion) + { + return false; + } + else + { + m_bPerformingConversion = true; + + CMP_DWORD dwBlock[MAX_BLOCK * 4]; + ConvertBlock(dwBlock, cBlock, w * h * 4); + SwizzleBlock(dwBlock, w * h); + if (WriteBlockRGBA(x, y, w, h, dwBlock)) + { + m_bPerformingConversion = false; + return true; + } + + CMP_WORD wBlock[MAX_BLOCK * 4]; + ConvertBlock(wBlock, cBlock, w * h * 4); + SwizzleBlock(wBlock, w * h); + if (WriteBlockRGBA(x, y, w, h, wBlock)) + { + m_bPerformingConversion = false; + return true; + } + + double dBlock[MAX_BLOCK * 4]; + ConvertBlock(dBlock, cBlock, w * h * 4); + SwizzleBlock(dBlock, w * h); + if (WriteBlockRGBA(x, y, w, h, dBlock)) + { + m_bPerformingConversion = false; + return true; + } + + float fBlock[MAX_BLOCK * 4]; + ConvertBlock(fBlock, cBlock, w * h * 4); + SwizzleBlock(fBlock, w * h); + if (WriteBlockRGBA(x, y, w, h, fBlock)) + { + m_bPerformingConversion = false; + return true; + } + + CMP_HALF hBlock[MAX_BLOCK * 4]; + ConvertBlock(hBlock, cBlock, w * h * 4); + SwizzleBlock(hBlock, w * h); + if (WriteBlockRGBA(x, y, w, h, hBlock)) + { + m_bPerformingConversion = false; + return true; + } + + assert(0); + m_bPerformingConversion = false; + return false; + } +} + +bool CCodecBuffer::ReadBlockRGBA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE cBlock[]) +{ +#ifdef USE_DBGTRACE + DbgTrace(()); +#endif + // Ok, so we don't support this format + // So we try other formats to find one that is supported + + if (m_bPerformingConversion) + { + return false; + } + else + { + m_bPerformingConversion = true; + + double dBlock[MAX_BLOCK * 4]; + if (ReadBlockRGBA(x, y, w, h, dBlock)) + { + SwizzleBlock(dBlock, w * h); + ConvertBlock(cBlock, dBlock, w * h * 4); + m_bPerformingConversion = false; + return true; + } + + float fBlock[MAX_BLOCK * 4]; + if (ReadBlockRGBA(x, y, w, h, fBlock)) + { + SwizzleBlock(fBlock, w * h); + ConvertBlock(cBlock, fBlock, w * h * 4); + m_bPerformingConversion = false; + return true; + } + + CMP_HALF hBlock[MAX_BLOCK * 4]; + if (ReadBlockRGBA(x, y, w, h, hBlock)) + { + SwizzleBlock(hBlock, w * h); + ConvertBlock(cBlock, hBlock, w * h * 4); + m_bPerformingConversion = false; + return true; + } + + CMP_DWORD dwBlock[MAX_BLOCK * 4]; + if (ReadBlockRGBA(x, y, w, h, dwBlock)) + { + SwizzleBlock(dwBlock, w * h); + ConvertBlock(cBlock, dwBlock, w * h * 4); + m_bPerformingConversion = false; + return true; + } + + CMP_WORD wBlock[MAX_BLOCK * 4]; + if (ReadBlockRGBA(x, y, w, h, wBlock)) + { + SwizzleBlock(wBlock, w * h); + ConvertBlock(cBlock, wBlock, w * h * 4); + m_bPerformingConversion = false; + return true; + } + + assert(0); + m_bPerformingConversion = false; + return false; + } +} + + bool CCodecBuffer::WriteBlockRGBA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE cBlock[]) { #ifdef USE_DBGTRACE DbgTrace(()); @@ -1730,7 +2119,7 @@ bool CCodecBuffer::ReadBlockRGBA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE return true; } - CMP_DWORD dwBlock[MAX_BLOCK]; + CMP_DWORD dwBlock[MAX_BLOCK*4]; if(ReadBlockRGBA(x, y, w, h, dwBlock)) { ConvertBlock(wBlock, (CMP_BYTE*) dwBlock, w*h*4); SwizzleBlock(wBlock, w*h); @@ -1738,6 +2127,14 @@ bool CCodecBuffer::ReadBlockRGBA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE return true; } + unsigned char cBlock[MAX_BLOCK]; + if (ReadBlockRGBA(x, y, w, h, cBlock)) + { + ConvertBlock(wBlock, cBlock, w * h); + m_bPerformingConversion = false; + return true; + } + assert(0); m_bPerformingConversion = false; return false; @@ -2043,6 +2440,7 @@ bool CCodecBuffer::ReadBlock(CMP_DWORD /*x*/, CMP_DWORD /*y*/, CMP_DWORD* /*pBlo return false; } + void CCodecBuffer::ConvertBlock(double dBlock[], float fBlock[], CMP_DWORD dwBlockSize) { #ifdef USE_DBGTRACE DbgTrace(()); @@ -2112,6 +2510,22 @@ void CCodecBuffer::ConvertBlock(double dBlock[], CMP_BYTE cBlock[], CMP_DWORD dw } } +void CCodecBuffer::ConvertBlock(double dBlock[], CMP_SBYTE cBlock[], CMP_DWORD dwBlockSize) +{ +#ifdef USE_DBGTRACE + DbgTrace(()); +#endif + + assert(dBlock); + assert(cBlock); + assert(dwBlockSize); + if (dBlock && cBlock && dwBlockSize) + { + for (CMP_DWORD i = 0; i < dwBlockSize; i++) + dBlock[i] = CONVERT_BYTE_TO_FLOAT(cBlock[i]); + } +} + void CCodecBuffer::ConvertBlock(float fBlock[], double dBlock[], CMP_DWORD dwBlockSize) { #ifdef USE_DBGTRACE DbgTrace(()); @@ -2182,6 +2596,22 @@ void CCodecBuffer::ConvertBlock(float fBlock[], CMP_BYTE cBlock[], CMP_DWORD dwB } } +void CCodecBuffer::ConvertBlock(float fBlock[], CMP_SBYTE cBlock[], CMP_DWORD dwBlockSize) +{ +#ifdef USE_DBGTRACE + DbgTrace(()); +#endif + + assert(fBlock); + assert(cBlock); + assert(dwBlockSize); + if (fBlock && cBlock && dwBlockSize) + { + for (CMP_DWORD i = 0; i < dwBlockSize; i++) + fBlock[i] = CONVERT_SBYTE_TO_FLOAT(cBlock[i]); + } +} + void CCodecBuffer::ConvertBlock(CMP_HALF hBlock[], double dBlock[], CMP_DWORD dwBlockSize) { #ifdef USE_DBGTRACE DbgTrace(()); @@ -2252,6 +2682,22 @@ void CCodecBuffer::ConvertBlock(CMP_HALF hBlock[], CMP_BYTE cBlock[], CMP_DWORD } } +void CCodecBuffer::ConvertBlock(CMP_HALF hBlock[], CMP_SBYTE cBlock[], CMP_DWORD dwBlockSize) +{ +#ifdef USE_DBGTRACE + DbgTrace(()); +#endif + + assert(hBlock); + assert(cBlock); + assert(dwBlockSize); + if (hBlock && cBlock && dwBlockSize) + { + for (CMP_DWORD i = 0; i < dwBlockSize; i++) + hBlock[i] = CONVERT_SBYTE_TO_FLOAT(cBlock[i]); + } +} + void CCodecBuffer::ConvertBlock(CMP_DWORD dwBlock[], double dBlock[], CMP_DWORD dwBlockSize) { #ifdef USE_DBGTRACE DbgTrace(()); @@ -2322,6 +2768,22 @@ void CCodecBuffer::ConvertBlock(CMP_DWORD dwBlock[], CMP_BYTE cBlock[], CMP_DWOR } } +void CCodecBuffer::ConvertBlock(CMP_DWORD dwBlock[], CMP_SBYTE cBlock[], CMP_DWORD dwBlockSize) +{ +#ifdef USE_DBGTRACE + DbgTrace(()); +#endif + + assert(dwBlock); + assert(cBlock); + assert(dwBlockSize); + if (dwBlock && cBlock && dwBlockSize) + { + for (CMP_DWORD i = 0; i < dwBlockSize; i++) + dwBlock[i] = CONVERT_BYTE_TO_DWORD(cBlock[i]); + } +} + void CCodecBuffer::ConvertBlock(CMP_WORD wBlock[], double dBlock[], CMP_DWORD dwBlockSize) { #ifdef USE_DBGTRACE DbgTrace(()); @@ -2392,6 +2854,22 @@ void CCodecBuffer::ConvertBlock(CMP_WORD wBlock[], CMP_BYTE cBlock[], CMP_DWORD } } +void CCodecBuffer::ConvertBlock(CMP_WORD wBlock[], CMP_SBYTE cBlock[], CMP_DWORD dwBlockSize) +{ +#ifdef USE_DBGTRACE + DbgTrace(()); +#endif + + assert(wBlock); + assert(cBlock); + assert(dwBlockSize); + if (wBlock && cBlock && dwBlockSize) + { + for (CMP_DWORD i = 0; i < dwBlockSize; i++) + wBlock[i] = CONVERT_SBYTE_TO_WORD(cBlock[i]); + } +} + void CCodecBuffer::ConvertBlock(CMP_BYTE cBlock[], double dBlock[], CMP_DWORD dwBlockSize) { #ifdef USE_DBGTRACE DbgTrace(()); @@ -2462,6 +2940,123 @@ void CCodecBuffer::ConvertBlock(CMP_BYTE cBlock[], CMP_WORD wBlock[], CMP_DWORD } } +//============= THESE NEW CALLS NEED VALIDATION =========================== + +void CCodecBuffer::ConvertBlock(CMP_SBYTE cBlock[], double dBlock[], CMP_DWORD dwBlockSize) +{ +#ifdef USE_DBGTRACE + DbgTrace(()); +#endif + + assert(cBlock); + assert(dBlock); + assert(dwBlockSize); + if (cBlock && dBlock && dwBlockSize) + { + for (CMP_DWORD i = 0; i < dwBlockSize; i++) + cBlock[i] = CONVERT_FLOAT_TO_SBYTE(dBlock[i]); + } +} + +void CCodecBuffer::ConvertBlock(CMP_SBYTE cBlock[], float fBlock[], CMP_DWORD dwBlockSize) +{ +#ifdef USE_DBGTRACE + DbgTrace(()); +#endif + + assert(cBlock); + assert(fBlock); + assert(dwBlockSize); + if (cBlock && fBlock && dwBlockSize) + { + for (CMP_DWORD i = 0; i < dwBlockSize; i++) + cBlock[i] = CONVERT_FLOAT_TO_SBYTE(fBlock[i]); + } +} + +void CCodecBuffer::ConvertBlock(CMP_SBYTE cBlock[], CMP_HALF hBlock[], CMP_DWORD dwBlockSize) +{ +#ifdef USE_DBGTRACE + DbgTrace(()); +#endif + + assert(cBlock); + assert(hBlock); + assert(dwBlockSize); + if (cBlock && hBlock && dwBlockSize) + { + for (CMP_DWORD i = 0; i < dwBlockSize; i++) + cBlock[i] = CONVERT_FLOAT_TO_SBYTE(hBlock[i]); + } +} + +void CCodecBuffer::ConvertBlock(CMP_SBYTE cBlock[], CMP_DWORD dwBlock[], CMP_DWORD dwBlockSize) +{ +#ifdef USE_DBGTRACE + DbgTrace(()); +#endif + + assert(cBlock); + assert(dwBlock); + assert(dwBlockSize); + if (cBlock && dwBlock && dwBlockSize) + { + for (CMP_DWORD i = 0; i < dwBlockSize; i++) + cBlock[i] = CONVERT_DWORD_TO_SBYTE(dwBlock[i]); + } +} + +void CCodecBuffer::ConvertBlock(CMP_SBYTE cBlock[], CMP_WORD wBlock[], CMP_DWORD dwBlockSize) +{ +#ifdef USE_DBGTRACE + DbgTrace(()); +#endif + + assert(cBlock); + assert(wBlock); + assert(dwBlockSize); + if (cBlock && wBlock && dwBlockSize) + { + for (CMP_DWORD i = 0; i < dwBlockSize; i++) + cBlock[i] = CONVERT_WORD_TO_SBYTE(wBlock[i]); + } +} + +void CCodecBuffer::ConvertBlock(CMP_SBYTE sBlock[], CMP_BYTE cBlock[], CMP_DWORD dwBlockSize) +{ +#ifdef USE_DBGTRACE + DbgTrace(()); +#endif + + assert(sBlock); + assert(cBlock); + assert(dwBlockSize); + if (cBlock && cBlock && dwBlockSize) + { + for (CMP_DWORD i = 0; i < dwBlockSize; i++) + sBlock[i] = cBlock[i] - 127; + } +} + +void CCodecBuffer::ConvertBlock(CMP_BYTE cBlock[], CMP_SBYTE sBlock[], CMP_DWORD dwBlockSize) +{ +#ifdef USE_DBGTRACE + DbgTrace(()); +#endif + + assert(cBlock); + assert(sBlock); + assert(dwBlockSize); + if (cBlock && sBlock && dwBlockSize) + { + for (CMP_DWORD i = 0; i < dwBlockSize; i++) + cBlock[i] = sBlock[i] + 127; + } +} + +//============ END OF NEW CODE =========================================== + + void CCodecBuffer::SwizzleBlock(double dBlock[], CMP_DWORD dwBlockSize) { #ifdef USE_DBGTRACE DbgTrace(()); diff --git a/cmp_compressonatorlib/buffer/codecbuffer.h b/cmp_compressonatorlib/buffer/codecbuffer.h index f5e20e445..b009124c1 100644 --- a/cmp_compressonatorlib/buffer/codecbuffer.h +++ b/cmp_compressonatorlib/buffer/codecbuffer.h @@ -45,6 +45,10 @@ typedef enum _CodecBufferType { CBT_BGR888, // Reserved for future work CBT_RG8, CBT_R8, + CBT_RGBA8888S, + CBT_RGB888S, + CBT_RG8S, + CBT_R8S, CBT_RGBA2101010, CBT_RGBA16, CBT_RG16, @@ -159,10 +163,13 @@ typedef enum _CodecBufferType { #define SWIZZLE_xGxR_RGBA(dsw) ((((dsw >> RGBA8888_OFFSET_A) & BYTE_MASK) << RGBA8888_OFFSET_R) | (((dsw >> RGBA8888_OFFSET_G) & BYTE_MASK) << RGBA8888_OFFSET_G) | (BYTE_MASK << RGBA8888_OFFSET_A)) #define SWIZZLE_RGBA_xGxR(dsw) ((((dsw >> RGBA8888_OFFSET_R) & BYTE_MASK) << RGBA8888_OFFSET_A) | (((dsw >> RGBA8888_OFFSET_G) & BYTE_MASK) << RGBA8888_OFFSET_G)) +#define SBYTE_MAXVAL 127 #define BYTE_MAXVAL 255 #define BYTE_MAX_FLOAT 255.0f #define CONVERT_FLOAT_TO_BYTE(f) static_cast(((f) * BYTE_MAX_FLOAT) + 0.5) +#define CONVERT_FLOAT_TO_SBYTE(f) static_cast(((f)*BYTE_MAX_FLOAT) + 0.5) #define CONVERT_BYTE_TO_FLOAT(b) (b) / BYTE_MAX_FLOAT +#define CONVERT_SBYTE_TO_FLOAT(b) (b) / BYTE_MAX_FLOAT #define DWORD_MAXVAL 4294967295.0f #define WORD_MAXVAL 65535.0f @@ -173,9 +180,13 @@ typedef enum _CodecBufferType { #define CONVERT_WORD_TO_DWORD(w) (((static_cast(w)) << 16) | static_cast(w)) #define CONVERT_DWORD_TO_WORD(dw) static_cast(min(((dw >> 16) + ((dw & 0x0000ffff) >= 0x00008000 ? 1 : 0)),WORD_MAXVAL)) #define CONVERT_BYTE_TO_DWORD(b) (((static_cast(b)) << 24) | ((static_cast(b)) << 16) | ((static_cast(b)) << 8) | static_cast(b)) +#define CONVERT_SBYTE_TO_DWORD(b) (((static_cast(b)) << 24) | ((static_cast(b)) << 16) | ((static_cast(b)) << 8) | static_cast(b)) #define CONVERT_DWORD_TO_BYTE(dw) static_cast(min(((dw >> 24) + ((dw & 0x00ffffff) >= 0x00800000 ? 1 : 0)), BYTE_MAXVAL)) +#define CONVERT_DWORD_TO_SBYTE(dw) static_cast(min(((dw >> 24) + ((dw & 0x00ffffff) >= 0x00800000 ? 1 : 0)), SBYTE_MAXVAL)) #define CONVERT_BYTE_TO_WORD(b) (((static_cast(b)) << 8) | static_cast(b)) +#define CONVERT_SBYTE_TO_WORD(b) (((static_cast(b)) << 8) | static_cast(b)) #define CONVERT_WORD_TO_BYTE(w) static_cast(min(((w >> 8) + ((w & BYTE_MASK) >= 128 ? 1 : 0)), BYTE_MAXVAL)) +#define CONVERT_WORD_TO_SBYTE(w) static_cast(min(((w >> 8) + ((w & BYTE_MASK) >= 128 ? 1 : 0)), SBYTE_MAXVAL)) #define CONVERT_10BIT_TO_WORD(b) (((static_cast(b)) << 6) | static_cast(b) >> 2) #define CONVERT_2BIT_TO_WORD(b) ((static_cast(b)) | ((static_cast(b)) << 2) | ((static_cast(b)) << 4) | ((static_cast(b)) << 6) | ((static_cast(b)) << 8) | ((static_cast(b)) << 10) | ((static_cast(b)) << 12) | ((static_cast(b)) << 14)) #define CONVERT_WORD_TO_10BIT(b) ((b >> 6) & TEN_BIT_MASK) @@ -293,6 +304,12 @@ class CCodecBuffer { virtual bool ReadBlockB(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE cBlock[]); virtual bool ReadBlockA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE cBlock[]); + virtual bool ReadBlockR(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE cBlock[]); + virtual bool ReadBlockG(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE cBlock[]); + virtual bool ReadBlockB(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE cBlock[]); + virtual bool ReadBlockA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE cBlock[]); + + virtual bool ReadBlockR(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_WORD wblock[]); virtual bool ReadBlockG(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_WORD wblock[]); virtual bool ReadBlockB(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_WORD wblock[]); @@ -323,6 +340,11 @@ class CCodecBuffer { virtual bool WriteBlockB(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE cBlock[]); virtual bool WriteBlockA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE cBlock[]); + virtual bool WriteBlockR(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE cBlock[]); + virtual bool WriteBlockG(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE cBlock[]); + virtual bool WriteBlockB(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE cBlock[]); + virtual bool WriteBlockA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE cBlock[]); + virtual bool WriteBlockR(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_WORD wblock[]); virtual bool WriteBlockG(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_WORD wblock[]); virtual bool WriteBlockB(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_WORD wblock[]); @@ -348,6 +370,9 @@ class CCodecBuffer { virtual bool WriteBlockB(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, double dBlock[]); virtual bool WriteBlockA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, double dBlock[]); + virtual bool ReadBlockRGBA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE cBlock[]); + virtual bool WriteBlockRGBA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE cBlock[]); + virtual bool ReadBlockRGBA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE cBlock[]); virtual bool WriteBlockRGBA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE cBlock[]); @@ -380,35 +405,41 @@ class CCodecBuffer { protected: + // Converts data from a source type to a destination type void ConvertBlock(double dBlock[], float fBlock[], CMP_DWORD dwBlockSize); void ConvertBlock(double dBlock[], CMP_HALF hBlock[], CMP_DWORD dwBlockSize); void ConvertBlock(double dBlock[], CMP_DWORD dwBlock[], CMP_DWORD dwBlockSize); void ConvertBlock(double dBlock[], CMP_WORD wBlock[], CMP_DWORD dwBlockSize); void ConvertBlock(double dBlock[], CMP_BYTE cBlock[], CMP_DWORD dwBlockSize); + void ConvertBlock(double dBlock[], CMP_SBYTE cBlock[], CMP_DWORD dwBlockSize); void ConvertBlock(float fBlock[], double dBlock[], CMP_DWORD dwBlockSize); void ConvertBlock(float fBlock[], CMP_HALF hBlock[], CMP_DWORD dwBlockSize); void ConvertBlock(float fBlock[], CMP_DWORD dwBlock[], CMP_DWORD dwBlockSize); void ConvertBlock(float fBlock[], CMP_WORD wBlock[], CMP_DWORD dwBlockSize); void ConvertBlock(float fBlock[], CMP_BYTE cBlock[], CMP_DWORD dwBlockSize); + void ConvertBlock(float fBlock[], CMP_SBYTE cBlock[], CMP_DWORD dwBlockSize); void ConvertBlock(CMP_HALF hBlock[], double dBlock[], CMP_DWORD dwBlockSize); void ConvertBlock(CMP_HALF hBlock[], float fBlock[], CMP_DWORD dwBlockSize); void ConvertBlock(CMP_HALF hBlock[], CMP_DWORD dwBlock[], CMP_DWORD dwBlockSize); void ConvertBlock(CMP_HALF hBlock[], CMP_WORD wBlock[], CMP_DWORD dwBlockSize); void ConvertBlock(CMP_HALF hBlock[], CMP_BYTE cBlock[], CMP_DWORD dwBlockSize); + void ConvertBlock(CMP_HALF hBlock[], CMP_SBYTE cBlock[], CMP_DWORD dwBlockSize); void ConvertBlock(CMP_DWORD dwBlock[], double dBlock[], CMP_DWORD dwBlockSize); void ConvertBlock(CMP_DWORD dwBlock[], float fBlock[], CMP_DWORD dwBlockSize); void ConvertBlock(CMP_DWORD dwBlock[], CMP_HALF hBlock[], CMP_DWORD dwBlockSize); void ConvertBlock(CMP_DWORD dwBlock[], CMP_WORD wBlock[], CMP_DWORD dwBlockSize); void ConvertBlock(CMP_DWORD dwBlock[], CMP_BYTE cBlock[], CMP_DWORD dwBlockSize); + void ConvertBlock(CMP_DWORD dwBlock[], CMP_SBYTE cBlock[], CMP_DWORD dwBlockSize); void ConvertBlock(CMP_WORD wBlock[], double dBlock[], CMP_DWORD dwBlockSize); void ConvertBlock(CMP_WORD wBlock[], float fBlock[], CMP_DWORD dwBlockSize); void ConvertBlock(CMP_WORD wBlock[], CMP_HALF hBlock[], CMP_DWORD dwBlockSize); void ConvertBlock(CMP_WORD wBlock[], CMP_DWORD dwBlock[], CMP_DWORD dwBlockSize); void ConvertBlock(CMP_WORD wBlock[], CMP_BYTE cBlock[], CMP_DWORD dwBlockSize); + void ConvertBlock(CMP_WORD wBlock[], CMP_SBYTE cBlock[], CMP_DWORD dwBlockSize); void ConvertBlock(CMP_BYTE cBlock[], double dBlock[], CMP_DWORD dwBlockSize); void ConvertBlock(CMP_BYTE cBlock[], float fBlock[], CMP_DWORD dwBlockSize); @@ -416,6 +447,16 @@ class CCodecBuffer { void ConvertBlock(CMP_BYTE cBlock[], CMP_DWORD dwBlock[], CMP_DWORD dwBlockSize); void ConvertBlock(CMP_BYTE cBlock[], CMP_WORD wBlock[], CMP_DWORD dwBlockSize); + // Signed Conversions + void ConvertBlock(CMP_BYTE cBlock[], CMP_SBYTE wBlock[], CMP_DWORD dwBlockSize); + void ConvertBlock(CMP_SBYTE cBlock[], double dBlock[], CMP_DWORD dwBlockSize); + void ConvertBlock(CMP_SBYTE cBlock[], float fBlock[], CMP_DWORD dwBlockSize); + void ConvertBlock(CMP_SBYTE cBlock[], CMP_HALF hBlock[], CMP_DWORD dwBlockSize); + void ConvertBlock(CMP_SBYTE cBlock[], CMP_DWORD dwBlock[], CMP_DWORD dwBlockSize); + void ConvertBlock(CMP_SBYTE cBlock[], CMP_WORD wBlock[], CMP_DWORD dwBlockSize); + void ConvertBlock(CMP_SBYTE cBlock[], CMP_BYTE wBlock[], CMP_DWORD dwBlockSize); + + void SwizzleBlock(double dBlock[], CMP_DWORD dwBlockSize); void SwizzleBlock(float fBlock[], CMP_DWORD dwBlockSize); void SwizzleBlock(CMP_HALF hBlock[], CMP_DWORD dwBlockSize); diff --git a/cmp_compressonatorlib/buffer/codecbuffer_r8s.cpp b/cmp_compressonatorlib/buffer/codecbuffer_r8s.cpp new file mode 100644 index 000000000..18c6cfbf3 --- /dev/null +++ b/cmp_compressonatorlib/buffer/codecbuffer_r8s.cpp @@ -0,0 +1,248 @@ +//=============================================================================== +// Copyright (c) 2007-2016 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2004-2006 ATI Technologies Inc. +//=============================================================================== +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// +// File Name: CodecBuffer_R8.cpp +// Description: implementation of the CCodecBuffer_R8 class +// +////////////////////////////////////////////////////////////////////////////// + +#include "common.h" +#include "codecbuffer_r8s.h" + +////////////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////////////// + +const int nChannelCount = 1; +const int nPixelSize = nChannelCount * sizeof(CMP_BYTE); + +CCodecBuffer_R8S::CCodecBuffer_R8S( + CMP_BYTE nBlockWidth, CMP_BYTE nBlockHeight, CMP_BYTE nBlockDepth, + CMP_DWORD dwWidth, CMP_DWORD dwHeight, CMP_DWORD dwPitch, CMP_BYTE* pData,CMP_DWORD dwDataSize) + : CCodecBuffer(nBlockWidth, nBlockHeight, nBlockDepth, dwWidth, dwHeight, dwPitch, pData,dwDataSize) { + assert((m_dwPitch == 0) || (m_dwPitch >= GetWidth() * nPixelSize)); + if(m_dwPitch <= GetWidth() * nPixelSize) + m_dwPitch = GetWidth() * nPixelSize; + + if(m_pData == NULL) { + CMP_DWORD dwSize = m_dwPitch * GetHeight(); + m_pData = (CMP_BYTE*) malloc(dwSize); + } +} + +CCodecBuffer_R8S::~CCodecBuffer_R8S() { + +} + +void CCodecBuffer_R8S::Copy(CCodecBuffer& srcBuffer) { + if(GetWidth() != srcBuffer.GetWidth() || GetHeight() != srcBuffer.GetHeight()) + return; + + const CMP_DWORD dwBlocksX = ((GetWidth() + 3) >> 2); + const CMP_DWORD dwBlocksY = ((GetHeight() + 3) >> 2); + + for(CMP_DWORD j = 0; j < dwBlocksY; j++) { + for(CMP_DWORD i = 0; i < dwBlocksX; i++) { + CMP_BYTE block[BLOCK_SIZE_4X4X4]; + srcBuffer.ReadBlockRGBA(i*4, j*4, 4, 4, block); + WriteBlockRGBA(i*4, j*4, 4, 4, block); + } + } +} + +bool CCodecBuffer_R8S::ReadBlock(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]) { + assert(x < GetWidth()); + assert(y < GetHeight()); + + if(x >= GetWidth() || y >= GetHeight()) + return false; + + CMP_DWORD dwWidth = min(w, (GetWidth() - x)); + + CMP_DWORD i, j; + for(j = 0; j < h && (y + j) < GetHeight(); j++) { + CMP_BYTE* pData = (CMP_BYTE*) (GetData() + ((y + j) * m_dwPitch) + (x * nPixelSize)); + for(i = 0; i < dwWidth; i++) + block[(j * w) + i] = *pData++; + + // Pad line with previous values if necessary + if(i < w) + PadLine(i, w, 1, &block[j * w]); + } + + // Pad block with previous values if necessary + if(j < h) + PadBlock(j, w, h, 1, block); + + return true; +} + +bool CCodecBuffer_R8S::WriteBlock(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]) { + assert(x < GetWidth()); + assert(y < GetHeight()); + + if(x >= GetWidth() || y >= GetHeight()) + return false; + + CMP_DWORD dwWidth = min(w, (GetWidth() - x)); + + for(CMP_DWORD j = 0; j < h && (y + j) < GetHeight(); j++) { + CMP_BYTE* pData = (CMP_BYTE*) (GetData() + ((y + j) * m_dwPitch) + (x * nPixelSize)); + for(CMP_DWORD i = 0; i < dwWidth; i++) + *pData++ = block[(j * w) + i]; + } + return true; +} + +bool CCodecBuffer_R8S::ReadBlockA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]) { + assert(x < GetWidth()); + assert(y < GetHeight()); + + if(x >= GetWidth() || y >= GetHeight()) + return false; + + memset(block, 0, w*h*nPixelSize); + + return true; +} + +bool CCodecBuffer_R8S::ReadBlockR(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]) { + return ReadBlock(x, y, w, h, block); +} + +bool CCodecBuffer_R8S::ReadBlockG(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]) { + assert(x < GetWidth()); + assert(y < GetHeight()); + + if(x >= GetWidth() || y >= GetHeight()) + return false; + + memset(block, 0, w*h*nPixelSize); + + return true; +} + +bool CCodecBuffer_R8S::ReadBlockB(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]) { + assert(x < GetWidth()); + assert(y < GetHeight()); + + if(x >= GetWidth() || y >= GetHeight()) + return false; + + memset(block, 0, w*h*nPixelSize); + + return true; +} + +bool CCodecBuffer_R8S::WriteBlockA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE /*w*/, CMP_BYTE /*h*/, CMP_BYTE /*block*/[]) { + assert(x < GetWidth()); + assert(y < GetHeight()); + + if(x >= GetWidth() || y >= GetHeight()) + return false; + + return true; +} + +bool CCodecBuffer_R8S::WriteBlockR(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]) { + return WriteBlock(x, y, w, h, block); +} + +bool CCodecBuffer_R8S::WriteBlockG(CMP_DWORD x, CMP_DWORD y, CMP_BYTE /*w*/, CMP_BYTE /*h*/, CMP_BYTE /*block*/[]) { + assert(x < GetWidth()); + assert(y < GetHeight()); + + if(x >= GetWidth() || y >= GetHeight()) + return false; + + return true; +} + +bool CCodecBuffer_R8S::WriteBlockB(CMP_DWORD x, CMP_DWORD y, CMP_BYTE /*w*/, CMP_BYTE /*h*/, CMP_BYTE /*block*/[]) { + assert(x < GetWidth()); + assert(y < GetHeight()); + + if(x >= GetWidth() || y >= GetHeight()) + return false; + + return true; +} + +#define GET_PIXEL(i, j) &block[(((j * w) + i) * 4)] +bool CCodecBuffer_R8S::ReadBlockRGBA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]) { + assert(x < GetWidth()); + assert(y < GetHeight()); + assert(x % w == 0); + assert(y % h == 0); + + if(x >= GetWidth() || y >= GetHeight()) + return false; + + CMP_DWORD dwWidth = min(w, (GetWidth() - x)); + + CMP_DWORD i, j; + for(j = 0; j < h && (y + j) < GetHeight(); j++) { + CMP_BYTE* pData = (CMP_BYTE*) (GetData() + ((y + j) * m_dwPitch) + (x * nPixelSize)); + for(i = 0; i < dwWidth; i++) { + CMP_BYTE* pDest = GET_PIXEL(i, j); + *pDest++ = 0; + *pDest++ = 0; + *pDest++ = *pData++; + *pDest++ = 0xff; + } + + // Pad block with previous values if necessary + if(i < w) + PadLine(i, w, 4, &block[j * w * 4]); + } + + // Pad block with previous values if necessary + if(j < h) + PadBlock(j, w, h, 4, block); + + return true; +} + +bool CCodecBuffer_R8S::WriteBlockRGBA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]) { + assert(x < GetWidth()); + assert(y < GetHeight()); + assert(x % 4 == 0); + assert(y % 4 == 0); + + if(x >= GetWidth() || y >= GetHeight()) + return false; + + CMP_DWORD dwWidth = min(w, (GetWidth() - x)); + + for(CMP_DWORD j = 0; j < h && (y + j) < GetHeight(); j++) { + CMP_BYTE* pData = (CMP_BYTE*) (GetData() + ((y + j) * m_dwPitch) + (x * nPixelSize)); + for(CMP_DWORD i = 0; i < dwWidth; i++) { + memcpy(pData, GET_PIXEL(i, j)+RGBA8888_CHANNEL_R, nPixelSize); + pData += nChannelCount; + } + } + return true; +} + + diff --git a/cmp_compressonatorlib/buffer/codecbuffer_r8s.h b/cmp_compressonatorlib/buffer/codecbuffer_r8s.h new file mode 100644 index 000000000..f5bce0b5d --- /dev/null +++ b/cmp_compressonatorlib/buffer/codecbuffer_r8s.h @@ -0,0 +1,74 @@ +//=============================================================================== +// Copyright (c) 2007-2016 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2004-2006 ATI Technologies Inc. +//=============================================================================== +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// File Name: CodecBuffer_R8.h +// Description: interface for the CCodecBuffer_R8 class +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef _CODECBUFFER_R8S_H_INCLUDED_ +#define _CODECBUFFER_R8S_H_INCLUDED_ + +#include "codecbuffer.h" + +class CCodecBuffer_R8S : public CCodecBuffer { + public: + CCodecBuffer_R8S( + CMP_BYTE nBlockWidth, CMP_BYTE nBlockHeight, CMP_BYTE nBlockDepth, + CMP_DWORD dwWidth, CMP_DWORD dwHeight, CMP_DWORD dwPitch = 0, CMP_BYTE* pData = 0,CMP_DWORD dwDataSize = 0); + virtual ~CCodecBuffer_R8S(); + + virtual void Copy(CCodecBuffer& srcBuffer); + + virtual CodecBufferType GetBufferType() const { + return CBT_R8; + }; + virtual CMP_DWORD GetChannelDepth() const { + return 8; + }; + virtual CMP_DWORD GetChannelCount() const { + return 1; + }; + virtual bool IsFloat() const { + return false; + }; + + virtual bool ReadBlockR(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]); + virtual bool ReadBlockG(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]); + virtual bool ReadBlockB(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]); + virtual bool ReadBlockA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]); + + virtual bool WriteBlockR(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]); + virtual bool WriteBlockG(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]); + virtual bool WriteBlockB(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]); + virtual bool WriteBlockA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]); + + virtual bool ReadBlockRGBA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]); + virtual bool WriteBlockRGBA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]); + + protected: + virtual bool ReadBlock(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]); + virtual bool WriteBlock(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]); +}; + +#endif // !defined(_CODECBUFFER_R8_H_INCLUDED_) diff --git a/cmp_compressonatorlib/buffer/codecbuffer_rg8s.cpp b/cmp_compressonatorlib/buffer/codecbuffer_rg8s.cpp new file mode 100644 index 000000000..26b624ed5 --- /dev/null +++ b/cmp_compressonatorlib/buffer/codecbuffer_rg8s.cpp @@ -0,0 +1,244 @@ +//=============================================================================== +// Copyright (c) 2007-2016 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2004-2006 ATI Technologies Inc. +//=============================================================================== +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// +// File Name: CodecBuffer_RG8.cpp +// Description: implementation of the CCodecBuffer_RG8 class +// +////////////////////////////////////////////////////////////////////////////// + +#include "common.h" +#include "codecbuffer_rg8s.h" + +////////////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////////////// + +const int nChannelCount = 2; +const int nPixelSize = nChannelCount * sizeof(CMP_BYTE); + +CCodecBuffer_RG8S::CCodecBuffer_RG8S( + CMP_BYTE nBlockWidth, CMP_BYTE nBlockHeight, CMP_BYTE nBlockDepth, + CMP_DWORD dwWidth, CMP_DWORD dwHeight, CMP_DWORD dwPitch, CMP_BYTE* pData,CMP_DWORD dwDataSize) + : CCodecBuffer(nBlockWidth, nBlockHeight, nBlockDepth, dwWidth, dwHeight, dwPitch, pData,dwDataSize) { + assert((m_dwPitch == 0) || (m_dwPitch >= GetWidth() * nPixelSize)); + if(m_dwPitch <= GetWidth() * nPixelSize) + m_dwPitch = GetWidth() * nPixelSize; + + if(m_pData == NULL) { + CMP_DWORD dwSize = m_dwPitch * GetHeight(); + m_pData = (CMP_BYTE*) malloc(dwSize); + } +} + +CCodecBuffer_RG8S::~CCodecBuffer_RG8S() { + +} + +void CCodecBuffer_RG8S::Copy(CCodecBuffer& srcBuffer) { + if(GetWidth() != srcBuffer.GetWidth() || GetHeight() != srcBuffer.GetHeight()) + return; + + const CMP_DWORD dwBlocksX = ((GetWidth() + 3) >> 2); + const CMP_DWORD dwBlocksY = ((GetHeight() + 3) >> 2); + + for(CMP_DWORD j = 0; j < dwBlocksY; j++) { + for(CMP_DWORD i = 0; i < dwBlocksX; i++) { + CMP_BYTE block[BLOCK_SIZE_4X4X4]; + srcBuffer.ReadBlockRGBA(i*4, j*4, 4, 4, block); + WriteBlockRGBA(i*4, j*4, 4, 4, block); + } + } +} + +bool CCodecBuffer_RG8S::ReadBlock(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[], CMP_DWORD dwChannelOffset) { + assert(x < GetWidth()); + assert(y < GetHeight()); + + if(x >= GetWidth() || y >= GetHeight()) + return false; + + CMP_DWORD dwWidth = min(w, (GetWidth() - x)); + + CMP_DWORD i, j; + for(j = 0; j < h && (y + j) < GetHeight(); j++) { + CMP_WORD* pData = (CMP_WORD*) (GetData() + ((y + j) * m_dwPitch) + (x * nPixelSize)); + for(i = 0; i < dwWidth; i++) + block[(j * w) + i] = static_cast(((*pData++) >> dwChannelOffset) & BYTE_MASK); + + // Pad line with previous values if necessary + if(i < w) + PadLine(i, w, 1, &block[j * w]); + } + + // Pad block with previous values if necessary + if(j < h) + PadBlock(j, w, h, 1, block); + + return true; +} + +bool CCodecBuffer_RG8S::WriteBlock(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[], CMP_DWORD dwChannelOffset) { + assert(x < GetWidth()); + assert(y < GetHeight()); + + if(x >= GetWidth() || y >= GetHeight()) + return false; + + CMP_DWORD dwChannelMask = ~((CMP_DWORD) BYTE_MASK << dwChannelOffset); + CMP_DWORD dwWidth = min(w, (GetWidth() - x)); + + for(CMP_DWORD j = 0; j < h && (y + j) < GetHeight(); j++) { + CMP_WORD* pData = (CMP_WORD*) (GetData() + ((y + j) * m_dwPitch) + (x * nPixelSize)); + for(CMP_DWORD i = 0; i < dwWidth; i++) { + *pData = (CMP_WORD) ((*pData & dwChannelMask) | (((CMP_WORD) block[(j * w) + i]) << dwChannelOffset)); + pData++; + } + } + return true; +} + +#define RG8_CHANNEL_R 1 +#define RG8_CHANNEL_G 0 + +#define RG8_OFFSET_R (RG8_CHANNEL_R * 8) +#define RG8_OFFSET_G (RG8_CHANNEL_G * 8) + +bool CCodecBuffer_RG8S::ReadBlockA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]) { + assert(x < GetWidth()); + assert(y < GetHeight()); + + if(x >= GetWidth() || y >= GetHeight()) + return false; + + memset(block, 0, w*h*nPixelSize); + + return true; +} + +bool CCodecBuffer_RG8S::ReadBlockR(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]) { + return ReadBlock(x, y, w, h, block, RG8_OFFSET_R); +} + +bool CCodecBuffer_RG8S::ReadBlockG(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]) { + return ReadBlock(x, y, w, h, block, RG8_OFFSET_G); +} + +bool CCodecBuffer_RG8S::ReadBlockB(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]) { + assert(x < GetWidth()); + assert(y < GetHeight()); + + if(x >= GetWidth() || y >= GetHeight()) + return false; + + memset(block, 0, w*h*nPixelSize); + + return true; +} + +bool CCodecBuffer_RG8S::WriteBlockA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE /*w*/, CMP_BYTE /*h*/, CMP_BYTE /*block*/[]) { + assert(x < GetWidth()); + assert(y < GetHeight()); + + if(x >= GetWidth() || y >= GetHeight()) + return false; + + return true; +} + +bool CCodecBuffer_RG8S::WriteBlockR(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]) { + return WriteBlock(x, y, w, h, block, RG8_OFFSET_R); +} + +bool CCodecBuffer_RG8S::WriteBlockG(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]) { + return WriteBlock(x, y, w, h, block, RG8_OFFSET_G); +} + +bool CCodecBuffer_RG8S::WriteBlockB(CMP_DWORD x, CMP_DWORD y, CMP_BYTE /*w*/, CMP_BYTE /*h*/, CMP_BYTE /*block*/[]) { + assert(x < GetWidth()); + assert(y < GetHeight()); + + if(x >= GetWidth() || y >= GetHeight()) + return false; + + return true; +} + +#define GET_PIXEL(i, j) &block[(((j * w) + i) * 4)] +bool CCodecBuffer_RG8S::ReadBlockRGBA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]) { + assert(x < GetWidth()); + assert(y < GetHeight()); + assert(x % w == 0); + assert(y % h == 0); + + if(x >= GetWidth() || y >= GetHeight()) + return false; + + CMP_DWORD dwWidth = min(w, (GetWidth() - x)); + + CMP_DWORD i, j; + for(j = 0; j < h && (y + j) < GetHeight(); j++) { + CMP_BYTE* pData = (CMP_BYTE*) (GetData() + ((y + j) * m_dwPitch) + (x * nPixelSize)); + for(i = 0; i < dwWidth; i++) { + CMP_BYTE* pDest = GET_PIXEL(i, j); + *pDest++ = 0; + memcpy(pDest, pData, nPixelSize); + pData += nChannelCount; + pDest += 2; + *pDest++ = 0xff; + } + + // Pad block with previous values if necessary + if(i < w) + PadLine(i, w, 4, &block[j * w * 4]); + } + + // Pad block with previous values if necessary + if(j < h) + PadBlock(j, w, h, 4, block); + + return true; +} + +bool CCodecBuffer_RG8S::WriteBlockRGBA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]) { + assert(x < GetWidth()); + assert(y < GetHeight()); + assert(x % 4 == 0); + assert(y % 4 == 0); + + if(x >= GetWidth() || y >= GetHeight()) + return false; + + CMP_DWORD dwWidth = min(w, (GetWidth() - x)); + + for(CMP_DWORD j = 0; j < h && (y + j) < GetHeight(); j++) { + CMP_BYTE* pData = (CMP_BYTE*) (GetData() + ((y + j) * m_dwPitch) + (x * nPixelSize)); + for(CMP_DWORD i = 0; i < dwWidth; i++) { + memcpy(pData, GET_PIXEL(i, j)+RG8_CHANNEL_G, nPixelSize); + pData += nChannelCount; + } + } + return true; +} + + diff --git a/cmp_compressonatorlib/buffer/codecbuffer_rg8s.h b/cmp_compressonatorlib/buffer/codecbuffer_rg8s.h new file mode 100644 index 000000000..5b15e3e06 --- /dev/null +++ b/cmp_compressonatorlib/buffer/codecbuffer_rg8s.h @@ -0,0 +1,74 @@ +//=============================================================================== +// Copyright (c) 2007-2016 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2004-2006 ATI Technologies Inc. +//=============================================================================== +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// File Name: CodecBuffer_RG8.h +// Description: interface for the CCodecBuffer_RG8 class +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef _CODECBUFFER_RG8S_H_INCLUDED_ +#define _CODECBUFFER_RG8S_H_INCLUDED_ + +#include "codecbuffer.h" + +class CCodecBuffer_RG8S : public CCodecBuffer { + public: + CCodecBuffer_RG8S( + CMP_BYTE nBlockWidth, CMP_BYTE nBlockHeight, CMP_BYTE nBlockDepth, + CMP_DWORD dwWidth, CMP_DWORD dwHeight, CMP_DWORD dwPitch = 0, CMP_BYTE* pData = 0,CMP_DWORD dwDataSize = 0); + virtual ~CCodecBuffer_RG8S(); + + virtual void Copy(CCodecBuffer& srcBuffer); + + virtual CodecBufferType GetBufferType() const { + return CBT_RG8; + }; + virtual CMP_DWORD GetChannelDepth() const { + return 8; + }; + virtual CMP_DWORD GetChannelCount() const { + return 2; + }; + virtual bool IsFloat() const { + return false; + }; + + virtual bool ReadBlockR(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]); + virtual bool ReadBlockG(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]); + virtual bool ReadBlockB(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]); + virtual bool ReadBlockA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]); + + virtual bool WriteBlockR(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]); + virtual bool WriteBlockG(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]); + virtual bool WriteBlockB(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]); + virtual bool WriteBlockA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]); + + virtual bool ReadBlockRGBA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]); + virtual bool WriteBlockRGBA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]); + + protected: + virtual bool ReadBlock(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[], CMP_DWORD dwChannelOffset); + virtual bool WriteBlock(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[], CMP_DWORD dwChannelOffset); +}; + +#endif // !defined(_CODECBUFFER_RG8_H_INCLUDED_) diff --git a/cmp_compressonatorlib/buffer/codecbuffer_rgb888s.cpp b/cmp_compressonatorlib/buffer/codecbuffer_rgb888s.cpp new file mode 100644 index 000000000..fa2f84b96 --- /dev/null +++ b/cmp_compressonatorlib/buffer/codecbuffer_rgb888s.cpp @@ -0,0 +1,217 @@ +//=============================================================================== +// Copyright (c) 2007-2016 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2004-2006 ATI Technologies Inc. +//=============================================================================== +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// +// File Name: CodecBuffer_RGB888.cpp +// Description: implementation of the CCodecBuffer_RGB888 class +// +////////////////////////////////////////////////////////////////////////////// + +#include "common.h" +#include "codecbuffer_rgb888s.h" + +////////////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////////////// + +const int nChannelCount = 3; +const int nPixelSize = nChannelCount * sizeof(CMP_BYTE); + +CCodecBuffer_RGB888S::CCodecBuffer_RGB888S( + CMP_BYTE nBlockWidth, CMP_BYTE nBlockHeight, CMP_BYTE nBlockDepth, + CMP_DWORD dwWidth, CMP_DWORD dwHeight, CMP_DWORD dwPitch, CMP_BYTE* pData,CMP_DWORD dwDataSize) + : CCodecBuffer(nBlockWidth, nBlockHeight, nBlockDepth, dwWidth, dwHeight, dwPitch, pData,dwDataSize) { + CMP_DWORD dwMinPitch = dwWidth * nPixelSize; + assert((m_dwPitch == 0) || (m_dwPitch >= dwMinPitch)); + + if(m_dwPitch < dwMinPitch) + m_dwPitch = dwMinPitch; + + if(m_pData == NULL) { + CMP_DWORD dwSize = m_dwPitch * GetHeight(); + m_pData = (CMP_BYTE*) malloc(dwSize); + } +} + +CCodecBuffer_RGB888S::~CCodecBuffer_RGB888S() { + +} + +void CCodecBuffer_RGB888S::Copy(CCodecBuffer& srcBuffer) { + if(GetWidth() != srcBuffer.GetWidth() || GetHeight() != srcBuffer.GetHeight()) + return; + + const CMP_DWORD dwBlocksX = ((GetWidth() + 3) >> 2); + const CMP_DWORD dwBlocksY = ((GetHeight() + 3) >> 2); + + for(CMP_DWORD j = 0; j < dwBlocksY; j++) { + for(CMP_DWORD i = 0; i < dwBlocksX; i++) { + CMP_BYTE block[BLOCK_SIZE_4X4X4]; + srcBuffer.ReadBlockRGBA(i*4, j*4, 4, 4, block); + WriteBlockRGBA(i*4, j*4, 4, 4, block); + } + } +} + +bool CCodecBuffer_RGB888S::ReadBlock(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[], CMP_DWORD dwChannelOffset) { + assert(x < GetWidth()); + assert(y < GetHeight()); + + if(x >= GetWidth() || y >= GetHeight()) + return false; + + CMP_DWORD dwWidth = min(w, (GetWidth() - x)); + + CMP_DWORD i, j; + for(j = 0; j < h && (y + j) < GetHeight(); j++) { + CMP_BYTE* pSrcData = (CMP_BYTE*) (GetData() + ((y + j) * m_dwPitch) + (x * sizeof(nChannelCount))) + dwChannelOffset; + for(i = 0; i < dwWidth; i++) { + block[(j * w) + i] = *pSrcData; + pSrcData += nChannelCount; + } + + // Pad line with previous values if necessary + if(i < w) + PadLine(i, w, 1, &block[j * w]); + } + + // Pad block with previous values if necessary + if(j < h) + PadBlock(j, w, h, 1, block); + + return true; +} + +bool CCodecBuffer_RGB888S::WriteBlock(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[], CMP_DWORD dwChannelOffset) { + assert(x < GetWidth()); + assert(y < GetHeight()); + + if(x >= GetWidth() || y >= GetHeight()) + return false; + + CMP_DWORD dwWidth = min(w, (GetWidth() - x)); + + for(CMP_DWORD j = 0; j < h && (y + j) < GetHeight(); j++) { + CMP_BYTE* pDestData = (CMP_BYTE*) (GetData() + ((y + j) * m_dwPitch) + (x * sizeof(nChannelCount))) + dwChannelOffset; + for(CMP_DWORD i = 0; i < dwWidth; i++) { + *pDestData++ = block[(j * w) + i]; + pDestData += nChannelCount; + } + } + return true; +} + +bool CCodecBuffer_RGB888S::ReadBlockA(CMP_DWORD /*x*/, CMP_DWORD /*y*/, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]) { + memset(block, 255, w * h); + return true; +} + +bool CCodecBuffer_RGB888S::ReadBlockR(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]) { + return ReadBlock(x, y, w, h, block, RGBA8888_OFFSET_R); +} + +bool CCodecBuffer_RGB888S::ReadBlockG(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]) { + return ReadBlock(x, y, w, h, block, RGBA8888_OFFSET_G); +} + +bool CCodecBuffer_RGB888S::ReadBlockB(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]) { + return ReadBlock(x, y, w, h, block, RGBA8888_OFFSET_B); +} + +bool CCodecBuffer_RGB888S::WriteBlockA(CMP_DWORD /*x*/, CMP_DWORD /*y*/, CMP_BYTE /*w*/, CMP_BYTE /*h*/, CMP_BYTE /*block*/[]) { + return false; +} + +bool CCodecBuffer_RGB888S::WriteBlockR(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]) { + return WriteBlock(x, y, w, h, block, RGBA8888_OFFSET_R); +} + +bool CCodecBuffer_RGB888S::WriteBlockG(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]) { + return WriteBlock(x, y, w, h, block, RGBA8888_OFFSET_G); +} + +bool CCodecBuffer_RGB888S::WriteBlockB(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]) { + return WriteBlock(x, y, w, h, block, RGBA8888_OFFSET_B); +} + +bool CCodecBuffer_RGB888S::ReadBlockRGBA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]) { + assert(x < GetWidth()); + assert(y < GetHeight()); + assert(x % w == 0); + assert(y % h == 0); + + if(x >= GetWidth() || y >= GetHeight()) + return false; + + CMP_DWORD dwWidth = min(w, (GetWidth() - x)); + + CMP_DWORD* pdwBlock = (CMP_DWORD*) block; + CMP_DWORD i, j; + for(j = 0; j < h && (y + j) < GetHeight(); j++) { + CMP_BYTE* pSrcData = (CMP_BYTE*) ((GetData() + ((y + j) * m_dwPitch) + (x * nChannelCount))); + CMP_BYTE* pDestData = (CMP_BYTE*) &pdwBlock[(j * w)]; + for(i = 0; i < dwWidth; i++) { + *pDestData++ = *pSrcData++; + *pDestData++ = *pSrcData++; + *pDestData++ = *pSrcData++; + *pDestData++ = 0xff; + } + + // Pad block with previous values if necessary + if(i < w) + PadLine(i, w, 4, (CMP_BYTE*) &pdwBlock[j * w]); + } + + // Pad block with previous values if necessary + if(j < h) + PadBlock(j, w, h, 4, (CMP_BYTE*) pdwBlock); + + return true; +} + +bool CCodecBuffer_RGB888S::WriteBlockRGBA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]) { + assert(x < GetWidth()); + assert(y < GetHeight()); + assert(x % 4 == 0); + assert(y % 4 == 0); + + if(x >= GetWidth() || y >= GetHeight()) + return false; + + CMP_DWORD dwWidth = min(w, (GetWidth() - x)); + CMP_DWORD* pdwBlock = (CMP_DWORD*) block; + + for(CMP_DWORD j = 0; j < h && (y + j) < GetHeight(); j++) { + CMP_BYTE* pSrcData = (CMP_BYTE*) &pdwBlock[(j * w)]; + CMP_BYTE* pDestData = (CMP_BYTE*) (GetData() + ((y + j) * m_dwPitch) + (x * sizeof(nChannelCount))); + for(CMP_DWORD i = 0; i < dwWidth; i++) { + *pDestData++ = *pSrcData++; + *pDestData++ = *pSrcData++; + *pDestData++ = *pSrcData++; + pSrcData++; + } + } + return true; +} + + diff --git a/cmp_compressonatorlib/buffer/codecbuffer_rgb888s.h b/cmp_compressonatorlib/buffer/codecbuffer_rgb888s.h new file mode 100644 index 000000000..a6e73a927 --- /dev/null +++ b/cmp_compressonatorlib/buffer/codecbuffer_rgb888s.h @@ -0,0 +1,74 @@ +//=============================================================================== +// Copyright (c) 2007-2016 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2004-2006 ATI Technologies Inc. +//=============================================================================== +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// File Name: CodecBuffer_RGB888.h +// Description: interface for the CCodecBuffer_RGB888 class +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef _CODECBUFFER_RGB888S_H_INCLUDED_ +#define _CODECBUFFER_RGB888S_H_INCLUDED_ + +#include "codecbuffer.h" + +class CCodecBuffer_RGB888S : public CCodecBuffer { + public: + CCodecBuffer_RGB888S( + CMP_BYTE nBlockWidth, CMP_BYTE nBlockHeight, CMP_BYTE nBlockDepth, + CMP_DWORD dwWidth, CMP_DWORD dwHeight, CMP_DWORD dwPitch = 0, CMP_BYTE* pData = 0,CMP_DWORD dwDataSize = 0); + virtual ~CCodecBuffer_RGB888S(); + + virtual void Copy(CCodecBuffer& srcBuffer); + + virtual CodecBufferType GetBufferType() const { + return CBT_RGB888S; + }; + virtual CMP_DWORD GetChannelDepth() const { + return 8; + }; + virtual CMP_DWORD GetChannelCount() const { + return 3; + }; + virtual bool IsFloat() const { + return false; + }; + + virtual bool ReadBlockR(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]); + virtual bool ReadBlockG(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]); + virtual bool ReadBlockB(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]); + virtual bool ReadBlockA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]); + + virtual bool WriteBlockR(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]); + virtual bool WriteBlockG(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]); + virtual bool WriteBlockB(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]); + virtual bool WriteBlockA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]); + + virtual bool ReadBlockRGBA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]); + virtual bool WriteBlockRGBA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[]); + + protected: + virtual bool ReadBlock(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[], CMP_DWORD dwChannelOffset); + virtual bool WriteBlock(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_BYTE block[], CMP_DWORD dwChannelOffset); +}; + +#endif // !defined(_CODECBUFFER_RGB888_H_INCLUDED_) diff --git a/cmp_compressonatorlib/buffer/codecbuffer_rgba2101010.cpp b/cmp_compressonatorlib/buffer/codecbuffer_rgba2101010.cpp index 82ab3d0cd..43a2b2df4 100644 --- a/cmp_compressonatorlib/buffer/codecbuffer_rgba2101010.cpp +++ b/cmp_compressonatorlib/buffer/codecbuffer_rgba2101010.cpp @@ -63,7 +63,7 @@ void CCodecBuffer_RGBA2101010::Copy(CCodecBuffer& srcBuffer) { for(CMP_DWORD j = 0; j < dwBlocksY; j++) { for(CMP_DWORD i = 0; i < dwBlocksX; i++) { - CMP_DWORD block[BLOCK_SIZE_4X4]; + CMP_WORD block[BLOCK_SIZE_4X4]; srcBuffer.ReadBlockRGBA(i*4, j*4, 4, 4, block); WriteBlockRGBA(i*4, j*4, 4, 4, block); } diff --git a/cmp_compressonatorlib/buffer/codecbuffer_rgba8888s.cpp b/cmp_compressonatorlib/buffer/codecbuffer_rgba8888s.cpp new file mode 100644 index 000000000..6376ea4e9 --- /dev/null +++ b/cmp_compressonatorlib/buffer/codecbuffer_rgba8888s.cpp @@ -0,0 +1,263 @@ +//=============================================================================== +// Copyright (c) 2007-2016 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2004-2006 ATI Technologies Inc. +//=============================================================================== +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// +// File Name: CodecBuffer_RGBA8888.cpp +// Description: implementation of the CCodecBuffer_RGBA8888 class +// +////////////////////////////////////////////////////////////////////////////// + +#include "common.h" +#include "codecbuffer_rgba8888s.h" + +////////////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////////////// + +const int nChannelCount = 4; +const int nPixelSize = nChannelCount * sizeof(CMP_BYTE); + +CCodecBuffer_RGBA8888S::CCodecBuffer_RGBA8888S( + CMP_BYTE nBlockWidth, CMP_BYTE nBlockHeight, CMP_BYTE nBlockDepth, + CMP_DWORD dwWidth, CMP_DWORD dwHeight, CMP_DWORD dwPitch, CMP_SBYTE* pData,CMP_DWORD dwDataSize) + : CCodecBuffer(nBlockWidth, nBlockHeight, nBlockDepth, dwWidth, dwHeight, dwPitch, (CMP_BYTE *) pData, dwDataSize) +{ + assert((m_dwPitch == 0) || (m_dwPitch >= GetWidth() * nPixelSize)); + if(m_dwPitch <= GetWidth() * nPixelSize) + m_dwPitch = GetWidth() * nPixelSize; + + if(m_pData == NULL) { + CMP_DWORD dwSize = m_dwPitch * GetHeight(); + m_pData = (CMP_BYTE*) malloc(dwSize); + } +} + +CCodecBuffer_RGBA8888S::~CCodecBuffer_RGBA8888S() { + +} + +void CCodecBuffer_RGBA8888S::Copy(CCodecBuffer& srcBuffer) { + if(GetWidth() != srcBuffer.GetWidth() || GetHeight() != srcBuffer.GetHeight()) + return; + + const CMP_DWORD dwBlocksX = ((GetWidth() + 3) >> 2); + const CMP_DWORD dwBlocksY = ((GetHeight() + 3) >> 2); + + for(CMP_DWORD j = 0; j < dwBlocksY; j++) { + for(CMP_DWORD i = 0; i < dwBlocksX; i++) { + CMP_SBYTE block[BLOCK_SIZE_4X4X4]; + srcBuffer.ReadBlockRGBA(i*4, j*4, 4, 4, block); + WriteBlockRGBA(i*4, j*4, 4, 4, block); + } + } +} + +bool CCodecBuffer_RGBA8888S::ReadBlock(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE block[], CMP_DWORD dwChannelOffset) { + assert(x < GetWidth()); + assert(y < GetHeight()); + + if(x >= GetWidth() || y >= GetHeight()) + return false; + + CMP_DWORD dwWidth = min(w, (GetWidth() - x)); + + CMP_DWORD i, j; + for(j = 0; j < h && (y + j) < GetHeight(); j++) { + CMP_DWORD* pData = (CMP_DWORD*) (GetData() + ((y + j) * m_dwPitch) + (x * sizeof(CMP_DWORD))); + for(i = 0; i < dwWidth; i++) + block[(j * w) + i] = static_cast(((*pData++) >> dwChannelOffset) & BYTE_MASK); + + // Pad line with previous values if necessary + if(i < w) + PadLine(i, w, 1, &block[j * w]); + } + + // Pad block with previous values if necessary + if(j < h) + PadBlock(j, w, h, 1, block); + + return true; +} + +bool CCodecBuffer_RGBA8888S::WriteBlock(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE block[], CMP_DWORD dwChannelOffset) { + assert(x < GetWidth()); + assert(y < GetHeight()); + + if(x >= GetWidth() || y >= GetHeight()) + return false; + + CMP_DWORD dwChannelMask = ~((CMP_DWORD) BYTE_MASK << dwChannelOffset); + CMP_DWORD dwWidth = min(w, (GetWidth() - x)); + + for(CMP_DWORD j = 0; j < h && (y + j) < GetHeight(); j++) { + CMP_DWORD* pData = (CMP_DWORD*) (GetData() + ((y + j) * m_dwPitch) + (x * sizeof(CMP_DWORD))); + for(CMP_DWORD i = 0; i < dwWidth; i++) { + *pData = (*pData & dwChannelMask) | (((CMP_DWORD) block[(j * w) + i]) << dwChannelOffset); + pData++; + } + } + return true; +} + +bool CCodecBuffer_RGBA8888S::ReadBlockA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE block[]) { + return ReadBlock(x, y, w, h, block, ATC_RGBA8888_OFFSET_A); +} + +bool CCodecBuffer_RGBA8888S::ReadBlockR(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE block[]) { + return ReadBlock(x, y, w, h, block, ATC_RGBA8888_OFFSET_R); +} + +bool CCodecBuffer_RGBA8888S::ReadBlockG(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE block[]) { + return ReadBlock(x, y, w, h, block, ATC_RGBA8888_OFFSET_G); +} + +bool CCodecBuffer_RGBA8888S::ReadBlockB(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE block[]) { + return ReadBlock(x, y, w, h, block, ATC_RGBA8888_OFFSET_B); +} + +bool CCodecBuffer_RGBA8888S::WriteBlockA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE block[]) { + return WriteBlock(x, y, w, h, block, ATC_RGBA8888_OFFSET_A); +} + +bool CCodecBuffer_RGBA8888S::WriteBlockR(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE block[]) { + return WriteBlock(x, y, w, h, block, ATC_RGBA8888_OFFSET_R); +} + +bool CCodecBuffer_RGBA8888S::WriteBlockG(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE block[]) { + return WriteBlock(x, y, w, h, block, ATC_RGBA8888_OFFSET_G); +} + +bool CCodecBuffer_RGBA8888S::WriteBlockB(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE block[]) { + return WriteBlock(x, y, w, h, block, ATC_RGBA8888_OFFSET_B); +} + +bool CCodecBuffer_RGBA8888S::ReadBlockRGBA(CMP_DWORD xw, CMP_DWORD yh, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE block[]) { + assert(xw < GetWidth()); + assert(yh < GetHeight()); + assert(xw % w == 0); + assert(yh % h == 0); + + if (xw >= GetWidth() || yh >= GetHeight()) + return false; + + CMP_DWORD* pdwBlock = (CMP_DWORD*)block; + if (w == 4 && h == 4 && (xw + w) <= GetWidth() && (yh + h) <= GetHeight()) { + // Fastpath for the key case to alleviate the drag this code puts on the really fast DXTC + CMP_DWORD* pData = (CMP_DWORD*)(GetData() + (yh * m_dwPitch) + (xw * sizeof(CMP_DWORD))); + + // The source is RGBA8888 and the codec reqiures a BGRA8888 + if (m_bSwizzle) { + pdwBlock[0] = SWIZZLE_RGBA_BGRA(pData[0]); + pdwBlock[1] = SWIZZLE_RGBA_BGRA(pData[1]); + pdwBlock[2] = SWIZZLE_RGBA_BGRA(pData[2]); + pdwBlock[3] = SWIZZLE_RGBA_BGRA(pData[3]); + pData += (m_dwPitch >> 2); + pdwBlock[4] = SWIZZLE_RGBA_BGRA(pData[0]); + pdwBlock[5] = SWIZZLE_RGBA_BGRA(pData[1]); + pdwBlock[6] = SWIZZLE_RGBA_BGRA(pData[2]); + pdwBlock[7] = SWIZZLE_RGBA_BGRA(pData[3]); + pData += (m_dwPitch >> 2); + pdwBlock[8] = SWIZZLE_RGBA_BGRA(pData[0]); + pdwBlock[9] = SWIZZLE_RGBA_BGRA(pData[1]); + pdwBlock[10] = SWIZZLE_RGBA_BGRA(pData[2]); + pdwBlock[11] = SWIZZLE_RGBA_BGRA(pData[3]); + pData += (m_dwPitch >> 2); + pdwBlock[12] = SWIZZLE_RGBA_BGRA(pData[0]); + pdwBlock[13] = SWIZZLE_RGBA_BGRA(pData[1]); + pdwBlock[14] = SWIZZLE_RGBA_BGRA(pData[2]); + pdwBlock[15] = SWIZZLE_RGBA_BGRA(pData[3]); + } else { + pdwBlock[0] = pData[0]; + pdwBlock[1] = pData[1]; + pdwBlock[2] = pData[2]; + pdwBlock[3] = pData[3]; + pData += (m_dwPitch >> 2); + pdwBlock[4] = pData[0]; + pdwBlock[5] = pData[1]; + pdwBlock[6] = pData[2]; + pdwBlock[7] = pData[3]; + pData += (m_dwPitch >> 2); + pdwBlock[8] = pData[0]; + pdwBlock[9] = pData[1]; + pdwBlock[10] = pData[2]; + pdwBlock[11] = pData[3]; + pData += (m_dwPitch >> 2); + pdwBlock[12] = pData[0]; + pdwBlock[13] = pData[1]; + pdwBlock[14] = pData[2]; + pdwBlock[15] = pData[3]; + } + } else { + CMP_DWORD minWidth = min(w, (GetWidth() - xw)); + CMP_DWORD srcOffset; + CMP_DWORD iw, jh; + CMP_BYTE *srcData = GetData(); + CMP_DWORD *pdwData; + + for (jh = 0; jh < h && (yh + jh) < GetHeight(); jh++) { + srcOffset = ((yh + jh) * m_dwPitch) + (xw * 4); + pdwData = (CMP_DWORD*)(srcData + srcOffset); + if (m_bSwizzle) { + for (iw = 0; iw < minWidth; iw++) { + pdwBlock[(jh * w) + iw] = SWIZZLE_RGBA_BGRA(pdwData[iw]); + } + } else { + for (iw = 0; iw < minWidth; iw++) { + pdwBlock[(jh * w) + iw] = pdwData[iw]; + } + } + + // Pad block with previous values if necessary + if (iw < w) + PadLine(iw, w, 4, (CMP_BYTE*)&pdwBlock[jh * w]); + } + + // Pad block with previous values if necessary + if (jh < h) + PadBlock(jh, w, h, 4, (CMP_BYTE*)pdwBlock); + } + + return true; +} + +bool CCodecBuffer_RGBA8888S::WriteBlockRGBA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE block[]) { + assert(x < GetWidth()); + assert(y < GetHeight()); + assert(x % 4 == 0); + assert(y % 4 == 0); + + if(x >= GetWidth() || y >= GetHeight()) + return false; + + CMP_DWORD dwWidth = min(w, (GetWidth() - x)); + CMP_DWORD* pdwBlock = (CMP_DWORD*) block; + + for(CMP_DWORD j = 0; j < h && (y + j) < GetHeight(); j++) { + CMP_DWORD* pData = (CMP_DWORD*) (GetData() + ((y + j) * m_dwPitch) + (x * sizeof(CMP_DWORD))); + for(CMP_DWORD i = 0; i < dwWidth; i++) + *pData++ = pdwBlock[(j * w) + i]; + } + return true; +} + + diff --git a/cmp_compressonatorlib/buffer/codecbuffer_rgba8888s.h b/cmp_compressonatorlib/buffer/codecbuffer_rgba8888s.h new file mode 100644 index 000000000..a7607673d --- /dev/null +++ b/cmp_compressonatorlib/buffer/codecbuffer_rgba8888s.h @@ -0,0 +1,74 @@ +//=============================================================================== +// Copyright (c) 2007-2016 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2004-2006 ATI Technologies Inc. +//=============================================================================== +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// File Name: CodecBuffer_RGBA8888.h +// Description: interface for the CCodecBuffer_RGBA8888 class +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef _CODECBUFFER_RGBA8888S_H_INCLUDED_ +#define _CODECBUFFER_RGBA8888S_H_INCLUDED_ + +#include "codecbuffer.h" + +class CCodecBuffer_RGBA8888S : public CCodecBuffer { + public: + CCodecBuffer_RGBA8888S( + CMP_BYTE nBlockWidth, CMP_BYTE nBlockHeight, CMP_BYTE nBlockDepth, + CMP_DWORD dwWidth, CMP_DWORD dwHeight, CMP_DWORD dwPitch = 0, CMP_SBYTE* pData = 0,CMP_DWORD dwDataSize = 0); + virtual ~CCodecBuffer_RGBA8888S(); + + virtual void Copy(CCodecBuffer& srcBuffer); + + virtual CodecBufferType GetBufferType() const { + return CBT_RGBA8888S; + }; + virtual CMP_DWORD GetChannelDepth() const { + return 8; + }; + virtual CMP_DWORD GetChannelCount() const { + return 4; + }; + virtual bool IsFloat() const { + return false; + }; + + virtual bool ReadBlockR(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE block[]); + virtual bool ReadBlockG(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE block[]); + virtual bool ReadBlockB(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE block[]); + virtual bool ReadBlockA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE block[]); + + virtual bool WriteBlockR(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE block[]); + virtual bool WriteBlockG(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE block[]); + virtual bool WriteBlockB(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE block[]); + virtual bool WriteBlockA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE block[]); + + virtual bool ReadBlockRGBA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE block[]); + virtual bool WriteBlockRGBA(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE block[]); + + protected: + virtual bool ReadBlock(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE block[], CMP_DWORD dwChannelOffset); + virtual bool WriteBlock(CMP_DWORD x, CMP_DWORD y, CMP_BYTE w, CMP_BYTE h, CMP_SBYTE block[], CMP_DWORD dwChannelOffset); +}; + +#endif // !defined(_CODECBUFFER_RGBA8888_H_INCLUDED_) diff --git a/cmp_compressonatorlib/cmakelists.txt b/cmp_compressonatorlib/cmakelists.txt index f3bb57086..5573c99d3 100644 --- a/cmp_compressonatorlib/cmakelists.txt +++ b/cmp_compressonatorlib/cmakelists.txt @@ -1,80 +1,35 @@ -cmake_minimum_required(VERSION 3.13) +cmake_minimum_required(VERSION 3.10) -# add_library(CompressonatorLIB) -# -# file(GLOB_RECURSE CMP_ASM_SRCS -# *.asm -# ) -# -# set(CMP_ASM_COMPILE_OPTIONS "/arch:SSE" "/arch:SSE2") -# -# foreach(asm_src ${CMP_ASM_SRCS}) -# set_source_files_properties(${asm_src} PROPERTIES -# LANGUAGE ASM_MASM -# COMPILE_DEFINITIONS X64=1 USE_SSE2=1 USE_SSE=1 -# ) -# endforeach() -# -# file(GLOB_RECURSE CMP_SRCS -# *.c -# *.cpp -# *.cxx -# *.h -# ) -# -# target_sources(CompressonatorLIB PRIVATE -# -# # ${CMP_ASM_SRCS} -# ${CMP_SRCS} -# ) -# -# if (CMP_HOST_WINDOWS) -# target_compile_definitions(CompressonatorLIB PRIVATE -D_WIN64) -# endif() -# -# target_include_directories(CompressonatorLIB PUBLIC -# ASTC -# ASTC/ARM/ -# ATC -# ATI -# BASIS -# BC6H -# BC7 -# Block -# Buffer -# DXT -# DXTC -# ETC -# ETC/etcpack/ -# GT -# Common -# ./ -# ) -# -# target_link_libraries(CompressonatorLIB PRIVATE -# -# PRIVATE -# CMP_Math -# CMP_Common -# CMP_FrameworkLIB -# CMP_Core -# -# Plugin_PluginManager -# Plugin_Common_KernelDef -# Plugin_Common_STBImage -# -# ExtOpenEXR -# Plugin_Half -# -# INTERFACE -# Plugin_Half -# ) +add_library(CMP_Compressonator STATIC "") -add_library(CompressonatorLIB STATIC "") +# Reserved for Future releases +file(GLOB_RECURSE CMP_CORE_SRC +# ${PROJECT_SOURCE_DIR}/cmp_core/shaders/bc1_encode_kernel.h +# ${PROJECT_SOURCE_DIR}/cmp_core/shaders/bc1_encode_kernel.cpp +# ${PROJECT_SOURCE_DIR}/cmp_core/shaders/bc2_encode_kernel.h +# ${PROJECT_SOURCE_DIR}/cmp_core/shaders/bc2_encode_kernel.cpp +# ${PROJECT_SOURCE_DIR}/cmp_core/shaders/bc3_encode_kernel.h +# ${PROJECT_SOURCE_DIR}/cmp_core/shaders/bc3_encode_kernel.cpp +# ${PROJECT_SOURCE_DIR}/cmp_core/shaders/bc4_encode_kernel.h +# ${PROJECT_SOURCE_DIR}/cmp_core/shaders/bc4_encode_kernel.cpp +# ${PROJECT_SOURCE_DIR}/cmp_core/shaders/bc5_encode_kernel.h +# ${PROJECT_SOURCE_DIR}/cmp_core/shaders/bc5_encode_kernel.cpp +# ${PROJECT_SOURCE_DIR}/cmp_core/shaders/bc6_encode_kernel.h +# ${PROJECT_SOURCE_DIR}/cmp_core/shaders/bc6_encode_kernel.cpp +# ${PROJECT_SOURCE_DIR}/cmp_core/shaders/bc7_encode_kernel.h +# ${PROJECT_SOURCE_DIR}/cmp_core/shaders/bc7_encode_kernel.cpp +# ${PROJECT_SOURCE_DIR}/cmp_core/shaders/bcn_common_kernel.h + ${PROJECT_SOURCE_DIR}/cmp_core/shaders/common_def.h +# ${PROJECT_SOURCE_DIR}/cmp_core/source/cmp_core.h +# ${PROJECT_SOURCE_DIR}/cmp_core/source/cmp_math_vec4.h +# ${PROJECT_SOURCE_DIR}/cmp_core/source/cmp_math_func.h +) file(GLOB_RECURSE CMP_SRCS "./*.h" "./*.cpp" + "apc/*.h" + "apc/*.cpp" "astc/*.h" "astc/*.cpp" "astc/arm/*.h" @@ -106,24 +61,23 @@ file(GLOB_RECURSE CMP_SRCS "etc/etcpack/*.cxx" "gt/*.h" "gt/*.cpp" - "apc/*.h" - "apc/*.cpp" "common/*.h" "common/*.cpp" - "../cmp_framework/common/*.h" - "../cmp_framework/common/*.cpp" - "../cmp_framework/common/half/*.h" - "../cmp_framework/common/half/*.cpp" - "../applications/_plugins/common/atiformats.cpp" - "../applications/_plugins/common/atiformats.h" + ${PROJECT_SOURCE_DIR}/cmp_framework/common/*.h + ${PROJECT_SOURCE_DIR}/cmp_framework/common/*.cpp + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half/*.h + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half/*.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/atiformats.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/atiformats.h ) -target_sources(CompressonatorLIB +target_sources(CMP_Compressonator PRIVATE ${CMP_SRCS} + ${CMP_CORE_SRC} ) -target_include_directories(CompressonatorLIB +target_include_directories(CMP_Compressonator PRIVATE ./ astc @@ -139,10 +93,13 @@ target_include_directories(CompressonatorLIB dxtc etc etc/etcpack/ - apc gt common - ../cmp_framework/common - ../cmp_framework/common/half - ../applications/_plugins/common) + ${PROJECT_SOURCE_DIR}/cmp_core/shaders + ${PROJECT_SOURCE_DIR}/cmp_core/source + ${PROJECT_SOURCE_DIR}/cmp_framework/common + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half + ${PROJECT_SOURCE_DIR}/applications/_plugins/common + ) +set_target_properties(CMP_Compressonator PROPERTIES FOLDER "Libs") diff --git a/cmp_compressonatorlib/common.h b/cmp_compressonatorlib/common.h index 71aa87637..a8cc34675 100644 --- a/cmp_compressonatorlib/common.h +++ b/cmp_compressonatorlib/common.h @@ -36,79 +36,122 @@ #include #include -#pragma warning( push ) +#pragma warning(push) #pragma warning(disable : 4244) #include -#pragma warning( pop ) +#pragma warning(pop) #ifndef CMP_MAKEFOURCC -#define CMP_MAKEFOURCC(ch0, ch1, ch2, ch3) \ - ((CMP_DWORD)(CMP_BYTE)(ch0) | ((CMP_DWORD)(CMP_BYTE)(ch1) << 8) | \ - ((CMP_DWORD)(CMP_BYTE)(ch2) << 16) | ((CMP_DWORD)(CMP_BYTE)(ch3) << 24 )) +#define CMP_MAKEFOURCC(ch0, ch1, ch2, ch3) \ + ((CMP_DWORD)(CMP_BYTE)(ch0) | ((CMP_DWORD)(CMP_BYTE)(ch1) << 8) | ((CMP_DWORD)(CMP_BYTE)(ch2) << 16) | ((CMP_DWORD)(CMP_BYTE)(ch3) << 24)) #endif -typedef half CMP_HALF; ///< A 16-bit floating point number class +typedef half CMP_HALF; ///< A 16-bit floating point number class -#define QT_KEY_SPACE 0x20 // Qt::Key_Space = 0x20 -#define QT_KEY_M 0x4d // Qt::Key_M = 0x4d +#define QT_KEY_SPACE 0x20 // Qt::Key_Space = 0x20 +#define QT_KEY_M 0x4d // Qt::Key_M = 0x4d #define UNREFERENCED_PARAMETER(P) (P) -typedef struct { - double SSIM; // Structural Similarity Index: Average of RGB Channels - double SSIM_Red; - double SSIM_Green; - double SSIM_Blue; - - double PSNR; // Peak Signal to Noise Ratio: Average of RGB Channels - double PSNR_Red; - double PSNR_Green; - double PSNR_Blue; +#ifndef cmp_isnan +#define cmp_isnan(x) std::isnan(x) +#endif - double MSE; // Mean Square Error +typedef enum _AnalysisErrorCodeType +{ + ANALYSIS_NOERROR = 0, + ANALYSIS_FAILED_FILESAVE = 1, + ANALYSIS_FAILED_FILELOAD = 2, + ANALYSIS_CUBEMAP_NOTSUPPORTED = 3, + ANALYSIS_CUBEMAP_TRANSCODE_NOTSUPPORTED = 4, + ANALYSIS_MEMORY_ERROR2 = 5, + ANALYSIS_MEMORY_ERROR3 = 6, + ANALYSIS_MEMORY_ERROR4 = 7, + ANALYSIS_MEMORY_ERROR5 = 8, + ANALYSIS_ATSC_TRANCODE_WITH_GPU_NOT_SUPPORTED = 9, + ANALYSIS_DECOMPRESSING_SOURCE = 10, + ANALYSIS_ERROR_COMPRESSING_DESTINATION_TEXTURE = 11, + ANALYSIS_MESH_COMPRESSION_FAILED = 12, + ANALYSIS_IMAGE_TESTFAILED = 13, + ANALYSIS_RETRIEVE_IMAGE_PROPERTIES = 14, + ANALYSIS_DESTINATION_TYPE_NOT_SUPPORTED = 15, + ANALYSIS_ASTC_DESTINATION_TYPE_NOT_SUPPORTED = 16, + ANALYSIS_ASTC_DESTINATION_FILE_FORMAT_NOTSET = 17, + ANALYSIS_ASTC_MIPMAP_DESTINATION_NOT_SUPPORTED = 18, + ANALYSIS_UNSUPPORTED_IMAGE_FORMAT = 19, + ANALYSIS_TRANSCODE_SRC_TO_DST_NOT_SUPPORTED = 20, + ANALYSIS_COMPRESSING_TEXTURE = 21, + ANALYSIS_MESH_OPTIMIZATION_FAILED = 22, + ANALYSIS_MESH_OPTIMIZATION_TYPE_FAILED = 23, +} AnalysisErrorCodeType; + +typedef struct +{ + double SSIM; // Structural Similarity Index: Average of RGB Channels + double SSIM_Red; + double SSIM_Green; + double SSIM_Blue; + + double PSNR; // Peak Signal to Noise Ratio: Average of RGB Channels + double PSNR_Red; + double PSNR_Green; + double PSNR_Blue; + + double MSE; // Mean Square Error + + unsigned int errCode; // Used in test automations if SSIM = {-1,-1,-1} this errCode is used to define types of processing error + // such as invaild transcoding formats, unsupport formats in destination files (dds,ktx,ktx2..) } CMP_ANALYSIS_DATA; - -struct CMP_CMIPS { +struct CMP_CMIPS +{ // User Configurable Print lines - int m_infolevel = 1; - void PrintError(const char* Format, ... ); - void(*PrintLine)(char *) = nullptr; + int m_infolevel = 1; + void PrintError(const char* Format, ...); + void (*PrintLine)(char*) = nullptr; void Print(const char* Format, ...); - CMP_MipLevel* GetMipLevel(const CMP_MipSet* pMipSet, CMP_INT nMipLevel, CMP_INT nFaceOrSlice=0); + CMP_MipLevel* GetMipLevel(const CMP_MipSet* pMipSet, CMP_INT nMipLevel, CMP_INT nFaceOrSlice = 0); - int GetMaxMipLevels(CMP_INT nWidth, CMP_INT nHeight, CMP_INT nDepth); + int GetMaxMipLevels(CMP_INT nWidth, CMP_INT nHeight, CMP_INT nDepth); - bool AllocateMipLevelTable(CMP_MipLevelTable** ppMipLevelTable, CMP_INT nMaxMipLevels, CMP_TextureType textureType, CMP_INT nDepth, CMP_INT& nLevelsToAllocate); + bool AllocateMipLevelTable(CMP_MipLevelTable** ppMipLevelTable, + CMP_INT nMaxMipLevels, + CMP_TextureType textureType, + CMP_INT nDepth, + CMP_INT& nLevelsToAllocate); bool AllocateAllMipLevels(CMP_MipLevelTable* pMipLevelTable, CMP_TextureType /*textureType*/, CMP_INT nLevelsToAllocate); - bool AllocateMipSet(CMP_MipSet* pMipSet, CMP_ChannelFormat channelFormat, CMP_TextureDataType textureDataType, CMP_TextureType textureType, CMP_INT nWidth, CMP_INT nHeight, CMP_INT nDepth); + bool AllocateMipSet(CMP_MipSet* pMipSet, + CMP_ChannelFormat channelFormat, + CMP_TextureDataType textureDataType, + CMP_TextureType textureType, + CMP_INT nWidth, + CMP_INT nHeight, + CMP_INT nDepth); bool AllocateMipLevelData(CMP_MipLevel* pMipLevel, CMP_INT nWidth, CMP_INT nHeight, CMP_ChannelFormat channelFormat, CMP_TextureDataType textureDataType); bool AllocateCompressedMipLevelData(CMP_MipLevel* pMipLevel, CMP_INT nWidth, CMP_INT nHeight, CMP_DWORD dwSize); - void FreeMipSet(CMP_MipSet* pMipSet); // Removes entire mipset - void FreeMipLevelData(CMP_MipLevel* pMipLevel); // removes a single miplevel generated by ...MipLevelData() + void FreeMipSet(CMP_MipSet* pMipSet); // Removes entire mipset + void FreeMipLevelData(CMP_MipLevel* pMipLevel); // removes a single miplevel generated by ...MipLevelData() - bool AllocateCompressedDestBuffer(CMP_MipSet *SourceTexture, CMP_FORMAT format, CMP_MipSet *DestTexture); + bool AllocateCompressedDestBuffer(CMP_MipSet* SourceTexture, CMP_FORMAT format, CMP_MipSet* DestTexture); // Progress - bool m_canceled = false; - void(*SetProgressValue)(unsigned int, bool *canceled) = nullptr; + bool m_canceled = false; + void (*SetProgressValue)(unsigned int, bool* canceled) = nullptr; void SetProgress(unsigned int value); - }; #define MAX_MIPLEVEL_SUPPORTED 20 -#define MIPSET_PIN_DATA_ID "69AEBDB3-5F67-436D-82C2-724FDC4972DA" +#define MIPSET_PIN_DATA_ID "69AEBDB3-5F67-436D-82C2-724FDC4972DA" -typedef CMP_CMIPS CMIPS; +typedef CMP_CMIPS CMIPS; - -extern void(*PrintStatusLine)(char *); +extern void (*PrintStatusLine)(char*); extern void PrintInfo(const char* Format, ...); #define MINIMUM_WEIGHT_VALUE 0.01f @@ -120,11 +163,11 @@ extern void PrintInfo(const char* Format, ...); #define AMD_CODEC_KNEEHIGH_DEFAULT 5 ///< This is the default value set for kneehigh value of hdr/exr input image #define AMD_CODEC_GAMMA_DEFAULT 2.2f ///< This is the default value set for gamma value of hdr/exr input image -#define CMP_MESH_COMP_LEVEL 7 ///< This is the default value set for draco compress level for mesh compression -#define CMP_MESH_POS_BITS 14 ///< This is the default value set for draco position quantization bits for mesh compression -#define CMP_MESH_TEXC_BITS 12 ///< This is the default value set for draco texture coordinate quantization bits for mesh compression -#define CMP_MESH_NORMAL_BITS 10 ///< This is the default value set for draco normal quantization bits for mesh compression -#define CMP_MESH_GENERIC_BITS 8 ///< This is the default value set for draco generic quantization bits for mesh compression +#define CMP_MESH_COMP_LEVEL 7 ///< This is the default value set for draco compress level for mesh compression +#define CMP_MESH_POS_BITS 14 ///< This is the default value set for draco position quantization bits for mesh compression +#define CMP_MESH_TEXC_BITS 12 ///< This is the default value set for draco texture coordinate quantization bits for mesh compression +#define CMP_MESH_NORMAL_BITS 10 ///< This is the default value set for draco normal quantization bits for mesh compression +#define CMP_MESH_GENERIC_BITS 8 ///< This is the default value set for draco generic quantization bits for mesh compression #ifdef USE_3DMESH_OPTIMIZE #define CMP_MESH_VCACHE_SIZE_DEFAULT 16 ///< This is the default value set for vertices cache size for mesh optimization @@ -134,16 +177,18 @@ extern void PrintInfo(const char* Format, ...); #define CMP_MESH_OPTVFETCH_DEFAULT 1 ///< This is the default boolean value set for vertices fetch mesh optimization. #endif -#define CMP_MIPSET_MAX_DEPTHS 6 ///< Maximum Depth (CubeMap Frames) for each MipSet +#define CMP_MIPSET_MAX_DEPTHS 6 ///< Maximum Depth (CubeMap Frames) for each MipSet -struct CMP_MAP_BYTES_SET { +struct CMP_MAP_BYTES_SET +{ CMP_BYTE B0; CMP_BYTE B1; CMP_BYTE B2; CMP_BYTE B3; }; -typedef enum _CodecType { +typedef enum _CodecType +{ CT_Unknown = 0, CT_None, CT_DXT1, @@ -158,6 +203,7 @@ typedef enum _CodecType { CT_ATI1N, CT_ATI1N_S, CT_ATI2N, + CT_ATI2N_S, CT_ATI2N_XY, CT_ATI2N_XY_S, CT_ATI2N_DXT5, @@ -168,7 +214,7 @@ typedef enum _CodecType { #ifdef SUPPORT_ETC_ALPHA CT_ETC_RGBA_Explicit, CT_ETC_RGBA_Interpolated, -#endif // SUPPORT_ETC_ALPHA +#endif // SUPPORT_ETC_ALPHA CT_ETC2_RGB, CT_ETC2_SRGB, CT_ETC2_RGBA, @@ -187,12 +233,11 @@ typedef enum _CodecType { CODECS_AMD_INTERNAL } CodecType; -typedef enum _CODECError { +typedef enum _CODECError +{ CE_OK = 0, CE_Unknown, CE_Aborted, } CodecError; - - #endif // !COMMON_H diff --git a/cmp_compressonatorlib/common/codec.cpp b/cmp_compressonatorlib/common/codec.cpp index a1a3fd7c5..eb083df12 100644 --- a/cmp_compressonatorlib/common/codec.cpp +++ b/cmp_compressonatorlib/common/codec.cpp @@ -169,10 +169,12 @@ CCodec* CreateCodec(CodecType nCodecType) { return new CCodec_ATI1N_S; case CT_ATI2N: return new CCodec_ATI2N; + case CT_ATI2N_S: + return new CCodec_ATI2N_S; case CT_ATI2N_XY: return new CCodec_ATI2N(CT_ATI2N_XY); case CT_ATI2N_XY_S: - return new CCodec_ATI2N(CT_ATI2N_XY_S); + return new CCodec_ATI2N_S(CT_ATI2N_XY_S); case CT_ATI2N_DXT5: return new CCodec_ATI2N_DXT5; case CT_ATC_RGB: @@ -271,6 +273,7 @@ CMP_DWORD CalcBufferSize(CodecType nCodecType, CMP_DWORD dwWidth, CMP_DWORD dwHe case CT_DXT5_RGxB: case CT_DXT5_xGxR: case CT_ATI2N: + case CT_ATI2N_S: case CT_ATI2N_XY: case CT_ATI2N_XY_S: case CT_ATI2N_DXT5: diff --git a/cmp_compressonatorlib/common/commontypes.h b/cmp_compressonatorlib/common/commontypes.h index eaef1dc9e..8e81a936d 100644 --- a/cmp_compressonatorlib/common/commontypes.h +++ b/cmp_compressonatorlib/common/commontypes.h @@ -59,77 +59,94 @@ typedef struct { /// Typically reordered in alpha betical order per catagory for easy referance // Use the enum by name and not by its value // -typedef enum { - CMP_FORMAT_Unknown, ///< Undefined texture format. +// Texture format. +// Texture format. +typedef enum +{ + CMP_FORMAT_Unknown = 0, // Undefined texture format. + // Channel Component formats -------------------------------------------------------------------------------- - CMP_FORMAT_ARGB_8888, ///< ARGB format with 8-bit fixed channels. - CMP_FORMAT_ABGR_8888, ///< ABGR format with 8-bit fixed channels. - CMP_FORMAT_RGBA_8888, ///< RGBA format with 8-bit fixed channels. - CMP_FORMAT_BGRA_8888, ///< BGRA format with 8-bit fixed channels. - CMP_FORMAT_RGB_888, ///< RGB format with 8-bit fixed channels. - CMP_FORMAT_BGR_888, ///< BGR format with 8-bit fixed channels. - CMP_FORMAT_RG_8, ///< Two component format with 8-bit fixed channels. - CMP_FORMAT_R_8, ///< Single component format with 8-bit fixed channels. - CMP_FORMAT_ARGB_2101010, ///< ARGB format with 10-bit fixed channels for color & a 2-bit fixed channel for alpha. - CMP_FORMAT_ARGB_16, ///< ARGB format with 16-bit fixed channels. - CMP_FORMAT_ABGR_16, ///< ABGR format with 16-bit fixed channels. - CMP_FORMAT_RGBA_16, ///< RGBA format with 16-bit fixed channels. - CMP_FORMAT_BGRA_16, ///< BGRA format with 16-bit fixed channels. - CMP_FORMAT_RG_16, ///< Two component format with 16-bit fixed channels. - CMP_FORMAT_R_16, ///< Single component format with 16-bit fixed channels. - CMP_FORMAT_RGBE_32F, ///< RGB format with 9-bit floating point each channel and shared 5 bit exponent - CMP_FORMAT_ARGB_16F, ///< ARGB format with 16-bit floating-point channels. - CMP_FORMAT_ABGR_16F, ///< ABGR format with 16-bit floating-point channels. - CMP_FORMAT_RGBA_16F, ///< RGBA format with 16-bit floating-point channels. - CMP_FORMAT_BGRA_16F, ///< BGRA format with 16-bit floating-point channels. - CMP_FORMAT_RG_16F, ///< Two component format with 16-bit floating-point channels. - CMP_FORMAT_R_16F, ///< Single component with 16-bit floating-point channels. - CMP_FORMAT_ARGB_32F, ///< ARGB format with 32-bit floating-point channels. - CMP_FORMAT_ABGR_32F, ///< ABGR format with 32-bit floating-point channels. - CMP_FORMAT_RGBA_32F, ///< RGBA format with 32-bit floating-point channels. - CMP_FORMAT_BGRA_32F, ///< BGRA format with 32-bit floating-point channels. - CMP_FORMAT_RGB_32F, ///< RGB format with 32-bit floating-point channels. - CMP_FORMAT_BGR_32F, ///< BGR format with 32-bit floating-point channels. - CMP_FORMAT_RG_32F, ///< Two component format with 32-bit floating-point channels. - CMP_FORMAT_R_32F, ///< Single component with 32-bit floating-point channels. - // Compression formats ----------------------------------------------------------------------------------- - CMP_FORMAT_ASTC, ///< ASTC (Adaptive Scalable Texture Compression) open texture compression standard - CMP_FORMAT_ATI1N, ///< Single component compression format using the same technique as DXT5 alpha. Four bits per pixel. - CMP_FORMAT_ATI2N, ///< Two component compression format using the same technique as DXT5 alpha. Designed for compression of tangent space normal maps. Eight bits per pixel. - CMP_FORMAT_ATI2N_XY, ///< Two component compression format using the same technique as DXT5 alpha. The same as ATI2N but with the channels swizzled. Eight bits per pixel. - CMP_FORMAT_ATI2N_DXT5, ///< ATI2N like format using DXT5. Intended for use on GPUs that do not natively support ATI2N. Eight bits per pixel. - CMP_FORMAT_ATC_RGB, ///< CMP - a compressed RGB format. - CMP_FORMAT_ATC_RGBA_Explicit, ///< CMP - a compressed ARGB format with explicit alpha. - CMP_FORMAT_ATC_RGBA_Interpolated, ///< CMP - a compressed ARGB format with interpolated alpha. - CMP_FORMAT_BC1, ///< A four component opaque (or 1-bit alpha) compressed texture format for Microsoft DirectX10. Identical to DXT1. Four bits per pixel. - CMP_FORMAT_BC2, ///< A four component compressed texture format with explicit alpha for Microsoft DirectX10. Identical to DXT3. Eight bits per pixel. - CMP_FORMAT_BC3, ///< A four component compressed texture format with interpolated alpha for Microsoft DirectX10. Identical to DXT5. Eight bits per pixel. - CMP_FORMAT_BC4, ///< A single component compressed texture format for Microsoft DirectX10. Identical to ATI1N. Four bits per pixel. - CMP_FORMAT_BC4_S, ///< A single component compressed texture format for Microsoft DirectX10. Identical to ATI1N. Four bits per pixel. - CMP_FORMAT_BC5, ///< A two component compressed texture format for Microsoft DirectX10. Identical to ATI2N_XY.(Red&Green) Eight bits per pixel. - CMP_FORMAT_BC5_S, ///< A two component compressed texture format for Microsoft DirectX10. Identical to ATI2N_XY.(Red&Green) Eight bits per pixel. - CMP_FORMAT_BC6H, ///< BC6H compressed texture format (UF) - CMP_FORMAT_BC6H_SF, ///< BC6H compressed texture format (SF) - CMP_FORMAT_BC7, ///< BC7 compressed texture format - CMP_FORMAT_DXT1, ///< An DXTC compressed texture matopaque (or 1-bit alpha). Four bits per pixel. - CMP_FORMAT_DXT3, ///< DXTC compressed texture format with explicit alpha. Eight bits per pixel. - CMP_FORMAT_DXT5, ///< DXTC compressed texture format with interpolated alpha. Eight bits per pixel. - CMP_FORMAT_DXT5_xGBR, ///< DXT5 with the red component swizzled into the alpha channel. Eight bits per pixel. - CMP_FORMAT_DXT5_RxBG, ///< swizzled DXT5 format with the green component swizzled into the alpha channel. Eight bits per pixel. - CMP_FORMAT_DXT5_RBxG, ///< swizzled DXT5 format with the green component swizzled into the alpha channel & the blue component swizzled into the green channel. Eight bits per pixel. - CMP_FORMAT_DXT5_xRBG, ///< swizzled DXT5 format with the green component swizzled into the alpha channel & the red component swizzled into the green channel. Eight bits per pixel. - CMP_FORMAT_DXT5_RGxB, ///< swizzled DXT5 format with the blue component swizzled into the alpha channel. Eight bits per pixel. - CMP_FORMAT_DXT5_xGxR, ///< two-component swizzled DXT5 format with the red component swizzled into the alpha channel & the green component in the green channel. Eight bits per pixel. - CMP_FORMAT_ETC_RGB, ///< ETC GL_COMPRESSED_RGB8_ETC2 backward compatible - CMP_FORMAT_ETC2_RGB, ///< ETC2 GL_COMPRESSED_RGB8_ETC2 - CMP_FORMAT_ETC2_SRGB, ///< ETC2 GL_COMPRESSED_SRGB8_ETC2 - CMP_FORMAT_ETC2_RGBA, ///< ETC2 GL_COMPRESSED_RGBA8_ETC2_EAC - CMP_FORMAT_ETC2_RGBA1, ///< ETC2 GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 - CMP_FORMAT_ETC2_SRGBA, ///< ETC2 GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC - CMP_FORMAT_ETC2_SRGBA1,///< ETC2 GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 - CMP_FORMAT_GT, ///< GT (Reserved for a future implementation) - //-------------------------------------------------------------------------------------------------------- - CMP_FORMAT_MAX = CMP_FORMAT_GT + CMP_FORMAT_RGBA_8888_S, // RGBA format with signed 8-bit fixed channels. + CMP_FORMAT_ARGB_8888_S, // ARGB format with signed 8-bit fixed channels. + CMP_FORMAT_ARGB_8888, // ARGB format with 8-bit fixed channels. + CMP_FORMAT_ABGR_8888, // ABGR format with 8-bit fixed channels. + CMP_FORMAT_RGBA_8888, // RGBA format with 8-bit fixed channels. + CMP_FORMAT_BGRA_8888, // BGRA format with 8-bit fixed channels. + CMP_FORMAT_RGB_888, // RGB format with 8-bit fixed channels. + CMP_FORMAT_RGB_888_S, // RGB format with 8-bit fixed channels. + CMP_FORMAT_BGR_888, // BGR format with 8-bit fixed channels. + CMP_FORMAT_RG_8_S, // Two component format with signed 8-bit fixed channels. + CMP_FORMAT_RG_8, // Two component format with 8-bit fixed channels. + CMP_FORMAT_R_8_S, // Single component format with signed 8-bit fixed channel. + CMP_FORMAT_R_8, // Single component format with 8-bit fixed channel. + CMP_FORMAT_ARGB_2101010, // ARGB format with 10-bit fixed channels for color & a 2-bit fixed channel for alpha. + CMP_FORMAT_ARGB_16, // ARGB format with 16-bit fixed channels. + CMP_FORMAT_ABGR_16, // ABGR format with 16-bit fixed channels. + CMP_FORMAT_RGBA_16, // RGBA format with 16-bit fixed channels. + CMP_FORMAT_BGRA_16, // BGRA format with 16-bit fixed channels. + CMP_FORMAT_RG_16, // Two component format with 16-bit fixed channels. + CMP_FORMAT_R_16, // Single component format with 16-bit fixed channels. + CMP_FORMAT_RGBE_32F, // RGB format with 9-bit floating point each channel and shared 5 bit exponent + CMP_FORMAT_ARGB_16F, // ARGB format with 16-bit floating-point channels. + CMP_FORMAT_ABGR_16F, // ABGR format with 16-bit floating-point channels. + CMP_FORMAT_RGBA_16F, // RGBA format with 16-bit floating-point channels. + CMP_FORMAT_BGRA_16F, // BGRA format with 16-bit floating-point channels. + CMP_FORMAT_RG_16F, // Two component format with 16-bit floating-point channels. + CMP_FORMAT_R_16F, // Single component with 16-bit floating-point channels. + CMP_FORMAT_ARGB_32F, // ARGB format with 32-bit floating-point channels. + CMP_FORMAT_ABGR_32F, // ABGR format with 32-bit floating-point channels. + CMP_FORMAT_RGBA_32F, // RGBA format with 32-bit floating-point channels. + CMP_FORMAT_BGRA_32F, // BGRA format with 32-bit floating-point channels. + CMP_FORMAT_RGB_32F, // RGB format with 32-bit floating-point channels. + CMP_FORMAT_BGR_32F, // BGR format with 32-bit floating-point channels. + CMP_FORMAT_RG_32F, // Two component format with 32-bit floating-point channels. + CMP_FORMAT_R_32F, // Single component with 32-bit floating-point channels. + + // Compression formats ------------ GPU Mapping DirectX, Vulkan and OpenGL formats and comments -------- + CMP_FORMAT_ASTC, // DXGI_FORMAT_UNKNOWN VK_FORMAT_ASTC_4x4_UNORM_BLOCK to VK_FORMAT_ASTC_12x12_UNORM_BLOCK + CMP_FORMAT_ATI1N, // DXGI_FORMAT_BC4_UNORM VK_FORMAT_BC4_UNORM_BLOCK GL_COMPRESSED_RED_RGTC1 Single component compression format using the same technique as DXT5 alpha. Four bits per pixel. + CMP_FORMAT_ATI2N, // DXGI_FORMAT_BC5_UNORM VK_FORMAT_BC5_UNORM_BLOCK GL_COMPRESSED_RG_RGTC2 Two component compression format using the same technique as DXT5 alpha. Designed for compression of tangent space normal maps. Eight bits per pixel. + CMP_FORMAT_ATI2N_XY, // DXGI_FORMAT_BC5_UNORM VK_FORMAT_BC5_UNORM_BLOCK GL_COMPRESSED_RG_RGTC2 Two component compression format using the same technique as DXT5 alpha. The same as ATI2N but with the channels swizzled. Eight bits per pixel. + CMP_FORMAT_ATI2N_DXT5, // DXGI_FORMAT_BC5_UNORM VK_FORMAT_BC5_UNORM_BLOCK GL_COMPRESSED_RG_RGTC2 ATI2N like format using DXT5. Intended for use on GPUs that do not natively support ATI2N. Eight bits per pixel. + CMP_FORMAT_ATC_RGB, // CMP - a compressed RGB format. + CMP_FORMAT_ATC_RGBA_Explicit, // CMP - a compressed ARGB format with explicit alpha. + CMP_FORMAT_ATC_RGBA_Interpolated, // CMP - a compressed ARGB format with interpolated alpha. + CMP_FORMAT_BC1, // DXGI_FORMAT_BC1_UNORM GL_COMPRESSED_RGBA_S3TC_DXT1_EXT A four component opaque (or 1-bit alpha) compressed texture format for Microsoft DirectX10. Identical to DXT1. Four bits per pixel. + CMP_FORMAT_BC2, // DXGI_FORMAT_BC2_UNORM VK_FORMAT_BC2_UNORM_BLOCK GL_COMPRESSED_RGBA_S3TC_DXT3_EXT A four component compressed texture format with explicit alpha for Microsoft DirectX10. Identical to DXT3. Eight bits per pixel. + CMP_FORMAT_BC3, // DXGI_FORMAT_BC3_UNORM VK_FORMAT_BC3_UNORM_BLOCK GL_COMPRESSED_RGBA_S3TC_DXT5_EXT A four component compressed texture format with interpolated alpha for Microsoft DirectX10. Identical to DXT5. Eight bits per pixel. + CMP_FORMAT_BC4, // DXGI_FORMAT_BC4_UNORM VK_FORMAT_BC4_UNORM_BLOCK GL_COMPRESSED_RED_RGTC1 A single component compressed texture format for Microsoft DirectX10. Identical to ATI1N. Four bits per pixel. + CMP_FORMAT_BC4_S, // DXGI_FORMAT_BC4_SNORM VK_FORMAT_BC4_SNORM_BLOCK GL_COMPRESSED_SIGNED_RED_RGTC1 A single component compressed texture format for Microsoft DirectX10. Identical to ATI1N. Four bits per pixel. + CMP_FORMAT_BC5, // DXGI_FORMAT_BC5_UNORM VK_FORMAT_BC5_UNORM_BLOCK GL_COMPRESSED_RG_RGTC2 A two component compressed texture format for Microsoft DirectX10. Identical to ATI2N_XY. Eight bits per pixel. + CMP_FORMAT_BC5_S, // DXGI_FORMAT_BC5_SNORM VK_FORMAT_BC5_SNORM_BLOCK GL_COMPRESSED_RGBA_BPTC_UNORM A two component compressed texture format for Microsoft DirectX10. Identical to ATI2N_XY. Eight bits per pixel. + CMP_FORMAT_BC6H, // DXGI_FORMAT_BC6H_UF16 VK_FORMAT_BC6H_UFLOAT_BLOCK GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT BC6H compressed texture format (UF) + CMP_FORMAT_BC6H_SF, // DXGI_FORMAT_BC6H_SF16 VK_FORMAT_BC6H_SFLOAT_BLOCK GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT BC6H compressed texture format (SF) + CMP_FORMAT_BC7, // DXGI_FORMAT_BC7_UNORM VK_FORMAT_BC7_UNORM_BLOCK GL_COMPRESSED_RGBA_BPTC_UNORM BC7 compressed texture format + CMP_FORMAT_DXT1, // DXGI_FORMAT_BC1_UNORM VK_FORMAT_BC1_RGB_UNORM_BLOCK GL_COMPRESSED_RGBA_S3TC_DXT1_EXT An DXTC compressed texture matopaque (or 1-bit alpha). Four bits per pixel. + CMP_FORMAT_DXT3, // DXGI_FORMAT_BC2_UNORM VK_FORMAT_BC2_UNORM_BLOCK GL_COMPRESSED_RGBA_S3TC_DXT3_EXT DXTC compressed texture format with explicit alpha. Eight bits per pixel. + CMP_FORMAT_DXT5, // DXGI_FORMAT_BC3_UNORM VK_FORMAT_BC3_UNORM_BLOCK GL_COMPRESSED_RGBA_S3TC_DXT5_EXT DXTC compressed texture format with interpolated alpha. Eight bits per pixel. + CMP_FORMAT_DXT5_xGBR, // DXGI_FORMAT_UNKNOWN DXT5 with the red component swizzled into the alpha channel. Eight bits per pixel. + CMP_FORMAT_DXT5_RxBG, // DXGI_FORMAT_UNKNOWN swizzled DXT5 format with the green component swizzled into the alpha channel. Eight bits per pixel. + CMP_FORMAT_DXT5_RBxG, // DXGI_FORMAT_UNKNOWN swizzled DXT5 format with the green component swizzled into the alpha channel & the blue component swizzled into the green channel. Eight bits per pixel. + CMP_FORMAT_DXT5_xRBG, // DXGI_FORMAT_UNKNOWN swizzled DXT5 format with the green component swizzled into the alpha channel & the red component swizzled into the green channel. Eight bits per pixel. + CMP_FORMAT_DXT5_RGxB, // DXGI_FORMAT_UNKNOWN swizzled DXT5 format with the blue component swizzled into the alpha channel. Eight bits per pixel. + CMP_FORMAT_DXT5_xGxR, // two-component swizzled DXT5 format with the red component swizzled into the alpha channel & the green component in the green channel. Eight bits per pixel. + CMP_FORMAT_ETC_RGB, // DXGI_FORMAT_UNKNOWN VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK GL_COMPRESSED_RGB8_ETC2 backward compatible + CMP_FORMAT_ETC2_RGB, // DXGI_FORMAT_UNKNOWN VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK GL_COMPRESSED_RGB8_ETC2 + CMP_FORMAT_ETC2_SRGB, // DXGI_FORMAT_UNKNOWN VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK GL_COMPRESSED_SRGB8_ETC2 + CMP_FORMAT_ETC2_RGBA, // DXGI_FORMAT_UNKNOWN VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK GL_COMPRESSED_RGBA8_ETC2_EAC + CMP_FORMAT_ETC2_RGBA1, // DXGI_FORMAT_UNKNOWN VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 + CMP_FORMAT_ETC2_SRGBA, // DXGI_FORMAT_UNKNOWN VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC + CMP_FORMAT_ETC2_SRGBA1, // DXGI_FORMAT_UNKNOWN VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 + CMP_FORMAT_PVRTC, // +#ifdef USE_APC + CMP_FORMAT_APC, //< APC Texture Compressor +#endif + // Transcoder formats - ------------------------------------------------------------ + CMP_FORMAT_GTC, //< GTC Fast Gradient Texture Compressor + CMP_FORMAT_BASIS, //< BASIS compression + + // End of list + CMP_FORMAT_MAX = CMP_FORMAT_BASIS } CMP_FORMAT; diff --git a/cmp_compressonatorlib/compress.cpp b/cmp_compressonatorlib/compress.cpp index 27739e98b..fadc2f48c 100644 --- a/cmp_compressonatorlib/compress.cpp +++ b/cmp_compressonatorlib/compress.cpp @@ -60,6 +60,7 @@ CodecType GetCodecType(CMP_FORMAT format) { return CT_None; case CMP_FORMAT_BGRA_8888: return CT_None; + case CMP_FORMAT_RGBA_8888_S: case CMP_FORMAT_ARGB_8888: return CT_None; case CMP_FORMAT_BGR_888: @@ -76,6 +77,7 @@ CodecType GetCodecType(CMP_FORMAT format) { return CT_None; case CMP_FORMAT_R_16: return CT_None; + case CMP_FORMAT_ABGR_16F: case CMP_FORMAT_ARGB_16F: return CT_None; case CMP_FORMAT_RG_16F: @@ -182,7 +184,7 @@ CodecType GetCodecType(CMP_FORMAT format) { return CT_Unknown; } } -#ifdef ENABLE_MAKE_COMPATIBLE_API + bool IsFloatFormat(CMP_FORMAT InFormat) { switch (InFormat) { case CMP_FORMAT_ARGB_16F: @@ -212,6 +214,103 @@ bool IsFloatFormat(CMP_FORMAT InFormat) { return false; } +bool IsCompressedFormat(CMP_FORMAT InFormat) +{ + switch (InFormat) + { + case CMP_FORMAT_ASTC: + case CMP_FORMAT_ATI1N: + case CMP_FORMAT_ATI2N: + case CMP_FORMAT_ATI2N_XY: + case CMP_FORMAT_ATI2N_DXT5: + case CMP_FORMAT_ATC_RGB: + case CMP_FORMAT_ATC_RGBA_Explicit: + case CMP_FORMAT_ATC_RGBA_Interpolated: + case CMP_FORMAT_BC1: + case CMP_FORMAT_BC2: + case CMP_FORMAT_BC3: + case CMP_FORMAT_BC4: + case CMP_FORMAT_BC4_S: + case CMP_FORMAT_BC5: + case CMP_FORMAT_BC5_S: + case CMP_FORMAT_BC6H: + case CMP_FORMAT_BC6H_SF: + case CMP_FORMAT_BC7: + case CMP_FORMAT_DXT1: + case CMP_FORMAT_DXT3: + case CMP_FORMAT_DXT5: + case CMP_FORMAT_DXT5_xGBR: + case CMP_FORMAT_DXT5_RxBG: + case CMP_FORMAT_DXT5_RBxG: + case CMP_FORMAT_DXT5_xRBG: + case CMP_FORMAT_DXT5_RGxB: + case CMP_FORMAT_DXT5_xGxR: + case CMP_FORMAT_ETC_RGB: + case CMP_FORMAT_ETC2_RGB: + case CMP_FORMAT_ETC2_SRGB: + case CMP_FORMAT_ETC2_RGBA: + case CMP_FORMAT_ETC2_RGBA1: + case CMP_FORMAT_ETC2_SRGBA: + case CMP_FORMAT_ETC2_SRGBA1: + case CMP_FORMAT_PVRTC: +#ifdef USE_APC + case CMP_FORMAT_APC: //< APC Texture Compressor +#endif + case CMP_FORMAT_GTC : //< GTC Fast Gradient Texture Compressor + case CMP_FORMAT_BASIS : //< BASIS compression + { + return true; + } + break; + default: + break; + } + + return false; +} + +CMP_BYTE FormatChannelBitSize(CMP_FORMAT InFormat) +{ + switch (InFormat) + { + case CMP_FORMAT_ARGB_16F: + case CMP_FORMAT_ABGR_16F: + case CMP_FORMAT_RGBA_16F: + case CMP_FORMAT_BGRA_16F: + case CMP_FORMAT_RG_16F: + case CMP_FORMAT_R_16F: + case CMP_FORMAT_ARGB_16: + case CMP_FORMAT_ABGR_16: + case CMP_FORMAT_RGBA_16: + case CMP_FORMAT_BGRA_16: + case CMP_FORMAT_RG_16: + case CMP_FORMAT_R_16: + case CMP_FORMAT_BC6H: + case CMP_FORMAT_BC6H_SF: + case CMP_FORMAT_ARGB_2101010: + { + return (CMP_BYTE)(16); + } + break; + + case CMP_FORMAT_ARGB_32F: + case CMP_FORMAT_ABGR_32F: + case CMP_FORMAT_RGBA_32F: + case CMP_FORMAT_BGRA_32F: + case CMP_FORMAT_RGB_32F: + case CMP_FORMAT_BGR_32F: + case CMP_FORMAT_RG_32F: + case CMP_FORMAT_R_32F: + case CMP_FORMAT_RGBE_32F: + { + return (CMP_BYTE)(32); + } + break; + } + + return (CMP_BYTE)(8); +} + bool NeedSwizzle(CMP_FORMAT destformat) { // determin of the swizzle flag needs to be turned on! switch (destformat) { @@ -285,7 +384,6 @@ CMP_ERROR Byte2HalfShort(CMP_HALFSHORT* hfsBlock, CMP_BYTE* cBlock, CMP_DWORD dw return CMP_OK; } - CMP_ERROR Float2Byte(CMP_BYTE cBlock[], CMP_FLOAT* fBlock, CMP_Texture* srcTexture, CMP_FORMAT destFormat, const CMP_CompressOptions* pOptions) { (destFormat); assert(cBlock); @@ -432,7 +530,85 @@ CMP_ERROR Float2Byte(CMP_BYTE cBlock[], CMP_FLOAT* fBlock, CMP_Texture* srcTextu return CMP_OK; } -#endif + +CMP_ERROR RGBA_Word2Byte(CMP_BYTE cBlock[], CMP_WORD* wBlock, CMP_Texture* srcTexture) +{ + assert(cBlock); + assert(wBlock); + assert(&srcTexture); + + if (cBlock && wBlock) + { + unsigned int cBlockIndex = 0; + unsigned int wBlockIndex = 0; + for (unsigned int y = 0; y < srcTexture->dwHeight; y++) + { + for (unsigned int x = 0; x < srcTexture->dwWidth; x++) + { + // 4 channel data conversion from 16 bit to 8 bit + cBlock[cBlockIndex++] = wBlock[wBlockIndex++] / 257; + cBlock[cBlockIndex++] = wBlock[wBlockIndex++] / 257; + cBlock[cBlockIndex++] = wBlock[wBlockIndex++] / 257; + cBlock[cBlockIndex++] = wBlock[wBlockIndex++] / 257; + } + } + } + + return CMP_OK; +} + +CMP_ERROR SByte2Byte(CMP_BYTE cBlock[], CMP_SBYTE* sBlock, CMP_Texture* srcTexture) +{ + assert(cBlock); + assert(sBlock); + assert(&srcTexture); + + if (cBlock && sBlock) + { + unsigned int cBlockIndex = 0; + unsigned int wBlockIndex = 0; + for (unsigned int y = 0; y < srcTexture->dwHeight; y++) + { + for (unsigned int x = 0; x < srcTexture->dwWidth; x++) + { + // 4 channel data conversion from 16 bit to 8 bit + cBlock[cBlockIndex++] = sBlock[wBlockIndex++] + 127; + cBlock[cBlockIndex++] = sBlock[wBlockIndex++] + 127; + cBlock[cBlockIndex++] = sBlock[wBlockIndex++] + 127; + cBlock[cBlockIndex++] = sBlock[wBlockIndex++] + 127; // ?? alpha + } + } + } + + return CMP_OK; +} + +CMP_ERROR Byte2SByte(CMP_SBYTE sBlock[], CMP_BYTE* cBlock, CMP_Texture* srcTexture) +{ + assert(cBlock); + assert(sBlock); + assert(&srcTexture); + + if (cBlock && sBlock) + { + unsigned int cBlockIndex = 0; + unsigned int wBlockIndex = 0; + for (unsigned int y = 0; y < srcTexture->dwHeight; y++) + { + for (unsigned int x = 0; x < srcTexture->dwWidth; x++) + { + sBlock[cBlockIndex++] = cBlock[wBlockIndex++] - 127; + sBlock[cBlockIndex++] = cBlock[wBlockIndex++] - 127; + sBlock[cBlockIndex++] = cBlock[wBlockIndex++] - 127; + sBlock[cBlockIndex++] = cBlock[wBlockIndex++] - 127; // ?? alpha + } + } + } + + return CMP_OK; +} + + CMP_ERROR GetError(CodecError err) { switch(err) { case CE_OK: @@ -463,16 +639,24 @@ CMP_ERROR CheckTexture(const CMP_Texture* pTexture, bool bSource) { if(pTexture->dwHeight <= 0 ) return (bSource ? CMP_ERR_INVALID_SOURCE_TEXTURE : CMP_ERR_INVALID_DEST_TEXTURE); - assert(pTexture->format >= CMP_FORMAT_ARGB_8888 && pTexture->format <= CMP_FORMAT_MAX); - if(pTexture->format < CMP_FORMAT_ARGB_8888 || pTexture->format > CMP_FORMAT_MAX) + assert(pTexture->format >= CMP_FORMAT_RGBA_8888_S && pTexture->format <= CMP_FORMAT_MAX); + if (pTexture->format < CMP_FORMAT_RGBA_8888_S || pTexture->format > CMP_FORMAT_MAX) return (bSource ? CMP_ERR_UNSUPPORTED_SOURCE_FORMAT : CMP_ERR_UNSUPPORTED_DEST_FORMAT); assert((pTexture->format != CMP_FORMAT_ARGB_8888 && pTexture->format != CMP_FORMAT_ARGB_2101010) || pTexture->dwPitch == 0 || pTexture->dwPitch >= (pTexture->dwWidth*4)); + + assert((pTexture->format != CMP_FORMAT_RGBA_8888_S && pTexture->format != CMP_FORMAT_ARGB_2101010) || pTexture->dwPitch == 0 || + pTexture->dwPitch >= (pTexture->dwWidth * 4)); + if((pTexture->format == CMP_FORMAT_ARGB_8888 || pTexture->format == CMP_FORMAT_ARGB_2101010) && pTexture->dwPitch != 0 && pTexture->dwPitch < (pTexture->dwWidth*4)) return (bSource ? CMP_ERR_UNSUPPORTED_SOURCE_FORMAT : CMP_ERR_UNSUPPORTED_DEST_FORMAT); + if ((pTexture->format == CMP_FORMAT_RGBA_8888_S || pTexture->format == CMP_FORMAT_ARGB_2101010) && pTexture->dwPitch != 0 && + pTexture->dwPitch < (pTexture->dwWidth * 4)) + return (bSource ? CMP_ERR_UNSUPPORTED_SOURCE_FORMAT : CMP_ERR_UNSUPPORTED_DEST_FORMAT); + assert(pTexture->pData); if(pTexture->pData == NULL) return (bSource ? CMP_ERR_INVALID_SOURCE_TEXTURE : CMP_ERR_INVALID_DEST_TEXTURE); @@ -599,6 +783,7 @@ CMP_ERROR CompressTexture(const CMP_Texture* pSourceTexture, CMP_Texture* pDestT pSourceTexture->nBlockWidth, pSourceTexture->nBlockHeight, pSourceTexture->nBlockDepth, pSourceTexture->dwWidth, pSourceTexture->dwHeight, pSourceTexture->dwPitch, pSourceTexture->pData, pSourceTexture->dwDataSize); + CCodecBuffer* pDestBuffer = pCodec->CreateBuffer( pDestTexture->nBlockWidth, pDestTexture->nBlockHeight, pDestTexture->nBlockDepth, pDestTexture->dwWidth, pDestTexture->dwHeight, pDestTexture->dwPitch, pDestTexture->pData, @@ -613,6 +798,10 @@ CMP_ERROR CompressTexture(const CMP_Texture* pSourceTexture, CMP_Texture* pDestT return CMP_ERR_GENERIC; } + pSrcBuffer->SetFormat(pSourceTexture->format); + pDestBuffer->SetFormat(pDestTexture->format); + + // GPUOpen issue # 59 and #67 fix pSrcBuffer->m_bSwizzle = swizzleSrcBuffer; DISABLE_FP_EXCEPTIONS; @@ -777,14 +966,18 @@ CMP_ERROR ThreadedCompressTexture(const CMP_Texture* pSourceTexture, CMP_Texture dwHeight = dwLinesRemaining; if(dwHeight > 0) { + threadData.m_pSrcBuffer = CreateCodecBuffer(srcBufferType, pSourceTexture->nBlockWidth, pSourceTexture->nBlockHeight, pSourceTexture->nBlockDepth, pSourceTexture->dwWidth, dwHeight, pSourceTexture->dwPitch, pSourceData, pSourceTexture->dwDataSize); + threadData.m_pSrcBuffer->SetFormat(pSourceTexture->format); + threadData.m_pDestBuffer = threadData.m_pCodec->CreateBuffer( pDestTexture->nBlockWidth, pDestTexture->nBlockHeight, pDestTexture->nBlockDepth, pDestTexture->dwWidth, dwHeight, pDestTexture->dwPitch, pDestData, pDestTexture->dwDataSize); + threadData.m_pDestBuffer->SetFormat(pDestTexture->format); pSourceData += CalcBufferSize(pSourceTexture->format, pSourceTexture->dwWidth, dwHeight, pSourceTexture->dwPitch, pSourceTexture->nBlockWidth, pSourceTexture->nBlockHeight); pDestData += CalcBufferSize(destType, pDestTexture->dwWidth, dwHeight, pDestTexture->nBlockWidth, pDestTexture->nBlockHeight); diff --git a/cmp_compressonatorlib/compressonator.cpp b/cmp_compressonatorlib/compressonator.cpp index 30a9a2aca..8481823f0 100644 --- a/cmp_compressonatorlib/compressonator.cpp +++ b/cmp_compressonatorlib/compressonator.cpp @@ -44,67 +44,90 @@ using namespace CMP; extern CodecType GetCodecType(CMP_FORMAT format); extern CMP_ERROR GetError(CodecError err); #ifdef ENABLE_MAKE_COMPATIBLE_API -extern bool IsFloatFormat(CMP_FORMAT InFormat); +extern bool IsFloatFormat(CMP_FORMAT InFormat); +extern bool IsCompressedFormat(CMP_FORMAT InFormat); +extern CMP_BYTE FormatChannelBitSize(CMP_FORMAT InFormat); extern CMP_ERROR Byte2HalfShort(CMP_HALFSHORT* hfsBlock, CMP_BYTE* cBlock, CMP_DWORD dwBlockSize); extern CMP_ERROR Float2Byte(CMP_BYTE cBlock[], CMP_FLOAT* fBlock, CMP_Texture* srcTexture, CMP_FORMAT destFormat, const CMP_CompressOptions* pOptions); +extern CMP_ERROR RGBA_Word2Byte(CMP_BYTE cBlock[], CMP_WORD* wBlock, CMP_Texture* srcTexture); +extern CMP_ERROR SByte2Byte(CMP_BYTE cBlock[], CMP_SBYTE* sBlock, CMP_Texture* srcTexture); +extern CMP_ERROR Byte2SByte(CMP_SBYTE sBlock[], CMP_BYTE* cBlock, CMP_Texture* srcTexture); #endif extern CMP_ERROR CheckTexture(const CMP_Texture* pTexture, bool bSource); -extern CMP_ERROR CompressTexture(const CMP_Texture* pSourceTexture, CMP_Texture* pDestTexture, const CMP_CompressOptions* pOptions, CMP_Feedback_Proc pFeedbackProc,CodecType destType); -extern CMP_ERROR ThreadedCompressTexture(const CMP_Texture* pSourceTexture, CMP_Texture* pDestTexture, const CMP_CompressOptions* pOptions, CMP_Feedback_Proc pFeedbackProc, CodecType destType); +extern CMP_ERROR CompressTexture(const CMP_Texture* pSourceTexture, + CMP_Texture* pDestTexture, + const CMP_CompressOptions* pOptions, + CMP_Feedback_Proc pFeedbackProc, + CodecType destType); +extern CMP_ERROR ThreadedCompressTexture(const CMP_Texture* pSourceTexture, + CMP_Texture* pDestTexture, + const CMP_CompressOptions* pOptions, + CMP_Feedback_Proc pFeedbackProc, + CodecType destType); #ifdef _LOCAL_DEBUG -char DbgTracer::buff[MAX_DBGBUFF_SIZE]; -char DbgTracer::PrintBuff[MAX_DBGPPRINTBUFF_SIZE]; +char DbgTracer::buff[MAX_DBGBUFF_SIZE]; +char DbgTracer::PrintBuff[MAX_DBGPPRINTBUFF_SIZE]; #endif -CMP_DWORD CMP_API CMP_CalculateBufferSize(const CMP_Texture* pTexture) { +CMP_DWORD CMP_API CMP_CalculateBufferSize(const CMP_Texture* pTexture) +{ #ifdef USE_DBGTRACE - DbgTrace(("-------> pTexture [%x]",pTexture)); + DbgTrace(("-------> pTexture [%x]", pTexture)); #endif assert(pTexture); - if(pTexture == NULL) + if (pTexture == NULL) return 0; assert(pTexture->dwSize == sizeof(CMP_Texture)); - if(pTexture->dwSize != sizeof(CMP_Texture)) + if (pTexture->dwSize != sizeof(CMP_Texture)) return 0; assert(pTexture->dwWidth > 0); - if(pTexture->dwWidth <= 0 ) + if (pTexture->dwWidth <= 0) return 0; assert(pTexture->dwHeight > 0); - if(pTexture->dwHeight <= 0 ) + if (pTexture->dwHeight <= 0) return 0; - assert(pTexture->format >= CMP_FORMAT_ARGB_8888 && pTexture->format <= CMP_FORMAT_MAX); - if(pTexture->format < CMP_FORMAT_ARGB_8888 || pTexture->format > CMP_FORMAT_MAX) + // Check format range is valid + assert(pTexture->format >= CMP_FORMAT_Unknown && pTexture->format <= CMP_FORMAT_MAX); + if (pTexture->format < CMP_FORMAT_Unknown || pTexture->format > CMP_FORMAT_MAX) return 0; return CalcBufferSize(pTexture->format, pTexture->dwWidth, pTexture->dwHeight, pTexture->dwPitch, pTexture->nBlockWidth, pTexture->nBlockHeight); } -CMP_DWORD CalcBufferSize(CMP_FORMAT format, CMP_DWORD dwWidth, CMP_DWORD dwHeight, CMP_DWORD dwPitch, CMP_BYTE nBlockWidth, CMP_BYTE nBlockHeight) { +CMP_DWORD CalcBufferSize(CMP_FORMAT format, CMP_DWORD dwWidth, CMP_DWORD dwHeight, CMP_DWORD dwPitch, CMP_BYTE nBlockWidth, CMP_BYTE nBlockHeight) +{ #ifdef USE_DBGTRACE - DbgTrace(("format %d dwWidth %d dwHeight %d dwPitch %d",format, dwWidth, dwHeight, dwPitch)); + DbgTrace(("format %d dwWidth %d dwHeight %d dwPitch %d", format, dwWidth, dwHeight, dwPitch)); #endif - switch(format) { + switch (format) + { case CMP_FORMAT_RGBA_8888: case CMP_FORMAT_BGRA_8888: case CMP_FORMAT_ARGB_8888: - case CMP_FORMAT_ARGB_2101010: + case CMP_FORMAT_RGBA_8888_S: return ((dwPitch) ? (dwPitch * dwHeight) : (dwWidth * 4 * dwHeight)); + case CMP_FORMAT_ARGB_2101010: + return ((dwPitch) ? (dwPitch * dwHeight) : (dwWidth * 4 * sizeof(CMP_WORD) * dwHeight)); + case CMP_FORMAT_BGR_888: case CMP_FORMAT_RGB_888: + case CMP_FORMAT_RGB_888_S: return ((dwPitch) ? (dwPitch * dwHeight) : ((((dwWidth * 3) + 3) >> 2) * 4 * dwHeight)); case CMP_FORMAT_RG_8: + case CMP_FORMAT_RG_8_S: return ((dwPitch) ? (dwPitch * dwHeight) : (dwWidth * 2 * dwHeight)); case CMP_FORMAT_R_8: + case CMP_FORMAT_R_8_S: return ((dwPitch) ? (dwPitch * dwHeight) : (dwWidth * dwHeight)); case CMP_FORMAT_ARGB_16: @@ -121,19 +144,19 @@ CMP_DWORD CalcBufferSize(CMP_FORMAT format, CMP_DWORD dwWidth, CMP_DWORD dwHeigh #ifdef ARGB_32_SUPPORT case CMP_FORMAT_ARGB_32: -#endif // ARGB_32_SUPPORT +#endif // ARGB_32_SUPPORT case CMP_FORMAT_ARGB_32F: return ((dwPitch) ? (dwPitch * dwHeight) : (dwWidth * 4 * sizeof(float) * dwHeight)); #ifdef ARGB_32_SUPPORT case CMP_FORMAT_RG_32: -#endif // ARGB_32_SUPPORT +#endif // ARGB_32_SUPPORT case CMP_FORMAT_RG_32F: return ((dwPitch) ? (dwPitch * dwHeight) : (dwWidth * 2 * sizeof(float) * dwHeight)); #ifdef ARGB_32_SUPPORT case CMP_FORMAT_R_32: -#endif // ARGB_32_SUPPORT +#endif // ARGB_32_SUPPORT case CMP_FORMAT_R_32F: return ((dwPitch) ? (dwPitch * dwHeight) : (dwWidth * 1 * sizeof(float) * dwHeight)); @@ -144,37 +167,42 @@ CMP_DWORD CalcBufferSize(CMP_FORMAT format, CMP_DWORD dwWidth, CMP_DWORD dwHeigh // This call is used to swizzle source data content. // Use example: CMP_Map_Bytes(pData, dwWidth, dwHeight, { 2, 1, 0, 3 },4); -void CMP_Map_Bytes(BYTE *src, int width, int height, CMP_MAP_BYTES_SET map, CMP_BYTE offset) { +void CMP_Map_Bytes(BYTE* src, int width, int height, CMP_MAP_BYTES_SET map, CMP_BYTE offset) +{ int i, j; BYTE b[4]; - for (i = 0; idwWidth; - CMP_DWORD dwHeight = pTexture->dwHeight; - CMP_FORMAT newSrcFormat = pTexture->format; - CMP_BYTE *pData; +void CMP_PrepareSourceForCMP_Destination(CMP_Texture* pTexture, CMP_FORMAT DestFormat) +{ + CMP_DWORD dwWidth = pTexture->dwWidth; + CMP_DWORD dwHeight = pTexture->dwHeight; + CMP_FORMAT newSrcFormat = pTexture->format; + CMP_BYTE* pData; pData = pTexture->pData; - switch (newSrcFormat) { - case CMP_FORMAT_BGRA_8888: { - switch (DestFormat) { + switch (newSrcFormat) + { + case CMP_FORMAT_BGRA_8888: + { + switch (DestFormat) + { case CMP_FORMAT_ATI1N: case CMP_FORMAT_ATI2N: case CMP_FORMAT_ATI2N_XY: @@ -216,7 +247,8 @@ void CMP_PrepareSourceForCMP_Destination(CMP_Texture* pTexture, CMP_FORMAT Dest case CMP_FORMAT_DXT5_RBxG: case CMP_FORMAT_DXT5_xRBG: case CMP_FORMAT_DXT5_RGxB: - case CMP_FORMAT_DXT5_xGxR: { + case CMP_FORMAT_DXT5_xGxR: + { // The source format is correct for these codecs break; } @@ -230,9 +262,10 @@ void CMP_PrepareSourceForCMP_Destination(CMP_Texture* pTexture, CMP_FORMAT Dest case CMP_FORMAT_ETC2_RGBA: case CMP_FORMAT_ETC2_RGBA1: case CMP_FORMAT_ETC2_SRGBA: - case CMP_FORMAT_ETC2_SRGBA1: { + case CMP_FORMAT_ETC2_SRGBA1: + { newSrcFormat = CMP_FORMAT_RGBA_8888; - CMP_Map_Bytes(pData, dwWidth, dwHeight, { 2, 1, 0, 3 },4); + CMP_Map_Bytes(pData, dwWidth, dwHeight, {2, 1, 0, 3}, 4); break; } default: @@ -240,8 +273,10 @@ void CMP_PrepareSourceForCMP_Destination(CMP_Texture* pTexture, CMP_FORMAT Dest } break; } - case CMP_FORMAT_RGBA_8888: { - switch (DestFormat) { + case CMP_FORMAT_RGBA_8888: + { + switch (DestFormat) + { case CMP_FORMAT_ATI1N: case CMP_FORMAT_ATI2N: case CMP_FORMAT_ATI2N_XY: @@ -264,9 +299,10 @@ void CMP_PrepareSourceForCMP_Destination(CMP_Texture* pTexture, CMP_FORMAT Dest case CMP_FORMAT_DXT5_RBxG: case CMP_FORMAT_DXT5_xRBG: case CMP_FORMAT_DXT5_RGxB: - case CMP_FORMAT_DXT5_xGxR: { + case CMP_FORMAT_DXT5_xGxR: + { newSrcFormat = CMP_FORMAT_BGRA_8888; - CMP_Map_Bytes(pData, dwWidth, dwHeight, { 2,1,0,3 },4); + CMP_Map_Bytes(pData, dwWidth, dwHeight, {2, 1, 0, 3}, 4); break; } case CMP_FORMAT_ASTC: @@ -279,7 +315,8 @@ void CMP_PrepareSourceForCMP_Destination(CMP_Texture* pTexture, CMP_FORMAT Dest case CMP_FORMAT_ETC2_RGBA1: case CMP_FORMAT_ETC2_SRGBA: case CMP_FORMAT_ETC2_SRGBA1: - case CMP_FORMAT_GT: { + case CMP_FORMAT_GT: + { // format is correct } default: @@ -287,8 +324,10 @@ void CMP_PrepareSourceForCMP_Destination(CMP_Texture* pTexture, CMP_FORMAT Dest } break; } - case CMP_FORMAT_ARGB_8888: { - switch (DestFormat) { + case CMP_FORMAT_ARGB_8888: + { + switch (DestFormat) + { case CMP_FORMAT_ATI1N: case CMP_FORMAT_ATI2N: case CMP_FORMAT_ATI2N_XY: @@ -309,9 +348,10 @@ void CMP_PrepareSourceForCMP_Destination(CMP_Texture* pTexture, CMP_FORMAT Dest case CMP_FORMAT_DXT5_RBxG: case CMP_FORMAT_DXT5_xRBG: case CMP_FORMAT_DXT5_RGxB: - case CMP_FORMAT_DXT5_xGxR: { + case CMP_FORMAT_DXT5_xGxR: + { newSrcFormat = CMP_FORMAT_BGRA_8888; - CMP_Map_Bytes(pData, dwWidth, dwHeight, { 3,2,1,0 }, 4); + CMP_Map_Bytes(pData, dwWidth, dwHeight, {3, 2, 1, 0}, 4); break; } case CMP_FORMAT_ASTC: @@ -324,9 +364,10 @@ void CMP_PrepareSourceForCMP_Destination(CMP_Texture* pTexture, CMP_FORMAT Dest case CMP_FORMAT_ETC2_RGBA1: case CMP_FORMAT_ETC2_SRGBA: case CMP_FORMAT_ETC2_SRGBA1: - case CMP_FORMAT_GT: { + case CMP_FORMAT_GT: + { newSrcFormat = CMP_FORMAT_RGBA_8888; - CMP_Map_Bytes(pData, dwWidth, dwHeight, { 1,2,3,0 }, 4); + CMP_Map_Bytes(pData, dwWidth, dwHeight, {1, 2, 3, 0}, 4); break; } default: @@ -340,26 +381,24 @@ void CMP_PrepareSourceForCMP_Destination(CMP_Texture* pTexture, CMP_FORMAT Dest // Update Source format to new one pTexture->format = newSrcFormat; - } - - // For now this function will only handle a single case // where the source data remains the same size and only RGBA channels // are swizzled according output compressed formats, // if source is compressed then no change is performed -void CMP_PrepareCMPSourceForIMG_Destination(CMP_Texture* pDstTexture, CMP_FORMAT SrcFormat) { - - CMP_DWORD dwWidth = pDstTexture->dwWidth; - CMP_DWORD dwHeight = pDstTexture->dwHeight; - CMP_FORMAT newDstFormat = pDstTexture->format; - CMP_BYTE *pData; +void CMP_PrepareCMPSourceForIMG_Destination(CMP_Texture* pDstTexture, CMP_FORMAT SrcFormat) +{ + CMP_DWORD dwWidth = pDstTexture->dwWidth; + CMP_DWORD dwHeight = pDstTexture->dwHeight; + CMP_FORMAT newDstFormat = pDstTexture->format; + CMP_BYTE* pData; pData = pDstTexture->pData; - switch (SrcFormat) { + switch (SrcFormat) + { // decompressed Data is in the form BGRA case CMP_FORMAT_ATI1N: case CMP_FORMAT_ATI2N: @@ -383,12 +422,15 @@ void CMP_PrepareCMPSourceForIMG_Destination(CMP_Texture* pDstTexture, CMP_FORMAT case CMP_FORMAT_DXT5_RBxG: case CMP_FORMAT_DXT5_xRBG: case CMP_FORMAT_DXT5_RGxB: - case CMP_FORMAT_DXT5_xGxR: { - switch (newDstFormat) { + case CMP_FORMAT_DXT5_xGxR: + { + switch (newDstFormat) + { case CMP_FORMAT_BGRA_8888: break; - case CMP_FORMAT_RGBA_8888: { - CMP_Map_Bytes(pData, dwWidth, dwHeight, { 2, 1, 0, 3 },4); + case CMP_FORMAT_RGBA_8888: + { + CMP_Map_Bytes(pData, dwWidth, dwHeight, {2, 1, 0, 3}, 4); break; } default: @@ -406,12 +448,15 @@ void CMP_PrepareCMPSourceForIMG_Destination(CMP_Texture* pDstTexture, CMP_FORMAT case CMP_FORMAT_ETC2_RGBA: case CMP_FORMAT_ETC2_RGBA1: case CMP_FORMAT_ETC2_SRGBA: - case CMP_FORMAT_ETC2_SRGBA1: { - switch (newDstFormat) { + case CMP_FORMAT_ETC2_SRGBA1: + { + switch (newDstFormat) + { case CMP_FORMAT_RGBA_8888: break; - case CMP_FORMAT_BGRA_8888: { - CMP_Map_Bytes(pData, dwWidth, dwHeight, { 2, 1, 0, 3 },4); + case CMP_FORMAT_BGRA_8888: + { + CMP_Map_Bytes(pData, dwWidth, dwHeight, {2, 1, 0, 3}, 4); break; } default: @@ -422,88 +467,149 @@ void CMP_PrepareCMPSourceForIMG_Destination(CMP_Texture* pDstTexture, CMP_FORMAT } #endif -CMP_ERROR CMP_API CMP_ConvertTexture(CMP_Texture* pSourceTexture, CMP_Texture* pDestTexture, const CMP_CompressOptions* pOptions, CMP_Feedback_Proc pFeedbackProc) { +CMP_ERROR CMP_API CMP_ConvertTexture(CMP_Texture* pSourceTexture, + CMP_Texture* pDestTexture, + const CMP_CompressOptions* pOptions, + CMP_Feedback_Proc pFeedbackProc) +{ #ifdef USE_DBGTRACE - DbgTrace(("-------> pSourceTexture [%x] pDestTexture [%x] pOptions [%x]",pSourceTexture, pDestTexture, pOptions)); + DbgTrace(("-------> pSourceTexture [%x] pDestTexture [%x] pOptions [%x]", pSourceTexture, pDestTexture, pOptions)); #endif CMP_ERROR tc_err = CheckTexture(pSourceTexture, true); - if(tc_err != CMP_OK) + if (tc_err != CMP_OK) return tc_err; #ifdef ENABLE_MAKE_COMPATIBLE_API - bool srcFloat = IsFloatFormat(pSourceTexture->format); + bool srcFloat = IsFloatFormat(pSourceTexture->format); bool destFloat = IsFloatFormat(pDestTexture->format); bool newBuffer = false; - if (srcFloat && !destFloat) { - CMP_DWORD size = pSourceTexture->dwWidth * pSourceTexture->dwHeight; - CMP_FLOAT*pfData = new CMP_FLOAT[pSourceTexture->dwDataSize] ; + if (srcFloat && !destFloat) + { + CMP_DWORD size = pSourceTexture->dwWidth * pSourceTexture->dwHeight; + CMP_FLOAT* pfData = new CMP_FLOAT[pSourceTexture->dwDataSize]; memcpy(pfData, pSourceTexture->pData, pSourceTexture->dwDataSize); - CMP_BYTE *byteData = new CMP_BYTE[size * 4]; + CMP_BYTE* byteData = new CMP_BYTE[size * 4]; Float2Byte(byteData, pfData, pSourceTexture, pDestTexture->format, pOptions); delete[] pfData; pSourceTexture->pData = byteData; - pSourceTexture->format = CMP_FORMAT_ARGB_8888; + pSourceTexture->format = CMP_FORMAT_ARGB_8888; pSourceTexture->dwDataSize = size * 4; - newBuffer = true; + newBuffer = true; } - else if (!srcFloat && destFloat) { - CMP_DWORD size = pSourceTexture->dwWidth * pSourceTexture->dwHeight; - CMP_BYTE *pbData = pSourceTexture->pData; - CMP_HALFSHORT *hfloatSData = new CMP_HALFSHORT[size * 4]; // wxh*4 channels of type half + else if (!srcFloat && destFloat) + { + CMP_DWORD size = pSourceTexture->dwWidth * pSourceTexture->dwHeight; + CMP_BYTE* pbData = pSourceTexture->pData; + CMP_HALFSHORT* hfloatSData = new CMP_HALFSHORT[size * 4]; // wxh*4 channels of type half Byte2HalfShort(hfloatSData, pbData, size * 4); - pSourceTexture->pData = (CMP_BYTE*)hfloatSData; - pSourceTexture->format = CMP_FORMAT_ARGB_16F; + pSourceTexture->pData = (CMP_BYTE*)hfloatSData; + pSourceTexture->format = CMP_FORMAT_ARGB_16F; pSourceTexture->dwDataSize = size * 4 * sizeof(CMP_HALFSHORT); - pSourceTexture->dwPitch = pSourceTexture->dwWidth * 4 * sizeof(CMP_HALFSHORT); - newBuffer = true; + pSourceTexture->dwPitch = pSourceTexture->dwWidth * 4 * sizeof(CMP_HALFSHORT); + newBuffer = true; + } + else if (!srcFloat && !destFloat) + { + // Both channels of not float, check for matching source & target formats. + CMP_BYTE srcbitsize = FormatChannelBitSize(pSourceTexture->format); + CMP_BYTE dstbitsize = FormatChannelBitSize(pDestTexture->format); + // we are compressing not transcoding need to make src compatable + if ((srcbitsize != dstbitsize) && (IsCompressedFormat(pDestTexture->format))) + { + CMP_DWORD size = pSourceTexture->dwWidth * pSourceTexture->dwHeight; + CMP_WORD* pwData = new CMP_WORD[pSourceTexture->dwDataSize]; + + memcpy(pwData, pSourceTexture->pData, pSourceTexture->dwDataSize); + + CMP_BYTE* byteData = new CMP_BYTE[size * 4]; + + RGBA_Word2Byte(byteData, pwData, pSourceTexture); + + delete[] pwData; + pSourceTexture->pData = byteData; + + pSourceTexture->format = CMP_FORMAT_ARGB_8888; + pSourceTexture->dwDataSize = size * 4; + newBuffer = true; + } + else + // we are compressing make sure bit signs are the same + if ((srcbitsize == 8) && (dstbitsize == 8) && (IsCompressedFormat(pDestTexture->format)) && (pSourceTexture->format == CMP_FORMAT_RGBA_8888_S)) + { + if ((pDestTexture->format != CMP_FORMAT_BC4_S) && ((pDestTexture->format != CMP_FORMAT_BC5_S))) + { + CMP_DWORD size = pSourceTexture->dwWidth * pSourceTexture->dwHeight; + CMP_BYTE* byteData = new CMP_BYTE[pSourceTexture->dwDataSize]; + + SByte2Byte(byteData, (CMP_SBYTE*)pSourceTexture->pData, pSourceTexture); + + pSourceTexture->pData = byteData; + pSourceTexture->format = CMP_FORMAT_ARGB_8888; + pSourceTexture->dwDataSize = pSourceTexture->dwDataSize; + newBuffer = true; + } + } } #endif tc_err = CheckTexture(pDestTexture, false); - if(tc_err != CMP_OK) + if (tc_err != CMP_OK) return tc_err; - if(pSourceTexture->dwWidth != pDestTexture->dwWidth || pSourceTexture->dwHeight != pDestTexture->dwHeight) + if (pSourceTexture->dwWidth != pDestTexture->dwWidth || pSourceTexture->dwHeight != pDestTexture->dwHeight) return CMP_ERR_SIZE_MISMATCH; CodecType srcType = GetCodecType(pSourceTexture->format); assert(srcType != CT_Unknown); - if(srcType == CT_Unknown) + if (srcType == CT_Unknown) return CMP_ERR_UNSUPPORTED_SOURCE_FORMAT; CodecType destType = GetCodecType(pDestTexture->format); assert(destType != CT_Unknown); - if(destType == CT_Unknown) + if (destType == CT_Unknown) return CMP_ERR_UNSUPPORTED_SOURCE_FORMAT; - if(srcType == destType) { + if (srcType == destType) + { // Easy case ? - if(pSourceTexture->format == pDestTexture->format && pSourceTexture->dwPitch == pDestTexture->dwPitch) + if (pSourceTexture->format == pDestTexture->format && pSourceTexture->dwPitch == pDestTexture->dwPitch) memcpy(pDestTexture->pData, pSourceTexture->pData, CMP_CalculateBufferSize(pSourceTexture)); - else { - CodecBufferType srcBufferType = GetCodecBufferType(pSourceTexture->format); + else + { + CodecBufferType srcBufferType = GetCodecBufferType(pSourceTexture->format); CodecBufferType destBufferType = GetCodecBufferType(pDestTexture->format); CCodecBuffer* pSrcBuffer = CreateCodecBuffer(srcBufferType, - pSourceTexture->nBlockWidth, pSourceTexture->nBlockHeight, pSourceTexture->nBlockDepth, - pSourceTexture->dwWidth, pSourceTexture->dwHeight, pSourceTexture->dwPitch, pSourceTexture->pData, - pSourceTexture->dwDataSize); + pSourceTexture->nBlockWidth, + pSourceTexture->nBlockHeight, + pSourceTexture->nBlockDepth, + pSourceTexture->dwWidth, + pSourceTexture->dwHeight, + pSourceTexture->dwPitch, + pSourceTexture->pData, + pSourceTexture->dwDataSize); assert(pSrcBuffer); - if(!pSrcBuffer) + if (!pSrcBuffer) return CMP_ERR_GENERIC; CCodecBuffer* pDestBuffer = CreateCodecBuffer(destBufferType, - pDestTexture->nBlockWidth, pDestTexture->nBlockHeight, pDestTexture->nBlockDepth, - pDestTexture->dwWidth, pDestTexture->dwHeight, pDestTexture->dwPitch, pDestTexture->pData, - pDestTexture->dwDataSize); + pDestTexture->nBlockWidth, + pDestTexture->nBlockHeight, + pDestTexture->nBlockDepth, + pDestTexture->dwWidth, + pDestTexture->dwHeight, + pDestTexture->dwPitch, + pDestTexture->pData, + pDestTexture->dwDataSize); assert(pDestBuffer); - if(!pDestBuffer) { + if (!pDestBuffer) + { delete pSrcBuffer; return CMP_ERR_GENERIC; } @@ -517,8 +623,9 @@ CMP_ERROR CMP_API CMP_ConvertTexture(CMP_Texture* pSourceTexture, CMP_Texture* p } return CMP_OK; - } else if(srcType == CT_None && destType != CT_None) { - + } + else if (srcType == CT_None && destType != CT_None) + { #ifndef USE_OLD_SWIZZLE CMP_PrepareSourceForCMP_Destination(pSourceTexture, pDestTexture->format); #endif @@ -532,13 +639,8 @@ CMP_ERROR CMP_API CMP_ConvertTexture(CMP_Texture* pSourceTexture, CMP_Texture* p // BC7/BC6H has issues with this setting - we already set multithreading via numThreads so // this call is disabled for BC7/BC6H ASTC Codecs. // if the use has set DiableMultiThreading then numThreads will be set to 1 (regradless of its original value) - if( - ((!pOptions || !pOptions->bDisableMultiThreading) && f_dwProcessorCount > 1) - && (bMultithread) - && (destType != CT_ASTC) - && (destType != CT_BC7) - && (destType != CT_BC6H) - && (destType != CT_BC6H_SF) + if (((!pOptions || !pOptions->bDisableMultiThreading) && f_dwProcessorCount > 1) && (bMultithread) && (destType != CT_ASTC) && (destType != CT_BC7) && + (destType != CT_BC6H) && (destType != CT_BC6H_SF) #ifdef USE_APC && (destType != CT_APC) #endif @@ -548,36 +650,43 @@ CMP_ERROR CMP_API CMP_ConvertTexture(CMP_Texture* pSourceTexture, CMP_Texture* p #ifdef USE_BASIS && (destType != CT_BASIS) #endif - ) { - tc_err = ThreadedCompressTexture(pSourceTexture, pDestTexture, pOptions, pFeedbackProc,destType); + ) + { + tc_err = ThreadedCompressTexture(pSourceTexture, pDestTexture, pOptions, pFeedbackProc, destType); #ifdef ENABLE_MAKE_COMPATIBLE_API - if (pSourceTexture->pData && newBuffer) { + if (pSourceTexture->pData && newBuffer) + { free(pSourceTexture->pData); pSourceTexture->pData = NULL; } #endif return tc_err; - } else -#endif // THREADED_COMPRESS + } + else +#endif // THREADED_COMPRESS { - tc_err = CompressTexture(pSourceTexture, pDestTexture, pOptions, pFeedbackProc, destType); + tc_err = CompressTexture(pSourceTexture, pDestTexture, pOptions, pFeedbackProc, destType); #ifdef ENABLE_MAKE_COMPATIBLE_API - if (pSourceTexture->pData && newBuffer) { + if (pSourceTexture->pData && newBuffer) + { free(pSourceTexture->pData); pSourceTexture->pData = NULL; } #endif return tc_err; } - } else if(srcType != CT_None && destType == CT_None) { + } + else if (srcType != CT_None && destType == CT_None) + { // Decompressing - CCodec* pCodec = CreateCodec(srcType); assert(pCodec); - if (pCodec == NULL) { + if (pCodec == NULL) + { #ifdef ENABLE_MAKE_COMPATIBLE_API - if (pSourceTexture->pData && newBuffer) { + if (pSourceTexture->pData && newBuffer) + { free(pSourceTexture->pData); pSourceTexture->pData = NULL; } @@ -587,31 +696,40 @@ CMP_ERROR CMP_API CMP_ConvertTexture(CMP_Texture* pSourceTexture, CMP_Texture* p CodecBufferType destBufferType = GetCodecBufferType(pDestTexture->format); - CCodecBuffer* pSrcBuffer = pCodec->CreateBuffer(pSourceTexture->nBlockWidth, pSourceTexture->nBlockHeight, pSourceTexture->nBlockDepth, - pSourceTexture->dwWidth, pSourceTexture->dwHeight, pSourceTexture->dwPitch, - pSourceTexture->pData, - pSourceTexture->dwDataSize); - - + CCodecBuffer* pSrcBuffer = pCodec->CreateBuffer(pSourceTexture->nBlockWidth, + pSourceTexture->nBlockHeight, + pSourceTexture->nBlockDepth, + pSourceTexture->dwWidth, + pSourceTexture->dwHeight, + pSourceTexture->dwPitch, + pSourceTexture->pData, + pSourceTexture->dwDataSize); pDestTexture->nBlockWidth = pSourceTexture->nBlockWidth; pDestTexture->nBlockHeight = pSourceTexture->nBlockHeight; pDestTexture->nBlockDepth = pSourceTexture->nBlockDepth; CCodecBuffer* pDestBuffer = CreateCodecBuffer(destBufferType, - pDestTexture->nBlockWidth, pDestTexture->nBlockHeight, pDestTexture->nBlockDepth, - pDestTexture->dwWidth, pDestTexture->dwHeight, pDestTexture->dwPitch, pDestTexture->pData, - pDestTexture->dwDataSize); + pDestTexture->nBlockWidth, + pDestTexture->nBlockHeight, + pDestTexture->nBlockDepth, + pDestTexture->dwWidth, + pDestTexture->dwHeight, + pDestTexture->dwPitch, + pDestTexture->pData, + pDestTexture->dwDataSize); // assert(pDestBuffer); // assert(pSrcBuffer); - if(pSrcBuffer == NULL || pDestBuffer == NULL) { + if (pSrcBuffer == NULL || pDestBuffer == NULL) + { SAFE_DELETE(pCodec); SAFE_DELETE(pSrcBuffer); SAFE_DELETE(pDestBuffer); #ifdef ENABLE_MAKE_COMPATIBLE_API - if (pSourceTexture->pData && newBuffer) { + if (pSourceTexture->pData && newBuffer) + { free(pSourceTexture->pData); pSourceTexture->pData = NULL; } @@ -622,15 +740,20 @@ CMP_ERROR CMP_API CMP_ConvertTexture(CMP_Texture* pSourceTexture, CMP_Texture* p DISABLE_FP_EXCEPTIONS; pSrcBuffer->SetBlockHeight(pSourceTexture->nBlockHeight); - pSrcBuffer->SetBlockWidth (pSourceTexture->nBlockWidth ); - pSrcBuffer->SetBlockDepth (pSourceTexture->nBlockDepth ); + pSrcBuffer->SetBlockWidth(pSourceTexture->nBlockWidth); + pSrcBuffer->SetBlockDepth(pSourceTexture->nBlockDepth); pSrcBuffer->SetFormat(pSourceTexture->format); pSrcBuffer->SetTranscodeFormat(pSourceTexture->transcodeFormat); + pDestBuffer->SetBlockHeight(pDestTexture->nBlockHeight); + pDestBuffer->SetBlockWidth(pDestTexture->nBlockWidth); + pDestBuffer->SetBlockDepth(pDestTexture->nBlockDepth); + pDestBuffer->SetFormat(pDestTexture->format); + CodecError err1 = pCodec->Decompress(*pSrcBuffer, *pDestBuffer, pFeedbackProc); RESTORE_FP_EXCEPTIONS; -#ifndef USE_OLD_SWIZZLE +#ifndef USE_OLD_SWIZZLE CMP_PrepareCMPSourceForIMG_Destination(pDestTexture, pSourceTexture->format); #endif @@ -639,24 +762,29 @@ CMP_ERROR CMP_API CMP_ConvertTexture(CMP_Texture* pSourceTexture, CMP_Texture* p SAFE_DELETE(pDestBuffer); #ifdef ENABLE_MAKE_COMPATIBLE_API - if (pSourceTexture->pData && newBuffer) { + if (pSourceTexture->pData && newBuffer) + { free(pSourceTexture->pData); pSourceTexture->pData = NULL; } #endif return GetError(err1); - } else { // Decompressing & then compressing + } + else + { // Decompressing & then compressing // Decompressing - CCodec* pCodecIn = CreateCodec(srcType); - CCodec* pCodecOut = CreateCodec(destType); + CCodec* pCodecIn = CreateCodec(srcType); + CCodec* pCodecOut = CreateCodec(destType); assert(pCodecIn); assert(pCodecOut); - if(pCodecIn == NULL || pCodecOut == NULL ) { + if (pCodecIn == NULL || pCodecOut == NULL) + { SAFE_DELETE(pCodecIn); SAFE_DELETE(pCodecOut); #ifdef ENABLE_MAKE_COMPATIBLE_API - if (pSourceTexture->pData && newBuffer) { + if (pSourceTexture->pData && newBuffer) + { free(pSourceTexture->pData); pSourceTexture->pData = NULL; } @@ -664,29 +792,38 @@ CMP_ERROR CMP_API CMP_ConvertTexture(CMP_Texture* pSourceTexture, CMP_Texture* p return CMP_ERR_UNABLE_TO_INIT_CODEC; } - CCodecBuffer* pSrcBuffer = pCodecIn->CreateBuffer( - pSourceTexture->nBlockWidth, pSourceTexture->nBlockHeight, pSourceTexture->nBlockDepth, - pSourceTexture->dwWidth, pSourceTexture->dwHeight, pSourceTexture->dwPitch, pSourceTexture->pData, - pSourceTexture->dwDataSize); - CCodecBuffer* pTempBuffer = CreateCodecBuffer(CBT_RGBA32F, - pDestTexture->nBlockWidth, pDestTexture->nBlockHeight, pDestTexture->nBlockDepth, - pDestTexture->dwWidth, pDestTexture->dwHeight); - CCodecBuffer* pDestBuffer = pCodecOut->CreateBuffer( - pDestTexture->nBlockWidth, pDestTexture->nBlockHeight, pDestTexture->nBlockDepth, - pDestTexture->dwWidth, pDestTexture->dwHeight, pDestTexture->dwPitch, pDestTexture->pData, - pDestTexture->dwDataSize); + CCodecBuffer* pSrcBuffer = pCodecIn->CreateBuffer(pSourceTexture->nBlockWidth, + pSourceTexture->nBlockHeight, + pSourceTexture->nBlockDepth, + pSourceTexture->dwWidth, + pSourceTexture->dwHeight, + pSourceTexture->dwPitch, + pSourceTexture->pData, + pSourceTexture->dwDataSize); + CCodecBuffer* pTempBuffer = CreateCodecBuffer( + CBT_RGBA32F, pDestTexture->nBlockWidth, pDestTexture->nBlockHeight, pDestTexture->nBlockDepth, pDestTexture->dwWidth, pDestTexture->dwHeight); + CCodecBuffer* pDestBuffer = pCodecOut->CreateBuffer(pDestTexture->nBlockWidth, + pDestTexture->nBlockHeight, + pDestTexture->nBlockDepth, + pDestTexture->dwWidth, + pDestTexture->dwHeight, + pDestTexture->dwPitch, + pDestTexture->pData, + pDestTexture->dwDataSize); assert(pSrcBuffer); assert(pTempBuffer); assert(pDestBuffer); - if(pSrcBuffer == NULL || pTempBuffer == NULL || pDestBuffer == NULL) { + if (pSrcBuffer == NULL || pTempBuffer == NULL || pDestBuffer == NULL) + { SAFE_DELETE(pCodecIn); SAFE_DELETE(pCodecOut); SAFE_DELETE(pSrcBuffer); SAFE_DELETE(pTempBuffer); SAFE_DELETE(pDestBuffer); #ifdef ENABLE_MAKE_COMPATIBLE_API - if (pSourceTexture->pData && newBuffer) { + if (pSourceTexture->pData && newBuffer) + { free(pSourceTexture->pData); pSourceTexture->pData = NULL; } @@ -696,13 +833,15 @@ CMP_ERROR CMP_API CMP_ConvertTexture(CMP_Texture* pSourceTexture, CMP_Texture* p DISABLE_FP_EXCEPTIONS; CodecError err2 = pCodecIn->Decompress(*pSrcBuffer, *pTempBuffer, pFeedbackProc); - if(err2 == CE_OK) { + if (err2 == CE_OK) + { err2 = pCodecOut->Compress(*pTempBuffer, *pDestBuffer, pFeedbackProc); } RESTORE_FP_EXCEPTIONS; #ifdef ENABLE_MAKE_COMPATIBLE_API - if (pSourceTexture->pData && newBuffer) { + if (pSourceTexture->pData && newBuffer) + { free(pSourceTexture->pData); pSourceTexture->pData = NULL; } @@ -711,7 +850,8 @@ CMP_ERROR CMP_API CMP_ConvertTexture(CMP_Texture* pSourceTexture, CMP_Texture* p } } -CMP_ERROR CMP_API CMP_ConvertMipTexture(CMP_MipSet* p_MipSetIn, CMP_MipSet* p_MipSetOut, const CMP_CompressOptions* pOptions, CMP_Feedback_Proc pFeedbackProc) { +CMP_ERROR CMP_API CMP_ConvertMipTexture(CMP_MipSet* p_MipSetIn, CMP_MipSet* p_MipSetOut, const CMP_CompressOptions* pOptions, CMP_Feedback_Proc pFeedbackProc) +{ assert(p_MipSetIn); assert(p_MipSetOut); assert(pOptions); @@ -727,27 +867,27 @@ CMP_ERROR CMP_API CMP_ConvertMipTexture(CMP_MipSet* p_MipSetIn, CMP_MipSet* p_Mi // Output // ------------- memset(p_MipSetOut, 0, sizeof(CMP_MipSet)); - p_MipSetOut->m_Flags = MS_FLAG_Default; - p_MipSetOut->m_format = pOptions->DestFormat; + p_MipSetOut->m_Flags = MS_FLAG_Default; + p_MipSetOut->m_format = pOptions->DestFormat; p_MipSetOut->m_nHeight = p_MipSetIn->m_nHeight; - p_MipSetOut->m_nWidth = p_MipSetIn->m_nWidth; + p_MipSetOut->m_nWidth = p_MipSetIn->m_nWidth; CMP_Format2FourCC(pOptions->DestFormat, p_MipSetOut); // Default compression block size if not set! - p_MipSetIn->m_nBlockWidth = (p_MipSetIn->m_nBlockWidth == 0) ? 4 : p_MipSetIn->m_nBlockWidth; + p_MipSetIn->m_nBlockWidth = (p_MipSetIn->m_nBlockWidth == 0) ? 4 : p_MipSetIn->m_nBlockWidth; p_MipSetIn->m_nBlockHeight = (p_MipSetIn->m_nBlockHeight == 0) ? 4 : p_MipSetIn->m_nBlockHeight; - p_MipSetIn->m_nDepth = (p_MipSetIn->m_nDepth < 1) ? 1 : p_MipSetIn->m_nDepth; + p_MipSetIn->m_nDepth = (p_MipSetIn->m_nDepth < 1) ? 1 : p_MipSetIn->m_nDepth; // Allocate compression data - p_MipSetOut->m_nMipLevels = 1; // this is overwriiten depending on input. + p_MipSetOut->m_nMipLevels = 1; // this is overwriiten depending on input. p_MipSetOut->m_ChannelFormat = CF_Compressed; p_MipSetOut->m_nMaxMipLevels = p_MipSetIn->m_nMaxMipLevels; - p_MipSetOut->m_nBlockWidth = p_MipSetIn->m_nBlockWidth; - p_MipSetOut->m_nBlockHeight = p_MipSetIn->m_nBlockHeight; - p_MipSetOut->m_nDepth = p_MipSetIn->m_nDepth; - p_MipSetOut->m_TextureType = p_MipSetIn->m_TextureType; + p_MipSetOut->m_nBlockWidth = p_MipSetIn->m_nBlockWidth; + p_MipSetOut->m_nBlockHeight = p_MipSetIn->m_nBlockHeight; + p_MipSetOut->m_nDepth = p_MipSetIn->m_nDepth; + p_MipSetOut->m_TextureType = p_MipSetIn->m_TextureType; - p_MipSetOut->m_nIterations = 0; // tracks number of processed data miplevels + p_MipSetOut->m_nIterations = 0; // tracks number of processed data miplevels //===================================================== // Case Uncompressed Source to Compressed Destination @@ -756,37 +896,39 @@ CMP_ERROR CMP_API CMP_ConvertMipTexture(CMP_MipSet* p_MipSetIn, CMP_MipSet* p_Mi srcTexture.dwSize = sizeof(srcTexture); #ifdef USE_BASIS - if (pOptions->DestFormat == CMP_FORMAT_BASIS) { - p_MipSetOut->m_format = CMP_FORMAT_BASIS; + if (pOptions->DestFormat == CMP_FORMAT_BASIS) + { + p_MipSetOut->m_format = CMP_FORMAT_BASIS; p_MipSetOut->m_transcodeFormat = CMP_FORMAT_BC1; - p_MipSetOut->m_TextureType = TT_2D; + p_MipSetOut->m_TextureType = TT_2D; p_MipSetOut->m_TextureDataType = TDT_ARGB; - p_MipSetOut->m_ChannelFormat = CF_Compressed; - p_MipSetOut->dwDataSize = 0; // will be set after compression - p_MipSetOut->dwWidth = 1; // Container for 1 linear data buffer - p_MipSetOut->dwHeight = 1; // Container for 1 linear data buffer - p_MipSetOut->m_nDepth = 1; + p_MipSetOut->m_ChannelFormat = CF_Compressed; + p_MipSetOut->dwDataSize = 0; // will be set after compression + p_MipSetOut->dwWidth = 1; // Container for 1 linear data buffer + p_MipSetOut->dwHeight = 1; // Container for 1 linear data buffer + p_MipSetOut->m_nDepth = 1; - if (!CMips.AllocateMipSet(p_MipSetOut, p_MipSetOut->m_ChannelFormat, TDT_ARGB, p_MipSetOut->m_TextureType, 1, 1, 1)) { + if (!CMips.AllocateMipSet(p_MipSetOut, p_MipSetOut->m_ChannelFormat, TDT_ARGB, p_MipSetOut->m_TextureType, 1, 1, 1)) + { return CMP_ERR_MEM_ALLOC_FOR_MIPSET; } //===================== // Uncompressed source //====================== - CMP_MipLevel* pInMipLevel = CMips.GetMipLevel(p_MipSetIn, 0, 0); - srcTexture.dwPitch = 0; - srcTexture.nBlockWidth = p_MipSetIn->m_nBlockWidth; - srcTexture.nBlockHeight = p_MipSetIn->m_nBlockHeight; - srcTexture.nBlockDepth = p_MipSetIn->m_nBlockDepth; - srcTexture.format = p_MipSetIn->m_format; - srcTexture.dwWidth = p_MipSetIn->m_nWidth; - srcTexture.dwHeight = p_MipSetIn->m_nHeight; + CMP_MipLevel* pInMipLevel = CMips.GetMipLevel(p_MipSetIn, 0, 0); + srcTexture.dwPitch = 0; + srcTexture.nBlockWidth = p_MipSetIn->m_nBlockWidth; + srcTexture.nBlockHeight = p_MipSetIn->m_nBlockHeight; + srcTexture.nBlockDepth = p_MipSetIn->m_nBlockDepth; + srcTexture.format = p_MipSetIn->m_format; + srcTexture.dwWidth = p_MipSetIn->m_nWidth; + srcTexture.dwHeight = p_MipSetIn->m_nHeight; srcTexture.transcodeFormat = p_MipSetIn->m_transcodeFormat; - srcTexture.pData = pInMipLevel->m_pbData; - srcTexture.dwWidth = pInMipLevel->m_nWidth; - srcTexture.dwHeight = pInMipLevel->m_nHeight; - srcTexture.pData = pInMipLevel->m_pbData; + srcTexture.pData = pInMipLevel->m_pbData; + srcTexture.dwWidth = pInMipLevel->m_nWidth; + srcTexture.dwHeight = pInMipLevel->m_nHeight; + srcTexture.pData = pInMipLevel->m_pbData; srcTexture.dwDataSize = CMP_CalculateBufferSize(&srcTexture); @@ -794,95 +936,106 @@ CMP_ERROR CMP_API CMP_ConvertMipTexture(CMP_MipSet* p_MipSetIn, CMP_MipSet* p_Mi // Compressed Destination //======================== CMP_Texture destTexture; - MipLevel* pOutMipLevel = CMips.GetMipLevel(p_MipSetOut, 0, 0); + MipLevel* pOutMipLevel = CMips.GetMipLevel(p_MipSetOut, 0, 0); pOutMipLevel->m_pvec8Data = new CMP_VEC8(); // size = 0 before compression, will be set during processing - p_MipSetOut->pData = pOutMipLevel->m_pbData; - - destTexture.dwSize = sizeof(destTexture); - destTexture.dwWidth = p_MipSetIn->m_nWidth; - destTexture.dwHeight = p_MipSetIn->m_nHeight; - destTexture.dwPitch = 0; - destTexture.nBlockWidth = p_MipSetIn->m_nBlockWidth; - destTexture.nBlockHeight = p_MipSetIn->m_nBlockHeight; - destTexture.format = pOptions->DestFormat; + p_MipSetOut->pData = pOutMipLevel->m_pbData; + + destTexture.dwSize = sizeof(destTexture); + destTexture.dwWidth = p_MipSetIn->m_nWidth; + destTexture.dwHeight = p_MipSetIn->m_nHeight; + destTexture.dwPitch = 0; + destTexture.nBlockWidth = p_MipSetIn->m_nBlockWidth; + destTexture.nBlockHeight = p_MipSetIn->m_nBlockHeight; + destTexture.format = pOptions->DestFormat; srcTexture.transcodeFormat = p_MipSetIn->m_transcodeFormat; - destTexture.dwDataSize = CMP_CalculateBufferSize(&srcTexture); // This will be reset after compression - destTexture.pData = pOutMipLevel->m_pbData; + destTexture.dwDataSize = CMP_CalculateBufferSize(&srcTexture); // This will be reset after compression + destTexture.pData = pOutMipLevel->m_pbData; //======================== // Process ConvertTexture //======================== CMP_ERROR cmp_status = CMP_ConvertTexture(&srcTexture, &destTexture, pOptions, pFeedbackProc); - if (cmp_status != CMP_OK) { + if (cmp_status != CMP_OK) + { return cmp_status; } - - - } else + } + else #endif { - if (!CMips.AllocateMipSet(p_MipSetOut, p_MipSetOut->m_ChannelFormat, TDT_ARGB, p_MipSetOut->m_TextureType, p_MipSetIn->m_nWidth, p_MipSetIn->m_nHeight, p_MipSetOut->m_nDepth)) { + if (!CMips.AllocateMipSet(p_MipSetOut, + p_MipSetOut->m_ChannelFormat, + TDT_ARGB, + p_MipSetOut->m_TextureType, + p_MipSetIn->m_nWidth, + p_MipSetIn->m_nHeight, + p_MipSetOut->m_nDepth)) + { return CMP_ERR_MEM_ALLOC_FOR_MIPSET; } p_MipSetOut->m_nMipLevels = p_MipSetIn->m_nMipLevels; - for (int nMipLevel = 0; nMipLevel < p_MipSetIn->m_nMipLevels; nMipLevel++) { - for (int nFaceOrSlice = 0; nFaceOrSlice < CMP_MaxFacesOrSlices(p_MipSetIn, nMipLevel); nFaceOrSlice++) { + for (int nMipLevel = 0; nMipLevel < p_MipSetIn->m_nMipLevels; nMipLevel++) + { + for (int nFaceOrSlice = 0; nFaceOrSlice < CMP_MaxFacesOrSlices(p_MipSetIn, nMipLevel); nFaceOrSlice++) + { //===================== // Uncompressed source //====================== CMP_MipLevel* pInMipLevel = CMips.GetMipLevel(p_MipSetIn, nMipLevel, nFaceOrSlice); - srcTexture.dwPitch = 0; - srcTexture.nBlockWidth = p_MipSetIn->m_nBlockWidth; - srcTexture.nBlockHeight = p_MipSetIn->m_nBlockHeight; - srcTexture.nBlockDepth = p_MipSetIn->m_nBlockDepth; - srcTexture.format = p_MipSetIn->m_format; - srcTexture.dwWidth = pInMipLevel->m_nWidth; - srcTexture.dwHeight = pInMipLevel->m_nHeight; - srcTexture.pData = pInMipLevel->m_pbData; - srcTexture.dwDataSize = CMP_CalculateBufferSize(&srcTexture); + srcTexture.dwPitch = 0; + srcTexture.nBlockWidth = p_MipSetIn->m_nBlockWidth; + srcTexture.nBlockHeight = p_MipSetIn->m_nBlockHeight; + srcTexture.nBlockDepth = p_MipSetIn->m_nBlockDepth; + srcTexture.format = p_MipSetIn->m_format; + srcTexture.dwWidth = pInMipLevel->m_nWidth; + srcTexture.dwHeight = pInMipLevel->m_nHeight; + srcTexture.pData = pInMipLevel->m_pbData; + srcTexture.dwDataSize = CMP_CalculateBufferSize(&srcTexture); // Temporary settings - p_MipSetIn->dwWidth = pInMipLevel->m_nWidth; - p_MipSetIn->dwHeight = pInMipLevel->m_nHeight; - p_MipSetIn->pData = pInMipLevel->m_pbData; + p_MipSetIn->dwWidth = pInMipLevel->m_nWidth; + p_MipSetIn->dwHeight = pInMipLevel->m_nHeight; + p_MipSetIn->pData = pInMipLevel->m_pbData; p_MipSetIn->dwDataSize = CMP_CalculateBufferSize(&srcTexture); //======================== // Compressed Destination //======================== CMP_Texture destTexture; - destTexture.dwSize = sizeof(destTexture); - destTexture.dwWidth = pInMipLevel->m_nWidth; - destTexture.dwHeight = pInMipLevel->m_nHeight; - destTexture.dwPitch = 0; - destTexture.nBlockWidth = p_MipSetIn->m_nBlockWidth; + destTexture.dwSize = sizeof(destTexture); + destTexture.dwWidth = pInMipLevel->m_nWidth; + destTexture.dwHeight = pInMipLevel->m_nHeight; + destTexture.dwPitch = 0; + destTexture.nBlockWidth = p_MipSetIn->m_nBlockWidth; destTexture.nBlockHeight = p_MipSetIn->m_nBlockHeight; - destTexture.format = pOptions->DestFormat; - destTexture.dwDataSize = CMP_CalculateBufferSize(&destTexture); + destTexture.format = pOptions->DestFormat; + destTexture.dwDataSize = CMP_CalculateBufferSize(&destTexture); + + p_MipSetOut->m_format = pOptions->DestFormat; - p_MipSetOut->m_format = pOptions->DestFormat;; p_MipSetOut->dwDataSize = CMP_CalculateBufferSize(&destTexture); - p_MipSetOut->dwWidth = pInMipLevel->m_nWidth; - p_MipSetOut->dwHeight = pInMipLevel->m_nHeight; + p_MipSetOut->dwWidth = pInMipLevel->m_nWidth; + p_MipSetOut->dwHeight = pInMipLevel->m_nHeight; //-------------------------------------- // Allocate MipSet for Block Compressors //-------------------------------------- CMP_MipLevel* pOutMipLevel = CMips.GetMipLevel(p_MipSetOut, nMipLevel, nFaceOrSlice); - if (!CMips.AllocateCompressedMipLevelData(pOutMipLevel, destTexture.dwWidth, destTexture.dwHeight, destTexture.dwDataSize)) { + if (!CMips.AllocateCompressedMipLevelData(pOutMipLevel, destTexture.dwWidth, destTexture.dwHeight, destTexture.dwDataSize)) + { return CMP_ERR_MEM_ALLOC_FOR_MIPSET; } - destTexture.pData = pOutMipLevel->m_pbData; + destTexture.pData = pOutMipLevel->m_pbData; p_MipSetOut->pData = pOutMipLevel->m_pbData; - //========================== // User suppiled Print Info //========================== - if (pOptions->m_PrintInfoStr) { + if (pOptions->m_PrintInfoStr) + { char buff[256]; snprintf(buff, sizeof(buff), @@ -891,7 +1044,8 @@ CMP_ERROR CMP_API CMP_ConvertMipTexture(CMP_MipSet* p_MipSetIn, CMP_MipSet* p_Mi srcTexture.dwWidth, srcTexture.dwHeight); pOptions->m_PrintInfoStr(buff); - if (destTexture.dwDataSize > 0) { + if (destTexture.dwDataSize > 0) + { snprintf(buff, sizeof(buff), "Destination Texture size = %d Bytes Resulting compression ratio = %2.2f:1\n", @@ -905,9 +1059,11 @@ CMP_ERROR CMP_API CMP_ConvertMipTexture(CMP_MipSet* p_MipSetIn, CMP_MipSet* p_Mi // Process ConvertTexture //======================== CMP_ERROR cmp_status = CMP_ConvertTexture(&srcTexture, &destTexture, pOptions, pFeedbackProc); - if (cmp_status != CMP_OK) { + if (cmp_status != CMP_OK) + { return cmp_status; - } else + } + else p_MipSetOut->m_nIterations++; } } @@ -917,5 +1073,3 @@ CMP_ERROR CMP_API CMP_ConvertMipTexture(CMP_MipSet* p_MipSetIn, CMP_MipSet* p_Mi return CMP_OK; } - - diff --git a/cmp_compressonatorlib/compressonator.h b/cmp_compressonatorlib/compressonator.h index ea66a3a6b..56b4fe393 100644 --- a/cmp_compressonatorlib/compressonator.h +++ b/cmp_compressonatorlib/compressonator.h @@ -39,7 +39,7 @@ typedef uint16_t WORD; typedef uint32_t DWORD; typedef int32_t LONG; typedef bool BOOL; -#ifdef _LINUX +#ifdef __linux__ typedef int32_t* DWORD_PTR; #else typedef size_t DWORD_PTR; @@ -78,14 +78,19 @@ typedef enum { CMP_FORMAT_Unknown = 0, // Undefined texture format. // Channel Component formats -------------------------------------------------------------------------------- + CMP_FORMAT_RGBA_8888_S, // RGBA format with signed 8-bit fixed channels. + CMP_FORMAT_ARGB_8888_S, // ARGB format with signed 8-bit fixed channels. CMP_FORMAT_ARGB_8888, // ARGB format with 8-bit fixed channels. CMP_FORMAT_ABGR_8888, // ABGR format with 8-bit fixed channels. CMP_FORMAT_RGBA_8888, // RGBA format with 8-bit fixed channels. CMP_FORMAT_BGRA_8888, // BGRA format with 8-bit fixed channels. CMP_FORMAT_RGB_888, // RGB format with 8-bit fixed channels. + CMP_FORMAT_RGB_888_S, // RGB format with 8-bit fixed channels. CMP_FORMAT_BGR_888, // BGR format with 8-bit fixed channels. + CMP_FORMAT_RG_8_S, // Two component format with signed 8-bit fixed channels. CMP_FORMAT_RG_8, // Two component format with 8-bit fixed channels. - CMP_FORMAT_R_8, // Single component format with 8-bit fixed channels. + CMP_FORMAT_R_8_S, // Single component format with signed 8-bit fixed channel. + CMP_FORMAT_R_8, // Single component format with 8-bit fixed channel. CMP_FORMAT_ARGB_2101010, // ARGB format with 10-bit fixed channels for color & a 2-bit fixed channel for alpha. CMP_FORMAT_ARGB_16, // ARGB format with 16-bit fixed channels. CMP_FORMAT_ABGR_16, // ABGR format with 16-bit fixed channels. @@ -232,6 +237,7 @@ struct KernelOptions { CMP_DWORD width; // Width of the encoded texture. CMP_FLOAT fquality; // Set the quality used for encoders 0.05 is the lowest and 1.0 for highest. CMP_FORMAT format; // Encoder codec format to use for processing + CMP_FORMAT srcformat; // Format of source data CMP_Compute_type encodeWith; // Host Type : default is HPC, options are [HPC or GPU] CMP_INT threads; // requested number of threads to use (1= single) max is 128 for HPC CMP_BOOL getPerfStats; // Set to true if you want to get Performance Stats @@ -239,11 +245,10 @@ struct KernelOptions { CMP_BOOL getDeviceInfo; // Set to true if you want to get target Device Info KernelDeviceInfo deviceInfo; // Data storage for the target device CMP_BOOL genGPUMipMaps; // When ecoding with GPU HW use it to generate Compressed MipMap images, valid only if source has not miplevels + CMP_INT miplevels; // When using GPU HW, generate upto this requested miplevel. CMP_BOOL useSRGBFrames; // Use SRGB frame buffer when generating HW based mipmaps (Default Gamma corretion will be set by HW) - // if the source is SNORM then this option is enabled regardless of setting - - - //private: data settings: Do not use it will be removed from this interface! + // if the source is SNORM then this option is enabled regardless of setting + //private: data settings: Do not use it will be removed from this interface! CMP_UINT size; // Size of *data void *data; // Data to pass down from CPU to kernel void *dataSVM; // Data allocated as Shared by CPU and GPU (used only when code is running in 64bit and devices support SVM) @@ -377,9 +382,9 @@ typedef struct { KernelPerformanceStats perfStats; // Data storage for the performance stats obtained from GPU or CPU while running encoder processing CMP_BOOL getDeviceInfo; // Set to true if you want to get target device info KernelDeviceInfo deviceInfo; // Data storage for the performance stats obtained from GPU or CPU while running encoder processing - CMP_BOOL genGPUMipMaps; // generate mipmaps using GPU HW, this option is only valid when EncodeWith is set to GPU HW + CMP_BOOL genGPUMipMaps; // When ecoding with GPU HW use it to generate MipMap images, valid only when miplevels is set else default is toplevel 1 CMP_BOOL useSRGBFrames; // when using GPU HW for encoding and mipmap generation use SRGB frames, default is RGB - + CMP_INT miplevels; // miplevels to use when GPU is used to generate them } CMP_CompressOptions; // The format of data in the channels of texture. @@ -409,6 +414,7 @@ typedef enum { TDT_RG = 4, // A two component texture. TDT_YUV_SD = 5, // An YUB Standard Definition texture. TDT_YUV_HD = 6, // An YUB High Definition texture. + TDT_RGB = 7, // An RGB texture } CMP_TextureDataType; typedef CMP_TextureDataType TextureDataType; @@ -750,19 +756,55 @@ typedef struct { } CMP_EncoderSetting; +typedef enum _CMP_ANALYSIS_MODES +{ + CMP_ANALYSIS_MSEPSNR = 0x00000000 // Enable Measurement of MSE and PSNR for 2 mipset image samples +} CMP_ANALYSIS_MODES; + +typedef struct +{ + // User settings + unsigned long analysisMode; // Bit mapped setting to enable various forms of image anlaysis + unsigned int channelBitMap; // Bit setting for active channels to do analysis on and reserved features + // msb(....ABGR)lsb + + // For HDR Image processing + float fInputDefog; // default = 0.0f + float fInputExposure; // default = 0.0f + float fInputKneeLow; // default = 0.0f + float fInputKneeHigh; // default = 5.0f + float fInputGamma; // default = 2.2f + + // Data return after anlysis + double mse; // Mean Square Error for all active channels in a given CMP_FORMAT + double mseR; // Mean Square for Red Channel + double mseG; // Mean Square for Green + double mseB; // Mean Square for Blue + double mseA; // Mean Square for Alpha + double psnr; // Peak Signal Ratio for all active channels in a given CMP_FORMAT + double psnrR; // Peak Signal Ratio for Red Chennel + double psnrG; // Peak Signal Ratio for Green + double psnrB; // Peak Signal Ratio for Blue + double psnrA; // Peak Signal Ratio for Alpha + +} CMP_AnalysisData; + + #ifdef __cplusplus extern "C" { #endif // MIP MAP Interfaces +CMP_INT CMP_API CMP_CalcMaxMipLevel(CMP_INT nHeight, CMP_INT nWidth, CMP_BOOL bForGPU); CMP_INT CMP_API CMP_CalcMinMipSize(CMP_INT nHeight, CMP_INT nWidth, CMP_INT MipsLevel); CMP_INT CMP_API CMP_GenerateMIPLevelsEx(CMP_MipSet* pMipSet, CMP_CFilterParams* pCFilterParams); CMP_INT CMP_API CMP_GenerateMIPLevels(CMP_MipSet *pMipSet, CMP_INT nMinSize); CMP_ERROR CMP_API CMP_CreateCompressMipSet(CMP_MipSet* pMipSetCMP, CMP_MipSet* pMipSetSRC); // MIP Map Quality -CMP_ERROR CMP_API CMP_CalcMipSetMSE_PSNR(CMP_MipSet* src1, CMP_MipSet* src2, CMP_INT nMipLevel, CMP_INT nFaceOrSlice, CMP_DOUBLE* outMSE, CMP_DOUBLE* outPSNR); +CMP_UINT CMP_API CMP_getFormat_nChannels(CMP_FORMAT format); +CMP_ERROR CMP_API CMP_MipSetAnlaysis(CMP_MipSet* src1, CMP_MipSet* src2, CMP_INT nMipLevel, CMP_INT nFaceOrSlice, CMP_AnalysisData* pAnalysisData); // CMP_MIPFeedback_Proc // Feedback function for conversion. diff --git a/cmp_compressonatorlib/dxtc/codec_dxtc.h b/cmp_compressonatorlib/dxtc/codec_dxtc.h index 9afb6220b..99c5fd8c7 100644 --- a/cmp_compressonatorlib/dxtc/codec_dxtc.h +++ b/cmp_compressonatorlib/dxtc/codec_dxtc.h @@ -47,6 +47,12 @@ class CCodec_DXTC : public CCodec_Block_4x4 { virtual bool GetParameter(const CMP_CHAR* pszParamName, CODECFLOAT& fValue); protected: + + // Signed Alpha Block + virtual CodecError CompressAlphaBlockSNorm(CMP_FLOAT alphaBlock[BLOCK_SIZE_4X4], CMP_DWORD compressedBlock[2]); + virtual void DecompressAlphaBlockInt8(CMP_SBYTE alphaBlock[BLOCK_SIZE_4X4], CMP_DWORD compressedBlock[2]); + + // Unsigned Alpha Block virtual CodecError CompressAlphaBlock(CMP_BYTE alphaBlock[BLOCK_SIZE_4X4], CMP_DWORD compressedBlock[2]); virtual CodecError CompressExplicitAlphaBlock(CMP_BYTE alphaBlock[BLOCK_SIZE_4X4], CMP_DWORD compressedBlock[2]); virtual CodecError CompressRGBBlock(CMP_BYTE rgbBlock[BLOCK_SIZE_4X4X4], CMP_DWORD compressedBlock[2], CODECFLOAT* pfChannelWeights = NULL, bool bDXT1 = false, bool bDXT1UseAlpha = false, CMP_BYTE nDXT1AlphaThreshold = 0); @@ -69,6 +75,7 @@ class CCodec_DXTC : public CCodec_Block_4x4 { virtual CodecError CompressRGBABlock_ExplicitAlpha(CODECFLOAT rgbaBlock[BLOCK_SIZE_4X4], CMP_DWORD compressedBlock[4], CODECFLOAT* pfChannelWeights = NULL); virtual void DecompressAlphaBlock(CMP_BYTE alphaBlock[BLOCK_SIZE_4X4], CMP_DWORD compressedBlock[2]); + virtual void DecompressExplicitAlphaBlock(CMP_BYTE alphaBlock[BLOCK_SIZE_4X4], CMP_DWORD compressedBlock[2]); virtual void DecompressRGBBlock(CMP_BYTE rgbBlock[BLOCK_SIZE_4X4X4], CMP_DWORD compressedBlock[2], bool bDXT1); virtual void DecompressRGBABlock(CMP_BYTE rgbBlock[BLOCK_SIZE_4X4X4], CMP_DWORD compressedBlock[2]); @@ -80,11 +87,15 @@ class CCodec_DXTC : public CCodec_Block_4x4 { virtual void DecompressRGBABlock(CODECFLOAT rgbBlock[BLOCK_SIZE_4X4X4], CMP_DWORD compressedBlock[2]); virtual void DecompressRGBABlock_ExplicitAlpha(CODECFLOAT rgbBlock[BLOCK_SIZE_4X4X4], CMP_DWORD compressedBlock[2]); -// Alpha decompression functions + // Alpha decompression functions void GetCompressedAlphaRamp(CMP_BYTE alpha[8],CMP_DWORD compressedBlock[2]); void GetCompressedAlphaRamp(CODECFLOAT alpha[8],CMP_DWORD compressedBlock[2]); -// RGB compression functions + // Signed Alpha decompression + void GetCompressedAlphaRampS(CMP_SBYTE alpha[8], CMP_DWORD compressedBlock[2]); + + + // RGB compression functions CODECFLOAT* CalculateColourWeightings(CMP_BYTE block[BLOCK_SIZE_4X4X4]); CODECFLOAT* CalculateColourWeightings(CODECFLOAT block[BLOCK_SIZE_4X4X4]); diff --git a/cmp_compressonatorlib/dxtc/codec_dxtc_alpha.cpp b/cmp_compressonatorlib/dxtc/codec_dxtc_alpha.cpp index f65d7427e..d799aa7b5 100644 --- a/cmp_compressonatorlib/dxtc/codec_dxtc_alpha.cpp +++ b/cmp_compressonatorlib/dxtc/codec_dxtc_alpha.cpp @@ -32,6 +32,8 @@ #include "compressonatorxcodec.h" #include "dxtc_v11_compress.h" +#include "common_def.h" + CodecError CCodec_DXTC::CompressAlphaBlock(CMP_BYTE alphaBlock[BLOCK_SIZE_4X4], CMP_DWORD compressedBlock[2]) { BYTE nEndpoints[2][2]; BYTE nIndices[2][BLOCK_SIZE_4X4]; @@ -44,6 +46,599 @@ CodecError CCodec_DXTC::CompressAlphaBlock(CMP_BYTE alphaBlock[BLOCK_SIZE_4X4], return CE_OK; } +// //========================== CMP_CORE Code =================================== +// #define CMP_QUALITY2 0.601f +// #ifndef MAX_ERROR +// #define MAX_ERROR 128000.f +// #endif +// #ifndef GBL_SCH_STEP +// #define GBL_SCH_STEP_MXS 0.018f +// #define GBL_SCH_EXT_MXS 0.1f +// #define LCL_SCH_STEP_MXS 0.6f +// #define GBL_SCH_STEP_MXQ 0.0175f +// #define GBL_SCH_EXT_MXQ 0.154f +// #define LCL_SCH_STEP_MXQ 0.45f +// +// #define GBL_SCH_STEP GBL_SCH_STEP_MXS +// #define GBL_SCH_EXT GBL_SCH_EXT_MXS +// #define LCL_SCH_STEP LCL_SCH_STEP_MXS +// #endif +// +// #define SCH_STPS 3 // number of search steps to make at each end of interval +// static CMP_CONSTANT CGU_FLOAT sMvF[] = {0.f, -1.f, 1.f, -2.f, 2.f, -3.f, 3.f, -4.f, 4.f, -5.f, 5.f, -6.f, 6.f, -7.f, 7.f, -8.f, 8.f}; +// #define MAX_POINTS 16 +// #define NUM_ENDPOINTS 2 +// #define CMP_ALPHA_RAMP 8 +// +// +// static CGU_INT QSortFCmp(const void* Elem1, const void* Elem2) +// { +// CGU_INT ret = 0; +// +// if (*(CGU_FLOAT*)Elem1 < *(CGU_FLOAT*)Elem2) +// ret = -1; +// else if (*(CGU_FLOAT*)Elem1 > *(CGU_FLOAT*)Elem2) +// ret = 1; +// return ret; +// } +// +// static CGU_FLOAT cmp_getRampError(CGU_FLOAT _Blk[BLOCK_SIZE_4X4], +// CGU_FLOAT _Rpt[BLOCK_SIZE_4X4], +// CGU_FLOAT _maxerror, +// CGU_FLOAT _min_ex, +// CGU_FLOAT _max_ex, +// CGU_INT _NmbrClrs) +// { // Max 16 +// CGU_INT i; +// CGU_FLOAT error = 0; +// const CGU_FLOAT step = (_max_ex - _min_ex) / 7; // (CGU_FLOAT)(dwNumPoints - 1); +// const CGU_FLOAT step_h = step * 0.5f; +// const CGU_FLOAT rstep = 1.0f / step; +// +// for (i = 0; i < _NmbrClrs; i++) +// { +// CGU_FLOAT v; +// // Work out which value in the block this select +// CGU_FLOAT del; +// +// if ((del = _Blk[i] - _min_ex) <= 0) +// v = _min_ex; +// else if (_Blk[i] - _max_ex >= 0) +// v = _max_ex; +// else +// v = (floor((del + step_h) * rstep) * step) + _min_ex; +// +// // And accumulate the error +// CGU_FLOAT del2 = (_Blk[i] - v); +// error += del2 * del2 * _Rpt[i]; +// +// // if we've already lost to the previous step bail out +// if (_maxerror < error) +// { +// error = _maxerror; +// break; +// } +// } +// return error; +// } +// +// +// static CGU_FLOAT cmp_linearBlockRefine(CGU_FLOAT _Blk[BLOCK_SIZE_4X4], +// CGU_FLOAT _Rpt[BLOCK_SIZE_4X4], +// CGU_FLOAT _MaxError, +// CMP_INOUT CGU_FLOAT CMP_PTRINOUT _min_ex, +// CMP_INOUT CGU_FLOAT CMP_PTRINOUT _max_ex, +// CGU_FLOAT _m_step, +// CGU_FLOAT _min_bnd, +// CGU_FLOAT _max_bnd, +// CGU_INT _NmbrClrs) +// { +// // Start out assuming our endpoints are the min and max values we've +// // determined +// +// // Attempt a (simple) progressive refinement step to reduce noise in the +// // output image by trying to find a better overall match for the endpoints. +// +// CGU_FLOAT maxerror = _MaxError; +// CGU_FLOAT min_ex = CMP_PTRINOUT _min_ex; +// CGU_FLOAT max_ex = CMP_PTRINOUT _max_ex; +// +// CGU_INT mode, bestmode; +// +// do +// { +// CGU_FLOAT cr_min0 = min_ex; +// CGU_FLOAT cr_max0 = max_ex; +// for (bestmode = -1, mode = 0; mode < SCH_STPS * SCH_STPS; mode++) +// { +// // check each move (see sStep for direction) +// CGU_FLOAT cr_min = min_ex + _m_step * sMvF[mode / SCH_STPS]; +// CGU_FLOAT cr_max = max_ex + _m_step * sMvF[mode % SCH_STPS]; +// +// cr_min = max(cr_min, _min_bnd); +// cr_max = min(cr_max, _max_bnd); +// +// CGU_FLOAT error; +// error = cmp_getRampError(_Blk, _Rpt, maxerror, cr_min, cr_max, _NmbrClrs); +// +// if (error < maxerror) +// { +// maxerror = error; +// bestmode = mode; +// cr_min0 = cr_min; +// cr_max0 = cr_max; +// } +// } +// +// if (bestmode != -1) +// { +// // make move (see sStep for direction) +// min_ex = cr_min0; +// max_ex = cr_max0; +// } +// } while (bestmode != -1); +// +// CMP_PTRINOUT _min_ex = min_ex; +// CMP_PTRINOUT _max_ex = max_ex; +// +// return maxerror; +// } +// +// static CGU_Vec2i cmp_getLinearEndPoints(CGU_FLOAT _Blk[BLOCK_SIZE_4X4], CMP_IN CGU_FLOAT fquality, CMP_IN CGU_BOOL isSigned) +// { +// CGU_UINT32 i; +// CGU_Vec2i cmpMinMax; +// +// CGU_FLOAT scalePts = 1.0f; //isSigned ? 128.0f : 255.0f; +// CGU_FLOAT scaleOffset = isSigned ? 0.25f : 0.5f; +// +// //================================================================ +// // Bounding Box +// // lowest quality calculation to get min and max value to use +// //================================================================ +// if (fquality < CMP_QUALITY2) +// { +// cmpMinMax.x = _Blk[0]; +// cmpMinMax.y = _Blk[0]; +// for (i = 1; i < BLOCK_SIZE_4X4; ++i) +// { +// cmpMinMax.x = min(cmpMinMax.x, _Blk[i]); +// cmpMinMax.y = max(cmpMinMax.y, _Blk[i]); +// } +// return cmpMinMax; +// } +// +// //================================================================ +// // Do more calculations to get the best min and max values to use +// //================================================================ +// CGU_FLOAT Ramp[2]; +// +// // Result defaults for SNORM or UNORM +// Ramp[0] = isSigned ? -1.0f : 0.0f; +// Ramp[1] = 1.0f; +// +// CGU_FLOAT afUniqueValues[BLOCK_SIZE_4X4]; +// CGU_FLOAT afValueRepeats[BLOCK_SIZE_4X4]; +// for (i = 0; i < BLOCK_SIZE_4X4; i++) +// afUniqueValues[i] = afValueRepeats[i] = 0.f; +// +// // For each unique value we compute the number of it appearances. +// CGU_FLOAT fBlk[BLOCK_SIZE_4X4]; +// +// memcpy(fBlk, _Blk, BLOCK_SIZE_4X4 * sizeof(CGU_FLOAT)); +// qsort((void*)fBlk, (size_t)BLOCK_SIZE_4X4, sizeof(CGU_FLOAT), QSortFCmp); +// +// CGU_FLOAT new_p = -2.0f; +// +// CGU_UINT32 dwUniqueValues = 0; +// afUniqueValues[0] = 0.0f; +// CGU_BOOL requiresCalculation = true; +// +// { +// // Ramp not fixed +// for (i = 0; i < BLOCK_SIZE_4X4; i++) +// { +// if (new_p != fBlk[i]) +// { +// afUniqueValues[dwUniqueValues] = new_p = fBlk[i]; +// afValueRepeats[dwUniqueValues] = 1.f; +// dwUniqueValues++; +// } +// else if (dwUniqueValues) +// afValueRepeats[dwUniqueValues - 1] += 1.f; +// } +// +// // if number of unique colors is less or eq 2, we've done +// if (dwUniqueValues <= 2) +// { +// Ramp[0] = floor(afUniqueValues[0] * scalePts + scaleOffset); +// if (dwUniqueValues == 1) +// Ramp[1] = Ramp[0] + 1.f; +// else +// Ramp[1] = floor(afUniqueValues[1] * scalePts + scaleOffset); +// requiresCalculation = false; +// } +// } // Ramp not fixed +// +// if (requiresCalculation) +// { +// CGU_FLOAT min_ex = afUniqueValues[0]; +// CGU_FLOAT max_ex = afUniqueValues[dwUniqueValues - 1]; +// CGU_FLOAT min_bnd = 0, max_bnd = 1.; +// CGU_FLOAT min_r = min_ex, max_r = max_ex; +// CGU_FLOAT gbl_l = 0, gbl_r = 0; +// CGU_FLOAT cntr = (min_r + max_r) / 2; +// +// CGU_FLOAT gbl_err = MAX_ERROR; +// // Trying to avoid unnecessary calculations. Heuristics: after some analisis +// // it appears that in integer case, if the input interval not more then 48 +// // we won't get much better +// bool wantsSearch = !((max_ex - min_ex) <= (48.f / scalePts)); +// +// if (wantsSearch) +// { +// // Search. +// // 1. take the vicinities of both low and high bound of the input +// // interval. +// // 2. setup some search step +// // 3. find the new low and high bound which provides an (sub) optimal +// // (infinite precision) clusterization. +// CGU_FLOAT gbl_llb = (min_bnd > min_r - GBL_SCH_EXT) ? min_bnd : min_r - GBL_SCH_EXT; +// CGU_FLOAT gbl_rrb = (max_bnd < max_r + GBL_SCH_EXT) ? max_bnd : max_r + GBL_SCH_EXT; +// CGU_FLOAT gbl_lrb = (cntr < min_r + GBL_SCH_EXT) ? cntr : min_r + GBL_SCH_EXT; +// CGU_FLOAT gbl_rlb = (cntr > max_r - GBL_SCH_EXT) ? cntr : max_r - GBL_SCH_EXT; +// +// for (CGU_FLOAT step_l = gbl_llb; step_l < gbl_lrb; step_l += GBL_SCH_STEP) +// { +// for (CGU_FLOAT step_r = gbl_rrb; gbl_rlb <= step_r; step_r -= GBL_SCH_STEP) +// { +// CGU_FLOAT sch_err; +// // an sse version is avaiable +// sch_err = cmp_getRampError(afUniqueValues, afValueRepeats, gbl_err, step_l, step_r, dwUniqueValues); +// if (sch_err < gbl_err) +// { +// gbl_err = sch_err; +// gbl_l = step_l; +// gbl_r = step_r; +// } +// } +// } +// +// min_r = gbl_l; +// max_r = gbl_r; +// } // want search +// +// // This is a refinement call. The function tries to make several small +// // stretches or squashes to minimize quantization error. +// CGU_FLOAT m_step = LCL_SCH_STEP / scalePts; +// cmp_linearBlockRefine(afUniqueValues, afValueRepeats, gbl_err, CMP_REFINOUT min_r, CMP_REFINOUT max_r, m_step, min_bnd, max_bnd, dwUniqueValues); +// +// min_ex = min_r; +// max_ex = max_r; +// max_ex *= scalePts; +// min_ex *= scalePts; +// +// Ramp[0] = floor(min_ex + scaleOffset); +// Ramp[1] = floor(max_ex + scaleOffset); +// } +// +// // Ensure that the two endpoints are not the same +// // This is legal but serves no need & can break some optimizations in the compressor +// if (Ramp[0] == Ramp[1]) +// { +// if (Ramp[1] < scalePts) +// Ramp[1] = Ramp[1] + .1f; +// else if (Ramp[1] > 0.0f) +// Ramp[1] = Ramp[1] - .1f; +// } +// +// cmpMinMax.x = Ramp[0]; +// cmpMinMax.y = Ramp[1]; +// +// return cmpMinMax; +// } + + + +static CGU_INT8 cmp_SNormFloatToSInt(CGU_FLOAT fsnorm) +{ + if (isnan(fsnorm)) + fsnorm = 0; + else if (fsnorm > 1) + fsnorm = 1; // Clamp to 1 + else if (fsnorm < -1) + fsnorm = -1; // Clamp to -1 + + fsnorm = fsnorm * 127U; + + // shift round up or down + if (fsnorm >= 0) + fsnorm += .5f; + else + fsnorm -= .5f; + + CGU_INT8 res = static_cast(fsnorm); + + return (res); +} + +static CGU_Vec2f cmp_OptimizeEndPoints(CGU_FLOAT* pPoints, CGU_INT8 cSteps, CGU_BOOL isSigned) +{ + CGU_Vec2f fendpoints; + CGU_FLOAT MAX_VALUE = 1.0f; + CGU_FLOAT MIN_VALUE = isSigned ? -1.0f : 0.0f; + + // Find Min and Max points, as starting point + CGU_FLOAT fX = MAX_VALUE; + CGU_FLOAT fY = MIN_VALUE; + + if (8 == cSteps) + { + for (CGU_INT8 iPoint = 0; iPoint < BLOCK_SIZE_4X4; iPoint++) + { + if (pPoints[iPoint] < fX) + fX = pPoints[iPoint]; + + if (pPoints[iPoint] > fY) + fY = pPoints[iPoint]; + } + } + else + { + for (CGU_INT8 iPoint = 0; iPoint < BLOCK_SIZE_4X4; iPoint++) + { + if (pPoints[iPoint] < fX && pPoints[iPoint] > MIN_VALUE) + fX = pPoints[iPoint]; + + if (pPoints[iPoint] > fY && pPoints[iPoint] < MAX_VALUE) + fY = pPoints[iPoint]; + } + + if (fX == fY) + { + fY = MAX_VALUE; + } + } + + //=================== + // Use Newton Method + //=================== + CGU_FLOAT cStepsDiv = static_cast(cSteps - 1); + CGU_FLOAT pSteps[8]; + CGU_FLOAT fc; + CGU_FLOAT fd; + + for (size_t iIteration = 0; iIteration < 8; iIteration++) + { + // reach minimum threashold break + if ((fY - fX) < (1.0f / 256.0f)) + break; + + CGU_FLOAT fScale = cStepsDiv / (fY - fX); + + // Calculate new steps + for (size_t iStep = 0; iStep < cSteps; iStep++) + { + fc = (cStepsDiv - (CGU_FLOAT)iStep) / cStepsDiv; + fd = (CGU_FLOAT)iStep / cStepsDiv; + pSteps[iStep] = fc * fX + fd * fY; + } + + if (6 == cSteps) + { + pSteps[6] = MIN_VALUE; + pSteps[7] = MAX_VALUE; + } + + // Evaluate function, and derivatives + CGU_FLOAT dX = 0.0f; + CGU_FLOAT dY = 0.0f; + CGU_FLOAT d2X = 0.0f; + CGU_FLOAT d2Y = 0.0f; + + for (size_t iPoint = 0; iPoint < BLOCK_SIZE_4X4; iPoint++) + { + float fDot = (pPoints[iPoint] - fX) * fScale; + + CGU_INT8 iStep; + if (fDot <= 0.0f) + { + iStep = ((6 == cSteps) && (pPoints[iPoint] <= (fX + MIN_VALUE) * 0.5f)) ? 6u : 0u; + } + else if (fDot >= cStepsDiv) + { + iStep = ((6 == cSteps) && (pPoints[iPoint] >= (fY + MAX_VALUE) * 0.5f)) ? 7u : (cSteps - 1); + } + else + { + iStep = uint32_t(fDot + 0.5f); + } + + // steps to improve quality + if (iStep < cSteps) + { + fc = (cStepsDiv - (CGU_FLOAT)iStep) / cStepsDiv; + fd = (CGU_FLOAT)iStep / cStepsDiv; + CGU_FLOAT fDiff = pSteps[iStep] - pPoints[iPoint]; + dX += fc * fDiff; + d2X += fc * fc; + dY += fd * fDiff; + d2Y += fd * fd; + } + } + + // Move endpoints + if (d2X > 0.0f) + fX -= dX / d2X; + + if (d2Y > 0.0f) + fY -= dY / d2Y; + + if (fX > fY) + { + float f = fX; + fX = fY; + fY = f; + } + + if ((dX * dX < (1.0f / 64.0f)) && (dY * dY < (1.0f / 64.0f))) + break; + } + + fendpoints.x = (fX < MIN_VALUE) ? MIN_VALUE : (fX > MAX_VALUE) ? MAX_VALUE : fX; + fendpoints.y = (fY < MIN_VALUE) ? MIN_VALUE : (fY > MAX_VALUE) ? MAX_VALUE : fY; + + return fendpoints; +} + +static CGU_Vec2i CMP_FindEndpointsAlphaBlockSnorm(CGU_FLOAT alphaBlockSnorm[]) +{ + + //================================================================ + // Bounding Box + // lowest quality calculation to get min and max value to use + //================================================================ + CGU_Vec2f cmpMinMax; + cmpMinMax.x = alphaBlockSnorm[0]; + cmpMinMax.y = alphaBlockSnorm[0]; + + for (size_t i = 0; i < BLOCK_SIZE_4X4; ++i) + { + if (alphaBlockSnorm[i] < cmpMinMax.x) + { + cmpMinMax.x = alphaBlockSnorm[i]; + } + else if (alphaBlockSnorm[i] > cmpMinMax.y) + { + cmpMinMax.y = alphaBlockSnorm[i]; + } + } + + CGU_Vec2i endpoints; + CGU_Vec2f fendpoints; + + // Are we done for lowest quality setting! + // CGU_FLOAT fquality = 1.0f; + // + // if (fquality < CMP_QUALITY2) { + // endpoints.x = (CGU_INT8)(cmpMinMax.x); + // endpoints.y = (CGU_INT8)(cmpMinMax.y); + // return endpoints; + // } + + //================================================================ + // Do more calculations to get the best min and max values to use + //================================================================ + if ((-1.0f == cmpMinMax.x || 1.0f == cmpMinMax.y)) + { + fendpoints = cmp_OptimizeEndPoints(alphaBlockSnorm, 6, true); + endpoints.x = cmp_SNormFloatToSInt(fendpoints.x); + endpoints.y = cmp_SNormFloatToSInt(fendpoints.y); + } + else + { + fendpoints = cmp_OptimizeEndPoints(alphaBlockSnorm, 8, true); + endpoints.x = cmp_SNormFloatToSInt(fendpoints.y); + endpoints.y = cmp_SNormFloatToSInt(fendpoints.x); + } + + return endpoints; +} + +static uint64_t cmp_getBlockPackedIndicesSNorm(CGU_Vec2f alphaMinMax, const float alphaBlockSnorm[], uint64_t data) +{ + CGU_FLOAT alpha[8]; + alpha[0] = alphaMinMax.x; + alpha[1] = alphaMinMax.y; + + if (alphaMinMax.x > alphaMinMax.y) + { + // 8-alpha block: derive the other six alphas. + // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated. + alpha[2] = (alpha[0] * 6.0f + alpha[1]) / 7.0f; + alpha[3] = (alpha[0] * 5.0f + alpha[1] * 2.0f) / 7.0f; + alpha[4] = (alpha[0] * 4.0f + alpha[1] * 3.0f) / 7.0f; + alpha[5] = (alpha[0] * 3.0f + alpha[1] * 4.0f) / 7.0f; + alpha[6] = (alpha[0] * 2.0f + alpha[1] * 5.0f) / 7.0f; + alpha[7] = (alpha[0] + alpha[1] * 6.0f) / 7.0f; + } + else + { + // 6-alpha block. + // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated. + alpha[2] = (alpha[0] * 4.0f + alpha[1]) / 5.0f; + alpha[3] = (alpha[0] * 3.0f + alpha[1] * 2.0f) / 5.0f; + alpha[4] = (alpha[0] * 2.0f + alpha[1] * 3.0f) / 5.0f; + alpha[5] = (alpha[0] + alpha[1] * 4.0f) / 5.0f; + alpha[6] = -1.0f; + alpha[7] = 1.0f; + } + + // Index all colors using best alpha value + for (CGU_UINT8 i = 0; i < BLOCK_SIZE_4X4; ++i) + { + CGU_UINT8 uBestIndex = 0; + CGU_FLOAT fBestDelta = CMP_FLOAT_MAX; + for (CGU_INT32 uIndex = 0; uIndex < 8; uIndex++) + { + CGU_FLOAT fCurrentDelta = fabsf(alpha[uIndex] - alphaBlockSnorm[i]); + if (fCurrentDelta < fBestDelta) + { + uBestIndex = uIndex; + fBestDelta = fCurrentDelta; + } + } + + data &= ~(uint64_t(0x07) << (3 * i + 16)); + data |= (uint64_t(uBestIndex) << (3 * i + 16)); + } + + return data; +} + +//============================================================================= + +CodecError CCodec_DXTC::CompressAlphaBlockSNorm(CMP_FLOAT alphaBlockSnorm[BLOCK_SIZE_4X4], CMP_DWORD compressedBlock[2]) +{ + union + { + CMP_DWORD compressedBlock[2]; + struct + { + int8_t red_0; + int8_t red_1; + uint8_t indices[6]; + }; + uint64_t data; + } BC4_Snorm_block; + + BC4_Snorm_block.data = 0LL; + + CGU_Vec2i reds; + reds = CMP_FindEndpointsAlphaBlockSnorm(alphaBlockSnorm); + + BC4_Snorm_block.red_0 = reds.x & 0xFF; + BC4_Snorm_block.red_1 = reds.y & 0xFF; + + // check low end boundaries + if (BC4_Snorm_block.red_0 == -128) + BC4_Snorm_block.red_0 = -127; + if (BC4_Snorm_block.red_1 == -128) + BC4_Snorm_block.red_1 = -127; + + // Normalize signed int -128..127 to float -1..1 + CGU_Vec2f alphaMinMax; + alphaMinMax.x = CGU_FLOAT(BC4_Snorm_block.red_0) / 127.0f; + alphaMinMax.y = CGU_FLOAT(BC4_Snorm_block.red_1) / 127.0f; + + BC4_Snorm_block.data = cmp_getBlockPackedIndicesSNorm(alphaMinMax, alphaBlockSnorm, BC4_Snorm_block.data); + + compressedBlock[0] = BC4_Snorm_block.compressedBlock[0]; + compressedBlock[1] = BC4_Snorm_block.compressedBlock[1]; + + return CE_OK; +} + CodecError CCodec_DXTC::CompressAlphaBlock_Fast(CMP_BYTE alphaBlock[BLOCK_SIZE_4X4], CMP_DWORD compressedBlock[2]) { #ifdef _WIN64 CompressAlphaBlock(alphaBlock, compressedBlock); @@ -81,7 +676,6 @@ void CCodec_DXTC::EncodeAlphaBlock(CMP_DWORD compressedBlock[2], BYTE nEndpoints } } - // // This function decompresses a block // @@ -104,6 +698,31 @@ void CCodec_DXTC::DecompressAlphaBlock(CMP_BYTE alphaBlock[BLOCK_SIZE_4X4], CMP_ } } +// +// This function decompresses a signed block +// +void CCodec_DXTC::DecompressAlphaBlockInt8(CMP_SBYTE alphaBlock[BLOCK_SIZE_4X4], CMP_DWORD compressedBlock[2]) +{ + CMP_SBYTE alpha[8]; + GetCompressedAlphaRampS(alpha, compressedBlock); + + for (int i = 0; i < BLOCK_SIZE_4X4; i++) + { + CMP_DWORD index; + if (i < 5) + index = (compressedBlock[0] & (0x7 << (16 + (i * 3)))) >> (16 + (i * 3)); + else if (i > 5) + index = (compressedBlock[1] & (0x7 << (2 + (i - 6) * 3))) >> (2 + (i - 6) * 3); + else + { + index = (compressedBlock[0] & 0x80000000) >> 31; + index |= (compressedBlock[1] & 0x3) << 1; + } + + alphaBlock[i] = alpha[index]; + } +} + void CCodec_DXTC::DecompressAlphaBlock(CODECFLOAT alphaBlock[BLOCK_SIZE_4X4], CMP_DWORD compressedBlock[2]) { CODECFLOAT alpha[8]; GetCompressedAlphaRamp(alpha, compressedBlock); @@ -132,19 +751,8 @@ CodecError CCodec_DXTC::CompressExplicitAlphaBlock(CMP_BYTE alphaBlock[BLOCK_SIZ } CodecError CCodec_DXTC::CompressExplicitAlphaBlock_Fast(CMP_BYTE alphaBlock[BLOCK_SIZE_4X4], CMP_DWORD compressedBlock[2]) { -#ifndef NO_LEGACY_BEHAVIOR -#ifdef _WIN32 -#ifndef DISABLE_TESTCODE - DXTCV11CompressExplicitAlphaBlockMMX(alphaBlock, compressedBlock); -#else - CompressExplicitAlphaBlock(alphaBlock, compressedBlock); -#endif -#else + // Should remove or update this: DXTCV11CompressExplicitAlphaBlockMMX(alphaBlock, compressedBlock); CompressExplicitAlphaBlock(alphaBlock, compressedBlock); -#endif -#else - CompressExplicitAlphaBlock(alphaBlock, compressedBlock); -#endif return CE_OK; } @@ -229,4 +837,36 @@ void CCodec_DXTC::GetCompressedAlphaRamp(CODECFLOAT alpha[8], CMP_DWORD compress alpha[6] = 0; // Bit code 110 alpha[7] = 1.0f; // Bit code 111 } -} \ No newline at end of file +} + +void CCodec_DXTC::GetCompressedAlphaRampS(CMP_SBYTE alpha[8], CMP_DWORD compressedBlock[2]) +{ + alpha[0] = (CMP_SBYTE)(compressedBlock[0] & 0xff); + alpha[1] = (CMP_SBYTE)((compressedBlock[0] >> 8) & 0xff); + + if (alpha[0] > alpha[1]) + { + // 8-alpha block: derive the other six alphas. + // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated. + alpha[2] = static_cast((6 * alpha[0] + 1 * alpha[1] + 3) / 7); // bit code 010 + alpha[3] = static_cast((5 * alpha[0] + 2 * alpha[1] + 3) / 7); // bit code 011 + alpha[4] = static_cast((4 * alpha[0] + 3 * alpha[1] + 3) / 7); // bit code 100 + alpha[5] = static_cast((3 * alpha[0] + 4 * alpha[1] + 3) / 7); // bit code 101 + alpha[6] = static_cast((2 * alpha[0] + 5 * alpha[1] + 3) / 7); // bit code 110 + alpha[7] = static_cast((1 * alpha[0] + 6 * alpha[1] + 3) / 7); // bit code 111 + } + else + { + // 6-alpha block. + // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated. + alpha[2] = static_cast((4 * alpha[0] + 1 * alpha[1] + 2) / 5); // Bit code 010 + alpha[3] = static_cast((3 * alpha[0] + 2 * alpha[1] + 2) / 5); // Bit code 011 + alpha[4] = static_cast((2 * alpha[0] + 3 * alpha[1] + 2) / 5); // Bit code 100 + alpha[5] = static_cast((1 * alpha[0] + 4 * alpha[1] + 2) / 5); // Bit code 101 + alpha[6] = -127; // Bit code 110 + alpha[7] = 127; // Bit code 111 + } +} + + + diff --git a/cmp_compressonatorlib/dxtc/dxtc_v11_compress.h b/cmp_compressonatorlib/dxtc/dxtc_v11_compress.h index 1b04a10df..89610be63 100644 --- a/cmp_compressonatorlib/dxtc/dxtc_v11_compress.h +++ b/cmp_compressonatorlib/dxtc/dxtc_v11_compress.h @@ -41,10 +41,8 @@ extern "C" { void DXTCV11CompressBlockSSE(DWORD *block_32, DWORD *block_dxtc); #ifdef _WIN32 -#ifndef DISABLE_TESTCODE void __cdecl DXTCV11CompressBlockSSE2(DWORD *block_32, DWORD *block_dxtc); #endif -#endif void DXTCV11CompressBlockSSEMinimal(DWORD *block_32, DWORD *block_dxtc); @@ -54,13 +52,11 @@ void DXTCV11CompressAlphaBlock(BYTE block_8[16], DWORD block_dxtc[2]); void DXTCV11CompressExplicitAlphaBlock(BYTE block_8[16], DWORD block_dxtc[2]); #ifdef _WIN32 -#ifndef DISABLE_TESTCODE #if defined(_WIN64) || defined(__linux__) void DXTCV11CompressExplicitAlphaBlockMMX(BYTE block_8[16], DWORD block_dxtc[2]); #else void __fastcall DXTCV11CompressExplicitAlphaBlockMMX(BYTE block_8[16], DWORD block_dxtc[2]); #endif // !_WIN64 -#endif #endif //_WIN32 #ifdef __cplusplus diff --git a/cmp_core/cmakelists.txt b/cmp_core/cmakelists.txt index 8c6a6b619..afdca5c2e 100644 --- a/cmp_core/cmakelists.txt +++ b/cmp_core/cmakelists.txt @@ -1,38 +1,37 @@ +cmake_minimum_required(VERSION 3.10) -add_library(CMP_Core) +add_library(CMP_Core STATIC) -target_sources(CMP_Core PRIVATE - shaders/bc1_encode_kernel.h - shaders/bc1_encode_kernel.cpp - shaders/bc2_encode_kernel.h - shaders/bc2_encode_kernel.cpp - shaders/bc3_encode_kernel.h - shaders/bc3_encode_kernel.cpp - shaders/bc4_encode_kernel.h - shaders/bc4_encode_kernel.cpp - shaders/bc5_encode_kernel.h - shaders/bc5_encode_kernel.cpp - shaders/bc6_encode_kernel.h - shaders/bc6_encode_kernel.cpp - shaders/bc7_encode_kernel.h - shaders/bc7_encode_kernel.cpp - shaders/bcn_common_kernel.h - shaders/common_def.h +target_sources(CMP_Core + PRIVATE + shaders/bc1_encode_kernel.h + shaders/bc1_encode_kernel.cpp + shaders/bc2_encode_kernel.h + shaders/bc2_encode_kernel.cpp + shaders/bc3_encode_kernel.h + shaders/bc3_encode_kernel.cpp + shaders/bc4_encode_kernel.h + shaders/bc4_encode_kernel.cpp + shaders/bc5_encode_kernel.h + shaders/bc5_encode_kernel.cpp + shaders/bc6_encode_kernel.h + shaders/bc6_encode_kernel.cpp + shaders/bc7_encode_kernel.h + shaders/bc7_encode_kernel.cpp + shaders/bcn_common_kernel.h + shaders/common_def.h + source/cmp_core.h + source/cmp_math_vec4.h + source/cmp_math_func.h + ) - source/cmp_core.h - source/cmp_math_vec4.h - source/cmp_math_func.h -) +target_include_directories(CMP_Core + PRIVATE + shaders + source) -target_include_directories(CMP_Core PUBLIC - shaders - source -) - -if (UNIX OR APPLE) - target_compile_definitions(CMP_Core PRIVATE ASPM_GPU) +if (UNIX) +target_compile_definitions(CMP_Core PRIVATE _LINUX ASPM_GPU) endif() -if (OPTION_BUILD_TESTING) - add_subdirectory(test) -endif() \ No newline at end of file +set_target_properties(CMP_Core PROPERTIES FOLDER "Libs") diff --git a/cmp_core/cmp_core.def b/cmp_core/cmp_core.def index baa5bc1b0..defbac392 100644 --- a/cmp_core/cmp_core.def +++ b/cmp_core/cmp_core.def @@ -43,7 +43,9 @@ CompressBlockBC1 CompressBlockBC2 CompressBlockBC3 CompressBlockBC4 +CompressBlockBC4S CompressBlockBC5 +CompressBlockBC5S CompressBlockBC6 CompressBlockBC7 @@ -51,6 +53,8 @@ DecompressBlockBC1 DecompressBlockBC2 DecompressBlockBC3 DecompressBlockBC4 +DecompressBlockBC4S DecompressBlockBC5 +DecompressBlockBC5S DecompressBlockBC6 DecompressBlockBC7 diff --git a/cmp_core/shaders/bc2_encode_kernel.cpp b/cmp_core/shaders/bc2_encode_kernel.cpp index bb4477379..94d1e56bb 100644 --- a/cmp_core/shaders/bc2_encode_kernel.cpp +++ b/cmp_core/shaders/bc2_encode_kernel.cpp @@ -115,7 +115,7 @@ int CMP_CDECL SetChannelWeightsBC2(void *options, return CGU_CORE_OK; } -int CMP_CDECL SetGammaBC2(void *options, +int CMP_CDECL SetSrgbBC2(void* options, CGU_BOOL sRGB) { if (!options) return CGU_CORE_ERR_INVALIDPTR; CMP_BC15Options *BC15optionsDefault = (CMP_BC15Options *)options; diff --git a/cmp_core/shaders/bc3_encode_kernel.cpp b/cmp_core/shaders/bc3_encode_kernel.cpp index 641fedd03..83c2deddb 100644 --- a/cmp_core/shaders/bc3_encode_kernel.cpp +++ b/cmp_core/shaders/bc3_encode_kernel.cpp @@ -119,8 +119,7 @@ int CMP_CDECL SetChannelWeightsBC3(void *options, return CGU_CORE_OK; } -int CMP_CDECL SetGammaBC3(void *options, - CGU_BOOL sRGB) { +int CMP_CDECL SetSrgbBC3(void* options,CGU_BOOL sRGB) { if (!options) return CGU_CORE_ERR_INVALIDPTR; CMP_BC15Options *BC15optionsDefault = (CMP_BC15Options *)options; diff --git a/cmp_core/shaders/bc4_encode_kernel.cpp b/cmp_core/shaders/bc4_encode_kernel.cpp index 7e7cbfda9..a9a1c2cc2 100644 --- a/cmp_core/shaders/bc4_encode_kernel.cpp +++ b/cmp_core/shaders/bc4_encode_kernel.cpp @@ -23,45 +23,86 @@ #include "bc4_encode_kernel.h" //============================================== BC4 INTERFACES ======================================================= - -void CompressBlockBC4_Internal(const CMP_Vec4uc srcBlockTemp[16], - CMP_GLOBAL CGU_UINT32 compressedBlock[2], - CMP_GLOBAL const CMP_BC15Options *BC15options) { - if (BC15options->m_fquality) { +// Processing UINT to either SNORM or UNORM +void CompressBlockBC4_Internal(const CMP_Vec4uc srcBlockTemp[16], CMP_GLOBAL CGU_UINT32 compressedBlock[2], CMP_GLOBAL const CMP_BC15Options* BC15options) +{ + if (BC15options->m_fquality) + { // Reserved! } + + CGU_Vec2ui cmpBlock; + CGU_FLOAT alphaBlock[16]; + CGU_FLOAT scaleByteChannel = 255.0f; + CGU_UINT8 shiftByteChannel = 0; + CGU_UINT8 blkindex = 0; CGU_UINT8 srcindex = 0; - CGU_FLOAT alphaBlock[16]; - for (CGU_INT32 j = 0; j < 4; j++) { - for (CGU_INT32 i = 0; i < 4; i++) { - alphaBlock[blkindex++] = srcBlockTemp[srcindex].x / 255.0f; // Red channel - srcindex++; + if (BC15options->m_bIsSNORM) + { + if (BC15options->m_sintsrc) + { + // Convert UINT (carrier of signed ) -> SINT -> SNORM + for (int i = 0; i < BLOCK_SIZE_4X4; i++) + { + char x = (char)(srcBlockTemp[i].x); + alphaBlock[i] = (CGU_FLOAT)(x) / 127.0f; + } + } + else + { + // Convert UINT -> SNORM + for (int i = 0; i < BLOCK_SIZE_4X4; i++) + { + alphaBlock[i] = (((CGU_FLOAT)(srcBlockTemp[i].x) / 255.0f) * 2.0f - 1.0f); + } + } + } + else + { + // Convert SINT -> UNORM + if (BC15options->m_sintsrc) + { + for (int i = 0; i < BLOCK_SIZE_4X4; i++) + { + char x = (char)(srcBlockTemp[i].x); + alphaBlock[i] = ((((CGU_FLOAT)(x) / 127.0f) * 0.5f) + 0.5f); + } + } + else + { + // Convert UINT -> UNORM + for (int i = 0; i < BLOCK_SIZE_4X4; i++) + { + alphaBlock[i] = (CGU_FLOAT)(srcBlockTemp[i].x) / 255.0f; + } } } - CGU_Vec2ui cmpBlock; + cmpBlock = cmp_compressAlphaBlock(alphaBlock, BC15options->m_fquality, BC15options->m_bIsSNORM); - cmpBlock = cmp_compressAlphaBlock(alphaBlock,BC15options->m_fquality,FALSE); compressedBlock[0] = cmpBlock.x; compressedBlock[1] = cmpBlock.y; } -void DecompressBC4_Internal(CMP_GLOBAL CGU_UINT8 rgbaBlock[64], - const CGU_UINT32 compressedBlock[2], - const CMP_BC15Options *BC15options) { - if (BC15options) {} +void DecompressBC4_Internal(CMP_GLOBAL CGU_UINT8 rgbaBlock[64], const CGU_UINT32 compressedBlock[2], const CMP_BC15Options* BC15options) +{ + if (BC15options) + { + } CGU_UINT8 alphaBlock[BLOCK_SIZE_4X4]; cmp_decompressAlphaBlock(alphaBlock, compressedBlock); CGU_UINT8 blkindex = 0; CGU_UINT8 srcindex = 0; - for (CGU_INT32 j = 0; j < 4; j++) { - for (CGU_INT32 i = 0; i < 4; i++) { - rgbaBlock[blkindex++] = (CGU_UINT8)alphaBlock[srcindex]; // R - rgbaBlock[blkindex++] = (CGU_UINT8)alphaBlock[srcindex]; // G - rgbaBlock[blkindex++] = (CGU_UINT8)alphaBlock[srcindex]; // B - rgbaBlock[blkindex++] = (CGU_UINT8)alphaBlock[srcindex]; // A + for (CGU_INT32 j = 0; j < 4; j++) + { + for (CGU_INT32 i = 0; i < 4; i++) + { + rgbaBlock[blkindex++] = (CGU_UINT8)(alphaBlock[srcindex]); // R + rgbaBlock[blkindex++] = (CGU_UINT8)(alphaBlock[srcindex]); // G + rgbaBlock[blkindex++] = (CGU_UINT8)(alphaBlock[srcindex]); // B + rgbaBlock[blkindex++] = (CGU_UINT8)(alphaBlock[srcindex]); // A srcindex++; } } @@ -69,11 +110,15 @@ void DecompressBC4_Internal(CMP_GLOBAL CGU_UINT8 rgbaBlock[64], void CompressBlockBC4_SingleChannel(const CGU_UINT8 srcBlockTemp[BLOCK_SIZE_4X4], CMP_GLOBAL CGU_UINT32 compressedBlock[2], - CMP_GLOBAL const CMP_BC15Options *BC15options) { - if (BC15options) {} - CGU_FLOAT alphaBlock[BLOCK_SIZE_4X4]; + CMP_GLOBAL const CMP_BC15Options* BC15options) +{ + if (BC15options) + { + } + CGU_FLOAT alphaBlock[BLOCK_SIZE_4X4]; - for (CGU_INT32 i = 0; i < BLOCK_SIZE_4X4; i++) alphaBlock[i] = (srcBlockTemp[i] / 255.0f); + for (CGU_INT32 i = 0; i < BLOCK_SIZE_4X4; i++) + alphaBlock[i] = (CGU_FLOAT)(srcBlockTemp[i]) / 255.0f; CGU_Vec2ui cmpBlock; cmpBlock = cmp_compressAlphaBlock(alphaBlock, BC15options->m_fquality, FALSE); @@ -81,23 +126,24 @@ void CompressBlockBC4_SingleChannel(const CGU_UINT8 srcBlockTemp[BLOCK_SIZE_4X4] compressedBlock[1] = cmpBlock.y; } -void DecompressBlockBC4_SingleChannel(CGU_UINT8 srcBlockTemp[16], - const CGU_UINT32 compressedBlock[2], - const CMP_BC15Options *BC15options) { - if (BC15options) {} +void DecompressBlockBC4_SingleChannel(CGU_UINT8 srcBlockTemp[16], const CGU_UINT32 compressedBlock[2], const CMP_BC15Options* BC15options) +{ + if (BC15options) + { + } cmp_decompressAlphaBlock(srcBlockTemp, compressedBlock); } - void CompressBlockBC4S_SingleChannel(const CGU_INT8 srcBlockTemp[BLOCK_SIZE_4X4], - CMP_GLOBAL CGU_INT32 compressedBlock[2], - CMP_GLOBAL const CMP_BC15Options* BC15options) { - if (BC15options) { - } + CMP_GLOBAL CGU_UINT32 compressedBlock[2], + CMP_GLOBAL const CMP_BC15Options* BC15options) +{ + if (BC15options) { } + CGU_FLOAT alphaBlock[BLOCK_SIZE_4X4]; for (CGU_INT32 i = 0; i < BLOCK_SIZE_4X4; i++) - alphaBlock[i] = (srcBlockTemp[i] / 255.0f); + alphaBlock[i] = (srcBlockTemp[i] / 127.0f); CGU_Vec2ui cmpBlock; cmpBlock = cmp_compressAlphaBlock(alphaBlock, BC15options->m_fquality, TRUE); @@ -105,149 +151,149 @@ void CompressBlockBC4S_SingleChannel(const CGU_INT8 srcBlockTemp[BLOCK_SIZE_4X4] compressedBlock[1] = cmpBlock.y; } -void DecompressBlockBC4S_SingleChannel(CGU_INT8 srcBlockTemp[16], const CGU_INT32 compressedBlock[2], const CMP_BC15Options* BC15options) { - if (BC15options) { - } +void DecompressBlockBC4S_SingleChannel(CGU_INT8 srcBlockTemp[16], const CGU_UINT32 compressedBlock[2], const CMP_BC15Options* BC15options) +{ + if (BC15options) { } cmp_decompressAlphaBlockS(srcBlockTemp, compressedBlock); } //============================================== USER INTERFACES ======================================================== #ifndef ASPM_GPU -int CMP_CDECL CreateOptionsBC4(void **options) { - CMP_BC15Options *BC15optionsDefault = new CMP_BC15Options; - if (BC15optionsDefault) { +int CMP_CDECL CreateOptionsBC4(void** options) +{ + CMP_BC15Options* BC15optionsDefault = new CMP_BC15Options; + if (BC15optionsDefault) + { SetDefaultBC15Options(BC15optionsDefault); (*options) = BC15optionsDefault; - } else { + } + else + { (*options) = NULL; return CGU_CORE_ERR_NEWMEM; } return CGU_CORE_OK; } -int CMP_CDECL DestroyOptionsBC4(void *options) { - if (!options) return CGU_CORE_ERR_INVALIDPTR; - CMP_BC15Options *BCOptions = reinterpret_cast (options); +int CMP_CDECL DestroyOptionsBC4(void* options) +{ + if (!options) + return CGU_CORE_ERR_INVALIDPTR; + CMP_BC15Options* BCOptions = reinterpret_cast(options); delete BCOptions; return CGU_CORE_OK; } -int CMP_CDECL SetQualityBC4(void *options, - CGU_FLOAT fquality) { - if (!options) return CGU_CORE_ERR_INVALIDPTR; - CMP_BC15Options *BC15optionsDefault = reinterpret_cast (options); - if (fquality < 0.0f) fquality = 0.0f; - else if (fquality > 1.0f) fquality = 1.0f; +int CMP_CDECL SetQualityBC4(void* options, CGU_FLOAT fquality) +{ + if (!options) + return CGU_CORE_ERR_INVALIDPTR; + CMP_BC15Options* BC15optionsDefault = reinterpret_cast(options); + if (fquality < 0.0f) + fquality = 0.0f; + else if (fquality > 1.0f) + fquality = 1.0f; BC15optionsDefault->m_fquality = fquality; return CGU_CORE_OK; } -int CMP_CDECL SetSignedBC4(void *options, - CGU_BOOL snorm) { - if (!options) return CGU_CORE_ERR_INVALIDPTR; - CMP_BC15Options *BC15optionsDefault = reinterpret_cast (options); - - BC15optionsDefault->m_bIsSNORM = snorm; - return CGU_CORE_OK; -} - -int CMP_CDECL CompressBlockBC4S(const char* srcBlock, unsigned int srcStrideInBytes, CMP_GLOBAL char cmpBlock[8], const void* options = NULL) { +// prototype code +int CMP_CDECL CompressBlockBC4S(const char* srcBlock, unsigned int srcStrideInBytes, CMP_GLOBAL unsigned char cmpBlock[8], const void* options = NULL) +{ char inBlock[16]; //---------------------------------- // Fill the inBlock with source data //---------------------------------- CGU_INT srcpos = 0; CGU_INT dstptr = 0; - for (CGU_UINT8 row = 0; row < 4; row++) { + for (CGU_UINT8 row = 0; row < 4; row++) + { srcpos = row * srcStrideInBytes; - for (CGU_UINT8 col = 0; col < 4; col++) { - inBlock[dstptr++] = CGU_UINT8(srcBlock[srcpos++]); + for (CGU_UINT8 col = 0; col < 4; col++) + { + inBlock[dstptr++] = CGU_INT8(srcBlock[srcpos++]); } } CMP_BC15Options* BC15options = (CMP_BC15Options*)options; - if (BC15options == NULL) { + if (BC15options == NULL) + { CMP_BC15Options BC15optionsDefault; BC15options = &BC15optionsDefault; SetDefaultBC15Options(BC15options); } - CompressBlockBC4S_SingleChannel(inBlock, (CMP_GLOBAL CGU_INT32*)cmpBlock, BC15options); + CompressBlockBC4S_SingleChannel(inBlock, (CMP_GLOBAL CGU_UINT32*)cmpBlock, BC15options); return CGU_CORE_OK; } -int CMP_CDECL DecompressBlockBC4S(const char cmpBlock[8], CMP_GLOBAL char srcBlock[16], const void* options = NULL) { +// prototype code +int CMP_CDECL DecompressBlockBC4S(const unsigned char cmpBlock[8], CMP_GLOBAL char srcBlock[16], const void* options = NULL) +{ CMP_BC15Options* BC15options = (CMP_BC15Options*)options; CMP_BC15Options BC15optionsDefault; - if (BC15options == NULL) { + if (BC15options == NULL) + { BC15options = &BC15optionsDefault; SetDefaultBC15Options(BC15options); } - DecompressBlockBC4S_SingleChannel(srcBlock, (CGU_INT32*)cmpBlock, BC15options); + DecompressBlockBC4S_SingleChannel((CGU_INT8*)srcBlock, (CGU_UINT32*)cmpBlock, BC15options); return CGU_CORE_OK; } -int CMP_CDECL CompressBlockBC4(const unsigned char *srcBlock, - unsigned int srcStrideInBytes, - CMP_GLOBAL unsigned char cmpBlock[8], - const void *options = NULL) { - +int CMP_CDECL CompressBlockBC4(const unsigned char* srcBlock, unsigned int srcStrideInBytes, CMP_GLOBAL unsigned char cmpBlock[8], const void* options = NULL) +{ CMP_BC15Options* BC15options = (CMP_BC15Options*)options; - if (BC15options == NULL) { + if (BC15options == NULL) + { CMP_BC15Options BC15optionsDefault; BC15options = &BC15optionsDefault; SetDefaultBC15Options(BC15options); } - if (BC15options->m_bIsSRGB) - return CompressBlockBC4S((const char*) srcBlock, srcStrideInBytes,(char*) cmpBlock, options); - unsigned char inBlock[16]; //---------------------------------- // Fill the inBlock with source data //---------------------------------- CGU_INT srcpos = 0; CGU_INT dstptr = 0; - for (CGU_UINT8 row = 0; row < 4; row++) { + for (CGU_UINT8 row = 0; row < 4; row++) + { srcpos = row * srcStrideInBytes; - for (CGU_UINT8 col = 0; col < 4; col++) { + for (CGU_UINT8 col = 0; col < 4; col++) + { inBlock[dstptr++] = CGU_UINT8(srcBlock[srcpos++]); } } - - CompressBlockBC4_SingleChannel(inBlock,(CMP_GLOBAL CGU_UINT32 *)cmpBlock, BC15options); + CompressBlockBC4_SingleChannel(inBlock, (CMP_GLOBAL CGU_UINT32*)cmpBlock, BC15options); return CGU_CORE_OK; } -int CMP_CDECL DecompressBlockBC4(const unsigned char cmpBlock[8], - CMP_GLOBAL unsigned char srcBlock[16], - const void *options = NULL) { - CMP_BC15Options *BC15options = (CMP_BC15Options *)options; - CMP_BC15Options BC15optionsDefault; - if (BC15options == NULL) { +int CMP_CDECL DecompressBlockBC4(const unsigned char cmpBlock[8], CMP_GLOBAL unsigned char srcBlock[16], const void* options = NULL) +{ + CMP_BC15Options* BC15options = (CMP_BC15Options*)options; + CMP_BC15Options BC15optionsDefault; + if (BC15options == NULL) + { BC15options = &BC15optionsDefault; SetDefaultBC15Options(BC15options); } - if (BC15options->m_bIsSRGB) - return DecompressBlockBC4S((const char *)cmpBlock, (char *)srcBlock, options); - - DecompressBlockBC4_SingleChannel(srcBlock, (CGU_UINT32 *)cmpBlock,BC15options); + DecompressBlockBC4_SingleChannel(srcBlock, (CGU_UINT32*)cmpBlock, BC15options); return CGU_CORE_OK; } - - #endif //============================================== OpenCL USER INTERFACE ==================================================== #ifdef ASPM_OPENCL -CMP_STATIC CMP_KERNEL void CMP_GPUEncoder( - CMP_GLOBAL const CMP_Vec4uc *ImageSource, - CMP_GLOBAL CGU_UINT8 *ImageDestination, CMP_GLOBAL Source_Info *SourceInfo, - CMP_GLOBAL CMP_BC15Options *BC15options) { +CMP_STATIC CMP_KERNEL void CMP_GPUEncoder(CMP_GLOBAL const CMP_Vec4uc* ImageSource, + CMP_GLOBAL CGU_UINT8* ImageDestination, + CMP_GLOBAL Source_Info* SourceInfo, + CMP_GLOBAL CMP_BC15Options* BC15options) +{ CGU_UINT32 xID; CGU_UINT32 yID; @@ -259,24 +305,27 @@ CMP_STATIC CMP_KERNEL void CMP_GPUEncoder( yID = 0; #endif - if (xID >= (SourceInfo->m_src_width / BlockX)) return; - if (yID >= (SourceInfo->m_src_height / BlockX)) return; + if (xID >= (SourceInfo->m_src_width / BlockX)) + return; + if (yID >= (SourceInfo->m_src_height / BlockX)) + return; int srcWidth = SourceInfo->m_src_width; - CGU_UINT32 destI = - (xID * BC4CompBlockSize) + (yID * (srcWidth / BlockX) * BC4CompBlockSize); - int srcindex = 4 * (yID * srcWidth + xID); - int blkindex = 0; + CGU_UINT32 destI = (xID * BC4CompBlockSize) + (yID * (srcWidth / BlockX) * BC4CompBlockSize); + int srcindex = 4 * (yID * srcWidth + xID); + int blkindex = 0; CMP_Vec4uc srcData[16]; srcWidth = srcWidth - 4; - for (CGU_INT32 j = 0; j < 4; j++) { - for (CGU_INT32 i = 0; i < 4; i++) { + for (CGU_INT32 j = 0; j < 4; j++) + { + for (CGU_INT32 i = 0; i < 4; i++) + { srcData[blkindex++] = ImageSource[srcindex++]; } srcindex += srcWidth; } - CompressBlockBC4_Internal(srcData, (CMP_GLOBAL CGU_UINT32 *)&ImageDestination[destI], BC15options); + CompressBlockBC4_Internal(srcData, (CMP_GLOBAL CGU_UINT32*)&ImageDestination[destI], BC15options); } #endif diff --git a/cmp_core/shaders/bc5_encode_kernel.cpp b/cmp_core/shaders/bc5_encode_kernel.cpp index 8006e4271..dfd81be4c 100644 --- a/cmp_core/shaders/bc5_encode_kernel.cpp +++ b/cmp_core/shaders/bc5_encode_kernel.cpp @@ -24,35 +24,83 @@ //============================================== BC5 INTERFACES ======================================================= -CGU_Vec4ui CompressBC5Block_Internal(CMP_IN CGU_FLOAT aBlockU[16], CMP_IN CGU_FLOAT aBlockV[16], CMP_IN CGU_FLOAT fquality) { +// Channel data aBlockU and aBlockV is either 0..1 or -1..1 +CGU_Vec4ui CompressBC5Block_Internal(CMP_IN CGU_FLOAT aBlockU[16], + CMP_IN CGU_FLOAT aBlockV[16], + CMP_IN CGU_FLOAT fquality, + CMP_IN CGU_BOOL isSNorm) +{ CGU_Vec4ui compBlock; CGU_Vec2ui cmpBlock; - cmpBlock = cmp_compressAlphaBlock(aBlockU, fquality, FALSE); + cmpBlock = cmp_compressAlphaBlock(aBlockU, fquality, isSNorm); compBlock.x = cmpBlock.x; compBlock.y = cmpBlock.y; - cmpBlock = cmp_compressAlphaBlock(aBlockV, fquality, FALSE); + cmpBlock = cmp_compressAlphaBlock(aBlockV, fquality, isSNorm); compBlock.z = cmpBlock.x; compBlock.w = cmpBlock.y; return compBlock; } #ifndef ASPM_HLSL -void CompressBlockBC5_Internal(CMP_Vec4uc srcBlockTemp[16], // range 0 to 255 +// rcBlockTemp[] is range 0 to 255 +void CompressBlockBC5_Internal(CMP_Vec4uc srcBlockTemp[16], CMP_GLOBAL CGU_UINT32 compressedBlock[4], CMP_GLOBAL CMP_BC15Options *BC15options) { CGU_Vec4ui cmpBlock; CGU_FLOAT alphaBlockU[16]; CGU_FLOAT alphaBlockV[16]; - CGU_UINT32 i; - for (i = 0; i < 16; i++) { - alphaBlockU[i] = srcBlockTemp[i].x / 255.0f; - alphaBlockV[i] = srcBlockTemp[i].y / 255.0f; + if (BC15options->m_bIsSNORM) + { + if (BC15options->m_sintsrc) + { + // Convert UINT (carrier of signed ) -> SINT -> SNORM + for (int i = 0; i < BLOCK_SIZE_4X4; i++) + { + char x = (char)(srcBlockTemp[i].x); + char y = (char)(srcBlockTemp[i].y); + alphaBlockU[i] = x / 127.0f; + alphaBlockV[i] = y / 127.0f; + } + } + else + { + // Convert UINT -> SNORM + for (int i = 0; i < BLOCK_SIZE_4X4; i++) + { + alphaBlockU[i] = ((srcBlockTemp[i].x / 255.0f) * 2.0f - 1.0f); + alphaBlockV[i] = ((srcBlockTemp[i].y / 255.0f) * 2.0f - 1.0f); + } + } } + else + { + // Convert SINT -> UNORM + if (BC15options->m_sintsrc) + { + for (int i = 0; i < BLOCK_SIZE_4X4; i++) + { + char x = (char)(srcBlockTemp[i].x); + char y = (char)(srcBlockTemp[i].y); + alphaBlockU[i] = (((x /127.0f) * 0.5f) + 0.5f); + alphaBlockV[i] = (((y /127.0f) * 0.5f) + 0.5f); + } + } + else { + // Convert UINT -> UNORM + for (int i = 0; i < BLOCK_SIZE_4X4; i++) + { + alphaBlockU[i] = srcBlockTemp[i].x / 255.0f; + alphaBlockV[i] = srcBlockTemp[i].y / 255.0f; + } + } + } + + - cmpBlock = CompressBC5Block_Internal(alphaBlockU, alphaBlockV,BC15options->m_fquality); + cmpBlock = CompressBC5Block_Internal(alphaBlockU, alphaBlockV, BC15options->m_fquality, BC15options->m_bIsSNORM); compressedBlock[0] = cmpBlock.x; compressedBlock[1] = cmpBlock.y; compressedBlock[2] = cmpBlock.z; @@ -97,16 +145,16 @@ void DecompressBC5_Internal(CMP_GLOBAL CGU_UINT8 rgbaBlock[64], } -void CompressBlockBC5_DualChannel_Internal(const CGU_UINT8 srcBlockR[16], - const CGU_UINT8 srcBlockG[16], +void CompressBlockBC5_DualChannel_Internal(const CGU_UINT8 srcBlockR[BLOCK_SIZE_4X4], + const CGU_UINT8 srcBlockG[BLOCK_SIZE_4X4], CMP_GLOBAL CGU_UINT32 compressedBlock[4], CMP_GLOBAL const CMP_BC15Options *BC15options) { if (BC15options) {} - CGU_Vec2ui cmpBlock; - CGU_FLOAT srcAlphaRF[16]; - CGU_FLOAT srcAlphaGF[16]; + CGU_Vec2ui cmpBlock; + CGU_FLOAT srcAlphaRF[BLOCK_SIZE_4X4]; + CGU_FLOAT srcAlphaGF[BLOCK_SIZE_4X4]; - for (CGU_INT i =0; i< 16; i++) { + for (CGU_INT i = 0; i < BLOCK_SIZE_4X4; i++) { srcAlphaRF[i] = (CGU_FLOAT)( srcBlockR[i] ) / 255.0f; srcAlphaGF[i] = (CGU_FLOAT)( srcBlockG[i] ) / 255.0f; } @@ -130,33 +178,33 @@ void DecompressBC5_DualChannel_Internal(CMP_GLOBAL CGU_UINT8 srcBlockR[16], } -void CompressBlockBC5S_DualChannel_Internal(const CGU_INT8 srcBlockR[16], +void CompressBlockBC5S_DualChannel_Internal(const CGU_INT8 srcBlockR[BLOCK_SIZE_4X4], const CGU_INT8 srcBlockG[16], - CMP_GLOBAL CGU_INT32 compressedBlock[4], + CMP_GLOBAL CGU_UINT32 compressedBlock[4], CMP_GLOBAL const CMP_BC15Options* BC15options) { if (BC15options) { } CGU_Vec2ui cmpBlock; - CGU_FLOAT srcAlphaRF[16]; - CGU_FLOAT srcAlphaGF[16]; + CGU_FLOAT srcAlphaRF[BLOCK_SIZE_4X4]; + CGU_FLOAT srcAlphaGF[BLOCK_SIZE_4X4]; - for (CGU_INT i = 0; i < 16; i++) { - srcAlphaRF[i] = (CGU_FLOAT)(srcBlockR[i]) / 255.0f; - srcAlphaGF[i] = (CGU_FLOAT)(srcBlockG[i]) / 255.0f; + for (CGU_INT i = 0; i < BLOCK_SIZE_4X4; i++) { + srcAlphaRF[i] = (CGU_FLOAT)(srcBlockR[i])/127.0f; + srcAlphaGF[i] = (CGU_FLOAT)(srcBlockG[i])/127.0f; } - cmpBlock = cmp_compressAlphaBlock(srcAlphaRF, BC15options->m_fquality, FALSE); + cmpBlock = cmp_compressAlphaBlock(srcAlphaRF, BC15options->m_fquality, TRUE); compressedBlock[0] = cmpBlock.x; compressedBlock[1] = cmpBlock.y; - cmpBlock = cmp_compressAlphaBlock(srcAlphaGF, BC15options->m_fquality, FALSE); + cmpBlock = cmp_compressAlphaBlock(srcAlphaGF, BC15options->m_fquality, TRUE); compressedBlock[2] = cmpBlock.x; compressedBlock[3] = cmpBlock.y; } void DecompressBC5S_DualChannel_Internal(CMP_GLOBAL CGU_INT8 srcBlockR[16], CMP_GLOBAL CGU_INT8 srcBlockG[16], - const CGU_INT32 compressedBlock[4], + const CGU_UINT32 compressedBlock[4], const CMP_BC15Options* BC15options) { if (BC15options) { } @@ -198,15 +246,6 @@ int CMP_CDECL SetQualityBC5(void *options, return CGU_CORE_OK; } -int CMP_CDECL SetSignedBC5(void *options, - CGU_BOOL snorm) { - if (!options) return CGU_CORE_ERR_INVALIDPTR; - CMP_BC15Options *BC15optionsDefault = reinterpret_cast (options); - - BC15optionsDefault->m_bIsSNORM = snorm; - return CGU_CORE_OK; -} - int CMP_CDECL CompressBlockBC5(const CGU_UINT8 *srcBlockR, unsigned int srcStrideInBytes1, const CGU_UINT8 *srcBlockG, @@ -268,11 +307,12 @@ int CMP_CDECL DecompressBlockBC5(const CGU_UINT8 cmpBlock[16], return CGU_CORE_OK; } +// prototype code int CMP_CDECL CompressBlockBC5S(const CGU_INT8* srcBlockR, unsigned int srcStrideInBytes1, const CGU_INT8* srcBlockG, unsigned int srcStrideInBytes2, - CMP_GLOBAL CGU_INT8 cmpBlock[16], + CMP_GLOBAL CGU_UINT8 cmpBlock[16], const void* options = NULL) { CGU_INT8 inBlockR[16]; @@ -284,7 +324,7 @@ int CMP_CDECL CompressBlockBC5S(const CGU_INT8* srcBlockR, for (CGU_UINT8 row = 0; row < 4; row++) { srcpos = row * srcStrideInBytes1; for (CGU_UINT8 col = 0; col < 4; col++) { - inBlockR[dstptr++] = CGU_UINT8(srcBlockR[srcpos++]); + inBlockR[dstptr++] = CGU_INT8(srcBlockR[srcpos++]); } } @@ -297,7 +337,7 @@ int CMP_CDECL CompressBlockBC5S(const CGU_INT8* srcBlockR, for (CGU_UINT8 row = 0; row < 4; row++) { srcpos = row * srcStrideInBytes2; for (CGU_UINT8 col = 0; col < 4; col++) { - inBlockG[dstptr++] = CGU_UINT8(srcBlockG[srcpos++]); + inBlockG[dstptr++] = CGU_INT8(srcBlockG[srcpos++]); } } @@ -308,11 +348,12 @@ int CMP_CDECL CompressBlockBC5S(const CGU_INT8* srcBlockR, SetDefaultBC15Options(BC15options); } - CompressBlockBC5S_DualChannel_Internal(inBlockR, inBlockG, (CMP_GLOBAL CGU_INT32*)cmpBlock, BC15options); + CompressBlockBC5S_DualChannel_Internal(inBlockR, inBlockG, (CMP_GLOBAL CGU_UINT32*)cmpBlock, BC15options); return CGU_CORE_OK; } -int CMP_CDECL DecompressBlockBC5S(const CGU_INT8 cmpBlock[16], +// prototype code +int CMP_CDECL DecompressBlockBC5S(const CGU_UINT8 cmpBlock[16], CMP_GLOBAL CGU_INT8 srcBlockR[16], CMP_GLOBAL CGU_INT8 srcBlockG[16], const void* options = NULL) { @@ -322,7 +363,7 @@ int CMP_CDECL DecompressBlockBC5S(const CGU_INT8 cmpBlock[16], BC15options = &BC15optionsDefault; SetDefaultBC15Options(BC15options); } - DecompressBC5S_DualChannel_Internal(srcBlockR, srcBlockG, (CGU_INT32*)cmpBlock, BC15options); + DecompressBC5S_DualChannel_Internal(srcBlockR, srcBlockG, (CGU_UINT32*)cmpBlock, BC15options); return CGU_CORE_OK; } diff --git a/cmp_core/shaders/bc6_encode_kernel.cpp b/cmp_core/shaders/bc6_encode_kernel.cpp index 7d4794e6c..7afd139ff 100644 --- a/cmp_core/shaders/bc6_encode_kernel.cpp +++ b/cmp_core/shaders/bc6_encode_kernel.cpp @@ -358,7 +358,7 @@ void eigenVector_d(CGU_FLOAT cov[MAX_DIMENSION_BIG][MAX_DIMENSION_BIG], CGU_FLOA for (j = 0; j < dimension; j++) c[0][i][j] = cov[i][j]; - p = (int)floorf(log((FLT_MAX_EXP - EV_SLACK) / ceilf(logf((CGU_FLOAT)dimension) / logf(2.0f))) / logf(2.0f)); + p = (int)floorf(log((BC6_FLT_MAX_EXP - EV_SLACK) / ceilf(logf((CGU_FLOAT)dimension) / logf(2.0f))) / logf(2.0f)); //assert(p>0); @@ -2176,10 +2176,18 @@ void SwapIndices(CGU_INT32 iEndPoints[MAX_SUBSETS][MAX_END_POINTS][MAX_DIMENSION size_t i = subset ? g_Region2FixUp[shape_pattern] : 0; if (iIndices[subset][i] & uHighIndexBit) { + +#ifdef ASPM_GPU + // high bit is set, swap the aEndPts and indices for this region + swap(iEndPoints[subset][0][0], iEndPoints[subset][1][0]); + swap(iEndPoints[subset][0][1], iEndPoints[subset][1][1]); + swap(iEndPoints[subset][0][2], iEndPoints[subset][1][2]); +#else // high bit is set, swap the aEndPts and indices for this region std::swap(iEndPoints[subset][0][0], iEndPoints[subset][1][0]); std::swap(iEndPoints[subset][0][1], iEndPoints[subset][1][1]); std::swap(iEndPoints[subset][0][2], iEndPoints[subset][1][2]); +#endif for (size_t j = 0; j < (size_t)entryCount[subset]; ++j) { iIndices[subset][j] = uNumIndices - 1 - iIndices[subset][j]; @@ -3688,9 +3696,9 @@ CMP_STATIC CMP_KERNEL void CMP_GPUEncoder( srcidx = i * stride; for (CGU_INT j = 0; j < BlockY; j++) { BC6HEncode_local.din[i*BlockX + j][0] = (CGU_UINT16)(p_source_pixels[srcOffset + srcidx++]); - if (BC6HEncode_local.din[i*BlockX + j][0] < 0.00001 || isnan(BC6HEncode_local.din[i*BlockX + j][0])) { + if (BC6HEncode_local.din[i*BlockX + j][0] < 0.00001 || cmp_isnan(BC6HEncode_local.din[i*BlockX + j][0])) { if (BC6HEncode->m_isSigned) { - BC6HEncode_local.din[i*BlockX + j][0] = (isnan(BC6HEncode_local.din[i*BlockX + j][0])) ? F16NEGPREC_LIMIT_VAL : -BC6HEncode_local.din[i*BlockX + j][0]; + BC6HEncode_local.din[i*BlockX + j][0] = (cmp_isnan(BC6HEncode_local.din[i*BlockX + j][0])) ? F16NEGPREC_LIMIT_VAL : -BC6HEncode_local.din[i*BlockX + j][0]; if (BC6HEncode_local.din[i*BlockX + j][0] < F16NEGPREC_LIMIT_VAL) { BC6HEncode_local.din[i*BlockX + j][0] = F16NEGPREC_LIMIT_VAL; } @@ -3700,9 +3708,12 @@ CMP_STATIC CMP_KERNEL void CMP_GPUEncoder( BC6HEncode_local.din[i*BlockX + j][1] = (CGU_UINT16)(p_source_pixels[srcOffset + srcidx++]); - if (BC6HEncode_local.din[i*BlockX + j][1] < 0.00001 || isnan(BC6HEncode_local.din[i*BlockX + j][1])) { + if (BC6HEncode_local.din[i * BlockX + j][1] < 0.00001 || cmp_isnan(BC6HEncode_local.din[i * BlockX + j][1])) + { if (BC6HEncode->m_isSigned) { - BC6HEncode_local.din[i*BlockX + j][1] = (isnan(BC6HEncode_local.din[i*BlockX + j][1])) ? F16NEGPREC_LIMIT_VAL : -BC6HEncode_local.din[i*BlockX + j][1]; + if (BC6HEncode_local.din[i * BlockX + j][1] < 0.00001 || cmp_isnan(BC6HEncode_local.din[i * BlockX + j][1])) + (isnan(BC6HEncode_local.din[i * BlockX + j][1])) ? F16NEGPREC_LIMIT_VAL : -BC6HEncode_local.din[i * BlockX + j][1]; + BC6HEncode_local.din[i*BlockX + j][1] = (cmp_isnan(BC6HEncode_local.din[i*BlockX + j][1])) ? F16NEGPREC_LIMIT_VAL : -BC6HEncode_local.din[i*BlockX + j][1]; if (BC6HEncode_local.din[i*BlockX + j][1] < F16NEGPREC_LIMIT_VAL) { BC6HEncode_local.din[i*BlockX + j][1] = F16NEGPREC_LIMIT_VAL; } diff --git a/cmp_core/shaders/bc6_encode_kernel.h b/cmp_core/shaders/bc6_encode_kernel.h index 18c41ea80..7722a5134 100644 --- a/cmp_core/shaders/bc6_encode_kernel.h +++ b/cmp_core/shaders/bc6_encode_kernel.h @@ -40,6 +40,7 @@ #define MAX_PARTITIONS 64 // Maximum number of partition types #define MAX_ENTRIES 64 #define MAX_TRY 20 +#define BC6_FLT_MAX_EXP 128 #define MAX_PARTITIONS_TABLE (1+64+64) #define DIMENSION 4 diff --git a/cmp_core/shaders/bc7_encode_kernel.cpp b/cmp_core/shaders/bc7_encode_kernel.cpp index 6f53b81bf..352796200 100644 --- a/cmp_core/shaders/bc7_encode_kernel.cpp +++ b/cmp_core/shaders/bc7_encode_kernel.cpp @@ -895,7 +895,7 @@ void GetProjectedIndex( CGV_IMAGE img_diff = image_max-image_min; if (img_diff == 0.0f) return; - if (isnan(img_diff)) return; + if (cmp_isnan(img_diff)) return; image_s = (clusters-1)/img_diff; @@ -4658,14 +4658,6 @@ int CMP_CDECL SetAlphaOptionsBC7(void *options, CGU_BOOL imageNeedsAlpha, CGU_BO return CGU_CORE_OK; } -int CMP_CDECL SetGammaBC7(void *options, - CGU_BOOL sRGB) { - CMP_UNUSED(sRGB); - if (!options) return CGU_CORE_ERR_INVALIDPTR; - // BC7_Encode *BC7options = (BC7_Encode *)options; - // BC7options->sRGB = sRGB; // TODO: Implement this option - return CGU_CORE_ERR_UNKOWN; // Since it doesn't work today -} int CMP_CDECL CompressBlockBC7( const unsigned char *srcBlock, unsigned int srcStrideInBytes, diff --git a/cmp_core/shaders/bcn_common_kernel.h b/cmp_core/shaders/bcn_common_kernel.h index bd8daa7f7..dc5ec5cdb 100644 --- a/cmp_core/shaders/bcn_common_kernel.h +++ b/cmp_core/shaders/bcn_common_kernel.h @@ -21,6 +21,12 @@ // //===================================================================== +//===================================================================== +// Block-compression (BC) functionality ref +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +//===================================================================== + //************************************************************************************ // ** NOTE ** // Content and data types may change, use CMP_Core.h for interface to your application @@ -124,8 +130,10 @@ static CMP_CONSTANT CGU_FLOAT sMvF[] = {0.f, -1.f, 1.f, -2.f, 2.f, -3.f, 3.f, -4 #ifndef ASPM_GPU -typedef union { - struct colorblock64U { +typedef union +{ + struct colorblock64U + { CGU_UINT8 col0; CGU_UINT8 col1; CGU_UINT8 indices[6]; @@ -135,8 +143,10 @@ typedef union { CGU_UINT64 cmp_data64; } CMP_BLOCK64_UNORM; -typedef union { - struct colorblock64S { +typedef union +{ + struct colorblock64S + { CGU_UINT8 col0; CGU_UINT8 col1; CGU_UINT8 indices[6]; @@ -146,7 +156,8 @@ typedef union { CGU_UINT64 cmp_data64[2]; } CMP_BLOCK64_SNORM; -typedef union { +typedef union +{ CGU_INT8 cmp_data8[16]; CGU_INT32 cmp_data32[4]; CGU_UINT64 cmp_data64[2]; @@ -154,17 +165,20 @@ typedef union { #endif -typedef struct { +typedef struct +{ CGU_UINT32 data; CGU_UINT32 index; } CMP_di; -typedef struct { +typedef struct +{ CGU_FLOAT data; CGU_UINT32 index; } CMP_df; -typedef struct { +typedef struct +{ // user setable CGU_FLOAT m_fquality; CGU_FLOAT m_fChannelWeights[3]; @@ -173,8 +187,9 @@ typedef struct { CGU_BOOL m_bUseFloat; CGU_BOOL m_b3DRefinement; CGU_BOOL m_bUseAlpha; - CGU_BOOL m_bIsSRGB; // Use Linear to SRGB color conversion used in BC1, default is false - CGU_BOOL m_bIsSNORM; // Reserved for support in BC4&5, currently always false! + CGU_BOOL m_bIsSRGB; // Use Linear to SRGB color conversion used in BC1, default is false + CGU_BOOL m_bIsSNORM; + CGU_BOOL m_sintsrc; // source data pointer is signed data CGU_UINT32 m_nRefinementSteps; CGU_UINT32 m_nAlphaThreshold; CGU_BOOL m_mapDecodeRGBA; @@ -182,28 +197,33 @@ typedef struct { CGU_UINT32 m_src_height; } CMP_BC15Options; -typedef struct { +typedef struct +{ CGU_Vec3f Color0; CGU_Vec3f Color1; } CMP_EndPoints; // gets 2 bit values from a 32 bit variable at the kth index range (0..15) // same as get values (0..3) from CGU_UINT32 variable[16] -static CGU_UINT32 cmp_get2Bit32(CGU_UINT32 value, CGU_UINT32 indexPos) { +static CGU_UINT32 cmp_get2Bit32(CGU_UINT32 value, CGU_UINT32 indexPos) +{ return (value >> (indexPos * 2)) & 0x3; } // sets 2 bit values into a 32 bit variable // same as set values (0..3) to CGU_UINT32 variable[16] -static CGU_UINT32 cmp_set2Bit32(CGU_UINT32 value, CGU_UINT32 indexPos) { +static CGU_UINT32 cmp_set2Bit32(CGU_UINT32 value, CGU_UINT32 indexPos) +{ return ((value & 0x3) << (indexPos * 2)); } -static CGU_UINT32 cmp_constructColor(CGU_UINT32 R, CGU_UINT32 G, CGU_UINT32 B) { +static CGU_UINT32 cmp_constructColor(CGU_UINT32 R, CGU_UINT32 G, CGU_UINT32 B) +{ return (((R & 0x000000F8) << 8) | ((G & 0x000000FC) << 3) | ((B & 0x000000F8) >> 3)); } -static CGU_Vec3f cmp_powVec3f(CGU_Vec3f color, CGU_FLOAT ex) { +static CGU_Vec3f cmp_powVec3f(CGU_Vec3f color, CGU_FLOAT ex) +{ #ifdef ASPM_GPU return pow(color, ex); #else @@ -215,7 +235,8 @@ static CGU_Vec3f cmp_powVec3f(CGU_Vec3f color, CGU_FLOAT ex) { #endif } -static CGU_Vec3f cmp_clamp3f(CGU_Vec3f value, CGU_FLOAT minValue, CGU_FLOAT maxValue) { +static CGU_Vec3f cmp_clamp3f(CGU_Vec3f value, CGU_FLOAT minValue, CGU_FLOAT maxValue) +{ #ifdef ASPM_GPU return clamp(value, minValue, maxValue); #else @@ -238,7 +259,8 @@ static CGU_Vec3f cmp_clamp3f(CGU_Vec3f value, CGU_FLOAT minValue, CGU_FLOAT maxV #endif } -static CGU_Vec3f cmp_saturate(CGU_Vec3f value) { +static CGU_Vec3f cmp_saturate(CGU_Vec3f value) +{ #ifdef ASPM_HLSL return saturate(value); #else @@ -248,19 +270,22 @@ static CGU_Vec3f cmp_saturate(CGU_Vec3f value) { // Helper functions to cut precision of floats // Prec is a power of 10 value from 1,10,100,...,10000... INT MAX power 10 -static CGU_BOOL cmp_compareprecision(CGU_FLOAT f1, CGU_FLOAT f2, CGU_INT Prec) { +static CGU_BOOL cmp_compareprecision(CGU_FLOAT f1, CGU_FLOAT f2, CGU_INT Prec) +{ CGU_INT scale1 = (CGU_INT)(f1 * Prec); CGU_INT scale2 = (CGU_INT)(f2 * Prec); return (scale1 == scale2); } // Helper function to compare floats to a set precision -static CGU_FLOAT cmp_getfloatprecision(CGU_FLOAT f1, CGU_INT Prec) { +static CGU_FLOAT cmp_getfloatprecision(CGU_FLOAT f1, CGU_INT Prec) +{ CGU_INT scale1 = (CGU_INT)(f1 * Prec); return ((CGU_FLOAT)(scale1) / Prec); } -static CGU_FLOAT cmp_linearToSrgbf(CMP_IN CGU_FLOAT Color) { +static CGU_FLOAT cmp_linearToSrgbf(CMP_IN CGU_FLOAT Color) +{ if (Color <= 0.0f) return (0.0f); if (Color >= 1.0f) @@ -271,14 +296,16 @@ static CGU_FLOAT cmp_linearToSrgbf(CMP_IN CGU_FLOAT Color) { return (pow(Color, 1.0f / 2.4f) * 1.055f - 0.055f); } -static CGU_Vec3f cmp_linearToSrgb(CMP_IN CGU_Vec3f Color) { +static CGU_Vec3f cmp_linearToSrgb(CMP_IN CGU_Vec3f Color) +{ Color.x = cmp_linearToSrgbf(Color.x); Color.y = cmp_linearToSrgbf(Color.y); Color.z = cmp_linearToSrgbf(Color.z); return Color; } -static CGU_FLOAT cmp_srgbToLinearf(CMP_IN CGU_FLOAT Color) { +static CGU_FLOAT cmp_srgbToLinearf(CMP_IN CGU_FLOAT Color) +{ if (Color <= 0.0f) return (0.0f); if (Color >= 1.0f) @@ -289,14 +316,16 @@ static CGU_FLOAT cmp_srgbToLinearf(CMP_IN CGU_FLOAT Color) { return pow((Color + 0.055f) / 1.055f, 2.4f); } -static CGU_Vec3f cmp_srgbToLinear(CMP_IN CGU_Vec3f Color) { +static CGU_Vec3f cmp_srgbToLinear(CMP_IN CGU_Vec3f Color) +{ Color.x = cmp_srgbToLinearf(Color.x); Color.y = cmp_srgbToLinearf(Color.y); Color.z = cmp_srgbToLinearf(Color.z); return Color; } -inline CGU_Vec3f cmp_min3f(CMP_IN CGU_Vec3f value1, CMP_IN CGU_Vec3f value2) { +inline CGU_Vec3f cmp_min3f(CMP_IN CGU_Vec3f value1, CMP_IN CGU_Vec3f value2) +{ #ifdef ASPM_GPU return min(value1, value2); #else @@ -308,7 +337,8 @@ inline CGU_Vec3f cmp_min3f(CMP_IN CGU_Vec3f value1, CMP_IN CGU_Vec3f value2) { #endif } -inline CGU_Vec3f cmp_max3f(CMP_IN CGU_Vec3f value1, CMP_IN CGU_Vec3f value2) { +inline CGU_Vec3f cmp_max3f(CMP_IN CGU_Vec3f value1, CMP_IN CGU_Vec3f value2) +{ #ifdef ASPM_GPU return max(value1, value2); #else @@ -324,13 +354,15 @@ static CGU_FLOAT cmp_getIndicesRGB(CMP_INOUT CGU_UINT32 CMP_PTRINOUT cmpindex, const CGU_Vec3f block[16], CGU_Vec3f minColor, CGU_Vec3f maxColor, - CGU_BOOL getErr) { + CGU_BOOL getErr) +{ CGU_UINT32 PackedIndices = 0; CGU_FLOAT err = 0.0f; CGU_Vec3f cn[4]; CGU_FLOAT minDistance; - if (getErr) { + if (getErr) + { // remap to BC1 spec for decoding offsets, // where cn[0] > cn[1] Max Color = index 0, 2/3 offset =index 2, 1/3 offset = index 3, Min Color = index 1 cn[0] = maxColor; @@ -346,7 +378,8 @@ static CGU_FLOAT cmp_getIndicesRGB(CMP_INOUT CGU_UINT32 CMP_PTRINOUT cmpindex, CGU_UINT32 index; CGU_FLOAT diff; - for (CGU_UINT32 i = 0; i < 16; i++) { + for (CGU_UINT32 i = 0; i < 16; i++) + { // Get offset from base scale diff = dot(block[i], ScaledRange) + Bias; index = ((CGU_UINT32)round(diff)) & 0x3; @@ -355,7 +388,8 @@ static CGU_FLOAT cmp_getIndicesRGB(CMP_INOUT CGU_UINT32 CMP_PTRINOUT cmpindex, index = indexMap[index]; // use err calc for use in higher quality code - if (getErr) { + if (getErr) + { minDistance = dot(block[i] - cn[index], block[i] - cn[index]); err += minDistance; } @@ -375,8 +409,10 @@ static CGU_FLOAT cmp_getIndicesRGB(CMP_INOUT CGU_UINT32 CMP_PTRINOUT cmpindex, //---------------------------------------- Common Utility Code ------------------------------------------------------- #ifndef ASPM_GPU -static void SetDefaultBC15Options(CMP_BC15Options* BC15Options) { - if (BC15Options) { +static void SetDefaultBC15Options(CMP_BC15Options* BC15Options) +{ + if (BC15Options) + { BC15Options->m_fquality = 1.0f; BC15Options->m_bUseChannelWeighting = false; BC15Options->m_bUseAdaptiveWeighting = false; @@ -401,20 +437,24 @@ static void SetDefaultBC15Options(CMP_BC15Options* BC15Options) { } #endif -static CMP_BC15Options CalculateColourWeightings(CGU_Vec4f rgbaBlock[BLOCK_SIZE_4X4], CMP_BC15Options BC15options) { +static CMP_BC15Options CalculateColourWeightings(CGU_Vec4f rgbaBlock[BLOCK_SIZE_4X4], CMP_BC15Options BC15options) +{ CGU_FLOAT fBaseChannelWeights[3] = {0.3086f, 0.6094f, 0.0820f}; - if (!BC15options.m_bUseChannelWeighting) { + if (!BC15options.m_bUseChannelWeighting) + { BC15options.m_fChannelWeights[0] = 1.0F; BC15options.m_fChannelWeights[1] = 1.0F; BC15options.m_fChannelWeights[2] = 1.0F; return BC15options; } - if (BC15options.m_bUseAdaptiveWeighting) { + if (BC15options.m_bUseAdaptiveWeighting) + { float medianR = 0.0f, medianG = 0.0f, medianB = 0.0f; - for (CGU_UINT32 k = 0; k < BLOCK_SIZE_4X4; k++) { + for (CGU_UINT32 k = 0; k < BLOCK_SIZE_4X4; k++) + { medianR += rgbaBlock[k].x; medianG += rgbaBlock[k].y; medianB += rgbaBlock[k].z; @@ -427,11 +467,13 @@ static CMP_BC15Options CalculateColourWeightings(CGU_Vec4f rgbaBlock[BLOCK_SIZE_ // Now skew the colour weightings based on the gravity center of the block float largest = max(max(medianR, medianG), medianB); - if (largest > 0) { + if (largest > 0) + { medianR /= largest; medianG /= largest; medianB /= largest; - } else + } + else medianR = medianG = medianB = 1.0f; // Scale weightings back up to 1.0f @@ -450,7 +492,9 @@ static CMP_BC15Options CalculateColourWeightings(CGU_Vec4f rgbaBlock[BLOCK_SIZE_ BC15options.m_fChannelWeights[0] *= fWeightScale; BC15options.m_fChannelWeights[1] *= fWeightScale; BC15options.m_fChannelWeights[2] *= fWeightScale; - } else { + } + else + { BC15options.m_fChannelWeights[0] = fBaseChannelWeights[0]; BC15options.m_fChannelWeights[1] = fBaseChannelWeights[1]; BC15options.m_fChannelWeights[2] = fBaseChannelWeights[2]; @@ -459,20 +503,24 @@ static CMP_BC15Options CalculateColourWeightings(CGU_Vec4f rgbaBlock[BLOCK_SIZE_ return BC15options; } -static CMP_BC15Options CalculateColourWeightings3f(CGU_Vec3f rgbBlock[BLOCK_SIZE_4X4], CMP_BC15Options BC15options) { +static CMP_BC15Options CalculateColourWeightings3f(CGU_Vec3f rgbBlock[BLOCK_SIZE_4X4], CMP_BC15Options BC15options) +{ CGU_FLOAT fBaseChannelWeights[3] = {0.3086f, 0.6094f, 0.0820f}; - if (!BC15options.m_bUseChannelWeighting) { + if (!BC15options.m_bUseChannelWeighting) + { BC15options.m_fChannelWeights[0] = 1.0F; BC15options.m_fChannelWeights[1] = 1.0F; BC15options.m_fChannelWeights[2] = 1.0F; return BC15options; } - if (BC15options.m_bUseAdaptiveWeighting) { + if (BC15options.m_bUseAdaptiveWeighting) + { float medianR = 0.0f, medianG = 0.0f, medianB = 0.0f; - for (CGU_UINT32 k = 0; k < BLOCK_SIZE_4X4; k++) { + for (CGU_UINT32 k = 0; k < BLOCK_SIZE_4X4; k++) + { medianR += rgbBlock[k].x; medianG += rgbBlock[k].y; medianB += rgbBlock[k].z; @@ -485,11 +533,13 @@ static CMP_BC15Options CalculateColourWeightings3f(CGU_Vec3f rgbBlock[BLOCK_SIZE // Now skew the colour weightings based on the gravity center of the block float largest = max(max(medianR, medianG), medianB); - if (largest > 0) { + if (largest > 0) + { medianR /= largest; medianG /= largest; medianB /= largest; - } else + } + else medianR = medianG = medianB = 1.0f; // Scale weightings back up to 1.0f @@ -508,7 +558,9 @@ static CMP_BC15Options CalculateColourWeightings3f(CGU_Vec3f rgbBlock[BLOCK_SIZE BC15options.m_fChannelWeights[0] *= fWeightScale; BC15options.m_fChannelWeights[1] *= fWeightScale; BC15options.m_fChannelWeights[2] *= fWeightScale; - } else { + } + else + { BC15options.m_fChannelWeights[0] = fBaseChannelWeights[0]; BC15options.m_fChannelWeights[1] = fBaseChannelWeights[1]; BC15options.m_fChannelWeights[2] = fBaseChannelWeights[2]; @@ -523,13 +575,15 @@ static CGU_FLOAT cmp_getRampErr(CGU_FLOAT Prj[BLOCK_SIZE_4X4], CGU_FLOAT StepErr, CGU_FLOAT lowPosStep, CGU_FLOAT highPosStep, - CGU_UINT32 dwUniqueColors) { + CGU_UINT32 dwUniqueColors) +{ CGU_FLOAT error = 0; CGU_FLOAT step = (highPosStep - lowPosStep) / 3; // using (dwNumChannels=4 - 1); CGU_FLOAT step_h = step * (CGU_FLOAT)0.5; CGU_FLOAT rstep = (CGU_FLOAT)1.0f / step; - for (CGU_UINT32 i = 0; i < dwUniqueColors; i++) { + for (CGU_UINT32 i = 0; i < dwUniqueColors; i++) + { CGU_FLOAT v; // Work out which value in the block this select CGU_FLOAT del; @@ -546,7 +600,8 @@ static CGU_FLOAT cmp_getRampErr(CGU_FLOAT Prj[BLOCK_SIZE_4X4], d *= d; CGU_FLOAT err = PreMRep[i] * d + PrjErr[i]; error += err; - if (StepErr < error) { + if (StepErr < error) + { error = StepErr; break; } @@ -554,10 +609,12 @@ static CGU_FLOAT cmp_getRampErr(CGU_FLOAT Prj[BLOCK_SIZE_4X4], return error; } -static CGU_Vec2ui cmp_compressExplicitAlphaBlock(const CGU_FLOAT AlphaBlockUV[16]) { +static CGU_Vec2ui cmp_compressExplicitAlphaBlock(const CGU_FLOAT AlphaBlockUV[16]) +{ CGU_Vec2ui compBlock = {0, 0}; CGU_UINT8 i; - for (i = 0; i < 16; i++) { + for (i = 0; i < 16; i++) + { CGU_UINT8 v = (CGU_UINT8)(AlphaBlockUV[i] * 255.0F); v = (v + 7 - (v >> 4)); v >>= 4; @@ -580,14 +637,16 @@ static CGU_FLOAT cmp_getRampError(CGU_FLOAT _Blk[BLOCK_SIZE_4X4], CGU_FLOAT _maxerror, CGU_FLOAT _min_ex, CGU_FLOAT _max_ex, - CGU_INT _NmbrClrs) { // Max 16 + CGU_INT _NmbrClrs) +{ // Max 16 CGU_INT i; CGU_FLOAT error = 0; const CGU_FLOAT step = (_max_ex - _min_ex) / 7; // (CGU_FLOAT)(dwNumPoints - 1); const CGU_FLOAT step_h = step * 0.5f; const CGU_FLOAT rstep = 1.0f / step; - for (i = 0; i < _NmbrClrs; i++) { + for (i = 0; i < _NmbrClrs; i++) + { CGU_FLOAT v; // Work out which value in the block this select CGU_FLOAT del; @@ -604,7 +663,8 @@ static CGU_FLOAT cmp_getRampError(CGU_FLOAT _Blk[BLOCK_SIZE_4X4], error += del2 * del2 * _Rpt[i]; // if we've already lost to the previous step bail out - if (_maxerror < error) { + if (_maxerror < error) + { error = _maxerror; break; } @@ -620,7 +680,8 @@ static CGU_FLOAT cmp_linearBlockRefine(CGU_FLOAT _Blk[BLOCK_SIZE_4X4], CGU_FLOAT _m_step, CGU_FLOAT _min_bnd, CGU_FLOAT _max_bnd, - CGU_INT _NmbrClrs) { + CGU_INT _NmbrClrs) +{ // Start out assuming our endpoints are the min and max values we've // determined @@ -633,10 +694,12 @@ static CGU_FLOAT cmp_linearBlockRefine(CGU_FLOAT _Blk[BLOCK_SIZE_4X4], CGU_INT mode, bestmode; - do { + do + { CGU_FLOAT cr_min0 = min_ex; CGU_FLOAT cr_max0 = max_ex; - for (bestmode = -1, mode = 0; mode < SCH_STPS * SCH_STPS; mode++) { + for (bestmode = -1, mode = 0; mode < SCH_STPS * SCH_STPS; mode++) + { // check each move (see sStep for direction) CGU_FLOAT cr_min = min_ex + _m_step * sMvF[mode / SCH_STPS]; CGU_FLOAT cr_max = max_ex + _m_step * sMvF[mode % SCH_STPS]; @@ -647,7 +710,8 @@ static CGU_FLOAT cmp_linearBlockRefine(CGU_FLOAT _Blk[BLOCK_SIZE_4X4], CGU_FLOAT error; error = cmp_getRampError(_Blk, _Rpt, maxerror, cr_min, cr_max, _NmbrClrs); - if (error < maxerror) { + if (error < maxerror) + { maxerror = error; bestmode = mode; cr_min0 = cr_min; @@ -655,7 +719,8 @@ static CGU_FLOAT cmp_linearBlockRefine(CGU_FLOAT _Blk[BLOCK_SIZE_4X4], } } - if (bestmode != -1) { + if (bestmode != -1) + { // make move (see sStep for direction) min_ex = cr_min0; max_ex = cr_max0; @@ -668,7 +733,8 @@ static CGU_FLOAT cmp_linearBlockRefine(CGU_FLOAT _Blk[BLOCK_SIZE_4X4], return maxerror; } -static CGU_Vec2f cmp_getLinearEndPoints(CGU_FLOAT _Blk[BLOCK_SIZE_4X4], CMP_IN CGU_FLOAT fquality, CMP_IN CGU_BOOL isSigned) { +static CGU_Vec2f cmp_getLinearEndPoints(CGU_FLOAT _Blk[BLOCK_SIZE_4X4], CMP_IN CGU_FLOAT fquality, CMP_IN CGU_BOOL isSigned) +{ CGU_UINT32 i; CGU_Vec2f cmpMinMax; @@ -676,10 +742,12 @@ static CGU_Vec2f cmp_getLinearEndPoints(CGU_FLOAT _Blk[BLOCK_SIZE_4X4], CMP_IN C // Bounding Box // lowest quality calculation to get min and max value to use //================================================================ - if (fquality < CMP_QUALITY2) { + if (fquality < CMP_QUALITY2) + { cmpMinMax.x = _Blk[0]; cmpMinMax.y = _Blk[0]; - for (i = 1; i < BLOCK_SIZE_4X4; ++i) { + for (i = 1; i < BLOCK_SIZE_4X4; ++i) + { cmpMinMax.x = min(cmpMinMax.x, _Blk[i]); cmpMinMax.y = max(cmpMinMax.y, _Blk[i]); } @@ -710,13 +778,15 @@ static CGU_Vec2f cmp_getLinearEndPoints(CGU_FLOAT _Blk[BLOCK_SIZE_4X4], CMP_IN C #else CGU_UINT32 j; - for (i = 0; i < BLOCK_SIZE_4X4; i++) { + for (i = 0; i < BLOCK_SIZE_4X4; i++) + { fBlk[i] = _Blk[i]; } CMP_df what[BLOCK_SIZE]; - for (i = 0; i < BLOCK_SIZE_4X4; i++) { + for (i = 0; i < BLOCK_SIZE_4X4; i++) + { what[i].index = i; what[i].data = fBlk[i]; } @@ -724,9 +794,12 @@ static CGU_Vec2f cmp_getLinearEndPoints(CGU_FLOAT _Blk[BLOCK_SIZE_4X4], CMP_IN C CGU_UINT32 tmp_index; CGU_FLOAT tmp_data; - for (i = 1; i < BLOCK_SIZE_4X4; i++) { - for (j = i; j > 0; j--) { - if (what[j - 1].data > what[j].data) { + for (i = 1; i < BLOCK_SIZE_4X4; i++) + { + for (j = i; j > 0; j--) + { + if (what[j - 1].data > what[j].data) + { tmp_index = what[j].index; tmp_data = what[j].data; what[j].index = what[j - 1].index; @@ -738,7 +811,7 @@ static CGU_Vec2f cmp_getLinearEndPoints(CGU_FLOAT _Blk[BLOCK_SIZE_4X4], CMP_IN C } for (i = 0; i < BLOCK_SIZE_4X4; i++) - fBlk[i] = what[i].data; + fBlk[i] = what[i].data; #endif CGU_FLOAT new_p = -2.0f; @@ -749,17 +822,21 @@ static CGU_Vec2f cmp_getLinearEndPoints(CGU_FLOAT _Blk[BLOCK_SIZE_4X4], CMP_IN C { // Ramp not fixed - for (i = 0; i < BLOCK_SIZE_4X4; i++) { - if (new_p != fBlk[i]) { + for (i = 0; i < BLOCK_SIZE_4X4; i++) + { + if (new_p != fBlk[i]) + { afUniqueValues[dwUniqueValues] = new_p = fBlk[i]; afValueRepeats[dwUniqueValues] = 1.f; dwUniqueValues++; - } else if (dwUniqueValues) + } + else if (dwUniqueValues) afValueRepeats[dwUniqueValues - 1] += 1.f; } // if number of unique colors is less or eq 2, we've done - if (dwUniqueValues <= 2) { + if (dwUniqueValues <= 2) + { Ramp[0] = floor(afUniqueValues[0] * 255.0f + 0.5f); if (dwUniqueValues == 1) Ramp[1] = Ramp[0] + 1.f; @@ -769,7 +846,8 @@ static CGU_Vec2f cmp_getLinearEndPoints(CGU_FLOAT _Blk[BLOCK_SIZE_4X4], CMP_IN C } } // Ramp not fixed - if (requiresCalculation) { + if (requiresCalculation) + { CGU_FLOAT min_ex = afUniqueValues[0]; CGU_FLOAT max_ex = afUniqueValues[dwUniqueValues - 1]; CGU_FLOAT min_bnd = 0, max_bnd = 1.; @@ -783,7 +861,8 @@ static CGU_Vec2f cmp_getLinearEndPoints(CGU_FLOAT _Blk[BLOCK_SIZE_4X4], CMP_IN C // we won't get much better bool wantsSearch = !((max_ex - min_ex) <= (48.f / 256.0f)); - if (wantsSearch) { + if (wantsSearch) + { // Search. // 1. take the vicinities of both low and high bound of the input // interval. @@ -795,12 +874,15 @@ static CGU_Vec2f cmp_getLinearEndPoints(CGU_FLOAT _Blk[BLOCK_SIZE_4X4], CMP_IN C CGU_FLOAT gbl_lrb = (cntr < min_r + GBL_SCH_EXT) ? cntr : min_r + GBL_SCH_EXT; CGU_FLOAT gbl_rlb = (cntr > max_r - GBL_SCH_EXT) ? cntr : max_r - GBL_SCH_EXT; - for (CGU_FLOAT step_l = gbl_llb; step_l < gbl_lrb; step_l += GBL_SCH_STEP) { - for (CGU_FLOAT step_r = gbl_rrb; gbl_rlb <= step_r; step_r -= GBL_SCH_STEP) { + for (CGU_FLOAT step_l = gbl_llb; step_l < gbl_lrb; step_l += GBL_SCH_STEP) + { + for (CGU_FLOAT step_r = gbl_rrb; gbl_rlb <= step_r; step_r -= GBL_SCH_STEP) + { CGU_FLOAT sch_err; // an sse version is avaiable sch_err = cmp_getRampError(afUniqueValues, afValueRepeats, gbl_err, step_l, step_r, dwUniqueValues); - if (sch_err < gbl_err) { + if (sch_err < gbl_err) + { gbl_err = sch_err; gbl_l = step_l; gbl_r = step_r; @@ -828,7 +910,8 @@ static CGU_Vec2f cmp_getLinearEndPoints(CGU_FLOAT _Blk[BLOCK_SIZE_4X4], CMP_IN C // Ensure that the two endpoints are not the same // This is legal but serves no need & can break some optimizations in the compressor - if (Ramp[0] == Ramp[1]) { + if (Ramp[0] == Ramp[1]) + { if (Ramp[1] < 255.f) Ramp[1] = Ramp[1] + 1.0f; else if (Ramp[1] > 0.0f) @@ -841,7 +924,8 @@ static CGU_Vec2f cmp_getLinearEndPoints(CGU_FLOAT _Blk[BLOCK_SIZE_4X4], CMP_IN C return cmpMinMax; } -static CGU_Vec2ui cmp_getBlockPackedIndices(CGU_Vec2f RampMinMax, CGU_FLOAT alphaBlock[BLOCK_SIZE_4X4], CMP_IN CGU_FLOAT fquality) { +static CGU_Vec2ui cmp_getBlockPackedIndices(CGU_Vec2f RampMinMax, CGU_FLOAT alphaBlock[BLOCK_SIZE_4X4], CMP_IN CGU_FLOAT fquality) +{ CGU_UINT32 i; CGU_UINT32 j; CGU_Vec2ui cmpBlock = {0, 0}; @@ -849,7 +933,8 @@ static CGU_Vec2ui cmp_getBlockPackedIndices(CGU_Vec2f RampMinMax, CGU_FLOAT alph CGU_UINT32 MaxRampU; CGU_INT32 pcIndices[BLOCK_SIZE_4X4]; - if (fquality < CMP_QUALITY2) { + if (fquality < CMP_QUALITY2) + { CGU_FLOAT Range; CGU_FLOAT RampSteps; // segments into 0..7 sections CGU_FLOAT Bias; @@ -862,13 +947,19 @@ static CGU_Vec2ui cmp_getBlockPackedIndices(CGU_Vec2f RampMinMax, CGU_FLOAT alph RampSteps = 7.f / Range; // segments into 0..7 sections Bias = -RampSteps * RampMinMax.y; - for (i = 0; i < 16; ++i) { + for (i = 0; i < 16; ++i) + { pcIndices[i] = (CGU_UINT32)round(alphaBlock[i] * RampSteps + Bias); - if (i < 5) { + if (i < 5) + { pcIndices[i] += (pcIndices[i] > 0) - (7 * (pcIndices[i] == 7)); - } else if (i > 5) { + } + else if (i > 5) + { pcIndices[i] += (pcIndices[i] > 0) - (7 * (pcIndices[i] == 7 ? 1 : 0)); - } else { + } + else + { pcIndices[i] += (pcIndices[i] > 0) - (7 * (pcIndices[i] == 7)); } } @@ -878,17 +969,21 @@ static CGU_Vec2ui cmp_getBlockPackedIndices(CGU_Vec2f RampMinMax, CGU_FLOAT alph cmpBlock.x = (MinRampU << 8) | MaxRampU; cmpBlock.y = 0; - for (i = 0; i < 5; ++i) { + for (i = 0; i < 5; ++i) + { cmpBlock.x |= (pcIndices[i] << (16 + (i * 3))); } { cmpBlock.x |= (pcIndices[5] << 31); cmpBlock.y |= (pcIndices[5] >> 1); } - for (i = 6; i < BLOCK_SIZE_4X4; ++i) { + for (i = 6; i < BLOCK_SIZE_4X4; ++i) + { cmpBlock.y |= (pcIndices[i] << (i * 3 - 16)); } - } else { + } + else + { CGU_UINT32 epoint; CGU_FLOAT alpha[BLOCK_SIZE_4X4]; CGU_FLOAT OverIntFctr; @@ -903,7 +998,8 @@ static CGU_Vec2ui cmp_getBlockPackedIndices(CGU_Vec2f RampMinMax, CGU_FLOAT alph // GetRmp1 { - if (RampMinMax.x <= RampMinMax.y) { + if (RampMinMax.x <= RampMinMax.y) + { CGU_FLOAT t = RampMinMax.x; RampMinMax.x = RampMinMax.y; RampMinMax.y = t; @@ -928,7 +1024,8 @@ static CGU_Vec2ui cmp_getBlockPackedIndices(CGU_Vec2f RampMinMax, CGU_FLOAT alph } // BldRmp1 // FixedRamp - for (i = 0; i < CMP_ALPHA_RAMP; i++) { + for (i = 0; i < CMP_ALPHA_RAMP; i++) + { alpha[i] = floor(alpha[i] + 0.5f); } } // GetRmp1 @@ -940,12 +1037,15 @@ static CGU_Vec2ui cmp_getBlockPackedIndices(CGU_Vec2f RampMinMax, CGU_FLOAT alph // For each colour in the original block, calculate its weighted // distance from each point in the original and assign it // to the closest cluster - for (i = 0; i < BLOCK_SIZE_4X4; i++) { + for (i = 0; i < BLOCK_SIZE_4X4; i++) + { shortest = 10000000.f; - for (j = 0; j < CMP_ALPHA_RAMP; j++) { + for (j = 0; j < CMP_ALPHA_RAMP; j++) + { adist = (alphaBlock[i] - alpha[j]); adist *= adist; - if (adist < shortest) { + if (adist < shortest) + { shortest = adist; pcIndices[i] = j; } @@ -960,75 +1060,294 @@ static CGU_Vec2ui cmp_getBlockPackedIndices(CGU_Vec2f RampMinMax, CGU_FLOAT alph cmpBlock.x = (MaxRampU << 8) | MinRampU; cmpBlock.y = 0; - for (i = 0; i < 5; i++) { + for (i = 0; i < 5; i++) + { cmpBlock.x |= (pcIndices[i]) << (16 + (i * 3)); } { cmpBlock.x |= (pcIndices[5] & 0x1) << 31; cmpBlock.y |= (pcIndices[5] & 0x6) >> 1; } - for (i = 6; i < BLOCK_SIZE_4X4; i++) { + for (i = 6; i < BLOCK_SIZE_4X4; i++) + { cmpBlock.y |= (pcIndices[i]) << (i * 3 - 16); } } return cmpBlock; } -static CGU_Vec2ui cmp_compressAlphaBlock(CMP_IN CGU_FLOAT alphaBlock[BLOCK_SIZE_4X4], CMP_IN CGU_FLOAT fquality, CMP_IN CGU_BOOL isSigned) { - CGU_Vec2f RampMinMax; - CGU_Vec2ui CmpBlock; +//======================= SNORM CODE ================================== - RampMinMax = cmp_getLinearEndPoints(alphaBlock, fquality, isSigned); - CmpBlock = cmp_getBlockPackedIndices(RampMinMax, alphaBlock, fquality); - return CmpBlock; -} +static CGU_INT8 cmp_snormFloatToSInt(CGU_FLOAT fsnorm) +{ + if (isnan(fsnorm)) + fsnorm = 0; + else if (fsnorm > 1) + fsnorm = 1; // Clamp to 1 + else if (fsnorm < -1) + fsnorm = -1; // Clamp to -1 -static void cmp_getCompressedAlphaRamp(CGU_UINT8 alpha[8], const CGU_UINT32 compressedBlock[2]) { - alpha[0] = (CGU_UINT8)(compressedBlock[0] & 0xff); - alpha[1] = (CGU_UINT8)((compressedBlock[0] >> 8) & 0xff); + fsnorm = fsnorm * 127U; + + // shift round up or down + if (fsnorm >= 0) + fsnorm += .5f; + else + fsnorm -= .5f; - if (alpha[0] > alpha[1]) { - // 8-alpha block: derive the other six alphas. - // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated. #ifdef ASPM_GPU - alpha[2] = (CGU_UINT8)((6 * alpha[0] + 1 * alpha[1] + 3) / 7); // bit code 010 - alpha[3] = (CGU_UINT8)((5 * alpha[0] + 2 * alpha[1] + 3) / 7); // bit code 011 - alpha[4] = (CGU_UINT8)((4 * alpha[0] + 3 * alpha[1] + 3) / 7); // bit code 100 - alpha[5] = (CGU_UINT8)((3 * alpha[0] + 4 * alpha[1] + 3) / 7); // bit code 101 - alpha[6] = (CGU_UINT8)((2 * alpha[0] + 5 * alpha[1] + 3) / 7); // bit code 110 - alpha[7] = (CGU_UINT8)((1 * alpha[0] + 6 * alpha[1] + 3) / 7); // bit code 111 + CGU_INT8 res = (CGU_INT8)fsnorm; #else - alpha[2] = static_cast((6 * alpha[0] + 1 * alpha[1] + 3) / 7); // bit code 010 - alpha[3] = static_cast((5 * alpha[0] + 2 * alpha[1] + 3) / 7); // bit code 011 - alpha[4] = static_cast((4 * alpha[0] + 3 * alpha[1] + 3) / 7); // bit code 100 - alpha[5] = static_cast((3 * alpha[0] + 4 * alpha[1] + 3) / 7); // bit code 101 - alpha[6] = static_cast((2 * alpha[0] + 5 * alpha[1] + 3) / 7); // bit code 110 - alpha[7] = static_cast((1 * alpha[0] + 6 * alpha[1] + 3) / 7); // bit code 111 + CGU_INT8 res = static_cast(fsnorm); #endif - } else { - // 6-alpha block. - // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated. + return (res); +} + +static CGU_Vec2f cmp_optimizeEndPoints(CGU_FLOAT pPoints[BLOCK_SIZE_4X4], CGU_INT8 cSteps, CGU_BOOL isSigned) +{ + CGU_Vec2f fendpoints; + CGU_FLOAT MAX_VALUE = 1.0f; + CGU_FLOAT MIN_VALUE = isSigned ? -1.0f : 0.0f; + + // Find Min and Max points, as starting point + CGU_FLOAT fX = MAX_VALUE; + CGU_FLOAT fY = MIN_VALUE; + + if (8 == cSteps) + { + for (CGU_INT8 iPoint = 0; iPoint < BLOCK_SIZE_4X4; iPoint++) + { + if (pPoints[iPoint] < fX) + fX = pPoints[iPoint]; + + if (pPoints[iPoint] > fY) + fY = pPoints[iPoint]; + } + } + else + { + for (CGU_INT8 iPoint = 0; iPoint < BLOCK_SIZE_4X4; iPoint++) + { + if (pPoints[iPoint] < fX && pPoints[iPoint] > MIN_VALUE) + fX = pPoints[iPoint]; + + if (pPoints[iPoint] > fY && pPoints[iPoint] < MAX_VALUE) + fY = pPoints[iPoint]; + } + + if (fX == fY) + { + fY = MAX_VALUE; + } + } + + //=================== + // Use Newton Method + //=================== #ifdef ASPM_GPU - alpha[2] = (CGU_UINT8)((4 * alpha[0] + 1 * alpha[1] + 2) / 5); // Bit code 010 - alpha[3] = (CGU_UINT8)((3 * alpha[0] + 2 * alpha[1] + 2) / 5); // Bit code 011 - alpha[4] = (CGU_UINT8)((2 * alpha[0] + 3 * alpha[1] + 2) / 5); // Bit code 100 - alpha[5] = (CGU_UINT8)((1 * alpha[0] + 4 * alpha[1] + 2) / 5); // Bit code 101 + CGU_FLOAT cStepsDiv = (CGU_FLOAT)(cSteps - 1); #else - alpha[2] = static_cast((4 * alpha[0] + 1 * alpha[1] + 2) / 5); // Bit code 010 - alpha[3] = static_cast((3 * alpha[0] + 2 * alpha[1] + 2) / 5); // Bit code 011 - alpha[4] = static_cast((2 * alpha[0] + 3 * alpha[1] + 2) / 5); // Bit code 100 - alpha[5] = static_cast((1 * alpha[0] + 4 * alpha[1] + 2) / 5); // Bit code 101 + CGU_FLOAT cStepsDiv = static_cast(cSteps - 1); #endif - alpha[6] = 0; // Bit code 110 - alpha[7] = 255; // Bit code 111 + CGU_FLOAT pSteps[8]; + CGU_FLOAT fc; + CGU_FLOAT fd; + + for (CGU_INT8 iIteration = 0; iIteration < 8; iIteration++) + { + // reach minimum threashold break + if ((fY - fX) < (1.0f / 256.0f)) + break; + + CGU_FLOAT fScale = cStepsDiv / (fY - fX); + + // Calculate new steps + for (CGU_INT8 iStep = 0; iStep < cSteps; iStep++) + { + fc = (cStepsDiv - (CGU_FLOAT)iStep) / cStepsDiv; + fd = (CGU_FLOAT)iStep / cStepsDiv; + pSteps[iStep] = fc * fX + fd * fY; + } + + if (6 == cSteps) + { + pSteps[6] = MIN_VALUE; + pSteps[7] = MAX_VALUE; + } + + // Evaluate function, and derivatives + CGU_FLOAT dX = 0.0f; + CGU_FLOAT dY = 0.0f; + CGU_FLOAT d2X = 0.0f; + CGU_FLOAT d2Y = 0.0f; + + for (CGU_INT8 iPoint = 0; iPoint < BLOCK_SIZE_4X4; iPoint++) + { + CGU_FLOAT fDot = (pPoints[iPoint] - fX) * fScale; + + CGU_INT8 iStep; + if (fDot <= 0.0f) + { + iStep = ((6 == cSteps) && (pPoints[iPoint] <= (fX + MIN_VALUE) * 0.5f)) ? 6u : 0u; + } + else if (fDot >= cStepsDiv) + { + iStep = ((6 == cSteps) && (pPoints[iPoint] >= (fY + MAX_VALUE) * 0.5f)) ? 7u : (cSteps - 1); + } + else + { + iStep = (CGU_UINT32)(fDot + 0.5f); + } + + // steps to improve quality + if (iStep < cSteps) + { + fc = (cStepsDiv - (CGU_FLOAT)iStep) / cStepsDiv; + fd = (CGU_FLOAT)iStep / cStepsDiv; + CGU_FLOAT fDiff = pSteps[iStep] - pPoints[iPoint]; + dX += fc * fDiff; + d2X += fc * fc; + dY += fd * fDiff; + d2Y += fd * fd; + } + } + + // Move endpoints + if (d2X > 0.0f) + fX -= dX / d2X; + + if (d2Y > 0.0f) + fY -= dY / d2Y; + + if (fX > fY) + { + float f = fX; + fX = fY; + fY = f; + } + + if ((dX * dX < (1.0f / 64.0f)) && (dY * dY < (1.0f / 64.0f))) + break; + } + + fendpoints.x = (fX < MIN_VALUE) ? MIN_VALUE : (fX > MAX_VALUE) ? MAX_VALUE : fX; + fendpoints.y = (fY < MIN_VALUE) ? MIN_VALUE : (fY > MAX_VALUE) ? MAX_VALUE : fY; + + return fendpoints; +} + +static CGU_Vec2i cmp_findEndpointsAlphaBlockSnorm(CGU_FLOAT alphaBlockSnorm[BLOCK_SIZE_4X4]) +{ + //================================================================ + // Bounding Box + // lowest quality calculation to get min and max value to use + //================================================================ + CGU_Vec2f cmpMinMax; + cmpMinMax.x = alphaBlockSnorm[0]; + cmpMinMax.y = alphaBlockSnorm[0]; + + for (CGU_UINT8 i = 0; i < BLOCK_SIZE_4X4; ++i) + { + if (alphaBlockSnorm[i] < cmpMinMax.x) + { + cmpMinMax.x = alphaBlockSnorm[i]; + } + else if (alphaBlockSnorm[i] > cmpMinMax.y) + { + cmpMinMax.y = alphaBlockSnorm[i]; + } + } + + CGU_Vec2i endpoints; + CGU_Vec2f fendpoints; + + // Are we done for lowest quality setting! + // CGU_FLOAT fquality = 1.0f; + // + // if (fquality < CMP_QUALITY2) { + // endpoints.x = (CGU_INT8)(cmpMinMax.x); + // endpoints.y = (CGU_INT8)(cmpMinMax.y); + // return endpoints; + // } + + //================================================================ + // Do more calculations to get the best min and max values to use + //================================================================ + if ((-1.0f == cmpMinMax.x || 1.0f == cmpMinMax.y)) + { + fendpoints = cmp_optimizeEndPoints(alphaBlockSnorm, 6, true); + endpoints.x = cmp_snormFloatToSInt(fendpoints.x); + endpoints.y = cmp_snormFloatToSInt(fendpoints.y); + } + else + { + fendpoints = cmp_optimizeEndPoints(alphaBlockSnorm, 8, true); + endpoints.x = cmp_snormFloatToSInt(fendpoints.y); + endpoints.y = cmp_snormFloatToSInt(fendpoints.x); } + + return endpoints; } -static void cmp_getCompressedAlphaRampS(CGU_INT8 alpha[8], const CGU_INT32 compressedBlock[2]) { +#ifndef ASPM_HLSL +static CGU_UINT64 cmp_getBlockPackedIndicesSNorm(CGU_Vec2f alphaMinMax, CGU_FLOAT alphaBlockSnorm[BLOCK_SIZE_4X4], CGU_UINT64 data) +{ + CGU_FLOAT alpha[8]; + alpha[0] = alphaMinMax.x; + alpha[1] = alphaMinMax.y; + + if (alphaMinMax.x > alphaMinMax.y) + { + // 8-alpha block: derive the other six alphas. + // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated. + alpha[2] = (alpha[0] * 6.0f + alpha[1]) / 7.0f; + alpha[3] = (alpha[0] * 5.0f + alpha[1] * 2.0f) / 7.0f; + alpha[4] = (alpha[0] * 4.0f + alpha[1] * 3.0f) / 7.0f; + alpha[5] = (alpha[0] * 3.0f + alpha[1] * 4.0f) / 7.0f; + alpha[6] = (alpha[0] * 2.0f + alpha[1] * 5.0f) / 7.0f; + alpha[7] = (alpha[0] + alpha[1] * 6.0f) / 7.0f; + } + else + { + // 6-alpha block. + // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated. + alpha[2] = (alpha[0] * 4.0f + alpha[1]) / 5.0f; + alpha[3] = (alpha[0] * 3.0f + alpha[1] * 2.0f) / 5.0f; + alpha[4] = (alpha[0] * 2.0f + alpha[1] * 3.0f) / 5.0f; + alpha[5] = (alpha[0] + alpha[1] * 4.0f) / 5.0f; + alpha[6] = -1.0f; + alpha[7] = 1.0f; + } + + // Index all colors using best alpha value + for (CGU_UINT8 i = 0; i < BLOCK_SIZE_4X4; ++i) + { + CGU_UINT8 uBestIndex = 0; + CGU_FLOAT fBestDelta = CMP_FLOAT_MAX; + for (CGU_INT32 uIndex = 0; uIndex < 8; uIndex++) + { + CGU_FLOAT fCurrentDelta = fabs(alpha[uIndex] - alphaBlockSnorm[i]); + if (fCurrentDelta < fBestDelta) + { + uBestIndex = uIndex; + fBestDelta = fCurrentDelta; + } + } + + data &= ~((CGU_UINT64)(0x07) << (3 * i + 16)); + data |= ((CGU_UINT64)(uBestIndex) << (3 * i + 16)); + } + + return data; +} +#endif +static void cmp_getCompressedAlphaRampS(CGU_INT8 alpha[8], const CGU_UINT32 compressedBlock[2]) +{ alpha[0] = (CGU_INT8)(compressedBlock[0] & 0xff); alpha[1] = (CGU_INT8)((compressedBlock[0] >> 8) & 0xff); - if (alpha[0] > alpha[1]) { + if (alpha[0] > alpha[1]) + { // 8-alpha block: derive the other six alphas. // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated. #ifdef ASPM_GPU @@ -1046,7 +1365,9 @@ static void cmp_getCompressedAlphaRampS(CGU_INT8 alpha[8], const CGU_INT32 compr alpha[6] = static_cast((2 * alpha[0] + 5 * alpha[1] + 3) / 7); // bit code 110 alpha[7] = static_cast((1 * alpha[0] + 6 * alpha[1] + 3) / 7); // bit code 111 #endif - } else { + } + else + { // 6-alpha block. // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated. #ifdef ASPM_GPU @@ -1065,18 +1386,21 @@ static void cmp_getCompressedAlphaRampS(CGU_INT8 alpha[8], const CGU_INT32 compr } } -static void cmp_decompressAlphaBlock(CGU_UINT8 alphaBlock[BLOCK_SIZE_4X4], const CGU_UINT32 compressedBlock[2]) { +static void cmp_decompressAlphaBlockS(CGU_INT8 alphaBlock[BLOCK_SIZE_4X4], const CGU_UINT32 compressedBlock[2]) +{ CGU_UINT32 i; - CGU_UINT8 alpha[8]; - cmp_getCompressedAlphaRamp(alpha, compressedBlock); + CGU_INT8 alpha[8]; + cmp_getCompressedAlphaRampS(alpha, compressedBlock); - for (i = 0; i < BLOCK_SIZE_4X4; i++) { + for (i = 0; i < BLOCK_SIZE_4X4; i++) + { CGU_UINT32 index; if (i < 5) index = (compressedBlock[0] & (0x7 << (16 + (i * 3)))) >> (16 + (i * 3)); else if (i > 5) index = (compressedBlock[1] & (0x7 << (2 + (i - 6) * 3))) >> (2 + (i - 6) * 3); - else { + else + { index = (compressedBlock[0] & 0x80000000) >> 31; index |= (compressedBlock[1] & 0x3) << 1; } @@ -1085,18 +1409,130 @@ static void cmp_decompressAlphaBlock(CGU_UINT8 alphaBlock[BLOCK_SIZE_4X4], const } } -static void cmp_decompressAlphaBlockS(CGU_INT8 alphaBlock[BLOCK_SIZE_4X4], const CGU_INT32 compressedBlock[2]) { +//============================================================================= + +// Processes Alpha Channel either as Unsigned Norm (0..1) or (Signed Norm -1..1) +static CGU_Vec2ui cmp_compressAlphaBlock(CMP_IN CGU_FLOAT alphaBlock[BLOCK_SIZE_4X4], CMP_IN CGU_FLOAT fquality, CMP_IN CGU_BOOL isSigned) +{ + CGU_Vec2ui CmpBlock; + + if (isSigned) + { +#ifndef ASPM_HLSL + union + { + CGU_INT32 compressedBlock[2]; + struct + { + CGU_INT8 red_0; + CGU_INT8 red_1; + CGU_UINT8 indices[6]; + }; + CGU_UINT64 data; + } BC4_Snorm_block; + +#ifndef ASPM_GPU + BC4_Snorm_block.data = 0LL; +#else + BC4_Snorm_block.data = 0; +#endif + + CGU_Vec2i reds; + reds = cmp_findEndpointsAlphaBlockSnorm(alphaBlock); + + BC4_Snorm_block.red_0 = reds.x & 0xFF; + BC4_Snorm_block.red_1 = reds.y & 0xFF; + + // check low end boundaries + if (BC4_Snorm_block.red_0 == -128) + BC4_Snorm_block.red_0 = -127; + if (BC4_Snorm_block.red_1 == -128) + BC4_Snorm_block.red_1 = -127; + + // Normalize signed int -128..127 to float -1..1 + CGU_Vec2f alphaMinMax; + alphaMinMax.x = (CGU_FLOAT)(BC4_Snorm_block.red_0) / 127.0f; + alphaMinMax.y = (CGU_FLOAT)(BC4_Snorm_block.red_1) / 127.0f; + + BC4_Snorm_block.data = cmp_getBlockPackedIndicesSNorm(alphaMinMax, alphaBlock, BC4_Snorm_block.data); + CmpBlock.x = BC4_Snorm_block.compressedBlock[0]; + CmpBlock.y = BC4_Snorm_block.compressedBlock[1]; +#else + CGU_Vec2f RampMinMax; + RampMinMax = cmp_getLinearEndPoints(alphaBlock, fquality, false); // revert code to remove the false param + CmpBlock = cmp_getBlockPackedIndices(RampMinMax, alphaBlock, fquality); +#endif + } + else + { + CGU_Vec2f RampMinMax; + RampMinMax = cmp_getLinearEndPoints(alphaBlock, fquality, false); // revert code to remove the false param + CmpBlock = cmp_getBlockPackedIndices(RampMinMax, alphaBlock, fquality); + } + + return CmpBlock; +} + +static void cmp_getCompressedAlphaRamp(CGU_UINT8 alpha[8], const CGU_UINT32 compressedBlock[2]) +{ + alpha[0] = (CGU_UINT8)(compressedBlock[0] & 0xff); + alpha[1] = (CGU_UINT8)((compressedBlock[0] >> 8) & 0xff); + + if (alpha[0] > alpha[1]) + { + // 8-alpha block: derive the other six alphas. + // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated. +#ifdef ASPM_GPU + alpha[2] = (CGU_UINT8)((6 * alpha[0] + 1 * alpha[1] + 3) / 7); // bit code 010 + alpha[3] = (CGU_UINT8)((5 * alpha[0] + 2 * alpha[1] + 3) / 7); // bit code 011 + alpha[4] = (CGU_UINT8)((4 * alpha[0] + 3 * alpha[1] + 3) / 7); // bit code 100 + alpha[5] = (CGU_UINT8)((3 * alpha[0] + 4 * alpha[1] + 3) / 7); // bit code 101 + alpha[6] = (CGU_UINT8)((2 * alpha[0] + 5 * alpha[1] + 3) / 7); // bit code 110 + alpha[7] = (CGU_UINT8)((1 * alpha[0] + 6 * alpha[1] + 3) / 7); // bit code 111 +#else + alpha[2] = static_cast((6 * alpha[0] + 1 * alpha[1] + 3) / 7); // bit code 010 + alpha[3] = static_cast((5 * alpha[0] + 2 * alpha[1] + 3) / 7); // bit code 011 + alpha[4] = static_cast((4 * alpha[0] + 3 * alpha[1] + 3) / 7); // bit code 100 + alpha[5] = static_cast((3 * alpha[0] + 4 * alpha[1] + 3) / 7); // bit code 101 + alpha[6] = static_cast((2 * alpha[0] + 5 * alpha[1] + 3) / 7); // bit code 110 + alpha[7] = static_cast((1 * alpha[0] + 6 * alpha[1] + 3) / 7); // bit code 111 +#endif + } + else + { + // 6-alpha block. + // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated. +#ifdef ASPM_GPU + alpha[2] = (CGU_UINT8)((4 * alpha[0] + 1 * alpha[1] + 2) / 5); // Bit code 010 + alpha[3] = (CGU_UINT8)((3 * alpha[0] + 2 * alpha[1] + 2) / 5); // Bit code 011 + alpha[4] = (CGU_UINT8)((2 * alpha[0] + 3 * alpha[1] + 2) / 5); // Bit code 100 + alpha[5] = (CGU_UINT8)((1 * alpha[0] + 4 * alpha[1] + 2) / 5); // Bit code 101 +#else + alpha[2] = static_cast((4 * alpha[0] + 1 * alpha[1] + 2) / 5); // Bit code 010 + alpha[3] = static_cast((3 * alpha[0] + 2 * alpha[1] + 2) / 5); // Bit code 011 + alpha[4] = static_cast((2 * alpha[0] + 3 * alpha[1] + 2) / 5); // Bit code 100 + alpha[5] = static_cast((1 * alpha[0] + 4 * alpha[1] + 2) / 5); // Bit code 101 +#endif + alpha[6] = 0; // Bit code 110 + alpha[7] = 255; // Bit code 111 + } +} + +static void cmp_decompressAlphaBlock(CGU_UINT8 alphaBlock[BLOCK_SIZE_4X4], const CGU_UINT32 compressedBlock[2]) +{ CGU_UINT32 i; - CGU_INT8 alpha[8]; - cmp_getCompressedAlphaRampS(alpha, compressedBlock); + CGU_UINT8 alpha[8]; + cmp_getCompressedAlphaRamp(alpha, compressedBlock); - for (i = 0; i < BLOCK_SIZE_4X4; i++) { + for (i = 0; i < BLOCK_SIZE_4X4; i++) + { CGU_UINT32 index; if (i < 5) index = (compressedBlock[0] & (0x7 << (16 + (i * 3)))) >> (16 + (i * 3)); else if (i > 5) index = (compressedBlock[1] & (0x7 << (2 + (i - 6) * 3))) >> (2 + (i - 6) * 3); - else { + else + { index = (compressedBlock[0] & 0x80000000) >> 31; index |= (compressedBlock[1] & 0x3) << 1; } @@ -1105,7 +1541,8 @@ static void cmp_decompressAlphaBlockS(CGU_INT8 alphaBlock[BLOCK_SIZE_4X4], const } } -static CGU_Vec3f cmp_565ToLinear(CGU_UINT32 n565) { +static CGU_Vec3f cmp_565ToLinear(CGU_UINT32 n565) +{ CGU_UINT32 r0; CGU_UINT32 g0; CGU_UINT32 b0; @@ -1132,7 +1569,8 @@ static void cmp_ProcessColors(CMP_INOUT CGU_Vec3f CMP_PTRINOUT colorMin, CMP_INOUT CGU_UINT32 CMP_PTRINOUT c0, CMP_INOUT CGU_UINT32 CMP_PTRINOUT c1, CGU_INT setopt, - CGU_BOOL isSRGB) { + CGU_BOOL isSRGB) +{ // CGU_UINT32 srbMap[32] = {0,5,8,11,12,13,14,15,16,17,18,19,20,21,22,23,23,24,24,25,25,26,26,27,27,28,28,29,29,30,30,31}; // CGU_UINT32 sgMap[64] = {0,10,14,16,19,20,22,24,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,42,43,43,44,45,45, // 46,47,47,48,48,49,50,50,51,52,52,53,53,54,54,55,55,56,56,57,57,58,58,59,59,60,60,61,61,62,62,63,63}; @@ -1142,15 +1580,19 @@ static void cmp_ProcessColors(CMP_INOUT CGU_Vec3f CMP_PTRINOUT colorMin, CGU_Vec3f MaxColorScaled; // Clamp or Transform is needed, the transforms have built in clamps - if (isSRGB) { + if (isSRGB) + { MinColorScaled = cmp_linearToSrgb(CMP_PTRINOUT colorMin); MaxColorScaled = cmp_linearToSrgb(CMP_PTRINOUT colorMax); - } else { + } + else + { MinColorScaled = cmp_clamp3f(CMP_PTRINOUT colorMin, 0.0f, 1.0f); MaxColorScaled = cmp_clamp3f(CMP_PTRINOUT colorMax, 0.0f, 1.0f); } - switch (setopt) { + switch (setopt) + { case 0: // Use Min Max processing MinColorScaled = floor(MinColorScaled * scale); MaxColorScaled = ceil(MaxColorScaled * scale); @@ -1188,7 +1630,8 @@ static void cmp_ProcessColors(CMP_INOUT CGU_Vec3f CMP_PTRINOUT colorMin, // The block is decompressed to 8 bits per channel // Result buffer is RGBA format, A is set to 255 //---------------------------------------------------- -static void cmp_decompressDXTRGBA_Internal(CGU_UINT8 rgbBlock[BLOCK_SIZE_4X4X4], const CGU_Vec2ui compressedBlock, const CGU_BOOL mapDecodeRGBA) { +static void cmp_decompressDXTRGBA_Internal(CGU_UINT8 rgbBlock[BLOCK_SIZE_4X4X4], const CGU_Vec2ui compressedBlock, const CGU_BOOL mapDecodeRGBA) +{ CGU_BOOL bDXT1 = TRUE; CGU_UINT32 n0 = compressedBlock.x & 0xffff; CGU_UINT32 n1 = compressedBlock.x >> 16; @@ -1215,21 +1658,25 @@ static void cmp_decompressDXTRGBA_Internal(CGU_UINT8 rgbBlock[BLOCK_SIZE_4X4X4], b0 += (b0 >> 5); b1 += (b1 >> 5); - if (!mapDecodeRGBA) { + if (!mapDecodeRGBA) + { //-------------------------------------------------------------- // Channel mapping output as BGRA //-------------------------------------------------------------- CGU_UINT32 c0 = 0xff000000 | (r0 << 16) | (g0 << 8) | b0; CGU_UINT32 c1 = 0xff000000 | (r1 << 16) | (g1 << 8) | b1; - if (!bDXT1 || n0 > n1) { + if (!bDXT1 || n0 > n1) + { CGU_UINT32 c2 = 0xff000000 | (((2 * r0 + r1) / 3) << 16) | (((2 * g0 + g1) / 3) << 8) | (((2 * b0 + b1) / 3)); CGU_UINT32 c3 = 0xff000000 | (((2 * r1 + r0) / 3) << 16) | (((2 * g1 + g0) / 3) << 8) | (((2 * b1 + b0) / 3)); - for (int i = 0; i < 16; i++) { + for (int i = 0; i < 16; i++) + { int index = (compressedBlock.y >> (2 * i)) & 3; - switch (index) { + switch (index) + { case 0: ((CGU_UINT32*)rgbBlock)[i] = c0; break; @@ -1244,14 +1691,18 @@ static void cmp_decompressDXTRGBA_Internal(CGU_UINT8 rgbBlock[BLOCK_SIZE_4X4X4], break; } } - } else { + } + else + { // Transparent decode CGU_UINT32 c2 = 0xff000000 | (((r0 + r1) / 2) << 16) | (((g0 + g1) / 2) << 8) | (((b0 + b1) / 2)); - for (int i = 0; i < 16; i++) { + for (int i = 0; i < 16; i++) + { int index = (compressedBlock.y >> (2 * i)) & 3; - switch (index) { + switch (index) + { case 0: ((CGU_UINT32*)rgbBlock)[i] = c0; break; @@ -1267,7 +1718,9 @@ static void cmp_decompressDXTRGBA_Internal(CGU_UINT8 rgbBlock[BLOCK_SIZE_4X4X4], } } } - } else { + } + else + { // MAP_BC15_TO_ABGR //-------------------------------------------------------------- // Channel mapping output as RGBA @@ -1276,13 +1729,16 @@ static void cmp_decompressDXTRGBA_Internal(CGU_UINT8 rgbBlock[BLOCK_SIZE_4X4X4], CGU_UINT32 c0 = 0xff000000 | (b0 << 16) | (g0 << 8) | r0; CGU_UINT32 c1 = 0xff000000 | (b1 << 16) | (g1 << 8) | r1; - if (!bDXT1 || n0 > n1) { + if (!bDXT1 || n0 > n1) + { CGU_UINT32 c2 = 0xff000000 | (((2 * b0 + b1 + 1) / 3) << 16) | (((2 * g0 + g1 + 1) / 3) << 8) | (((2 * r0 + r1 + 1) / 3)); CGU_UINT32 c3 = 0xff000000 | (((2 * b1 + b0 + 1) / 3) << 16) | (((2 * g1 + g0 + 1) / 3) << 8) | (((2 * r1 + r0 + 1) / 3)); - for (int i = 0; i < 16; i++) { + for (int i = 0; i < 16; i++) + { int index = (compressedBlock.y >> (2 * i)) & 3; - switch (index) { + switch (index) + { case 0: ((CMP_GLOBAL CGU_UINT32*)rgbBlock)[i] = c0; break; @@ -1297,13 +1753,17 @@ static void cmp_decompressDXTRGBA_Internal(CGU_UINT8 rgbBlock[BLOCK_SIZE_4X4X4], break; } } - } else { + } + else + { // Transparent decode CGU_UINT32 c2 = 0xff000000 | (((b0 + b1) / 2) << 16) | (((g0 + g1) / 2) << 8) | (((r0 + r1) / 2)); - for (int i = 0; i < 16; i++) { + for (int i = 0; i < 16; i++) + { int index = (compressedBlock.y >> (2 * i)) & 3; - switch (index) { + switch (index) + { case 0: ((CMP_GLOBAL CGU_UINT32*)rgbBlock)[i] = c0; break; @@ -1326,7 +1786,8 @@ static void cmp_decompressDXTRGBA_Internal(CGU_UINT8 rgbBlock[BLOCK_SIZE_4X4X4], //-------------------------------------------------------------------------------------------------------- // Decompress is RGB (0.0f..255.0f) //-------------------------------------------------------------------------------------------------------- -static void cmp_decompressRGBBlock(CMP_INOUT CGU_Vec3f rgbBlock[BLOCK_SIZE_4X4], const CGU_Vec2ui compressedBlock) { +static void cmp_decompressRGBBlock(CMP_INOUT CGU_Vec3f rgbBlock[BLOCK_SIZE_4X4], const CGU_Vec2ui compressedBlock) +{ CGU_UINT32 n0 = compressedBlock.x & 0xffff; CGU_UINT32 n1 = compressedBlock.x >> 16; CGU_UINT32 index; @@ -1339,13 +1800,16 @@ static void cmp_decompressRGBBlock(CMP_INOUT CGU_Vec3f rgbBlock[BLOCK_SIZE_4X4], CGU_Vec3f c2; CGU_Vec3f c3; - if (n0 > n1) { + if (n0 > n1) + { c2 = (c0 * 2.0f + c1) / 3.0f; c3 = (c1 * 2.0f + c0) / 3.0f; - for (CGU_UINT32 i = 0; i < 16; i++) { + for (CGU_UINT32 i = 0; i < 16; i++) + { index = (compressedBlock.y >> (2 * i)) & 3; - switch (index) { + switch (index) + { case 0: rgbBlock[i] = c0; break; @@ -1360,13 +1824,17 @@ static void cmp_decompressRGBBlock(CMP_INOUT CGU_Vec3f rgbBlock[BLOCK_SIZE_4X4], break; } } - } else { + } + else + { // Transparent decode c2 = (c0 + c1) / 2.0f; - for (CGU_UINT32 i = 0; i < 16; i++) { + for (CGU_UINT32 i = 0; i < 16; i++) + { index = (compressedBlock.y >> (2 * i)) & 3; - switch (index) { + switch (index) + { case 0: rgbBlock[i] = c0; break; @@ -1385,7 +1853,8 @@ static void cmp_decompressRGBBlock(CMP_INOUT CGU_Vec3f rgbBlock[BLOCK_SIZE_4X4], } // The source is 0..1, decompressed data using cmp_decompressRGBBlock is 0..255 which is converted down to 0..1 -static float CMP_RGBBlockError(const CGU_Vec3f src_rgbBlock[BLOCK_SIZE_4X4], const CGU_Vec2ui compressedBlock, CGU_BOOL isSRGB) { +static float CMP_RGBBlockError(const CGU_Vec3f src_rgbBlock[BLOCK_SIZE_4X4], const CGU_Vec2ui compressedBlock, CGU_BOOL isSRGB) +{ CGU_Vec3f rgbBlock[BLOCK_SIZE_4X4]; // Decompressed block channels are 0..255 @@ -1400,12 +1869,16 @@ static float CMP_RGBBlockError(const CGU_Vec3f src_rgbBlock[BLOCK_SIZE_4X4], con float sR, sG, sB, R, G, B; - for (int j = 0; j < 16; j++) { - if (isSRGB) { + for (int j = 0; j < 16; j++) + { + if (isSRGB) + { sR = round(cmp_linearToSrgbf(src_rgbBlock[j].x) * 255.0f); sG = round(cmp_linearToSrgbf(src_rgbBlock[j].y) * 255.0f); sB = round(cmp_linearToSrgbf(src_rgbBlock[j].z) * 255.0f); - } else { + } + else + { sR = round(src_rgbBlock[j].x * 255.0f); sG = round(src_rgbBlock[j].y * 255.0f); sB = round(src_rgbBlock[j].z * 255.0f); @@ -1428,7 +1901,8 @@ static float CMP_RGBBlockError(const CGU_Vec3f src_rgbBlock[BLOCK_SIZE_4X4], con } // Processing input source 0..1.0f) -static CGU_Vec2ui CompressRGBBlock_FM(const CGU_Vec3f rgbBlockUVf[16], CMP_IN CGU_FLOAT fquality, CGU_BOOL isSRGB, CMP_INOUT CGU_FLOAT CMP_PTRINOUT errout) { +static CGU_Vec2ui CompressRGBBlock_FM(const CGU_Vec3f rgbBlockUVf[16], CMP_IN CGU_FLOAT fquality, CGU_BOOL isSRGB, CMP_INOUT CGU_FLOAT CMP_PTRINOUT errout) +{ CGU_Vec3f axisVectorRGB = {0.0f, 0.0f, 0.0f}; // The axis vector for index projection CGU_FLOAT pos_on_axis[16]; // The distance each unique falls along the compression axis CGU_FLOAT axisleft = 0; // The extremities and centre (average of left/right) of srcRGB along the compression axis @@ -1458,10 +1932,12 @@ static CGU_Vec2ui CompressRGBBlock_FM(const CGU_Vec3f rgbBlockUVf[16], CMP_IN CG // Get average and modifed src // find average position and save list of pixels as 0F..255F range for processing // Note: z (blue) is average of blue+green channels - for (CGU_INT32 i = 0; i < BLOCK_SIZE_4X4; i++) { + for (CGU_INT32 i = 0; i < BLOCK_SIZE_4X4; i++) + { srcMin = cmp_min3f(srcMin, rgbBlockUVf[i]); srcMax = cmp_max3f(srcMax, rgbBlockUVf[i]); - if (!fastProcess) { + if (!fastProcess) + { rgb = isSRGB ? cmp_linearToSrgb(rgbBlockUVf[i]) : cmp_saturate(rgbBlockUVf[i]); rgb.z = (rgb.y + rgb.z) * 0.5F; // Z-axiz => (R+G)/2 srcRGB[i] = rgb; @@ -1473,13 +1949,16 @@ static CGU_Vec2ui CompressRGBBlock_FM(const CGU_Vec3f rgbBlockUVf[16], CMP_IN CG cmp_ProcessColors(CMP_REFINOUT srcMin, CMP_REFINOUT srcMax, CMP_REFINOUT c0, CMP_REFINOUT c1, isSRGB ? 1 : 0, isSRGB); // Save simple min-max encoding - if (c0 < c1) { + if (c0 < c1) + { Q1CompData.x = (c0 << 16) | c1; CGU_UINT32 index; errLQ = cmp_getIndicesRGB(CMP_REFINOUT index, rgbBlockUVf, srcMin, srcMax, false); Q1CompData.y = index; CMP_PTRINOUT errout = errLQ; - } else { + } + else + { // Most simple case all colors are equal or 0.0f Q1compressedBlock.x = (c1 << 16) | c0; Q1compressedBlock.y = 0; @@ -1508,10 +1987,12 @@ static CGU_Vec2ui CompressRGBBlock_FM(const CGU_Vec3f rgbBlockUVf[16], CMP_IN CG CGU_FLOAT bg_pos = 0.0f; CGU_FLOAT rb_pos = 0.0f; - for (CGU_INT32 i = 0; i < BLOCK_SIZE_4X4; i++) { + for (CGU_INT32 i = 0; i < BLOCK_SIZE_4X4; i++) + { rgb = srcRGB[i] - average_rgb; axisVectorRGB = axisVectorRGB + fabs(rgb); - if (rgb.x > 0) { + if (rgb.x > 0) + { rg_pos += rgb.y; rb_pos += rgb.z; } @@ -1527,7 +2008,8 @@ static CGU_Vec2ui CompressRGBBlock_FM(const CGU_Vec3f rgbBlockUVf[16], CMP_IN CG axisVectorRGB.x = -axisVectorRGB.x; if (bg_pos < 0) axisVectorRGB.z = -axisVectorRGB.z; - if ((rg_pos == bg_pos) && (rg_pos == 0)) { + if ((rg_pos == bg_pos) && (rg_pos == 0)) + { if (rb_pos < 0) axisVectorRGB.z = -axisVectorRGB.z; } @@ -1563,7 +2045,8 @@ static CGU_Vec2ui CompressRGBBlock_FM(const CGU_Vec3f rgbBlockUVf[16], CMP_IN CG { axisleft = CMP_FLOAT_MAX; axisright = -CMP_FLOAT_MAX; - for (CGU_INT32 i = 0; i < BLOCK_SIZE_4X4; i++) { + for (CGU_INT32 i = 0; i < BLOCK_SIZE_4X4; i++) + { // Compute the distance along the axis of the point of closest approach CGU_Vec3f temp = (srcRGB[i] - average_rgb); pos_on_axis[i] = dot(temp, axisVectorRGB); @@ -1615,13 +2098,16 @@ static CGU_Vec2ui CompressRGBBlock_FM(const CGU_Vec3f rgbBlockUVf[16], CMP_IN CG // Force to be a 4-colour opaque block - in which case, c0 is greater than c1 swap = 0; - if (c0 < c1) { + if (c0 < c1) + { CGU_UINT32 t; t = c0; c0 = c1; c1 = t; swap = 1; - } else if (c0 == c1) { + } + else if (c0 == c1) + { // This block will always be encoded in 3-colour mode // Need to ensure that only one of the two points gets used, // avoiding accidentally setting some transparent pixels into the block @@ -1653,7 +2139,8 @@ static CGU_Vec2ui CompressRGBBlock_FM(const CGU_Vec3f rgbBlockUVf[16], CMP_IN CG // cn[2] = cn[0]*2.0f/3.0f + cn[1]*1.0f/3.0f; // cn[3] = cn[0]*1.0f/3.0f + cn[1]*2.0f/3.0f; - for (CGU_INT32 i = 0; i < BLOCK_SIZE_4X4; i++) { + for (CGU_INT32 i = 0; i < BLOCK_SIZE_4X4; i++) + { // Endpoints (indicated by block > average) are 0 and 1, while // interpolants are 2 and 3 if (fabs(pos_on_axis[i]) >= division) @@ -1677,10 +2164,12 @@ static CGU_Vec2ui CompressRGBBlock_FM(const CGU_Vec3f rgbBlockUVf[16], CMP_IN CG CompMinErr = CMP_RGBBlockError(rgbBlockUVf, compressedBlock, isSRGB); Q1CompErr = CMP_RGBBlockError(rgbBlockUVf, Q1CompData, isSRGB); - if (CompMinErr > Q1CompErr) { + if (CompMinErr > Q1CompErr) + { compressedBlock = Q1CompData; CMP_PTRINOUT errout = Q1CompErr; - } else + } + else CMP_PTRINOUT errout = CompMinErr; } } @@ -1691,10 +2180,11 @@ static CGU_Vec2ui CompressRGBBlock_FM(const CGU_Vec3f rgbBlockUVf[16], CMP_IN CG #ifndef CMP_USE_LOWQUALITY static CMP_EndPoints CompressRGBBlock_Slow(CGU_Vec3f BlkInBGRf_UV[BLOCK_SIZE_4X4], - CGU_FLOAT Rpt[BLOCK_SIZE_4X4], - CGU_UINT32 dwUniqueColors, - CGU_Vec3f channelWeightsBGR, - CGU_UINT32 m_nRefinementSteps) { + CGU_FLOAT Rpt[BLOCK_SIZE_4X4], + CGU_UINT32 dwUniqueColors, + CGU_Vec3f channelWeightsBGR, + CGU_UINT32 m_nRefinementSteps) +{ CMP_UNUSED(channelWeightsBGR); CMP_UNUSED(m_nRefinementSteps); ALIGN_16 CGU_FLOAT Prj0[BLOCK_SIZE_4X4]; @@ -1715,15 +2205,19 @@ static CMP_EndPoints CompressRGBBlock_Slow(CGU_Vec3f BlkInBGRf_UV[BLOCK_SIZE_4X CGU_Vec3f PosG1 = {0.0f, 0.0f, 0.0f}; CGU_UINT32 i; - for (i = 0; i < dwUniqueColors; i++) { + for (i = 0; i < dwUniqueColors; i++) + { BlkUV[i] = BlkInBGRf_UV[i]; } // if not more then 2 different colors, we've done - if (dwUniqueColors <= 2) { + if (dwUniqueColors <= 2) + { rsltC0 = BlkInBGRf_UV[0] * 255.0f; rsltC1 = BlkInBGRf_UV[dwUniqueColors - 1] * 255.0f; - } else { + } + else + { // This is our first attempt to find an axis we will go along. // The cumulation is done to find a line minimizing the MSE from the // input 3D points. @@ -1745,7 +2239,8 @@ static CMP_EndPoints CompressRGBBlock_Slow(CGU_Vec3f BlkInBGRf_UV[BLOCK_SIZE_4X // sum position of all points CGU_FLOAT fNumPoints = 0.0f; - for (ii = 0; ii < dwUniqueColors; ii++) { + for (ii = 0; ii < dwUniqueColors; ii++) + { Mdl.x += BlkUV[ii].x * Rpt[ii]; Mdl.y += BlkUV[ii].y * Rpt[ii]; Mdl.z += BlkUV[ii].z * Rpt[ii]; @@ -1755,7 +2250,8 @@ static CMP_EndPoints CompressRGBBlock_Slow(CGU_Vec3f BlkInBGRf_UV[BLOCK_SIZE_4X // and then average to calculate center coordinate of block Mdl /= fNumPoints; - for (ii = 0; ii < dwUniqueColors; ii++) { + for (ii = 0; ii < dwUniqueColors; ii++) + { // calculate output block as offsets around block center BlkSh[ii] = BlkUV[ii] - Mdl; @@ -1775,13 +2271,15 @@ static CMP_EndPoints CompressRGBBlock_Slow(CGU_Vec3f BlkInBGRf_UV[BLOCK_SIZE_4X CGU_FLOAT mxRGB2 = 0.0f; CGU_FLOAT fEPS = fNumPoints * EPS; - for (kk = 0, jj = 0; jj < 3; jj++) { + for (kk = 0, jj = 0; jj < 3; jj++) + { if (RGB2[jj] >= fEPS) kk++; else RGB2[jj] = 0.0f; - if (mxRGB2 < RGB2[jj]) { + if (mxRGB2 < RGB2[jj]) + { mxRGB2 = RGB2[jj]; i0 = jj; } @@ -1795,27 +2293,35 @@ static CMP_EndPoints CompressRGBBlock_Slow(CGU_Vec3f BlkInBGRf_UV[BLOCK_SIZE_4X AxisIsSmall = AxisIsSmall && (RGB2[2] < fEPS2); // all are very small to avoid division on the small determinant - if (AxisIsSmall) { + if (AxisIsSmall) + { rsltC0 = BlkInBGRf_UV[0] * 255.0f; rsltC1 = BlkInBGRf_UV[dwUniqueColors - 1] * 255.0f; - } else { + } + else + { // !AxisIsSmall if (kk == 1) // really only 1 dimension LineDir0[i0] = 1.; - else if (kk == 2) { // really only 2 dimensions + else if (kk == 2) + { // really only 2 dimensions i1 = (RGB2[(i0 + 1) % 3] > 0.f) ? (i0 + 1) % 3 : (i0 + 2) % 3; CGU_FLOAT Crl = (i1 == (i0 + 1) % 3) ? Crrl[i0] : Crrl[(i0 + 2) % 3]; LineDir0[i1] = Crl / RGB2[i0]; LineDir0[i0] = 1.; - } else { + } + else + { CGU_FLOAT maxDet = 100000.f; CGU_FLOAT Cs[3]; // select max det for precision - for (jj = 0; jj < 3; jj++) { + for (jj = 0; jj < 3; jj++) + { // 3 = nDimensions CGU_FLOAT Det = RGB2[jj] * RGB2[(jj + 1) % 3] - Crrl[jj] * Crrl[jj]; Cs[jj] = fabs(Crrl[jj] / sqrt(RGB2[jj] * RGB2[(jj + 1) % 3])); - if (maxDet < Det) { + if (maxDet < Det) + { maxDet = Det; i0 = jj; } @@ -1887,7 +2393,8 @@ static CMP_EndPoints CompressRGBBlock_Slow(CGU_Vec3f BlkInBGRf_UV[BLOCK_SIZE_4X // in 3 dim space Ai(s) represent a line direction, along which // we again try to find (sub)optimal quantizer. // That's what our for(;;) loop is about. - for (;;) { + for (;;) + { // 1. Project input set on the axis in consideration. // From Foley & Van Dam: Closest point of approach of a line (P + v) to a // point (R) is @@ -1900,7 +2407,8 @@ static CMP_EndPoints CompressRGBBlock_Slow(CGU_Vec3f BlkInBGRf_UV[BLOCK_SIZE_4X for (i = 0; i < BLOCK_SIZE_4X4; i++) Prj0[i] = Prj[i] = PrjErr[i] = PreMRep[i] = 0.f; - for (i = 0; i < dwUniqueColors; i++) { + for (i = 0; i < dwUniqueColors; i++) + { Prj0[i] = Prj[i] = dot(BlkSh[i], LineDir); PrjErr[i] = dot(BlkSh[i] - LineDir * Prj[i], BlkSh[i] - LineDir * Prj[i]); PrjBnd0 = min(PrjBnd0, Prj[i]); @@ -1920,7 +2428,8 @@ static CMP_EndPoints CompressRGBBlock_Slow(CGU_Vec3f BlkInBGRf_UV[BLOCK_SIZE_4X const CGU_FLOAT Scl2 = (Scl1 - Scl0) * (Scl1 - Scl0); const CGU_FLOAT overScl = 1.f / (Scl1 - Scl0); - for (i = 0; i < dwUniqueColors; i++) { + for (i = 0; i < dwUniqueColors; i++) + { // scale them Prj[i] = (Prj[i] - Scl0) * overScl; // premultiply the scale square to plug into error computation later @@ -1947,12 +2456,15 @@ static CMP_EndPoints CompressRGBBlock_Slow(CGU_Vec3f BlkInBGRf_UV[BLOCK_SIZE_4X CGU_FLOAT err; int l, h; - for (l = 0, lowPosStep = lowStartEnd; l < 8; l++, lowPosStep += searchStep) { - for (h = 0, highPosStep = highStartEnd; h < 8; h++, highPosStep -= searchStep) { + for (l = 0, lowPosStep = lowStartEnd; l < 8; l++, lowPosStep += searchStep) + { + for (h = 0, highPosStep = highStartEnd; h < 8; h++, highPosStep -= searchStep) + { // compute an error for the current pair of end points. err = cmp_getRampErr(Prj, PrjErr, PreMRep, StepErr, lowPosStep, highPosStep, dwUniqueColors); - if (err < StepErr) { + if (err < StepErr) + { // save better result StepErr = err; Pos0 = lowPosStep; @@ -1966,7 +2478,8 @@ static CMP_EndPoints CompressRGBBlock_Slow(CGU_Vec3f BlkInBGRf_UV[BLOCK_SIZE_4X Pos1 = Pos1 * (Scl1 - Scl0) + Scl0; // did we find somthing better from the previous run? - if (StepErr + 0.001 < ErrG) { + if (StepErr + 0.001 < ErrG) + { // yes, remember it ErrG = StepErr; LineDirG = LineDir; @@ -1990,7 +2503,8 @@ static CMP_EndPoints CompressRGBBlock_Slow(CGU_Vec3f BlkInBGRf_UV[BLOCK_SIZE_4X // shifted and normalized CGU_FLOAT indxAvrg = 3.0f / 2.0f; // (dwNumChannels=4 - 1); - for (i = 0; i < dwUniqueColors; i++) { + for (i = 0; i < dwUniqueColors; i++) + { CGU_FLOAT del; // CGU_UINT32 n = (CGU_UINT32)((b - _min_ex + (step*0.5f)) * rstep); if ((del = Prj0[i] - Pos0) <= 0) @@ -2009,7 +2523,8 @@ static CMP_EndPoints CompressRGBBlock_Slow(CGU_Vec3f BlkInBGRf_UV[BLOCK_SIZE_4X CGU_Vec3f Crs = {0.0f, 0.0f, 0.0f}; CGU_FLOAT Len = 0.0f; - for (i = 0; i < dwUniqueColors; i++) { + for (i = 0; i < dwUniqueColors; i++) + { const CGU_FLOAT PreMlt = RmpIndxs[i] * Rpt[i]; Len += RmpIndxs[i] * PreMlt; Crs.x += BlkSh[i].x * PreMlt; @@ -2018,7 +2533,8 @@ static CMP_EndPoints CompressRGBBlock_Slow(CGU_Vec3f BlkInBGRf_UV[BLOCK_SIZE_4X } LineDir.x = LineDir.y = LineDir.z = 0.0f; - if (Len > 0.0f) { + if (Len > 0.0f) + { CGU_FLOAT Len2; LineDir = Crs / Len; // 6. Plug the projections as a new directional vector for the axis. @@ -2027,7 +2543,8 @@ static CMP_EndPoints CompressRGBBlock_Slow(CGU_Vec3f BlkInBGRf_UV[BLOCK_SIZE_4X Len2 = sqrt(Len2); LineDir /= Len2; } - } else // We was not able to find anything better. Drop out. + } + else // We was not able to find anything better. Drop out. break; } @@ -2054,19 +2571,22 @@ static CMP_EndPoints CompressRGBBlock_Slow(CGU_Vec3f BlkInBGRf_UV[BLOCK_SIZE_4X if (inpRmpEndPts0.x <= _Min) inpRmpEndPts0.x = _Min; - else { + else + { inpRmpEndPts0.x += floor(128.f / Fctrs1.x) - floor(inpRmpEndPts0.x / Fctrs1.x); inpRmpEndPts0.x = min(inpRmpEndPts0.x, _Max); } if (inpRmpEndPts0.y <= _Min) inpRmpEndPts0.y = _Min; - else { + else + { inpRmpEndPts0.y += floor(128.f / Fctrs1.y) - floor(inpRmpEndPts0.y / Fctrs1.y); inpRmpEndPts0.y = min(inpRmpEndPts0.y, _Max); } if (inpRmpEndPts0.z <= _Min) inpRmpEndPts0.z = _Min; - else { + else + { inpRmpEndPts0.z += floor(128.f / Fctrs1.z) - floor(inpRmpEndPts0.z / Fctrs1.z); inpRmpEndPts0.z = min(inpRmpEndPts0.z, _Max); } @@ -2076,19 +2596,22 @@ static CMP_EndPoints CompressRGBBlock_Slow(CGU_Vec3f BlkInBGRf_UV[BLOCK_SIZE_4X inpRmpEndPts1 = floor(rsltC1); if (inpRmpEndPts1.x <= _Min) inpRmpEndPts1.x = _Min; - else { + else + { inpRmpEndPts1.x += floor(128.f / Fctrs1.x) - floor(inpRmpEndPts1.x / Fctrs1.x); inpRmpEndPts1.x = min(inpRmpEndPts1.x, _Max); } if (inpRmpEndPts1.y <= _Min) inpRmpEndPts1.y = _Min; - else { + else + { inpRmpEndPts1.y += floor(128.f / Fctrs1.y) - floor(inpRmpEndPts1.y / Fctrs1.y); inpRmpEndPts1.y = min(inpRmpEndPts1.y, _Max); } if (inpRmpEndPts1.z <= _Min) inpRmpEndPts1.z = _Min; - else { + else + { inpRmpEndPts1.z += floor(128.f / Fctrs1.z) - floor(inpRmpEndPts1.z / Fctrs1.z); inpRmpEndPts1.z = min(inpRmpEndPts1.z, _Max); } @@ -2106,12 +2629,13 @@ static CMP_EndPoints CompressRGBBlock_Slow(CGU_Vec3f BlkInBGRf_UV[BLOCK_SIZE_4X // Process a rgbBlock which is normalized (0.0f ... 1.0f), signed normal is not implemented static CGU_Vec2ui CompressBlockBC1_RGBA_Internal(const CGU_Vec3f rgbBlockUVf[BLOCK_SIZE_4X4], - const CGU_FLOAT BlockA[BLOCK_SIZE_4X4], - CGU_Vec3f channelWeights, - CGU_UINT32 dwAlphaThreshold, - CGU_UINT32 m_nRefinementSteps, - CMP_IN CGU_FLOAT fquality, - CGU_BOOL isSRGB) { + const CGU_FLOAT BlockA[BLOCK_SIZE_4X4], + CGU_Vec3f channelWeights, + CGU_UINT32 dwAlphaThreshold, + CGU_UINT32 m_nRefinementSteps, + CMP_IN CGU_FLOAT fquality, + CGU_BOOL isSRGB) +{ CGU_Vec2ui cmpBlock = {0, 0}; CGU_FLOAT errLQ = 1e6f; @@ -2121,7 +2645,8 @@ static CGU_Vec2ui CompressBlockBC1_RGBA_Internal(const CGU_Vec3f rgbBlockUVf[BLO //------------------------------------------------------------------ // Processing is in 0..255 range, code needs to be normized to 0..1 //------------------------------------------------------------------ - if ((errLQ > 0.0f) && (fquality > CMP_QUALITY2)) { + if ((errLQ > 0.0f) && (fquality > CMP_QUALITY2)) + { CGU_Vec3f rgbBlock_normal[BLOCK_SIZE_4X4]; CGU_UINT32 nCmpIndices = 0; CGU_UINT32 c0, c1; @@ -2142,7 +2667,8 @@ static CGU_Vec2ui CompressBlockBC1_RGBA_Internal(const CGU_Vec3f rgbBlockUVf[BLO CGU_Vec3ui nEndpoints0 = {0, 0, 0}; // Endpoints are stored BGR as x,y,z CGU_Vec3ui nEndpoints1 = {0xFF, 0xFF, 0xFF}; // Endpoints are stored BGR as x,y,z - for (i = 0; i < BLOCK_SIZE_4X4; i++) { + for (i = 0; i < BLOCK_SIZE_4X4; i++) + { Rpt[i] = 0.0f; } @@ -2151,7 +2677,8 @@ static CGU_Vec2ui CompressBlockBC1_RGBA_Internal(const CGU_Vec3f rgbBlockUVf[BLO CGU_UINT32 dwColors = 0; CGU_UINT32 dwBlk[BLOCK_SIZE_4X4]; CGU_UINT32 R, G, B, A; - for (i = 0; i < BLOCK_SIZE_4X4; i++) { + for (i = 0; i < BLOCK_SIZE_4X4; i++) + { // Do any color conversion prior to processing the block rgbBlock_normal[i] = isSRGB ? cmp_linearToSrgb(rgbBlockUVf[i]) : rgbBlockUVf[i]; @@ -2165,18 +2692,22 @@ static CGU_Vec2ui CompressBlockBC1_RGBA_Internal(const CGU_Vec3f rgbBlockUVf[BLO A = 255; // Punch Through Alpha in BC1 Codec (1 bit alpha) - if ((dwAlphaThreshold == 0) || (A >= dwAlphaThreshold)) { + if ((dwAlphaThreshold == 0) || (A >= dwAlphaThreshold)) + { // copy to local RGB data and have alpha set to 0xFF dwBlk[dwColors++] = A << 24 | R << 16 | G << 8 | B; } } - if (!dwColors) { + if (!dwColors) + { // All are colors transparent EndPoints.Color0.x = EndPoints.Color0.y = EndPoints.Color0.z = 0.0f; EndPoints.Color1.x = EndPoints.Color1.y = EndPoints.Color0.z = 255.0f; nCmpIndices = 0xFFFFFFFF; - } else { + } + else + { // We have colors to process nCmpIndices = 0; // Punch Through Alpha Support ToDo @@ -2197,7 +2728,8 @@ static CGU_Vec2ui CompressBlockBC1_RGBA_Internal(const CGU_Vec3f rgbBlockUVf[BLO CGU_UINT32 j; CMP_di what[BLOCK_SIZE_4X4]; - for (i = 0; i < dwColors; i++) { + for (i = 0; i < dwColors; i++) + { what[i].index = i; what[i].data = dwBlk[i]; } @@ -2205,9 +2737,12 @@ static CGU_Vec2ui CompressBlockBC1_RGBA_Internal(const CGU_Vec3f rgbBlockUVf[BLO CGU_UINT32 tmp_index; CGU_UINT32 tmp_data; - for (i = 1; i < dwColors; i++) { - for (j = i; j > 0; j--) { - if (what[j - 1].data > what[j].data) { + for (i = 1; i < dwColors; i++) + { + for (j = i; j > 0; j--) + { + if (what[j - 1].data > what[j].data) + { tmp_index = what[j].index; tmp_data = what[j].data; what[j].index = what[j - 1].index; @@ -2226,23 +2761,30 @@ static CGU_Vec2ui CompressBlockBC1_RGBA_Internal(const CGU_Vec3f rgbBlockUVf[BLO CGU_UINT32 dwUniqueColors = 0; new_p = dwBlkU[0] = dwBlk[0]; Rpt[dwUniqueColors] = 1.f; - for (i = 1; i < dwColors; i++) { - if (new_p != dwBlk[i]) { + for (i = 1; i < dwColors; i++) + { + if (new_p != dwBlk[i]) + { dwUniqueColors++; new_p = dwBlkU[dwUniqueColors] = dwBlk[i]; Rpt[dwUniqueColors] = 1.f; - } else + } + else Rpt[dwUniqueColors] += 1.f; } dwUniqueColors++; // Simple case of only 2 colors to process // no need for futher processing as lowest quality methods work best for this case - if (dwUniqueColors <= 2) { + if (dwUniqueColors <= 2) + { return Q1CompData; - } else { + } + else + { // switch from int range back to UV floats - for (i = 0; i < dwUniqueColors; i++) { + for (i = 0; i < dwUniqueColors; i++) + { R = (dwBlkU[i] >> 16) & 0xff; G = (dwBlkU[i] >> 8) & 0xff; B = (dwBlkU[i] >> 0) & 0xff; @@ -2263,7 +2805,8 @@ static CGU_Vec2ui CompressBlockBC1_RGBA_Internal(const CGU_Vec3f rgbBlockUVf[BLO //=================================================================== // Process Cluster INPUT is constant EndPointsf OUTPUT is pcIndices //=================================================================== - if (nCmpIndices == 0) { + if (nCmpIndices == 0) + { R = (CGU_UINT32)(EndPoints.Color0.z); G = (CGU_UINT32)(EndPoints.Color0.y); B = (CGU_UINT32)(EndPoints.Color0.x); @@ -2276,12 +2819,15 @@ static CGU_Vec2ui CompressBlockBC1_RGBA_Internal(const CGU_Vec3f rgbBlockUVf[BLO CGU_Vec3f InpRmp[NUM_ENDPOINTS]; if ((cluster0 <= cluster1) // valid for 4 channels - // || (cluster0 > cluster1) // valid for 3 channels - ) { + // || (cluster0 > cluster1) // valid for 3 channels + ) + { // inverse endpoints InpRmp[0] = EndPoints.Color1; InpRmp[1] = EndPoints.Color0; - } else { + } + else + { InpRmp[0] = EndPoints.Color0; InpRmp[1] = EndPoints.Color1; } @@ -2290,12 +2836,14 @@ static CGU_Vec2ui CompressBlockBC1_RGBA_Internal(const CGU_Vec3f rgbBlockUVf[BLO CGU_FLOAT srcblockA[BLOCK_SIZE_4X4]; // Swizzle the source RGB to BGR for processing - for (i = 0; i < BLOCK_SIZE_4X4; i++) { + for (i = 0; i < BLOCK_SIZE_4X4; i++) + { srcblockBGR[i].z = rgbBlock_normal[i].x * 255.0f; srcblockBGR[i].y = rgbBlock_normal[i].y * 255.0f; srcblockBGR[i].x = rgbBlock_normal[i].z * 255.0f; srcblockA[i] = 0.0f; - if (dwAlphaThreshold > 0) { + if (dwAlphaThreshold > 0) + { CGU_UINT32 alpha = (CGU_UINT32)BlockA[i]; if (alpha >= dwAlphaThreshold) srcblockA[i] = BlockA[i]; @@ -2336,11 +2884,15 @@ static CGU_Vec2ui CompressBlockBC1_RGBA_Internal(const CGU_Vec3f rgbBlockUVf[BLO // For each colour in the original block assign it // to the closest cluster and compute the cumulative error - for (i = 0; i < BLOCK_SIZE_4X4; i++) { + for (i = 0; i < BLOCK_SIZE_4X4; i++) + { alpha = (CGU_UINT32)srcblockA[i]; - if ((dwAlphaThreshold > 0) && alpha == 0) { //*((CGU_DWORD *)&_Blk[i][AC]) == 0) + if ((dwAlphaThreshold > 0) && alpha == 0) + { //*((CGU_DWORD *)&_Blk[i][AC]) == 0) pcIndices |= cmp_set2Bit32(4, i); // dwNumChannels 3 or 4 (default is 4) - } else { + } + else + { CGU_FLOAT shortest = 99999999999.f; CGU_UINT8 shortestIndex = 0; @@ -2349,12 +2901,14 @@ static CGU_Vec2ui CompressBlockBC1_RGBA_Internal(const CGU_Vec3f rgbBlockUVf[BLO channelWeightsBGR.y = channelWeights.y; channelWeightsBGR.z = channelWeights.x; - for (CGU_UINT8 rampindex = 0; rampindex < 4; rampindex++) { + for (CGU_UINT8 rampindex = 0; rampindex < 4; rampindex++) + { // r is either 1 or 4 // calculate the distance for each component CGU_FLOAT distance = dot(((srcblockBGR[i] - LerpRmp[rampindex]) * channelWeightsBGR), ((srcblockBGR[i] - LerpRmp[rampindex]) * channelWeightsBGR)); - if (distance < shortest) { + if (distance < shortest) + { shortest = distance; shortestIndex = rampindex; } @@ -2390,9 +2944,11 @@ static CGU_Vec2ui CompressBlockBC1_RGBA_Internal(const CGU_Vec3f rgbBlockUVf[BLO if (nCmpIndices == 0) nCmpIndices = pcIndices; - if (c0 <= c1) { + if (c0 <= c1) + { cmpBlock.x = c1 | (c0 << 16); - } else + } + else cmpBlock.x = c0 | (c1 << 16); cmpBlock.y = nCmpIndices; @@ -2409,7 +2965,8 @@ static CGU_Vec2ui CompressBlockBC1_RGBA_Internal(const CGU_Vec3f rgbBlockUVf[BLO //============================= Alpha: New single header interfaces: supports GPU shader interface ================================================== // Compress a BC1 block -static CGU_Vec2ui CompressBlockBC1_UNORM(CGU_Vec3f rgbablockf[BLOCK_SIZE_4X4], CMP_IN CGU_FLOAT fquality, CGU_BOOL isSRGB) { +static CGU_Vec2ui CompressBlockBC1_UNORM(CGU_Vec3f rgbablockf[BLOCK_SIZE_4X4], CMP_IN CGU_FLOAT fquality, CGU_BOOL isSRGB) +{ CGU_FLOAT BlockA[BLOCK_SIZE_4X4]; // Not used but required CGU_Vec3f channelWeights = {1.0f, 1.0f, 1.0f}; @@ -2423,7 +2980,8 @@ static CGU_Vec2ui CompressBlockBC1_UNORM(CGU_Vec3f rgbablockf[BLOCK_SIZE_4X4], C } // Compress a BC2 block -static CGU_Vec4ui CompressBlockBC2_UNORM(CMP_IN CGU_Vec3f BlockRGB[16], CMP_IN CGU_FLOAT BlockA[16], CGU_FLOAT fquality, CGU_BOOL isSRGB) { +static CGU_Vec4ui CompressBlockBC2_UNORM(CMP_IN CGU_Vec3f BlockRGB[16], CMP_IN CGU_FLOAT BlockA[16], CGU_FLOAT fquality, CGU_BOOL isSRGB) +{ CGU_Vec2ui compressedBlocks; CGU_Vec4ui compBlock; compressedBlocks = cmp_compressExplicitAlphaBlock(BlockA); @@ -2438,7 +2996,8 @@ static CGU_Vec4ui CompressBlockBC2_UNORM(CMP_IN CGU_Vec3f BlockRGB[16], CMP_IN C } // Compress a BC3 block -static CGU_Vec4ui CompressBlockBC3_UNORM(CMP_IN CGU_Vec3f BlockRGB[16], CMP_IN CGU_FLOAT BlockA[16], CGU_FLOAT fquality, CGU_BOOL isSRGB) { +static CGU_Vec4ui CompressBlockBC3_UNORM(CMP_IN CGU_Vec3f BlockRGB[16], CMP_IN CGU_FLOAT BlockA[16], CGU_FLOAT fquality, CGU_BOOL isSRGB) +{ CGU_Vec4ui compBlock; CGU_Vec2ui cmpBlock; @@ -2454,21 +3013,24 @@ static CGU_Vec4ui CompressBlockBC3_UNORM(CMP_IN CGU_Vec3f BlockRGB[16], CMP_IN C } // Compress a BC4 block -static CGU_Vec2ui CompressBlockBC4_UNORM(CMP_IN CGU_FLOAT Block[16], CGU_FLOAT fquality) { +static CGU_Vec2ui CompressBlockBC4_UNORM(CMP_IN CGU_FLOAT Block[16], CGU_FLOAT fquality) +{ CGU_Vec2ui cmpBlock; cmpBlock = cmp_compressAlphaBlock(Block, fquality, FALSE); return cmpBlock; } // Compress a BC4 block -static CGU_Vec2ui CompressBlockBC4_SNORM(CMP_IN CGU_FLOAT Block[16], CGU_FLOAT fquality) { +static CGU_Vec2ui CompressBlockBC4_SNORM(CMP_IN CGU_FLOAT Block[16], CGU_FLOAT fquality) +{ CGU_Vec2ui cmpBlock; cmpBlock = cmp_compressAlphaBlock(Block, fquality, TRUE); return cmpBlock; } // Compress a BC5 block -static CGU_Vec4ui CompressBlockBC5_UNORM(CMP_IN CGU_FLOAT BlockU[16], CMP_IN CGU_FLOAT BlockV[16], CGU_FLOAT fquality) { +static CGU_Vec4ui CompressBlockBC5_UNORM(CMP_IN CGU_FLOAT BlockU[16], CMP_IN CGU_FLOAT BlockV[16], CGU_FLOAT fquality) +{ CGU_Vec4ui compressedBlock = {0, 0, 0, 0}; CGU_Vec2ui cmpBlock; cmpBlock = cmp_compressAlphaBlock(BlockU, fquality, FALSE); diff --git a/cmp_core/shaders/common_def.h b/cmp_core/shaders/common_def.h index da849cda0..337c1d7e9 100644 --- a/cmp_core/shaders/common_def.h +++ b/cmp_core/shaders/common_def.h @@ -30,17 +30,23 @@ #ifndef _COMMON_DEFINITIONS_H #define _COMMON_DEFINITIONS_H +#ifdef __linux__ +#ifndef _LINUX +#define _LINUX +#endif +#endif + // The shaders for UE4 require extension in the form of .ush in place of standard .h // this directive is used to make the change without users requiring to modify all of the include extensions // specific to UE4 #ifdef ASPM_HLSL_UE4 #pragma once -#define INC_cmp_math_vec4 "cmp_math_vec4.ush" -#define INC_cmp_math_func "cmp_math_func.ush" +#define INC_cmp_math_vec4 "cmp_math_vec4.ush" +#define INC_cmp_math_func "cmp_math_func.ush" #else -#define INC_cmp_math_vec4 "cmp_math_vec4.h" -#define INC_cmp_math_func "cmp_math_func.h" +#define INC_cmp_math_vec4 "cmp_math_vec4.h" +#define INC_cmp_math_func "cmp_math_func.h" #endif // Features @@ -55,17 +61,17 @@ // Using OpenCL Compiler #ifdef __OPENCL_VERSION__ -#define ASPM_GPU -#define ASPM_OPENCL +#define ASPM_GPU +#define ASPM_OPENCL #endif // Using DirectX fxc Compiler // Note use the /DASPM_HLSL command line to define this #ifdef ASPM_HLSL -#define ASPM_GPU +#define ASPM_GPU #endif -#ifdef _LINUX +#ifdef __linux__ #undef ASPM_GPU #undef ASPM_OPENCL #ifndef ASPM_HLSL @@ -84,31 +90,32 @@ #define CMP_MIN(x, y) (((x) < (y)) ? (x) : (y)) #endif -#ifndef ASPM_GPU -#define CMP_STATIC_CAST(x,y) static_cast(y) +#ifdef ASPM_GPU +#define cmp_isnan(x) isnan(x) +#define CMP_STATIC_CAST(x, y) (x)(y) #else -#define CMP_STATIC_CAST(x,y) (x)(y) +#define cmp_isnan(x) std::isnan(x) +#define CMP_STATIC_CAST(x, y) static_cast(y) #endif - -#define CMP_SET_BC13_DECODER_RGBA // Sets mapping BC1, BC2 & BC3 to decode Red,Green,Blue and Alpha +#define CMP_SET_BC13_DECODER_RGBA // Sets mapping BC1, BC2 & BC3 to decode Red,Green,Blue and Alpha // RGBA to channels [0,1,2,3] else BGRA maps to [0,1,2,3] // BC4 alpha always maps as AAAA to channels [0,1,2,3] // BC5 decoded (Red&Green) maps R,G,B=0,A=255 to [0,1,2,3] else maps [B=0,G,R,A=255] to [0,1,2,3] //#define USE_BLOCK_LINEAR -#define CMP_FLOAT_MAX 3.402823466e+38F // max value used to detect an Error in processing -#define CMP_FLOAT_MAX_EXP 38 -#define USE_PROCESS_SEPERATE_ALPHA // Enable this to use higher quality code using CompressDualIndexBlock -#define COMPRESSED_BLOCK_SIZE 16 // Size of a compressed block in bytes -#define MAX_DIMENSION_BIG 4 // Max number of channels (RGBA) -#define MAX_SUBSETS 3 // Maximum number of possible subsets -#define MAX_SUBSET_SIZE 16 // Largest possible size for an individual subset -#define BLOCK_SIZE_4X4X4 64 -#define BLOCK_SIZE_4X4 16 -#define BlockX 4 -#define BlockY 4 +#define CMP_FLOAT_MAX 3.402823466e+38F // max value used to detect an Error in processing +#define CMP_FLOAT_MAX_EXP 38 +#define USE_PROCESS_SEPERATE_ALPHA // Enable this to use higher quality code using CompressDualIndexBlock +#define COMPRESSED_BLOCK_SIZE 16 // Size of a compressed block in bytes +#define MAX_DIMENSION_BIG 4 // Max number of channels (RGBA) +#define MAX_SUBSETS 3 // Maximum number of possible subsets +#define MAX_SUBSET_SIZE 16 // Largest possible size for an individual subset +#define BLOCK_SIZE_4X4X4 64 +#define BLOCK_SIZE_4X4 16 +#define BlockX 4 +#define BlockY 4 //#define USE_BLOCK_LINEAR // Source Data is organized in linear form for each block : Experimental Code not fully developed //#define USE_DOUBLE // Default is to use float, enable to use double data types only for float definitions @@ -118,45 +125,45 @@ #ifdef ASPM_HLSL // ==== Vectors ==== -typedef float2 CGU_Vec2f; -typedef float2 CGV_Vec2f; -typedef float3 CGU_Vec3f; -typedef float3 CGV_Vec3f; -typedef float4 CGU_Vec4f; -typedef float4 CGV_Vec4f; - -typedef int2 CGU_Vec2i; -typedef int2 CGV_Vec2i; -typedef uint2 CGU_Vec2ui; -typedef uint2 CGV_Vec2ui; - -typedef int3 CGU_Vec3i; -typedef int3 CGV_Vec3i; -typedef uint3 CGU_Vec3ui; -typedef uint3 CGV_Vec3ui; - -typedef uint4 CGU_Vec4ui; -typedef uint4 CGV_Vec4ui; +typedef float2 CGU_Vec2f; +typedef float2 CGV_Vec2f; +typedef float3 CGU_Vec3f; +typedef float3 CGV_Vec3f; +typedef float4 CGU_Vec4f; +typedef float4 CGV_Vec4f; + +typedef int2 CGU_Vec2i; +typedef int2 CGV_Vec2i; +typedef uint2 CGU_Vec2ui; +typedef uint2 CGV_Vec2ui; + +typedef int3 CGU_Vec3i; +typedef int3 CGV_Vec3i; +typedef uint3 CGU_Vec3ui; +typedef uint3 CGV_Vec3ui; + +typedef uint4 CGU_Vec4ui; +typedef uint4 CGV_Vec4ui; // ==== Scalar Types ==== to remove from code -typedef int CGU_INT8; -typedef uint CGU_INT; -typedef int CGV_INT; -typedef uint CGU_UINT8; -typedef uint CGU_UINT; +typedef int CGU_INT8; +typedef uint CGU_INT; +typedef int CGV_INT; +typedef uint CGU_UINT8; +typedef uint CGU_UINT; // ==== Scalar Types ==== -typedef int CGU_BOOL; -typedef int CGV_BOOL; -typedef int CGU_INT32; -typedef int CGV_INT32; -typedef uint CGU_UINT32; -typedef uint CGV_UINT32; -typedef float CGV_FLOAT; -typedef float CGU_FLOAT; -typedef min16float CGU_MIN16_FLOAT; // FP16 GPU support defaults to 32 bit if no HW support - -#define TRUE 1 +typedef int CGU_BOOL; +typedef int CGV_BOOL; +typedef int CGU_INT32; +typedef int CGV_INT32; +typedef uint CGU_UINT32; +typedef uint CGV_UINT32; +typedef float CGV_FLOAT; +typedef float CGU_FLOAT; +typedef min16float CGU_MIN16_FLOAT; // FP16 GPU support defaults to 32 bit if no HW support + +#define TRUE 1 #define FALSE 0 #define CMP_CDECL @@ -171,129 +178,125 @@ typedef min16float CGU_MIN16_FLOAT; // FP16 GPU support defaults to #define CMP_STATIC #define CMP_REFINOUT #define CMP_PTRINOUT -#define CMP_INOUT inout -#define CMP_OUT out -#define CMP_IN in -#define CMP_UNUSED(x) (x); -#define CMP_UNROLL [unroll] - - +#define CMP_INOUT inout +#define CMP_OUT out +#define CMP_IN in +#define CMP_UNUSED(x) (x); +#define CMP_UNROLL [unroll] #else -typedef enum { - CGU_CORE_OK = 0, // No errors, call was successfull - CGU_CORE_ERR_UNKOWN, // An unknown error occurred - CGU_CORE_ERR_NEWMEM, // New Memory Allocation Failed - CGU_CORE_ERR_INVALIDPTR, // The pointer value used is invalid or null - CGU_CORE_ERR_RANGERED, // values for Red Channel is out of range (too high or too low) - CGU_CORE_ERR_RANGEGREEN, // values for Green Channel is out of range (too high or too low) - CGU_CORE_ERR_RANGEBLUE, // values for Blue Channel is out of range (too high or too low) +typedef enum +{ + CGU_CORE_OK = 0, // No errors, call was successfull + CGU_CORE_ERR_UNKOWN, // An unknown error occurred + CGU_CORE_ERR_NEWMEM, // New Memory Allocation Failed + CGU_CORE_ERR_INVALIDPTR, // The pointer value used is invalid or null + CGU_CORE_ERR_RANGERED, // values for Red Channel is out of range (too high or too low) + CGU_CORE_ERR_RANGEGREEN, // values for Green Channel is out of range (too high or too low) + CGU_CORE_ERR_RANGEBLUE, // values for Blue Channel is out of range (too high or too low) } CGU_ERROR_CODES; - #ifdef ASPM_OPENCL // GPU Based code using OpenCL // ==== Vectors ==== -typedef float2 CGU_Vec2f; -typedef float2 CGV_Vec2f; -typedef float3 CMP_Vec3f; -typedef float3 CGU_Vec3f; -typedef float3 CGV_Vec3f; -typedef float4 CGU_Vec4f; -typedef float4 CGV_Vec4f; - -typedef uchar3 CGU_Vec3uc; -typedef uchar3 CGV_Vec3uc; - -typedef uchar4 CMP_Vec4uc; -typedef uchar4 CGU_Vec4uc; -typedef uchar4 CGV_Vec4uc; - -typedef int2 CGU_Vec2i; -typedef int2 CGV_Vec2i; -typedef int3 CGU_Vec3i; -typedef int3 CGV_Vec3i; -typedef int4 CGU_Vec4i; -typedef int4 CGV_Vec4i; - -typedef uint2 CGU_Vec2ui; -typedef uint2 CGV_Vec2ui; -typedef uint3 CGU_Vec3ui; -typedef uint3 CGV_Vec3ui; -typedef uint4 CGU_Vec4ui; -typedef uint4 CGV_Vec4ui; - +typedef float2 CGU_Vec2f; +typedef float2 CGV_Vec2f; +typedef float3 CMP_Vec3f; +typedef float3 CGU_Vec3f; +typedef float3 CGV_Vec3f; +typedef float4 CGU_Vec4f; +typedef float4 CGV_Vec4f; + +typedef uchar3 CGU_Vec3uc; +typedef uchar3 CGV_Vec3uc; + +typedef uchar4 CMP_Vec4uc; +typedef uchar4 CGU_Vec4uc; +typedef uchar4 CGV_Vec4uc; + +typedef int2 CGU_Vec2i; +typedef int2 CGV_Vec2i; +typedef int3 CGU_Vec3i; +typedef int3 CGV_Vec3i; +typedef int4 CGU_Vec4i; +typedef int4 CGV_Vec4i; + +typedef uint2 CGU_Vec2ui; +typedef uint2 CGV_Vec2ui; +typedef uint3 CGU_Vec3ui; +typedef uint3 CGV_Vec3ui; +typedef uint4 CGU_Vec4ui; +typedef uint4 CGV_Vec4ui; #define USE_BC7_SP_ERR_IDX #define BC7_ENCODECLASS -#define ASPM_PRINT(args) printf args +#define ASPM_PRINT(args) printf args #define CMP_EXPORT #define INLINE #define uniform #define varying -#define CMP_GLOBAL __global -#define CMP_KERNEL __kernel -#define CMP_CONSTANT __constant +#define CMP_GLOBAL __global +#define CMP_KERNEL __kernel +#define CMP_CONSTANT __constant #define CMP_STATIC -#define CMP_REFINOUT & -#define CMP_PTRINOUT * +#define CMP_REFINOUT & +#define CMP_PTRINOUT * #define CMP_INOUT #define CMP_OUT #define CMP_IN #define CMP_UNUSED(x) #define CMP_UNROLL -typedef unsigned int CGU_DWORD; //32bits -typedef int CGU_INT; //32bits -typedef bool CGU_BOOL; -typedef unsigned short CGU_SHORT; //16bits -typedef float CGU_FLOAT; -typedef half CGU_MIN16_FLOAT; // FP16 GPU support defaults to 32 bit if no HW support -typedef unsigned int uint32; // need to remove this def - -typedef int CGV_INT; -typedef unsigned int CGU_UINT; -typedef int CGUV_INT; -typedef int CGV_BOOL; - -typedef char CGU_INT8; -typedef unsigned char CGU_UINT8; -typedef short CGU_INT16; -typedef unsigned short CGU_UINT16; -typedef int CGU_INT32; -typedef unsigned int CGU_UINT32; -typedef unsigned long CGU_UINT64; - -typedef char CGV_INT8; -typedef unsigned char CGV_UINT8; -typedef short CGV_INT16; -typedef unsigned short CGV_UINT16; -typedef int CGV_INT32; -typedef unsigned int CGV_UINT32; -typedef unsigned long CGV_UINT64; - -typedef float CGV_FLOAT; - -#define TRUE 1 +typedef unsigned int CGU_DWORD; //32bits +typedef int CGU_INT; //32bits +typedef bool CGU_BOOL; +typedef unsigned short CGU_SHORT; //16bits +typedef float CGU_FLOAT; +typedef half CGU_MIN16_FLOAT; // FP16 GPU support defaults to 32 bit if no HW support +typedef unsigned int uint32; // need to remove this def + +typedef int CGV_INT; +typedef unsigned int CGU_UINT; +typedef int CGUV_INT; +typedef int CGV_BOOL; + +typedef char CGU_INT8; +typedef unsigned char CGU_UINT8; +typedef short CGU_INT16; +typedef unsigned short CGU_UINT16; +typedef int CGU_INT32; +typedef unsigned int CGU_UINT32; +typedef unsigned long long CGU_UINT64; + +typedef char CGV_INT8; +typedef unsigned char CGV_UINT8; +typedef short CGV_INT16; +typedef unsigned short CGV_UINT16; +typedef int CGV_INT32; +typedef unsigned int CGV_UINT32; +typedef unsigned long CGV_UINT64; + +typedef float CGV_FLOAT; + +#define TRUE 1 #define FALSE 0 #define CMP_CDECL #else // CPU & ASPM definitions -#define CMP_REFINOUT & -#define CMP_PTRINOUT * +#define CMP_REFINOUT & +#define CMP_PTRINOUT * #define CMP_INOUT #define CMP_OUT #define CMP_IN -#define CMP_UNUSED(x) (void)(x); +#define CMP_UNUSED(x) (void)(x); #define CMP_UNROLL - -#ifdef ASPM // SPMD ,SIMD CPU code +#ifdef ASPM // SPMD ,SIMD CPU code // using hybrid (CPU/GPU) aspm compiler -#define ASPM_PRINT(args) print args +#define ASPM_PRINT(args) print args #define CMP_USE_FOREACH_ASPM #define __ASPM__ #define BC7_ENCODECLASS @@ -301,23 +304,22 @@ typedef float CGV_FLOAT; #define USE_BC7_SP_ERR_IDX //#define USE_BC7_RAMP -#define CMP_EXPORT export -#define TRUE true -#define FALSE false -typedef uniform bool CGU_BOOL; -typedef bool CGV_BOOL; +#define CMP_EXPORT export +#define TRUE true +#define FALSE false +typedef uniform bool CGU_BOOL; +typedef bool CGV_BOOL; -typedef unsigned int8 uint8; -typedef unsigned int16 uint16; -typedef unsigned int32 uint32; -typedef unsigned int64 uint64; -typedef uniform float CGU_FLOAT; -typedef varying float CGV_FLOAT; -typedef uniform float CGU_MIN16_FLOAT; - -typedef uniform uint8 CGU_UINT8; -typedef varying uint8 CGV_UINT8; +typedef unsigned int8 uint8; +typedef unsigned int16 uint16; +typedef unsigned int32 uint32; +typedef unsigned int64 uint64; +typedef uniform float CGU_FLOAT; +typedef varying float CGV_FLOAT; +typedef uniform float CGU_MIN16_FLOAT; +typedef uniform uint8 CGU_UINT8; +typedef varying uint8 CGV_UINT8; typedef CGV_UINT8<4> CGV_Vec4uc; typedef CGU_UINT8<4> CGU_Vec4uc; @@ -336,53 +338,53 @@ typedef CGU_UINT32<4> CGU_Vec4ui; typedef CGV_UINT32<4> CGV_Vec4ui; #define CMP_CDECL -#else // standard CPU code +#else // standard CPU code #include #include #include INC_cmp_math_vec4 // using CPU compiler -#define ASPM_PRINT(args) printf args +#define ASPM_PRINT(args) printf args #define USE_BC7_RAMP #define USE_BC7_SP_ERR_IDX #define CMP_EXPORT #define BC7_ENCODECLASS BC7_EncodeClass:: -#define TRUE 1 -#define FALSE 0 +#define TRUE 1 +#define FALSE 0 #define uniform #define varying -typedef char int8; -typedef short int16; -typedef int int32; -typedef long int64; -typedef unsigned char uint8; -typedef unsigned short uint16; -typedef unsigned int uint32; -typedef unsigned long uint64; - -typedef int8 CGV_BOOL; -typedef bool CGU_BOOL; -typedef int16 CGU_WORD; -typedef uint8 CGU_SHORT; -typedef int64 CGU_LONG; -typedef uint64 CGU_ULONG; - -typedef uniform float CGU_FLOAT; -typedef varying float CGV_FLOAT; -typedef uniform float CGU_MIN16_FLOAT; - -typedef uniform uint8 CGU_UINT8; -typedef varying uint8 CGV_UINT8; - -typedef CMP_Vec3ui CGU_Vec3ui; -typedef CMP_Vec3ui CGV_Vec3ui; - -typedef CMP_Vec4ui CGU_Vec4ui; -typedef CMP_Vec4ui CGV_Vec4ui; -typedef CMP_Vec4f CGU_Vec4f; -typedef CMP_Vec4f CGV_Vec4f; +typedef char int8; +typedef short int16; +typedef int int32; +typedef long long int64; +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; +typedef unsigned long long uint64; + +typedef int8 CGV_BOOL; +typedef bool CGU_BOOL; +typedef int16 CGU_WORD; +typedef uint8 CGU_SHORT; +typedef long CGU_LONG; +typedef unsigned long CGU_ULONG; + +typedef uniform float CGU_FLOAT; +typedef varying float CGV_FLOAT; +typedef uniform float CGU_MIN16_FLOAT; + +typedef uniform uint8 CGU_UINT8; +typedef varying uint8 CGV_UINT8; + +typedef CMP_Vec3ui CGU_Vec3ui; +typedef CMP_Vec3ui CGV_Vec3ui; + +typedef CMP_Vec4ui CGU_Vec4ui; +typedef CMP_Vec4ui CGV_Vec4ui; +typedef CMP_Vec4f CGU_Vec4f; +typedef CMP_Vec4f CGV_Vec4f; #if defined(WIN32) || defined(_WIN64) #define CMP_CDECL __cdecl @@ -397,55 +399,52 @@ typedef CMP_Vec4f CGV_Vec4f; #define CMP_GLOBAL #define CMP_KERNEL -#define __local const -#define __constant const -#define CMP_CONSTANT const -#define INLINE inline -#define CMP_STATIC static - - -typedef uniform int32 CGU_DWORD; -typedef uniform uint8 CGU_UBYTE; -typedef uniform int CGU_INT; -typedef uniform int8 CGU_INT8; - -typedef uniform int16 CGU_INT16; -typedef uniform uint16 CGU_UINT16; -typedef uniform int32 CGU_INT32; -typedef uniform uint32 CGU_UINT32; -typedef uniform uint64 CGU_UINT64; - -typedef int CGV_INT; -typedef int8 CGV_INT8; -typedef int16 CGV_INT16; -typedef int32 CGV_INT32; -typedef uint16 CGV_UINT16; -typedef uint32 CGV_UINT32; -typedef uint64 CGV_UINT64; - - -#endif // else ASPM_GPU - -typedef struct { - CGU_UINT32 m_src_width; - CGU_UINT32 m_src_height; - CGU_UINT32 m_width_in_blocks; - CGU_UINT32 m_height_in_blocks; - CGU_FLOAT m_fquality; +#define __local const +#define __constant const +#define CMP_CONSTANT const +#define INLINE inline +#define CMP_STATIC static + +typedef uniform int32 CGU_DWORD; +typedef uniform uint8 CGU_UBYTE; +typedef uniform int CGU_INT; +typedef uniform int8 CGU_INT8; + +typedef uniform int16 CGU_INT16; +typedef uniform uint16 CGU_UINT16; +typedef uniform int32 CGU_INT32; +typedef uniform uint32 CGU_UINT32; +typedef uniform uint64 CGU_UINT64; + +typedef int CGV_INT; +typedef int8 CGV_INT8; +typedef int16 CGV_INT16; +typedef int32 CGV_INT32; +typedef uint16 CGV_UINT16; +typedef uint32 CGV_UINT32; +typedef uint64 CGV_UINT64; + +#endif // else ASPM_GPU + +typedef struct +{ + CGU_UINT32 m_src_width; + CGU_UINT32 m_src_height; + CGU_UINT32 m_width_in_blocks; + CGU_UINT32 m_height_in_blocks; + CGU_FLOAT m_fquality; } Source_Info; -typedef unsigned char* CGU_PTR; +typedef unsigned char* CGU_PTR; // Ref Compute_CPU_HPC -struct texture_surface { - CGU_PTR ptr; - CGU_INT width, - height, - stride; - CGU_INT channels; +struct texture_surface +{ + CGU_PTR ptr; + CGU_INT width, height, stride; + CGU_INT channels; }; +#endif // else ASPM_HLSL -#endif // else ASPM_HLSL - -#endif // Common_Def.h +#endif // Common_Def.h diff --git a/cmp_core/source/cmp_core.h b/cmp_core/source/cmp_core.h index b1e084f38..c355570f2 100644 --- a/cmp_core/source/cmp_core.h +++ b/cmp_core/source/cmp_core.h @@ -44,24 +44,24 @@ // Block level setting option: Create and Destroy Reference Pointers //====================================================================================================== // Context create and destroy to use for BCn codec settings, where n is the set [1,2,3,4,5,6,7] -// All codecs will use default max quality settings, users can create multiple contexts to +// All codecs will use default max quality settings, users can create multiple contexts to // set quality levels, masks , channel mapping, etc... -int32_t CMP_CDECL CreateOptionsBC1(void **optionsBC1); -int32_t CMP_CDECL CreateOptionsBC2(void **optionsBC2); -int32_t CMP_CDECL CreateOptionsBC3(void **optionsBC3); -int32_t CMP_CDECL CreateOptionsBC4(void **optionsBC4); -int32_t CMP_CDECL CreateOptionsBC5(void **optionsBC5); -int32_t CMP_CDECL CreateOptionsBC6(void **optionsBC6); -int32_t CMP_CDECL CreateOptionsBC7(void **optionsBC7); +int CMP_CDECL CreateOptionsBC1(void **optionsBC1); +int CMP_CDECL CreateOptionsBC2(void **optionsBC2); +int CMP_CDECL CreateOptionsBC3(void **optionsBC3); +int CMP_CDECL CreateOptionsBC4(void **optionsBC4); +int CMP_CDECL CreateOptionsBC5(void **optionsBC5); +int CMP_CDECL CreateOptionsBC6(void **optionsBC6); +int CMP_CDECL CreateOptionsBC7(void **optionsBC7); -int32_t CMP_CDECL DestroyOptionsBC1(void *optionsBC1); -int32_t CMP_CDECL DestroyOptionsBC2(void *optionsBC2); -int32_t CMP_CDECL DestroyOptionsBC3(void *optionsBC3); -int32_t CMP_CDECL DestroyOptionsBC4(void *optionsBC4); -int32_t CMP_CDECL DestroyOptionsBC5(void *optionsBC5); -int32_t CMP_CDECL DestroyOptionsBC6(void *optionsBC6); -int32_t CMP_CDECL DestroyOptionsBC7(void *optionsBC7); +int CMP_CDECL DestroyOptionsBC1(void *optionsBC1); +int CMP_CDECL DestroyOptionsBC2(void *optionsBC2); +int CMP_CDECL DestroyOptionsBC3(void *optionsBC3); +int CMP_CDECL DestroyOptionsBC4(void *optionsBC4); +int CMP_CDECL DestroyOptionsBC5(void *optionsBC5); +int CMP_CDECL DestroyOptionsBC6(void *optionsBC6); +int CMP_CDECL DestroyOptionsBC7(void *optionsBC7); //====================================================================================================== @@ -70,47 +70,43 @@ int32_t CMP_CDECL DestroyOptionsBC7(void *optionsBC7); // Setting channel Weights : Applies to BC1, BC2 and BC3 valid ranges are [0..1.0f] Default is {1.0f, 1.0f , 1.0f} // Use channel weightings. With swizzled formats the weighting applies to the data within the specified channel not the channel itself. -int32_t CMP_CDECL SetChannelWeightsBC1(void *options, float WeightRed, float WeightGreen, float WeightBlue); -int32_t CMP_CDECL SetChannelWeightsBC2(void *options, float WeightRed, float WeightGreen, float WeightBlue); -int32_t CMP_CDECL SetChannelWeightsBC3(void *options, float WeightRed, float WeightGreen, float WeightBlue); +int CMP_CDECL SetChannelWeightsBC1(void *options, float WeightRed, float WeightGreen, float WeightBlue); +int CMP_CDECL SetChannelWeightsBC2(void *options, float WeightRed, float WeightGreen, float WeightBlue); +int CMP_CDECL SetChannelWeightsBC3(void *options, float WeightRed, float WeightGreen, float WeightBlue); // True sets mapping CMP_Core BC1, BC2 & BC3 to decode Red,Green,Blue and Alpha as // RGBA to channels [0,1,2,3] else BGRA maps to [0,1,2,3] // Default is set to true. -int32_t CMP_CDECL SetDecodeChannelMapping(void* options, bool mapRGBA); +int CMP_CDECL SetDecodeChannelMapping(void *options, bool mapRGBA); -int32_t CMP_CDECL SetQualityBC1(void *options, float fquality); -int32_t CMP_CDECL SetQualityBC2(void *options, float fquality); -int32_t CMP_CDECL SetQualityBC3(void *options, float fquality); -int32_t CMP_CDECL SetQualityBC4(void *options, float fquality); -int32_t CMP_CDECL SetQualityBC5(void *options, float fquality); -int32_t CMP_CDECL SetQualityBC6(void *options, float fquality); -int32_t CMP_CDECL SetQualityBC7(void *options, float fquality); +int CMP_CDECL SetQualityBC1(void *options, float fquality); +int CMP_CDECL SetQualityBC2(void *options, float fquality); +int CMP_CDECL SetQualityBC3(void *options, float fquality); +int CMP_CDECL SetQualityBC4(void *options, float fquality); +int CMP_CDECL SetQualityBC5(void *options, float fquality); +int CMP_CDECL SetQualityBC6(void *options, float fquality); +int CMP_CDECL SetQualityBC7(void *options, float fquality); -int32_t CMP_CDECL SetAlphaThresholdBC1(void* options, uint8_t alphaThreshold); +int CMP_CDECL SetAlphaThresholdBC1(void *options, unsigned char alphaThreshold); -int32_t CMP_CDECL SetMaskBC6(void* options, uint32_t mask); -int32_t CMP_CDECL SetMaskBC7(void* options, uint8_t mask); +int CMP_CDECL SetMaskBC6(void *options, unsigned int mask); +int CMP_CDECL SetMaskBC7(void *options, unsigned char mask); -int32_t CMP_CDECL SetAlphaOptionsBC7(void *options, bool imageNeedsAlpha, bool colourRestrict, bool alphaRestrict); -int32_t CMP_CDECL SetErrorThresholdBC7(void *options, float minThreshold, float maxThreshold); +int CMP_CDECL SetAlphaOptionsBC7(void *options, bool imageNeedsAlpha, bool colourRestrict, bool alphaRestrict); +int CMP_CDECL SetErrorThresholdBC7(void *options, float minThreshold, float maxThreshold); // Set if the content is in sRGB color space (true) or linear (false). // The default is false. -int32_t CMP_CDECL SetGammaBC1(void *options, bool sRGB); -int32_t CMP_CDECL SetGammaBC2(void *options, bool sRGB); -int32_t CMP_CDECL SetGammaBC3(void *options, bool sRGB); -int32_t CMP_CDECL SetGammaBC7(void *options, bool sRGB); +int CMP_CDECL SetSrgbBC1(void* options, bool sRGB); +int CMP_CDECL SetSrgbBC2(void* options, bool sRGB); +int CMP_CDECL SetSrgbBC3(void* options, bool sRGB); // Set if the content is signed (true) or unsigned (false). // The default is false. -// For BC4 and BC5 this determines if the encoded or decoded byte is treated as SNORM or UNORM. // For BC6, the encoded or decoded data is always FP16, but affects the clamping of the values UF16 vs SF16. -int32_t CMP_CDECL SetSignedBC4(void *options, bool snorm); -int32_t CMP_CDECL SetSignedBC5(void *options, bool snorm); -int32_t CMP_CDECL SetSignedBC6(void *options, bool sf16); +int CMP_CDECL SetSignedBC6(void* options, bool sf16); //====================================================================================================== // (4x4) Block level 4 channel source CompressBlock and DecompressBlock API for BCn Codecs @@ -120,13 +116,13 @@ int32_t CMP_CDECL SetSignedBC6(void *options, bool sf16); // CompressBlockBC1(srcBlock,16,cmpBlock); For "C++" calls // // To use this parameter first create the options context using the CreateOptions call -// then use the Set Options to set various codec settings and pass them to the appropriate +// then use the Set Options to set various codec settings and pass them to the appropriate // Compress or Decompress API. // The source (srcBlock) channel format is expected to be RGBA:8888 by default for LDR Codecs // for BC6H the format is RGBA Half float (16 bits per channel) //------------------------------------------------------------------------------------------------------ #ifdef __cplusplus -#define CMP_DEFAULTNULL =nullptr +#define CMP_DEFAULTNULL =NULL #else #define CMP_DEFAULTNULL #endif @@ -135,53 +131,52 @@ int32_t CMP_CDECL SetSignedBC6(void *options, bool sf16); // 4 channel Sources, default format RGBA:8888 is processed as a 4x4 block starting at srcBlock location // where each row of the block is calculated from srcStride //========================================================================================================= -int32_t CMP_CDECL CompressBlockBC1(const uint8_t* srcBlock, uint32_t srcStrideInBytes, uint8_t cmpBlock[8 ], const void* options CMP_DEFAULTNULL); -int32_t CMP_CDECL CompressBlockBC2(const uint8_t* srcBlock, uint32_t srcStrideInBytes, uint8_t cmpBlock[16], const void* options CMP_DEFAULTNULL); -int32_t CMP_CDECL CompressBlockBC3(const uint8_t* srcBlock, uint32_t srcStrideInBytes, uint8_t cmpBlock[16], const void* options CMP_DEFAULTNULL); -int32_t CMP_CDECL CompressBlockBC7(const uint8_t* srcBlock, uint32_t srcStrideInBytes, uint8_t cmpBlock[16], const void* options CMP_DEFAULTNULL); +int CMP_CDECL CompressBlockBC1(const unsigned char *srcBlock, unsigned int srcStrideInBytes, unsigned char cmpBlock[8 ], const void *options CMP_DEFAULTNULL); +int CMP_CDECL CompressBlockBC2(const unsigned char *srcBlock, unsigned int srcStrideInBytes, unsigned char cmpBlock[16], const void *options CMP_DEFAULTNULL); +int CMP_CDECL CompressBlockBC3(const unsigned char *srcBlock, unsigned int srcStrideInBytes, unsigned char cmpBlock[16], const void *options CMP_DEFAULTNULL); +int CMP_CDECL CompressBlockBC7(const unsigned char *srcBlock, unsigned int srcStrideInBytes, unsigned char cmpBlock[16], const void *options CMP_DEFAULTNULL); -int32_t CMP_CDECL DecompressBlockBC1(const uint8_t cmpBlock[8 ], uint8_t srcBlock[64], const void* options CMP_DEFAULTNULL); -int32_t CMP_CDECL DecompressBlockBC2(const uint8_t cmpBlock[16], uint8_t srcBlock[64], const void* options CMP_DEFAULTNULL); -int32_t CMP_CDECL DecompressBlockBC3(const uint8_t cmpBlock[16], uint8_t srcBlock[64], const void* options CMP_DEFAULTNULL); -int32_t CMP_CDECL DecompressBlockBC7(const uint8_t cmpBlock[16], uint8_t srcBlock[64], const void* options CMP_DEFAULTNULL); +int CMP_CDECL DecompressBlockBC1(const unsigned char cmpBlock[8 ], unsigned char srcBlock[64], const void *options CMP_DEFAULTNULL); +int CMP_CDECL DecompressBlockBC2(const unsigned char cmpBlock[16], unsigned char srcBlock[64], const void *options CMP_DEFAULTNULL); +int CMP_CDECL DecompressBlockBC3(const unsigned char cmpBlock[16], unsigned char srcBlock[64], const void *options CMP_DEFAULTNULL); +int CMP_CDECL DecompressBlockBC7(const unsigned char cmpBlock[16], unsigned char srcBlock[64], const void *options CMP_DEFAULTNULL); //================================================ // 1 channel Source 4x4 8 bits per block //================================================ -int32_t CMP_CDECL CompressBlockBC4(const uint8_t* srcBlock, uint32_t srcStrideInBytes, uint8_t cmpBlock[8], const void* options CMP_DEFAULTNULL); -int32_t CMP_CDECL DecompressBlockBC4(const uint8_t cmpBlock[8], uint8_t srcBlock[16], const void* options CMP_DEFAULTNULL); - -int32_t CMP_CDECL CompressBlockBC4S(const int8_t* srcBlock, uint32_t srcStrideInBytes, int8_t cmpBlock[8], const void* options CMP_DEFAULTNULL); -int32_t CMP_CDECL DecompressBlockBC4S(const int8_t cmpBlock[8], int8_t srcBlock[16], const void* options CMP_DEFAULTNULL); +int CMP_CDECL CompressBlockBC4(const unsigned char *srcBlock, unsigned int srcStrideInBytes, unsigned char cmpBlock[8], const void *options CMP_DEFAULTNULL); +int CMP_CDECL DecompressBlockBC4(const unsigned char cmpBlock[8], unsigned char srcBlock[16], const void *options CMP_DEFAULTNULL); +// BC4 Signed channel +int CMP_CDECL CompressBlockBC4S(const char* srcBlock, unsigned int srcStrideInBytes, unsigned char cmpBlock[8], const void* options = NULL); +int CMP_CDECL DecompressBlockBC4S(const unsigned char cmpBlock[8], char srcBlock[16], const void* options CMP_DEFAULTNULL); //================================================ // 2 channel Source 2x(4x4 8 bits) //================================================ -int32_t CMP_CDECL CompressBlockBC5(const uint8_t* srcBlock1, - uint32_t srcStrideInBytes1, - const uint8_t* srcBlock2, - uint32_t srcStrideInBytes2, - uint8_t cmpBlock[16], - const void* options CMP_DEFAULTNULL); -int32_t CMP_CDECL DecompressBlockBC5(const uint8_t cmpBlock[16], uint8_t srcBlock1[16], uint8_t srcBlock2[16], const void* options CMP_DEFAULTNULL); - +int CMP_CDECL CompressBlockBC5(const unsigned char *srcBlock1, unsigned int srcStrideInBytes1, + const unsigned char *srcBlock2, unsigned int srcStrideInBytes2, + unsigned char cmpBlock[16], const void *options CMP_DEFAULTNULL); -int32_t CMP_CDECL CompressBlockBC5S(const int8_t* srcBlock1, - uint32_t srcStrideInBytes1, - const int8_t* srcBlock2, - uint32_t srcStrideInBytes2, - int8_t cmpBlock[16], - const void* options CMP_DEFAULTNULL); +int CMP_CDECL DecompressBlockBC5(const unsigned char cmpBlock[16], unsigned char srcBlock1[16], unsigned char srcBlock2[16], const void *options CMP_DEFAULTNULL); -int32_t CMP_CDECL DecompressBlockBC5S(const int8_t cmpBlock[16], int8_t srcBlock1[16], int8_t srcBlock2[16], - const void* options CMP_DEFAULTNULL); +// BC5 Signed channel +int CMP_CDECL CompressBlockBC5S(const char* srcBlock1, + unsigned int srcStrideInBytes1, + const char* srcBlock2, + unsigned int srcStrideInBytes2, + unsigned char cmpBlock[16], + const void* options CMP_DEFAULTNULL); +int CMP_CDECL DecompressBlockBC5S(const unsigned char cmpBlock[16], + char srcBlock1[16], + char srcBlock2[16], + const void* options CMP_DEFAULTNULL); //======================================================================================== // For 3 channel Source RGB_16, Note srcStride is in unsigned short steps (2 bytes each) //======================================================================================== -int32_t CMP_CDECL CompressBlockBC6(const uint16_t* srcBlock, uint32_t srcStrideInShorts, uint8_t cmpBlock[16], const void* options CMP_DEFAULTNULL); -int32_t CMP_CDECL DecompressBlockBC6(const uint8_t cmpBlock[16], uint16_t srcBlock[48], const void* options CMP_DEFAULTNULL); +int CMP_CDECL CompressBlockBC6(const unsigned short *srcBlock, unsigned int srcStrideInShorts, unsigned char cmpBlock[16], const void *options CMP_DEFAULTNULL); +int CMP_CDECL DecompressBlockBC6(const unsigned char cmpBlock[16], unsigned short srcBlock[48], const void *options CMP_DEFAULTNULL); #endif // CMP_CORE diff --git a/cmp_core/source/cmp_math_vec4.h b/cmp_core/source/cmp_math_vec4.h index 97b548b00..d4af27734 100644 --- a/cmp_core/source/cmp_math_vec4.h +++ b/cmp_core/source/cmp_math_vec4.h @@ -27,7 +27,7 @@ // Vector Class definitions for CPU & Intrinsics //==================================================== -#if defined (_LINUX) || defined (_WIN32) +#if defined(__linux__) || defined(_WIN32) //============================================= VEC2 ================================================== template class vec3; @@ -459,13 +459,15 @@ class Vec4 { }; }; +#ifdef CMP_USE_XMMINTRIN + #include #include "xmmintrin.h" #include #include // SSE Vec4 -#ifdef _LINUX +#ifdef __linux__ class CMP_SSEVec4f #else #include "intrin.h" @@ -476,7 +478,7 @@ class __declspec(align(16)) CMP_SSEVec4f union { __m128 vec128; // float Vector 128 bits in total (16 Bytes) = array of 4 floats -#ifdef _LINUX +#ifdef __linux__ float f32[4]; #endif }; @@ -504,7 +506,7 @@ class __declspec(align(16)) CMP_SSEVec4f }; // indexing -#ifdef _LINUX +#ifdef __linux__ inline const float& operator[](int i) const { return f32[i]; }; @@ -618,6 +620,8 @@ class __declspec(align(16)) CMP_SSEVec4f }; +#endif + typedef Vec4 CMP_Vec4f; typedef Vec4 CMP_Vec4d; typedef Vec4 CMP_Vec4i; diff --git a/cmp_core/test/cmakelists.txt b/cmp_core/test/cmakelists.txt index ae6ddb570..17bc8fe51 100644 --- a/cmp_core/test/cmakelists.txt +++ b/cmp_core/test/cmakelists.txt @@ -1,26 +1,34 @@ -add_executable(CMP_Core_Tests_BIN) +add_executable(test_core) -target_sources(CMP_Core_Tests_BIN - PRIVATE - blockconstants.h - compressonatortests.cpp - compressonatortests.h - testsmain.cpp -) +# CTestDashboardTargets for catch2 (enable for ref only!) +# add_subdirectory( +# ${PROJECT_EXTERNAL_LIBDIR}/catch2 +# ${PROJECT_EXTERNAL_LIBDIR}/catch2/bin +# ) -target_include_directories(CMP_Core_Tests_BIN PUBLIC - ${PROJECT_SOURCE_DIR}/../common/lib/ext/catch2 -) +target_sources(test_core + PRIVATE + testsmain.cpp + compressonatortests.cpp + compressonatortests.h + blockconstants.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/utilfuncs.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/utilfuncs.h + ) -target_link_libraries(CMP_Core_Tests_BIN - PRIVATE - CMP_Core - Plugin_Common_UtilFuncs -) +target_include_directories(test_core PUBLIC + ./ + ${PROJECT_SOURCE_DIR}/cmp_core/shaders + ${PROJECT_SOURCE_DIR}/cmp_core/source + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/ + ${PROJECT_SOURCE_DIR}/../common/lib/ext/catch2 + ) -set_target_properties(CMP_Core_Tests_BIN PROPERTIES FOLDER Tests) +target_link_libraries(test_core + CMP_Core + ) -add_test(NAME CMP_Core_Tests - COMMAND $ -) +set_target_properties(test_core + PROPERTIES FOLDER "Tests" + ) diff --git a/cmp_framework/cmakelists.txt b/cmp_framework/cmakelists.txt index c02ba5839..082965064 100644 --- a/cmp_framework/cmakelists.txt +++ b/cmp_framework/cmakelists.txt @@ -1,126 +1,107 @@ -add_library(CMP_FrameworkLIB INTERFACE) +cmake_minimum_required(VERSION 3.10) -target_sources(CMP_FrameworkLIB -INTERFACE - cmp_framework.h -) +add_library(CMP_Framework STATIC "") -target_include_directories(CMP_FrameworkLIB -INTERFACE - ./ -) -# add_library(CMP_Framework) -# -# source_group("Common" FILES -# ${FOLDER_COMMON} ${PROJECT_SOURCE_DIR}/Applications/_Plugins/Common/ATIFormats.cpp -# ${FOLDER_COMMON} ${PROJECT_SOURCE_DIR}/Applications/_Plugins/Common/ATIFormats.h -# ) -# -# target_sources(CMP_Framework -# INTERFACE -# CMP_Framework.h -# ) -# -# target_include_directories(CMP_Framework -# PRIVATE -# ${PROJECT_SOURCE_DIR}/CMP_CompressonatorLib -# ${PROJECT_SOURCE_DIR}/CMP_Framework/Common -# ${PROJECT_SOURCE_DIR}/CMP_Framework/Common/half -# ) -# -# target_link_libraries(CMP_Framework PRIVATE -# CMP_Common -# CMP_FrameworkLIB -# CompressonatorLIB -# Plugin_PluginManager -# ) +if(CMP_HOST_WINDOWS) + target_compile_definitions(CMP_Framework PUBLIC + -DCMP_USE_XMMINTRIN + ) +endif() -add_library(CMP_Framework STATIC "") -file(GLOB_RECURSE half - "common/half/*.h" - "common/half/*.cpp" - ) -file(GLOB_RECURSE DDS - "../applications/_plugins/cimage/dds/*h" - "../applications/_plugins/cimage/dds/*.cpp" - ) -file(GLOB_RECURSE HPC - "../applications/_plugins/ccmp_encode/hpc/*h" - "../applications/_plugins/ccmp_encode/hpc/*.cpp" - ) +file(GLOB_RECURSE HALF_SRC + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half/*.h + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half/*.cpp + ) +file(GLOB_RECURSE DDS_SRC + ${PROJECT_SOURCE_DIR}/applications/_plugins/cimage/dds/*.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/cimage/dds/*.cpp + ) +file(GLOB_RECURSE HPC_SRC + ${PROJECT_SOURCE_DIR}/applications/_plugins/ccmp_encode/hpc/*.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/ccmp_encode/hpc/*.cpp + ) +file(GLOB_RECURSE CORE_SHADERS_SRC + ${PROJECT_SOURCE_DIR}/cmp_core/shaders/*.h + ${PROJECT_SOURCE_DIR}/cmp_core/shaders/*.cpp + ) +file(GLOB_RECURSE CORE_SRC + ${PROJECT_SOURCE_DIR}/cmp_core/source/*.h + ${PROJECT_SOURCE_DIR}/cmp_core/source/*.cpp + ) +file(GLOB_RECURSE CMP_FRAMEWORK_SRC + ${PROJECT_SOURCE_DIR}/cmp_framework/*.h + ${PROJECT_SOURCE_DIR}/cmp_framework/*.cpp + ) +file(GLOB_RECURSE CMP_FRAMEWORK_COMMON_SRC + ${PROJECT_SOURCE_DIR}/cmp_framework/common/*.h + ${PROJECT_SOURCE_DIR}/cmp_framework/common/*.cpp + ) target_sources(CMP_Framework - PRIVATE - ../cmp_core/shaders/bc1_encode_kernel.h - ../cmp_core/shaders/bc1_encode_kernel.cpp - ../cmp_core/shaders/bc2_encode_kernel.h - ../cmp_core/shaders/bc2_encode_kernel.cpp - ../cmp_core/shaders/bc3_encode_kernel.h - ../cmp_core/shaders/bc3_encode_kernel.cpp - ../cmp_core/shaders/bc4_encode_kernel.h - ../cmp_core/shaders/bc4_encode_kernel.cpp - ../cmp_core/shaders/bc5_encode_kernel.h - ../cmp_core/shaders/bc5_encode_kernel.cpp - ../cmp_core/shaders/bc6_encode_kernel.h - ../cmp_core/shaders/bc6_encode_kernel.cpp - ../cmp_core/shaders/bc7_encode_kernel.h - ../cmp_core/shaders/bc7_encode_kernel.cpp - ../cmp_core/shaders/bcn_common_kernel.h - ../cmp_core/source/cmp_core.h - ../applications/_libs/cmp_math/cmp_math_common.h - ../applications/_libs/cmp_math/cmp_math_common.cpp - ../applications/_libs/cmp_math/cmp_math_cpuid.h - ../applications/_libs/cmp_math/cmp_math_cpuid.cpp - ../applications/_plugins/common/atiformats.cpp - ../applications/_plugins/common/atiformats.h - ../applications/_plugins/common/pluginbase.h - ../applications/_plugins/common/plugininterface.h - ../applications/_plugins/common/pluginmanager.h - ../applications/_plugins/common/pluginmanager.cpp - ../applications/_plugins/common/query_timer.h - ../applications/_plugins/common/query_timer.cpp - ../applications/_plugins/common/stb_image.h - ../applications/_plugins/common/tc_pluginapi.h - ../applications/_plugins/common/tc_plugininternal.h - ../applications/_plugins/common/tc_plugininternal.cpp - ../applications/_plugins/common/utilfuncs.h - ../applications/_plugins/common/utilfuncs.cpp - ../applications/_plugins/ccmp_sdk/bc1/bc1.h - ../applications/_plugins/ccmp_sdk/bc1/bc1.cpp - ../applications/_plugins/ccmp_sdk/bc2/bc2.h - ../applications/_plugins/ccmp_sdk/bc2/bc2.cpp - ../applications/_plugins/ccmp_sdk/bc3/bc3.h - ../applications/_plugins/ccmp_sdk/bc3/bc3.cpp - ../applications/_plugins/ccmp_sdk/bc4/bc4.h - ../applications/_plugins/ccmp_sdk/bc4/bc4.cpp - ../applications/_plugins/ccmp_sdk/bc5/bc5.h - ../applications/_plugins/ccmp_sdk/bc5/bc5.cpp - ../applications/_plugins/ccmp_sdk/bc6/bc6h.h - ../applications/_plugins/ccmp_sdk/bc6/bc6h.cpp - ../applications/_plugins/ccmp_sdk/bc7/bc7.h - ../applications/_plugins/ccmp_sdk/bc7/bc7.cpp - common/cmp_boxfilter.cpp - common/cmp_boxfilter.h - common/cmp_mips.cpp - common/cmp_mips.h - compute_base.cpp - compute_base.h - ${half} - ${DDS} - ${HPC} - ) + PRIVATE + ${HALF_SRC} + ${DDS_SRC} + ${HPC_SRC} + ${CORE_SHADERS_SRC} + ${CORE_SRC} + ${CMP_FRAMEWORK_SRC} + ${CMP_FRAMEWORK_COMMON_SRC} + + # CMP_Math + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_math/cmp_math_common.h + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_math/cmp_math_common.cpp + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_math/cmp_math_cpuid.h + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_math/cmp_math_cpuid.cpp + + # CMP_COMMON + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/atiformats.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/atiformats.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/pluginbase.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/plugininterface.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/pluginmanager.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/pluginmanager.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/query_timer.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/query_timer.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/stb_image.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/tc_pluginapi.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/tc_plugininternal.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/tc_plugininternal.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/utilfuncs.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/common/utilfuncs.cpp + + # CMP_SDK + ${PROJECT_SOURCE_DIR}/applications/_plugins/ccmp_sdk/bc1/bc1.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/ccmp_sdk/bc1/bc1.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/ccmp_sdk/bc2/bc2.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/ccmp_sdk/bc2/bc2.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/ccmp_sdk/bc3/bc3.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/ccmp_sdk/bc3/bc3.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/ccmp_sdk/bc4/bc4.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/ccmp_sdk/bc4/bc4.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/ccmp_sdk/bc5/bc5.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/ccmp_sdk/bc5/bc5.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/ccmp_sdk/bc6/bc6h.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/ccmp_sdk/bc6/bc6h.cpp + ${PROJECT_SOURCE_DIR}/applications/_plugins/ccmp_sdk/bc7/bc7.h + ${PROJECT_SOURCE_DIR}/applications/_plugins/ccmp_sdk/bc7/bc7.cpp + ) + + target_include_directories(CMP_Framework - PRIVATE - ../cmp_core/source - ../cmp_core/shaders - ../cmp_compressonatorlib - common/half/ - ../applications/_plugins/common/ - ../applications/_libs/cmp_math/ - ../applications/_libs/gpu_decode - ./ - ) + PRIVATE + ./ + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half + ${PROJECT_SOURCE_DIR}/cmp_core/source + ${PROJECT_SOURCE_DIR}/cmp_core/shaders + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib + ${PROJECT_SOURCE_DIR}/applications/_plugins/common + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_math + ${PROJECT_SOURCE_DIR}/applications/_libs/gpu_decode + ) + if (UNIX) -target_compile_definitions(CMP_Framework PRIVATE _LINUX) + target_compile_definitions(CMP_Framework PRIVATE _LINUX) endif() + +set_target_properties(CMP_Framework PROPERTIES FOLDER "Libs") diff --git a/cmp_framework/cmp_framework.h b/cmp_framework/cmp_framework.h index 5164a62e7..807189a5c 100644 --- a/cmp_framework/cmp_framework.h +++ b/cmp_framework/cmp_framework.h @@ -27,19 +27,20 @@ #include #include -typedef int CMP_INT; -typedef unsigned int CMP_UINT; -typedef bool CMP_BOOL; -typedef void CMP_VOID; -typedef float CMP_FLOAT; -typedef char CMP_SBYTE; -typedef unsigned char CMP_BYTE; -typedef std::uint16_t CMP_WORD; -typedef std::uint32_t CMP_DWORD; -typedef short CMP_HALFSHORT; -typedef std::vector CMP_VEC8; -typedef size_t CMP_DWORD_PTR; -typedef double CMP_DOUBLE; +typedef int CMP_INT; +typedef unsigned int CMP_UINT; +typedef bool CMP_BOOL; +typedef void CMP_VOID; +typedef float CMP_FLOAT; +typedef char CMP_SBYTE; +typedef char CMP_CHAR; +typedef unsigned char CMP_BYTE; +typedef std::uint16_t CMP_WORD; +typedef std::uint32_t CMP_DWORD; +typedef short CMP_HALFSHORT; +typedef std::vector CMP_VEC8; +typedef size_t CMP_DWORD_PTR; +typedef double CMP_DOUBLE; #if defined(WIN32) || defined(_WIN64) #define CMP_API __cdecl @@ -51,91 +52,102 @@ typedef double CMP_DOUBLE; // Common with Compressonator SDK //=================================== #ifndef CMP_FORMAT -typedef enum { - CMP_FORMAT_Unknown = 0, // Undefined texture format. +// Texture format. +typedef enum +{ + CMP_FORMAT_Unknown = 0, // Undefined texture format. // Channel Component formats -------------------------------------------------------------------------------- - CMP_FORMAT_ARGB_8888, // ARGB format with 8-bit fixed channels. - CMP_FORMAT_ABGR_8888, // ABGR format with 8-bit fixed channels. - CMP_FORMAT_RGBA_8888, // RGBA format with 8-bit fixed channels. - CMP_FORMAT_BGRA_8888, // BGRA format with 8-bit fixed channels. - CMP_FORMAT_RGB_888, // RGB format with 8-bit fixed channels. - CMP_FORMAT_BGR_888, // BGR format with 8-bit fixed channels. - CMP_FORMAT_RG_8, // Two component format with 8-bit fixed channels. - CMP_FORMAT_R_8, // Single component format with 8-bit fixed channels. - CMP_FORMAT_ARGB_2101010, // ARGB format with 10-bit fixed channels for color & a 2-bit fixed channel for alpha. - CMP_FORMAT_ARGB_16, // ARGB format with 16-bit fixed channels. - CMP_FORMAT_ABGR_16, // ABGR format with 16-bit fixed channels. - CMP_FORMAT_RGBA_16, // RGBA format with 16-bit fixed channels. - CMP_FORMAT_BGRA_16, // BGRA format with 16-bit fixed channels. + CMP_FORMAT_RGBA_8888_S, // RGBA format with signed 8-bit fixed channels. + CMP_FORMAT_ARGB_8888_S, // ARGB format with signed 8-bit fixed channels. + CMP_FORMAT_ARGB_8888, // ARGB format with 8-bit fixed channels. + CMP_FORMAT_ABGR_8888, // ABGR format with 8-bit fixed channels. + CMP_FORMAT_RGBA_8888, // RGBA format with 8-bit fixed channels. + CMP_FORMAT_BGRA_8888, // BGRA format with 8-bit fixed channels. + CMP_FORMAT_RGB_888, // RGB format with 8-bit fixed channels. + CMP_FORMAT_RGB_888_S, // RGB format with 8-bit fixed channels. + CMP_FORMAT_BGR_888, // BGR format with 8-bit fixed channels. + CMP_FORMAT_RG_8_S, // Two component format with signed 8-bit fixed channels. + CMP_FORMAT_RG_8, // Two component format with 8-bit fixed channels. + CMP_FORMAT_R_8_S, // Single component format with signed 8-bit fixed channel. + CMP_FORMAT_R_8, // Single component format with 8-bit fixed channel. + CMP_FORMAT_ARGB_2101010, // ARGB format with 10-bit fixed channels for color & a 2-bit fixed channel for alpha. + CMP_FORMAT_ARGB_16, // ARGB format with 16-bit fixed channels. + CMP_FORMAT_ABGR_16, // ABGR format with 16-bit fixed channels. + CMP_FORMAT_RGBA_16, // RGBA format with 16-bit fixed channels. + CMP_FORMAT_BGRA_16, // BGRA format with 16-bit fixed channels. CMP_FORMAT_RG_16, // Two component format with 16-bit fixed channels. - CMP_FORMAT_R_16, // Single component format with 16-bit fixed channels. - CMP_FORMAT_RGBE_32F, // RGB format with 9-bit floating point each channel and shared 5 bit exponent - CMP_FORMAT_ARGB_16F, // ARGB format with 16-bit floating-point channels. - CMP_FORMAT_ABGR_16F, // ABGR format with 16-bit floating-point channels. - CMP_FORMAT_RGBA_16F, // RGBA format with 16-bit floating-point channels. - CMP_FORMAT_BGRA_16F, // BGRA format with 16-bit floating-point channels. - CMP_FORMAT_RG_16F, // Two component format with 16-bit floating-point channels. + CMP_FORMAT_R_16, // Single component format with 16-bit fixed channels. + CMP_FORMAT_RGBE_32F, // RGB format with 9-bit floating point each channel and shared 5 bit exponent + CMP_FORMAT_ARGB_16F, // ARGB format with 16-bit floating-point channels. + CMP_FORMAT_ABGR_16F, // ABGR format with 16-bit floating-point channels. + CMP_FORMAT_RGBA_16F, // RGBA format with 16-bit floating-point channels. + CMP_FORMAT_BGRA_16F, // BGRA format with 16-bit floating-point channels. + CMP_FORMAT_RG_16F, // Two component format with 16-bit floating-point channels. CMP_FORMAT_R_16F, // Single component with 16-bit floating-point channels. - CMP_FORMAT_ARGB_32F, // ARGB format with 32-bit floating-point channels. - CMP_FORMAT_ABGR_32F, // ABGR format with 32-bit floating-point channels. - CMP_FORMAT_RGBA_32F, // RGBA format with 32-bit floating-point channels. - CMP_FORMAT_BGRA_32F, // BGRA format with 32-bit floating-point channels. - CMP_FORMAT_RGB_32F, // RGB format with 32-bit floating-point channels. - CMP_FORMAT_BGR_32F, // BGR format with 32-bit floating-point channels. - CMP_FORMAT_RG_32F, // Two component format with 32-bit floating-point channels. + CMP_FORMAT_ARGB_32F, // ARGB format with 32-bit floating-point channels. + CMP_FORMAT_ABGR_32F, // ABGR format with 32-bit floating-point channels. + CMP_FORMAT_RGBA_32F, // RGBA format with 32-bit floating-point channels. + CMP_FORMAT_BGRA_32F, // BGRA format with 32-bit floating-point channels. + CMP_FORMAT_RGB_32F, // RGB format with 32-bit floating-point channels. + CMP_FORMAT_BGR_32F, // BGR format with 32-bit floating-point channels. + CMP_FORMAT_RG_32F, // Two component format with 32-bit floating-point channels. CMP_FORMAT_R_32F, // Single component with 32-bit floating-point channels. - // Compression formats ----------------------------------------------------------------------------------- - CMP_FORMAT_ASTC, // ASTC (Adaptive Scalable Texture Compression) open texture compression standard - CMP_FORMAT_ATI1N, // Single component compression format using the same technique as DXT5 alpha. Four bits per pixel. - CMP_FORMAT_ATI2N, // Two component compression format using the same technique as DXT5 alpha. Designed for compression of tangent space normal maps. Eight bits per pixel. - CMP_FORMAT_ATI2N_XY, // Two component compression format using the same technique as DXT5 alpha. The same as ATI2N but with the channels swizzled. Eight bits per pixel. - CMP_FORMAT_ATI2N_DXT5, // ATI2N like format using DXT5. Intended for use on GPUs that do not natively support ATI2N. Eight bits per pixel. - CMP_FORMAT_ATC_RGB, // CMP - a compressed RGB format. - CMP_FORMAT_ATC_RGBA_Explicit, // CMP - a compressed ARGB format with explicit alpha. - CMP_FORMAT_ATC_RGBA_Interpolated, // CMP - a compressed ARGB format with interpolated alpha. - CMP_FORMAT_BC1, // A four component opaque (or 1-bit alpha) compressed texture format for Microsoft DirectX10. Identical to DXT1. Four bits per pixel. - CMP_FORMAT_BC2, // A four component compressed texture format with explicit alpha for Microsoft DirectX10. Identical to DXT3. Eight bits per pixel. - CMP_FORMAT_BC3, // A four component compressed texture format with interpolated alpha for Microsoft DirectX10. Identical to DXT5. Eight bits per pixel. - CMP_FORMAT_BC4, // A single component compressed texture format for Microsoft DirectX10. Identical to ATI1N. Four bits per pixel. - CMP_FORMAT_BC4_S,// A single component compressed texture format for Microsoft DirectX10. Identical to ATI1N. Four bits per pixel. - CMP_FORMAT_BC5, // A two component compressed texture format for Microsoft DirectX10. Identical to ATI2N_XY. Eight bits per pixel. - CMP_FORMAT_BC5_S,// A two component compressed texture format for Microsoft DirectX10. Identical to ATI2N_XY. Eight bits per pixel. - CMP_FORMAT_BC6H,// BC6H compressed texture format (UF) - CMP_FORMAT_BC6H_SF,// BC6H compressed texture format (SF) - CMP_FORMAT_BC7, // BC7 compressed texture format - CMP_FORMAT_DXT1,// An DXTC compressed texture matopaque (or 1-bit alpha). Four bits per pixel. - CMP_FORMAT_DXT3,// DXTC compressed texture format with explicit alpha. Eight bits per pixel. - CMP_FORMAT_DXT5,// DXTC compressed texture format with interpolated alpha. Eight bits per pixel. - CMP_FORMAT_DXT5_xGBR, // DXT5 with the red component swizzled into the alpha channel. Eight bits per pixel. - CMP_FORMAT_DXT5_RxBG, // swizzled DXT5 format with the green component swizzled into the alpha channel. Eight bits per pixel. - CMP_FORMAT_DXT5_RBxG, // swizzled DXT5 format with the green component swizzled into the alpha channel & the blue component swizzled into the green channel. Eight bits per pixel. - CMP_FORMAT_DXT5_xRBG, // swizzled DXT5 format with the green component swizzled into the alpha channel & the red component swizzled into the green channel. Eight bits per pixel. - CMP_FORMAT_DXT5_RGxB, // swizzled DXT5 format with the blue component swizzled into the alpha channel. Eight bits per pixel. - CMP_FORMAT_DXT5_xGxR, // two-component swizzled DXT5 format with the red component swizzled into the alpha channel & the green component in the green channel. Eight bits per pixel. - CMP_FORMAT_ETC_RGB, // ETC GL_COMPRESSED_RGB8_ETC2 backward compatible - CMP_FORMAT_ETC2_RGB, // ETC2 GL_COMPRESSED_RGB8_ETC2 - CMP_FORMAT_ETC2_SRGB, // ETC2 GL_COMPRESSED_SRGB8_ETC2 - CMP_FORMAT_ETC2_RGBA, // ETC2 GL_COMPRESSED_RGBA8_ETC2_EAC - CMP_FORMAT_ETC2_RGBA1, // ETC2 GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 - CMP_FORMAT_ETC2_SRGBA, // ETC2 GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC - CMP_FORMAT_ETC2_SRGBA1, // ETC2 GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 - CMP_FORMAT_PVRTC, - - CMP_FORMAT_APC, //< APC Texture Compressor + // Compression formats ------------ GPU Mapping DirectX, Vulkan and OpenGL formats and comments -------- + CMP_FORMAT_ASTC, // DXGI_FORMAT_UNKNOWN VK_FORMAT_ASTC_4x4_UNORM_BLOCK to VK_FORMAT_ASTC_12x12_UNORM_BLOCK + CMP_FORMAT_ATI1N, // DXGI_FORMAT_BC4_UNORM VK_FORMAT_BC4_UNORM_BLOCK GL_COMPRESSED_RED_RGTC1 Single component compression format using the same technique as DXT5 alpha. Four bits per pixel. + CMP_FORMAT_ATI2N, // DXGI_FORMAT_BC5_UNORM VK_FORMAT_BC5_UNORM_BLOCK GL_COMPRESSED_RG_RGTC2 Two component compression format using the same technique as DXT5 alpha. Designed for compression of tangent space normal maps. Eight bits per pixel. + CMP_FORMAT_ATI2N_XY, // DXGI_FORMAT_BC5_UNORM VK_FORMAT_BC5_UNORM_BLOCK GL_COMPRESSED_RG_RGTC2 Two component compression format using the same technique as DXT5 alpha. The same as ATI2N but with the channels swizzled. Eight bits per pixel. + CMP_FORMAT_ATI2N_DXT5, // DXGI_FORMAT_BC5_UNORM VK_FORMAT_BC5_UNORM_BLOCK GL_COMPRESSED_RG_RGTC2 ATI2N like format using DXT5. Intended for use on GPUs that do not natively support ATI2N. Eight bits per pixel. + CMP_FORMAT_ATC_RGB, // CMP - a compressed RGB format. + CMP_FORMAT_ATC_RGBA_Explicit, // CMP - a compressed ARGB format with explicit alpha. + CMP_FORMAT_ATC_RGBA_Interpolated, // CMP - a compressed ARGB format with interpolated alpha. + CMP_FORMAT_BC1, // DXGI_FORMAT_BC1_UNORM GL_COMPRESSED_RGBA_S3TC_DXT1_EXT A four component opaque (or 1-bit alpha) compressed texture format for Microsoft DirectX10. Identical to DXT1. Four bits per pixel. + CMP_FORMAT_BC2, // DXGI_FORMAT_BC2_UNORM VK_FORMAT_BC2_UNORM_BLOCK GL_COMPRESSED_RGBA_S3TC_DXT3_EXT A four component compressed texture format with explicit alpha for Microsoft DirectX10. Identical to DXT3. Eight bits per pixel. + CMP_FORMAT_BC3, // DXGI_FORMAT_BC3_UNORM VK_FORMAT_BC3_UNORM_BLOCK GL_COMPRESSED_RGBA_S3TC_DXT5_EXT A four component compressed texture format with interpolated alpha for Microsoft DirectX10. Identical to DXT5. Eight bits per pixel. + CMP_FORMAT_BC4, // DXGI_FORMAT_BC4_UNORM VK_FORMAT_BC4_UNORM_BLOCK GL_COMPRESSED_RED_RGTC1 A single component compressed texture format for Microsoft DirectX10. Identical to ATI1N. Four bits per pixel. + CMP_FORMAT_BC4_S, // DXGI_FORMAT_BC4_SNORM VK_FORMAT_BC4_SNORM_BLOCK GL_COMPRESSED_SIGNED_RED_RGTC1 A single component compressed texture format for Microsoft DirectX10. Identical to ATI1N. Four bits per pixel. + CMP_FORMAT_BC5, // DXGI_FORMAT_BC5_UNORM VK_FORMAT_BC5_UNORM_BLOCK GL_COMPRESSED_RG_RGTC2 A two component compressed texture format for Microsoft DirectX10. Identical to ATI2N_XY. Eight bits per pixel. + CMP_FORMAT_BC5_S, // DXGI_FORMAT_BC5_SNORM VK_FORMAT_BC5_SNORM_BLOCK GL_COMPRESSED_RGBA_BPTC_UNORM A two component compressed texture format for Microsoft DirectX10. Identical to ATI2N_XY. Eight bits per pixel. + CMP_FORMAT_BC6H, // DXGI_FORMAT_BC6H_UF16 VK_FORMAT_BC6H_UFLOAT_BLOCK GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT BC6H compressed texture format (UF) + CMP_FORMAT_BC6H_SF, // DXGI_FORMAT_BC6H_SF16 VK_FORMAT_BC6H_SFLOAT_BLOCK GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT BC6H compressed texture format (SF) + CMP_FORMAT_BC7, // DXGI_FORMAT_BC7_UNORM VK_FORMAT_BC7_UNORM_BLOCK GL_COMPRESSED_RGBA_BPTC_UNORM BC7 compressed texture format + CMP_FORMAT_DXT1, // DXGI_FORMAT_BC1_UNORM VK_FORMAT_BC1_RGB_UNORM_BLOCK GL_COMPRESSED_RGBA_S3TC_DXT1_EXT An DXTC compressed texture matopaque (or 1-bit alpha). Four bits per pixel. + CMP_FORMAT_DXT3, // DXGI_FORMAT_BC2_UNORM VK_FORMAT_BC2_UNORM_BLOCK GL_COMPRESSED_RGBA_S3TC_DXT3_EXT DXTC compressed texture format with explicit alpha. Eight bits per pixel. + CMP_FORMAT_DXT5, // DXGI_FORMAT_BC3_UNORM VK_FORMAT_BC3_UNORM_BLOCK GL_COMPRESSED_RGBA_S3TC_DXT5_EXT DXTC compressed texture format with interpolated alpha. Eight bits per pixel. + CMP_FORMAT_DXT5_xGBR, // DXGI_FORMAT_UNKNOWN DXT5 with the red component swizzled into the alpha channel. Eight bits per pixel. + CMP_FORMAT_DXT5_RxBG, // DXGI_FORMAT_UNKNOWN swizzled DXT5 format with the green component swizzled into the alpha channel. Eight bits per pixel. + CMP_FORMAT_DXT5_RBxG, // DXGI_FORMAT_UNKNOWN swizzled DXT5 format with the green component swizzled into the alpha channel & the blue component swizzled into the green channel. Eight bits per pixel. + CMP_FORMAT_DXT5_xRBG, // DXGI_FORMAT_UNKNOWN swizzled DXT5 format with the green component swizzled into the alpha channel & the red component swizzled into the green channel. Eight bits per pixel. + CMP_FORMAT_DXT5_RGxB, // DXGI_FORMAT_UNKNOWN swizzled DXT5 format with the blue component swizzled into the alpha channel. Eight bits per pixel. + CMP_FORMAT_DXT5_xGxR, // two-component swizzled DXT5 format with the red component swizzled into the alpha channel & the green component in the green channel. Eight bits per pixel. + CMP_FORMAT_ETC_RGB, // DXGI_FORMAT_UNKNOWN VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK GL_COMPRESSED_RGB8_ETC2 backward compatible + CMP_FORMAT_ETC2_RGB, // DXGI_FORMAT_UNKNOWN VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK GL_COMPRESSED_RGB8_ETC2 + CMP_FORMAT_ETC2_SRGB, // DXGI_FORMAT_UNKNOWN VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK GL_COMPRESSED_SRGB8_ETC2 + CMP_FORMAT_ETC2_RGBA, // DXGI_FORMAT_UNKNOWN VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK GL_COMPRESSED_RGBA8_ETC2_EAC + CMP_FORMAT_ETC2_RGBA1, // DXGI_FORMAT_UNKNOWN VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 + CMP_FORMAT_ETC2_SRGBA, // DXGI_FORMAT_UNKNOWN VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC + CMP_FORMAT_ETC2_SRGBA1, // DXGI_FORMAT_UNKNOWN VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 + CMP_FORMAT_PVRTC, // +#ifdef USE_APC + CMP_FORMAT_APC, //< APC Texture Compressor +#endif // Transcoder formats - ------------------------------------------------------------ - CMP_FORMAT_GTC, //< GTC Fast Gradient Texture Compressor - CMP_FORMAT_BASIS, //< BASIS compression + CMP_FORMAT_GTC, //< GTC Fast Gradient Texture Compressor + CMP_FORMAT_BASIS, //< BASIS compression // End of list CMP_FORMAT_MAX = CMP_FORMAT_BASIS } CMP_FORMAT; + #endif /// Compress error codes #ifndef CMP_ERROR -typedef enum { +// Compress error codes +typedef enum +{ CMP_OK = 0, // Ok. CMP_ABORTED, // The conversion was aborted. CMP_ERR_INVALID_SOURCE_TEXTURE, // The source texture is invalid. @@ -158,6 +170,11 @@ typedef enum { CMP_ERR_UNABLE_TO_LOAD_ENCODER, // Unable to load an encode library CMP_ERR_NOSHADER_CODE_DEFINED, // No shader code is available for the requested framework CMP_ERR_GPU_DOESNOT_SUPPORT_COMPUTE, // The GPU device selected does not support compute + CMP_ERR_NOPERFSTATS, // No Performance Stats are available + CMP_ERR_GPU_DOESNOT_SUPPORT_CMP_EXT, // The GPU does not support the requested compression extension! + CMP_ERR_GAMMA_OUTOFRANGE, // Gamma value set for processing is out of range + CMP_ERR_PLUGIN_SHAREDIO_NOT_SET, // The plugin C_PluginSetSharedIO call was not set and is required for this plugin to operate + CMP_ERR_UNABLE_TO_INIT_D3DX, // Unable to initialize DirectX SDK or get a specific DX API CMP_ERR_GENERIC // An unknown error occurred. } CMP_ERROR; #endif @@ -167,55 +184,62 @@ typedef enum { //=================================== /// The format of data in the channels of texture. -typedef enum { - CF_8bit = 0, // 8-bit integer data. - CF_Float16 = 1, // 16-bit float data. - CF_Float32 = 2, // 32-bit float data. - CF_Compressed = 3, // Compressed data. - CF_16bit = 4, // 16-bit integer data. - CF_2101010 = 5, // 10-bit integer data in the color channels & 2-bit integer data in the alpha channel. - CF_32bit = 6, // 32-bit integer data. - CF_Float9995E = 7, // 32-bit partial precision float. - CF_YUV_420 = 8, // YUV Chroma formats - CF_YUV_422 = 9, // YUV Chroma formats - CF_YUV_444 = 10, // YUV Chroma formats - CF_YUV_4444 = 11, // YUV Chroma formats +typedef enum +{ + CF_8bit = 0, // 8-bit integer data. + CF_Float16 = 1, // 16-bit float data. + CF_Float32 = 2, // 32-bit float data. + CF_Compressed = 3, // Compressed data. + CF_16bit = 4, // 16-bit integer data. + CF_2101010 = 5, // 10-bit integer data in the color channels & 2-bit integer data in the alpha channel. + CF_32bit = 6, // 32-bit integer data. + CF_Float9995E = 7, // 32-bit partial precision float. + CF_YUV_420 = 8, // YUV Chroma formats + CF_YUV_422 = 9, // YUV Chroma formats + CF_YUV_444 = 10, // YUV Chroma formats + CF_YUV_4444 = 11, // YUV Chroma formats } CMP_ChannelFormat; -typedef CMP_ChannelFormat ChannelFormat; +typedef CMP_ChannelFormat ChannelFormat; /// The type of data the texture represents. -typedef enum { - TDT_XRGB = 0, // An RGB texture padded to DWORD width. - TDT_ARGB = 1, // An ARGB texture. +typedef enum +{ + TDT_XRGB = 0, // An RGB texture padded to DWORD width. + TDT_ARGB = 1, // An ARGB texture. TDT_NORMAL_MAP = 2, // A normal map. - TDT_R = 3, // A single component texture. - TDT_RG = 4, // A two component texture. - TDT_YUV_SD = 5, // An YUB Standard Definition texture. - TDT_YUV_HD = 6, // An YUB High Definition texture. + TDT_R = 3, // A single component texture. + TDT_RG = 4, // A two component texture. + TDT_YUV_SD = 5, // An YUB Standard Definition texture. + TDT_YUV_HD = 6, // An YUB High Definition texture. + TDT_RGB = 7 } CMP_TextureDataType; typedef CMP_TextureDataType TextureDataType; /// The type of the texture. -typedef enum { - TT_2D = 0, // A regular 2D texture. data stored linearly (rgba,rgba,...rgba) - TT_CubeMap = 1, // A cubemap texture. +typedef enum +{ + TT_2D = 0, // A regular 2D texture. data stored linearly (rgba,rgba,...rgba) + TT_CubeMap = 1, // A cubemap texture. TT_VolumeTexture = 2, // A volume texture. - TT__2D_Block = 3, // 2D texture data stored as [Height][Width] blocks as individual channels using cmp_rgb_t or cmp_yuv_t - TT_Unknown = 4, // Unknown type of texture : No data is stored for this type + TT__2D_Block = 3, // 2D texture data stored as [Height][Width] blocks as individual channels using cmp_rgb_t or cmp_yuv_t + TT_Unknown = 4, // Unknown type of texture : No data is stored for this type } CMP_TextureType; typedef CMP_TextureType TextureType; -typedef struct { - union { +typedef struct +{ + union + { CMP_BYTE rgba[4]; ///< The color as an array of components. CMP_DWORD asDword; ///< The color as a DWORD. }; } CMP_COLOR; -typedef struct { +typedef struct +{ int nFilterType; // This is either CPU Box Filter or GPU Based DXD3X Filters unsigned long dwMipFilterOptions; // Selects options for the Filter Type int nMinSize; // Minimum MipMap Level requested @@ -228,25 +252,27 @@ typedef struct { /// \sa \link TC_AppAllocateMipLevelData() TC_AppAllocateMipLevelData \endlink, /// \link TC_AppAllocateCompressedMipLevelData() TC_AppAllocateCompressedMipLevelData \endlink, /// \link MipSet \endlink -typedef struct { - CMP_INT m_nWidth; ///< Width of the data in pixels. - CMP_INT m_nHeight; ///< Height of the data in pixels. - CMP_DWORD m_dwLinearSize; ///< Size of the data in bytes. - union { - CMP_SBYTE* m_psbData; // pointer signed 8 bit.data blocks - CMP_BYTE* m_pbData; // pointer unsigned 8 bit.data blocks - CMP_WORD* m_pwData; // pointer unsigned 16 bit.data blocks - CMP_COLOR* m_pcData; // pointer to a union (array of 4 unsigned 8 bits or one 32 bit) data blocks - CMP_FLOAT* m_pfData; // pointer to 32-bit signed float data blocks - CMP_HALFSHORT* m_phfsData; // pointer to 16 bit short data blocks - CMP_DWORD* m_pdwData; // pointer to 32 bit data blocks - CMP_VEC8* m_pvec8Data; // std::vector unsigned 8 bits data blocks +typedef struct +{ + CMP_INT m_nWidth; ///< Width of the data in pixels. + CMP_INT m_nHeight; ///< Height of the data in pixels. + CMP_DWORD m_dwLinearSize; ///< Size of the data in bytes. + union + { + CMP_SBYTE* m_psbData; // pointer signed 8 bit.data blocks + CMP_BYTE* m_pbData; // pointer unsigned 8 bit.data blocks + CMP_WORD* m_pwData; // pointer unsigned 16 bit.data blocks + CMP_COLOR* m_pcData; // pointer to a union (array of 4 unsigned 8 bits or one 32 bit) data blocks + CMP_FLOAT* m_pfData; // pointer to 32-bit signed float data blocks + CMP_HALFSHORT* m_phfsData; // pointer to 16 bit short data blocks + CMP_DWORD* m_pdwData; // pointer to 32 bit data blocks + CMP_VEC8* m_pvec8Data; // std::vector unsigned 8 bits data blocks }; } CMP_MipLevel; -typedef CMP_MipLevel MipLevel; +typedef CMP_MipLevel MipLevel; -typedef CMP_MipLevel* CMP_MipLevelTable; ///< A pointer to a set of MipLevels. +typedef CMP_MipLevel* CMP_MipLevelTable; ///< A pointer to a set of MipLevels. // Each texture and all its mip-map levels are encapsulated in a MipSet. // Do not depend on m_pMipLevelTable being there, it is an implementation detail that you see only because there is no easy cross-complier @@ -256,41 +282,50 @@ typedef CMP_MipLevel* CMP_MipLevelTable; ///< A pointer to a set of MipLevels. // Cube maps have multiple faces or sides for each mip-map level . Instead of making a totally new data type, we just made each one of these faces be represented by a MipLevel, even though the terminology can be a bit confusing at first. So if your cube map consists of 6 faces for each mip-map level, then your first mip-map level will consist of 6 MipLevels, each having the same m_nWidth, m_nHeight. The next mip-map level will have half the m_nWidth & m_nHeight as the previous, but will be composed of 6 MipLevels still. // A volume texture is a 3D texture. Again, instead of creating a new data type, we chose to make use of multiple MipLevels to create a single mip-map level of a volume texture. So a single mip-map level of a volume texture will consist of many MipLevels, all having the same m_nWidth and m_nHeight. The next mip-map level will have m_nWidth and m_nHeight half of the previous mip-map level's (to a minimum of 1) and will be composed of half as many MipLevels as the previous mip-map level (the first mip-map level takes this number from the MipSet it's part of), to a minimum of one. -typedef struct { - CMP_INT m_nWidth; // User Setting: Width in pixels of the topmost mip-map level of the mip-map set. Initialized by TC_AppAllocateMipSet. - CMP_INT m_nHeight; // User Setting: Height in pixels of the topmost mip-map level of the mip-map set. Initialized by TC_AppAllocateMipSet. - CMP_INT m_nDepth; // User Setting: Depth in MipLevels of the topmost mip-map level of the mip-map set. Initialized by TC_AppAllocateMipSet. See Remarks. - CMP_FORMAT m_format; // User Setting: Format for this MipSet +typedef struct +{ + CMP_INT m_nWidth; // User Setting: Width in pixels of the topmost mip-map level of the mip-map set. Initialized by TC_AppAllocateMipSet. + CMP_INT m_nHeight; // User Setting: Height in pixels of the topmost mip-map level of the mip-map set. Initialized by TC_AppAllocateMipSet. + CMP_INT m_nDepth; // User Setting: Depth in MipLevels of the topmost mip-map level of the mip-map set. Initialized by TC_AppAllocateMipSet. See Remarks. + CMP_FORMAT m_format; // User Setting: Format for this MipSet // set by various API for internal use and user ref - ChannelFormat m_ChannelFormat; // A texture is usually composed of channels, such as RGB channels for a texture with red green and blue image data. m_ChannelFormat indicates the representation of each of these channels. So a texture where each channel is an 8 bit integer would have CF_8bit for this. A compressed texture would use CF_Compressed. - TextureDataType m_TextureDataType; // An indication of the type of data that the texture contains. A texture with just RGB values would use TDT_XRGB, while a texture that also uses the alpha channel would use TDT_ARGB. - TextureType m_TextureType; // Indicates whether the texture is 2D, a cube map, or a volume texture. Used to determine how to treat MipLevels, among other things. - CMP_UINT m_Flags; // Flags that mip-map set. - CMP_BYTE m_CubeFaceMask; // A mask of MS_CubeFace values indicating which cube-map faces are present. - CMP_DWORD m_dwFourCC; // The FourCC for this mip-map set. 0 if the mip-map set is uncompressed. Generated using MAKEFOURCC (defined in the Platform SDK or DX SDK). - CMP_DWORD m_dwFourCC2; // An extra FourCC used by The Compressonator internally. Our DDS plugin saves/loads m_dwFourCC2 from pDDSD ddpfPixelFormat.dwPrivateFormatBitCount (since it's not really used by anything else) whether or not it is 0. Generated using MAKEFOURCC (defined in the Platform SDK or DX SDK). The FourCC2 field is currently used to allow differentiation between the various swizzled DXT5 formats. These formats must have a FourCC of DXT5 to be supported by the DirectX runtime but The Compressonator needs to know the swizzled FourCC to correctly display the texture. - CMP_INT m_nMaxMipLevels; // Set by The Compressonator when you call TC_AppAllocateMipSet based on the width, height, depth, and textureType values passed in. Is really the maximum number of mip-map levels possible for that texture including the topmost mip-map level if you integer divide width height and depth by 2, rounding down but never falling below 1 until all three of them are 1. So a 5x10 2D texture would have a m_nMaxMipLevels of 4 (5x10 2x5 1x2 1x1). - CMP_INT m_nMipLevels; // The number of mip-map levels in the mip-map set that actually have data. Always less than or equal to m_nMaxMipLevels. Set to 0 after TC_AppAllocateMipSet. - CMP_FORMAT m_transcodeFormat; // For universal format: Sets the target data format for data processing and analysis - CMP_BOOL m_compressed; // New Flags if data is compressed (example Block Compressed data in form of BCxx) - CMP_FORMAT m_isDeCompressed; // The New MipSet is a decompressed result from a prior Compressed MipSet Format specified - CMP_BOOL m_swizzle; // Flag is used by image load and save to indicate channels were swizzled from the origial source - CMP_BYTE m_nBlockWidth; // Width in pixels of the Compression Block that is to be processed default for ASTC is 4 - CMP_BYTE m_nBlockHeight; // Height in pixels of the Compression Block that is to be processed default for ASTC is 4 - CMP_BYTE m_nBlockDepth; // Depth in pixels of the Compression Block that is to be processed default for ASTC is 1 - CMP_BYTE m_nChannels; // Number of channels used min is 1 max is 4, 0 defaults to 1 - CMP_BYTE m_isSigned; // channel data is signed (has + and - data values) + ChannelFormat + m_ChannelFormat; // A texture is usually composed of channels, such as RGB channels for a texture with red green and blue image data. m_ChannelFormat indicates the representation of each of these channels. So a texture where each channel is an 8 bit integer would have CF_8bit for this. A compressed texture would use CF_Compressed. + TextureDataType + m_TextureDataType; // An indication of the type of data that the texture contains. A texture with just RGB values would use TDT_XRGB, while a texture that also uses the alpha channel would use TDT_ARGB. + TextureType + m_TextureType; // Indicates whether the texture is 2D, a cube map, or a volume texture. Used to determine how to treat MipLevels, among other things. + CMP_UINT m_Flags; // Flags that mip-map set. + CMP_BYTE m_CubeFaceMask; // A mask of MS_CubeFace values indicating which cube-map faces are present. + CMP_DWORD + m_dwFourCC; // The FourCC for this mip-map set. 0 if the mip-map set is uncompressed. Generated using MAKEFOURCC (defined in the Platform SDK or DX SDK). + CMP_DWORD + m_dwFourCC2; // An extra FourCC used by The Compressonator internally. Our DDS plugin saves/loads m_dwFourCC2 from pDDSD ddpfPixelFormat.dwPrivateFormatBitCount (since it's not really used by anything else) whether or not it is 0. Generated using MAKEFOURCC (defined in the Platform SDK or DX SDK). The FourCC2 field is currently used to allow differentiation between the various swizzled DXT5 formats. These formats must have a FourCC of DXT5 to be supported by the DirectX runtime but The Compressonator needs to know the swizzled FourCC to correctly display the texture. + CMP_INT + m_nMaxMipLevels; // Set by The Compressonator when you call TC_AppAllocateMipSet based on the width, height, depth, and textureType values passed in. Is really the maximum number of mip-map levels possible for that texture including the topmost mip-map level if you integer divide width height and depth by 2, rounding down but never falling below 1 until all three of them are 1. So a 5x10 2D texture would have a m_nMaxMipLevels of 4 (5x10 2x5 1x2 1x1). + CMP_INT + m_nMipLevels; // The number of mip-map levels in the mip-map set that actually have data. Always less than or equal to m_nMaxMipLevels. Set to 0 after TC_AppAllocateMipSet. + CMP_FORMAT m_transcodeFormat; // For universal format: Sets the target data format for data processing and analysis + CMP_BOOL m_compressed; // New Flags if data is compressed (example Block Compressed data in form of BCxx) + CMP_FORMAT m_isDeCompressed; // The New MipSet is a decompressed result from a prior Compressed MipSet Format specified + CMP_BOOL m_swizzle; // Flag is used by image load and save to indicate channels were swizzled from the origial source + CMP_BYTE m_nBlockWidth; // Width in pixels of the Compression Block that is to be processed default for ASTC is 4 + CMP_BYTE m_nBlockHeight; // Height in pixels of the Compression Block that is to be processed default for ASTC is 4 + CMP_BYTE m_nBlockDepth; // Depth in pixels of the Compression Block that is to be processed default for ASTC is 1 + CMP_BYTE m_nChannels; // Number of channels used min is 1 max is 4, 0 defaults to 1 + CMP_BYTE m_isSigned; // channel data is signed (has + and - data values) // set by various API for internal use. These values change when processing MipLevels - CMP_DWORD dwWidth; // set by various API for ref,Width of the current active miplevel. if toplevel mipmap then value is same as m_nWidth - CMP_DWORD dwHeight; // set by various API for ref,Height of the current active miplevel. if toplevel mipmap then value is same as m_nHeight - CMP_DWORD dwDataSize; // set by various API for ref,Size of the current active miplevel allocated texture data. - CMP_BYTE* pData; // set by various API for ref,Pointer to the current active miplevel texture data: used in MipLevelTable + CMP_DWORD dwWidth; // set by various API for ref,Width of the current active miplevel. if toplevel mipmap then value is same as m_nWidth + CMP_DWORD dwHeight; // set by various API for ref,Height of the current active miplevel. if toplevel mipmap then value is same as m_nHeight + CMP_DWORD dwDataSize; // set by various API for ref,Size of the current active miplevel allocated texture data. + CMP_BYTE* pData; // set by various API for ref,Pointer to the current active miplevel texture data: used in MipLevelTable // Structure to hold all mip levels buffers - CMP_MipLevelTable* m_pMipLevelTable; // set by various API for ref, This is an implementation dependent way of storing the MipLevels that this mip-map set contains. Do not depend on it, use TC_AppGetMipLevel to access a mip-map set's MipLevels. - void* m_pReservedData; // Reserved for ArchitectMF ImageLoader + CMP_MipLevelTable* + m_pMipLevelTable; // set by various API for ref, This is an implementation dependent way of storing the MipLevels that this mip-map set contains. Do not depend on it, use TC_AppGetMipLevel to access a mip-map set's MipLevels. + void* m_pReservedData; // Reserved for ArchitectMF ImageLoader // Reserved for internal data tracking CMP_INT m_nIterations; @@ -300,69 +335,125 @@ typedef struct { CMP_INT m_atfaceorslice; } CMP_MipSet; - - //=================================== // API Definitions for CMP Framework //=================================== -typedef struct { - CMP_FLOAT mipProgress; // The percentage progress of the current MIP level texture compression - CMP_INT mipLevel; // returns the current MIP level been processed 0..max available for the image - CMP_INT cubeFace; // returns the current Cube Face been processed 1..6 +typedef struct +{ + CMP_FLOAT mipProgress; // The percentage progress of the current MIP level texture compression + CMP_INT mipLevel; // returns the current MIP level been processed 0..max available for the image + CMP_INT cubeFace; // returns the current Cube Face been processed 1..6 } CMP_MIPPROGRESSPARAM; /// An enum selecting the different GPU driver types. -typedef enum { - CMP_CPU = 0, //Use CPU Only, encoders defined CMP_CPUEncode or Compressonator lib will be used - CMP_HPC = 1, //Use CPU High Performance Compute Encoders with SPMD support defined in CMP_CPUEncode) - CMP_GPU_OCL = 2, //Use GPU Kernel Encoders to compress textures using OpenCL Framework - CMP_GPU_DXC = 3, //Use GPU Kernel Encoders to compress textures using DirectX Compute Framework - CMP_GPU_VLK = 4, //Use GPU Kernel Encoders to compress textures using Vulkan Compute Framework - CMP_GPU_HW = 5 //Use GPU Kernel Encoders to compress textures using GPU HW Framework +typedef enum +{ + CMP_CPU = 0, //Use CPU Only, encoders defined CMP_CPUEncode or Compressonator lib will be used + CMP_HPC = 1, //Use CPU High Performance Compute Encoders with SPMD support defined in CMP_CPUEncode) + CMP_GPU_OCL = 2, //Use GPU Kernel Encoders to compress textures using OpenCL Framework + CMP_GPU_DXC = 3, //Use GPU Kernel Encoders to compress textures using DirectX Compute Framework + CMP_GPU_VLK = 4, //Use GPU Kernel Encoders to compress textures using Vulkan Compute Framework + CMP_GPU_HW = 5 //Use GPU Kernel Encoders to compress textures using GPU HW Framework } CMP_Compute_type; - -typedef enum CMPComputeExtensions { - CMP_COMPUTE_FP16 = 0x0001, ///< Enable Packed Math Option for GPU +typedef enum CMPComputeExtensions +{ + CMP_COMPUTE_FP16 = 0x0001, ///< Enable Packed Math Option for GPU CMP_COMPUTE_MAX_ENUM = 0x7FFF } CMP_ComputeExtensions; +struct KernelPerformanceStats +{ + CMP_FLOAT m_computeShaderElapsedMS; // Total Elapsed Shader Time to process all the blocks + CMP_INT m_num_blocks; // Number of Texel (Typically 4x4) blocks + CMP_FLOAT m_CmpMTxPerSec; // Number of Mega Texels processed per second +}; + +struct KernelDeviceInfo +{ + CMP_CHAR m_deviceName[256]; // Device name (CPU or GPU) + CMP_CHAR m_version[128]; // Kernel pipeline version number (CPU or GPU) + CMP_INT m_maxUCores; // Max Unit device CPU cores or GPU compute units (CU) + // AMD GCN::One compute unit combines 64 shader processors + // with 4 Texture Mapping units (TMU) +}; -struct KernelOptions { - CMP_ComputeExtensions Extensions; // Compute extentions to use, set to 0 if you are not using any extensions - CMP_DWORD height; // Height of the encoded texture. - CMP_DWORD width; // Width of the encoded texture. - CMP_FLOAT fquality; // Set the quality used for encoders 0.05 is the lowest and 1.0 for highest. - CMP_FORMAT format; // Encoder codec format to use for processing - CMP_Compute_type encodeWith; // Host Type : default is HPC, options are [HPC or GPU] - CMP_INT threads; // requested number of threads to use (1= single) max is 128 for HPC - CMP_BOOL genGPUMipMaps; // When ecoding with GPU HW use it to generate Compressed MipMap images, valid only if source has not miplevels - CMP_BOOL useSRGBFrames; // Use SRGB frame buffer when generating HW based mipmaps (Default Gamma corretion will be set by HW) - // if the source is SNORM then this option is enabled regardless of setting - - //private: data settings: Do not use it will be removed from this interface! - CMP_UINT size; // Size of *data - void *data; // Data to pass down from CPU to kernel - void *dataSVM; // Data allocated as Shared by CPU and GPU (used only when code is running in 64bit and devices support SVM) - char *srcfile; // Shader source file location +struct KernelOptions +{ + CMP_ComputeExtensions Extensions; // Compute extentions to use, set to 0 if you are not using any extensions + CMP_DWORD height; // Height of the encoded texture. + CMP_DWORD width; // Width of the encoded texture. + CMP_FLOAT fquality; // Set the quality used for encoders 0.05 is the lowest and 1.0 for highest. + CMP_FORMAT format; // Encoder codec format to use for processing + CMP_FORMAT srcformat; // Format of source data + CMP_Compute_type encodeWith; // Host Type : default is HPC, options are [HPC or GPU] + CMP_INT threads; // requested number of threads to use (1= single) max is 128 for HPC + CMP_BOOL getPerfStats; // Set to true if you want to get Performance Stats + KernelPerformanceStats perfStats; // Data storage for the performance stats obtained from GPU or CPU while running encoder processing + CMP_BOOL getDeviceInfo; // Set to true if you want to get target Device Info + KernelDeviceInfo deviceInfo; // Data storage for the target device + CMP_BOOL genGPUMipMaps; // When ecoding with GPU HW use it to generate MipMap images, valid only when miplevels is set else default is toplevel 1 + CMP_INT miplevels; // When using GPU HW, generate upto this requested miplevel. + CMP_BOOL useSRGBFrames; // Use SRGB frame buffer when generating HW based mipmaps (Default Gamma corretion will be set by HW) + // if the source is SNORM then this option is enabled regardless of setting + //private: data settings: Do not use it will be removed from this interface! + CMP_UINT size; // Size of *data + void* data; // Data to pass down from CPU to kernel + void* dataSVM; // Data allocated as Shared by CPU and GPU (used only when code is running in 64bit and devices support SVM) + char* srcfile; // Shader source file location }; -struct ComputeOptions { +struct ComputeOptions +{ //public: data settings - bool force_rebuild; /// -void CMP_GenerateMipLevelF(CMP_MipLevel* pCurMipLevel, CMP_MipLevel* pPrevMipLevelOne, CMP_MipLevel* pPrevMipLevelTwo, T* curMipData, T* prevMip1Data, T* prevMip2Data) { +void CMP_GenerateMipLevelF(CMP_MipLevel* pCurMipLevel, + CMP_MipLevel* pPrevMipLevelOne, + CMP_MipLevel* pPrevMipLevelTwo, + T* curMipData, + T* prevMip1Data, + T* prevMip2Data) +{ assert(pCurMipLevel); assert(pPrevMipLevelOne); - if (pCurMipLevel && pPrevMipLevelOne) { - if (!pPrevMipLevelTwo) { + if (pCurMipLevel && pPrevMipLevelOne) + { + if (!pPrevMipLevelTwo) + { bool bDiffHeights = pCurMipLevel->m_nHeight != pPrevMipLevelOne->m_nHeight; - bool bDiffWidths = pCurMipLevel->m_nWidth != pPrevMipLevelOne->m_nWidth; + bool bDiffWidths = pCurMipLevel->m_nWidth != pPrevMipLevelOne->m_nWidth; assert(bDiffHeights || bDiffWidths); - T *pDst = curMipData; - for (int y = 0; y < pCurMipLevel->m_nHeight; y++) { - T *pSrc = prevMip1Data + (2 * y * pPrevMipLevelOne->m_nWidth * 4); - T *pSrc2; - if (bDiffHeights) { + T* pDst = curMipData; + for (int y = 0; y < pCurMipLevel->m_nHeight; y++) + { + T* pSrc = prevMip1Data + (2 * y * pPrevMipLevelOne->m_nWidth * 4); + T* pSrc2; + if (bDiffHeights) + { pSrc2 = pSrc + (pPrevMipLevelOne->m_nWidth * 4); - } else { + } + else + { //if no change in height, then use same line as source pSrc2 = pSrc; } - for (int x = 0; x < pCurMipLevel->m_nWidth; x++, pSrc += 8, pSrc2 += 8) { + for (int x = 0; x < pCurMipLevel->m_nWidth; x++, pSrc += 8, pSrc2 += 8) + { T c1[4], c2[4], c3[4], c4[4]; memcpy(c1, pSrc, sizeof(c1)); memcpy(c3, pSrc2, sizeof(c3)); - if (bDiffWidths) { + if (bDiffWidths) + { memcpy(c2, pSrc + 4, sizeof(c2)); memcpy(c4, pSrc2 + 4, sizeof(c4)); - } else { + } + else + { memcpy(c2, pSrc, sizeof(c2)); memcpy(c4, pSrc2, sizeof(c4)); } @@ -61,37 +77,47 @@ void CMP_GenerateMipLevelF(CMP_MipLevel* pCurMipLevel, CMP_MipLevel* pPrevMipLev *pDst++ = (c1[i] + c2[i] + c3[i] + c4[i]) / T(4.f); } } - } else { + } + else + { //working with volume texture, avg both slices together as well as 4 corners bool bDiffHeights = pCurMipLevel->m_nHeight != pPrevMipLevelOne->m_nHeight; - bool bDiffWidths = pCurMipLevel->m_nWidth != pPrevMipLevelOne->m_nWidth; + bool bDiffWidths = pCurMipLevel->m_nWidth != pPrevMipLevelOne->m_nWidth; //don't need to check that either height or width is diff, b/c slices are diff - T *pDst = curMipData; - for (int y = 0; y < pCurMipLevel->m_nHeight; y++) { + T* pDst = curMipData; + for (int y = 0; y < pCurMipLevel->m_nHeight; y++) + { T *pSrc, *pSrc2, *pOtherSrc, *pOtherSrc2; - pSrc = prevMip1Data + (2 * y * pPrevMipLevelOne->m_nWidth * 4); + pSrc = prevMip1Data + (2 * y * pPrevMipLevelOne->m_nWidth * 4); pOtherSrc = prevMip2Data + (2 * y * pPrevMipLevelTwo->m_nWidth * 4); - if (bDiffHeights) { + if (bDiffHeights) + { //point to next line, same column - pSrc2 = pSrc + (pPrevMipLevelOne->m_nWidth * 4); + pSrc2 = pSrc + (pPrevMipLevelOne->m_nWidth * 4); pOtherSrc2 = pOtherSrc + (pPrevMipLevelTwo->m_nWidth * 4); - } else { + } + else + { //if no change in height, then use same line as source - pSrc2 = pSrc; + pSrc2 = pSrc; pOtherSrc2 = pOtherSrc; } - for (int x = 0; x < pCurMipLevel->m_nWidth; x++, pSrc += 8, pSrc2 += 8, pOtherSrc += 8, pOtherSrc2 += 8) { + for (int x = 0; x < pCurMipLevel->m_nWidth; x++, pSrc += 8, pSrc2 += 8, pOtherSrc += 8, pOtherSrc2 += 8) + { T c1[4], c2[4], c3[4], c4[4], c5[4], c6[4], c7[4], c8[4]; memcpy(c1, pSrc, sizeof(c1)); memcpy(c3, pSrc2, sizeof(c3)); memcpy(c5, pOtherSrc, sizeof(c5)); memcpy(c7, pOtherSrc2, sizeof(c7)); - if (bDiffWidths) { + if (bDiffWidths) + { memcpy(c2, pSrc + 4, sizeof(c2)); memcpy(c4, pSrc2 + 4, sizeof(c4)); memcpy(c6, pOtherSrc + 4, sizeof(c6)); memcpy(c8, pOtherSrc2 + 4, sizeof(c8)); - } else { + } + else + { memcpy(c2, pSrc, sizeof(c2)); memcpy(c4, pSrc2, sizeof(c4)); memcpy(c6, pOtherSrc, sizeof(c6)); @@ -106,68 +132,99 @@ void CMP_GenerateMipLevelF(CMP_MipLevel* pCurMipLevel, CMP_MipLevel* pPrevMipLev } //nMinSize : The size in pixels used to determine how many mip levels to generate. Once all dimensions are less than or equal to nMinSize your mipper should generate no more mip levels. -CMP_INT CMP_API CMP_GenerateMIPLevelsEx(CMP_MipSet* pMipSet, CMP_CFilterParams *CFilterParam) { +CMP_INT CMP_API CMP_GenerateMIPLevelsEx(CMP_MipSet* pMipSet, CMP_CFilterParams* CFilterParam) +{ CMP_CMIPS CMips; assert(pMipSet); assert(pMipSet->m_nMipLevels); - CMP_INT nWidth = pMipSet->m_nWidth; - CMP_INT nHeight = pMipSet->m_nHeight; - CMP_HALFSHORT *null_half = 0; - CMP_FLOAT *null_float = 0; - CMP_MipLevel *null_tempMipTwo = nullptr; + CMP_INT nWidth = pMipSet->m_nWidth; + CMP_INT nHeight = pMipSet->m_nHeight; + CMP_HALFSHORT* null_half = 0; + CMP_FLOAT* null_float = 0; + CMP_MipLevel* null_tempMipTwo = nullptr; - while (nWidth > CFilterParam->nMinSize && nHeight > CFilterParam->nMinSize) { - nWidth = CMP_MAX(nWidth >> 1, 1); - nHeight = CMP_MAX(nHeight >> 1, 1); - CMP_INT nCurMipLevel = pMipSet->m_nMipLevels; - CMP_INT maxFacesOrSlices = CMP_MAX((pMipSet->m_TextureType == TT_VolumeTexture) ? (CMP_MaxFacesOrSlices(pMipSet, nCurMipLevel-1)>>1) : CMP_MaxFacesOrSlices(pMipSet, nCurMipLevel-1), 1); - for(CMP_INT nFaceOrSlice=0; nFaceOrSlice CFilterParam->nMinSize && nHeight > CFilterParam->nMinSize) + { + nWidth = CMP_MAX(nWidth >> 1, 1); + nHeight = CMP_MAX(nHeight >> 1, 1); + CMP_INT nCurMipLevel = pMipSet->m_nMipLevels; + CMP_INT maxFacesOrSlices = CMP_MAX((pMipSet->m_TextureType == TT_VolumeTexture) ? (CMP_MaxFacesOrSlices(pMipSet, nCurMipLevel - 1) >> 1) + : CMP_MaxFacesOrSlices(pMipSet, nCurMipLevel - 1), + 1); + for (CMP_INT nFaceOrSlice = 0; nFaceOrSlice < maxFacesOrSlices; nFaceOrSlice++) + { CMP_MipLevel* pThisMipLevel = CMips.GetMipLevel(pMipSet, nCurMipLevel, nFaceOrSlice); - if (!pThisMipLevel) continue; - assert(CMips.GetMipLevel(pMipSet, nCurMipLevel-1, nFaceOrSlice)->m_pbData); //prev miplevel ok + if (!pThisMipLevel) + continue; + assert(CMips.GetMipLevel(pMipSet, nCurMipLevel - 1, nFaceOrSlice)->m_pbData); //prev miplevel ok - if(pThisMipLevel->m_pbData) { // Space for mip level already allocated ? - if(pThisMipLevel->m_nWidth != nWidth || pThisMipLevel->m_nHeight != nHeight) { + if (pThisMipLevel->m_pbData) + { // Space for mip level already allocated ? + if (pThisMipLevel->m_nWidth != nWidth || pThisMipLevel->m_nHeight != nHeight) + { // Wrong size - release & reallocate //CMips->FreeMipLevelData(pThisMipLevel); - if(CMips.AllocateMipLevelData(pThisMipLevel, nWidth, nHeight, pMipSet->m_ChannelFormat, pMipSet->m_TextureDataType) == NULL) { + if (CMips.AllocateMipLevelData(pThisMipLevel, nWidth, nHeight, pMipSet->m_ChannelFormat, pMipSet->m_TextureDataType) == NULL) + { return CMP_ERR_GENERIC; } } - } else if(CMips.AllocateMipLevelData(pThisMipLevel, nWidth, nHeight, pMipSet->m_ChannelFormat, pMipSet->m_TextureDataType) == NULL) { + } + else if (CMips.AllocateMipLevelData(pThisMipLevel, nWidth, nHeight, pMipSet->m_ChannelFormat, pMipSet->m_TextureDataType) == NULL) + { return CMP_ERR_GENERIC; } assert(pThisMipLevel->m_pbData); - if(pMipSet->m_TextureType != TT_VolumeTexture) { - - CMP_MipLevel *tempMipOne = CMips.GetMipLevel(pMipSet, nCurMipLevel - 1, nFaceOrSlice); + if (pMipSet->m_TextureType != TT_VolumeTexture) + { + CMP_MipLevel* tempMipOne = CMips.GetMipLevel(pMipSet, nCurMipLevel - 1, nFaceOrSlice); if (pMipSet->m_ChannelFormat == CF_8bit) - CMP_GenerateMipLevelF(pThisMipLevel, tempMipOne, NULL, pThisMipLevel->m_pbData, tempMipOne->m_pbData); + { + if (pMipSet->m_format == CMP_FORMAT_RGBA_8888_S) + CMP_GenerateMipLevelF(pThisMipLevel, tempMipOne, NULL, pThisMipLevel->m_psbData, tempMipOne->m_psbData); + else + CMP_GenerateMipLevelF(pThisMipLevel, tempMipOne, NULL, pThisMipLevel->m_pbData, tempMipOne->m_pbData); + } else if (pMipSet->m_ChannelFormat == CF_Float16) - CMP_GenerateMipLevelF(pThisMipLevel, tempMipOne, null_tempMipTwo, pThisMipLevel->m_phfsData, tempMipOne->m_phfsData,null_half); - else if(pMipSet->m_ChannelFormat == CF_Float32) - CMP_GenerateMipLevelF(pThisMipLevel, tempMipOne, null_tempMipTwo, pThisMipLevel->m_pfData, tempMipOne->m_pfData,null_float); - } else { - if(CMP_MaxFacesOrSlices(pMipSet, nCurMipLevel-1) > 1) { - CMP_MipLevel *tempMipOne = CMips.GetMipLevel(pMipSet, nCurMipLevel - 1, nFaceOrSlice * 2); - CMP_MipLevel *tempMipTwo = CMips.GetMipLevel(pMipSet, nCurMipLevel - 1, nFaceOrSlice * 2 + 1); + CMP_GenerateMipLevelF(pThisMipLevel, tempMipOne, null_tempMipTwo, pThisMipLevel->m_phfsData, tempMipOne->m_phfsData, null_half); + else if (pMipSet->m_ChannelFormat == CF_Float32) + CMP_GenerateMipLevelF(pThisMipLevel, tempMipOne, null_tempMipTwo, pThisMipLevel->m_pfData, tempMipOne->m_pfData, null_float); + } + else + { + if (CMP_MaxFacesOrSlices(pMipSet, nCurMipLevel - 1) > 1) + { + CMP_MipLevel* tempMipOne = CMips.GetMipLevel(pMipSet, nCurMipLevel - 1, nFaceOrSlice * 2); + CMP_MipLevel* tempMipTwo = CMips.GetMipLevel(pMipSet, nCurMipLevel - 1, nFaceOrSlice * 2 + 1); //prev miplevel had 2 or more slices, so avg together slices if (pMipSet->m_ChannelFormat == CF_8bit) - CMP_GenerateMipLevelF(pThisMipLevel, tempMipOne, tempMipTwo, pThisMipLevel->m_pbData, tempMipOne->m_pbData, tempMipTwo->m_pbData); + { + if (pMipSet->m_format == CMP_FORMAT_RGBA_8888_S) + CMP_GenerateMipLevelF(pThisMipLevel, tempMipOne, tempMipTwo, pThisMipLevel->m_psbData, tempMipOne->m_psbData, tempMipTwo->m_psbData); + else + CMP_GenerateMipLevelF(pThisMipLevel, tempMipOne, tempMipTwo, pThisMipLevel->m_pbData, tempMipOne->m_pbData, tempMipTwo->m_pbData); + } else if (pMipSet->m_ChannelFormat == CF_Float16) CMP_GenerateMipLevelF(pThisMipLevel, tempMipOne, tempMipTwo, pThisMipLevel->m_phfsData, tempMipOne->m_phfsData, tempMipTwo->m_phfsData); - else if(pMipSet->m_ChannelFormat == CF_Float32) + else if (pMipSet->m_ChannelFormat == CF_Float32) CMP_GenerateMipLevelF(pThisMipLevel, tempMipOne, tempMipTwo, pThisMipLevel->m_pfData, tempMipOne->m_pfData, tempMipTwo->m_pfData); - } else { - CMP_MipLevel *tempMipOne = CMips.GetMipLevel(pMipSet, nCurMipLevel - 1, nFaceOrSlice); + } + else + { + CMP_MipLevel* tempMipOne = CMips.GetMipLevel(pMipSet, nCurMipLevel - 1, nFaceOrSlice); if (pMipSet->m_ChannelFormat == CF_8bit) - CMP_GenerateMipLevelF(pThisMipLevel, tempMipOne, NULL, pThisMipLevel->m_pbData, tempMipOne->m_pbData); + { + if (pMipSet->m_format == CMP_FORMAT_RGBA_8888_S) + CMP_GenerateMipLevelF(pThisMipLevel, tempMipOne, NULL, pThisMipLevel->m_psbData, tempMipOne->m_psbData); + else + CMP_GenerateMipLevelF(pThisMipLevel, tempMipOne, NULL, pThisMipLevel->m_pbData, tempMipOne->m_pbData); + } else if (pMipSet->m_ChannelFormat == CF_Float16) CMP_GenerateMipLevelF(pThisMipLevel, tempMipOne, null_tempMipTwo, pThisMipLevel->m_phfsData, tempMipOne->m_phfsData, null_half); - else if(pMipSet->m_ChannelFormat == CF_Float32) - CMP_GenerateMipLevelF(pThisMipLevel, tempMipOne, null_tempMipTwo, pThisMipLevel->m_pfData, tempMipOne->m_pfData,null_float); + else if (pMipSet->m_ChannelFormat == CF_Float32) + CMP_GenerateMipLevelF(pThisMipLevel, tempMipOne, null_tempMipTwo, pThisMipLevel->m_pfData, tempMipOne->m_pfData, null_float); } } } @@ -183,7 +240,8 @@ CMP_INT CMP_API CMP_GenerateMIPLevelsEx(CMP_MipSet* pMipSet, CMP_CFilterParams * return CMP_OK; } -CMP_INT CMP_API CMP_GenerateMIPLevels(CMP_MipSet* pMipSet, CMP_INT nMinSize) { +CMP_INT CMP_API CMP_GenerateMIPLevels(CMP_MipSet* pMipSet, CMP_INT nMinSize) +{ CMP_CFilterParams CFilterParam; CFilterParam.dwMipFilterOptions = 0; CFilterParam.nFilterType = 0; diff --git a/cmp_framework/common/cmp_mips.cpp b/cmp_framework/common/cmp_mips.cpp index 2302b2a7f..d54f74bec 100644 --- a/cmp_framework/common/cmp_mips.cpp +++ b/cmp_framework/common/cmp_mips.cpp @@ -26,17 +26,19 @@ #include "cmp_mips.h" -#include -#include +#include +#include #include -void(*PrintStatusLine)(char *) = NULL; +void (*PrintStatusLine)(char*) = NULL; -void PrintInfo(const char* Format, ... ) { - if (PrintStatusLine) { +void PrintInfo(const char* Format, ...) +{ + if (PrintStatusLine) + { // define a pointer to save argument list va_list args; - char buff[1024]; + char buff[1024]; // process the arguments into our debug buffer va_start(args, Format); vsnprintf(buff, 1024, Format, args); @@ -45,22 +47,26 @@ void PrintInfo(const char* Format, ... ) { } } -void CMP_CMIPS::PrintError(const char* Format, ... ) { +void CMP_CMIPS::PrintError(const char* Format, ...) +{ char buff[1024]; // define a pointer to save argument list va_list args; // process the arguments into our debug buffer va_start(args, Format); - vsnprintf(buff,1024,Format,args); + vsnprintf(buff, 1024, Format, args); va_end(args); PrintInfo(buff); } -void CMP_CMIPS::Print(const char* Format, ...) { - if (!PrintLine) return; +void CMP_CMIPS::Print(const char* Format, ...) +{ + if (!PrintLine) + return; - if (m_infolevel & 0x01) { + if (m_infolevel & 0x01) + { char buff[1024]; // define a pointer to save argument list va_list args; @@ -73,19 +79,65 @@ void CMP_CMIPS::Print(const char* Format, ...) { } } -CMP_INT CMP_API CMP_CalcMinMipSize(CMP_INT nHeight, CMP_INT nWidth, CMP_INT MipsLevel) { - while (MipsLevel > 0) { +// Determins the active channels used for a given format +// See CMP_AnalysisData for more details on the bits set +CMP_UINT CMP_API CMP_getFormat_nChannels(CMP_FORMAT format) +{ + CMP_UINT RGBAChannels; + + switch (format) + { + case CMP_FORMAT_ATI1N: + case CMP_FORMAT_BC4: + case CMP_FORMAT_BC4_S: // All channels are used and equal, Red is used as active channel + RGBAChannels = 0b0001; // R + break; + case CMP_FORMAT_ATI2N_XY: + case CMP_FORMAT_BC5_S: + case CMP_FORMAT_BC5: // Only Red & Green channels active + RGBAChannels = 0b0011; // GR + break; + default: + RGBAChannels = 0b0111; // BGR , alpha skipped user can set this after + break; + } + + return RGBAChannels; +} + +CMP_INT CMP_API CMP_CalcMinMipSize(CMP_INT nHeight, CMP_INT nWidth, CMP_INT MipsLevel) +{ + while (MipsLevel > 1) + { nWidth = CMP_MAX(nWidth >> 1, 1); nHeight = CMP_MAX(nHeight >> 1, 1); MipsLevel--; } - - if (nWidth > nHeight) - return (nHeight); return (nWidth); } -CMP_ERROR CMP_API CMP_CreateCompressMipSet(CMP_MipSet* pMipSetCMP,CMP_MipSet* pMipSetSRC) { +CMP_INT CMP_API CMP_CalcMaxMipLevel(CMP_INT nHeight, CMP_INT nWidth, CMP_BOOL bForGPU) +{ + CMP_INT MaxLipLevel = 1; + while (MaxLipLevel < MAX_MIPLEVEL_SUPPORTED) + { + nWidth = CMP_MAX(nWidth >> 1, 1); + nHeight = CMP_MAX(nHeight >> 1, 1); + if (bForGPU) + { + if ((nWidth % 4) || (nHeight % 4)) + break; + } + MaxLipLevel++; + if ((nWidth == 1) || (nHeight == 1)) + break; + } + + return (MaxLipLevel); +} + +CMP_ERROR CMP_API CMP_CreateCompressMipSet(CMP_MipSet* pMipSetCMP, CMP_MipSet* pMipSetSRC) +{ CMP_CMIPS CMips; pMipSetCMP->m_Flags = MS_FLAG_Default; @@ -99,10 +151,11 @@ CMP_ERROR CMP_API CMP_CreateCompressMipSet(CMP_MipSet* pMipSetCMP,CMP_MipSet* pM pMipSetCMP->m_nBlockWidth = 4; pMipSetCMP->m_nMaxMipLevels = pMipSetSRC->m_nMaxMipLevels; pMipSetCMP->m_nMipLevels = 1; - pMipSetCMP->m_TextureType = pMipSetSRC->m_TextureType; - pMipSetCMP->m_nDepth = pMipSetSRC->m_nDepth; + pMipSetCMP->m_TextureType = pMipSetSRC->m_TextureType; + pMipSetCMP->m_nDepth = pMipSetSRC->m_nDepth; - if (!CMips.AllocateMipSet(pMipSetCMP, CF_Compressed, TDT_ARGB, pMipSetCMP->m_TextureType,pMipSetSRC->m_nWidth, pMipSetSRC->m_nHeight,pMipSetCMP->m_nDepth)) + if (!CMips.AllocateMipSet( + pMipSetCMP, CF_Compressed, TDT_ARGB, pMipSetCMP->m_TextureType, pMipSetSRC->m_nWidth, pMipSetSRC->m_nHeight, pMipSetCMP->m_nDepth)) return CMP_ERR_MEM_ALLOC_FOR_MIPSET; //---------------------------------------------------------------- @@ -114,82 +167,595 @@ CMP_ERROR CMP_API CMP_CreateCompressMipSet(CMP_MipSet* pMipSetCMP,CMP_MipSet* pM //----------------------------------------------------------------------- // Calculate the target compressed block buffer size (4 bytes x 4 bytes) //----------------------------------------------------------------------- - unsigned int Width = ((pMipSetSRC->m_nWidth + 3) / 4) * 4; - unsigned int Height = ((pMipSetSRC->m_nHeight + 3) / 4) * 4; - pMipSetCMP->dwDataSize = Width * Height; + unsigned int Width = ((pMipSetSRC->m_nWidth + 3) / 4) * 4; + unsigned int Height = ((pMipSetSRC->m_nHeight + 3) / 4) * 4; + pMipSetCMP->dwDataSize = Width * Height; //---------------------------------------------------------------- // Allocate memory for the mip level 0 //---------------------------------------------------------------- - if (!CMips.AllocateCompressedMipLevelData(pOutMipLevel, pMipSetSRC->m_nWidth, pMipSetSRC->m_nHeight, pMipSetCMP->dwDataSize)) { + if (!CMips.AllocateCompressedMipLevelData(pOutMipLevel, pMipSetSRC->m_nWidth, pMipSetSRC->m_nHeight, pMipSetCMP->dwDataSize)) + { std::printf("Memory Error(1): allocating MIPSet compression level data buffer\n"); return CMP_ERR_MEM_ALLOC_FOR_MIPSET; } - pMipSetCMP->pData = pOutMipLevel->m_pbData; + pMipSetCMP->pData = pOutMipLevel->m_pbData; return CMP_OK; } -void CMP_calcMSE_PSNRb(MipLevel* pCurMipLevel, CMP_BYTE* pdata1, CMP_BYTE* pdata2, CMP_DOUBLE* outMSE, CMP_DOUBLE* outPSNR, CMP_INT numchannels) { - CMP_DOUBLE mseRGB = 0.0; - CMP_INT totalPixels = 0; - for (int y = 0; y < pCurMipLevel->m_nHeight; y++) { - for (int x = 0; x < pCurMipLevel->m_nWidth; x++) { - // calc Gamma for the all color channels - for (int i = 0; i < 3 && i < numchannels; i++) { - mseRGB += pow(abs(*pdata1 - *pdata2), 2); - pdata1++; - pdata2++; +// Code duplication for all 3 functions to calc MSE & PSNR +// can reduce to typed templates + +void CMP_calcMSE_PSNRb(MipLevel* pCurMipLevel, CMP_BYTE* pdata1, CMP_BYTE* pdata2, CMP_AnalysisData* pAnalysisData) +{ + CMP_UINT RGBAChannels = pAnalysisData->channelBitMap; + CMP_DOUBLE mse; + CMP_DOUBLE mseR = 0.0; + CMP_DOUBLE mseG = 0.0; + CMP_DOUBLE mseB = 0.0; + CMP_DOUBLE mseA = 0.0; + CMP_DOUBLE mseRGBA = 0.0; + CMP_INT totalPixelsR = 0; + CMP_INT totalPixelsG = 0; + CMP_INT totalPixelsB = 0; + CMP_INT totalPixelsA = 0; + CMP_INT totalPixels = 0; + + for (int y = 0; y < pCurMipLevel->m_nHeight; y++) + { + for (int x = 0; x < pCurMipLevel->m_nWidth; x++) + { + // calc Gamma for the all active channels + // Red channel + if (RGBAChannels & 0b0001) + { + mse = pow(abs(*pdata1 - *pdata2), 2); + mseR += mse; + mseRGBA += mse; + totalPixelsR++; + totalPixels++; + } + pdata1++; + pdata2++; + // Green channel + if (RGBAChannels & 0b0010) + { + mse = pow(abs(*pdata1 - *pdata2), 2); + mseG += mse; + mseRGBA += mse; + totalPixelsG++; + totalPixels++; + } + pdata1++; + pdata2++; + // Blue channel + if (RGBAChannels & 0b0100) + { + mse = pow(abs(*pdata1 - *pdata2), 2); + mseB += mse; + mseRGBA += mse; + totalPixelsB++; + totalPixels++; + } + pdata1++; + pdata2++; + // Alpha channel + if (RGBAChannels & 0b1000) + { + mse = pow(abs(*pdata1 - *pdata2), 2); + mseA += mse; + mseRGBA += mse; + totalPixelsA++; + totalPixels++; + } + pdata1++; + pdata2++; + } + } + + if (totalPixels == 0) + { + totalPixels = 1; + } + + pAnalysisData->mse = mseRGBA / totalPixels; + pAnalysisData->mseR = mseR / totalPixelsR; + pAnalysisData->mseG = mseG / totalPixelsG; + pAnalysisData->mseB = mseB / totalPixelsB; + pAnalysisData->mseA = mseA / totalPixelsA; + + if (pAnalysisData->mse <= 0.0) + { + pAnalysisData->mse = 0.0; + pAnalysisData->mseR = 0.0; + pAnalysisData->mseG = 0.0; + pAnalysisData->mseB = 0.0; + pAnalysisData->mseA = 0.0; + + pAnalysisData->psnr = 128; + pAnalysisData->psnrR = 128; + pAnalysisData->psnrG = 128; + pAnalysisData->psnrB = 128; + pAnalysisData->psnrA = 128; + } + else + { + if (pAnalysisData->mse > 0.0) + pAnalysisData->psnr = 10 * log((1.0 * 255 * 255) / pAnalysisData->mse) / log(10.0); + if (pAnalysisData->mseR > 0.0) + pAnalysisData->psnrR = 10 * log((1.0 * 255 * 255) / pAnalysisData->mseR) / log(10.0); + if (pAnalysisData->mseG > 0.0) + pAnalysisData->psnrG = 10 * log((1.0 * 255 * 255) / pAnalysisData->mseG) / log(10.0); + if (pAnalysisData->mseB > 0.0) + pAnalysisData->psnrB = 10 * log((1.0 * 255 * 255) / pAnalysisData->mseB) / log(10.0); + if (pAnalysisData->mseA > 0.0) + pAnalysisData->psnrA = 10 * log((1.0 * 255 * 255) / pAnalysisData->mseA) / log(10.0); + } +} + +static CMP_FLOAT HalfShorttoFloat(CMP_HALFSHORT f) +{ + CMP_HALF A; + A.setBits(f); + return ((CMP_FLOAT)A); +} + +inline float clamp(float a, float l, float h) +{ + return (a < l) ? l : ((a > h) ? h : a); +} + +inline float knee(double x, double f) +{ + return float(log(x * f + 1.f) / f); +} + +static float cmp_findKneeValue(float x, float y) +{ + float f0 = 0; + float f1 = 1.f; + + while (knee(x, f1) > y) + { + f0 = f1; + f1 = f1 * 2.f; + } + + for (int i = 0; i < 30; ++i) + { + const float f2 = (f0 + f1) / 2.f; + const float y2 = knee(x, f2); + + if (y2 < y) + { + f1 = f2; + } + else + { + f0 = f2; + } + } + + return (f0 + f1) / 2.f; +} + +// This is the default convertion used for CT_Foat16 to CF_8Bit in Compressonators transcoding example. EXR to BC1..5,7 +static CMP_BYTE HalfShorttoByteCMP(CMP_HALFSHORT halfdata, CMP_BOOL alpha, CMP_AnalysisData* pAnalysisData) +{ + + CMP_FLOAT fdata = HalfShorttoFloat(halfdata); + CMP_FLOAT fInputKneeHigh, fInputGamma; + + if (pAnalysisData->fInputKneeHigh <= 0.0f) + fInputKneeHigh = 5.0f; + else + fInputKneeHigh = pAnalysisData->fInputKneeHigh; + + float kl = powf(2.f, pAnalysisData->fInputKneeLow); + float f = cmp_findKneeValue(powf(2.f, fInputKneeHigh) - kl, powf(2.f, 3.5f) - kl); + float luminance3f = powf(2, -3.5); // always assume max intensity is 1 and 3.5f darker for scale later + + if (pAnalysisData->fInputGamma == 0.0f) + fInputGamma = 2.2f; + else + fInputGamma = pAnalysisData->fInputGamma; + + float invGamma = 1 / fInputGamma; //for gamma correction + float scale = 255.0f * powf(luminance3f, invGamma); + CMP_BYTE retb; + + // 1) Compensate for fogging by subtracting defog from the raw pixel values. + if (pAnalysisData->fInputDefog > 0.0) + { + fdata = fdata - pAnalysisData->fInputDefog; + } + + // 2) Multiply the defogged pixel values by 2^(exposure + 2.47393). + const float exposeScale = powf(2, pAnalysisData->fInputExposure + 2.47393f); + fdata = fdata * exposeScale; + + // 3) Values that are now 1.0 are called "middle gray". + // If defog and exposure are both set to 0.0, then + // middle gray corresponds to a raw pixel value of 0.18. + // In step 6, middle gray values will be mapped to an + // intensity 3.5 f-stops below the display's maximum + // intensity. + + // 4) Apply a knee function. The knee function has two + // parameters, kneeLow and kneeHigh. Pixel values + // below 2^kneeLow are not changed by the knee + // function. Pixel values above kneeLow are lowered + // according to a logarithmic curve, such that the + // value 2^kneeHigh is mapped to 2^3.5. (In step 6, + // this value will be mapped to the the display's + // maximum intensity.) + if (fdata > kl) + { + fdata = kl + knee(fdata - kl, f); + } + + // 5) Gamma-correct the pixel values, according to the + // screen's gamma. (We assume that the gamma curve + // is a simple power function.) + fdata = powf(fdata, invGamma); + + // 6) Scale the values such that middle gray pixels are + // mapped to a frame buffer value that is 3.5 f-stops + // below the display's maximum intensity. + fdata *= scale; + + retb = (CMP_BYTE)clamp(fdata, 0.f, 255.f); + return (retb); +} + +void CMP_calcMSE_PSNRHalfShort(MipLevel* pCurMipLevel, CMP_HALFSHORT* pdata1, CMP_HALFSHORT* pdata2, CMP_AnalysisData* pAnalysisData) +{ + CMP_UINT RGBAChannels = pAnalysisData->channelBitMap; + CMP_DOUBLE mse; + CMP_DOUBLE mseR = 0.0; + CMP_DOUBLE mseG = 0.0; + CMP_DOUBLE mseB = 0.0; + CMP_DOUBLE mseA = 0.0; + CMP_DOUBLE mseRGBA = 0.0; + CMP_INT totalPixelsR = 0; + CMP_INT totalPixelsG = 0; + CMP_INT totalPixelsB = 0; + CMP_INT totalPixelsA = 0; + CMP_INT totalPixels = 0; + + CMP_FLOAT pixf1; + CMP_FLOAT pixf2; + + for (int y = 0; y < pCurMipLevel->m_nHeight; y++) + { + for (int x = 0; x < pCurMipLevel->m_nWidth; x++) + { + // calc Gamma for the all active channels + // Red channel + if (RGBAChannels & 0b0001) + { + pixf1 = HalfShorttoFloat(*pdata1); // convert short int to float + pixf2 = HalfShorttoFloat(*pdata2); // convert short int to float + mse = pow(abs(pixf1 - pixf2), 2); + mseR += mse; + mseRGBA += mse; + + totalPixelsR++; + totalPixels++; + } + pdata1++; + pdata2++; + // Green channel + if (RGBAChannels & 0b0010) + { + pixf1 = HalfShorttoFloat(*pdata1); // convert short int to float + pixf2 = HalfShorttoFloat(*pdata2); // convert short int to float + mse = pow(abs(pixf1 - pixf2), 2); + mseG += mse; + mseRGBA += mse; + totalPixelsG++; + totalPixels++; + } + pdata1++; + pdata2++; + // Blue channel + if (RGBAChannels & 0b0100) + { + pixf1 = HalfShorttoFloat(*pdata1); // convert short int to float + pixf2 = HalfShorttoFloat(*pdata2); // convert short int to float + mse = pow(abs(pixf1 - pixf2), 2); + mseB += mse; + mseRGBA += mse; + totalPixelsB++; + totalPixels++; + } + pdata1++; + pdata2++; + // Alpha channel + if (RGBAChannels & 0b1000) + { + pixf1 = HalfShorttoFloat(*pdata1); // convert short int to float + pixf2 = HalfShorttoFloat(*pdata2); // convert short int to float + mse = pow(abs(pixf1 - pixf2), 2); + mseA += mse; + mseRGBA += mse; + totalPixelsA++; + totalPixels++; + } + pdata1++; + pdata2++; + } + } + + if (totalPixels == 0) + { + totalPixels = 1; + } + + pAnalysisData->mse = mseRGBA / totalPixels; + pAnalysisData->mseR = mseR / totalPixelsR; + pAnalysisData->mseG = mseG / totalPixelsG; + pAnalysisData->mseB = mseB / totalPixelsB; + pAnalysisData->mseA = mseA / totalPixelsA; + + if (pAnalysisData->mse <= 0.0) + { + pAnalysisData->mse = 0.0; + pAnalysisData->mseR = 0.0; + pAnalysisData->mseG = 0.0; + pAnalysisData->mseB = 0.0; + pAnalysisData->mseA = 0.0; + + pAnalysisData->psnr = 128; + pAnalysisData->psnrR = 128; + pAnalysisData->psnrG = 128; + pAnalysisData->psnrB = 128; + pAnalysisData->psnrA = 128; + } + else + { + if (pAnalysisData->mse > 0.0) + pAnalysisData->psnr = 10 * log((1.0 * 255 * 255) / pAnalysisData->mse) / log(10.0); + if (pAnalysisData->mseR > 0.0) + pAnalysisData->psnrR = 10 * log((1.0 * 255 * 255) / pAnalysisData->mseR) / log(10.0); + if (pAnalysisData->mseG > 0.0) + pAnalysisData->psnrG = 10 * log((1.0 * 255 * 255) / pAnalysisData->mseG) / log(10.0); + if (pAnalysisData->mseB > 0.0) + pAnalysisData->psnrB = 10 * log((1.0 * 255 * 255) / pAnalysisData->mseB) / log(10.0); + if (pAnalysisData->mseA > 0.0) + pAnalysisData->psnrA = 10 * log((1.0 * 255 * 255) / pAnalysisData->mseA) / log(10.0); + } +} + +void CMP_calcMSE_PSNRHalfShortByte(MipLevel* pCurMipLevel, CMP_HALFSHORT* pdata1, CMP_BYTE* pdata2, CMP_AnalysisData* pAnalysisData) +{ + CMP_UINT RGBAChannels = pAnalysisData->channelBitMap; + CMP_DOUBLE mse; + CMP_DOUBLE mseR = 0.0; + CMP_DOUBLE mseG = 0.0; + CMP_DOUBLE mseB = 0.0; + CMP_DOUBLE mseA = 0.0; + CMP_DOUBLE mseRGBA = 0.0; + CMP_INT totalPixelsR = 0; + CMP_INT totalPixelsG = 0; + CMP_INT totalPixelsB = 0; + CMP_INT totalPixelsA = 0; + CMP_INT totalPixels = 0; + + CMP_BYTE pixf1; + CMP_BYTE pixf2; + + for (int y = 0; y < pCurMipLevel->m_nHeight; y++) + { + for (int x = 0; x < pCurMipLevel->m_nWidth; x++) + { + // calc Gamma for the all active channels + // Red channel + if (RGBAChannels & 0b0001) + { + pixf1 = HalfShorttoByteCMP(*pdata1, false, pAnalysisData); + pixf2 = *pdata2; + mse = pow(abs(pixf1 - pixf2), 2); + mseR += mse; + mseRGBA += mse; + + totalPixelsR++; totalPixels++; } - // if alpha skip it - if (numchannels > 3) { - pdata1++; - pdata2++; + pdata1++; + pdata2++; + // Green channel + if (RGBAChannels & 0b0010) + { + pixf1 = HalfShorttoByteCMP(*pdata1, false, pAnalysisData); + pixf2 = *pdata2; + mse = pow(abs(pixf1 - pixf2), 2); + mseG += mse; + mseRGBA += mse; + totalPixelsG++; + totalPixels++; } + pdata1++; + pdata2++; + // Blue channel + if (RGBAChannels & 0b0100) + { + pixf1 = HalfShorttoByteCMP(*pdata1, false, pAnalysisData); + pixf2 = *pdata2; + mse = pow(abs(pixf1 - pixf2), 2); + mseB += mse; + mseRGBA += mse; + totalPixelsB++; + totalPixels++; + } + pdata1++; + pdata2++; + // Alpha channel + if (RGBAChannels & 0b1000) + { + pixf1 = HalfShorttoByteCMP(*pdata1, true, pAnalysisData); + pixf2 = *pdata2; + mse = pow(abs(pixf1 - pixf2), 2); + mseA += mse; + mseRGBA += mse; + totalPixelsA++; + totalPixels++; + } + pdata1++; + pdata2++; } } + if (totalPixels == 0) + { totalPixels = 1; - *outMSE = mseRGB / totalPixels; - if (*outMSE == 0) - *outPSNR = 128; + } + + pAnalysisData->mse = mseRGBA / totalPixels; + pAnalysisData->mseR = mseR / totalPixelsR; + pAnalysisData->mseG = mseG / totalPixelsG; + pAnalysisData->mseB = mseB / totalPixelsB; + pAnalysisData->mseA = mseA / totalPixelsA; + + if (pAnalysisData->mse <= 0.0) + { + pAnalysisData->mse = 0.0; + pAnalysisData->mseR = 0.0; + pAnalysisData->mseG = 0.0; + pAnalysisData->mseB = 0.0; + pAnalysisData->mseA = 0.0; + + pAnalysisData->psnr = 128; + pAnalysisData->psnrR = 128; + pAnalysisData->psnrG = 128; + pAnalysisData->psnrB = 128; + pAnalysisData->psnrA = 128; + } else - *outPSNR = 10 * log((1.0 * 255 * 255) / *outMSE) / log(10.0); + { + if (pAnalysisData->mse > 0.0) + pAnalysisData->psnr = 10 * log((1.0 * 255 * 255) / pAnalysisData->mse) / log(10.0); + if (pAnalysisData->mseR > 0.0) + pAnalysisData->psnrR = 10 * log((1.0 * 255 * 255) / pAnalysisData->mseR) / log(10.0); + if (pAnalysisData->mseG > 0.0) + pAnalysisData->psnrG = 10 * log((1.0 * 255 * 255) / pAnalysisData->mseG) / log(10.0); + if (pAnalysisData->mseB > 0.0) + pAnalysisData->psnrB = 10 * log((1.0 * 255 * 255) / pAnalysisData->mseB) / log(10.0); + if (pAnalysisData->mseA > 0.0) + pAnalysisData->psnrA = 10 * log((1.0 * 255 * 255) / pAnalysisData->mseA) / log(10.0); + } } -template -void CMP_calcMSE_PSNRf(MipLevel* pCurMipLevel, T* pdata1, T* pdata2, CMP_DOUBLE* outMSE, CMP_DOUBLE* outPSNR, CMP_INT numchannels) { - CMP_DOUBLE mseRGB = 0.0; - CMP_INT totalPixels = 0; - for (int y = 0; y < pCurMipLevel->m_nHeight; y++) { - for (int x = 0; x < pCurMipLevel->m_nWidth; x++) { - // calc mse for the all color channels - for (int i = 0; i < 3 && i < numchannels; i++) { - mseRGB += pow(abs(*pdata1 - *pdata2), 2); +void CMP_calcMSE_PSNRf32(MipLevel* pCurMipLevel, CMP_FLOAT* pdata1, CMP_FLOAT* pdata2, CMP_AnalysisData* pAnalysisData) +{ + CMP_UINT RGBAChannels = pAnalysisData->channelBitMap; + CMP_DOUBLE mse; + CMP_DOUBLE mseR = 0.0; + CMP_DOUBLE mseG = 0.0; + CMP_DOUBLE mseB = 0.0; + CMP_DOUBLE mseA = 0.0; + CMP_DOUBLE mseRGBA = 0.0; + CMP_INT totalPixelsR = 0; + CMP_INT totalPixelsG = 0; + CMP_INT totalPixelsB = 0; + CMP_INT totalPixelsA = 0; + CMP_INT totalPixels = 0; + + for (int y = 0; y < pCurMipLevel->m_nHeight; y++) + { + for (int x = 0; x < pCurMipLevel->m_nWidth; x++) + { + // calc Gamma for the all active channels + // Red channel + if (RGBAChannels & 0b0001) + { + mse = pow(abs(*pdata1 - *pdata2), 2); + mseR += mse; + mseRGBA += mse; + totalPixelsR++; + totalPixels++; + } + pdata1++; + pdata2++; + // Green channel + if (RGBAChannels & 0b0010) + { + mse = pow(abs(*pdata1 - *pdata2), 2); + mseG += mse; + mseRGBA += mse; + totalPixelsG++; totalPixels++; - pdata1++; - pdata2++; } - // if alpha skip it - if (numchannels > 3) { - pdata1++; - pdata2++; + pdata1++; + pdata2++; + // Blue channel + if (RGBAChannels & 0b0100) + { + mse = pow(abs(*pdata1 - *pdata2), 2); + mseB += mse; + mseRGBA += mse; + totalPixelsB++; + totalPixels++; + } + pdata1++; + pdata2++; + // Alpha channel + if (RGBAChannels & 0b1000) + { + mse = pow(abs(*pdata1 - *pdata2), 2); + mseA += mse; + mseRGBA += mse; + totalPixelsA++; + totalPixels++; } + pdata1++; + pdata2++; } } if (totalPixels == 0) + { totalPixels = 1; - *outMSE = mseRGB / totalPixels; - if (*outMSE == 0) - *outPSNR = 128; + } + + pAnalysisData->mse = mseRGBA / totalPixels; + pAnalysisData->mseR = mseR / totalPixelsR; + pAnalysisData->mseG = mseG / totalPixelsG; + pAnalysisData->mseB = mseB / totalPixelsB; + pAnalysisData->mseA = mseA / totalPixelsA; + + if (pAnalysisData->mse <= 0.0) + { + pAnalysisData->mse = 0.0; + pAnalysisData->mseR = 0.0; + pAnalysisData->mseG = 0.0; + pAnalysisData->mseB = 0.0; + pAnalysisData->mseA = 0.0; + pAnalysisData->psnr = 128; + pAnalysisData->psnrR = 128; + pAnalysisData->psnrG = 128; + pAnalysisData->psnrB = 128; + pAnalysisData->psnrA = 128; + } else - *outPSNR = 10 * log((1.0 * 255 * 255) / *outMSE) / log(10.0); + { + if (pAnalysisData->mse > 0.0) + pAnalysisData->psnr = 10 * log((1.0 * 255 * 255) / pAnalysisData->mse) / log(10.0); + if (pAnalysisData->mseR > 0.0) + pAnalysisData->psnrR = 10 * log((1.0 * 255 * 255) / pAnalysisData->mseR) / log(10.0); + if (pAnalysisData->mseG > 0.0) + pAnalysisData->psnrG = 10 * log((1.0 * 255 * 255) / pAnalysisData->mseG) / log(10.0); + if (pAnalysisData->mseB > 0.0) + pAnalysisData->psnrB = 10 * log((1.0 * 255 * 255) / pAnalysisData->mseB) / log(10.0); + if (pAnalysisData->mseA > 0.0) + pAnalysisData->psnrA = 10 * log((1.0 * 255 * 255) / pAnalysisData->mseA) / log(10.0); + } } -CMP_ERROR CMP_API CMP_CalcMipSetMSE_PSNR(CMP_MipSet* src1, CMP_MipSet* src2, CMP_INT nMipLevel, CMP_INT nFaceOrSlice, CMP_DOUBLE* outMSE, CMP_DOUBLE* outPSNR) { +CMP_ERROR CMP_API CMP_MipSetAnlaysis(CMP_MipSet* src1, CMP_MipSet* src2, CMP_INT nMipLevel, CMP_INT nFaceOrSlice, CMP_AnalysisData* pAnalysisData) +{ if ((!src1) || (!src2)) return CMP_ERR_GENERIC; // sanity checks @@ -198,8 +764,8 @@ CMP_ERROR CMP_API CMP_CalcMipSetMSE_PSNR(CMP_MipSet* src1, CMP_MipSet* src2, CMP CMIPS CMips; MipLevel* pCurMipLevel1; MipLevel* pCurMipLevel2; - *outMSE = 0.0f; - *outPSNR = 0.0f; + pAnalysisData->mse = 0.0f; + pAnalysisData->psnr = 0.0f; pCurMipLevel1 = CMips.GetMipLevel(src1, nMipLevel, nFaceOrSlice); if (!pCurMipLevel1) @@ -210,15 +776,19 @@ CMP_ERROR CMP_API CMP_CalcMipSetMSE_PSNR(CMP_MipSet* src1, CMP_MipSet* src2, CMP // processed only if both codecs have the same channel formats if ((src1->m_ChannelFormat == CF_8bit) && (src2->m_ChannelFormat == CF_8bit)) - CMP_calcMSE_PSNRb(pCurMipLevel1, pCurMipLevel1->m_pbData, pCurMipLevel2->m_pbData, outMSE, outPSNR, 4); + CMP_calcMSE_PSNRb(pCurMipLevel1, pCurMipLevel1->m_pbData, pCurMipLevel2->m_pbData, pAnalysisData); else if ((src1->m_ChannelFormat == CF_Float16) && (src2->m_ChannelFormat == CF_Float16)) - CMP_calcMSE_PSNRf(pCurMipLevel1, pCurMipLevel1->m_phfsData, pCurMipLevel2->m_phfsData, outMSE, outPSNR, 4); - else if ((src1->m_ChannelFormat == CF_Float32) &&(src1->m_ChannelFormat == CF_Float32)) - CMP_calcMSE_PSNRf(pCurMipLevel1, pCurMipLevel1->m_pfData, pCurMipLevel2->m_pfData, outMSE, outPSNR, 4); + CMP_calcMSE_PSNRHalfShort(pCurMipLevel1, pCurMipLevel1->m_phfsData, pCurMipLevel2->m_phfsData, pAnalysisData); + else if ((src1->m_ChannelFormat == CF_Float32) && (src2->m_ChannelFormat == CF_Float32)) + CMP_calcMSE_PSNRf32(pCurMipLevel1, pCurMipLevel1->m_pfData, pCurMipLevel2->m_pfData, pAnalysisData); + else if ((src1->m_ChannelFormat == CF_Float16) && (src2->m_ChannelFormat == CF_8bit)) + CMP_calcMSE_PSNRHalfShortByte(pCurMipLevel1, pCurMipLevel1->m_phfsData, pCurMipLevel2->m_pbData, pAnalysisData); + return CMP_OK; } -CMP_INT CMP_MaxFacesOrSlices(const CMP_MipSet* pMipSet, CMP_INT nMipLevel) { +CMP_INT CMP_MaxFacesOrSlices(const CMP_MipSet* pMipSet, CMP_INT nMipLevel) +{ if (!pMipSet) return 0; @@ -229,58 +799,72 @@ CMP_INT CMP_MaxFacesOrSlices(const CMP_MipSet* pMipSet, CMP_INT nMipLevel) { return pMipSet->m_nDepth; int nMaxSlices = pMipSet->m_nDepth; - for (int i = 0; im_nMipLevels; i++) { + for (int i = 0; i < pMipSet->m_nMipLevels; i++) + { if (i == nMipLevel) return nMaxSlices; - nMaxSlices = nMaxSlices>1 ? nMaxSlices >> 1 : 1; //div by 2, min of 1 + nMaxSlices = nMaxSlices > 1 ? nMaxSlices >> 1 : 1; //div by 2, min of 1 } - return 0; //nMipLevel was too high + return 0; //nMipLevel was too high } -CMP_MipLevel* CMP_CMIPS::GetMipLevel(const CMP_MipSet* pMipSet, int nMipLevel, int nFaceOrSlice) { - if(!pMipSet) { +CMP_MipLevel* CMP_CMIPS::GetMipLevel(const CMP_MipSet* pMipSet, int nMipLevel, int nFaceOrSlice) +{ + if (!pMipSet) + { assert(pMipSet); return NULL; } - if (!pMipSet->m_pMipLevelTable) { + if (!pMipSet->m_pMipLevelTable) + { return NULL; } - if (nMipLevel > MAX_MIPLEVEL_SUPPORTED) { + if (nMipLevel > MAX_MIPLEVEL_SUPPORTED) + { return NULL; } - if(nMipLevel > pMipSet->m_nMaxMipLevels) { + if (nMipLevel > pMipSet->m_nMaxMipLevels) + { assert(nMipLevel <= pMipSet->m_nMaxMipLevels); return NULL; } - if(nFaceOrSlice < 0) { - return NULL; //not an error, indicates requested face doesn't exist + if (nFaceOrSlice < 0) + { + return NULL; //not an error, indicates requested face doesn't exist } int nDepth = pMipSet->m_nDepth, index = 0, whichMipLevel = 0; - switch(pMipSet->m_TextureType) { + switch (pMipSet->m_TextureType) + { case TT_2D: - if(nFaceOrSlice != 0) { + if (nFaceOrSlice != 0) + { return NULL; } return (pMipSet->m_pMipLevelTable)[nMipLevel]; case TT_CubeMap: - if(nFaceOrSlice > 6) { //cubemap have at most 6 faces + if (nFaceOrSlice > 6) + { //cubemap have at most 6 faces assert(nFaceOrSlice > 6); return NULL; } return (pMipSet->m_pMipLevelTable)[nMipLevel * nDepth + nFaceOrSlice]; case TT_VolumeTexture: - while(whichMipLevel <= nMipLevel) { - if(whichMipLevel == nMipLevel) { + while (whichMipLevel <= nMipLevel) + { + if (whichMipLevel == nMipLevel) + { return (pMipSet->m_pMipLevelTable)[index + nFaceOrSlice]; - } else { + } + else + { index += nDepth; whichMipLevel++; - nDepth = nDepth>1 ? nDepth>>1 : 1; + nDepth = nDepth > 1 ? nDepth >> 1 : 1; } } return NULL; @@ -293,48 +877,56 @@ CMP_MipLevel* CMP_CMIPS::GetMipLevel(const CMP_MipSet* pMipSet, int nMipLevel, // Mip Levels are coded as follows // 1 is original size 2 = 1/2 size 3 = 1/4 size etc... // this value is based on arrays been MipMap[maxMipLevels] -int CMP_CMIPS::GetMaxMipLevels(int nWidth, int nHeight, int nDepth) { +int CMP_CMIPS::GetMaxMipLevels(int nWidth, int nHeight, int nDepth) +{ int maxMipLevels = 0; assert(nWidth > 0 && nHeight > 0 && nDepth > 0); - while (nWidth >= 1 || nHeight >= 1 || nDepth > 1) { + while (nWidth >= 1 || nHeight >= 1 || nDepth > 1) + { maxMipLevels++; if (nWidth == 1 || nHeight == 1) break; //div by 2 - nWidth = nWidth >1 ? nWidth >>1 : 1; - nHeight = nHeight>1 ? nHeight>>1 : 1; - nDepth = nDepth >1 ? nDepth >>1 : 1; + nWidth = nWidth > 1 ? nWidth >> 1 : 1; + nHeight = nHeight > 1 ? nHeight >> 1 : 1; + nDepth = nDepth > 1 ? nDepth >> 1 : 1; } return maxMipLevels; } -bool CMP_CMIPS::AllocateMipLevelTable(CMP_MipLevelTable** ppMipLevelTable, int nMaxMipLevels, CMP_TextureType textureType, int nDepth, int& nLevelsToAllocate) { +bool CMP_CMIPS::AllocateMipLevelTable(CMP_MipLevelTable** ppMipLevelTable, int nMaxMipLevels, CMP_TextureType textureType, int nDepth, int& nLevelsToAllocate) +{ //TODO test assert(nDepth > 0); nLevelsToAllocate = 0; //determine # miplevels to allocate based on texture type - switch(textureType) { + switch (textureType) + { case TT_2D: nLevelsToAllocate = nMaxMipLevels; - if(nDepth != 1) { + if (nDepth != 1) + { return false; } break; case TT_CubeMap: - if (nDepth > 6) { + if (nDepth > 6) + { return false; } nLevelsToAllocate = nMaxMipLevels * nDepth; break; case TT_VolumeTexture: - for(int i=0; i < nMaxMipLevels; i++) { + for (int i = 0; i < nMaxMipLevels; i++) + { nLevelsToAllocate += nDepth; - if(nDepth > 1) { + if (nDepth > 1) + { nDepth >>= 1; } } @@ -348,17 +940,22 @@ bool CMP_CMIPS::AllocateMipLevelTable(CMP_MipLevelTable** ppMipLevelTable, int n return (*ppMipLevelTable != NULL); } -bool CMP_CMIPS::AllocateAllMipLevels(CMP_MipLevelTable* pMipLevelTable, CMP_TextureType /*textureType*/, int nLevelsToAllocate) { +bool CMP_CMIPS::AllocateAllMipLevels(CMP_MipLevelTable* pMipLevelTable, CMP_TextureType /*textureType*/, int nLevelsToAllocate) +{ //TODO test //allocate each MipLevel that the table points to - for(int i=0; i(calloc(sizeof(CMP_MipLevel), 1)); //make sure it was allocated ok assert(pMipLevelTable[i]); - if(!pMipLevelTable[i]) { + if (!pMipLevelTable[i]) + { //free previous mipLevels - for(i -= 1; i>=0; i--) { - if (pMipLevelTable[i]) { + for (i -= 1; i >= 0; i--) + { + if (pMipLevelTable[i]) + { free(pMipLevelTable[i]); pMipLevelTable[i] = NULL; } @@ -369,37 +966,49 @@ bool CMP_CMIPS::AllocateAllMipLevels(CMP_MipLevelTable* pMipLevelTable, CMP_Text return true; } -bool CMP_CMIPS::AllocateMipSet(CMP_MipSet* pMipSet, CMP_ChannelFormat channelFormat, TextureDataType textureDataType, CMP_TextureType textureType, int nWidth, int nHeight, int nDepth) { +bool CMP_CMIPS::AllocateMipSet(CMP_MipSet* pMipSet, + CMP_ChannelFormat channelFormat, + TextureDataType textureDataType, + CMP_TextureType textureType, + int nWidth, + int nHeight, + int nDepth) +{ //TODO test assert(pMipSet); - if (!(nWidth > 0 && nHeight > 0 && nDepth > 0)) return false; + if (!(nWidth > 0 && nHeight > 0 && nDepth > 0)) + return false; - if(pMipSet->m_pMipLevelTable) { + if (pMipSet->m_pMipLevelTable) + { assert(!pMipSet->m_pMipLevelTable); return false; } //depth only matters for this when its volume texture - pMipSet->m_nMaxMipLevels = GetMaxMipLevels(nWidth, nHeight, textureType== TT_VolumeTexture ? nDepth : 1); + pMipSet->m_nMaxMipLevels = GetMaxMipLevels(nWidth, nHeight, textureType == TT_VolumeTexture ? nDepth : 1); - if(pMipSet->m_nMipLevels > pMipSet->m_nMaxMipLevels || pMipSet->m_nMipLevels < 0) + if (pMipSet->m_nMipLevels > pMipSet->m_nMaxMipLevels || pMipSet->m_nMipLevels < 0) pMipSet->m_nMipLevels = 0; - pMipSet->m_ChannelFormat = channelFormat; + pMipSet->m_ChannelFormat = channelFormat; pMipSet->m_TextureDataType = textureDataType; - pMipSet->m_TextureType = textureType; + pMipSet->m_TextureType = textureType; //Probably shouldn't wipe this out either pMipSet->m_Flags = MS_Default; //On second thought, DONT wipe this out pMipSet->m_CubeFaceMask = 0; - pMipSet->m_nWidth = nWidth; + pMipSet->m_nWidth = nWidth; pMipSet->m_nHeight = nHeight; - pMipSet->m_nDepth = nDepth; + pMipSet->m_nDepth = nDepth; int numLevelsToAllocate; - if(!AllocateMipLevelTable(&pMipSet->m_pMipLevelTable, pMipSet->m_nMaxMipLevels, textureType, nDepth, numLevelsToAllocate)) { + if (!AllocateMipLevelTable(&pMipSet->m_pMipLevelTable, pMipSet->m_nMaxMipLevels, textureType, nDepth, numLevelsToAllocate)) + { //mipleveltable allocation failed return false; } - if(!AllocateAllMipLevels(pMipSet->m_pMipLevelTable, textureType, numLevelsToAllocate)) { + if (!AllocateAllMipLevels(pMipSet->m_pMipLevelTable, textureType, numLevelsToAllocate)) + { //allocation of mip levels failed - if (pMipSet->m_pMipLevelTable) { + if (pMipSet->m_pMipLevelTable) + { free(pMipSet->m_pMipLevelTable); pMipSet->m_pMipLevelTable = NULL; } @@ -416,7 +1025,8 @@ bool CMP_CMIPS::AllocateMipLevelData(CMP_MipLevel* pMipLevel, int nWidth, int nH assert(nWidth > 0 && nHeight > 0); CMP_DWORD dwBitsPerPixel; - switch(channelFormat) { + switch (channelFormat) + { case CF_8bit: case CF_2101010: case CF_Float9995E: @@ -438,57 +1048,63 @@ bool CMP_CMIPS::AllocateMipLevelData(CMP_MipLevel* pMipLevel, int nWidth, int nH return false; } - switch(textureDataType) { + switch (textureDataType) + { case TDT_XRGB: case TDT_ARGB: case TDT_NORMAL_MAP: dwBitsPerPixel *= 4; break; - - case TDT_R: + case TDT_RGB: + dwBitsPerPixel *= 3; break; - case TDT_RG: dwBitsPerPixel *= 2; break; - + case TDT_R: + break; default: assert(0); return false; } - CMP_DWORD dwPitch = CMP_PAD_BYTE(nWidth, dwBitsPerPixel); + CMP_DWORD dwPitch = CMP_PAD_BYTE(nWidth, dwBitsPerPixel); pMipLevel->m_dwLinearSize = dwPitch * nHeight; - pMipLevel->m_nWidth = nWidth; - pMipLevel->m_nHeight = nHeight; + pMipLevel->m_nWidth = nWidth; + pMipLevel->m_nHeight = nHeight; pMipLevel->m_pbData = reinterpret_cast(malloc(pMipLevel->m_dwLinearSize)); return (pMipLevel->m_pbData != NULL); } -bool CMP_CMIPS::AllocateCompressedMipLevelData(CMP_MipLevel* pMipLevel, int nWidth, int nHeight, CMP_DWORD dwSize) { +bool CMP_CMIPS::AllocateCompressedMipLevelData(CMP_MipLevel* pMipLevel, int nWidth, int nHeight, CMP_DWORD dwSize) +{ //TODO test assert(pMipLevel); assert(nWidth > 0 && nHeight > 0); pMipLevel->m_dwLinearSize = dwSize; - pMipLevel->m_nWidth = nWidth; - pMipLevel->m_nHeight = nHeight; + pMipLevel->m_nWidth = nWidth; + pMipLevel->m_nHeight = nHeight; pMipLevel->m_pbData = reinterpret_cast(malloc(pMipLevel->m_dwLinearSize)); return (pMipLevel->m_pbData != NULL); } -void CMP_CMIPS::FreeMipSet(CMP_MipSet* pMipSet) { +void CMP_CMIPS::FreeMipSet(CMP_MipSet* pMipSet) +{ //TODO test int nTotalOldMipLevels = 0; assert(pMipSet); - if(pMipSet) { - if(pMipSet->m_pMipLevelTable) { + if (pMipSet) + { + if (pMipSet->m_pMipLevelTable) + { //determine number of miplevels in the old mipleveltable - switch(pMipSet->m_TextureType) { + switch (pMipSet->m_TextureType) + { case TT_2D: nTotalOldMipLevels = pMipSet->m_nMaxMipLevels; break; @@ -496,11 +1112,11 @@ void CMP_CMIPS::FreeMipSet(CMP_MipSet* pMipSet) { nTotalOldMipLevels = pMipSet->m_nMaxMipLevels * pMipSet->m_nDepth; break; case TT_VolumeTexture: - for(int depth=pMipSet->m_nDepth, mipLevels=0; - mipLevels < pMipSet->m_nMaxMipLevels; - mipLevels++) { + for (int depth = pMipSet->m_nDepth, mipLevels = 0; mipLevels < pMipSet->m_nMaxMipLevels; mipLevels++) + { nTotalOldMipLevels += depth; - if(depth > 1) { + if (depth > 1) + { depth >>= 1; } } @@ -509,21 +1125,26 @@ void CMP_CMIPS::FreeMipSet(CMP_MipSet* pMipSet) { assert(0); } //free all miplevels and their data except the one use in gui view - for(int i=0; im_pMipLevelTable[i]->m_pbData) { + for (int i = 0; i < nTotalOldMipLevels - 2; i++) + { + if (pMipSet->m_pMipLevelTable[i]->m_pbData) + { #ifdef USE_BASIS - if (pMipSet->m_format == CMP_FORMAT_BASIS) { - CMP_VEC8 &basis_data = *(pMipSet->m_pMipLevelTable[i]->m_pvec8Data); + if (pMipSet->m_format == CMP_FORMAT_BASIS) + { + CMP_VEC8& basis_data = *(pMipSet->m_pMipLevelTable[i]->m_pvec8Data); if (basis_data.size() > 0) delete &basis_data; - } else + } + else #endif free(pMipSet->m_pMipLevelTable[i]->m_pbData); pMipSet->m_pMipLevelTable[i]->m_pbData = NULL; } - if (pMipSet->m_pMipLevelTable[i]) { + if (pMipSet->m_pMipLevelTable[i]) + { free(pMipSet->m_pMipLevelTable[i]); pMipSet->m_pMipLevelTable[i] = NULL; } @@ -537,21 +1158,23 @@ void CMP_CMIPS::FreeMipSet(CMP_MipSet* pMipSet) { } } -void CMP_CMIPS::FreeMipLevelData(CMP_MipLevel* pMipLevel) { - if (pMipLevel->m_pbData) { +void CMP_CMIPS::FreeMipLevelData(CMP_MipLevel* pMipLevel) +{ + if (pMipLevel->m_pbData) + { free(pMipLevel->m_pbData); pMipLevel->m_pbData = NULL; } } - -bool CMP_CMIPS::AllocateCompressedDestBuffer(CMP_MipSet *SourceTexture, CMP_FORMAT format, CMP_MipSet *DestTexture) { - CMP_MipLevel* pInMipLevel = GetMipLevel(SourceTexture, 0); - SourceTexture->dwWidth = pInMipLevel->m_nWidth; - SourceTexture->dwHeight = pInMipLevel->m_nHeight; - SourceTexture->dwDataSize = pInMipLevel->m_dwLinearSize; - SourceTexture->pData = pInMipLevel->m_pbData; - SourceTexture->m_swizzle = false; +bool CMP_CMIPS::AllocateCompressedDestBuffer(CMP_MipSet* SourceTexture, CMP_FORMAT format, CMP_MipSet* DestTexture) +{ + CMP_MipLevel* pInMipLevel = GetMipLevel(SourceTexture, 0); + SourceTexture->dwWidth = pInMipLevel->m_nWidth; + SourceTexture->dwHeight = pInMipLevel->m_nHeight; + SourceTexture->dwDataSize = pInMipLevel->m_dwLinearSize; + SourceTexture->pData = pInMipLevel->m_pbData; + SourceTexture->m_swizzle = false; memset(DestTexture, 0, sizeof(CMP_MipSet)); DestTexture->m_Flags = MS_FLAG_Default; @@ -571,9 +1194,11 @@ bool CMP_CMIPS::AllocateCompressedDestBuffer(CMP_MipSet *SourceTexture, CMP_FORM DestTexture->m_nMaxMipLevels = SourceTexture->m_nMaxMipLevels; DestTexture->m_nMipLevels = 1; DestTexture->m_nDepth = SourceTexture->m_nDepth; - if (DestTexture->m_nDepth < 1) DestTexture->m_nDepth = 1; // depthsupport on compressed data ? + if (DestTexture->m_nDepth < 1) + DestTexture->m_nDepth = 1; // depthsupport on compressed data ? - if (!AllocateMipSet(DestTexture, CF_8bit, TDT_ARGB, TT_2D, SourceTexture->m_nWidth, SourceTexture->m_nHeight, 1)) { + if (!AllocateMipSet(DestTexture, CF_8bit, TDT_ARGB, TT_2D, SourceTexture->m_nWidth, SourceTexture->m_nHeight, 1)) + { // std::printf("Memory Error(1): allocating MIPSet Compression buffer\n"); return false; } @@ -586,26 +1211,27 @@ bool CMP_CMIPS::AllocateCompressedDestBuffer(CMP_MipSet *SourceTexture, CMP_FORM //---------------------------------------------------------------- // Calculate the target compressed block buffer size (4 bytes x 4 bytes) //---------------------------------------------------------------- - unsigned int Width = ((SourceTexture->m_nWidth + 3) / 4) * 4; - unsigned int Height = ((SourceTexture->m_nHeight + 3) / 4) * 4; - DestTexture->dwDataSize = Width * Height; + unsigned int Width = ((SourceTexture->m_nWidth + 3) / 4) * 4; + unsigned int Height = ((SourceTexture->m_nHeight + 3) / 4) * 4; + DestTexture->dwDataSize = Width * Height; //---------------------------------------------------------------- // Allocate memory for the mip level 0 //---------------------------------------------------------------- - if (!AllocateCompressedMipLevelData(pOutMipLevel, DestTexture->dwWidth, DestTexture->dwHeight, DestTexture->dwDataSize)) { + if (!AllocateCompressedMipLevelData(pOutMipLevel, DestTexture->dwWidth, DestTexture->dwHeight, DestTexture->dwDataSize)) + { //std::printf("Memory Error(1): allocating MIPSet compression level data buffer\n"); return false; } - DestTexture->pData = pOutMipLevel->m_pbData; + DestTexture->pData = pOutMipLevel->m_pbData; return true; } -void CMP_CMIPS::SetProgress(unsigned int value) { - if (SetProgressValue) { +void CMP_CMIPS::SetProgress(unsigned int value) +{ + if (SetProgressValue) + { SetProgressValue(value, &m_canceled); } } - - diff --git a/cmp_framework/common/hdr_encode.cpp b/cmp_framework/common/hdr_encode.cpp index 5663312b7..491957841 100644 --- a/cmp_framework/common/hdr_encode.cpp +++ b/cmp_framework/common/hdr_encode.cpp @@ -1251,7 +1251,7 @@ void eigenVector_d(float cov[MAX_DIMENSION_BIG][MAX_DIMENSION_BIG], float vector for (j = 0; j0); diff --git a/cmp_framework/common/hdr_encode.h b/cmp_framework/common/hdr_encode.h index 6359e7740..64999cd5b 100644 --- a/cmp_framework/common/hdr_encode.h +++ b/cmp_framework/common/hdr_encode.h @@ -60,7 +60,7 @@ extern void Partition( int shape, // Used by optQuantAnD_d #define MAX_ENTRIES 64 #define MAX_TRY 4000 -#define FLT_MAX_EXP 128 // DBL_MAX_EXP_ = 1024 +#define HDR_FLT_MAX_EXP 128 // DBL_MAX_EXP_ = 1024 #define MAX_PARTITIONS_TABLE (1+64+64) // Out contains all endpoints (out) calaculated for the input shape (data) and pattern (index) diff --git a/cmp_framework/compute_base.cpp b/cmp_framework/compute_base.cpp index bf5c5b2ae..3b0a02b9c 100644 --- a/cmp_framework/compute_base.cpp +++ b/cmp_framework/compute_base.cpp @@ -38,7 +38,7 @@ #include -CMP_CHAR* GetFormatDesc(CMP_FORMAT nFormat); +CMP_CHAR* GetFormatDesc(CMP_FORMAT nFormat); PluginManager g_pluginManager; PluginInterface_Encoder* plugin_encoder_codec = NULL; static CMP_BOOL HostPluginsRegistered = FALSE; @@ -239,6 +239,7 @@ CMP_ERROR CMP_API CMP_CreateComputeLibrary(MipSet* srcTexture, KernelOptions* ke // Initialize the compression codec, pass kernel options kernel_options->height = srcTexture->dwHeight; kernel_options->width = srcTexture->dwWidth; + kernel_options->srcformat = srcTexture->m_format; if (plugin_encoder_codec->TC_Init(kernel_options) != 0) { PrintInfo("Failed to init encoder\n"); return CMP_ERR_UNABLE_TO_INIT_COMPUTELIB; @@ -359,6 +360,8 @@ CodecType GetCodecType2(CMP_FORMAT format) { return CT_None; case CMP_FORMAT_ARGB_8888: return CT_None; + case CMP_FORMAT_RGBA_8888_S: + return CT_None; case CMP_FORMAT_BGR_888: return CT_None; case CMP_FORMAT_RGB_888: @@ -617,6 +620,7 @@ CMP_DWORD CalcBufferSize2(CMP_FORMAT format, CMP_DWORD dwWidth, CMP_DWORD dwHeig case CMP_FORMAT_RGBA_8888: case CMP_FORMAT_BGRA_8888: case CMP_FORMAT_ARGB_8888: + case CMP_FORMAT_RGBA_8888_S: case CMP_FORMAT_ARGB_2101010: return ((dwPitch) ? (dwPitch * dwHeight) : (dwWidth * 4 * dwHeight)); @@ -686,8 +690,8 @@ CMP_DWORD CMP_API CMP_CalculateBufferSize2(const CMP_Texture* pTexture) { if (pTexture->dwHeight <= 0) return 0; - assert(pTexture->format >= CMP_FORMAT_ARGB_8888 && pTexture->format <= CMP_FORMAT_MAX); - if (pTexture->format < CMP_FORMAT_ARGB_8888 || pTexture->format > CMP_FORMAT_MAX) + assert(pTexture->format >= CMP_FORMAT_RGBA_8888_S && pTexture->format <= CMP_FORMAT_MAX); + if (pTexture->format < CMP_FORMAT_RGBA_8888_S || pTexture->format > CMP_FORMAT_MAX) return 0; return CalcBufferSize2(pTexture->format, pTexture->dwWidth, pTexture->dwHeight, pTexture->dwPitch, pTexture->nBlockWidth, pTexture->nBlockHeight); @@ -1016,7 +1020,11 @@ CMP_ERROR CMP_API CMP_SaveTexture(const char* DestFile, CMP_MipSet* MipSetIn) { std::string file_extension = fn.substr(fn.find_last_of(".") + 1); std::transform(file_extension.begin(), file_extension.end(), file_extension.begin(), toupperChar); - if ((((file_extension.compare("DDS") == 0) || file_extension.compare("KTX") == 0)) != TRUE) { + if (((((file_extension.compare("DDS") == 0) + || file_extension.compare("KTX") == 0) + || file_extension.compare("KTX2") == 0)) + != TRUE) + { return CMP_ERR_INVALID_DEST_TEXTURE; } diff --git a/docs/makefile b/docs/makefile index 288f5d41c..3aa8ea056 100644 --- a/docs/makefile +++ b/docs/makefile @@ -3,8 +3,8 @@ # You can set these variables from the command line. SPHINXOPTS = -SPHINXBUILD = python -msphinx -SPHINXPROJ = Compressonator +SPHINXBUILD = sphinx-build +SPHINXPROJ = RadeonGPUAnalyzer SOURCEDIR = source BUILDDIR = build diff --git a/docs/source/analysis/index.rst b/docs/source/analysis/index.rst index a29cf9f32..b8eb592f6 100644 --- a/docs/source/analysis/index.rst +++ b/docs/source/analysis/index.rst @@ -142,6 +142,6 @@ CPU performance based timing, that measures the overall end to end time it took -.. |analysis_image1| image:: media/CMP_v3.2_Run_Time_Analysis.png -.. |analysis_image2| image:: media/CMP_v3.2_Analysis_Output.png +.. |analysis_image1| image:: media/cmp_v3.2_run_time_analysis.png +.. |analysis_image2| image:: media/cmp_v3.2_analysis_output.png .. |analysis_image3| image:: ../gui_tool/user_guide/media/image2020-3-17_13-43-45.png diff --git a/docs/source/build_from_source/build_instructions.rst b/docs/source/build_from_source/build_instructions.rst index 036199581..63d3bd6e8 100644 --- a/docs/source/build_from_source/build_instructions.rst +++ b/docs/source/build_from_source/build_instructions.rst @@ -9,9 +9,9 @@ which includes command line tools, GUI and binaries for use in developer applica The following build instructions are provided for developers interested in building the latest sources available on `Compressonator GitHub `_ or from the source code downloaded from the `releases `_ page. -**Note**: Git submodule contents in Compressonator/Common/Lib folder will not be automatically included in downloaded source code. You can either navigate to all the "url" listed in the `gitmodule `_ and download zip from each of the url and extract them into the Compressonator/Common/Lib folder OR if you have Git installed in your system, run the Git command: +.. code-block:: console -``git clone --recursive https://github.com/GPUOpen-Tools/Compressonator.git`` + git clone --recursive https://github.com/GPUOpen-Tools/Compressonator.git to get all the source code including the submodules contents. @@ -19,189 +19,87 @@ to get all the source code including the submodules contents. Build Instructions for Compressonator SDK Libs ============================================== -Use the following project files to build binaries as needed for use in your own applications or link to your own SDK +VS2017 project soultion files are provided to build SDK libs in the folder compressonator/vs2017 -Texture Compression Codecs --------------------------- -Prebuilt Binaries for Compressonator Visual Studio Libs are supplied when using the installer in `release `_ OR you can build from `CompressonatorLib.sln `_ . - -This solution will create a build folder that contains DLL and Libs under Compressonator\\Build\\VS2015\\(configuration)\\(platform)\\ - -Example: Compressonator/Build/VS2015/Release_MD/Win32 - -Mesh Optimization Lib ---------------------- -(Interfaces to this lib will change in future releases) -"/Compressonator/VS2015/CMP_MeshOptimizer" - -Mesh Compression Lib ---------------------- -(Interfaces to this lib will change in future releases) -"/Compressonator/VS2015/CMP_MeshCompressor" - - -Build Instructions for Compressonator GUI and CLI applications on Windows 10 and up -=================================================================================== - -1. To build the applications you will need to have Visual Studio 2015 (VS 2015) installed and Vulkan SDK version 1.0.68.0 from `Vulkan LunarG website `_ . Only Vulkan SDK version 1.0.68.0 is tested in the build. - -Your installed VS 2015 should include Visual C++ development packages in order to properly run the solution files later in the build process. - -2. After Vulkan SDK installed, you will need to build libraries from the Vulkan SDK. These libraries are installed using the CMakeList.txt scripts provided in the default installation path C:\\VulkanSDK\\1.0.68.0\\glslang and C:\\VulkanSDK\\1.0.68.0\\spirv-tools folders. For sake of interest, the libraries are listed here: glslang.lib, SPIRV.lib, HLSL.lib, OGLCompiler.lib, OSDependent.lib, SPVRemapper.lib, SPIRV-Tools-opt.lib, SPIRV-Tools.lib - -3. To build the Vulkan-related libs in step 2, first download cmake tools from https://cmake.org/download/). During installation, make sure to checkmark `Add to PATH for all users` or otherwise manually set the environment PATH to include \\bin which contains cmake.exe. You will need to download python 2.7 as well (from https://www.python.org/downloads/) for glslang and spirv-tools build later. - -4. Then, copy and paste the following commands (these commands are for x64bit build, for 32bit build, change the build folder name from VS2015_64 to VS2015) in a window batch file, save the batch file in the default installation path C:\\VulkanSDK\\1.0.68.0\\glslang and run the batch file. - -.. code-block:: bat - - set CurrDir=%CD% - for %%* in (.) do set CurrDirName=%%~nx* - IF EXIST %CurrDir%\VS2015_64 (rmdir VS2015_64 /s /q) - mkdir VS2015_64 - cd VS2015_64 - cmake -G "Visual Studio 14 2015 Win64" ..\..\%CurrDirName% - cd %CurrDir% - -5. After the batch file run success (make sure there is no cmake errors or missing dependencies), open the glslang.sln file in the VS2015_64 folder with Microsoft Visual Studio 2015 and build the solution in Debug or Release mode. - -6. Repeat creating the same batch file in the default installation path C:\\VulkanSDK\\1.0.68.0\\spirv-tools and run the batch file in the path and then build the spirv-tools.sln file in the VS2015_64 or VS2015 folder. - -**Note**: Compressonator GUI project expects these glslang libs and spirv-tools libs are built in the C:\\VulkanSDK\\1.0.68.0\\\\ subfolders. If you have a different libs output paths, please remember to update all the paths macros Compressonator_GLSLANG accordingly in the Compressonator/Compressonator_Root.props file. - -7. The build will also requires QT V5.7 msvc2015 and/or msvc2015_64 opensource downloaded from `QT website `_ . - -8. It is also recommended that you install and configure Visual Studio Qt5 Package extension from `MSDN Visual Studio Gallery `_ , and set in Qt Options dialog, Qt Default Version name V5.7 and path to default download path C:\\Qt\\Qt5.7.1\\5.7\\msvc2015_64\\bin\\ -- you may have to `configure this from Visual Studio `_ . -**Note**: Compressonator GUI project solution files assume QT is installed in default path (C:/Qt/Qt5.7.1/5.7). and Vulkan SDK Environment variable (VULKAN_SDK) for path to the source code is set. Users must build the Vulkan SDK binaries prior to building the GUI and CLI applications. If you use different path, please change the project property accordingly in the Compressonator_Root.props file. +Build Instructions for Compressonator GUI and CLI applications +============================================================== -9.Compressonator applications are using the Windows SDK version 10.0.15063.0. You can either install the required version of Windows SDK or change the SDK version to any Window 10 version in the project property pages or by right-clicking the solution and selecting "Retarget solution". +Building on Windows +As a preliminary step, make sure that you have the following installed on your system: -10. The directory of your workspace should be as follows +- CMake 3.15 or above +- Vulkan SDK version 1.2.141.2 or above is required +- Python 3.6 or above +- Qt 5.12.6 is recommended -- Common -- Compressonator -- docs -- LICENSE -- README.md +Once installed users can run any of the following to build: -Build using Batch Files ------------------------- +Documents +--------- +cd to the Compressonator folder, and run: -After completing all the steps to install Vulkan SDK, Qt and projects set up as described above, -simply run one of the batch files from a window console: +.. code-block:: console -To build the Compressonator GUI tool, run BuildGUI.bat. + call scripts/windows_build_docs.bat -To build the Compressonator Command line tool, run BuildCLI.bat. +Applications +------------- -OR +.. code-block:: console -Build using Visual Studio solution files: ------------------------------------------ + call scripts/windows_build_apps.bat -After completing all the steps to install Vulkan SDK, Qt and projects set up as described above, +This call will first download additional dependencies used into a "common" folder above the compressonator source folder. It calls a python script called "fetch_dependencies.py" and cmake to build a vs2017 solution file into a "build" folder -go to the associated directory where Visual Studio Solution files are located as indicated below. -All of the Applications apart from the SDK libs can be built as either Release_MD or Debug_MD applications. - -Also make sure that the default startup projects are set as follows: - -CompressonatorCLI VS2015.sln set to CompressonatorCLI project -CompressonatorGUI VS2015.sln set to MainApp project - -Always clean and rebuild the projects. - -More details about: - -Command line tool ------------------- - -use: /Compressonator/Applications/CompressonatorCLI/VS2015/VS2015.sln - -startup project set to CompressonatorCLI - -This solution will create a build folder that contains a -CompressonatorCLI.exe and a new plugins folder under -/Compressonator/Build/VS2015/(configuration)/(platform)/ - -The command line tool has dependencies on the following: - -Qt (V5.7 and up) -- Qt5Core.dll -- Qt5Gui.dll -- qt.conf - -OpenGL -- glew32.dll -- libGLESv2.dll - -OpenCV -- opencv_core249.dll -- opencv_imgproc249.dll -- opencv_highgui249.dll - -Required only when using Vulkan plugin -- vulkan-1.dll -- texture.vert.spv -- texture.frag.spv - -Optional for image loading and saving -- qtga.dll -- qtiff.dll - -The dlls above are copied to CompressonatorCLI.exe folder by "CopyFiles.bat" in the VS2015.sln. - - -GUI Tool --------- -use: /Compressonator/Applications/CompressonatorGUI/VS2015/VS2015.sln - -startup project set to MainApp +Building all sdk libs +--------------------- +.. code-block:: console -This solution will create a build folder that contains a -Compressonator.exe and a plugins folder under -/Compressonator/Build/VS2015/(configuration)/(platform)\ + call scripts/windows_build_sdk.bat -If building in Debug_MD configuration, please make sure the working directory is in /Compressonator/Build/VS2015/Debug_MD/(platform)/ +If you have Visual Studio 2017, build solutions can also be setup by running the vsbuild.bat file that calls fetch_dependencies.py and cmake. -The GUI tool has dependencies on Qt(V5.7 and up) - These file are copied to the Compressonator.exe folder when the project solution builds and run "CopyFiles.bat". +Linux Builds Ubuntu 18.04 +-------------------------- +As a preliminary step, make sure that you have the following packages installed on your system: -Build Instructions for Linux CompressonatorCLI command line application -======================================================================= +- CMake 3.10 or above +- Vulkan SDK version 1.2.141.2 or above is required +- Python 3.6 or above +- Qt 5.9.2 (Qt 5.12.6 is recommended) -Note: For all unix build with this scripts, Do NOT move the shell/batch scripts provided (run in the same folder (Compressonator/Applications/CompressonatorCLI/Make/)) -For Ubuntu build (only v14.04LTS and v16.04LTS build tested, WSL is tested as well): +Additional packages are required and can be installed using the shell script: initsetup_ubuntu.sh -run initsetup_ubuntu.sh to install the required packages for command line app: +Then run the following in the compressonator/scripts folder -* cmake at least 2.8.12 -* gcc-6 and g++-6 (v6.3.0) -* glew and opengl (libglew-dev libegl1-mesa-dev) -* qt5 at least 5.5.1 -* openexr v2.2 -* opencv v2.49 -* Boost at least 1.59 (filesystem and system lib) +.. code-block:: console + python3 fetch_dependencies.py --no-hooks -run buildCLI_ubuntu_cmake.sh to build (or rebuild) all the dependencies libraries with CompressonatorCLI executable generated in the same folder +To build applications use -Note: For more details on unix build, please refer to `readme.txt `_ +.. code-block:: console + cd compressonator + export VULKAN_SDK_VER=1.2.141.2 + export VULKAN_SDK=/opt/VulkanSDK/$VULKAN_SDK_VER/ + QT_ROOT=/opt/Qt/Qt5.9.2/5.9.2/gcc_64 + cmake -DQT_PACKAGE_ROOT=$QT_ROOT . + make -Build Instructions for Linux Example command line application -============================================================= +The QT_ROOT and VULKAN_SDK should be set to your package install folder -To build the static Compressonator library or the Example command line applicatio under folder "Compressonator\Examples" you need to have the latest GCC **v6.3.0** toolchain installed (tested with 6.3.0 on WSL). +To build documents use -There are makefiles prepared for you: +.. code-block:: console -* Static library: makefile location "Compressonator/Make/ExampleMake/", output in "Compressonator/Build/Linux/libcompressonator.a" -* Example CLI: makefile location "Compressonator/Examples/Make", output in "Compressonator/Build/Linux/Example/cmpcli" + set -x + cd compressonator/docs + make -j 4 clean + make -j 4 html -Example only take dds file as input. For more file format input, please try build CompressonatorCLI. -Usage for the Example CLI: ./cmpcli src.dds out.dds ATC_RGB 1 diff --git a/docs/source/command_line_tool/commandline.rst b/docs/source/command_line_tool/commandline.rst index 0e9cee994..ff380a292 100644 --- a/docs/source/command_line_tool/commandline.rst +++ b/docs/source/command_line_tool/commandline.rst @@ -5,21 +5,26 @@ Usage CompressonatorCLI.exe [options] SourceFile DestFile +------------------------+----------------------------------------------+ |Mip Map Options: | | +========================+==============================================+ -|-nomipmap | Turns off Mipmap generation | +| -nomipmap | Turns off Mipmap generation | +------------------------+----------------------------------------------+ -|-\mipsize | The size in pixels used to determine | +| -\mipsize | The size in pixels used to determine | | | how many mip levels to generate | +------------------------+----------------------------------------------+ -|-\miplevels | Sets Mips Level for output, | +| -\miplevels | Sets Mips Level for output, | | | (mipSize overides this option): default is 1 | +------------------------+----------------------------------------------+ +| -GenGPUMipMap | When encoding with GPU this flag will enable | +| | mip map level generation using GPU HW | ++------------------------+----------------------------------------------+ +| -UseSRGBFrames | When encoding with GPU, GL_FRAMEBUFFER_SRGB | +| | will be enabled else use GL_FRAMEBUFFER | ++------------------------+----------------------------------------------+ + +---------------------+------------------------------------------------------------+ |Compression Options | | +=====================+============================================================+ -| -fs | Optionally specifies the source texture format to use | -+---------------------+------------------------------------------------------------+ | -fd | Specifies the destination texture format to use | +---------------------+------------------------------------------------------------+ | -decomp | If the destination file is compressed optionally | @@ -29,53 +34,33 @@ Usage CompressonatorCLI.exe [options] SourceFile DestFile | | with the sources format,decompress formats are typically | | | set to ARGB_8888 or ARGB_32F | +---------------------+------------------------------------------------------------+ -| -UseGPUDecompress | By default decompression is done using CPU | +| -UseGPUDecompress | By default decompression is done using CPU, | | | when set OpenGL will be used by default, this can be | | | changed to DirectX or Vulkan using DecodeWith setting | +---------------------+------------------------------------------------------------+ -| -EncodeWith | Compression with CPU, HPC, OCL or DXC | +| -EncodeWith | Compression with CPU, HPC, OCL, DXC, GPU. | | | Default is CPU. | +| | GPU will use GL Compress Extensions | | | OCL & DXC is only available on Windows Version | +---------------------+------------------------------------------------------------+ -| -DecodeWith | Sets OpenGL, DirectX or Vulkan for GPU decompress | +| -DecodeWith | GPU based decompression using OpenGL,DirectX or Vulkan | | | Default is OpenGL, UseGPUDecompress is implied when | | | this option is set | +---------------------+------------------------------------------------------------+ +| -draco | Enable draco compression. (only support glTF files) | ++---------------------+------------------------------------------------------------+ | -doswizzle | Swizzle the source images Red and Blue channels | +---------------------+------------------------------------------------------------+ +-----------------------+----------------------------------------------------------+ |Channel Formats | | +=======================+==========================================================+ -|ARGB_16 |ARGB format with 16-bit fixed channels | +|ARGB_8888 |ARGB format with 8-bit fixed channels | +-----------------------+----------------------------------------------------------+ |ARGB_16F |ARGB format with 16-bit floating-point channels | +-----------------------+----------------------------------------------------------+ |ARGB_32F |ARGB format with 32-bit floating-point channels | +-----------------------+----------------------------------------------------------+ -|ARGB_2101010 |ARGB format with 10-bit fixed channels for color | -| |and a 2-bit fixed channel for alpha | -+-----------------------+----------------------------------------------------------+ -|ARGB_8888 |ARGB format with 8-bit fixed channels | -+-----------------------+----------------------------------------------------------+ -|R_8 |Single component format with 8-bit fixed channels | -+-----------------------+----------------------------------------------------------+ -|R_16 |Single component format with 16-bit fixed channels | -+-----------------------+----------------------------------------------------------+ -|R_16F |Two component format with 32-bit floating-point channels | -+-----------------------+----------------------------------------------------------+ -|R_32F |Single component with 32-bit floating-point channels | -+-----------------------+----------------------------------------------------------+ -|RG_8 |Two component format with 8-bit fixed channels | -+-----------------------+----------------------------------------------------------+ -|RG_16 |Two component format with 16-bit fixed channels | -+-----------------------+----------------------------------------------------------+ -|RG_16F |Two component format with 16-bit floating-point channels | -+-----------------------+----------------------------------------------------------+ -|RG_32F |Two component format with 32-bit floating-point channels | -+-----------------------+----------------------------------------------------------+ -|RGB_888 |RGB format with 8-bit fixed channels | -+-----------------------+----------------------------------------------------------+ +-----------------------+-----------------------------------------------------------+ |Compression Formats | | @@ -111,9 +96,13 @@ Usage CompressonatorCLI.exe [options] SourceFile DestFile |BC3 |Four component compressed texture format with interpolated | | |alpha. Eight bits per pixel | +-----------------------+-----------------------------------------------------------+ -|BC4 |Single component compressed texture format for Microsoft | +|BC4 |Single component (red channel)compressed texture format | ++-----------------------+-----------------------------------------------------------+ +|BC4_S |Signed Channel compression using BC4 format | ++-----------------------+-----------------------------------------------------------+ +|BC5 |Two component (reg and green channels) compressed format | +-----------------------+-----------------------------------------------------------+ -|BC5 |Two component compressed texture format for Microsoft | +|BC5_S |Signed Channel compression using BC5 format | +-----------------------+-----------------------------------------------------------+ |BC6H |High-Dynamic Range compression format | +-----------------------+-----------------------------------------------------------+ @@ -229,7 +218,7 @@ Usage CompressonatorCLI.exe [options] SourceFile DestFile +-----------------------------+----------------------------------------------------------+ |-logcsv |Logs process information to a user defined csv file | +-----------------------------+----------------------------------------------------------+ -|-\f\f ,..., |File filters used for processing a list of image files | +|-\f\f ,,..., |File filters used for processing a list of image files | | |with specified extensions in a given directory folder | | |supported are any of the following combinations: | | |DDS,KTX,TGA,EXR,PNG,BMP,HDR,JPG,TIFF,PPM | @@ -260,6 +249,7 @@ Example Compression Example Compression using GPU ----------------------------- +`CompressonatorCLI.exe -fd BC1 -EncodeWith GPU image.bmp result.dds` |br| `CompressonatorCLI.exe -fd BC1 -EncodeWith OCL image.bmp result.dds` |br| `CompressonatorCLI.exe -fd BC1 -EncodeWith DXC image.bmp result.dds` |br| @@ -277,7 +267,7 @@ Compression Followed by Decompression GPU Based Decompression ------------------------ -`CompressonatorCLI.exe -DecodeWith OpenGL result.dds image.bmp` +`compressonatorCLI.exe -DecodeWith OpenGL result.dds image.bmp` Mesh Compression @@ -296,14 +286,18 @@ The following mesh compression uses default quantization bits with Google Draco - quantization bits value for normal = 10. -`CompressonatorCLI.exe -draco source.gltf dest.gltf` +`compressonatorcli.exe -draco source.gltf dest.gltf` + +`compressonatorcli.exe -draco source.obj dest.drc` Mesh Decompression ------------------ (support glTF and obj file only) -`CompressonatorCLI.exe source.gltf dest.gltf` +`compressonatorcli.exe source.gltf dest.gltf` + +`compressonatorcli.exe source.drc dest.obj` Mesh Optimization @@ -312,13 +306,13 @@ Mesh Optimization The following uses default settings that optimizes vertices with cache size = 16, overdraw with ACMR Threshold = 1.05 and vertices fetch. |br| -`CompressonatorCLI.exe -meshopt source.gltf dest.gltf` +`compressonatorcli.exe -meshopt source.gltf dest.gltf` -`CompressonatorCLI.exe -meshopt source.obj dest.obj` +`compressonatorcli.exe -meshopt source.obj dest.obj` Specifies settings: -`CompressonatorCLI.exe -meshopt -optVCacheSize 32 -optOverdrawACMRThres 1.03 -optVFetch 0 source.gltf dest.gltf` +`compressonatorcli.exe -meshopt -optVCacheSize 32 -optOverdrawACMRThres 1.03 -optVFetch 0 source.gltf dest.gltf` CLI mesh optimization include settings: @@ -360,7 +354,7 @@ Multiple processes will append results to this file with a dash line separator. In addition to the -log and -logfile two command-line options are avilable to output analysis data into comma-separated file format. use -logcsv or -logcsvfile to generate a .csv file suitable to use in any application that supports viewing these files in a table as shown in this sample: -|imageCSV| +|image432| The CLI also support processing image files from a folder, without the need to specify a file name. Using a file filter, specific files types can also be selected for compression as needed. @@ -375,11 +369,28 @@ Processes all image file with BC7 Compression into results folder Processes only images with extension bmp, png and exr. Notice that BC7 compression is been applied to HDR images, this is an automatic Adaptive Channel Format feature (ACF) that transcodes the image half float channels to byte prior to processing. + +CSV File Update to Support Automation +-------------------------------------- + +An error code field is added to log the state of a processed image when using the command-line application option “-logcsv”. + +|image433| + +The error code will be 0 for processed images, else a value is set to indicate any errors encountered while the image was processed. + +For a list of the most recent codes look for `AnalysisErrorCodeType `__ in the sdk file cmp_compressonatorlib/common.h + + + + .. |image127| image:: ../gui_tool/user_guide/media/image127.png .. |image128| image:: ../gui_tool/user_guide/media/image128.png .. |image129| image:: ../gui_tool/user_guide/media/image129.png .. |image130| image:: ../gui_tool/user_guide/media/image130.png -.. |imageCSV| image:: ../gui_tool/user_guide/media/image2020-3-17_13-39-6.png +.. |image432| image:: ../gui_tool/user_guide/media/image2020-3-17_13-39-6.png +.. |image433| image:: ../gui_tool/user_guide/media/csvfilesupport.png + .. |br| raw:: html
diff --git a/docs/source/developer_sdk/cmp_framework/index.rst b/docs/source/developer_sdk/cmp_framework/index.rst index d77bff082..47438fd94 100644 --- a/docs/source/developer_sdk/cmp_framework/index.rst +++ b/docs/source/developer_sdk/cmp_framework/index.rst @@ -1,7 +1,7 @@ CMP Framework ============== -This library depends only standard libaray. +This library depends only on standard libaray. CMP Error Codes --------------- @@ -9,37 +9,40 @@ CMP Error Codes .. code-block:: c typedef enum { - CMP_OK = 0, // Ok. - CMP_ABORTED, // The conversion was aborted. - CMP_ERR_INVALID_SOURCE_TEXTURE, // The source texture is invalid. - CMP_ERR_INVALID_DEST_TEXTURE, // The destination texture is invalid. - CMP_ERR_UNSUPPORTED_SOURCE_FORMAT, // The source format is not a supported format. - CMP_ERR_UNSUPPORTED_DEST_FORMAT, // The destination format is not a supported format. - CMP_ERR_UNSUPPORTED_GPU_ASTC_DECODE, // The gpu hardware is not supported. - CMP_ERR_UNSUPPORTED_GPU_BASIS_DECODE, // The gpu hardware is not supported. - CMP_ERR_SIZE_MISMATCH, // The source and destination texture sizes do not match. - CMP_ERR_UNABLE_TO_INIT_CODEC, // Compressonator was unable to initialize the codec needed for conversion. - CMP_ERR_UNABLE_TO_INIT_DECOMPRESSLIB, // GPU_Decode Lib was unable to initialize the codec needed for decompression . - CMP_ERR_UNABLE_TO_INIT_COMPUTELIB, // Compute Lib was unable to initialize the codec needed for compression. - CMP_ERR_CMP_DESTINATION, // Error in compressing destination texture - CMP_ERR_MEM_ALLOC_FOR_MIPSET, // Memory Error: allocating MIPSet compression level data buffer - CMP_ERR_UNKNOWN_DESTINATION_FORMAT, // The destination Codec Type is unknown! In SDK refer to GetCodecType() - CMP_ERR_FAILED_HOST_SETUP, // Failed to setup Host for processing - CMP_ERR_PLUGIN_FILE_NOT_FOUND, // The required plugin library was not found - CMP_ERR_UNABLE_TO_LOAD_FILE, // The requested file was not loaded - CMP_ERR_UNABLE_TO_CREATE_ENCODER, // Request to create an encoder failed - CMP_ERR_UNABLE_TO_LOAD_ENCODER, // Unable to load an encode library - CMP_ERR_NOSHADER_CODE_DEFINED, // No shader code is available for the requested framework - CMP_ERR_GPU_DOESNOT_SUPPORT_COMPUTE, // The GPU device selected does not support compute - CMP_ERR_GENERIC // An unknown error occurred. + CMP_OK = 0, // Ok. + CMP_ABORTED, // The conversion was aborted. + CMP_ERR_INVALID_SOURCE_TEXTURE, // The source texture is invalid. + CMP_ERR_INVALID_DEST_TEXTURE, // The destination texture is invalid. + CMP_ERR_UNSUPPORTED_SOURCE_FORMAT, // The source format is not a supported format. + CMP_ERR_UNSUPPORTED_DEST_FORMAT, // The destination format is not a supported format. + CMP_ERR_UNSUPPORTED_GPU_ASTC_DECODE, // The gpu hardware is not supported. + CMP_ERR_UNSUPPORTED_GPU_BASIS_DECODE, // The gpu hardware is not supported. + CMP_ERR_SIZE_MISMATCH, // The source and destination texture sizes do not match. + CMP_ERR_UNABLE_TO_INIT_CODEC, // Compressonator was unable to initialize the codec needed for conversion. + CMP_ERR_UNABLE_TO_INIT_DECOMPRESSLIB, // GPU_Decode Lib was unable to initialize the codec needed for decompression . + CMP_ERR_UNABLE_TO_INIT_COMPUTELIB, // Compute Lib was unable to initialize the codec needed for compression. + CMP_ERR_CMP_DESTINATION, // Error in compressing destination texture + CMP_ERR_MEM_ALLOC_FOR_MIPSET, // Memory Error: allocating MIPSet compression level data buffer + CMP_ERR_UNKNOWN_DESTINATION_FORMAT, // The destination Codec Type is unknown! In SDK refer to GetCodecType() + CMP_ERR_FAILED_HOST_SETUP, // Failed to setup Host for processing + CMP_ERR_PLUGIN_FILE_NOT_FOUND, // The required plugin library was not found + CMP_ERR_UNABLE_TO_LOAD_FILE, // The requested file was not loaded + CMP_ERR_UNABLE_TO_CREATE_ENCODER, // Request to create an encoder failed + CMP_ERR_UNABLE_TO_LOAD_ENCODER, // Unable to load an encode library + CMP_ERR_NOSHADER_CODE_DEFINED, // No shader code is available for the requested framework + CMP_ERR_GPU_DOESNOT_SUPPORT_COMPUTE, // The GPU device selected does not support compute + CMP_ERR_NOPERFSTATS, // No Performance Stats are available + CMP_ERR_GPU_DOESNOT_SUPPORT_CMP_EXT, // The GPU does not support the requested compression extension! + CMP_ERR_GAMMA_OUTOFRANGE, // Gamma value set for processing is out of range + CMP_ERR_PLUGIN_SHAREDIO_NOT_SET, // The plugin C_PluginSetSharedIO call was not set and is required for this plugin to operate + CMP_ERR_UNABLE_TO_INIT_D3DX, // Unable to initialize DirectX SDK or get a specific DX API + CMP_ERR_GENERIC // An unknown error occurred. } CMP_ERROR; Kernel Options and Extensions ----------------------------- -**Note** GPU processing will be available on next release. - .. code-block:: c typedef enum CMPComputeExtensions { @@ -68,6 +71,30 @@ Kernel Options and Extensions CMP_INT threads; // requested number of threads to use (1=single) max is 128 for HPC, 0 for Auto }; + typedef enum _CMP_ANALYSIS_MODES + { + CMP_ANALYSIS_MSEPSNR = 0x00000000 // Enable Measurement of MSE and PSNR for 2 mipset image samples + } CMP_ANALYSIS_MODES; + + typedef struct + { + unsigned long analysisMode; // Bit mapped setting to enable various forms of image anlaysis + unsigned int channelBitMap; // Bit setting for active channels to do analysis on and reserved features + // msb(....ABGR)lsb + double mse; // Mean Square Error for all active channels in a given CMP_FORMAT + double mseR; // Mean Square for Red Channel + double mseG; // Mean Square for Green + double mseB; // Mean Square for Blue + double mseA; // Mean Square for Alpha + double psnr; // Peak Signal Ratio for all active channels in a given CMP_FORMAT + double psnrR; // Peak Signal Ratio for Red Chennel + double psnrG; // Peak Signal Ratio for Green + double psnrB; // Peak Signal Ratio for Blue + double psnrA; // Peak Signal Ratio for Alpha + } CMP_AnalysisData; + + + Encoder Settings ---------------- @@ -89,14 +116,25 @@ Mip Map Interfaces .. code-block:: c - // MIP MAP Interfaces - CMP_INT CMP_MaxFacesOrSlices(const CMP_MipSet* pMipSet, CMP_INT nMipLevel); - CMP_INT CMP_API CMP_CalcMinMipSize(CMP_INT nHeight, CMP_INT nWidth, CMP_INT MipsLevel); + // MIP MAP Interfaces + CMP_INT CMP_MaxFacesOrSlices(const CMP_MipSet* pMipSet, CMP_INT nMipLevel); + CMP_INT CMP_API CMP_CalcMinMipSize(CMP_INT nHeight, CMP_INT nWidth, CMP_INT MipsLevel); + + CMP_VOID CMP_API CMP_FreeMipSet(CMP_MipSet *MipSetIn); + CMP_VOID CMP_API CMP_GetMipLevel(CMP_MipLevel *data, const CMP_MipSet* pMipSet, CMP_INT nMipLevel, CMP_INT nFaceOrSlice); + CMP_INT CMP_API CMP_CalcMaxMipLevel(CMP_INT nHeight, CMP_INT nWidth, CMP_BOOL bForGPU); + CMP_INT CMP_API CMP_CalcMinMipSize(CMP_INT nHeight, CMP_INT nWidth, CMP_INT MipsLevel); + + CMP_INT CMP_API CMP_GenerateMIPLevels(CMP_MipSet *pMipSet, CMP_INT nMinSize); + CMP_INT CMP_API CMP_GenerateMIPLevelsEx(CMP_MipSet* pMipSet, CMP_CFilterParams* pCFilterParams); + + CMP_ERROR CMP_API CMP_CreateCompressMipSet(CMP_MipSet* pMipSetCMP, CMP_MipSet* pMipSetSRC); + + // MIP Map Quality + CMP_UINT CMP_API CMP_getFormat_nChannels(CMP_FORMAT format); + CMP_ERROR CMP_API CMP_MipSetAnlaysis(CMP_MipSet* src1, CMP_MipSet* src2, CMP_INT nMipLevel, CMP_INT nFaceOrSlice, CMP_AnalysisData* pAnalysisData); + - CMP_VOID CMP_API CMP_FreeMipSet(CMP_MipSet *MipSetIn); - CMP_VOID CMP_API CMP_GetMipLevel(CMP_MipLevel *data, const CMP_MipSet* pMipSet, CMP_INT nMipLevel, CMP_INT nFaceOrSlice); - CMP_INT CMP_API CMP_GenerateMIPLevels(CMP_MipSet *pMipSet, CMP_INT nMinSize); - CMP_ERROR CMP_API CMP_CreateCompressMipSet(CMP_MipSet* pMipSetCMP, CMP_MipSet* pMipSetSRC); User Processing Callback @@ -104,12 +142,12 @@ User Processing Callback .. code-block:: c - // CMP_MIPFeedback_Proc - // Feedback function for conversion. - // \param[in] fProgress The percentage progress of the texture compression. - // \param[in] mipProgress The current MIP level been processed, value of fProgress = mipProgress - // \return non-NULL(true) value to abort conversion - typedef bool(CMP_API* CMP_MIPFeedback_Proc)(CMP_MIPPROGRESSPARAM mipProgress); + // CMP_MIPFeedback_Proc + // Feedback function for conversion. + // \param[in] fProgress The percentage progress of the texture compression. + // \param[in] mipProgress The current MIP level been processed, value of fProgress = mipProgress + // \return non-NULL(true) value to abort conversion + typedef bool(CMP_API* CMP_MIPFeedback_Proc)(CMP_MIPPROGRESSPARAM mipProgress); Texture Load and Save diff --git a/docs/source/gui_tool/getting_started/media/compressonator_window.jpg b/docs/source/gui_tool/getting_started/media/compressonator_window.jpg index bb4155d1b0513d1e53aa701654b596520675b612..226e2b11dee607126e69899abacb7250f6fdf6ad 100644 GIT binary patch literal 91812 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 literal 88765 zcmeFZcUV*F)-M_a1VuzdqzPG41gR=jK!SxP0-^#^qZDaEMCmP75D*X$5D+3=YNR7A zQllawH3C8)L8*a+5+Uh-&oU- z0~ZZV3?Xc6Y>*q^4}>)Xku(VQyaj=nnL*?s5C}K;*+B?9_>K+y`Hzb=0XYxZ#m4sg z@Bh5mIoW@|c5`sBb8_wG;`;r?y@zKHH}_s{F0Q@2d-w9}123*U`}uhH?f?D#?+^L? z-+%uv@OK|K7x(XX{J*q~!v24;^`D&m!WTct*Dg>8 z99+Ni#kMO5e6jO$a2`Fm``}qiuA6=WlBXVUA3B$mTh+ctO7#j^(E0Ab-ow&rGcuIl zS^E#p{yoMX{(s`^KNI#v$(?|N;|E% zBv{3$skym{T=#5f@)Gq#%}1$PgN(5lnHcG0lggm(92g7oFaqG|)=qm;xdg9ji>sJ# zxckjZG=eb~MXD@Umb{zXsc3%QNK(7H`UI!(Z}$ztqL&3&kYgvwaAubo9}8kewpqi_ z?C5epIKG~b)JwTa*dL7ss{7xwAflDEV!V82qe3S3F`H*>?4S3m{S(UT=*KLG3t|k9Z96;^Fv&Ax(H`M_VYEULGqlOWw{Ia`qk+2iI=CqI+OzVcrc>2T z$-)QD{)VGi5G;5A_pVH?r!80W7RQ64m2M2TWb1UKH)skqp_6}H6wLM+6cM!f{C(Nw zbll3r1=P|K)dgrRVfJIlh}BM%gvZ-&-JgAKXC5M(JBj|_NyCkH)w(T36TnOEtMT8yzHiCt)O)iS% zO_zG}@QZprm5#P9uk6SBc|Nv?g(W?=Pu+m;Lbji09t<}c&8-@w8$)oT9sD+F;rralzUBUJ?;1jmPAW=${ycyf`zbu4Y-MdW^=UA4@>oS>$+|*N zTQqFs&Qzb3|DZ#uP4$4L4BE$;Nwi=JD@?-~{76s>fbjwgLib7pZlE6^Hw>7dj%K8a zSrAGo5$S;j!$0)ih4R7>*)G%dt;9YR;@r~~fdKazC85sH9U!qIQVdg7|0JL+qkQ^p z&WG>#OUj&G^5@GR>jrUmOYOZrcVtMI1^Kbj6j6*;AFpq{;^QBo?lEW^!I47swjTYo z(A}-GFz`u!Vscw2jE{c8<~?Rdk_Fkrf{>GjK!!%3j~?d?)Tb?d zzkKNI6)=2(^mIYaEjKZWo9aesW$4x*{puY)FYn?p$E)7O*RmX!#|L!$QGX`&8nTl! zoQDO;Qz3(9svPIK6B9RjAiu4Id9+YC{oheZ|M_U@+Hz z(_8nliK0TATwy@+<&RbNp}w%2uyF#GI?G6Fk)@o%o*Qk^Xp^pvCHUxEscNqGwzX3a zyq@!uoA>kiJ3M}(Ay;ms%5v>Lou5=ocb>5OzH2Mh99 zuP~YgLHW^=WDiD4)-AY$B@$&^v=cW$n7oYI|E?3&K=4`U`qOyAL6~ zTwrs{7_uz$=B6TyPD}ir2)e|dLe*dE4pw&(wN8ZtU_>~vlLlo*U{{P%Sdc$kr{$mN zajG(a{;m4IRsXlw|LygE2mHSS{=bnf|Gn1#z1IJCrFja80FHJJ;G%kyE83bVVps`2 z^6r?R!1@DT64IkSwPm)IBI2WhX+ak}_ZmrYa7i0tg|9yP%hV98bpO5F)EWj>U(-eS z8bo5aRnfei5Q&Qnvm4zkYb;k|=yhW49Q#HXU6lvroMU8Fq!X4)-@HzfhU`&a`R~l? z;Ie>42l61y(_-9+xO{>h0pw2e3cS}-uO?3=<^za}06Ae*nN(PfTEQoPaV~8m6 za-(#{9k$DdIA{MNnXUSB^7vQD>>U=QhJ&d^<|E}&EN1#+BISZR34t+rfI>%(^pHD6 zCMw=1;HDokf}j)j!g}cT?SXvPAtmF3QmIGZzCS6r@#Mcf9kuDnkz+K|-Dr})k_jM1 zj8g|0$iwY81y<*(6A$D!$JH%z?6jDLO-pjrGy5fX;_ zi=_XrYF`{(klY_bhmx_TZz;tw={*w1<6YCQjJOODho*CEr9<|f=Lk3AINN2(<(|?T zulrFZB|LF>;4iaR*b(G35qdv|6FAC(>V0Ubw8z;83V29-c?pl%yE#)N0g z=B-Y&Qku1N=35@4taqa7=SY5eUeIb-4=NVL(sBcBnF3eh*<=~VvZL*m((QpUg?tRN$j_jW`j%17Gr2R!>ofbC8M)Jwh%P8c zZ9n%S2uN5U>pX%E&Rg1jlBvHZ7V^AVWBz6CCqWqti&8nXp5+yDm0N*BWpZZMq827Y z^MB&wz))h|42!|nx_7{y2DT}-4afjNl1QJ0q_G&EaK|GWLiK@dw;Y6J9?{M%q!~=6 zW(=ghjfbX$?*}0_B|AC?ph>Xc1dvw1`8>5u|HEyf6yF$fskllv)kY&d$?=<`R}f|D z<)%@r@lp3(Jjl~S_XMRMZ8-{>wZg+17FUCo${KvrRO4T`NF-UBRwgx@_D3MvzRZ2l zM$r$#3_$cN3_EU~MJOrvUlk5N-9$d~>dHb}$l%J#veL4Ox{aRK_L?bge3Z*J_lZy8 zopa)?TTe05*^wM?8834I7cvWCKu+%zSD_&I_O#`Q>28%`VVq-SO{40WN)7Aw13j6%L z&(uvPD3}Pge4Bl0qISj5^Z+K$|C)hxdUEwjYI5CHU9!pTRNPgQYbn<2&Fh|On2i$0 zmC5W4y$AqCI~v?P4ee%}zeYwq(-kNJWV?HMwz?%mL8+dyY}&z_M_Zioyo!Az`OUso zqyD~qFYh-CnR-k=)|Mu~?6<=%=h8F)#LSlpf|+Lbjsk?Br%Ej*dRYXEsIBplktbbt8d-&>ls=12W~$xJ zbiE6?$$9CS<_QRH@X%$hXyzAKO=4mTCuIu@=Wmk=UTSBa7^UBwVeoa{IXi>lb^t<0 zJMfp@jZ_Mbxep`2a2a8zZ|uMKDEvjTS=f_9m)Q)!7aRt-GxsdaM0n54#a2YhIKsLR zqT%`;vJ}}^>6lttzEX3B;*j|sDZMPt7ts-==Z^=we(teVmav`O*%b@<(Y6;j-{k}W zTngvg-<*d3)h6fBFk>)H& z`&an-c@|{l3=7gYF2{mAdX4!B0%hOBz+hWzvuPB?f~=X(uplHBgmGGmJavKvNoqyV zf>;ofm>oeHh^&2l77k+9RRZ8shQbBY3z%amKp8cKV$oT_HDl8i9}4AI)EKvZM~L&R zHTL)3GgtL+`>gGz@Tvp1uQLbM3$J?M6q|a-`+14&JBWJ57WYLkA$D4GQQo;>hsYs8)_o^~havhGna+K#qZ%zu z%0Y|6$)8zHzcf@b#@9*0l6Z+}9oA%p6I|jfjcEIdL?SS7UTf6XIbVJ3+Cl zw>OV^5Wa|wtxP9O7Bu9zdJZfXPVr~z9#W8+B@UFWf$_RsOHa#$cFb%hD`NlB#SzTR zB|xBKD=lrjK4Z=+Wg)X~bF`u$-)znfrGocw%m==#m&ZTGZwI6^6=A4=_-It z!0Y+Y&8hSvx<%r}=HYOu%IkD9F!DYM=-<TD2skO!EM}p`PUE8C;)P>Sw;d)Ip*q0~>1UP~QW!AL19DPjY0+{fu$rQS&+S zl>3W_@u#;d+#hspPyipbmdw}9K~|wl?GU|uC@SaPQBtl%vInrsQpxzKqFT*ui!>@>r2E zF=(IV7B$}+!;`hRT3lCLK+|>h^P#<^v-37)}|(>AQcd`O?C9|Wxfn$;wwAT z*_lnhcy4`sxrOuG9tY{yA_iu(VEVpMT}_JKk`pDp0|ol70GRNqbZ(F_T_NgWyT;8* z@$;1aJV~{VO)=5|r!O}i9h?~!S7vp#!r~H${@K!KU=KKNItMSb*L#So6 z@L*Zfbi%MCuFFOf&b7K+J88~c;WAm;ADko>P^I*nZG+j-`t4ZfU^j~yjzLhg=|Dq= zlL#ZB#fmSs<=!+VdT23;yG5r2K-t$1EN9t27VAAE;Gk^?|5DG_Ap0Zc3%51;e)w_P z+3;g1ES^2U(TEtmJmH2Q^rlnzdbGf%gtx~*y;P)6i5Y=cn?vCQ%??}QXtsOy)8osN zdR}r%SJT2Y%b3?l?!NUuvPq+s=Hm_}1xNB*YaK~pO9Rc=w8j23$3TWFISms;IHTdY zs&?jT=bAylnWhQ+$J=X{l0JM$DS2pnwKw&e@q?FIHkaoU-|e=*!sB)j%QUh@kA&{% z>$au=x?tXvcf;2)$=1(r?cT2&S1DAN+D75*bm>7fUZ5Aw$gj&<>IduK38vym-72qF1Qd?hf0Oz7N@ULQ+(Fm+Vd1mx`_i^#ExW3JOLk5QZl+2DKVC11cF#Q~MfR%P z58$=v)PtN{Oj~TwKGwK(cgnH4=7Ff~$ADs=n76YYM!i>Lo{7t=gbwdajV}y-Y@-jF zG~3D?FP0JV)z+kXSxMB#g?a~Og@#CPw61#8W*N=L3#Lq~aDJm}k~QLHCY4q9XT1J) zw%E?lJn;NJZ_#};`Pk7sFL^VANq=q)!mCu2OAHV|nKJ^iC|m?+)Ur~Y9HLEhv3|#} zSfya$yp(3ZuaGDm;Qd-2ZYjMz1X z;Eb&7n&+|Fn!wiawhwuo%hslPAD=v@!!BmRb4WT${;YiQZ-J@Xa0-liyOq33I!-3)Q?}a0(9blQ zib^O5IDB_!MB#^~)a=!#Y7=ofF;Xo}QJ-A*sGZur`bkt{d`5TvIp0^6Z_P7_Pa}CE zKce>2WeLqsnkdld4IWc*{lcM1#VZ9jgws6tgjnlI9n@4laB_Blv+tt1PT>~qBXta$ z=jqf}l=^Ala+w^~*K=aPkpX}e`O;kfn1VTTfDzrcCvkZq+w>#3Kl)Zzkls0aQqAM-{tr_E zZ_MxdVf~ildTrK~J(=4!^egl+njsHk3I*8ABEYV^6RZWfiUl3Oh%Q5 zWKU@z*gR&c+b`(tX&dnIZxu4D8Lktv&(?Kt@IrbeQ%?wv8(8r(lI-0-xK4HUUD`)! zQw7(8*rhZlQItXBK*e%E+2dHb%`B-?GOzlTCmRk|U-&s{Xt}0fCg129kd*qMEI*YX zzjjw8@kZLI{m5HC8C)9lBe8{Er-%L{kBvI02T-2XNX_$XWdN zoZs_I-#Gg;KT-~CzVTdNOH1u?zI>{SIaDAJeg<~+Vx7z0yRYg^@B?0<;C#yeZ6p53 z|FRr-CbFi#3lWE}O>f8W0fUIRDa8T%F6Tvy8QT~xgN(j~|x+%xzH1RN3 z#=UoU1L{_NNOe1}>QwjM>CkntNHWU$GTJs3J&*ru!cHUGPwOdoqr_m&t873@S9(0M z!b|jSR#_BO?Q=mTam6n=!uwn8J3CX7iET!j&i&FowER!crX!_MJuTx&ekpPDf!&i1 z|I|LVm&^fwj0ncF|EYFl86i>3GfjIxD!(ngk z^0pf}pBhYYf4TubLcd~WV_fA)%7hC!89(S``?NY&VccJc{3o`U_z+*4lSn`xHks~S zJrwNXK#?gGQCjscn|FI;`*>G)*R@;xpEN$$9r$8(;Ix+S8Rz?&|J1HkCeYOCA*6HS z7!36NgJ@WF<%+I&USEm5C$1^>TV=i$$9c$I;YW&DmUjx$v>-VOYr@F(*&*myMHW+V zFJD|vP#WKO40*I)RnHwyGh9_kRH1!e#rEn$+wkP{#8oJb!h(zqfO9j!Oz<5B%E%69 z(72d5J2d}a~U{Mr8HnqHH8VoW{$LhK_%Q9 z6h6$Ic9~{0MrT1(NLpj@6Ol*-+zt8TFww6zP!o5Dy7WZM?wJ8WlvA{G z8JC_f9(QpUZ=pmWh3l#I}+6m#*%R7DLEZ?P_zU`WCvp3DH{D>${V@9r8uB z_LVyqI=5_?4f97^g#MR};^KpY2sR12ZoHeL=`--X8Yo05eBo;=E~fHmrlog~dR3r7 z)t%;N2K7<5*`H;;Me&E*=K-CanUSqCnEm6nKmavu7pB>nx_AHv2TRQ0!9{&$c|IEa^V<^p)y`EYi|iw#F_swcSu z8`l!zN9g5Im@rm*RBCcKbK!Kdg*0+ch7U1_$&ost5WmPq#r&mfUq5tt_dm%z{?Gh~ z>nEIkq#um>USR$X4gwY-Cot^|XlfA6XfU=PL==-*ke| zBHO7ap2h!tdk>=ii@+RAD%d>t^hc~XwwnDB{PVm2?C1%r{6|MGX-$O`L)nji533Dq zl3_s{gJvoY8HUN(XS>L53KE+NU!V>|^3WY{voQ&2IRfcsF?rcDS)@C&qpmy* zK61cFWfL6UB!P&2uUce~%En6sL7HW~E_*fsEUaFH3!*{%VreFOH~uQc=9!ApbbYhwt+ktV zv4Xo`dN$if;|-0idy{{p@~DdL^vl{*`g<2N5{l(*`TG1M zUxlyH-(9v6C=5I3FBi7ix=o#LwLnFW`oWqzRopGMn=lsZ+A3rA9ia&rA-ZhS&-tGt zicDO$j-UM4*nR#YS0j0C$qRg+MH^t=Sr!d+uHhR!;Y*B@CzrC%eX}XF$zcvR zJrh-FH)Wh7`SPY^g%vwZ3jY=|Yb=RrNH*lYU*luo@`Quppk=qZbf-+^rv=5R%4UCl zRH`si0ser(wcJm0#7Fi+x#?o$RTHxDsB${Vv&iCJNiA$#2Q3?PvS%&td9`Y#5mYG% z1#Ll4?h$M{BHS$~LJ#Q*uY-}64P8H!uh@TO-z^qs^4sF>yPeN)PXOg!3*Z3}zAL!K zXpkQErf;S!B#-&!QAY#!ngH^i_D_?Gzj5Aj%0TBc5H6l)gme0B`}T9llLtI$LvHB1CTkJu*RY_OAYin`h-sGks0V^ory^VdL$V);-|0_^#EO z?{%+m)@`EqFTN@N*k<}dYajPK_6<60BvP;*?L68bIyw9NL$hmwseQ;{%a!uSHa)Of zOm`L^SiM8rj)v@1rW7v3^Y&oRx9k z1as78V-{pKQ|3#FEJscy4PG2!0po`!R}_k^=IB*}qa@jrJ?#jY|-}}*a zX?baOk42M)UZc(qrwL_~CI{HltT>X=ZfH~BMI+ba!i;(wHYd)b_s-6Df7+_XBQxk! zuwjM8*Hp$3zqFo0-8^ogdz6by8C+^Rh8F^PVv=NjLChkP&+oL}^WtmZwtj>Syv<;_}PeByl zHmx9X5<|ZF21J?rO6@(`q!H1Fy?@70_8cEjn&{$xBe~B zel>cEnT0|b5%!>o&7ylzz)?;5foO2YQcdyWHsQ#xIr2@E?ztZE^Tz3yn}+Iqo?lNE<_)SLvgKM%?o##A&sxth|5t=+{2y=18m6$F&UVP<6CL`H7WcSb4fz}T0_ zzI-p!yJ;Wayh|rZm)J;{B>KXlgL_m_Rk|t!Z@BRMokb0hrGq-SZL0cEIb{3AHZi&?&`od@ zFaQ!MvBdY(`m~2@_zQ3)mqzC`mGJ}{Mf1J;Uhzgl4&HkKb60tUB(ETV3qph-y8Y+~ z^YrWqoh9#taVdC`oaIooO)DhH7f3#Zg28^4o)AZbnsW^Q5x*4_gaYW9f zy|*Cj_-_#_!qc3SAkNedYXcQ4U>kXWk%na+JW4>R_R$=W?ljr)d(>AeUnWs{{g?!e zxmXAogQhbv``X09MJUSe^61(?w;!?0zW02HUmjEu>)TF**X`R^BEo&=PSCT5`dC~Y z8NM7p&A7+h6HWOD_HPr75*^5iU8V)1MsaFOhgBocH=yo~R^Qu=`tC=rDxTu;p$*>G zGm6l|TZ0aA1-Lc3$n9IJj(D{K4F6^;ujW+Cbo zQ+0|o>rX4Q@}*PuYd`KfOp6yw4Co&*T4t~ylH%ahz_TWBk4cV-!ZVb`v#HL>OEm{gq}NQ)Diri`Vo9(y;*b}T*XF$*bT}Y}*jU4Y z#Cm^hsp>jRHaTSDXP#rH-9hRK`Hb#A{hIGKSu?Jp?YCfABV0K#N9T-hj~H;!jYrZ* z(OWVK@?3bgNW0XTJ};XDW@nBV1^a-x2iXB5Rwz53TJOZId5+tgyupU!n!-JWqkINZ z-X3XseEZa65q8Pe*#9L5VPMLjfzM*L^PyWuz~PR7L&)}z$aO>5%4rbiUsTx1??i6P z-eEz&C4%`X2|x%VKTI_+Kp3E)YllcR2D0-UBC#-TMRdiK#WJ49Bl(SJ-7BfS*e~J* zCGV`YbV+`=Hy_YeEXb49LS#RbUh0Xbr@&R1{V3mWcKzxlXl z-^D@Wso2=h77zg&!3O1s2!lJ>Mg}Yh5789126~iyCO9%6J>m!_az$@4lnG$5VbRc5 z-TbWkOlvnYOZw_jVfjxujuTQ3`F1z19t_tX)IEy!5OvBAqK2&=ayk>0A@pLEnvuN6 zSLD>|Cta66jX87mKAXKCjEsfW%RY?cr=LsjXnGN;H5{-MCKs@WM2AiL-0R};;pmsY zFlchq^OR9*?hDRa`vL`?S5$?C<>nA>BkNDpXE){+d_oXDVK>b=y_u0nd+#M$Yr%-G(PjIJ1|9teeh2nL=%; zEQl_0t)v*aW+sg~vJsQ-78c-k^WpyD;sZuZ4jjw?f;dlUHmrB78ZYqDVhd2xLX!Gm z|M-vhhA(rS|Ao#>)REfYu?(LPyYug!4sWc!oZg4pKsltK)(4v$Yx(^nAGiRc*OfX<;WDZj3zB~u-~)qfaTF3L z-3*mu&jysptJCSrlci4ha2>#ky!u|V>4hohBsN-&!DE`XsIK$S`0K?d!xH+ZA-G(y zirX5*%q23d)K-}$m7%eadE`vk0(|{Hjbq3Nt8_#e%(}j>V5vFTxYPag@SXMF3I3J4 zELp6QgxffEvG@5m?VML3T%eU=`<>Z-2YJCS`lAgS(N!W8m|&mj$5n8IYc_N4&FlTq z|0q8wxpf1*4dy1PW;!fLvUg72eKo~M6;IE@bw>u6tJj)6qtna2NpX*la~pqGK5PBi z=j(>ltBty%@LOLud^D!Mj8F77)WzJ4fz|m_$73Pv$L3@q`|ILB-5F7^MvtpZ1FlN> z-uAM)^F7oRj1!yFk=MYq(rL$r4npUjivGj3`YrXv2E+XZe#$#dUhkPm*G_vCn3Dlr zI1>WP{tJCy(9>S1^b;>whq*>EJ8Ten745QQ!4d>9i`IDXddpG5N2#Rq`4ufP*Coz! z_04$80ia0)v*goiw+@VB+qoEbz`s+yGQh>^iK)53da{nv1DR~?= zb8;9nZ$=4XvV+pcdRGI{6odufNr3NONFQx_(MR;TS!-%SBG^QY`nlf=+_!xFkfFVe z{(BAAU>tj(Q4U?1(L#q(?qWNGM6I(39z=>}%qEm)f>AUm7vF3<;ozvgjA`BWk%{6A z{_ghD(LOouU40h*qvo2m8U0=QRJ8P9^Ab3)0Atj{@2UMljt6Ln*efhZu4XqL;04W6 zyquv*-2lQUd|gxQbwCgyaX05EyIN=Aa2Thj2A*qtHJ1jiNDJ8E2V){LPpFKKUFuA z?Lr-H69LDCzmnot^4&^;^Lj>!7WYGJ(>!KhsQD#oLo@C}TN6<)-7nIpD8RFjZaJXK zcaI|QzMiR0Q7^@NNddZMHMj-ZR*;_M+^$MZI45%GA%8-!mL<*?Lq>jN^uP7T zY!1BqFw8GG8mrR}{35+FgzFBjn(#{h8EVgvn!Sl1sO!NAnw$7qCGt+`I& zXMh;=f&}kZlCB8>b#Q^Fus(gJLXTjYO1nVm3lp5pQ?F@E8XX-E-641THt5_IBUD!BkO&FR-jws4DSv#@S$M9=DDyXdHJc`~-bi#m03sOas$ z_^q+M-@nud&zw3TpZnpv;cl}~n@76d`E(<4P8K>i$a+Yf#B;sDw3*k)yvel8bl$<& z#5wJh<-h}uQ_0-AqJcNtd34&gf9oUQl*v`mx2j@gE^+Kh3wt-Kg0=SP&l{+WC&DTgaN7kh`!;L0HRY z>krZYem%9afqp#o8!!US8V>;`xxlTZ%R`%HQ-|AV0daP5*?MAOOC~=XpE*i(^dIqS ze&TUQXpE-%OO+*?Pj-it`* zaD{HH0@pymj&oWtG_nq6jysoK{_ztX-(^iRsDsG_mTf+z`}^L427=924klno1%!TmfNO zS4Yi``nJ84`)MD$jB9e3n+^$w@(Vp!* zN9`GHvC-^=$Ad%B0erj*hc#>>*?>pTC_-#lSlm{IvNLmhXxvx!afxfHzA~H0jYyEX zCud)S=ZT1eiRkWhIQ?$oKZ;-O#YDO$m_&16EZ{n}2U62jQri|zM8E7#wra0?!4mnJ89+Ji;)?} zFDnpHLcy3TI6Wb{YRa^Ee973ESE7myI{}ZAyv`fol)h9GFE!@KJ*$R)Yya6rx_gOp z5)v=WEI@X={)(JS0IlhfDVllOh%V2|#SC7A0%{<>Lrm2UgWanC@nhQ?Z`7I`6l)@# zd~rO?C~ODwkzPMK>Pu>FP++>#)fxA1Q4+f=c6%p27=`VRbz+1qygJp?C9><47w@#-jX`t_DZa(`E!X*W|cv1?#q z>bJLzW#F#8yNcy2%G6Hup@h>nVs5043%fNu%@46O?A>|yFHFTc`OWuj{H=WHh3%>cES};7Iakbjx!Wuxw=hYMQ06|AVx7Wc4!y$k1W1m%x++B8c?rpA0 zHtKjL6e5!Sa<)eGq;#_RW+40@Ez$DVufI?*(R}QrrNbMdM`bTlaQ7pyW)9$^i#j4H zpI|eWJz(qD%OHYTh+{#H6j=5QIHv!jz+e2L5B(!Pf@8n@?H3)rVEaGyF%tI(7QoIR zW(mZ3=VQDVEZC7BQu0|4+%wpYk~g-+p4@9R;D1_0X{L#0P+}cZ*p_qSadrVINNS}@ zTnDF!sq2r?Rm(Sjh&}J&Poa8@8@nl5>0LILX4ewGUpUNX#HiEb0aA#??-fW2?Lp(# zsl*6vCf1I;xl(Z5&VFj#(Lg(-kpH{kVxrigD-+VUZ>;Hx^2nZx%k9$r#>F5>ez`E|0U$Lyu@0$(F*Yw`2Pxj6Vulk7}< z6!2dtest7S-j0;<@C!GQ3S!N6Nu>9W5c|O378uzCFB*RSt828~kQ$7A$!4JMX33HX zC9E5Ipi-p*b#Bxjs8_B+h}oB4T@33w`N1mLy>GVk$L`XJTBz0aiJ86`zr4+veOeNWhlV137&8OhX-BushZV7~lKZv>i>if}%=1=8D(MoGJQe*+< zo|8nrN6-T;%Hut$uL0uOSPLF6n{^|~y>316XhY1PKS}TN%ME)ExcP{n^iW=oY$Hua zV@9Cz+ekk+n)WZL_aV)ZA{xfS!5XSTV~tSc0wSS4ijJwkjY0T^-`bwin92J6IhOs|J4rhP1$P;Uu3 zOlcVVT;ouF1*-Zv=@D&?t^w|VoQHx0&?uT9_K?pMv*?Cx@R7l45Xb__!s+IbJgCWQ zTMfujnoeeq)0XNKB{Fic+6h&WIRaIjM?&V@q$>bUO0L2yEqm z=OiK@`q0l(bta6vZO5e;#5(8ESYwuG*H2Vu)`yT^Wx4z-C2#(na~_x&)`=h#O4OX^1lK@*QC z%G#{9SgoTs3CnpNVt%ULu&Lg5EL++6z9T^3qsOixaoreZV#2#|kZXa+(Kb1XM)aAu zvjw)>-xPEYz%qCgc3V5H^gh>zol08vP6QY6ZX$d~Or4Lus~TJd1v7FtIR)3i!5?K6 z5RLsh1|}kc=`4tFK2u029yxy-Os@9!vLMeca}6J|0pE}tTfAA2^S8iN^}aQn?wklX zIH`k0at#>!-z%svPwGQ2Tf$X9n2|t1Gk7zjnQXR5N-S)~FBr#+>H~8JNI3@RhD4k- z2nUO6p12;&hK3gu;B*7e6_opxyhCh%Q}?&*{)Y=P0GBH`6YJs+S&mVSdzd63Ahrs`?vigzrETE+3siVHwDiLISFE%{S2(76CCUY zHc=Al2>pOHY(liIr-X_m!DaiRgz4psxIo?7OLOd%p?(Q>|HZuFPBe`V`7xCaHe{+~ z4Ox&uZf_7reUez>B_TQL&w#c&$>DJ4noJeS%1mnKbJsJc#Ntb3o8rGn<>+QNe*8@! z_9yb@>h>wjV<=U69=~&zH&keOlLe7d>FTG6N`m|5^)ld}x6^4q5EPJsi!hIN7NmIy zEE(v}z)^QyEAVJoOb-eW%SWz7K*?#rWy~04JdCUf(lWb4l~;(Gvs_n6D{bx1DI{S7AW-taCRET%^W~+k~W&-Rc6E!avATaqVVy-znYoBPQ|ZmkZ_jt2Q-}z0mqwsgxA!7IH#EMX)(S+*3z)ZUDhw+v@W9 z;3v%3k6F)KYqLFZJ&_%@1oVy!Qw>~VC&j=%$2>@WO*eeQlc5Hoj=#muJzpvu_&5ljmZ5bqa64yaIynY+PlJ}9Sd10i-S zaz>G2!#D_@?u(rEjR#xL8-F5Y>D!yU>EP90`Hf0OlkA%gE3ZdH}7g>+YSqICY4^7p=Tn zUa3OLVcH*;%Kk0*pgwPFSHTHQe#SH#$9i{|UF_O%aPUy#a8o;1VQSyWtz)#4+JU#f`*!5&ZePICSaP>@(Z`PhCcwd8zm#==jqvd90s`TuX>Fo zav%kPSp2yfT!7=noyEnqu}q>_tLAY5-J^L8Iiqd@v+vfWPHrN(Bh@sc>86O8$uPnT zGo9bv*}9mgp*SXLd{=E1KuSL-qzL?Mdf<>!5M_c*0bW2%p12K ztmg^1#4!da_NX)DesHUhH)d-`g{f)u3lFFLt?e}!SFKG4{Fmk;F5Y$M+ldt0 z_MT5q&9Yc4V+yPu2nW;bhwkC7jTEkUT`tN|;^3yex0z`tQ?ed+G#Oe|7NTjRF{*J# zeP5 ztg4`E>mMFhey%8c>(%ET9@l-Q2n!q^;DX-=!hTe+VTQ~DO9p>rrv!NV3$hMRV+eth zUU_~#Q`)!NmLemO87Ie%d;7X?k+$rMXg;|>#T({iH3*_QTj7S}){)Ws zRHDgo3j394%V8x4#fJJD4xFa;4p?u_nv_7emqJg>BCKYMBtsFvV(P7a##I38Go<(e z?Js4;--9KVy3z)gEXUgCcx*`<{mUO#wwj~`M4Xr&PRCvS4fe%B7lgW@fJ}cU?Adr- zJnktvJS;%`>0ZSf6!46;tiaq((z}``gHsD8!V0>+B9T}2zOwNF4`mCRL2WZ}$kh!v z-M3U{U7bGeKeDhDxG>mz&Na)Stk&s#n`|o_74xJnRSC|TEdON zI{P1F1)e;-)d^c?fPF^%JO-FN@{I4_d0c`aSKqMBAdHM{69ErbCIvCUc>%Cq0@s0G z3`&aq=c2*_^k3;LKk31GfFQWZ>;Kt@20pysZi?M8`h{xnWVK)Dn7t7`X%Hdxixd+7 zM8@3{dcJvWNq-{y7dlQ0|1CY=zoqxL?fp60{-5T#SbQz#B&oRF0!-Z90Ws!bx7nTW z`T@YUxUM15VcjUr=SNu4b?d|X!aHb~K`_a-4BFRF<>{ ztUY>Z;Qog1AHgkhz*4WSEm*@||EdUr*L;w9>n0?YyG5)dSfQ(7rzi*P4u)TEu(2V- zgCi7+5{?bF4G&-i`*xbU_Q|_Hy?DO0&HveUD8F|2=m`2e;u26ijTqm$)-+T*!Gc7$ zX${wk@x)&gR)_DKGNF2$H>~`G<1Rhb^V_*sIa=?DKmP}NZypcz+y9ShkxG)R5mON% zM3%BmC3})R#FTv(5i-1skUfMbrYzY_Wtps#y$D$+W0^seeTEvtES<~ebANy5zVCB> zkMHt0kH5f0*m2iv+-Z00gz zc7MZrc;z0CzWI2d>(QCS@BX) z>a4YIwl=rz#3`dg5AO~b#Gg{ONWXbRQ?CiPuW6^Bpfbm6b@(X}avAaEY*3VUuX}KJ ze3%&`4*Gsicd|WHaeM$16kgi3R2a=g3-hB{&4oCCXG17js_dvYv{=ubUq_u>=EG2Ipyo|K>nAPo0)|X!_ ztfG2CtY>TeZY^$Zx#Kr&>F7T!hlAX`L+LB7n7+=@*EGxEa6Eo{6=#f7l9AJkml+oRM$oEnBLRj8j9fV`5b6#2L3Q z)_x5U=No)$J>}Y=_ZxOltC4fwQp(S5{)D4@hgSsKC$2>(abZ&Avhy^^4+~WFBU4q0 zuE9eTtrxcO9m)@MpH%$Hk?6>%wZtiJxJNJE)a-tJqw7Q#%ic4eoPDUGHKFz6b4!J5 zbK_)jjf>6U+2Lx0A_sN|@3PT~U5I4dZTVscBjZw>TW; z4`C5uDN#R9GpI>!|HE?3eG|uTGYrSySGE@(T7DhVhw8;_2uRCcvVzIGA7@cp-&HdM z8*{ud8pz3iR`U#|W{~cY@pSPBkfUeG9=*VIqug|>#ry+DkIuKrkDq$Yaae~O6)Q4d zeo1A%$Dk498S3a8@+Q8)HZYotT}#nhVr^hJuv_8je$Bi&>}R=76}p`3QrO53%f_{B zwQx0hW4{gw-mxOx)x5tE=bf>LCm5v$`<|%Nwytn`jFK0S=Ka*^U#_!%{sTG&;-UMI zRX%1kpR2);emBPy8=s6flGpQnUBmkHnyW&H%cZuEoszXYog_f-*%b`#p_q4;e0fw4(-l1&a= zrHAWuR9)T1{pXu6QIq?m(TrhKHdDfG7X2iMKS-5_2K>02gHZ|(%DpqX1Y)|T_!*`5h*DkXitBr zSy+>uj=j?qa}X`&AmMP}UaNw%|G6gVvKQ02`Q=E?52TE;N_N^(g|d;Vk=b$-`|6bl zorF*RA06TFFPGI__oR)&onF9?>KAntdkh)5x=))-$BM=gdP44Yw+4E5xwBrzYjK)B zFz^1{lj(yBz}h1g+)YO1=Lu*6Y$&@Ok6wuc@~*=RxFL^h-B`t*c%e|kHh1-NiW`^x zXD-ayrM@T13q_v0gKEf?>a3tyxz*b>0gUGkwbXLR8yMJ_JRTF{cCYO@ndqD@o+7}8 zx3kc?c-jpcCs zjLLGa`z$<`J2;oXFO z?Rb1~?`do3Y0MJNb~dJ?A(;iqK8&<|ZQE9&uKaFdJS3i$nq+mD_+EKJ`R?IWU0!?6 zWac$h;X+(Az#XZ2fuHb(-m;=@LqpMtsixUXivZRli-3so-3> z;Lu*%eYZVCK~F2`=SP)VPqXRF7AjK+sapSacDS;re%R?lUBfWXe&^H~ugr9ozAxPC z2`_K^8!OGL@-K^eyIBWvc(l%3RP;?Hj@SQ6Hh(P*>0uIXpf&;&(?)W zcD7#bC(Wa&@Y#%av&ndv?odXTd$?=%QQv(!J4w_M9Nz5}a_!k& zu`){T)j8a>qi0?X;&ES$x?6P}6|=$*-(|Tdu;=*CI`hW#KP)1lNG8lUs57K2E@HNqyA}QiUinVvgKKb0#%U1~LI>pNp&9(A0V>aR3 zcRD8xp?`0dZ&=CuihCm*9zESyTNQKNFr-pWtRdLKF&W5^m3K#-SJ*tQg^9$e*ZrjSg?s5&85wBO?zG@Q&AQ4xV z5=ARH)i0k073y=wuenB?-#A28Tuz-|L-(V8&0$b)(q(Khzex}UxBeSfRmUg~5K`+L znjcd7guJA5brAk;(z|0I>#J%GGbp9Hgt*ldyktEkGfHSj2+=u1#8FV;XNj1=IZxYR zi`-L~Y}1?;>&%x8ie#YcrDtg(&f1?fpZMdj z7?JzYXQjwHqX>55fB=BEx2iC*w%@`}oO=b7}YW+qRzUKK_{N&{2KG zUv#Vt1hd0=rUENbta01=#=8+Z5Zk6`ZZLNNL2m`pbC#2Op^cc?0^?t7W*=#kBb&~e zlfu5^Pk*n?_KqCyQL1|GtE3dtR7blwF5J;zjzetee1i=8*2 z0=_Gv2_V>MWuzpj8qUl1`5y<>(JQJ z7vimLR(ekVSm10dkd= zF4UfJ@L?Q*D)V-yFuGw}h1OSdhGiBUpH6R{rPKh2^u`e~U~n)U#&o`K(7Lk*M4VZR z0KB{cwgdimQn+--V7V@iT(Vsu4|(9$X~}pXWMbn%a)YKaB_U!hAj)ZyOCaqCo%f&9 zq{$Jg?>LM{UdgR^>4LH81!JQ`vc}Heq`n4Yvjk&aD@vwbFeElWU#iGpq*zDc^ejZ8 zpT(2KaP%(7(?K1yQXOh(LG1%WfQlthW#56GbngtyN5Z}r|CClw4@|DO8JB#Ag*n1)LmNzB9&5_GRprEcR4tQBvVxEF4cwT3UtQ z0#1WJlcKQmx2OP-`un3~s_a^;IQS@N2F_%$hSu%E^*by@3zuhL;{rP$+3+*j%fMMT zZ%0i8DiC}Kdir%339PKgMNDVH6D)O?D`>RE7F_sdQqkBe=C_@GEdu;J;yYd3cPq2` z6hveNvwhkuhR)AEtp9RFolg@$OZi}9%8aL-wkd9-?LL7 z;(Nj&A?9sOaKHN~ZOqh1e+gE#C|aHx#7FqUl01ZC9yB9b{gnj&I|1w1=)ib)XR+)2s& zj}-ERl87P7;AJ)r=A2Wvx4dvBm}9?kB+D37Nuc9aCHq z+#Maydwz+R@u5+iu)#^@XY5F%l7`D@UQ`S0JZy%OwzDUM(a-YA?hZ2^lo)cIUo@e) zQSGpEZ3rOm0k<_WJ_YhZv18~TmI#~ka?Fbexh~86;%$cMNC@m2dL?Q);}1)wa1fIY zn8Pl~C7K=88#~tpHY!#gD6DQ(%GS7uZk9=T@f8&C8>0P& z{bAv^XA`!Lt6~mTd^dfeF$uYAaA4AhyOagXP%k->>(Q)`))Y`86#5e4-2oUTze0g+6)!cW;f`8 z#gBIu&)wAtT$5oPrEY8dGcxf|k2IP#2TDpohPZ&u+4qA9oZMEPlB!{|iFxS|ivAGZ zC*h#(?m<}Zn`LgnW{nYiwiuGp7ALvVQniNHm@+C+PfOHDFz$s@FGA+Yra;awIagNM zzw%{O1Up8U*1iD@fF0C+wSn6t&`W#5BK259y>9>zia08kSTXYYJx(pPqxL13Jc zm?FI01Z1g|lXf(~mvJJxiJbxnP)g>EDkB&9!Z3=)yc=Egjn-$2h}CV`Z)x)1}unWaG}D)fYAIG2Z@`F>8TULoJYi z4b`|~N@@tH1mynIMtEVWZ@{!@vl8#bSPOKvS>ayQ1^s!Kr_VD@W0+|!fpV2&Pnc7tsWOXs;ai+WPC z7MG!U@f}r?W@Z0#!_ANCVvAr4J_7>$-pQWvUpsa-V%(jjc4fT#(Czs;=$qrTN&*12 z(BoB^F9r=Dka^!fr7k$~*q zQtN~`Xx&fq5YS4wXA87aa>TQ_MgK(_%>hVV2)Yjd(18)z#tgDK^sd^=YED{p<7^PeGcfiaAF*&z&@Vl$^16D?Heyj z(~YD{6n#c7Qs&|+7CV2YeEhQir1f{xZ{JJL&0d7TIuKq^%OR{6nvMAhUK0)2oCCl6 zDM3+4zr4?{Z+Wn?>)sL77wTMhOgV=C^ko;`?&h=>*Q(0tV;7K?V10CNK|(ONTb;J? zbUn6k1SmD4ZV;fBEL{=_hainu5yZ1#~9{d7D~7KL4>kkIv7?rNi&~6bvJ^ToT2zR}S{bG~U&fzf#=( zGUU}M_1jLo8+9h(1`n!N`PHOs)@(a!Ov%c}Dee+zawy1TVOZp@A zbL&Tr`>JWVN>Hv+he%bF=b$oQx&xJ+KfY0dq;?ahZYbXLs%u8WByY&(YyaK>TcwGj zd%WxFZ_ZkO!nfF_i)dVEF}qjrB0Fi7R;NSnx$b^C#M-NVTHr4aLVsY&2H8 zre{pWZOL=eJq-9Z9z~I^T?pS7-X|hLZ6R&UT+bx?PS@7mKPxu8QeC20w_J2OUF+lO zciHb}40)X$UZF3dUn{|=D&v<)MIFiesS>vsuN8CT+H;lPh33csLi*T7-)Hr7qM-iM zQk~`6dmS9Jk`M)*4E~WZ(>8HPrQ+ThxTR(VEsvuRD z>+}`n)=Q2FO1F_<^F-)TeXSUYNMZ7e8UD@{x#`@*nV#rJ-e{!eX7mZ?j0=sm`fUb% zqMR6qs4ML+PxikK?*-m(RUXsfv^~txsf)wvMz3t?SE)_qllU>W{?b+@ed32Li`hrd z4NQ!dyH5H&NiGcIpoN8h&OJchT>eesB+fUG<6GeDYnrfy`&JJU(NAjjR@p9DKDPa= zc)Ghht*lL!|3o-BR=V>ZC4D9}cV*C~%({Bg&}MV)y2{k}@B4(sUt;5r1Cnc&Lf1cZ z^1F}OO^w?xAas!TfHjP$P8|`r#Qg{d$KSMK6?UNc6kZ=Hq~>xcuBpRPM{|K8~ z-#(ysa`;EFF#i*X7+-Hg_k^OFd8tDM!DTaE{Ff`M-Kr1V!3TRPo1inih z=lVc@71e+2#A~UcIUD-E`EiB}2FaHCgETz?~ zRvTLcI3mr!jNbSu7_FeeY|Xn@4sl zU|z>}#~qjH~*U-eL?rQaEguvEganP`=RJx{5} zKAC>&eSlh#p=ky8v|$g(mTR71j@j=4!CKb1JVbSe=^Df)m_cK6)8Og5X$BI}d%j;g zDLZa`C8EJnN6eMbe1!QmoRez7qpDm;(?K4m#?my~^7h)nJFGG`MPwdXvXptxT`d;B zmN0WO;lq}4s){+yp!7vr>H?v__jGNh_k~G~m%ah+zaPk!Cd-94eScqyeU!QB*74A*L`Y}0 z!;HE!F`Fbt9Z7+HirO^|OHj$ZFf2NBXGq3D(5K90eYd5)x1hC{-b2f&!M&25R&1g_ zjlTtTVGkpfINN=B>B7|BLgYzu69s$sYw*3!P36&M{^4IOexmr39I)(yt3U@M*U3x3 z8~-tvMoD3O&^$ziQ3j}NGfRnDEzKv$P|O#g^!|bx(?SlZziYj6ZI9Q(s%s^pRgdZ> zx%3vll_u(k8u$f`=q%zfJwahIF24pRn#>e}A87XF*AHd)DvzYSuf^08%(tpX0?5GA zd>o#)D`{0z7*9t6f1MpPyYnmS!MEIo_FGWc)|t5Z?IX~L?>DWG+g6;kv*7Fi5Jxg$ zKl2nd-=19gf<&jQjkpZTofb<)8+~k-!e5XQi(0T_Wt0)X z-mi$Fk#|X)30^*lfGWlOvJ)~foV-(1Z~8_h>gKa=2@dw(yxt{9^3KDBAa^v!1k@r{ z!)_}R)P^O!K;9zvjy6>M%&;jiv;QWBNxS8ummXibf_damyt_2<=%(t321)^YKEd}* z%FCknW7YZhH^&PH3f@-pTIST57kURi6BTCq*Hn1h)u@+I-hjpLGlpcR@)NO7m|WEK zLgXb72&{&6=uV?*$e`r`0nJfOr;2qK$vynvZlFbviGqa=dM~CfMD_(Yp~sGGsA}Lr z;4*nvK=g>M{-7eJ`R%Pc28{ZPaktN(fs2z4-*;#-o`E_Mj|Jsouuh0*)gQ6yu)pHL zd2hyb-UZwyR%-`z4U8}n&XocqPGg=$m7?#=ZPnZe~-51u(kXJ-0a zHtDUl-E91;hrXV}EAPI0SZDep)scAs1VMtPQPFfGGJJMl3aJk!be%0`d9~WLp}ryN ztz(8Ww?s<1lgnr41l85M*QGzyl64=`0ZX;_CWe%hyyL#|#Z1PqtWy6G_xjrM+WM#; znd(C!=RWRA)qh_2;c~U~u3@Vnn${Rn0!&OI0x3!^z#ePlCNkLC@cuSS+xu!tblppA zqfUO;6Lnx$JK{LmI&JX`GpQO%xasjG)>)ee@-?EW75!*(dk@XumVRtRiDr9YwR%e_7&r$y?{_@IqoVfmfq;_< zaiUVUyv3g0bW{&o@uf*pRVcSYs$an4Ml}`4=^1&WnRtz)L@|?ZIx0uj{GBA{?>@@A zVNi8WV(v37emZ$&63}nC6*P#VJ6#FI8S0K0RAJrR%C(i~KAr7JTYyS)Gi) zjaPgv*LQhY%_UWo-wKYgZXA{qbFW-03XT2ln2|CWe;!sMHp*3U@q_ll>f}OMV@_jD zoK-E(_Pn}zGRa`cg{N4e~tI@(ibwNF1IM6{A4KSao-ENER zf%dBJgs&y^n2bExwg_Hq50kp|PS}|DGI%cW5f?1;OWy{RnG7rB-Ha=58@6yO1y@Ub z?dJgSu#~D1c5$R2ZPQzZT>og{mulPSiY|+jWq!k-2KY{uo%_ve zBUQy|V*P3K^z+gQB~vL3hko8!aD^^?H&VQ$FyQdl{kTkf6g1HM)BB0PuD_Ns6+8EL zMUc;-bYSFpnZkg}PAL|7I|Z_~V>oR<%`p`=ad|q$@AwVgr`yP;J4+cEp8H*v5?Fe7 z=eN8&)Eo`eW%G9_53Ghe>)^MgZ)BJi7VGdR2Twl@V5X9$p%jb0B3@R!R>ip z)lSO7f21({GodetF>3N0-D(S!CJ&&jgN)kZMhQHj$j`n&E9wnjE~#t7fyEEe|u}6n~VI1yOlg8c)y5UY$q>3ZotV?fr!VG}krJ!c@QZhI5zrSj z4VwP|%>jsOa`-n`Wp`@-u=L*j!_otmR72UtnKL~ZYt!BpznYUt`{$_7H~hP1-an`? z4YGTnhD!gtA2$$GWffog=o-BWORER{P&RX=kM`36T1%_X9ChCmrKxb!vEWX6e!WjN z-9ZzZ7+z3B7rKaHdb%>LfwAHwkZuU+f>$f14U92vxWq$?q4-E_LA*@Yxi5hZf{e?c zjxtaOie3$;)$Y{M1?otqS3|VgB9J{ERN)&#M$Cub0cbUP=o#pW&wbDp0LAox8EWvb z04aBOQk?%IC9GO4o_78pE!z;+tZ|TH$RnsKqM#mQI)IZvMc@?BZCJ{^OfysdvKR323^|4-C`e2h)bY z7&8TLo8S${v;o71GsIs-j0L0rT3k7PBTtroU51DcL!tR{&?+=&Rp5?fW%th1-UL-R z{$Z(Ky!aGyp224B3|Vj&7_zU0fDrEAS+(GUjIKW{fjjGXufX^p7A4Ro85DD=;m?$YEYn>ZL6QVLAm>tLAmCjT+f|yL1!X(yg@NwXJ_C*F{bsL%P$e@Olf-m!`YG< zbh8z6KLWHplW7YvM!?J;!THihIO(8uw9*3jdp~Grcr`g90SRbcF{Yl{*8&iJHPD29 z=3)u>I0by{%e2KZMs9Q+FZ#zQP2b$1Az|HEk~Rl+!wkMY2_j^crof5uLh$)QXbu4T zlZtiL|7(~yJ1q0jwL{GPEub`GCg>q!2#hLMkuQBXnT`Q%d6S=>$;gWRYXg#ZK=nXM z9Mp2W7V6aj#sF-8SOSL2zyO8r3=sDpmNRh1kl7hM?DB;1UwuvZ3s6TmNzjjSf->QB zfH`k!8=y#fO{k3<*!3vTAYRQr?XwMBf3J<%;vLEs_5e%L*T8PvfUn2ws~!*oT{0kBKiPKOgS$KmtApie5mm3T>j#J7RlW%ubO>+KdE zQrlpyW(h&_V0CuGcm8a}Jn$NP>`MpNddfD!s^nWSaS7z!*bCTzZErX+n+DAXfZV$O z$c+WL<>&<<_hNFH`Lb!N#mW{;Ny~B;ou9@$FjU2Kh5h~4EQpwE3(W(ltlJ9c1eOHb zG+|GT@}Ye{ZT0QYScDdu&b{VK?n+u)((}tukItVgReJ5!5tZ>4SDE0m`yK`U3Vut8&eOAM>oO*{nykhFkU8$CY5*C zzsJ6hCTt}G(hi=T+&97El3$~zf~KUy#=!Z!vtMl@)hl-f#6A~QwU4ls1bs*So!bqR zEsv(B0R_xOsEQ2R*+)|Ht}og~E41yOpsF(Zw-P|vML=k8ieCPWVLHOLq(KCiVMCd} zDcXQY*bdJ!CGk-PyTJ$0O9Zp85b+3{8PAKDh7ctHTjlrH;V1QTdV0y~)RJ8Ul6$98 zI6V_oTIx(y!!R8XTXLXKbI8R3+G*}Brz9P~?*vMdz__-<*V%wFl&M7PBx)Tj>ZcG* zD$Durqbu`R`#hk-ffc{0?ieNlTIbx!S=6=?!-T`v`Twxk=UH1y?<{@iP`&4XODnTR za$=bF*mW5ATLFR%%wlFT9Sk(Cg!#Dx#G?U38tr}f$D9@Yc>EBtpV0}nps+UB>ya7N z=;eN@DYkI~EP5a4uzd3J8X36vMoQ+Z-jazbQgW%=36|F15{%dXVUf*TW2SJgV!wql zuc4>e7_q7<%%@uRQ8W$cQxEw(v-1h|SN|K}Fuskj?hNgRFY6Cre?L6lK z6h*KX--Ib(UslO&b`xeYna{#MM*vo#h{?MplBvyOl^e*)V{+-g{rq2SqwEXqZOO*W z>k=&}imWHJZB{UJ#R>Xn{Marg68e4vIM(QcF<7i%|HMpv3szxjb4W8Ar-4-!t7>S% zAYk@x|9?41G#r40#CgD;<-s?Ffd;NOaFW{#B&1iNRW-mi=D4%{oI|S z(diB%fWQ53FSh^50{tI;0|WdU=^jKMfYFiw6;qCb|I0MKj1??zQw%; zC&IP`ad(J>EgnVm_69i7F7bB#iw@F#5e{(W(=q@mN*ADCyRPTXDi)*J0V@#6B-rzU zfA7;dFyxq6MFzsYakR$Mm-D9}U+NPaezowfUNl~2&f;E8tjmR6#%57)O)8wpE-?#? z31LV!B81nXxRP~08Y1GdiwD%D(YiB}@D9PSktN5Th`EX@FIQn+680USHP6n4J$n=W1yH^J+@BiDZSX#d0+Y00kFp8y_`+oZ2A;O*Xt2t z-x+7q9#9UrA21Kqwqq=Wd&^mdY5X-6;kSVC8p)5XoihT9;cth)@p=$)!XBq1GpF5%l35pF*#RX)p6)0@5v0k8;PGnIGz^kTP7=Mm|59-)~_~c+&@UD z&pf+XZ?`=4dEE8;_Cg)SnaAYX?znZJTL41$roUniX|j_lBT2y8m@~#^q@sQ5B)Kg( zceEF&BqjR%4&R|Sq4}axmEWaG=6CD2bQRyIiOWP2@#EpyL6#m1Z#Z>(7N}b*OXDs) zCgyal86nsKH-3C&Nr-2Wd$x09W>Aoo!v5*nD4wcM%fS{FdsBm1xXmQbRJwaz=)gGF z`Nt8lKF^{O=;8T4juf;}M5efoW%G@*`>Bys5snUVtq6)4Rw{JYc;$ z_rZifR0HzMzLBRGL}p~NDTv`P-mFXo%b4wK_@`!7opmp!IMvpQWV9s0r*+U+>=MfC z^svUgoW?^N2C}~w2ATJ9{i)aC$W(-yd%aX>wUh<(Dyk=23K3uniLOC-X-W^(nu-1B+=?apyw?hWV8eG?m*^#=At%%=s_#^55+_6!&YJewWO)0xx8K zg<&?k(Bnew%2l3NR;)wV(T!H--dZzSj%PcoUx?qamSa;(N5XZ+PTbiH-Wt~e9KXy>h&zqF2Cu^_1zudD9nty3fm^ zU!G-DMZs&-;Cj9IoYBqnmoE$k;%|-CjKwl=>V!6-q913<`X)8Ti}9QOrZp7A*ACvT zd$Yj(FgpdsD1s9)2%csZIxm?L3F2Qj?;+V`jnrsZwcW>8KVW6FUcS&64NtX=&*0vl zdQ9TxB`g2(8~eyu{1EeCD7j(UYGtH6yq|fFQKE<*tHiYkiodJ}p-e85S_zgg9<&q2 zGI!AxFLPGrOW{NL;;Z;>>ny!O!8nu0739gTUI{S*(=c!HO*yK`%b>Iq~T7uApX>a2%?deb@bSTI>y7U0krMwhcT!ufuYIh6+IsbBh4@I-eDYbB zU)R<9f5_@psMZ#GuhtAmK!Z}!>qA5|oi}C%*M@iqukDItN_#Mc$*}gdF|}(MhMfEh zHvZP*)R_M2oaMp{oVemkLqT8dU7B}4s2Qq6((i!N=n->b$3Ipof(`Bt?8CbvDk^KF zn>2)4j|QubdiD!l4wY_Hn3z0q^-;EP;qB^Z1yP}dFW=)#o;l5SuNE$sZ`+i4h=i6Y zc`j8MSQZ_V?m&m!dsAH>FzCNNHmNqYX=TM|L!W0^wsE5AABk1erW&tr>?ZeSFV)Fe>Bw%edBqWF7I3H!7h^9>w)2y7EP=roN#4L z7O?jZE87CVQp7m@+9;e@(Y&{Tn(P=D=1U4SB2#imRYXR$S9h-T$d3e}7eCWd6DJ!!7o%06DCN>;CH2=E*SgcfeFpsLr(U8` z!)%PV_fy@;{JK;uY3VZAHKwwl=_}HxsHJoPbJVuSXK375@H6-GIg6A7I%YK^H(mGc z0UnWQ0M~1|rnwjV!uLJJ3TTSCHFJExz2M_Z?b4W>YyIK=^s-N?!6}ZGr^T0o&r0JO z4@^rVSb||XU%f0Gs|LJozs1}*$9EzB=5V-y^m-8UK*~(wc}f&@jP`&zia2tora4A? zxCXxaXa9rOL;7ET^;m`E8Ex%<-+5b>jWzP9rem704bP%E1#w99;u7|(jlTOxowkbS z=^&TotjHqOrxC?XTNgTewhHeP^7I!P?%Vlbt-^)q`d>BAQ91R=x>13NYV9JzxQf&b z<6$oe>x#9Le(3>$#K>(Rx}OXW0v#*2;d_?{weU3y)>cp+X5zM+Mk225m-NDIuy#U^ z-`*GL?Giu_99-@&F)zI!*A-Z}48XeHKyn z@3Yj4C(GGaGiG>oo+-CB_&x2mY&K-Xz&-l$H0FUNaI*0OIw%u9B-rByv{#DtfQ_guLtfWeM1K&mlPo269R*^{~Hrqnv} z$+HNrM!pfs=JpR&-pW|CQQ~WWN3Rv; zpz{t{VGrM2bMB`2jjl3AS7a;3jjXqnKv!VYd=SNcnY7kLNXmw_B;$oTf-}Gv zkDBK1=p02lhs|(CgmZ`-AWbHA^r9&q;~@%Ha0)sZi}Q29kl88V+OBmLd7Z-txgL9X z6<#+Ax|{Hw@_w{P3cspWXY;935q}f{ng2 z1;4@v0SQ&Bq+cBf-$zBXs)lv7b4bAi7vC83d8Bn6DwRbG5+`}pz6VJwq|NdCVX<5; z`V6_E5^#uJaN;*OKK~Z*W_*Jx*I}*+lf?U(}1nV~TW$I*8Lqn~*V#^%xf&6U+VzKYiE@$1zGX;X~T1XP!7s2k!IrHJ)>r4%vD<^YV-FWt< zNpjFbBj{z*Lf~2n5nhX3W}n7V#k-c_G!djRHEafcOrNG}IK#>DvsSP)IF7RHd1T^} zb9Se+)w4F?yqqT`JRS$1q8mb#eq~05pPUE=eyMmErJ7PQCBpzicfm$}{h9I9X(e^> zrX}@F=uz!%Khy%k2nnQc5&TGgDlhGd8yui4HJaj7JhUeovG=FFEcqI~GnS}yf}`HK zF;ZUYVZ+sA@!nFaL#4V!%xoJQhZLzgMBwyVY1S@r0t(UVCE?f=U-Bt#GwHDHkYvkXPnUB5EL0h@V z`vcxt0xF_jZNmFlb~jIvRbs5#r+9`HGwK?ul5bR}ex0!Wut7W=rILT{PNVCIx1uMU z1W(IeQ_R1j`g0gzsPoy*+Jn$IxLIB>xtf}&BDEOkJhg^Bf1NV&Gb4_Yh&RBaKqMpX zwAq-I&*<<0Sanw?dd!#D-v(F>dwyySdEdHFuWtuGsg=ZT{U-Qi6vDLHA(jL0^f*ug zC_S4jplV^ub>vVAADCCma2!Io8NRfAIG_B3l98rQTItA08ZZA9FP?irJC7^MWnJ*n zm5g0?jB~X3QS(03;D!XXf$m@cft)YY`s?k*eo0@`e>*IT~ z3C6Jn>t1=u%S#8V&!q0{;g`#(K#l8CL`lX#IzoBINU1R%+cYOfXOYcjRNB?#IH-jZ zWRBHWO!Xl|kL}o+%v}1@r+qQ48$!Og8iz%CRD*% zhQ9^`Kle$j#823J`xbniC=rQ@X+nmQa1<3rIo4BdEWQ;k(8xCr% zNYUxA8%wI34`0l1%mmwBsh{Ydw;k_#Z?ToLF6c6GEYj!D$GI!<;|TI(ktg>;ZLZj;@9StYrJ-@Xqy>TW)?%b&l->!o=xK;pMtGn6 zk#o$Gxu2V?+V+vDh-01&mS7$=w8ieTwGSR%;?q1fJpc02Tk9ULZX1C+8J0(`4_2C= zcaC-3)LqF0Oo$`(4$YbFzZ;ae|x=!(m82Mo##tV8k_yF%MmWEcZlWxO%^( zrD-PMgWdMIi+w^&T{OkHHr$*WyiFkJwcZkRP(p_Gz%d;0hb_IAq)fN>p1#_6Z1k)8>9yEVI&Q?%e%Q9hP zVQ7h0m{t^`jLoWDIfLK4g?snwG>^&6oc}Erir52o@qKVr&m{1`iFr&Os$mIT0Ni4b zLS+7F-zRe;Ntgp>5d8R>H9ESW$wr=fG@lBL15Ou>R5ec4-D#9G;N z-4*oxad%n$5$|-D>u7h82b!s&L|=ZSC6!&J6&C$g>rGc+0o9*5+a50UHd>zN#$;^H ziQ2nyuk`oG?{$9sGIBGWcR(s}G~KnaB5+`xkZHWSp_nmx-#wqxr761zPYDZKy5~7% zY1Fh@S>6xeX5tGh$rvUW-h-&gT!E7jUE4)bzm0&&cib!%7`v|ehUH3@D2Gq){+2|P+u??YH+tw{1L>@R_=MX`fsQMa$4&JmHf4gEeviB;O*MTsx}KK4 zy`i=;S7wPHwH$!GXjTJbcQQDGgPS5oVvD9*yk0~cf?Vu?Cqd4mJ>^E~g1L2pLS1L& zybt+(79ptgZ;4p!Pi0{z{&+%<%g1Q=+ zuY22&Q+b5%0@}N$&C8^(B(R-%n`=G((*@{vf z^J#(0W`>g{38p`v**-&*dN-_69=L^DBY~Vf;vn)$4D&!bxonyc!#o)r)BzL0x3+ z7Dc#+nuX~?04k~o&Lfz7$OQ@@8k#)O7i1ju?8IEO&S`q;>@(l!(_GAZsnPi|p!&6R z00VEpPM5Ep@}WJR?Q2@`d4ob#Q^z+Rcz$mXz#i2US%C8~MV4}TaVv9;U+MaHG_#6Z zgum4GYcvj2`=7`V2Z10QR79l@eLoU{23#LLh{AZ~0W@#h~qnr*epz5~U^ODw<(};v!9k1M`*f3^( zrHQ60yL`lQJzX-pZ~9}&l#Z;+PN$#aK5ehjx%SNBqR%$V(~ptZI-rp_VRn zXrBD=VQ_Vh{N_2vMXtu3QUH5jtsrIy%Q`t(jmk{rTrA`(Ew5zhjvx@aUY0_7b-s!R(jb4VN2 zvIV8@J;UW6`fiU{uD;l2C6Gbt_nD==tnszHdQw39it6b+Ad)P2^*;b6cEF7PADs9P zznA|laKa@W2%xMOujqS7!d|vrA{R(dB)_@r*}jJI@dbkC7zWDxp}IESbPsc3;{32B z>LK$Cu=7Gi@!ONIPpajPy7}|57cT^n?QP@{O|gzH*VQ&U0z)N>LoHEqq3)Z{rp3}c zbun2D+J4O}jJ#$T(#qQiDKm@SM@_H3aXnA5!UMmt%;GAJkQwjkAaIsl|tQ&)rDExdP%2z4JK#2YRy#cN6?uO(y_iUPvlVx*eg%9G) z2bI`m%&s*j75;A3t4%CTkx~4u)Yr@BuD<2gv8ph%jP7x z1h9;qfU`Mpbc{o90pXdvk~dtJVEp|Dl-|foNNjj?tk=WLy`s%~N3`rkw?o3%f=X>*R8<$b%1n5`=Z2>PugT;=Ul48dnbcCM? zAAlcLzf9^PVpfdFo~`#c5H_Au8f4c;bnn}h<4*4~0zIXP7k8~xik*1r)OEM*#Gzy> zsgFY)*Ujhg_ir`WW_=dRp-uHvxi`r^5I8F)efrDf#4Ie$)c1$m!sLQ?0Hb*D_l3X; zg{k!sdXMLa3^$a!-OrD_7t|9XR5#3U^P4L<cCUu9YCrc$qYQnXDU|kIR@M z%$`-@%M3E)spZRdJUx}ny~6J%7m? ?ixKHD*0pZC6%8=1Y%841Vew)Nu=d&@D8Y^i&fTYFwx za4kvRz4X@op4V9cMy;(@%#r7noSu z50s-T?JpO)Yt!S;IO@3EeAaT%z~UVHm#$;tjmgaYYlU=!D#pV`_F^C{YQfkG7;@;B z|8gevwt7TgzISgfx>;pjls3W5j8iD`Wt*?GiYU^_Jvk%SYn)=O<}o~x(YJD8%nO5a zSQvO*VxYhDC9gqk!?PAIQU8?|@oM=TENTJRa<8M8ZC9v~|e{m-s)}d-HfG-@b2Lt4i{< zix@=_p{!Y_5<(I}*`|_pVzLj$j22`Mp@^xFWJ|Uznd~9SI$6hzE!k$!7-s1?y6)fa zd9LdIUiWo<@9Vzr=k;9QKfGQX=a@O?JkR4eKA-pJ{eFM?%FoCh)JWa%ksEDVTzR2i zm2~9{&uO7>b%}{;Ep4dBe6DgbDcvUf(IZ;|TuPD(M_c=r#qF%JIoCcLvdFPi3pnlz z9*)#A`=Vr*@zgBlbA%2LL!0?x-j^$~PSV(#JQIdsiF9y96)hD>y@IONZ|2K#)7`Y&Ekn6?(feG3Oj>WPUQ`KwR#IAnJ>{H` z*amCQco;|7clXP+O}Cb&tDKVBl}D{*caLzCiA{5J)O(rxmA;vw9llI3&ZLUjJ3 z@s9>xp6`t&Hy^a)*|X;;ikEZ(&w!HCsbwEktjha6ud~FF!RIN@DxbU^2=afDGg5dP zrYD-O6~2i-?dT)ro>F#y=lZvF1?m%K*sAGJN0XIWrX(0DNG(Hgu8ae zE1vm~lQU`*L$~lB+?)~pw%LUNW!^#P@Y1a591S=j^nRqv>5(obHU}9{PxY@t)S{lR z>YdGG_`ob~x0vOeO7w|Wwk+rcro8P%ugp9L?20tuAwAj(9_;71>pD7B3d&977UUG3 z2CW+8G!gbhG$A;z12vNZi)SLc0~j%}bUK;YzDuL)I41yfV_m+nS=Mx{>r4k*N1!r31e9RcX;!T5gHlHyfb%$Il@B!uo zQ~tOJqO5Nk$x7kK`@E;2J(@i4TT>(x4U5jS+)=NRl|q)KoA)f643GAFnF~S4Q`G}6 z=e^c9wyK#=8K_*5E+3TL2Sn;QFIHGQqBT&WEl4+Z1Yl#+>qZr**aYl4JiY`m{Gsz9 zDIXO~>&ii?eykHkV#ZmyL@4KH9JdylCE8!B`}m}jmay`CDzBZ#`9cJB&?UP4eLF2t zw`%W8*~hdT`nFCA?sU8{C8D`l44GvdJ7D>$pP^3VJT-DAOYE{?nZc&&M{y62za>R4 zebSh-poM_5NbYLkx+^HgzpRI&D%b5J2hXz3tM8l?5Xy1I{?Wre?qZh~@QKHXlUsM29uAsP?iZ*3OkB2Hr`tkPs5F$pvt z41cVAbCJp$_0@pdWYN=yMm-|1 zr+C*$NU>f;M5EZAI8m|dit82cl(kOL?OP_RQq~Iu-H)aT-GP>I2H7^j2!`3WEisuB}*BsgACEM+=7td^s{LjvVLJ1R|TQ2 zXC?zB?w!fv7M&As+x~`r6=g@p)pNy(X~6{CHOj_>X!!x-qP)h@#=YivKO znaAHJH6&`Z^q#vnRexVqjq4mu8+`4@h_Eq*0fwGh3q;TX8Er;6lttpuHdo+4UUT?V zJ|Szg6))`3SP)w|<7y+69{Vy^ckB|YK`qEXve2ypv@Sx6*FU4hk>H&+pkW_P`*Ocj zI@7oGakpywGKTl~!J4ECbC(_4lkOCDVEpoP+K>#YFmoFTG2}xHEvF%hU90WrUl$Zw{cTjz+(8-l&H}?JZ+7{TNaTXm@oQKU zd&7N*3$8bVeG&wg(n@Pj(%pcLa;6>=5g@h$kJ2rG$t}KdA`RQxE1mAqx*mT;{Dfm~ zu`t{=C$wTX{?&qf^R5S56pP>JfvbS;9j7^u?TRNEMDy0af<749MKi9yeb)Jdw+HSe zqhRLI!kh^nH=dS;h(hhmMcQ7V#gjUX8#Of9U-rn?N{m+(9C_ffU0Ou<*ptcS$9_3q zV+5I+qtI|z`_sk3a^~WN0UW$cKZmckSARnrtCq5n?`zd>Y>X5}k5uv}(++35#C@xEP*5zAL<#mRdC2J; zfZXLlCXG*5PJAvu)T_LT*gZC6+eBS%kRx4y8#-9+Nac|AjAlBP9=+=?d9KsdB5Kra)l#j4A6Yo~rp^Iub>VNx#(j^^XfQTRlX-fKd|+PrJFU}%tLQGZW2Up8Mh`i}kXowsF=?yrA$RWr>}c0A`$Z)|x_SFv{aq*bM2 zfT4X$r>)Dm9DZ}9j= z_`G4^>YuW<0f~@H(*FT z7QSGV=v{!;9=?wZV{WfIiXjeCtiQ1xas71&{z*EEt_$iV(1UBZ6)uFuI(7{Cjg4`1 z22dbBt59wOyUeaA+`1#Mt&P8#rci$y?Hjx#G=^~tL?ft6(SG*9&Yho@S))E`$r&>` z4x^v!2ZvtIyB2(Ll>MMpnhr1Zh_ZavpN>HS*6;_wJk(?R&-|bP=k*7YwlpBqfyI9M zE+B9N{)$(b^;f|N*s|#rAbJ3*9C-uc_j0^M5P`bHO5z1W{~!y03C%Xv=s}8)jF#+f zA&`@rZoFq_)B~qzr+>#OI*cDix5Bf~a^KjN#$RkiMEe#>xeS$S>)_viAb{q9{u&cf zBWS;w4E->E0QPc-fQ^1ttQ$vGg|cMgX&1-9J$pE%Y1P$prw}Z*c@hM$f@+6H5dg+PPc_ zG!gKo{3k?|t<)Gt5P&xGF&H4DZ^6bydjTanA~CGnLu#z;06uO$zrFMS{nNVSpQ+xh z!VwluxY#rOu(VvFCiY=T7n^VuGan9S0jS-skjF|MTk!0qGY2 zhONM@Pyqc1vkmfper3D(iy#%=gDdyI%#a8y)~*#uWTlrKt6PV2c{0wVZr{L4`OVgi zb4eyUBmOXJTL%VK^qVLQMXdpxl@= z+q8To?;Z9!W7^)7$hOr>hmTo@x&#<_OU(pJo9a75k%&t9V* z6*)yZ{ggBjIiwluWAfTM_UeZdGRAS|b`)z@(zr+nAsUSI7;Ni=7>%_^mb}9!g1b#o z97;Av@0sD1eTG*DZWtEZ@wwU@Oj0yFH2nb$OQ|c}X}XJi|4p&1|5`E$sKHR_V;>fi zdr%u5*Se*NL}MuDYTupQDrq zwCtUT9L2i~5bzw6eWsE;vz3XbZY^pe64e{aKPo~VG_T3_vBVfgv^5VB3;_IZA-r08 z*^Mp-pSo%2ClKWhi8Oo?^Xu&(i9B>Tv1v=2tHgS`G;&K5Fmg#iIr00?Bz_4Y*-)L=!nS-w(;~9Lsb`)S~E;AeCu@=4H z2N88R$rb&qkHH=M?9!JnzJa-3oSpsIH&k8pZg{|w_wGH5uR()FLZo~tJeCaj%=~F% ztxYiAS~ISQ%YC__=c><&oyMyPa)^i)i-)uCC`=yva`EUk(?|yzS4(Od?5zlb2G(jJ zK^C7!lhR^Wm?@Qz`mD_{27^FP=r}W2D?-BrKYwVMXD+C+us2rAU>c%n-B0FNy#3?& zRZbg`uAT3P{N^=pmFf?Dkc{n~j63~qB=P9(!f^hmC*jGiMyU!4N{F#=d~Zd{?67W% za#?I4L({#tj7e7;!$*5|nW&VjYKz?MTU;O3*}QCW8GPvtXkKzfRUR^_p-!7zkOm7YLb*6P<_!)N z;_oWg_*tdcnu#_(_9KX0$#{~CYCaNrEso0)5e*YU??VnZLily|GA>edk|yjAuDbND ziYV-KO|e0F78EO=G6|X<{}8apftV#C$Q@_60=o+-jc@9Tb>Q!pLwSIwmh_<{-(Wl# zG+{>uQM=BObM6P(zai--+1r7@b|-IodJ6aHoz~%6vR75)5@NMD4;xmmL@;Zli{b{1 zhE}|D{m5f@=d#be-VWV`U;90f3(M0u+Z@kI>H?g#E01W;L3cIz{xQ+2VML@BLZdJ? zl<>qz2$YTwtA%c}FW7z7%KPLn?`6E<#1c)~Ae8&YId+YDdMTo!8-S86jk@mdxY*S}-4wbLCEKD`p(T?3|#%Dm%#e z%v84r%o^zMgg=V#RV#$>^%14@3n=XNJJRjkmTA^RM6>-iU&3vx%v~B`0|`%HdP)XC z!&B^ZXHBOhm-h+LjR#csK1tL8>dHYHkmJ4{J=8y4_%yk=a3R~aq8ca5cZ2LTol$1A zQAl`&n~=$8jXIbxFJ)osPiu()&u!LTGA>*h!B2Z?%8xvUZ=r64#R!yK9ClM05#ns> z^3pmRuBfK5O`$T(`tk(}GJ(N|Cr+1VwdKPuA@dtIIPnq14C|*~(qdyUE|f#5*Z4+8 zYzoZVq=$-PBr*;pBsa?Nw~48ft;fYm$ZDjN=mIzFR+h5q58?nP)1rC%Ek$YsgnN1*w6q=3es`zCFKHz} zhlEFlj5cE{E_JAzr=O6z0ow$47~-@VVo^F}%RGi1g~%R7uAD3Dbt(B+t1>=py5~&A za#^@>hRg))^)0tUoKx>C+C+A87n}{)p|y=JNXvR#eJ+ACw*eL<>_T;EiiMK)rTNFZ z5Yx4J4wY9TIdd}7X^B?lJWFGXPD#`I#D%uFjvwWIKA9_WsBlRtx5m$TY;2|G&1$#p zm_tiiOhNyGtb29+aDFB3>6bFpkljWFf+nqS`wHcBf~ZupLBkCiaC_LRbye;%t1;ek zdEN%dwEtjmhm^7`WoH(2k-6yNkr^}>kQ<__OCv~EcAv)73bj{Bh;+1PnW`MQho zsbV*_u5pV+WX5gT=1Mn|PhCstN?e$u$aW68WqX8pPMwO)PO`0WuP8T$+P(-_@f`Bl z*aAeNotYp6=VSXH`q{K@3bZM2Y2L6t2T@F$Hi&I2K~P);38zFb zV&^c!xQ(->VC2Wz_D zW&$DNLZ>zCKlkrXM(I5#pe7Cw-s`%sIcarqJ1<9t?t53kx(x^ptSeCJFrez-)n*tw zP2*tXS63)%w-!*Y1DMO{W)&30VdksdmnA8Z4Ts5_$J=8JQU$mcd)*l@TE{pJ@M~R| z5iMgJ*mvKh#Z9bE{~2eU)!QHOT(TNPao$m#05q9s#H^|z6~O8$3v6Ny7*b*&?tfBK zkD-}?Sxy;Jzi2Of!3y!qIFxvtOki@3fef}G3dH+y-R%vZd!CkJei;YbOlvquGCavJ z7M~TA;U8xusGrQA{>ykg?VJ`kPu&2JERvssq-CPa`(?HA^8&)J2QUp@MBIi*tsqEF z_~XAf98V7G&e3xQXY=y`7H>Q!NK{70aSZ7KV0^o;Kt5JRR_*6u2>B5b!3tm62Er&k zkoUB@PF?YJGrNTQW&GExCd-j!^v}PV3uqpodp`*P?S))cbPzRdeT6vFw6ycH!?Dup zYD(%YQe`xH8k|jdTgm;)$9aVaFM`J}I*TQ2Oe8S5 zzTfTo(Mn*dYXH7-hXM``<3Y(VE3UHyX+0gM`^qr;PI;oFFhrUYeC|Zfn!$6M^+b&# z{?IDDi#Y%HCt%998<{oVZFYzVV6%e-Haq!|ar)?Ljt1fEh z-KftqkI|C2k%j9nMZL49&Zd0mB|#XGYgjTDlN0`uj9^3wTQdVeuKak{1Z)3aH}9sOt8=;PvcN&>!?H@icWD%Qc?aidhrU!t*uF-|0<7)FGXhO#)vznQ&5-piK-%NFWk9-E3e1sbE36=7);BhBV73k+-yg$t znet&Kxmbpex&Azr|FWyHRQES#SnhD zIxPGkI=`^C#VCpf{OZAKzfFV!WAJKHHG3IX#zmzXtJmUc4N1X3Qc;br+}dknX9#_; zRCP5)swl_oG!PKk7Ta0iNA@W9U`LW)q6+L?sYiVoYh5o|t-8k8J3d@Qg`% zzfX|X6)W^@Z+;|wh@I`_hwbi9%oRO1B-cGxtzQL$7^`Dx-;;ya%0284=9do*4=5}` zT|O79;c;+m_}EsOHjyAoYit?gCrgFbnzhJ49R5|3_24SC)0j;ZS?T@HDep^@;& z@wj_-Q7zur;puCIFhaR0fNA1%09n^`8|n;MFh-?NkabExSn95v3P-+}7yT14Ax#Il z9-50f&CJu;3ye35Qi;U^D1EY27(=h{p7MgMHGbhpF{17GW+A5phqIa6Kl)eBW!A-`IF*rHwlK`n0T($_qTh7y8Ca(=S#ZZxU#e*rJ>( zF?qr^q4_*nVL^&KM~Yp$>w!|5SlWh+nrV=C#9!@I>43K>TkVy!EATuk>ES6k{Bh&L ziX05i62Oi6VJ<-Mgy0Y=#z)gecA9Ijn6>LG8A^QvYFw7&2qGb-49fGRj<=Uon`B5t(K%6%$x zoE&;310K__%c@cWoNshD9x*@mTkB!_A{~#acjJkvM;tXZ4YSL}mZz*75T5kHU3UU= zq~#`p*KM>uu%a#IWn?L-U>^~Hv3BjH@0=Zi#pB#CgYiUk0y)&(YCyPrEkZ?EjoJ{f ztDw#yFYq>g6KR*Ndy4tV;(q%8Y8}mtsL6Eix=4FCE-FB8)IezzmAgGh-s%~c&@W3z zBy0B}#9bvqW+U#&WHhoTSm#P<4=g|rC3@%#DKmGkznK#IGFNIcY=F1d_83{Uk^KO? zWIy3{)bY4!?0G^ej>7KIQgS3cNSx5C>aR6+a@tcY5^A?p7g8nd zuqG^fZIi>TX-2X|1f9BwCuR-?dP(-sx`3GXrmMHcY?I11~NE56p}batmsmA zrUFm;1{+6LDSmv#FY{sW%DZvW8xG=H6J`&?h8%{y3#=Sm>kfVN<5zIYb5kpE7#x*8 ze(+%IL!oQj_e_m?%)IVrF^b-Q;5Cm*_4D;Gvjco6_oXnPO&rcmBpI~y>P#XlhkBVWXYu0bkwZquP|VjASJ@(B-f^`XUJ4+lIQ zdNxzdHSD$tj>nzr=qEW|MK+}h7ZsH#EP^8c$S=e3`3^06D~X)PPVKF+*)u>pUuI?A^udqkKE1A^J~9Hefh5* z|Ib;_tP0M9H0Y2Axs}yiqrl`V*QMTNK4CbKhe^(nM9elCv;i$y)2A$F+$5MPCQh-d z-u5Qr&7+i(msW8tbHT@|*{7HLPwlJB!d-=M&r%^JYmLWU>Po{c$)hQgiB49KDvNiA z9@F~i#~3Qir)Xsw42b+n$ur0<XIrZ$-`6Ev9HzRTV8!-FIcc zu{@D*p`^QbarK&-ndCsX$@5l@OZCd#9UPM8?-P$_?mN8VIQrfl1KbQYj~I~0=#5k$ z7gUR2Nz9pM+qnBX@C{tz*SkGh*mS1ix=Undl#&{CwF(X=+}LEh@Ucln6PXrs`xx6BY{}OHsR`fGkk1%$~A-ESe>ry zEnJ;*^Q;?M;;(&G=B-Or9o~1OgW3-{21_#?j2QnK4>sReh=7)Tpcg#1l@#ld)qP~L zkC@?cWD`6x`$Pmc^BIdoCEib@C8%+SMjDXt^T9+yBq;6Uq7y+LkB%w z!md83J^F4rH{q!CZu4zll!3cWy$wk2oIlQgeYdD$8v&)oEQ2AbY1m6R{t7jf>< zx;yDHet%Tt*}|6#C^~t9wR3cw5P=c)TR<9;4J^99(mSn|ZS#va*~SYs`Eei1JUGy^ z;3MQVWjX2&YdfI05IggNwv&Fq6@+XIC!(nTTx&|Ec%QQ9fnX1#OIOaMTlYz(kxrtZ zK@Fn$!68C1+t*#paNME&9LiN?-60#5%Z3LB77fSR60~`z7Fi2CnAowQLn(!jGiH|n zMv&?TUF8GmG5H|0C7xznhTYiZ1`=Q%?1Z_}si-ax_174JJntD6)IbljK!RZpsy!bq zOhLu+{x>#ZwvSIsLG~cugZX?o7d%Qvm{`aU?-{BrT>#$iuiqGC7el7ZZ6F!~cnsw+ z=Pp?&i)TTKbt?_S+L}xIp6K`|f$FIV6eP744A(9H*PI7V3gZEz9X4$)7G&WC2kLfG zQd5fMegho;Od;Gd~>6D0720j~e7ZlE!dhh0OHJ*C0ap|g&(QdO>B-4I{ zA1($Azz2=Lea2j;D=_4{?!w4-0sMjhmtcu;pbi1Rw^H;dvxZqSwGMj5cr5FTu6WST zE#M9@&a-Es8?oHw#WBzl(|Q0WAmdi(22DDL$>*Z;zoep#bgB9I(L0>dOcGu^T?}w6 z=ac@xwqVzHgN8DUX<7&kHhB`sZ_2ETH^tF_TC1=-fT^2HelTEzw z$Fyssro=uS?Tg!gbN>Skh2bA>ZY|~#Gq!F!GQAPQr(9^;SiPqlPI)?MPiyjHwCl|#MpO;7Hi!iUM>eW@fDuWxtBb&=^ z8AJ&rT1D=CWhrd9>(-_K39k*^zxG0#w%yuO=AqCSq?7vGFl0}P`H+NHp5b$QWv}5g zH*?ag1D#NtstxyML>_#QXt)9YtACZrTV$WiW$cR|=Y&)7%yOI~C|Tq;>PV;8Z{$X+ zWEeMjbjdR<$FB$qwA1cx3!jn2(GD&}YN>!Z=tyRtpQT?0a%7P@ zC6o%K|0W*A?H522Y3ljPH0n!9X;>VahGP#`_=oa*l+pmuV02E}B#*{ySbj-9pcfqIctr88kkJ7X10@USIpYkFDwY8E&4t9j8UIAL1uhCWcNpUL*jYFBeP{ zSUpT}v;&CSI-GM@ZWM`2PTk@pF&8eGE7-=>(y zy{++dXZkWczbDb<@drCB)#@(vwm(|F=DaRCJx2tXm-Zv6c z%#v=l6?ND$@91#SqN$G1?VfeQw9^c^<)EYI?%V!EdDcn}9GJ=Jp} zqH|ALq}JkP0{Tr3=e502_JP|Ugm+kFL|Cp^dPh{3yARhdAJ-=k>*6Sb*ZaF3X>Fp* zv6^t3fgNc49-1j}<~EA=3Hl>$cddeaZq7xTKS#c1(<>ySRBpo7BYbLvZD+ND{6Y5Y ztbj@D`3SlQ${l1*3Fu~sfX;q*AeJnV``X&f#S0svMYKTp4)$BOumt*SETfK#S6^_w z?w_>xmWq&*ua(tSapn>e1YkgL2tb!{tguy%8#$Ob7Cnwzh{btUMHvmF?ak-g&gceYVt-#4ra_ZExH|gb!l5c zQEQ6ZfvZi4K1r4`?v@H_zNFq+=gI6Lo5e)a3&Td%bjjAyu*U^`Ne*s$IRTTyaQ(zy zYiBfy5yHIh2R3=K85a#;K=YLp345?9B+d}I8f)UYkpoLceezm`1|4Dh?Msv6tPCC6 zB#xZYlq`J2Yo9vrMm8k^&w_ChX2`3S8U+g8?-X{Oe_^VPv4kzZJ+ZH zt$avz?Nf}i5+BvSaYl}&N9SV5(fBD_NG?$*CF1exUzcL6Z_!)StOYC82*ci+#5W}d zib|X~bZ^JRj!<X6et1r5jVsE8Wy{ zU4~3*l+i%|_sEBoVsHDI4&qt~5OFf>&T3~c$u#b3UYTaTz1SE>tl$}*19Gu7FT%q| z&LpB|Rdd^GZ*X45)OS_GVli%*c)}~wc_w*a&9H=08C`1;NYdQ~Gxt2l>I42?`z`Ih+@LtRF z(vAZSFPtzb;?eKkjXOT2@x$aqWL z=k_q|1qN|zY3YceL~Om_)K=+Mwri6)ix;L}y^1Mh9o9gvtV~5)&*r3Fcp>lVJv#cD zz&JLI%I|pi3~#Ghx}3$@=>&B7@e7vutU-bZ&TUk%UPqq79>(y?`z&}5Sur5;pjPDZ zp#HY%le-%QUTmjzf0V^!5zp)|T`|E&e+S2YdyTYy%Y$*(Lr_Xd^9}-Jx$^m(XD0iX z>biG1RUKO&=-I>~OT<{u;}HJAAif` zvsE!$O)hkkl0+gK`++(ikobu*5X@@mcouZqky<@+z)6}eU5)jgwI6HSGjnLX!wsFd=ck zgiw%*SF|@1q8gezbhy6+Bz?~0Gmac0n8y)5#M*p1%H3FQU%{0sW2uL3LJvW|KmAbh z`3@6BXbfzRFJ`aF)2Zv8W5LlPbJwH}sZ>=Ys@J{>JdAVGB30Dxq;EwD(VXbV-61=C z6~fWny)5A`-CEWgn=eY(d=YuOdu6f1c>fTt)*B|B>wn=&u)2nM%-8$rIJaEO3yC3B ziRV2IExefjZ7S^IamKN!O62R_Nt1xm+G+$sX$lesc?p(3DY`bp9k^PG4ABs8D=;Eu zqKb0C4Qd)1cEfWvc~QSPY)nsgA%^&=nm7ID?;wVbrL9-8c4mxs-4?`gunv zm+YMMZ1zT{$1^wT`C%K~Bk__6r|$CeKWy_p6}t1PVXI`?I)a$X_$`>KM>|QW03FaE zG!ddLgm1_$y2H%@o*%8Len0%u{?Zhy^2p>+8T0*Rq53;IL%ging+5+LH2}4X?E4n( z?I!Rg??Tbq_PjtVKbUPL>#$};N^Rb3&e)3$;F^2>{Qp1&{-0ZY4~h*8_}Tlo;1ZxA z!bu9PSWzVGZ3#m?+LR2VqSsaWYcJ){Epcf_uSo7weEs>gNYFBTJykK5^1U@=C${YJ z=u2>08*maspNj#Sbw`Z~lCexI2uhX;Ef~_jjL1f$zW+R}Z4|Xkpy^`VpzE__C~Ko1 z+7SQapK-9Gf4=H37v}%TEAgD6L5rqH7%{V*$XxMH$(l+?*Ew?MV>vv7d1O~XuUcs$ zh^YfIpT?b^))x6s)RV2bC{ol!vS|ZXByiu2k5u{Wi~S9C`gJD0aiFJOR{qHmnZg5s zQO}MK?+M+xH$Y%hfS9boTg+aR8bz(CPNsWR+jwCp{#H4EPx*tot|J)7Dp&I>Vl(H9 z=~dj7`<>QH9^GQMF3IcYYF}^$AQ@H`9c80NFloPkkT2c#%NrF~8hnzj1QeQWl%+Ud zT)5cwfm*{N`;Z7dGBai<<%!Rf7S^RQTs6^PD74hWQfk-E<8QZ8F5PiFY^X1@FdyqK z+lJ;t14;lVONo|i(Cuq)ZByA-tLHov9xqTzEVMXn?>uU0pIsI_jIf|^ju>Xwb zBYdG%`E$!0d7Bb1v|F)!-Bq_-XFh9LitdDUIC?h@SZ`IQQsKx_o7;^;)*D)-Aul#= zEbu{VtOj4}gB}G7C*v+&v$izrkgQ7kxX-41V{W-0wq$~OV0LD)L_+DPrCFlk+s#jV z{bWNNO>ZrJAc^GLJ|DQ~9M+W39-~-La9sS>4vAYbr%nD+g7?m+Opk*9CeSxDXYw6~PN=*N$xp>OK(UAX)2 zN#XxH#~1*v`cbF=isnD?p^CpK?7>ZDHiCjBVD8(+HzFHZ`d@Ji1m+h;793 zGYpyeFWh0j$$8f1L^#Jzbo%I33{}-HA6Xo;g^Ki8KO}y#HTFV!-v;hy9FHJUf!#X*E2m~`b!M>BT;mqy^i8pGu9*AMGY1j$* zd~z>IumN*d>=4B{*NfaroVm+zEB=xye24DW)VF$u)#$cg;h{u$aO~+F1ZmFfYn#e8 zZ5;f@wp)E1x50@5^_j*}QZefyKtF#T8YtuI>7EO1YW_v` z`6sac2W^@-zlTsPu8)DuH0zqO8c_1MR zi8fJcPnk1`BfPK4G_JX=DS70JR(Y7Q`qkQPV%+2{KpuIUx{W3>!5Q3&{Vltj;YV@= z=DbaKokMkr_G-pa9w$y#SnL-#rrfY2rz*N5q$Z&4trOkg7X{U?nyP}Sv@28U?H=Wa z^>RA+uRR*JF}AI8V3LZk=iL3C2s^Z!yy1D1SbjgTqWV(DL*HrZ@~=9l`*c)kA9`Rr zPClkJf;_k}g8A6jgR7x>B<)R8&)kM`3XgVYjeTig>Oi!wq+X)_J)QT0A)$RRb{!R< zSL9l7uBdd7?!At3g2a`lo9H&CvSj)_<>gn;G`}ss>+1FCP0l0Dtvs#I_uezo7e00E zlTI8h48+joGz>+lB~z&14=^8GQ@R@Pn4#a?cumkmrS>=V9OK)ARG8)?(pYWf>Wo3fdfU+*<%ous?{>C2L1BZHxBIqTkc;`ns0 zmu4j&@VmpmvFF*eC;py$RzB() z(|hgYU-l*mm3J`l>|^QH!Ta7mU_&9qI!KaR8*oF%&_WCY%CjfqA!1z(osOh!6#o9N zE%Ak2FS4zv75GHWGq|edn3wE>yy_-*_a@er&_KarYZEPi(nxFuQ`pZ0GqF`w<95R` zkXpU*r*A);XvyrK>LaVYzrEDd6y*}|pnRTcBVO+Y&ceP~^?{EK++Va2n_&?QCBAp5< zR!I%C^>1w6Ypew0^FNtO|9l+wy_L%-hGGobwL@L%K_|A}RanBRHlLY8;J_adJ@T)_ z$Ka;EQ}G6sAsfCbtA0Q3yBC6zBwo`%(dNKE?j#|dTcw}OXJ-39#Rva;KKGxSwVKe6 z0L~&2h5E);6$~0%<~;wYjpZ*dDE{8YWfAvpZCpwLoIDEbRo`uheb*Jw{UK_|dTR@8 zIT3(b^J!d64dD%qIxk8pBRdUBKXm&W+qv);+J*@uC|$s9*I8i!#O_JqPpw+)Fux}j z(BZ#3jS-p^52evbM*ME-u{tg;}8FjwHhS|AvnxX8GyW1 zWr@^KZq;YVh@wgG5YWKieV00^I>!M#3P_MF)T$9d?KOD75?O@ONnc8(VNO z>o8{4V4enD3qnUP>qG#5xh@TmA^hWgzkdpl>f(wYp`1Xk)kE>Dw#VPtdY`rt79bHc7`Da2#f6fm=V&V^}9W{3mEmJ}$M z^z3j)47#vPIVNV99fS=%IoQWj=*t4cgsmxK-15k`N4?ZXizDPp&Y-`D_9 zd6|i&8=sHr?HasBuLz7$^BAdWHEHa#YW;Z;eugrr13g7!6$G1mQL->@PApuw@ghg} zvV50PCxD~+=UXrlp2wTPLtt{m2@blW{iRdczb(&Vd7#z@C|Rs1H30sRF>XK@4a(@@ zxirUbY`S;GkTP~$z zCA>XHCmCZ73lF>Y4ios6_+PsFVgE?PE%PrIpy31P$1(2)DYmSZcbL_-9P6Dw@9Xdy z<|F9jbz0#!HpgqwjcYym>+#CK;VhqZ7=4|YwGHg-4B2JCcLL>=Kh}M+txgsI-%T z%?mJ1e=otZv>4%(k$zh7y*p2B^!Fk}8?8_kib6l2P%rRxfk8n3l6%`oFuxeg#wE^|{A?WG)mR zq7{Q;w#qn$E}*i+lR>L5e_aer1?!!Cf25+qKS|&RYvyzn40)g4~f2{)UFYD?J?2oLs8r&eD%ymT(sH&iZ`piTI_h7|ztsO^7 zo$eYs3S0a)o^c<*6o?wP{s=%&n@++p9^pYoGJg?|TP@ZvzVyqieZJ{$Y?ju)zsFi? zzx~%kBY#~~i+F#*um&Sm&ds;GqV{H2Pjcpxf@XN@FKVSH-ba{S>uq?S~r%?6)QgLj>(6&4Q;dEUr7ct{& zZd@ViWC60VsZOf)<;$wx1&i}h4bp`ZnZ|(!#cgd*8S{Dw9y!CdZ;$I1<39$cy~ut2 zzkSZw|1nCo#VR(YP6V$bHz&5OyswtP?V9RW`<|8T6tYpW3RHyC9x7DNG{7Z40pOiJK(Pwe9fckeTpm z4!UsTMyBXMKyMl|$w+l};2WDxgiC*KlvnWc4Ihn1g>FUn)(Xn!Dk6satfwHiFmCZt zX$JFC6eSZft1=&%ezE5YnX$$?_^~#?j|Agt_K*Y7wxpWcZ*3vZMguj6TpzSeiYrQ* z@o%a&2ECkF>H`eO_^_rzq(vu@(`O6On~yNxKKF=RiPx(0a?gT4-Qlyx@{+a z?ztmxfpopE5U_!>cv$3gt%ttu=Vo6VYs*kP*C{#krKq!QsI88!*OhClV5*9~kIZil zg55-^g%r%$gvBLgyWP<4oLZQi+xDj5jN*sICYk43BmtF}t8rItOPfA=U>u86I|m2b&aFt)fqs?ZPjPc6$(!k2VY=8irG|OEv)nsq4?KmY0GOE2GKEHM zr4}8oK3s*CdVy?t24Qfja^=>U?#ecq{31~yRJ(@DGfp%L-3BR}iL5n_0{^3 z>~))ALoE51lsB<0gURhd;I7Q+mFE6}%C!YBfx4&)+%97%88*>d7O2rJj=+Xl6 zAbY#=zNBHJi&6YrhZ5mR%w#ECX=TdAsi9sC+v|D{>&-=6bvNwkB46sH@xUWt(-^Yr zLM2|y#4GFV+D42(r9eKq8JXVxI((;DhJOjyZWk}8w9xbS%h-IMbMh1mFZvQ%Le z>j$49%V;!U(v@(4vPExrzTFywZ}_doOq1y#mv#Bavbj!od+3Fpa`>oxx3F=nc{~1m z7&(u5s46p>(ja(Gmpj91c>d2;&aRLxi>Pd|gIM1>DmhX^X<*GkBuhF`6V z#{c6Jq}P5x(%E^}3q`}@Y}W6@)WLkY?d~AK%+X>e;_d)#Vlai*fa3TeEhRqpGu~Rb zGMp6Ge(5yc+H3T|W4Rk822-X8k)maF@r322MuZ^a9P@D^u@PMvzcP((OL8+y;wpSWePNU0(I2nqKJ+u_RieDDON zZeUZY%v)lpgjOhg*|@Q_fyGwai@Nj}Iee)+6Ek>gg~iR(hl;|^^ukl}Z+LoKar6z) zetod6UGb%IaEh{PJR|9mt9ui$4@{7z5bM{*8aqJHbl9xOf%*68A3}bDFynI>s zhRMm}#qV)EtR5XnO7874d&gG7Z(5g#kVC&7SY zq|FtJlDl3B?!-O_DWr4KQdXLAFH;-r9qr!qsf6g7lXa2;FabW2sn*ts+A$`QajgoH zF@}=M?@hhow?&it8qJdpl}GGrivt0=w$O^kt2^m6>(x~NyvtEelVSDM#8;l7%q(RI zGhmcepb!Pbtjrb5+PG<3h>}T7MR|YEU{e5>i_a0`Z9^?h+f|pu9lp+Lc;>?khW8dM zdMrNKL)p9k-kB%eQ5ut8$xbo`=TEXHz%sz>I9KjP4xrxoh~{O;(;n!^Fm_YIiVAu^ z`|SK*?R|GtlUtWJDhN`fNJl^g1*9oe1c_XviGV0YK!{2w7m!{Oh{B~vm3|2VN)wR~ zQ6N%7M|y8TN&x8yp#})KbNs%w-Wk1jzW1H?n_08gjQquUR+8tOefHUBpR@P={hUD+ z`a5CgbA^3U?Z2~raTPOhV~^-i$Upl#2~%qjo}9__4J#xhkgAOMI$Y&^zMQH^g5}4A z=8EX*qO?NuHctL44KSJehsANp+J#vkgA_k+$ld)AY+3r=-^V2}y%1F~h?!7QC~#DB8oX^YInaly`KV@qn#n7v)w-f|5EOil|; zM`1?I*UafKMZ)l0V3jYXiBpwsy(Sm!lyI1$*C%OPU72PLNn&)c9CHe4p+8Ye{o*l` z3CW8{t3~<%CRd$k5Vi&>krQOQr}k!X*y?W6PNZM9nNUCs$FTdmD-YKJcIiAm| z{lMOMf}7SS2v(o~r)~f(+npp>q6-}AbI%PrVI?wDooa_fgD-(15c{y}xVs!#W{D~|7^)d|fb5(=|V=q|KG zs#xTbm>YOQOVLZ5{`#@G*LO`*upSbZ%o~F}lq*>{u2aXuIy{;8AP`RENC=2YHW(NA zQ0Tp;oMA1K?>W(~S&O@<2FD7(@-ews7T#x&KZ0N4+~vBRB`PVzZSBr-)PnE2@KY-n zPWr+(%?3#~OS3Wj3`ob_%#Z&HQ%)xM~G1 z_93h>QknA84Y{t>*beRQG7;8*tw1*t9atoD+dplLT;3>YVCt)V{(Jl>(Fd}kN=2;?#-e7kX4){0E)Hy+JtC|Vmbsz{arbH_a}Eu!puoY#5;R|V!q7KDxxCU zg!ebS{8oqOt-rRlUvjW9kUg$_%Uzq@)s7n669EV_^Ok4?!vV)QLYqIgI^V2gN{< zK@3Ku=N#A3aW1u(rd8)htl{S)$Q?BvQbS2mX~*xh&L$0{G>BQW$v$2>cpW&AYgB)w zu5A$yLba&=3PeoTxb=AHzOqK(14?`5=_;h_G?LfXWp`S$a@yH_w+=PlLv#r#bmCnt zi*w^U#?+ikSkC8N)2-I4JzxDXT97e__RN)TlHP$1S-+oh458h(6#$14IbY?f5%x*} zGXt!dOZ9T^>aSm_S(X$zW4-vS%qArPR7!H!*K-WjkA8c?`z6k(G9v!qYBg822w&w& zg6S9v|M6u8n_*3*GICJ?>%Es0k4sNaIMmO=f&3yzOf-N!3az0f15}j|10rA3KCVIX zLPGOL}61Rylypy|f`^CNEY>RWBic{$ysKDSGr0z-q4$T*g`9&(RvNLwF?mpxKJT~z8AVBWH{*T>3c`FG7WxjHtDPh>Uu<< zGR+NYaN}{A>SWGyTKPw~vk1Lu6H*evVTP#H$gkTdz(9f#|De*0_8KAIeKR)ZXnw6Z zgXcBZ2g~Y~_zTGKaUEq2#!kL0C61LyBDN{s>4T*Osk=!Afq+IZ11_xQD$%5L`qk?ND3jOImw-M{Phq7j-T&6vM;ncrIvzXW z?}*~Bn4ga@#YzJ!>56nD%{L?Y$T}F`v?lzQxi})E(Mqu2d98G;yijhLdpYzXQ9=Ccc^qDGYIQOeIaN5Ed0eVw(`QOBGGP(QCSQCm^hsWtwU48OFzcuI zX>rbL8&xk@#2HngCrmvH6#61ElFg;ebi}uf_E3}!3-nefFY1knyg=eubxB3!7e|L} z_xDl`G}M z%o(jvq&+T-iy}n~1rXeX!^;HSG~9=Fw=b!A+SYsLOgvJBH3<5$2~SQ4q9E_ea*1at z^pu_&iC$-EshYD0p7mRh6IH6M%y<#h@=dNgy21_~)sZ?!=+#;c>$=1^YF^eKw77?r)pnIyc@!fie9N_1%Y&g2S8@Ft2js{l z-&{@EU89$26@&Wb@?)Q!@DTsq_dNx^oJ-B8kWQDqCn(b?<6?5yK53Jc%;ooG+ti*a z5u4CxT%r2m#!ikAh9d}T+^VL!8HWOtD;P>i;xt*U*Zi)Kwsy)zlrkSi(qr?sC;7K z?U&+Dj<|Vc)8Pwof55@mr4wrJ^|h=h-WgW;q2JK; z>^Eo9jHfp$nqUnJ-*qxP6H4N?HM{p%jhaP^+H@Y<(#=O&-g!bCjz&P#y7-j;oNOXg z)~Vuc$oPxnw`;wqh~`OCT`KS0R{V%5az>S)@cYg`gGKV6Tb1=!SRxq1zY47p1K`uH z;b^k@8x2mh#O*pi6~mX<-$IPz5*ZUkUh?aK*iA_8G<2V_6QlvJBZ(EAppHD67kBuZ z6mrXB@G9YI^cuE3fHE-OqJO8MY_4ASiHGi!8kFSIf9DP$L!LrCH6+yB~ z6{I1Pt4f1o{Ima;By_=|52vel_pfDN%Z3FW(c)8NLqwMfbs?X}H;Rs28b??HGXgC6 z!ajfX3fcyheWSP~^&!gahUl%7%iJTHk>&MmikJ5^sjSgBH0ID>wSEz~jNJ=Z*meCo zqoUC_vU#xYZ+M+=a=oMq+<>X2;?@P2DT-FP>mST3h|Et5zpmLSa>=N)ZEzTmmzf&Y zKe>9O$24sC1L70_wSj@-h@}){3z2xFg{)2rD{@p-8bC|Ri%G7cC*)(~gD#By9v3U| z+qU&YF#zZMw-ShrfNtC=&D>w)qJ&4hb2tY7Uf>Hq%8!^CIq6hQ+=;Vi!aS3wo9%b` za-f>doIv953yMON|+mxH_lyf4UoL!;%l{B|gHdb>{(x3H$4#NYHndq==O6C1s=D#DYW=~xB#JQoWUncltoKH>~mzI~u zSZb`~!YzJ#b_Suv{DK>@|4VSH{-k8ZJj2bF>6WNczUn)3!@rc4_pM}Xj9TIuJk1_V z6m*JPa_-GuSCA7c(b@fE+2L8SihPbtudy!&afm*m^x<)E0;~0fkj4dnm=#(-K+(+U zj~GkSF_W81Emg|Y{5bylx0nk?@uT)KGRe>9e0goC#F|e$Tz-C2PD^Pq5Y~?q8;cAaA4x#e0##i}Q-`xQCnQ?U%2ODhSJ&RE$1$& z2Lbf+eHKmer^Up<-KSe?YAhp>|=g-e3JR)qTHjJPPOtT0OOAB|NJmy?nS zbIV2t=XSsRe%P@;%rk~Bc-rTgigd0zd^-9fpSbfbOF3(f`^|QiWKQ~oj(qOIxK*jf zvU<8M{A*{TVsnbs#pEu2&Y>m#v#YD63eSY-_z5vO!lv-tI1WTZ$cD}5Q){+0h9x-6 za@%21k>K$5p%XleEGf+fGG^>t$+W=$i>&ugk|Gd&6Pm2fydxn4F1*Pe=`6DJ>r-cX z(a9QJIQv|)p__&ns&s`%)NO-*B3IwgaWtg!N268LKZS|hHaIoD)Vrh@?OV2ZOshgp zuQICqpa4>FbvSwWRbV${gLN3PoQ~Z@J?DS@&t+f#+k}CWkZD})D%T8T2f5R<8wGCv zJ|Mb94j7s$ih^;0;}iBHE@wC0-Z^h7I=4GF&wZnw{Sj4S_?@98J28nMa8ECHR3Tc( z+|f*NHv{tL4+4?>#dppB#%^o9vkgf-oR0+v7AJkBDZx-~$odNDEdZmCL;m>JFf@jK8;J!A= z+WmIkj4B&1o^XGy6Q-hpL_p|EaFGu5-j0gW!aw-46jKYY1 zBAjvpNyv;KT8uRi)ptVIyV8mYHShXsE-Yac@FfNfT307pAK&C?dmbXgJwNug2ls$E zjKb3^u{934O)9p2y||uRT3U*0GGNzWs&aT`%*u5eTuFcXdqBTaaS>NSL{w$;nQS8? z$M>Kv!M+hi!wLNz;EY6i*i8eLBy5J3!k@{3+yJ=KKR&4ripcy|=4Gpi*PD>4fB2h_ zlFQJ|4JhzvFa5i!DZeOiWL-X`!S#f>Lj?lg)Rdn52bGkbsY%6P(U`}F^j*a}I&v?H zkHOR5hHwK;UqdsaCX@VfC%w|Su?xl`=Rp7ijy$rcP;;%4sN!Ae)xJ#US^gCot?wvq?FI`R$>3>@-j93`%KqGrcKcGThd{H zQlvBjl;b`O(1LP;O;Fss>>03+(w1pRytNT>QrB~rW$FyTGWUDL+-Go!iniD2Zj5bY zA1 zm!#qU)db#Jf9CYIs?!j}b7S`Qq(Oet?{NRjj{k^sMJYW^$nMsFHFCq)0LA`4V9mAu zrZfWoXRV>L9St3>UPDJQc?c^!&S5QWYD|%pDF~tY5|=vr>q*Oz52xdGga=WgMaq*V zHvddE*0=BcXIDmAWm{SPDVuiy_E+hbP zoe7RJh7e8QKPMKSDLy+~Ou9OSKQzUtNSiv~7WvW|h zf6r3Ng-hKwnE+j*b(NOj`zSnD^CIhss&yZQadZLY)tR0WL}{`2&9H1_ad1BEfq{tU z$1oz`-Io9jFGC*V& z&Cw`p(ScFA+|Cm{z>_0%WLcH*W<+y8*W}w>{yZ5(?(>E7uONiJjz{H zXDpO2v|b1yX~`SxQ^N#FM-;&g81xsz>w;IY!5FJ;DT`kQd8g$Z`tIhnR~v_)R~vnP z!dTd+b}QeW6d*r{o|W-++1K`BJOp<>Ikx3(AKk#3{3OG>c-obn{|xn|Ae3D9ebuFtQWHUd`c>2Kqet_URc z{38N;jiBo<@|UjvV0_*cZ}~ zyQjo=zxm#Scui*N;eMmrw4FXUMr27zzJ!a4xQCmyli@3Df|hp#Tjt|}#M-g3V=;rT z{-C0p1C>kHzv&y*1;A|xTevBDY##T$I#{mN>RsH_uO2dA>e?8z(S021DDEii1FGa2&q#Q?4uB>yyo>_yrK?P0n0DWhhANT#_y@J?JIYRK?}v zZ@Crd+3n+*)zz$BmGaxIpv_PoIZ`N*S!@gEaQ;re)8B!%FF_`H;Nq z%2{YY3IPsybSyCg%p|oH+W#}IMOz?NE+t=4Kx4xW*Wjea&J*ptXEZN z;glMoEWT}e&LY5Sk9;Y4NKCX$oLVwZ&wUe;Y)dt#!i=5u{FAoTOmHBT=6-!0=9#4H7+Lu zF&afF8>RS#`eu1s_MK#$lozuG2JW{7FRQsKBEfz>H$CpXFp zntJ$>DZ@LJnk{>6Q}y9?lclzMHOPTXY1i_t_Dr)hzgpJ$)6p@3gBb`;-=U-ksT zCD6VHK)m=C3R4k|^hIzzcof;~3>N3{VJO+XOti2w3i>kRB~Z}L z$!`S2KeW+TML+Unu17TdK?OmrQBJym!{e&p4{gU@joP0g`YEEHTJ&?F{kPkqAxI<{ z>P$$p=KHKpv)xp+fq$0m9UWzu@@VXc^7XoFpB57|YRRp@NXL}q^g{@GE2w=}*#lRI zV-;$Ko6@~FC+oaC#xv7n2##;T5njg!t03za7V$g+bCdUyP%QNd5*3c;)e&QUGb%G^ zjzdENj-2ojd4L39h1%Z%T6jS}WRkgD34-;4qieN+rv|E&i2?z-^%Va8t;jc$g%T*9 z^VqT&5gSR*LZkO=CvLk%J?*Q6kiy|pm_vF{c7$~gg&IH0t`H36>I5#)v~yQ$(n z7H~4*nxS*fhs9A8WF0cDfy6*zFk6?dL$a$F%rLx#L{A6}t7r?A>3CgJ)@H~aY3?G9 z;0sUP;NAtewN4PtirsbvH7%LQC|n1m>~dx|NqU3%&%^tZgtHv&9`{hsH=ckAFztfMs zcM4hOTSMQe61yW9!!W1gc408I_Tt_rE(bvv;VzFFStP^Zul#UTaQq6wAnUG^IS$+x z0qWW+SOjf?rJ=T9dTKhw?ml;_r6opRjcFVDtWxx4h~MKO3^T_ywO#J+3| eb8%EVWg=lI&@Iy>{XSyJ$j4=J|0d**(SHF>jwr+c diff --git a/docs/source/gui_tool/user_guide/applicationoptions.rst b/docs/source/gui_tool/user_guide/applicationoptions.rst index e2daa6b78..c6221ff09 100644 --- a/docs/source/gui_tool/user_guide/applicationoptions.rst +++ b/docs/source/gui_tool/user_guide/applicationoptions.rst @@ -11,13 +11,15 @@ Application Settings Window Encode with ~~~~~~~~~~~ -For compressed images this option selects how images are compressed either with CPU, HPC or -with GPU using shaders for OpenCL (OCL) or DirectX Compute (DXC), this option can be selected from the GUI using "Encode With" +For compressed images, this option selects how images are compressed either with CPU, HPC, or +with GPU using shaders for OpenCL (OCL), DirectX Compute (DXC), or Graphics Hardware Extension (GPU). these options can be selected from the GUI using "Encode With" HPC runs codecs optimized for vector extensions and SPMD processing on CPU. *Note* -Only BC1 to BC7 format are supported with HPC Compress, if you choose other format under HPC Compress, they will be compressed with generalized CPU instructions -See latest release notes for details on what GPU codecs are available for encoding with. +Only BC1 to BC7 format is supported with HPC Compress, if you choose another format under HPC Compress, they will be compressed with generalized CPU instructions +See the latest release notes for details on what GPU codecs are available for encoding. + +Graphics Hardware Extension (GPU) is set for BCn codecs. |EncodeWith| @@ -27,8 +29,9 @@ Decode with ~~~~~~~~~~~ This option is a drop-down combo list where users can choose to decode -the image with CPU, GPU\_OpenGL, GPU\_DirectX or GPU\_Vulkan. It is used -when users click to view image on the Image View window. +the image with CPU, GPU\_OpenGL, GPU\_DirectX, or GPU\_Vulkan. It is used +when users click to view an image on the Image View window. + Reload Image Views on Selection ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -36,10 +39,10 @@ Reload Image Views on Selection This option when set will always close the current image view and open a new image view. This is useful when an image has been processed to say a new compression format and changed visually from when it was last -viewed. By default, this is turned on (check marked). If you turn this +viewed. By default, this is turned on (check-marked). If you turn this option off then the view will not be refreshed every time you click on -viewing an image from the Project Explorer. Advantage of switching this -mode, is that for large compressed images the image view takes +viewing an image from Project Explorer. The advantage of switching this +mode is that for large compressed images the image view takes considerable time to decompress and not necessary if the compressed file content has not changed. @@ -48,29 +51,28 @@ Close all Image Views Prior to Process ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This option when set will close all Image Views in the application, -prior to processing selected image destination settings in the Project +before processing selected image destination settings in the Project Explorer. This will free up system memory, to avoid out of memory issues when processing large files. + Mouse click on icon to view image ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This option is checked by default. When checked, the application will -load the image/model onto Image View window when user click on the icon -next to the image file node in the Project Explorer. When it is +load the image/model onto the Image View window when the user clicks on the icon +next to the image file node in Project Explorer. When it is unchecked (off), the application will load the image/model onto the -Image View window when user click on the image filename or icon. +Image View window when the user clicks on the image filename or icon. + Load Recent Project on Startup ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This option off by default, will load the last project you worked on. +This option off by default will load the last project you worked on. This saves you time selecting it from the welcome page or the recent files list from the file menu. -|image38| -|image39| - Set Image Diff Contrast ~~~~~~~~~~~~~~~~~~~~~~~ @@ -81,7 +83,7 @@ Set Number of Threads ~~~~~~~~~~~~~~~~~~~~~~~ Sets the number of threads to use for texture compression, max is 128 threads distributed over multiple cores -Default 0 sets auto detection, where the total threads = number of processor cores, if auto detection fails default = 8 +Default 0 sets auto-detection, where the total threads = number of processor cores, if auto-detection fails default = 8 When selecting this option users can also view what the host processor has available and is shown in square brackets as illustrated below: diff --git a/docs/source/gui_tool/user_guide/imageview.rst b/docs/source/gui_tool/user_guide/imageview.rst index ec2f03517..31844a601 100644 --- a/docs/source/gui_tool/user_guide/imageview.rst +++ b/docs/source/gui_tool/user_guide/imageview.rst @@ -85,27 +85,33 @@ the application +--------------+----------------------------------+ | DDS | Direct Draw Surface files | +--------------+----------------------------------+ -| KTX | Khronos Texture Files | +| KTX, KTX2 | Khronos Texture Files | +--------------+----------------------------------+ | TGA | Targa Texture files | +--------------+----------------------------------+ | TIFF | Tagged Image File Format files | +--------------+----------------------------------+ -Support for EXR, DDS and KTX are linked into the main application and +Support for ASTC, EXR, TGA, DDS and KTX are linked into the main application and cannot be overwritten -PNG and BMP are supported by QT (along with a number of other formats -PBM, PGM, PPM, XBM and XPM.) - -TGA (24 bit uncompressed images) and TIF are supported by DLL’s in the -plugin folder AMD Compress\\plugins\\imageformats - Users can add additional file format by placing appropriate Qt Image plugins into \\plugins\\imageformats folder. -Note: These DLL’s must support variations of decompressed images in -ARGB32 or ARGB32F + +KTX2 Support Notes +------------------ +KTX2 is supported by a DLL in the plugin folder AMD Compress\\plugins\\imageformats and at the root folder as ktx.dll +it supports saving and loading multichannel images, BCn, ETCn, and ASTC codecs. + +The following custom image formats supported in Compressonator KTX is not available in KTX2 +ATC_RGB,ATC_RGBA_Explicit,ATI1N,ATI2N,ATI2N_XY, DXT5_xGBR, ATI2N_DXT5, +DXT5_xGBR,DXT5_RxBG ,DXT5_RBxG,DXT5_xRBG,DXT5_RGxB and DXT5_xGxR. + +For ATI1N use BC4 and for ATI2N use BC5. + +Additional support for universal textures and streaming can be added upon request. +For specific KTX2 feature enablement, please file a request at https://github.com/GPUOpen-Tools/compressonator/issues Image View switch between Original and Processed @@ -140,6 +146,17 @@ To change the Processed Image View back to Compressed Image View, click on the same icon |image115|. +PSNR Display Feature for GUI Image Views +---------------------------------------- +|image434| + + +All Compressed image views in the GUI application display a PSNR value for each MIP level and cube face. + +Users can log and compare the quality of the compressed images and the effects that various filter options have on mip levels after processing. + + + .. |image57| image:: media/image59.png .. |image58| image:: media/image60.png .. |image110| image:: media/image114.png @@ -150,4 +167,6 @@ on the same icon |image115|. .. |image115| image:: media/image116.png .. |image121| image:: media/image121.png .. |image131| image:: media/image131.png +.. |image434| image:: media/psnrdisplayfeature.png + diff --git a/docs/source/gui_tool/user_guide/media/appsettings1.png b/docs/source/gui_tool/user_guide/media/appsettings1.png new file mode 100644 index 0000000000000000000000000000000000000000..df0581a40c150f92908a6fd8463b7460234cb86e GIT binary patch literal 7488 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

`xbv1A+6=mn{6TC3(ItgSW8WvKeZJ5 ze6ZM{Z`G%?RBU0;=?0fgAp{J1+d+1u{Du-J+n z@2jZtE1XSu3ywRcuVTQ*E-R(=!`W-fafA#kiZ|hG;uFO*_h=N2)^|2`apBaDE`6vv zi4K`?Kg`(AdwLSkx`txiVQYke`XBZi#_jts9fTe+byAUh1hQ6;My~ty zOJ`Eb*?LH(yBBj-3zPW%4YWLR9+WaU_&?3HSBNy!f{d!oasC5}eu*0YE1z&Z(jN)QKpt2H|H47c zufkFsyzhr<2{^^Jx92=T&Wf5xEt!0*YS;VJ_3V~nB=7}ie$+fL^qSq_3P zlJBJ7(K)~CxA80&A!ztW0;$UCW#oB&UbzJIU*{Ysz6{u@rPyF$-~lVf7DDO3E7vBD ztfaJiDd>XeWSyBoW_eg{l+Hix+q~`c{7y7yy?!D`p~c#93Z>=FR@uL28~7a=KElA+ z-R90In`E%3bTeYAAtBV?PF#{~-D*mbr!j&ta_0st4CXc9Th46q;P?>`A*BJCf5X@h zU{ZvooOhRv$K-r!ZgO!dxo&a7K62!w7@L^Y4z+c+NV{hr)W%cBJ?gQeZCr%o2C*9< zlEY-K43ypV`rsVM7VaQL>wp`Rd`FDeb9j`dTgoitdiZUiYQn!xaY8L~097+)`sjtG z>r(3p9ee#M5v((|I+E-qrN#Vx?ofq(daXx2!g0LLb!LKn?kXaoUy$vi z(Y1uFi?^>{al1Li+6!LZ!j0W>Wx+v!cFY6chHGlAR5V=K)9VVb z>}?%-g=RBT-emrYxdPVovAaU0jybs1>da^SoZS}(KGs-DLL>Qv$Q*we5HBU+08+$o zR)ZO^F?LR{s0@n$&JNo$IDWf4i02l|HX6m~asV)qr}EJb*FUoV9?wGpjHK2ZvnkK; zcwe34I;#x@6#k<_MPFq+_TwqGq{JGFRpyY(Y&oSN=39pEZUu-_o_bR0+y?=Rv~1q| EFJjf-=>Px# literal 0 HcmV?d00001 diff --git a/docs/source/gui_tool/user_guide/media/appsettings2.png b/docs/source/gui_tool/user_guide/media/appsettings2.png new file mode 100644 index 0000000000000000000000000000000000000000..01ac7702dbb7ca98b4ff9bf859cdfe00af538810 GIT binary patch literal 7531 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 G{(k_ya00pj literal 0 HcmV?d00001 diff --git a/docs/source/gui_tool/user_guide/media/csvfilesupport.png b/docs/source/gui_tool/user_guide/media/csvfilesupport.png new file mode 100644 index 0000000000000000000000000000000000000000..e28faa8cae665a36a089f40076a34a35412fcab3 GIT binary patch literal 48513 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 literal 0 HcmV?d00001 diff --git a/docs/source/gui_tool/user_guide/media/image2020-3-26_14-19-1.png b/docs/source/gui_tool/user_guide/media/image2020-3-26_14-19-1.png index 8e2e6fa91a699ed521235982f243f1508a56e2b7..2635d88753d7b43527d4e385ec0e48d9154f614e 100644 GIT binary patch literal 11856 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 literal 12252 zcmZvC2Q*w?^zIl$5JU;0C(%36dqN^IdZLXoLNJV8ql^*}kp$6OFnSoH4x+b&h&FmB z5+%Cm-MdnL|NmR>t#y~V>$>-xea_zdJ9~e7hiPl7l94cwfIuKJwFfG?AP^V|+!tNG z1pHlZ&*}n!*g$G3cOE~(TT9jW;Lz@|==FX*f|z|unPOExbC_+^Tpco+pls8>MlNbw zB73PbjG%d>BZzHW)DS<2`0{R3&+a$o>yu0BJ{FcQto)cKnB;u20>z)Dl1JJsC6Tfi z(i+wgq_LP7(W{H2?TW|Ib|VTl=^Ji)XQon40fQq20hp>4FN2}6DxcoGhQ)(Bcr;4? zuF)cJi{Uj?g{e+H8KE}l@1=sLM5_4TJ4!*!JRtbm1H*!Q|2|U!USR#dUcfP8r}a)> zPRy0xI?kv4pP?f3@sw8Jp_f|!8(h;AcrvJZ^;qZMbP@VN6?ax! ze!M>KgzTKE^B$1j`)SD-H0%ehwv*XF>__{#Nn$u1J?G!oCRc~03Gq~jd8c^TT5jjF z3SY+-B-ez~jD{fXxK#h{4t)?9tb2Y`HEsF=bI;bazmO2>wK44xu$LFG+`L>hb2>q6 zetM?dxLCaVf$?YUmCb#^tDKJ{QwN=~!fxp*_)uh-*TR^-JTuudVNqufakui2?6`>- zm>0Iy;hzG6rb%K03*3vfLJ&d2^}hR)stPBR6pzoBO&gN}hr<~qCuh!z8$HgqL+GN9 z-@L6()|h0~46L`%g)Rg@J^8z6~EA z%X$dljuw5-R~5EqDY)2jqR=xZ;a=5RekVULSkD2U#jo9B(et;AY31zRC3bG5^JDZ_ zzTE9#u}w2~rH|UXYpR=Gq<@stSrcCJf@eP2uUnqlW1BLcGje#|WNM+%h?eG^{rL-t z*S2a?RJ&i-k_1bg!pL&ZI_!t}D{9IV944xR|EjK4UkMM5;3?JGs3M1gO*YOX8;4l2 zR3Alg^O)kXd-a>-12r!B#S^+=L zmd;OOe0LNTlAH8LaC4m@{{4~ENOGzkwK*R4eU00PavwZET4g+@&0Vp(%)%ofH8l>m z=W9YXb!2BGVP4cc+l=$byJo$aD}=VXjV*{$n1t)7mGJ&wOE&*V!u_8GUobLsL+X@`@y*E9A{j|mEC>UIppaz9>|BjThsRRgIgS?EBC z0F{zLJ)4WS5OON-ihI}y~NMW&ka_&tmmCOzOAfy!Gbj!C}Ws-g4wzY>h_dpFz zkIFJXYy}#syk&d1DsQ1Sr2)0mGJGf?Bp_JY%pb>@GTVI`KC_ZDHTUYTfpKL9cIAT~ zn<_?gdLNwuF3XSuogW=RiNVN8B|LPJyW0R#%mYY3(BpxlS5Q$%PDcVo#1|Fyzvvl+#|=Q1LZNC>L ztcItAP|wnU{Rf$Q1t3&~LAC9be@JVtPNIUV2{>=~5q#+Yq!38)4gdF#eJ>G607Tu? zMzo3N2V!j^+6-bo1iroXHl$@t`NP!T$vM zhA+DXtVXpZjn~^!vQhl+?BDPwwt+(h_q~e#kC+$3jz$3@$x_A7T&mok06{Z3{;aDh z|ELLy+HFa&j-X)pU((Ve&9D8$#|GX1!^f!a*gN75&j{cVt=&QgC_4wGfHKHAs z|2B{a(6#K)K=5J*mJvuG7Z8#~Ee`14AGUag*tNmJYLwxh({}?Q~Nhff4$o_lt-AH8kMo*Oh z2smd<<1c-%Bi!I!?1V0U zIQfBAq!>(x{1%?0d%ot4b|KT{t<**U<>%{43X_KBgFbX*c}N>9rieqHhfj@5nhx{h z#r3JI*x8ow9e+KdM@#=_+CTG~X76+gG4h%iHN9gQ6wcV3Z@!-}_8*+f>y$7=U%@OEct#Jgd4$hKa_vpOIy1{llBQdqAPP^G)!#0}lt8gci)DYE z-TB<*=OarX_ChS-#%CwE%zfPpU0~h-c(9dLmMevF~6Ov z`CT^mrqm#=oLitS;%iHk7!`

Iy5UYo^oe_6>=nFk=FJBk2KyfMkB- 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 J=&t$m{{w|8TxtLS diff --git a/docs/source/gui_tool/user_guide/media/image42.png b/docs/source/gui_tool/user_guide/media/image42.png index 3ffc0cdd26b7955937f442d77d57b0cafd2580bf..8f9b56cdee0e5311cbb3c6b938d3903e4e9ae0d2 100644 GIT binary patch literal 30351 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%0D(oLXgeqc@dkkU6*Ukq2yN+3$O$+tY1DZ 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 IzA*CtZ>IiCy8r+H literal 24131 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 MYC5WAPp!iK10~d5(f|Me diff --git a/docs/source/gui_tool/user_guide/media/image43.png b/docs/source/gui_tool/user_guide/media/image43.png index 2a87be937e944a58fd7fed7859e25633221a047d..5118643b35b260076eb58b6c39e41cbbc324bfdc 100644 GIT binary patch literal 52043 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

&Rwqegb^-LxV2BM7{Z%BW(WY7yD9 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⪙t^8EskHx=ClYLJ1| zET)uQxY84UOY6CrS>{%GoVZC~acX3;B<>B6VM?Q(K6yCOIo(Xejudt=z8bg+*y!E& z#LFBfI!PJIc{NU$ER}};$M&zOG2>1La9f+7 zK|8kvXlQsorPoLB(W8Op@h8zJ_7&(9Myw990ArOLrg#CSTVi6IND~z!qfHB}s3+R> z7|QZeeEnZMivKMp@h?Q60HR6;buROd5OIK)3W-Ahn~)ggK3QKWme|t+Ex`+@V(~Gn zKT3N1MbiB~-;tuLD2W>ySsrJW=r9>)CSAl6mdBnke|B*?#sn2oL z^z0w^WOhy_L{^bGrJly8-h@ZCVYMfrYv$;b;8K?!n49ej#roLdn63^flaWjVd7b8F z8|R_$8PtHpDBQhJ>@;z?t&(4a76vavxPw=YIn{0yPIpKh{-&r{eS+0Jfwp6Wy?nei zBN{%LWH|(Ob(CQ_%!;IK^PXu(_d%Vz1ytK|D*RVQl1Ehrm`KfIY?wB<{7rexNv>G> zmaesG+Pxpd+`)i7V zamcObf=pc7-zz%1ZLlr@YEccIG(CS08Gv85CEcLOu`n~c0)6uF&%Ko&(P&dXOc`yf zK1;lN@dDBwe*P!YTQKL;iP)A9mNHt@MRaRJy}4aWY(|V_m#IKtPWF}o1KS2|)BU6p z6K3%MsSI{Meh_(VQO3yRLRVEF&*B0oDa85V2*GPT%eK^JYhwYU8|b-2o@O1v^O-uA zAvKNi1i}?q7B}#fHSjv|jm2#b8E{u>*(YBPQt3nE(SX3P$T@o5mDp9WTc+vzS``Qb zUioC^*uaAtFHuN?-&$8G{48w{>h4f!?+N&~q&pY=>Mq{go ztCAAQWBtqpA(y*2w+c-xe zVk=rV9w`lvtlE1n5Xirp7CDCSbM(WY7H)q=>&hD}!#Ni3Irx^CVwUs~`(?-O19Z~pIJ+!l~2|ZnNo9P%A%ibh*C$XVkTJ9Oa z!h|%#H3*0k8BU?133%pj&QN-F1T>P&iso)nrH#NM6lhdB`=M&mFm)ZUcyj0 z-&O7{RC?PCE*tr>pxlvT;%kSHU#%4i#f$+|U1tvRU`X+h4b|M%ZZ|Q~E@9U_N2U@^ zvI{)dyYkf2v5zcP0!BLXPdwLf{G?9GfY?q}LffKdVBHHuLrL*6$9+Xh=?E$QW1hrv zguG|1gyP5d8KyyPs2Ryuvv2LcSBTAr>;OtQ9-5VYch4^(xt!~?x7wWc%!~cD-}OZ3 zm)GK7_X@xYk?yArrfA2z#A4HC$zE=ig)8BF;K0MBVZBYHOzI1*fpR@~$%bsJcZqdP zU{^#sd=X&_ni?HX{>CD$XyTuL7G{xIuP!}eT)R;}Wsk3zsX|naS+Xxbo5y(A8E!CZ zPiz)~@UU-&#(TMK71dYdO-*IaYa||C0`I)(d`B3N_hjlV8Qk8MK6y8l0<%`#0zpc6 zbJ^(iD|#kN2@fUQy;qg+v-0qhFGtjjbI#^Y_9eVR`5_)quS4HyKW&i1*;A$R7KFBn!kit*PC zWJ+1Gno*okRD`R8QlE|IvUs-6ERt$js@&pbCqOSOTbsLkqiH)ekLOICQ}PKIVaGfE z&dM(vjMFK6c9H(F(8njxfz&qb`7d3%9=ZZI%Og=5MhW!$0WV!_wtxkak-*};a~u~{ zxTr1gKwM7#W%XQa2EkUsvzT`!m|3G2X0kF`S?bd_@8Vv7RM#NDjNXbBNgGUlD^w?s zdDor@Ej))0x7V3Q8fX|tO0g3b^kiRzVZSpafK1crmmTZX;L{aYe+-9wZov*2Tu?qu zvXA%k36Q*fwJ`Up>CC3GgI9h)t!L8J0)xN=cYzMR$+mFK)lTT*!LI9ySB4C zaelwG{V9IG7M&Z+Vn=_hfX(lWQ^3_JiPXD+F6s*%^)*eok?6vr@&>;=b#aY+$1S;? zX0O{Mg>&OEs(sP)@htVNWHA|jnfhk|3p%h$r~NqpNk?}a?b;_^WtNFm3k<$M?o(dD zZmn9Up$vbW>ps@i)}(<5epa%!;TDY(n;Z?v4W=ij&Q6NvjguG1gY~`ZtK3dA%qgn! zqu$9d^2!4+MA)&nMuuj@$GIV?*jQGqb$w;*8Nsr=l zB)>U=&`bAii9OlN1e2&b|Mo3mQ8-&0k75AkQDT z8B|$1@BH2@GCZ~6B?Pn?8x8P;KWudgNJ0ysepopFhMRGy`NNAkYE|Fj&)1GayEMUJ7TWHm~H zoZ4{!^yJ<#bUXJ}eYM%vN7o-|uUT2W0`C}zNXMw9T=1gj^0P z%Z2NYztt`7>%g?sxZdGGVpb%CLC()W$jYWVhxXEOc0(~)Cg%p?%RO=}A$m1+kPb*F z^5Hd#ZEszhtpT-o>P}xIITf@eaJw$`zn55z0HM-Id$!{brTNmmw^l9IH~Hq`k{n`_ z0`x;N(&S_D?x66Qv~|z5eRxBJ#YJvHZjwT0iWtu&qohpCsz6F_eq)P6+lY(noGioFJEhLoP$;TXJDL+I7xt zU#n#eSGN@0qlN)CLJAh6v@|!_y+LItFr| z-0{$(?YlDP=5)hNLN%Fw-m|jda>HI|aVhM;WMz`2Evn8_46+M4 z$aP(S$VaDH*I$+oBy+oVbRT$;ehy@jdYOl~P)fXNKfj}&><0vh(pFh>hI^jdw_rvK z>FfEDluMsIGBw>q^IN+L#H0z{;!D3^{b zHL@kGxS_siTklgchdF{Jbu7MU4Zw@3%PrAcuG{VporyXmng((>AtweA?*<#nHzFT3wq%<^OcooetKB6_4I8P3m+rz zp>@4&2*YH(ud~cMJTt>cEVD+mRs2losZ-CP+D743AOk>VqLa=Xi}J4gPvw+2s9~2_&V>!lEoj1hH5mdL-p!^n#uT} z17lc1;w?|&`kPpz-}mrV2)L_Z9al#~uN6jA+l|w?dRN$LO;tA8m+jlRb$iGK1Uj@& zkalzhA_HApm*9iyV7*nUPj$_z7m=9?M0VmZg?xkyFeR8gPsT>NM4?y!*`|)HexoSO zdn;a|c7<8i=ZA77cHj;w;kVC$ZmApTDz4S_o`{>$_AWQQrn&XG9-tae)4$!3TdaB_ zPSBz&T4&JaJ!1@)|aw@{8=WGq#7RLsQgZ9 zdqMW#xf}p9#!k>Hq)z=v)|n)Q7d89*h0UR(K_kWoX-pZO zENwnn?$0-AKIC)jVhMQQ1jJ7fHc`*($MHK&tC4-g%9e)jg0aJHG3o}vm%wAv;E(E5Uh4K&A`u<9wr#rC znvtYHGY8Zxqjh^JF??HVbf**&A#&X17d_xc6GZ@^#|fS`#O}r)q@mMhTaaN!7tuQIHO; zMbFoe*+qzRWIiqWMG57>+ge;j?#6F%))otULO12tf+cms zUdjJU{bGuJM8?#N`~F@*wZlGB5Xo@W=T;^9uLvu-fGVYK`*Dmz&9E`@%M+7i;*OVP`ke zH=2^!aqM3`78gug%9H$W3Up#T_p8RX9W)K}562VMipy})^aF4ce0ORef6Ul+@%Ct2 zSSuWY04D1D4||p(ccg58(UN(Lp11qKm(t^FJ(!K)orUv19T5Dgy|hlzI;BF3m@e_d zYm1{kuySH#$$CMNulpU&%gpHzch#cgTDj3aV}`1B$}cRUnDB4F#B^M}p)(S{?N8c= zJfD5hl_7M{!gB7B9D-t_>XqtEQlfbaPQO~IPAK_DRjbW zO?paTF}lispBeaq*A&zwC4TNJF|~Rjf3IUsqXrG|sjuQ-NoHvvOdIulNh8tWLpQqP z2J_0)7d%Om%E)<`QG(i&v{ncNb~F-6$uw}QyhI+-7r15FwJ&%JLi1{XaqaN8&U-Q( Ub?pgRKs7Wf3hIww56s^D51&SQ_ce+W7$kyg2oEH<1a}EcaCe8`4ub_4Tmk_S+}+*XHTdA}?lQREdEO-N z_xtMBz4g`o=T;3&ovG8^r+e?-YxUmie3O?2VxkkF!@p1YdFHeVRire2OmR+MGOb` zkRtI(SlLzgFar*%>MdLSl@;#GP`fC?|KBc&1(A`FaWy&E%+ds&Zj4kcMOyclPbBeU+5~n; zZa|Z!cH0`Jo!{Q4zL?SRBCWP+Ncu|G^|t2+@V?eu8ilA)<`ho}#9HkWH#{A~(dCS$zl?N1Az9*zz@KxE` zFG?_0+AbESqI$7ly$W*rxEspyc*_ZK!xfW|l{t_V3n%)D~l7a(~@NC5KouF1C9+n%yZ zNZ$8`(@s&tk;S88DlZc+|8J0;m%ORHPCl*7BwuUVTQ6O>rvKXXJ*t*^>8NjH{&?8e z0bkjVo}Q3{LR#6KAU(h7mc|`!85}?r-eX2mYNzHMQ9A>{WP>I4F0W8%wJ@SSh}Zk+ z>kEhVqDS~EvkvRQZh<aks7asC(+`i7FkRbye@Dp}c5!R=Q@P$w#-c)P&; zaS=Z*GN61$Q?!itBM?d*9yGT0nfx)sX%EoVCkpDLF?%BIf-I#>!Tl$?Cy zV=-V;;UI0Kbzy;7)-v;EJm=?;B8hDtDhSg_51I;T>a$Ci*^-}rlJ>y8!#;s;DRN%R z`D5jWd^A+u=~}L-ApWhXRP-i+e2_we)h+9+LnwpF)_Coa52QjAmFuv)t2*yBsFmor z-+dyL>@@MwD2oz291&n6Gy}x)h0kj0s7R5B@Ig~vG@v65ljhw2Nf|jMocj3J27Plv zzjQ?tT6!xRVLo)bPgmA?+1mEBRaUOwflyy3M?M{s z#eH!I%u5+}3YLV%LWp9L4K2*E`o=r@kL%iZZTd3b@e`peatC55C1(A^{Z#j6BtPeX z+IG(}TpMLzo}AWUVQWF|zq^+_ywYZ%N&qkj+l~Ac!G|*^5?t#$K%tpZos2CTh76P8 z#C;uL;`vX6c}_{C#2a8E$+g!O;l3h=1bX^{0dqb+n$^mc)LKjBv#ikBeTt0 zsWJVeFZ@8JgP(Y3%*iV{Lk7+w`!nVo!MBy$L3NH#S1Vr6?C1kRALwavd1%ZQt@Zc5 z??k92q!A$>IPYy0jxRQK+h{{{v{8j-EO$*FA9aniR+AW;>w+gIga2M8fsKsuA z8h@um(ib{V`JxRtXUw)ZF}lqD-*$ay%K7=(6KXTYb05r?-z;zNHn*ZaNm2X_C0w^> zl>dhzvNu%nzr*DXxGL=ZKgGrkvA<5m6|Q9b|1BQ>zYLauju+#%+Ubk%fq9U^lNv$# z5HSdc1Yt5d9-WUb~NFZ&RACyju~6y8qpo%p`YhiL`m&Hh`i{{F+wfj&^?n-A-OduhZRuKMQm7 z5}9{Sv^TQK=*avN%a|dy8Ru6%|JVNpl#EOMYq;+TFTcVyr7u+IO8kW-Xqz!<=zxPP z|Kgc4L(tW?kNx zFiMYE0C$gV%BcAPCJwj}7lM}q!sMK@oqo9XVn5!?5?JbMV- z?m`xw0P$^pCkbooI2sIJ7!~=plS4umNSCnI&Feu(xVt!u78KVfQh0ZA-a=x}xy+*s zaE~A|NRd0O?_cyTa4z(aF>dII(nTZ)^=I@naOHDy)G^26Uy1TH0O2#x7g@V=^%Xf8 zf|PNp99{u$7nTq^rkYJ{FK9r87AuTX;7{~;j>^{{t| zbSJj?GhDD4EQSl}{ZPIK+O#x{$)=YcE7>_dw|>cZMv|v}ATmk?^WWYMBn16M*n@ye zR~h37kSM8U;m%841VA7x@_gj1))-x&1GFk&k=`DR^vKqvU>5c)zNLTbZdNn%piaNr z+kDrhD@Q2TLyCkb?&4>}8%2irhPpMR1%#xs0e(q)Pb7!rarYN;7wVRWycKbxm;3o z1Ailyf?UE-5gq!rR|)FH(YuBwqXvArLAJJ}xlA?7T^;MTBjFw`UT^0sv$scriL7OL z^bro*LIV6L@jIO}i~=Ne9l*~)d0AQ6z-!V+#1+rlOfRLQ^h*M63+?uqZ@F%N!v@+uNWU>L`e zCwklq4Pp6)-d*@hc42%?-1(>49=y~;o_Ba?NRfU4>Nvblh&IW5@GCN`I2 zi|u;E-_o`F_3aADpe7h&GfB<)FGQMlb<24<{WY6?;irKAd712S`g7nc!ddx$#a}Mm ztgTzohYPj70xT44GK>@+C&jHl4&fHj&v41) zf+b%upqBrG-4<4fD`?c&=;)I474SR>IQ)JTFzqJ4BDYa5#?7i9wla6P67GZN=I>D+ zaDKVV7~j0}fC!^jFogR0`m%%LQ}4_ue3@ofwP4JSUaJ-dk3N3%>p=;z$Z#S%H+8uH z_!K;n%ze5BB=73WYW3Evu@JfUTj)G)I>A~2-!oV>9o0f$6f;blcQCHZyC*cRb6=&% zULdDDBsQi9QW~hT2&|1h3c;l_*sI(kX~Q@=$1O65BC65X=$D+$22p|TZgtDIVzoM{ zlcGI2&L<0$G9;p?3#T_9uUEv=XaNV*R>%^zb-^MYiyc$V_tYw_7Mnq#_d4Zk{_y6J}$8Y^+RwxxB{VjdXS z*U;vbXJ6fuhxyox0Sx9Md+g+sto74c7B+Drr!UUL}x`S=oyxc)vx?#NV3W++U*)nzZW)!JSv)$jJZ4Vlp3|^KpyD6}%(p6>szQ}-d-IlL~)BfSIW9J8Y z&qU||z)T|!KE9)Zgi-ChMbV{Vx-uZ#G@5ae6+;1XZ|zIpf)@-rg|cqB;k8;aj-}t-P#M==ShN z*6GFk9}X~bxBe9Oz6|Cz_y&!5hgG8jS_flv?v2}pF?0s)N#q^uZ3o{17Qd)Be8Eay zzO>tQ-F-Tp>b70GLQM_A7WY7a?lAeAuYj>P+HSEpmaI_Y1y6X@ncWd^tQrZ2;x&3iHp_8hGZn zP2rVK;W-&p&DW%>4$JJ7(-n8G8_tdAl;d36??vXEE+1N_yUtnU^SZNE(-sdR3%LPf zXf>5^EecqR;R+qTbun+8ER#^e8)Zbdj5Xc0`ZP2s3$GgWT3tAfY7@L z!Ke5L`tiLUP~ln2^)f(??+oca)$>Y&aXX2_x{#8tH&9nu^}_R$7&&gFvb-V%m&p)8 zsv5yZ_baRVgVLrOLF-OOHjf)S*N3ydHB-TRjfbR+tjSTqXFht(>KC=kUoE+AH={C) z2V)NxAPw7zma9c&t?SSJ`Mcw_)>@=Te*z*?My5kbX+d@-D-I)QV8_<>D-PJLgG!x` z!LO{(6ZvBsA?CtV88Xm`@1-AS!1L3N^-XCu)ug5TiyDiWz(jU*eX)MU4;mHd7E^QR zy6&0po7q-Rk|ShEOI{4dGWd;U0lS7yOrcI;T9XCHQWdUR)s;B4i}t3o;F%=@H1L?N zCLeJ~TM`;3M-dwDNi{!P7t?N%O(Tu%!C9LiI?!bwiTAMF&qRm!THqW{+VaWKb$^At zeK}MwD9Akxv&v+2YsUv2t_jJ)N)wTzsETlM$&haM%z?&(*ZSS$^Me!DR{qf%e%p;$ z!wg9E;$mjG#l0$bqeWZl-JDU|q@dL-vus3+30*UUs3u&FxQgy8*GV5QorugfNgdK#YcD!==1g31kwg1k#}G@5&}7E z@J@9-PI6}S6IJjij)g+R%>myOtbXQv(L&MdKACwpy`DZqEZSz5U7?$Gi`1ymG_F|VT~7*Ol&iHTWi$?h?i8j zE?E%ITIFp+*2@{oFR=p3^{-r*P4_vO^UM&c=W@`h7re-4o#J%r^D_>}_d;)m5fhza?9o_tTL+Uf{0c}+OJhi?D#>yO=wZ{+r@0?<@6MEwOUE+{Q%?JP zfV$%hMTI4}X&rT_aif)+$H&$~7|UAk3d5a$(}83@K^l>~i5M3m6YmfNS`-I>uVQ|M z1U$@PJk^D)6stI_>ZB~WO8SgXW=r9bn2dBJyo;!`fSqm;hqBN#M=oFtN>S)-qn%b*`>?c$4EmD|-^ zpx1Snc~(BLog`GZ!P{lS;Jn4O3`$vu>6Sf(CTqeUs{T?6x#8 zU+@jt80LribLYm)dr1=7n|S!6oOwdiu*}_TvYcsl)8P0|cAlPEm7=8#>S^T~%V}?! z!iGZa1_xyP!Rlg2Ppw+9X1_~8PG-c-2Kn=gx+Q1gMaNP8;)3$#`{Skt9SvAC@5}Q5 z-|gi=THLR1cmmfWO9__@Lm6&t-(QS*y(Mv3wD-tLq*2PX!{P~WG|J07p#-FiO8`IY z$+iRV8H*uacJdfK;{-%nF%u%!i;i+!n?FPyVrx>urGZ>fhTq4q5xI!wAS62I{nxYL zH`Cb4q7Y&6V!Am)Yc#32nD*)uybhN4DJV)IQfmt*_#G5Mr0`mtRu}-hGJhG1NgqG# zy%#coB+-rDg+5C2En(n9h{>=|S5pe2l&axarK`Z?a&~lRPfsXb`@>0Fz)7A5rj~Uj zI?wS6%GHXkSANA_uJ$j8!rMEVHv__YzB^0|J`0_IK2lnMacb}C3WqWm~+mEzu29@+PO3fFT#s;S2yVhfhTr`mmSsb$*|vq8@5 zOrM4)ZQZ!-sFObc3l5h*V4J#t-VB4(zL+u9ZJSVc_p^yJ_tmTs^hysjCdM=Wn=Rkc zRER4`h+!|WWxboF3|Z_&C_uqQQ&$yQC?1i&u7UiLYVGJud9dvt>BskZ8^YrD*Lt$Rwy2$Kh2vy;r>J03`D#r)RQ(Q za*FK5Y8@h=%Ut%s$}&0k>%}e-HwWJU^azsomgM0m3p4=xKmX_PvCrr!`Ty zI<)RpRKKJ6PM`2HWlT&WPpPWcz^M}Zt19y&GHaiCrvQ%r1_B_+ZK z5o-@l?TRN2maWssf6%VUL{imfsWclCxp`VJm*+D^AgR7DwOdpW4C)OdvNsUzLB~x3 zS?uR4q;)`CRsyDyY%xjYc(E<47I1n;>3quahP~ay^g7029PuPXHzM#&fuESc(y)c7 zU$0EM#v0>k)pSY5)UlvnE-ze!M_+U>PXVt2peTNo8&2^x&NF-cY?lzWmZD1agk)dk~G-BlrPgY!4zq$-y zaK4?Aj_@s9gh9I;j@s}it6?|l>0>i;`N_}sLTm6rU7XO^4$~D60E`m{e68X1DM<5# z1CrTuk&hL=V6!X+7z}>BG>9U-_M?cBT-DLmC2vA{;9u=_K;j-POw~~ro z_v|9@VEhZA{<)>+^cQOAT9a6j0h9+Mle1aGs9KUqyjdJL&I2d70M?qd4LZ`l7EkK1 z3A0#JB)+VL;Dr{KAF9>!vn>`f;`>x9Uzk-G_mR46W<}Ik&V-B6)a-@lY%OKPgmCc0 zuqM|^810?x?H82=dS1;1-A|(`e;Kc-f#>0o%xl6T<5PE^PASw@l4#qKa2XnA>?8N` z7#UrH$i+dN@-H4t;wZzpM6&+7)D$N_2L^Nj;sGykqlzj7v>QCwhf?cEVi|O3kf|64 zV@v#BSF;-sia`~6r?>*cwM}f5mBuJtKsu}EIk0o=^-707OIC)P!N>ft00dlWhYN>S zY3Mh{?kAmb8@(Nj)>z&g>hdFjjm+SE=^OBfcKA}WDki~4&`UO(dZ!%lOoa2BAdyFg zI`4oQ7hhlylQ(c#?}eOaGtv{k303purG=~49X0daJ;KdbXEfgd&^7J7`FF}hZfPD97!_)y;?R{n05IuTDpQmv@Y^Bh@tMg)W%mWp{4nx zbpmeVT3RtGVL@yA2^r2F5>R!74O|sR{)^0}(bsHO`B~xX#3RKgE9uqG3Bo-LEmxor z2Peh3>sO?6!^`F|v7*@gIJGNa_b>z-CBet;&(0a@e>1!k1?TKB88_*f!FYTgI{iIEdg{31?tnuJbsiVi!|80s!$v69*}OpGn}X>02IXu>W~aTunMXW_+iR=J zN~C@38Y=SJY{SS>ru&_8PbpM=-m znaLt=)X&HDcroE9%ToNp$pWu^JINY739r~Yz%jS0lkkmx|KzCt+zmy!>&0OCMb%D5 zZ``PG)*e$=E8R+vl1n0d+;QFO(%Cko+Qnv$mHZ$Z6XlfR?w#mZ6{*t%oo}NIEwat} z840Eth7Nm*-jR_iDguw}kU|wY>wdWy<;`9e8AuW^`2Fvyp2FjqgD~e$;ni zeL`H1m$1`nIOkc#=7AQmN<;2AO_F74L!eh$BwnL9NA=%o=u;gm^`nts*~1~w;X)w|TcH{;(M(Z|E;ujpmqS&uc^3#Y%_2o5OG18=gQfJpKCo2`+LaDd~YM~y8O41gUj?E76eCwT3LZavzcAwQ*Q z6L(*%$zpN@F+JV09#&3VskB-emn0&~XMvEd)S8gMWA;q59!@McVYe@+!M8N@@w{*L z4J)MRnKaO(Vq@A_tLZ9MDfg!feZ-tDVUCxor-`@5r{>^8fJggIEnOoVu;ddLzN{c9 z$#(Gd?x5WVan082o_NJ7X!}#oyGPxv`Si4t^rVTGpzL^b%EkscC*i!B@q&i?qdM}s z>t%P_R_CAT#Fr47ES*M>0Xc)R)p6VNlidJKUUzHS^TQ5_9%13Rz}<=0Gk~e(Vw?!D zq#SIcisx1AJ^0n>Uvx)g^ySf(fT-sKYzagUQ`ukRl1&OI{#?3>*fUOy(|zdJcy z4w13xl=%uP?Gf{82rj7y%ZAl>syxaC4d^Plo%P^DpCAW6bPiWu^VflhY@IV6b4iFP zuRzltq5NAhx=Mcz#$5(sg{1>Cz6LDi9=GW9=N9dX^#5$3@K@63rQvn_IPHlq&B`Xe zl!sT;T~i@b{)bVcI@or)3L}o9YuRUs*V)H@!nBKX!t{(UKVG}il_k1V7kR--1=Rw_ z3-Bhe6=R~604*d|TO5-%P~OO160oJcaGAgkP;FmN5G%L1 z^_A2c)F3O$=jzV%3+I!iOU2R~y(NmXwM+1qdgZ~dhCg1_=3@(aeteKnkEUO%P;ukW zf4N{?w`2(aDS!qy2xLL?X!YyYPfB)HVJ{=ECurL3uP-;eFE$EJvy!H*hLpEFKe>?Q zHnoh)B`M<(Nj(cIjy2)ie)d<=7}PqXDZBW{wia#7)00U5IGz97v!HDhwqhz{mK}#b z2NIK}{i(z1yvxMfFtuzoIPqdZwDc*ji&Uuav8(0Bs?g$0VuE_(==B2i7IX9;9EJ#H zbEgE#GsQ^-BtJvKTw@I@G~^r6YnMS((qJ#<@9sNJW)p?T$u7qY5AFe1rc{L@!!m$$ z4%OHs3j?C?>JnTGjVFFQck*V$8cX`scfK~NN~H3_Kwu>v6HP!vD1Li#kK-2&R;E!_ zd{0M%vvap{!yX4Rt(eTn-K8eZQG*~-=hYo=2Zf5yGi|D&RDssHR}CI4E3MQR5T$UeJC4Gb}YS+>k&2B?{p% z?f|3Uhi(Nz=;0O>Gx(Q~I*SVri3cRL9F|~F-QynZD~!4ESgmw@g|`esqawv+w9N?B zipylku&1jhC`P}idJAgY!PoQXa9PPodxI9Rb+I?GruTe@zi8KmX?IjJb8cP%%Knzh zbmyL)WeEbR@?=WPs6FkgwT%}OGpV?dj}=so@4eN>OjLKC!{AqxuB9NPsPqY=-?ho)XH3}XSM@@ZnFiTDjgBn$!17@k%W2;d7kYnd zts_Ks&IMmNejSZUlG^;Rp2JNhgGDctc9#tx^rr+5`;YKU6@26zWO@8LCEHM$jAN6U zhIqcia?y09Jj%6TN}kTrCXOH~Daq3Y`aoDVrN`Htr1^IHGXdX?r|ZG+wL$-3Liy-2 zC~-$vn?U|dg%m?zBG40u2&oK;o=2zyz&L# z;@myP&2E;Qoj&Gl;j(J^k@G^=$guNwG2;(rpX1aPJ$x1S8h0?856OOumplnHYI=_og9UsZG!AbSKL={1gB-388-dzxXw=f z>_=8Qn%zQS^(I)Zumj;Cvh79aE|q$5%qmnlMDO9CT~*5(OUtH#+p5W0w3P-xx|Wrd zXz6DYC_2L!+jD=?Hd?kI@jh+n&Q>(HJu@IlB&@N>fC@6IP`vs?PerHN^_`|@b$rA+ zeMZ#Np)h?_~sp$oLz1%k+ozI|5U6XA+NU|HCBhA1-Hg;NOpZ%RqDIkEA z9tZW5RRiUV73?Jf(iu?V7ctJ91vns#(!)2juskFU&;5hnRGhVmt}wg zE-3Lq-hdTmur3c~eCBHa`k@Tv^-C6XxepK1M6|%|bz1Q^nHtTF`A{pA!S^ULDye_YMgotJw%B`=*!Jb~E!^h$?OL zZsQCLlJIOA_i`omfDQktu!Usyk-2{lg*8L5wwLVLhCw}k#5VVzY~I~lTLr>Wm|{## z6-A3+{%U`U&Y-(buXC5F)yHu}0LxF*{f955>1tbFxvsc^=_JnDtyq)KhlCEm z{Nb12mtQYiQ!h57=T6TlFl`jrW9Y`Y zu@;V_prW8QnLJWFjL3ZOKaOtNMYa4m6#)~R8kx`k^Vlp=v~&%Lg^IF(A6);)O6NZO zqM>zJb~8P4CfUgw%9e;~&VL~PE2Sa^7(ZnP|CNit>i9_in;P}+ZU0W=x8lmQ%fOHv zmOB<;R`^@GU4u^0(T3i2?B!kiy@RcVGUZ(64^RgMr{K3G>X{0`2ZhWo%e+vDu=P1i=JW*#Rv5HDV$ z67SA#I)5TzdRF4z?pgq7m)Z0?napNZ7Jg?ykbnI*$opCvA*1zrkA(q91KkB=-PJHn z6r^opb+v2qL4>Y};r9%~l0#CS=AS-r9YuX(7K?ay)ig%ZGQ{nh@7ZO8LyA6@OyCZ( zCs-u7*Xj)G3Q1pas_T58wuqi0pQfA9^&^t9_q^7Q_++eg`2}aH4OHs$KP#cy-uDtQo(`$~OY4NrFP%Z+d849wyeJfx>6Vh-D*q6xh)9qByhTIv{tN;#|x*hThO{#-)_sgrW?hO`Dalv-<9vQ~^TK-41g zIZkeM7lX$CBNR1HYPeVAStH|lTl5&S5c?m08R?Y`2pPx(V|PODIf8QkDPK);#UZ6? zCY)Cip4n41JIg2GCh_P(;!2~fyW;G+>ELW{*c!n{%Ufb-K?^N;w+Kcf>Ev@0eHd0m%%UwC#1l z=KAEJ^$=5W(oN(zRQEi`)n^~P-RB$y)p_5F{+L0<);Dg2ot_0&{}O%-ufuEfG6e z%#G!urnA?#oHA>H@)&&BJ3%5Z0S$6D`8)CttY!`>{)c;I25IO`UDnDtwTDQ4JS(xX z-t3!^DBg4~vAF9u9PTxk1#S zfmptBvb?l)s{`Gi(0NzmI`|$xalKYI2c^U0T$AchVbc_XNe^ z;L=e`816U(aALF)P&-Fjel(|F`6qaDb^BCbw)t2q;XbBs^zpOvLiKYUyms6Pw?lGR zGEA5uJVDJ{fM8a`i{~$Ui_!QJ9R(CHj*i=488i}XHktpE_K&gRRQUsA6qHp_l^6ME z`E){WMWf2NKqRImt_m6~|EftS# zhXFh=@JI%MXKj0bhj`KApv+4C#UairH^!xaOD|yq7uS&p#lOPPjbgR+y)sy^hZlUj z^a@RT`VStB^olg+I#{i+AG_^yjmX6lt^|^UE;G-);BjM4+MZkg`^*QrmaG3tbS(`- z2Q*eZ%YKJKlD__}3sn5#i2tbsOGE$P-jevgt8z!~jOIh__Hvjy+5W)mNT5VtIH_B| zqH0^uzlz}js=N=`?6cI@bBII#*NTAUh2e^M12`RZ1j)Lv|%yO$&myzMpM?#cf^ zY@W82fyS6`Lcuqs2t4GpA}GnT{z4Yo;E* z5VffSq7*PU2tR7zUBs{;!0ST$z)7AY2Gs3%w3vcVVpnq9Q^q?&2 zS`b>w3C|HC0fLPFd-`E?pYS__pBf+%l4(4@T{d-BYat67-RMdx(N`ZSP)A0~)+vQ?|O8+gUx- z`iZvj&ohw2Uw8(+c!rnrZ$1C;AAS35>yb+Aa0@nn2J99N6UMgPnR(hI!j;@g^J$l| zb{*0cpDr6zSJYD9qVm(kt96~z4&fl-F>9qY5bbond=_Fzh8@8F>EB&{S7r2hLZKrM zuvP%EH5wR*DB1ll}q!R z&l*z;N&4mp8g6R^R6fwQ1-)|7L6;lbikdNJ;aF#^SLRs-@3nU-$=c>*etaF;yDBGN!@2c~MaCwz|i_iqmK3+w*fd&cZT9e|89tD?8mQodoTEqYAsAdERTqM^F!P&m{H|1FX^ef7AC;5 zI%(J;s|9zD+ZQ@#JIri1S^l1NpS^?m*KIm*_WyX-iig-rp~0euR#&xDyMK^Y2lhoV zh;B9Un!R`Z^}jkFCQx@I9*^Q4knXQa+rBW4eE8>pud8qIe`zmFu-JhYv-Scefdo}M z;1;)YFk}es;ud|$AmE84d;z51OB}G%p;`md|F2#mFdsfsk$Of4*z{%9?fyP?{`;Tf zCC6-2o34FNjMcY+V!R*{$@$;YDz64=+z$A%DGjn9>@#?B?x zd+~(ZT&UZvpGkwb$7|wa`oLpygYA`cewg96X>Eyf&o1_=4c+nN5@W2!@k35Vi6hYl zC++{_W8Z`L5gT0u@e*a8>|3aRn{*^1Ds1DkF{yXTzAbVSyVVO`+ZN?5~ zyj3TcS3If@M@beD&@NW;kTc^>HH7^^!uhAfKSv~DWMWu85tdV9(OU0;N%tm7n?#@} znALfP-%|zfQy)()cc)4duHe`#CWEbe$_lb6VFn(rD986&%TprM^O4&zC~>|ae7kZh zs@>i_QIL)(-qBpLAT;wv(`fExuO$YO*T-2x>aKr=q^w#-?d=e#(v`?pQt1OItm!wS z$|uvPzWbTjxn7G>h^@9_A)4uNQEk;&58(FER-d@zcfsvXxDECBURo#GCC5>WG<@fN zWjx$95_b*w@=V8N%dMIyP1nW#!)+a~M^G@%=g>0J({sR$ z@qAlRPo=bg?-o`}er@bAh=bGJn@FU9dk?q>F>`VhY`N~1Bdzb|E1dfjWX5(88|F3^ zVp3di$X1-2~`wy&snh`%)7TGG4R;^Y_k{PHtWL>u11Mv^EuW3yA<@T z%RtmrLNf-twC>MhZl;LAU7XxRt@t=0(!&0i4UGv*9T>Vk&uDsOY z-=+1|MbiW0GK`zu=9Nv)Be#BX*oqfnV2+5s#uup`66KkcZ$`sONm5(P9_%MZR7%GD zV$Gw-1seo&aO^wRKXUS~`yN?rLk0!P*xL6OcQpEX_!3vlYKxWL^;<$$C7tPkeGl^y z{Ppqn%~|R4f8{;Nn zf}g_x39LN5Ym~=iT2^0lcMk8RYNOa9cABY2%+3JoGhD+__YEfN%1~El{bZ9$&4)$n z2dl}lTZyRQA)sz+HjTkhL@d-%_hb0X>-XS}aLS;Y5)S)9H$Glw*^kI!3{zHHV&>dvW6N_h7;szqI5G~E{RS<_#Z$Y4244&CRPCc%4bx{9w} zBP}S_$f9P7;CuM2z1<=}km|KcQGdCuMOR(ywJ-d5H=d}>Cf2&Dlp)fi!bhYI%M*V% zxohAXO2WS6^*;l#T`aNp1sVFHBQL7Xc-jG}TcWN(i?==Vb&g6(@~>qwJW~znUxzeu zrFE4v4|(68X=otlP>o194BcM8{}n=b4}!hQ6pX2Qvd@k{}^ zn?GN&quqD)Sp4dt^sl-7$YxFcpbRqfa4VDJ9!18TgsEZA2Je?@Fr%yHLMYNG?|ejf zJkmb}fkHzb&xP@p7SpukGl%<$G1yt%jFKO{jq1!QS$ulMN&@eO+y9ZU)ZL$fm!P8_ z;W1q1VyyR@<|FEhWiRdS2!oKnOxgaK1PqKxh|3U%EzjUKMTvbjm2{sy0Ds&n+o1Nn z=DKmR^9JQLd8PC+S9tU1$@vWX$oD-JBSUeiPsz=b5!mKPC1xZ&gY}S<_e^u_=RepN z6bOk}n7Cg}PnPl12x`uGk0msJ;TD2c;$?2zTd1GLP5opOdou}Xmi+#n z7%EAE8Eqa9-orb;rD@xy=(;u1qG^b6FuEP)`}o1lnY zHBu_dC$8{^?Mn)v8*)zIAlCr-zfL?Y60XNdB4{hUoURmq#~#%!;eAsub2s;vT99VM zXs2)d#vYfQ^B=*T8mLmH-iwXdN=jZqtdCDG#j(CmNN3+VMI_4fj$)FST!q$LZe%7J zm)`_?sTBuo#y!4nTYApka|0ko3GOXE&e$tBa@Blc5MKug9CGq}cKAoI4;*mjEE{_2CXfK^snm)Oo>n;GFKb(PN8tVk3uxdW2SubWl;PnJ*dGb0hO zy}KC!73XIY)mAN`4akv#S1pCrTW<^S3njliZT5R~n;mpgYidvVnFMSy1!ndetI)a4 zHWIRJAQ?xbv4)`I7CWKm?B?g~NR9xIvkh94|}pvxKR-S61)?C1j(M9l@u z;r8Wag(c#-r*`&D1R$lVR%FBJi1=TE5R?sGDDYflGJXK2440qdEz|fv zA~%(DJU7ZOJL2m?-Ncm662u+0<;6~q{z)^Pd@GaoR@hOrQLG1ioBrMAI1fAf!R<4u zXqfae!TSxdDlN9Tc%h901pWMM{wL~84+;=9z@^GF>^Y>g_?n2cA@$TtQukMmLISuvOdDg9Eh zh-^%j6c%K8svB1NyV#`tGMG_nq!LPO!kEKF-L91^W!+;!34wcYHWX=T=ayuw$%5O) zT|6EYhBB(xdTdhvM=l)0WR;s{4EaFga~ZY8=L{A(hsD|lma-Ib;*IwP>!+_C3$wm- zllT2xX%^W0)!O!j*B)+kbOhLFl)ky68`)b%9G=EWZ4051eoT~D|3~Oy((=*jWmFmt zY&RI0Kh%dL5=W*%=Wl7onEdUn=6&XQQGs6UlEi3!yTEX`lb8T#;L;7 zL+4YHePZ0B8FYln$qvj*UOP6rmg#tbi5w$#>bnH**)&s~6W}fY=(@emZ zHIh;K%t61?le?`W=7RX6%a*LRhFK=4FzP zMC5eyWEoOQ;#sr-H zkr_i`XDfTX(cjzr8i;62l&jYm-~k>ebNglY6d#9qaM}~XuCe4bMjuA5X3uM$I{U^+ z-YQG4eB&1d*e0LDzCerATz~~zpj(cPb(%@FwLAeFE1Z>$G%XhkoxMftXA0JX_BA_$ zt-h!Ef9nEX6op5=@*)9(p?5zh@1G)%y5ac*dCB*}eInD`C5YN=xWLQt&BA%imzLDu zqTtSy$}X#frNN5HeIpS$XB)H@HlM*GmOFW$+Ftfn7z3TbBqk3O-$@Aa5y< zrx9}v9pYOa^og+#>Yu%OE>#EgGu=~xo1P^mpm-?MUgHh-WgD5>b{74b-@ErbW^F+~ zHa~+kf4%~K?*iUJ0r$4O=DJr|!@O;i9k)jwv*Ln5p(Ar%W2~t%CQX?Y0n{!6n=cZ7 zoOLh-Pyc6Y1rfIWBH#HbsU8O4mnm|qq*GJA{yAW$6muSee)~>Ob*!N@dxFQQ-x1y7 zL%jYt4)+$D$c%DnmrP)-4dVT&LKC~~jgkf}E4GyJ#*DaInNmFmOR^-9Yyp)>hpx6b zOCyJu(~s+hbjqm0uBYCtSe1Zf$qQ}~Tk7cxwAXB$*;>knao7X9+u%E)fwUkH^W?A1 zFnd;AwkENtu>XyQwqbqNWU-%q1?{!(Ag$gW-X3Pk+OS|Ld(J+4?*q&)V5T19@@|tu)#-CTgl`q`iRou+^Hx`o@?F7(!8ov)0_7FS}u1)(6aNxYDXeT%pJ=Pnk zXBWX#I~Ur|m0vZHWY?gLI+S8&qgEYLXC~U18F1CqBaT&LK)0&v}&`I}gb)-#)zStVllA(n1Ash`8sL9w%t3@km(c^>ovdRy&Awsk&Q3zuhqP zk2H$L09*PsIK-G=pPtPbGnpnwK>+L2UFCCR(Fr zIrAiMzL&3Nf*HS+>o4ZBtcEOkiJSDL+ghkEy=IuXiM}>u%2Wd~{1gkKH8VUsthQhp zrjyfXabT#jx!)&CXXKW~s?%;NETykA9$RnA01D5kkY0MI9B7aj%PWZ%1?4g`58u=n z5($j9O%+$xDK%fdZ(f=HR6t+H?m>Po(Csa_y!2W+Kd8&@%j#$Dmwij4W(lMu=v0=P zT)o4eET(~MIjNu@G|EEQ8jbGJ2p@Wwdl~T#+$ZgaeMc6b{CPj-cWqt<8C~6@)2|S} zl5)KD>Cs+{a%2bg(iH&;vzzjW0MPz@Y%D4PmmK=`9hH&Lv9cC}OR>RESswfx6$e6aN&`AZ2A?g79L7)hGr&%xE!fEx&SB^B&Lokj8HM39$JPTx`!cDRZ!VDk zRl@bJ$*u*-r22;ui`cZ_DweC-OCQ?nhyz%WzI>tF;-!csz9n+K>7daYFmDXQhre;E{YVDWVNZV0zP4dTAspieM4 z_<8y7EL)`aOy*KP>^;$YmBLT=cN_t4UkN+B%6@)0#A)B4?N%GEAh9wy1N&kABWuvl zD(giX;|27K{$*wI{m|^Ru=Z*3%Et~51FahMm8`crgnhb{M@}|mc^G;w#!1L{_r%1x zWb2qieKQloIC6aW*j;tTQL{Z2Z@WP6g&OB{{5ShTb%sDAQiVH?-KwGRNU()|5V=p6<7bE<7#y>C84u*pL58=a73nnG>zNt z%B-z64zpqmURb@<-Ccm&z$ca6+O0y1o^H4Z!06{u!~^Ojw{R)B^$72K6oSGQ&VRe0 zUFS0;Ozg2Y^doj&4cCiyy^OBF_x={Za=J}nyoiSdpxk7FPX#d|Tb=RI`S>pjY@hJf zD0-vuhrV-|APJryb{0QHJRR*!p0N1nNDIpJ4QluIv*OJ&&?!Cp3Q{-5#yrYowGo&8 z2;@YkLaR%{FaM@}F*a;{y;lTFT7RQcs%I#GEu262$9N4q%D!j3K*ed$x z*POP3+Ti*umYPh!%6xk|~>E|(b#mps?&jLA{z|pn#IH~7etA4++7V_r~_5hE2D+AJEG}s%eU4)i5 zZZ@r}{W=|W#Xs)=Ai=|{9q<*jRJ}l^fiPjv9Iw7ZAp8RI%pp$Bii!54k&Yi-Y`p|e zynlIncoZLF2e+1dS-2l8$s{1!b}A$+CfbqBnn6zIq1n#h6;}AkcvV3eqNKCg;VI^m}I=0s8?bAd(E)h1y`0jU$KB19_ajPax)SiI6K zrhZdCE{)L4M2EFKA_T-5ir+2p2I@e3j*0{#zQIvXcjvs^j$$u^xk_li^jF$lTKDX&VpH9P zZ0!Y?RD!kMmyu90AM6EU5yVp9V%0^mfTXpa?jd_(9Y&1Dj4V4UzfC@&-ybbY@G5jU z`!22RFA?`h0JIoml(704FjbFGU`&|r_qRNjN26N`xEA~4Yze=#bdRL~A<8e|sx z9#6+sB=$Ya#EULYE#Sb=KqnyG^C{^F@9YaHz{-S#p$Me6$ItOhzb!6*7iw}gxZx`eqG%-a$R2mp)634M`ySPq|0d$Cn-5G%%S61*FeL=kUuSui>y$}${omb8DOGa> z{m?_AvstM-e+QXAxQyEg>i;V${eNmNmFA!vPz*Zl%0Qf+iUHe%$1VRm{JeA5W|$d% z9(3Z#XvtlJU!l8}&f`ZA`pd$|?K?a6^4PoPyzsh`_x}Q<{&w&dnfUorm!s3QCPMLR zZ!X?1{vBLs4p`=K)r*|ylpeQ``)u;7e);96chHjzl*wm2*{cRTPG674*SL<{F%fzi zq_mvfyVm6oa>JC0D$qI6m z70l1~YB(+$>G*6oFrX|nMmUssH$^ZE+j|J%w6$S5B6yh%T3!v;`@H%Yi&*wxD^_`P z22;Wj+nE+$0eaJzoq#A8+U(gs>hpHnWtq5SlG!kmYawR(Zp{oX+J6s#{A;8A|18ng z!P%t}EtCD)q0O;FBR&)>j`|q%iGg{tO2>I_@qLui$~9q$n_-SKN@5CGtxfS@VkqV- zZblHGDXf-huHMdiGArYH)By5U;g&5g$yj}z;|3uF^@0A8MX~5;)cs-`o!cak6Kk8+ zl&;$6uuD~l`>o4*pvNfdej5OTBkf#IQp;S9XYtCL3PLQ6oF$t8kK=+b;v?Xby8_c%WMw=Fm)B4`Q3QHT7O^;% zmtW_PJpnt4)7rSnQq$yY<)U|$P%xo-D8Ju4@$4>|QiI`~zWOp!GSw7aD;CDem%UEx zvRFjk+60S~TfH{Ta5Fl|?EypCg;22!_s{CDcZ+je@PkpOcs4!L_D_PPCO6KlZu%$N zvN-@8giY!}iZ=l?X;;5^~{MEF&LM0t^G+*xkI~ zaZew>QMEztEgZAN+qjKqLwo@qFs4HB8d8B=PCRNw4}x8{bx{`rXa9u>D7q8lH@KgO(Kghh~wi^4qEXdf! zjpiU1&2PUOvB>q@UuQ_dt)(Cc%F0A{WY?_VY_KVVVQ<#k!Nn-cpo*6>06 zw2nr*Ckak_r*vU2(_>%g2xMkXx6lgD<3{fQ0aOt+kA_(H7Wb%dBrH6|92{WpcSOIYwGxk{88}aRz?=9{< zf(;ev12z>0D;?Vi9(HSzH?~6GDmLWdSnODkOOAi>o5b^145uCS96T5G!#H^pk5cRo zO~C1}Ql)^0MSb3!uo4DFggM}3LBIV#>=&zDd#uFdS3i01&~P!LYplxzN){=>5l+Sr z9!kA^!4Ja4k_4_=>7feof|vIMc?`SPvB#v^&7ve6lvs8J{tZhcA>u^R+puJUC4n#x zK(Y9*+|`3^EKqA$aaJ>Z2Fn2=BG}?%!|L5%y=#eK=3ZN?H)1GaRYLu4uVER2e`6XF z(P{pKTQJ>`P)riqH20zWN6@SUDoa6Gr&&MzXf?p?m?& zwjDb7)Qx=jM(>!T$b5GoglYoc4{q2z+`bi5(x>O&$KDFqt6?JB7QERWZE`yPNo5?# zM zCi8E%t$VM+X9HEp2)XzwFN6Lx zi5tbI8**)4cDriI&F98R1pf2;&NL#ngeUo5?#zIfX2tg@-WOl+E>NeYa4``$&rjlk z=g`VF9n(`2DqLM;gIoPucsaq%Al0ubw{IlPf6j%N}4Il!rZi5b3hdU z50jWRF!8YuzWk1Sc`XQIMO3wnohRXYgH?&_^0KLKKPcYXTl1%uiWvdzxamE5vdI4; zevP>+leGV`;zj0v3^zXqG=xOe{|EUs>GfW3TR$_bkY5pJ&F^<$*^=2oP zt6|ag_68>wE#w(Z%(eMeIb{?f(j#?Dx8TL(-gR0aQD3T_1a5%LdOGCG^n4QTe&1;^ zALWnEGt3;is4qrzM{ zuX$SKpk_kzldhK^)akt+FE{T?D3TmoR^`b=u=f)YgIk2zkfQnIvt$IhDco=@$h8dS zqKs#38&E*FZtQCn)GDTc-t_{`C95y8df@x~yx5ag#{dQmsJMEil$%O^9Vw`xZ66+j zn(nt#UyHiQlh$onog5KNbO-g?fGFVvrYra7v2!wHVlCMs(u|B?r!ACP%; zH>_^g>AFVT#O>ll(M6H1R{zusZ;UVRYOk-JH(N8;4tq<_`K5ty>#JyOB5kleVS>}? zvom*|T{bG;vK%HdpXi$A-N8p%hINKK!CCK2x@->*imjzL{B8i74V)#2F|naVJypyv zlH~x+Fhb3}YI25_0HTFz`~x%XFPRirT@i+?$X$~B^;%yml6nms5*_{U++*aSDa|W-HiEdz`3*fuS*M(ZNy~fNk14}^jhX1U;R05t zO8fi`36v=u2`=+XeA}X0mj})?PEL96>mN{J7MEe4UJz>vY~7qdpFg2N?eFYA8;F2^ zwR9AAf!JUOlzn6GyCAY`P&c6d7zsOa3B*MZyuK3oX5ovM*K7(4RZ7d9v&{x-T{f|Y zO)!+Q*Cps)!uliU4S_betDAWnVZ6c#3<&5c5&TLG@0FTwQOM@ z_NxF#e$ZJv0@T*rMQWZ_Q~;NcIwlj{ys8&+Pm0?6 z?!4ln1AM~qmINqF!L#W`5eN2o{;_C%OLTRP5iOV0;{09n`{$pbzpPIIrmk}>P5<1) zSK?L=3KvNvzX&1w&d`M=p)P*4J$nQJ>tXst`g*|SptWc*5ojX$^!l33kmv=kMtfP8 zg_=O+;Bvn|!*hzKQfD`Ye)FoZ{3q&-EBWk7>)%Bv0pMHrE6nxuC=mCCOxWU`Cu(yS z;yzr*$XhXCf@Dw9q-p0}sw|gcU?Wm~VWv1cO+(We`u50|E?+3%mzNgKllLiqw9U6d zOOmaI%LbfM5pFT469yPCjv%h^WS`MG+HBl%(TQ3KgR8S7QJSpsNtjWQEBfvo%n^Ej zUylE%GgZYLVW}I2u4@XZ!rZU|)TZu|dlUl*gikP(S-;W?ua6VP+ytv z5@vc*a{qX%hTB$qbA+KNSpQ&#t%1pKG0(3HlG}*2rjB~Rr+2%Ng#!tK2usrx1M>#L zTOafI%!4Cq;mn=G!;C557=MZ8>M4rKP{9?hn4@ISl=2egcq!&+EyZfgRL4-nl7<$k zp6zL|c%t|xW!8x%8#EXF*_d@#I`#iuwC7FKyc1SGHg{}1XkT|L?0VV!*#73$Kx;N$ zvLAWUxnzR3MnFXj52rCQTxYL!+cQo-i;L{%Xjg^^cb+?q zchq{!%vVmgo3m4cg>M@TU$%gYJtTZ8aj86BF{eZle16U;Vxn0#OIRw+ZW&M17c1i} zDka5h^nkcSf_O(6Krn?l)Y9trB!h|x^K{HUeR*mwghK4j*=P-jFI-}R{Q?dK8N6i9 zJ>@XCC#&_Pz%qQm%{47|My-Qh6&g`cBZsW{3vWpX+1g%IlCV+Nofu!nBXY$_s89iY zdyDgsS8g@Wmj)*V55#IcMU~t(R8eeG)O_Z8KV-Q`tof3(;+|FsPUHC@o(miXZ4GH{ zA2a>HiA)%8`c8a}VZcwzsY7pgV7 zOzy991(bW+HBJDhd-4cB7XehQZ+L35BVDH-Ugn=w9a5QWU@G3s{3l~ZH()16X1kwJ z5RgKie$P_=mQr(nHccPut|BXZN=?FuH}Che^rM1E`T#TBk0rijMsEObAz^FRPp^sz zN4ywH#f36@BoW$&Yfriyr77&qIBb-9K$zWh2|s|)oLc4b)#%xqyc;TDu zUa;k#9v7t79t~_m@6;lMhC_PKcS&LM3GqjxzJC6L1dmpp!{F5pkGJZJ;1$9g%9Y{c@sDU2*5{Nr5DM5>~rVWon|@EHl6vgya$L+8m^BlsO@`wt#gJSZX>Q~wm30n zuwtI=lBw;!qY1N8iaQf?$BzEIMcntjW@JIq%p@r$q+%O@mZlM65o;hbJX;}Tkp^rD zd{bO?T>jH`)nfj9e6*u0OO8XhGJ^oBOa6aiDn6&3odIeftzGmYnF3yz37^p>xE?3| zW{Xsa7x^VII={)T;X&SEg4*URdsselzs+-GK1{Y-mxZ_O7aEmVrO$Z*#5%X39P5ad9CI>hm;(oaQqRc=*yLZ(dHtA3gJ!TtQ+>a-R$MedrkB}fAi<`t#Gv@ayh}~i9A!cvsOX& z=k$`9<%ZpIHuE-92((_d%l=(rxmuI&nETJCOIZbovQzq#iBb0HM*=T}yk!CD^!PhB z<&r!Y6G|0C5*-OCsa2VR=OJ$ou=(dU1E>A2( zxJGM;8@-}023F9b4s}OncLzLxbaA}9ROT?q<>?cv(DMEu!9AwXplkY{u-TM|%72w= z_W!lqMh~2<3jN1AJIc)Ryy)F)E~bH^M=|~@Wm4YayfuVLZ9>MJG(=VP`~P=9!VUhh1L(Od>2cQrVnAgQKU@o9?6LZEbhTY1?qt}xQ7|_sPDF}i-Z}Z z*gnGFV3XW?4Q+Te(9*`L;#P-Vh^tHTfoR(bl#lCM!lTjfK!OfEVg$n&Wsks(sta7Y zq$-U^F64!D`-HvljR_y49UiYs%$Ya}ohF@ngUVNPcaSJlNP))ctiq1hS|&x#J^Tme~PCAxipOl0AbEMpPg}0=Ed*fLaWb&&Xl2zgQs3(g9$;6 zcllOkAh2{CqaRC0QES2-LbHpxN)(wRw^Hu@u{$-5QV}u_MoVJBhur z?gou?A-6a17mYskT#f*IHa`|1^!w^MARQ&%B-qP`l;HKBm9f~n$G<5I@>bH@oOpAG z{n}!-&X%!v3@Sq;ERp$2IXpV^4V5s_uN(lrA8gPQx{bn0ZKP>Lv0e({s}quDLbL#3 zn;01H_;^^5Syb~P%cXV+mVbYufO5h_WUXWaORtpY6(#4QF{Ev23~H8E+y&`MZd7x3~b4e zsjTMm^tBIK*jc-1dNqq782}p}>Zir>&XQ!$pU{yc*V06l`8Pd51TKUSfXO>haL1VE z+U3o&!?)Xg5p1O^XtDm8jJ1od{UW}w@e?*l1RPJmAutnrjMyO_*wF`#B9?U-H0{s} z5fn1a+D@;-ZK}nh>Wscp!ny)=4TbdBkN3xK{koyhEAdemMMCpr-P-2l1H?akl7A~i z9KLvg-`Z)tCcQgSetlJmiO_l8fo0~>cNKlfU45wR0dE^HZklOZJ^ENn=5YDmrEVV6 zToVS)<)z*oNWipwfVf=0JZ^jQ$*|?Bb6zATg%=I9WhRLU0+~J|uy>v~w_m8>BQMvE z`(;eA+DCf_xW|h`GcsZ9$Qs4E16lcwd#dyvX{U8r5Qc=NNYG%q5c`Wc>Y^7VCgW=R zWUO9A3v-&geguFde8jeLxNf5gz6 zzEZ)VJj^8Ml9c~eu>W<%{J)h8_Qy0`cp&Lf9^vqY%$! zZqfByNRkJ?>2$(L**CknV@gt zD_xs!u=*3WBKh$H=D2(%y`MF|eG-~>e9va;IYLkBX6g;c`0_1v0wN?hLaPiGJ3Eh9 zrLLQ~&}y{DXDd2lb?8i{2&3}u!3Bwjw)bE5NMuNpw#7dissXhr)+W?H;cJ6E8_#0# z#d7DyiW&O8N*`WlwVh!`WL0k?UgPEWdn7q(^`@VP81;y;ey?xZz7)McT2a4w_J+Rv`Wo{kn%Im~z|hSe7O%We-W6Jq7SSE5-l9?S7F@6qaS z9O(uOQj5DKZL+J5JVnJ1TbD$O0{Atq1Sv|0WpH8a{~~BQHU|$I@*~2dc}-u_AktT` zfi-xk;#a^QlK4Eu1T&_AD_4)_1`2}o$Jyv_*NvdREV06<>SYzI4p<*+F_AIL4^CHn z#y~Ad|4>aKsXTf6(==)lYvYj{b?wO;v=heup9M`jP;%RZq3=m_M$llPcww+m0y`Gf zKxkMV<{9<;{vM%%{xXTL7rFdYw#6g8a^3J}>_;J}()+tpmv+iz#qr*?&#s z{`ZUniiJ1S&t@lG0`r!evwU)u7`qr9T$mpIWn%FUWMUzIWj)QoeEI8nYjSYL#{a1* zU)Oq=-(VQxn6L}I{`5(a(?jP$IOyA79uxma9uxl-k~%{0_EI9l8*2n+9UO6x2IG#i zc#~qQ`ae;qo9u;kZW1KBQ!)4`I3r8mP-J_)>CO8ql>FoQ;ru5<$zKK^|F{Mp|5h9F zFBiApMuh)O70<)JUB&$YwuAfZET+iogmLtLlrW$Ffo&7uL3hndYcXl+Skr<hQ}-&TnYyX!N#f5lqlOwptLa$P5TtkV)snjeyPj#(s$f& z9*mR2ns2uM5UIY6#7i5IutWUKQ)PL@7ABD)7+UVLAz}r85f>uRd-77DK6u2+{a1ZH zKL~5I^gz|zzx`DbWrr9)&=qNa_!c0Qal1UtmXderg>%why1@T0jvD_~2NTU^jhKNxFd&~q&=y5g2#*toD6H&I3ymMZT;Vud&QNQ8~2deYG?}+A%=?k3|6f%i{OHwZ*Sj`z(G` z=+3mxd(oMTK?<8CN68POp=D3(5SAVbhe6uik-or}mVe`WufjPG_SNa}()r_Q=lQ z`yVzA+0h3NURJy#F#y(*_D{ZB9bX)@UkDZM20dZuIPZdAi5sxXK+hF2m5@^qtvkwP zS@`@mN&TLl?y2WQ3yg8oW{-T}%_AalSPu90_@_Af>pfCm+=@_)!S4yPE4_@SxRM{Z zY}9+`7QKTI@CHZJC^EswU6~<#3933j_OP?bi2k-qVY(a^-GjhIuJWgzDJ<uC+i-U2wz`Lj6A^Kzv*a@DYf7|C4)dFUC>?ZcQs*Q+OK`EF-kL5=z zIuPF;<@|vLC4Jb}_=WG{a=CY=)Ry_ohU{&mtwmp*G}SSAnNJPdYch)^*wCFe=lknT zikOOpB1Zmc5}J?}5wV`IWlQIS&Zr+feo_gcZ|6+H-uC;XdhmL^Bb!w(yl+t|&fwfN zulsVflTJG*wh)G$sEmdXcU0VK5T9w9FzP<`Sz`q8mEW9TsT846>WVOj4bGbtDvnnd zmbq->R4>)H>J6}Qbby>mzLc0VY;gJs^9;qXBsYCmKjqLGaKVb+GrJQUYq#;L&%aN@ z?M<#5CuX+YlTxrGp3r$8N;gTcoGW>oXRlJYsx&yh-cwryJ01@6PXk0@Urb3Xhf`mt zP&}dF*IV$Y+EVQXNQb2#Kl(hhWz&@^-lQ5WMS>@{`Bi;Mcb|p2RnP76j?PoZ!DUsv z@*Df@eHTqS#`^btrRfrp5F2&>*aO#=Y`+ls=_CnU{M9Z&tI=DQi8n3deYeD-n{(X6 zTa(-pNg9&rh+NelthxL=?JAzQwq_Xk>jBqlIV_(BKdGc{`(1UCtqrrtairV5(`o~C zF7H&~gzL^=B?RbZaaAAq!v~4t?!Lyp{4IEO-rPbID-?8MM<1*QFr{hYq*xkP3{V0^Mm(7Fie zS~|@N=WS+DOC)jIIahEgU57Z=VSYXZ%^;>Wi7X#G_52D{eSdvL`}?nf=}ft%YsMs> z!hBcW&O$bR4>dv{d%*sd#kZKKaQ50KeF~VRg?E9!1UUUpAfbAlGI5lPFPOFK<$= zFVMrp{sf@A>2nG%sJbMr@*|yO3C^w@6VD6vWw##OlBCzZ874JLd{+8013ax`v&0=z zEx8pQJ)Bz}(eYv?lOoAinE67mljrV>3yRRyU7`|;n=>|97Ih=(&eByc63ov?!--}x zvTu5~&V*UTyKQde+rqzE(3^b3KpeBRv2(*7)?dj4=*~%yI(9cm+N~diD52{v_r$mr zbi3mreo9AZQ|xZIj8$GxD+mdAz_nXmGhsDYO@}c6(l%~~TNhDMpA>HjjmyO}Jl!O$KIbrXsy%-34R~7}p9Oz& zqnMAVP^^jReEvp%ZzC))a9G(Q!IAsun{;A$yBj07oN%~j^e*xA*c2_9^%XmJl?IqOhaATH%!NV zl@m@#vnTboE4-fMZ<)^2s*f#tOG$#}^cF3{E~+Zu+I$RW8uOt@4H<1DqRQ4w8@xUB zE4>pt6FKrKok-X;7`^547WypF=41m2-FnGrG6NT@-fAa%U`*;eAe@yf+{$af(h@yH z=9V4r_a1M9j_XU_JfV!sr_~m5XI~bIblu77W9R%s|IC|75{R4CW($TWX;b2ZVg3#d zVtay5AZFd6(P%3vZ-GtPu(xk9`7RI37|gbqXY*AL0FFu`$a|lbFhLq<{3I}9z~%+l z0$XcmHnQowV-I(l(e<{jWo(Rvo!(G;ZycdUhaT{8FkV^ldhb)tgfyaT>Fk6D&RoHC zKl>^Y8Z}Vu$2ONN@6RfHHJQ#rP;$2uKPpg})@gpeWY4}u2WyRMGte`MS-&ACO%K6{ zg?eA*5=zG<7P0PK()eTo6E`^uTLt+CfEZo__VT_39H0DX)OsQdvZ?5IeL4ZS|wG_x|AN z_lO0RUFC}(9b?^tUq|`5H0Zp-Q8+iF|M+ zIY4c_oGeXM(}c}mXoTY@3}e$gka`=AS82&CP z`ggf)eJ4V98nWaVoEX4zgcojG0&QL-y_m60NPhU6JL(}|MzT{Ox)mOwS4YL(++ZE7 zonGC04a$(@+{9$_SlgR+clayNznyf8K#j42JV9@lj=#*rhpNC*54I4r@CC2mS8iP> zq|*@T<-DqBv&TpvJ(AATerb!!G#js8gAXqi4xD@L>Z8V~%;OZu*Ti2=iE}1_Mny%1 z^GXiteCw=l{itp=1osMlI?H@I?>v)CCpM1LVkTpo{QR(a*z z?Fm%^1~TX1&Vqg;B{E^|jW52IXD2b3&>skPM(IvJF(Igv?}2aYpCO_)S->+&&+Q3(BG=PrpuA(|E66=h0$f=NATc;iLk z#HoyPHPXVZ0mzs`9oTpqCf;Vm(T>Z*fb2e75(lJn5W*|(+L6=*K!1=&NbK)*4tNrIpU7_f}c9KmdMbS z*L>V-7onEQ78iR9?4P~=M2g9?SdN0z-oNW9--Pb-i;o9A>?QftH)SP2w-Z2FNkkLt z8tV_&ihsCOb+^kXH)VDJxF!M^UGCSX3~YEEV7!RegZA@N2QRvzvB@mOz=*8TK7Q%m$8lmMHT-@Q!?rFN{N3S8-CVfn53VyRoyJ;jfvEz#@*;jRSxX;b zkljx_>>F11SWs&GbZaZF&b1{#)RB~N7OFDe0rlqGJ9`$?s83_N_sV9Sw(2?NHvci; zWw+?#ub?RMEfxUF;i!yFSHuROBe25*f9dF}UM=TOQNgiQGy6AO&j@tRHP?$E&^CkQ zF9;>V>^+^ERemilPpN(kYbc$J9-p3kBYW=|XJLah0pT=RAh56U_Nj^K5;4cdvO_ zc%cz7{d|N9HgAxJ$5=>v-P+>`-s`MkjuIqkCfL9vD&h;D^P@K24|ZyDfkose`K4vM z5c$2iPyW_X_sgGTTf@?}O{$MCZG_vAgPYq)8og6>()y^I+|9j1zq)7jN=dM%a=)c( z@I7>LsC3M!u1Rmr-1aMj%dx3zAQ0r7$K@xyOCo18wFs8H&c11{K|p*-Uo<3c5?T(B z(+!PE8k7ZAJy?fi&y^hJ`!9HwX~N5-DPL6eK>E19yw3@1(ciy8mSq{Ymez`CmOEQ< z+@!W{<&TkWny`%lN%dWeR_AY3ln>OiJ9v7&2+V%M;IVa)uvAcGQ&_M_yn*^9@BD?- z!Jpnf^exeG*d?BX>ow+6@9>7{W+zmV8IHg9@tt7zYs7u@N#9uJP8^2ui21W4g#4_W z(Zv0Wn3r)kcoU%htFY^p;tTqqd8RXCmlS_1Xs?!=bQr-fL?99NR!L^os>psta%A14 zdh18s5~=$zj3y*SQ3-B>L9Ef3&|d*?{(M4n}g>#{jQyzX_GQ@74laJqg_Kqdpm} ze1Ou=?W3s0Ue4Cv&pxX(2GPB_?c6el*)Tr%<*YM`toSZ40z}TNZpYw8@JYW&lSgaG>gNjs5v1qy z{c~c7Ul|z*M6Yk5iGs(cPKO2$Snrs~pcgMFlr#=DYowo`bn@+xV!-2_@r6u0AvY4+ ze_4&yDYryA3zDlU6^dCFc{^_)2DQRo$;}Pat^oJXsl6BM?6cz?gk(D$cGwK0FpC2} z-s2*q26lmB_AD3nuR`Fzj2pe2cQ?tNqGgLUv4fE>g#o^5`-n~jY-Z1Ob@`R);#vo_e_sb>iOvAq4Z6H%)s^Pb&QWF(OJ*yvW{7Zs{FlJ8nZSp9&UhBoSk)2*f&K`WAY8(?-Jp_=h3ydzM zIi>woc3s?uCFKP=ea}ygX@_j5m6;g3(l-)7lVpR-)ov))?qo4b4DVWGe^{YtPC`qT zRJquJn9KI;D>?w-oX5faYSc@F@WiTE%+H!6(I*#Hs+(0_6ugj|I>Dfitv=2eQu3qq zn2SpdO&+8~#@3VU*CdP}$aY-e;Ny3cRW(L_R%^`P;*G4UWjp-hNj?O&Yr|uop72^C zQYBiu;0NhZL5N? zv**PbU9Ie=XOcI0>-q7s+^c?R*+=iDnLq|gyc-0e9B-$p1c5X=ih528+3B$O-O;Kl zfro)o5nH(7Ha_LOJNS=n{JT&w=EVydF6`n_*rhG0gKwu3+Y=Q9&J=uqWRY;p?QPqd zG;tE3k|XzU*m`jVs! zE(|)LZDE@SRK6iw13s$Wt%A;S(jqFc;DLjeCg!_2;e`Y(Y3)=T5rCk!l!kX{4X>0{ z&u%+@9&M7+u&MOS3yT_297$B&ar%9;K1!msK2bdwfpynuvZ@l8nVY{@)>ZH=5mT3@v2` z(hID7C|Zrs;b5cF9;Z<1WEzaq%J6+On|kn|;TqYUy{~4Ff!oA*rEuu-vlB8w%V z&zXwQIY08`Dc2ib(L#Q$;Zw)KY|^S4VF#j;Qz>@nKu!Klo!Ud!YWqVG8li2+PvaKW ziOG^;=2pe;p=6ZM`v8`whmYOS^Eca;o`M;rLGI&LpioNjtJjSH{89RQiVecO{Ew6Z zSQf(hPknbPiG-bViKiL&IWIRtWD_rmie{!%i@c}k#b(mhl*t-tP8KKeZ6*^}jYXye z#`S;AW#9&hTfSAL+`BoQLpoW;%U-m@CYc!Or?fAT0HvNCv!O6?MeQK@86bYt%*5FR zQ6BceMDy57_w7*7v0lBxj>e-?s~;~tL=M`GpM#GLt{+LgUj3M7N?jenK0(Ffa?Wf6 z60sahdXRuH*mUUVCP<}Nfs23aHm~{669Erzwr*mH*17IXMw+R^DRRN<>f93!^=_?f zoy1u0=`^MPBx}H6T)2IxFG)wU;o~?%*F7gG=Zn{vSR<3g zD*q$OIs7AlmZoHAeGVwBco|{`fc>QQyWU`>=4igLJUaNXY1{++lXP$FiT+ACS6N~M za<*@5+E%t|M&&FnOSB++_f8R0g6acdUy>~Smd(9V0nyp9CFAU>u}kf``v5vH6?&safi%UTK1hnn#Qm5g!}$d2`+{roYE!#lhJRP$67+a9coLx#O~5 zTbAdwl6iq!gT6mxq-^&%mQm1bN~Jam7YAql<_{d6JI}q3e6%1STUflXxX%zTbFSmy zD1P$Q=|eqZ-<}cuk(6cHSpvrOYSCSTY!h)l6qR0aUm`q3C z@Y0mnG*=y*bjL}7aV*PJDfV2$3h(E{52x_)A0Py&>Zz1u2NAfa1K)M4{W!6x<0Ivk zkJLyOhqZaxyL;Ix{4-V!mGL`~O*NvQ6DA5Goxge>bN*Oiy5O8LJ2a{Gh+b>RiglW& zATTo1n|oe|kJHce`y<(TA}8v8*P~L0HQ+u0DrzygO1zull&}EPPt~ zB$swxrE5C`DXmICsv+?bFL9!7){Na;C77SWGl9qv+_V9{Fd)q#Dcd`AE;nhp@9|{A z=-r8NEyOVMkUjr+{92RxA1^?orTW_IC)cQ9Ydbvjv8hK3?BT1+FSQBvipZ@+Vhx&e) zXVMXhIgbxte0bLLXb-fo^&CC`GIXEd+W9Hfs_1#wS0f4WLxW6t@XK-`vXad7T#MqV zf!NmO%e`aL+g8ad=d6!8qTd<^52Y;gm{F`$Fv(^O`^bu`hwZOTf0S%j93LL8I~v{c z5Og<;Z0v<)f;i18_i&#rJ-FK5M)#gqNe$NgY~xAsc&*IZCa-DlR9(e*fIlBwl zi{#-TWPAB>Q@(Ljyd;k@KE?daHwhix7pobZD=hE)W{u<56=t3i^n}F)p9vQXAX^eo zwSM+*tmd}($T>GAHYVyPb%$=IjKozN`&Y&+k+5%{Q#GX4EhUW@m_Hts6#Zbpzt~$7 zFZR{cXZ$VBO>b=U4N!UD58jn=vD-js@n}H2&jORP!vRz zUP5mo(g{TfkuF#e5U|i8R6|K1k&*-wii(2LduSmbJt1_65X!f3pZELr{mwo2o_p_~ z`{U)$T*+E%uB^Gn9OIeedB!3MTy@{HjqzY$Cqlw`<&^xD8Z3V7A3%k(4UG@)8Dp_{0@@Q)bAxinKGLMS8uHGQ2Q^h=v=x zG6MQPX@?+fw9;qnKn{u1+O&*}SqszZHT_zxMAv5IbgbDJrIkZ8HYniDqR`mq`g1HD z3B2&T7$)I~9}-?hiE2XxPkqiAQetWEmdOsX@0ArBuGtn+e)7rS{t8DOy>DLe^9%$M zh`wDx5>hIVTuftD%s7W@HYynQ4?cnA8JsQcD?L4}V;NdmSO_zdH1%d=m}SUTYSg8Y z^VoZ&H;$VtATogAd;0ThQ6K`Vo z#)?5znZ5coq+TYe*^P=GNd~8&O2rp8-!&le{KJdOz40nF3eq{uuNQ@eod&+)yZgoN z8S1WiKQY=TC{xu&gUpi?dw(SrY-_r_sDGU>(Dg$7X7la8*jZTK`fG@iz^rbz7a7oy z`)fBEZbZ&_hz|PwX^52tF~_HR)v1aw6=0#rOq&` z@|W*!44D>3i`V9nanC2;XH62eQ)SmR0$r~xy|K>@0he&cwxINM#iG9#FLcMOd{ z6v&J1i3CFztm{Nno<}&1R@dHEkWXAtAx|1KXzJc{DEgqvW1UlD*SsJBgwcDaB@W3+ z7hy|1(wHIZ3t!xX(uwygZ^S_&Un-?Jp64}a$0bp|`J0KUAqx6F0?yhmM zLjVkM?N&F-uGH+Pt9{?o;Hm9QN7&4MMUrFb!Pi|I3~E}lMk22IEy-+I8W?dpd@~lh zZV35F%yPLy`i>it*plGVOIejwKz{!b&hyPWJ?NfJ0;wyBYF>9#cEUUCv!86agAFN- zOT0+}k)2{jQIPieq~G2-90UwhA22a<{a&2tTdQ?Z@dI=Cd-wI_GVkE@g$rC27~oc( z2AIzn{p4JwnvP~AFs0^iONBaXlhJulwxWzbhh4xj`qqI3O<#q3FzgvO4l8(QhrY{h zsn=4iJ+t?rxx}Vhg8PAmyR+8oMN37!n2i$>Q_QAmu9}jqA!S3rc9eZ}PmllkIMCrARQBf8=d07Tn6pS5^p|ez^S~QPo4*yaoObo4phPMFDQ+)mgv* zt@;eYdO(@8I&?|UqLfMb-Zi3LbMr>8VGj|ROa$=P>#S0Q*p%vd+=657P0I(mxc+02H zdc5SmGuo`1k-C+vY)G)?=pLhTPit?y2vW1NR54%tEAz5YYlN1%KWirzYMXh!0y8V; zWNpTeH2uI#(fQk14p=d~>kTE3`+##)Uj#q-A}|cmRaE<-ghoXuY05l;rL3NupRUnP zCUhn3J7rnVEcDd!h~hpZ`-Ay|h$$%v18Kh^=K9M$H^R-9ePEJ`ow+HRvOyI24gd|*ZBH=Ytp-IbgL&_|16?JZYvZ2b8qFQ)6(`$ELP&fn{9 z`Ufq`CtqX|4A$KJA z9`0clm!&Vg<1ISE9iuR_q(IxBKijjiC9rNe0}?CV>&uFGiH11{c-s}<>olGBBZzGZ zxP)cB>uKNF?gWR-0Ta`=9Ke8zan`8T<#3(rrVhLdFrqCk-n}SgCdk3+SGV~{=7X7g z*D){+Ge*@BsO&|5-SGHPi*QJRHL)|n+&HzmQ+t#bYzR1O(u1QNGEjd0DdRCgG#c2bMf{vfQv&_jN@5IpvF%IQz+v#xm5!o;6_6 zb}Sl*7^AOZOax|?R&==Zl5|#e>Ga!FM^>AtAZ2^wFrkU_alRD5r%OuzFP{!Mr($Z) z7~3ZP#Jyh4X0Nv=pP{MUsP|04nI-yljBCK=OGr$QU*9i>S@n}|juMP!e*yvI(~IBX zX}hS!Kkza|PZSYBI+@U`i|>drBl3)FJnRUEKTZ`QEz0YT9`X`yEti5_xA2@(bf3+j zdt0=wMwx`=Uq%!v8uwzpZ<7D-+j3{RlhN zvDG4R?a;?LE;3yZGjx|R1&(ij)r;3pmp1t01RnjeG7tz+Fj?1WNIYwOJHxw-G_)4dLfk)^D|v5&X+@W&9$h`O*66R%?hn z%)TWa-Yuo|=P2wSK?80KX7}z(`1O}+a#&pO6VaXfiKh5`GqF$To|f2igsssg5cAcIlfL6%=~$7y6x^sA(aP3AAUU_3p~E3gr9k0 zlL?P}QLX(K5Idd*{0q4LBN#P)%iaH&ol)U!eX1oFDP+o z>w2(q$mT-1>8)NK5fPsv>8o$U6FHI(9QSi>iSxw(t^)>DAP+dD^V2`hwymj%3z1yIOy-*O_xEJSM{pqWKEAU0j>lFWG#5&9O`Jji{*7MOitfF0V zfBfF5mIb`;FZybi+_H!YuvbxpAHkITd`7Y1Skl2q2YyJdaFnQT_#c-Kx;nT-fTFtF z;ZrQLM=2oSh%Cc+{=Y*eAK$?;5y%cH{6~xb-&PeF+1I-}4!I@#xhpL4KUWiHfiwO< zj{M@{;!I}AQ2%`@zwR>)(Wi0jzwh|1?{7$Z&~xae1F-i0qV?={UngKFTZ|_lPL{lb zoYWPdn+st4ihx@QFq>IeerW?HTdZRdZ-g@6M~!KdBDNy$j-{6UblPO&%Y>Zjw~`r~ zykQ5rp&7S!a^#Eaj|m02j>Sq^ZNu;j;Pky}vO^qS(XbWEo8Jb)Keh`US=dL=vazxg z>mfQzkYJ-Q>yfRQ#pSzFgJ+873qpq2p#B;Ro=Fn0xC-bi?q{?1^A`tEzT;F2%t@*X z1_{<-%M0|PLZ@%R&SoXv88^Um`kz2Ls#b~YKsKsb=(tQg#?mGdjV=#F=H*p@F((MN zFi3f2(ewmz&bdKP@NFAo zjnoR6t{|J`H|l2*N+O=UbiY6)Ak(y{p)u{8YUjdtxH^qaCXynGRdB|Pfa=@2S@W6p zz1p5lNS9E>6vmsQT-hiM*O?y;bA7v47FTDG>&YT>vBcH3zV_ga0+PJ4m;E-1*VaGT zg&@BFbWC-lYMaEjFyLol zk#jxKcR1INE;(=$R1)roWknQ(F|y6Rk;18!emFnr*7mxVm#_hNf(3!W@|46KRlsV+$*jQo(Gn+EjG zT?`ca#F4*Q?J`UIptQs`S*uJ{Zc{SQZYQABnuq%*s>IlHTt5O&AuI1K$$qA6Y1pQ5 zajOj1@4ri%sHIS|-yZHO$PQC>?6DF)mXzR%k#%mabKIQbs8ujqavCS}YzH1^VaX5# zCc|C->VQlVtNUxJ)g_Z`mBL0)1(afU>Pi=AvrrA!cAq=z<%W4raWpoxRXU6+Jz7L> z6&Wc0AmTQb(Dln=JMbl%zUh-ecvOa4s^nxqdRKEO$PbR4ZZ>8s?}y9BgLid%%C@&w zRp_fXOT$J|=VuGf$bUcbi7J)q(X0#_+D#isA5W3n3z{C}qqhdx7u@Y2orEo>1wuHV z|1c_GM%NTRra15nYBh(~fr+Opz$2tONKr@$(RAFm1UICkFBN9oxV@L@f)1n>Hx#y? zM^!7+(A$E{v8}sqzAEk=vdc%)wlT^td*TVR!_xdJ8(L+YU}_ODsdCEOjv=GoGyJCf znT|e-NUry@rGa4q7N;)x7VG@>IxTxGrfduYR+xBv=H_2qVX3 z1l#$Gpbi zS^u`x1gCYA31hhOhPv`bwHtQl(`{#&4@=gA2__@Hbn01|?iHkZ z2a;f<5o=e|=^wSxa$YkdVs6mB;?&sn^h&a+2+ho=ad>ZyFPU%fBZm)&HO6aezyHgC zAx#xFB0@uCF06iWZt*vD4&e&=TpF@uq4U@x?dcA+dC~7Vv2b-64Kd4;!Mgf>P$_Xj zYz0qei@4Y7!-ZVAR?tN|Rn-#DeXQ#A#C`S))Jl94JW-1ZY)EQHmeaP({=?3d;;~%> z>1T~k(97&FP;tKx$${rpqrygFCFC0-*qO>@{S)lbz+Sb-?JBTe&^Z>SvzyhQgYJ_t zXS05wZVEV9%EFM0$ijMmT#~K3Riwr&5Cg>tRqp-tCe;>-UYvqHoJhSqIz6A^LWX!Ce;7 zpRi+{QRd1QY)r#xr4#B>>4E|Z=Dj`)G3*p(c-la)cZ5?o@GwGo?~Y8}HDj}HJ`rRt zQR4ni!c6sNG#Hf~Hpc0Pcc#7=2^sRn`|+!W<%2})iiO5r>TgR7c!1_@Ph~KPG1STW z3kD?${R{7?O0|-mxVAuWLxxl(bP!(<+(+u_BHtB4|Egbn{4ArO!6kmrLSdPWy*r`6 zz8B^H5@~t?&nwgKWdjouZz#1WDjM|n!tC{m^uG5F+ym^KQ5?PKH0opqWWnwOw8k3xU0aDTf9Gd>>=R(p95=HPTR1F%5Kg1A0`ma z57EKvM7o0Y?$~X!9VJsuulmd63O(=a>nVu@xyx?-exC0d1+bwtaADQE-kPpqI{P0i zkT*k7%10pheyBe^)&s$0jYJo?=o3Zfg1g>}oj6MPP?&bXMZA60ZiMyX3hzbKSQHkHo1^R=BXCgtfPV*R)t9Xry~?!pU8rM)LxT?xq>~v+o#KW z8A*#bpYhWc%_%3x6$Tqt0{Iqzv%eATY7d^5V3@#F*2WVeF{;5Zk}LD4*#xAthso|!V+mr7 zP8)VmMVWxRRx^8|Qb?D6SvM&METtBX6gs=_YO?|STI8H1GAuHsFTts=p=nnON$Yq% zoE=!v;+Q;eygVA~NrVV53~JjqfeH;#Xx9^$T2Sx95Rt+B1%26$ltY((4fp2BCjh)# z?RHM^Y9Uo_(e{1KGFl)&EBX*&7P6;&n5D~Bt`@P19BSd%DGs|&K$WmpR?FF8@JW1% z-+NSKVRb!wokyHq4UcU(MC4vMz9Vk19PmKRJ zhb$gi8gBejLvZyAD=1Jyz#xB=Z4B-|C}YZR&Yulx{w_`_bIM$td=p}q&pn2;KJ7!r zEPl4S;+8A8v1GQZP*7!Qv+&rp3*NgY#X(59t{A{BY=9?IR27Uhe@6AV`_h*5DW+wk zmI&f7=>!%LksMNj=q`77a4S3a^Q*Q^4|(($I1J)c)*Nf&5uimc=xtytOc-Uho-5F+ z8~!2Tw~u6!7RR257h*Rl=Y)MWD#bUYO?LWgK~Q;&=!fF z5xH*}D&R1IwA@|M4UDim?g>4VJUe!G>LjDGi_psmSWD7_{939DhNbMPgj(A|;Q(-V z8}7IaPLXEDo7*SndUw3$2o`+?=WTPi+|L1<_wSn&#c0X8ZhDh>?uCmintzdvOzF68 z9hA!5m9;8L7_pjBd5k96oEpBxS3y?Pf8v5eSCk=!xc3spMj^Pjp7=UZ3(4luG7kB0 z%s0CMO_7w)p9VfZ>`H`F+4zcGDSoLX(cEeJF+H{TKBw1H^cn8t@7c1x3Dr66xzUYV zhHqXTlcsc$va@^pZ**;KIGFX1gGCJrTvZ=j>bfCi<+}gAdG7tq&jOBMPbej9XpAxT zJiiBfp8w`m%{a2rMrIdrlQo;jUR@+%o6w`#StJEyqiPWJX6{t*W9>XO4imw-}U%$~yT z*AO4IFiS=|=LxF6-U#pOF6?A>Hxd$!LjPD8YEJ1lk@}b%>@^aq58uUYLOOd1LP9*G zR!=IIcU=MvY^P0^T&wD;i~oK@zs}WtgZbdgZi3yWe^OE*zYp61*x?tJfJo-870B(D z(UQ66G(4il;3eK1Ym#1=K=$@#gdGNDJ`=1ijV9Q;M^f$@=oSTfE=8oTT9*(P0x{Pj z*EI#^G;lV2t^$E01`h;fs}sJMIqP6yjB_u}`XyjH!iqeW&-r`JDD;Yps>=Q9I6@CJ zL73JzJLgHA{}l9x;59?>KmP3eE9s>L(k^nQ_QT$$b)8XJZP=20d)c& zFPGo5mLgO{spROPhr@HR#xfG(vs(IaWjuiy%*@N!7)|S{5UTg_DLJkDDSlm5KWIWh z2gZr%`vts%-ZdXnZKnHpY~WqV`QQQX>fk$l8Idxh@{#?c0^4|17tnY8eDQ{l;N^kH61oJaf^ z3&id3FawPWbfM%n>~Q_?k@g{7o@z6<5bj4g{ysleOUx4+M$39nl6D<1CFIN*!Kc-thCUnTY(t0}3q+eJ@M_G^;^GmgKe~Ms z5;ZR_N;PH}JQcg5Xms>Qp@5WSO3JYDz4)qa^e!{IhgIL@g+CTajj9=-%1%qwr7{Kd z4VK((J}V<#sYmof=G8ZTET)>td&i$v*jCpYa+t?~ZE#M8h_THibH1aed?(!@8l;zu zJ<~X`Qzag%^ub<%Kg1dD>&&K6+IPx^TKEIF9>#mJGWCLnj+9gBNai_2e`Zt^j{C-- zp<=Wdf3=^B_F9Ir4c!{GziYE`UkJ*WqW-zdILf_@tSI2X*+f|%(nKQPzpPzOIc}kj z7^%JJ7{5q`^%}10x`Hx=!5f2Ge*AU$w`v-)a={_1-f*=ocAk2^ZF{9da*Q7wfk{>c zljjgbhI>qJdxPXPCNo(KMEV%)*)Gkb75-?rzSM3`7P(;wWIfzktONM#7HQ6~yYm&T z(v@*ve9=;&M!|(HX{FfF8r+KXJR)(O^L>L(YP?zS5Ayb0M_t}DP9Q zv~3rYaLMv6m}M=!XJW+hbanG$E8 zFg^n*2}N#llaFF!WH2=u=`htcut@Y$7*bMQH@Q&G2F-z1W(l0rY$Yac-%XCs%q2EI zA)7THy*uS+eHug1Az5so%ASq%*O&>ix0#x*wjw^nj)fL7M?V=B>7PqrQ(ZqK zcW0i!N4_tVfXo^+*-s5#xuTYbhaY5?H2y<@1Z+Ny;PNgB z1$gk9PL_;R2bHy2rk?IWev6+|3Rp*WQ<8WXSxw)hx(go@zh8D}_cx?*VOH32F>p<%Tvxnd1r22LUp0iMdqHvL8zYhat-ThW!Dl@ z_p?5v)!G3px%&~6=`^TH#h>#cK!)2cA1qqQsa7@{(SL&c6RKkC3>u>|W3qN7R>;;{ zcKVMss6Ri)mh0|pW!lq;_XRj5=sQCLmdyZ_^jo+T|1V7trs3Q2e@KF?{+p7Z2{yuW zG&qF_x2Wp>@dTg>6+ou%0k531bqBp^_M&T;e2=DPj(QlKVwj1E>G0g{P#Ae^EvEjr zW~fpP&vm%FV7p%x2(f3Klnl4rO+}Cm*o`2rdQa4;P7WCO7xWTO< zNDcii^!=X6nE?ILN|nBn3U&9el)lHdQNh|HFB1M10{saHfnG^;uX6Z!I9s`ri9=T< zYmLJ;&r&BiA}`oz)-`(!nDI(_ei7pq#`KvQNIC{aC2eF2w<5c8ziG;K1=WqH!YYpT zd#RN&u%fu9Z~rroU8qYO zrV3CGh>)&pW%_wNVqbc3;Yl-(7({${Q`O4o1J<^iuWz}j%D6ok0z-E}Q_kT|q|zsTs7 zZ!=Ogg2Y={Hg<^_)Xc4}*C2OjMd7{PEfx4Zd(RGkw}&>e-DZdHW;~Np|L|p8`rGYU z06X}q9WeCu#VGkrWA5jAmaYF(9?6_YpxMkF?YuPIj7YQBx&K~gHEtvP^Tn|Ab4B;h zyMQEHz;3MB+&4^3qn=r8+)|Ys%gl37Q??{~;)b;x@>2p=pVYvv{*#Sq@pO1~e$ld( zA=sblqAW&$+Z0~p-yYt)Zr@9*zrl?=9My5)YeEnBAIblaAYGGOjk3C>E3kdE-n;R^ z_}dk`$|}_M2?aLuvmQYm79L6}w-3J~Qb&|OO#AD9e?4D{;;thxyQQ1lx856o&G9+_ z#h$Ph)O5{s>qcpDrzg-AM zO`+tvj*xfS;>UKcO2%Suti4GR#N=J+FH1e-m?&SKNGy9NKLYK#Vq0#<$y6(f@mKG< zVh6~QIx2L+CQ4CCEJlMQYb9b;{ipM1-5PM{>+G55P)V{%L4b>@d=8LMrS88)MZP@$ z5EbclWytU6=veJ&>g(Y0I{wxaDU&t11F-(6KPIVD$Iuoh#F43DQbc2?ho|OAhxNY; zR2zeSzF!A#N>Lr-o*xS-+MHUn|J?W?kmxt@b(|^R;UcY-19efqiVq`n-T#FCL^VKxeJchi zg6BHp5B+XxP7gpqShsBvx?o4qUSqJwcD`dEfThM402ARmZ7tF0k50|HUZx>6*??GS z6>~QdVti3i;2)q41{|=!wy%#Rf6@Ym4j93vnddr3slOTjUw040m;e*-@Alv0@NYZ# z_dNKwANc>~2L}F+H3EGGhuk^}UG=I=|4|z7pJ-S`f2hAMya>TYtQCck_1 zy$eM6gTI4{fq%FE9*6%w+JW?i2vhS>e|`l~i|N3ag3#SF@v^Oz#}}gf^FpIjI$({Jkg;p$AZ0%Ibgu^v;$%9OoaFZngi@&Vwo?rjpJ z`wPdW;C2V8P5dzy(}MS3v>rJ>Q<$-ulSCv@diQ=jA^wvE*-@^Pk}k~YPHMV}v7#q$rvL?si5OJY2tCwq2! zm{ng?cwHH$#4N`8@^gi%krYof|KyH=QeNNlu1|?w8EdW-duPKC!!2VAs@1Gu3bcQv zR715U|0`O3%}JRtd-nY7Zl=6%YQ5l=rc@xMff%sAtOF4wjvh%az@6Q38)ej@&h%u> zKBjX%|E9_`a}$U0(($HDZ0FXg%CwVIVVab?ZP7luuMe!w{JStk4tZvGBIbsW$dJV; zy>0aH2%_xaw)-$`hJA>JFOIh39fQgL*gAxN^6}xqyTZKd9ZNpJ{m*;i*&+)QGS+6t zM_YBPdbchLQ$CMq|2&2Ly5Rrg!x7hx;vhXPXdy=3CUzrwXl7S{WT<|B32DAq5lFrM zxFRKEE#d;*1je!Bw!^3m`H639NFJ_f_Doi>1Q*J*`RlIb?cTuXvBO{RgkWVUDyI@n z$9qSL4VZQ%TVW7nP8f?THz4KIDp6UIZx@_{UdmLwX3DuXn&T>s_Z&Y$^!}}fB)74W zYt1D*Gb;CZAZ1&`k~yE@hv845y1K>3ZK0?k=Teu{=3%=QgR_t>r_D4G>lK(#dFx@S z`r_TQD_i|Am5!kLvUY>WERFV8%raj6W7!V*`qT2As@L{I>XNZ{-ZPb#hR#&haW7hH z`;yy2e{$_^29UK2^81>CIz}H+SACRcq)!wFz3#YvAE+pDz@94N9Cwn8off*?-l>(6 zGHS)#m>A3Ht;5F`7F*irA^!UCwWQ1rj5*NR!{-O^7MPlTawezwuv3`!P;agGTW;~% zLj9oIv%htrVd_Yl+ZK1a{nqbf!nkUwRrDB4(iZ-Lc{19DZ?pE#U#0!KO2tfB?xA|> zen`*F`nY;XHjG#3#=bB~O}rRkTe5OK_1+fKY-*vP)E3lkil6G(ROk3Y$}G~bkv+Vm zIk(k+W|R2ySXT5}SQ$~c;|E2IW!CJU;;^yWpupp|`~&xQ86zeCD2@R8kMC4pS9b$4 zJL<*Bt$-||sp>da=&qW+P9Se>LGyNa@v4#nO=0pUIH~5I|IA(YogCSKuyx*cp5EiwLGYq;K*-X2dhMA~NE`8p+z9VrsAIr!OZ`!}?J(<;vDuhtqn?oM#}kR`zm}zk@ekh$XYU$v?M|K`r<@B7jqKF&D}YRk?`29l^j*D-KfR$ za1b-@n?W!N;l zjDFV(r=-Omqn6i;W**cu;xZ^Nj23}$P&GzhGKfoj`=C(+ zo`Yz4HVn5YhOM2V?q)^IRsR5ATyp+C`{()wGt^V~j)M0Y= z4$67ULL%L!SSie*n2KZoB3A>wK3u)z{)5|wws?b9n7Vb^JPqz&YEg(jbOWLvTvx~ zB|{{gG#f&o^{23M<3y9jnjIUK@tuocRzPrSgb!GI6#g}IUh3Gx@M<$C-2){&nH70| zvBwm{=wE(oynVwpb*AiciNxd%hQOVK6!!MiJ#q#!Q0Cqv?s>fKww#6{d4y`}9v(XM zL;361VUh6Z$ZjRg)|?eTZHX^yAlGK6EVQ?QBrq1ywf0vykr~{9;4HO=sWKu zcey5R_|A(E8Bb0N5!cWiuD4GRN5wL3y>alsu$S2o&n?Baawm(EU7JgEWCTM;UcQlq zwjD(+mPdLg<$Z~GN!A|4rv@r+&xOCq=5=N2Ubd$C?r(x*<20kYn>=xidSO zApHu@_m9&_8Qya1L>Pf_@y-!Saz@8Ag_ULY6c7>FK0Mog{n}i2t#OGFv>8E`)Ts)p z+v-jdEK0hi{v}@_05<9$?4|#LAYSC*bvFxi?k?Y&Cgz}wFx|oRppHU?McXrkn?2N#i9Mm+GI|x(#EJ+2| zG?tS0^NmhR+o^FsI7H!Vmy-uK9eYhEAj)ByQl&-<(B|LX{TudpyF{n>dgB+C)N686MD$Qnyrb|tR~U2{**aIa>La*bG{h-+{%I_ z*0#JwQa10-^~94sb&c(bRdZndb^{ZWZZZAqG?(`MA`{+yqxbJF?VldJxfLC}m-tLB z_vc~N(6ZiX8e4lBBwVi$I*{g!rT{=i{jAdx1_K`mAa>zrFsae^0Q>}osN=BXVv5f zG?(m>%#Tp_Dt%_&R#w13j2`p+xnBu7ChyjN6_f;mN4lJb1wmj_ajlX^Ev*?(8i~r+ zYb%vAO+c}m1cWEb&tTe-IvKD8x#XFta<|ia(Oqr70hZ`&JF1(3|5wq|&Eh*7H=x0P z*Ezx88vIMLF7!jn>C3&p73&{L|CeH2*Ia5s4S3HN;2z);L0ElGhEnlLcB;II&@dXE zFupw4@?3wZP#CA{b?dJP&K{?}$Ge_m%#X7+{5fI%s?XN%tlE{790JfG-w7a@X{5U* zRLs72N3BbD?E$-m@y3I{_;54Jd;P+IlHAwv!MX(t)B~N@v*i%bH)Gpy)_yAQxw0DL z$-eJJYQMU9VyU3hETFrM=at|*eth99^?laDCJe;~eG=R^kq)vGI@)CtGt*Qvv^y$( za{dinc_hf>U7_l=YL7d5Sw2-@)=w{x#+uU#4O{x4=nS>}owZl2r=gbW)a5oUljwu$ zHGukyV*ah0_J66}ZPtz@^MI0xF7ZGe^kTg?zCh1%eC-#&!dS)StJ;_oUai?zIJA4m20z?!wWeRwdn{rLw- zMB+`Yy6MCDlIuZstyy6X$Fe$&bERp_t6N(OFKT7FspNefY^BtPXCWco2lW7MEg9eB zlPU8)5wDw5f=GC_FNof4?&=u`EnL=NV$W#bz{`S;Z&)~$WH#3_Mk%LxDME$Cz(VTb zhhAi}aGN|{P?|~lCK2ZAgXwQO!c)X=YhZVzT{}YDoW7Ylf9uSKN50I1!|t*i3CD*1Nmf#1W@ZX`Rm$N!hIc}xs_Jt#cg zTG39K>?sv3DWzl_Q!W+oX(t6nl+5t$eK^~!%tO&|1{DMZxXRWJf%Fd zl+|R&4u@yyp}1HHWG7-w`8xmVkbervj3S>Ze+EEGfG)CJK5-Fa;Q7cGZ-Gy457hMH zLoE${HSx>UYp%P0$(j;VW;BGmVfIxSejKhPc2sDem?d1nSH}+pyiKpZ>^3OYEM6Ij z8OuVW7k!H<#`|yQ2^H`lhm!;43mS6{f@GU#@Y{*31@kX=ugZUMUDEKn4OFCjVnfa2 z!5Q@I;q;EvJB9ZHZAxzC6JrG9H+Yrc>cklQUnFIHCP`t>~MdJysK2FzO6 zbHL_dRYIEc*i*HT0EvtGyD~m`hvieh-87(Qr>3-wE5{|6dBwq7TA7wo4;zY>zLEB= zVu7cJq7FduS*M-VNIA1W7_^l&0yuS+4CNw3g;r5FtzOzb%Y$#r)W|5;C%jkS%Momt zcX+P2xcEp7!8m%tINDYEo$?9AfR6+#`F~0T@`8Y(gaf;9%lNO-h6iOc&-s2_kUf^D ztm6Os@TW~vC;ECX18C*Ni{Iqay=&V51r88buYqd4gN^@A(dqx`aetF+Lon?TtAqV4 zf68tu@Yok$J-EH!J^ztzZ;I>M1q748Lh z(ytzzl{*xZi>S!S-dj#f5Ry&o=f(!~o#1Bw-MgC`gY;R!BR>|bK?6!u0ON48EN%-k z)n&$ay=kTIG^guesQfnMsC3Y!;#M!;h@sro`E=397cF|2?y{S(m%~M_8S)1wJ}tZ& zWmIE#%Fg-l&rAiXrNPTuy>!%hi#qgaGdm}QE1m7#v)K`6mx-^4&S9DueD%~7KP-L` zA8e;f?n#w&8n`1BR;g?@Q(pt-c4gcA{g4zX$B!Q$)z*LOV7fKYOU19ZQ)yS!rHze! z#|5$A5g2`CKuIdzJ8tT-fFM^tc1@ShJCi{!Fd@>4TrgqHi%p zc97}p^2w1#dc?2m^?Bg6demkz|C$T%6A~rvXsVJsdAU447LGt7vbIWN@1Dp@{#~|} z@n&l35yRN@8M-9EnvYv#yMzSa0R9iQhkN`Rci_zp^xa^IvGl!Pkh*%Z9W|M<_l9?{ z&_G>A3Lv+tz77XR{2~5=F>WOw8TO=Z;GU;~VK_2|6 zOWyyS53Zduu6=_FJ}P2Gd{PJ3%5|s7JL_uNNOa~W%5GdeyngWIryw(jRMPnT1N{r& zU`C18(rES+e)R6|qM?mQa%`R%?iMgb*Bmhtmg?DfK6t#}yAgALD01$Tn`=G_K5K;pGv!e2!BsDz@ z4qC9F^nY}=Zy}dHo`(8|ElIn-k4~wE<+6WCy$TDgZhr%8N5_*(6s~`2cMPi6yNVZ$ zZ!xV2o7*5{G#Y<{L;3e7|69xZi^Qwe<}$C^<>JQ24+dtcmXo4#a@5aUu9%u zw!*^ukI4<|yDOzDx``E8|Mvn$i38xjxXD)dQpIBMKemHyE7c0EX8cn+{Q&i+RpwJ7 z^{+kt`><}O%ReW-7;Bs#{mbb9b_YW`HQfO6|I=u<)6~FEG`kspb2PAxe;9o{$RzaN zqQUDa-r@|#;_if%K^y1)*Ia(<%T*au=>OQi$m|PgJ8w|i|F|$1r6p)pB~9HAnMYdo zU+w;Dyp)ep5Rv0nru3GW)!|gL^}i3%u7hpW82M9+c`6K3o8W=}>(0B02d9|d%Ko1G zodQ(;E9`Uj1>fO+98|&aeQh6LxAW=$*;*C;(id}e{d2*8?`f^{pEiA{qiq!4FOkdy zn1pUNyi2iBe^cS~3>6VD&`NQTS<||4aTY#BR!<5Q&b$vE?%0?+gCMWkW>z5g8!WP@ z<_4{9C&X`GAh+Eg8AVUpF<8X@v(fW*(6AJ8S@sa;ckSMmb=`9O&37Q;#lQcm-sP+M z?hPmNx1%{FL|0_vn+Q-`VYKTD*g+ROETP`ps`t*wp7e)51D{d%T&WvLBbw*lvY&?d z1pb2xnc{%(@+l#5oDTWR@046T@9~6=a3Oz8;5q7|MHF?bt1rYcp?b~u?GfC#mqr+? zRJP)SiV=UL+1H3GmW1O$>?|ZvdG$EF@t_Au%N^qVxY-_dx6!pM{l|vbuuc%W)$jhh zIrjh4_ox$+%dq#D{L*7XTm5}V`G3a>svB5Xlnd7E|MkmYa@^)v^S`i(By{(Di5M)p zo+)p?jgT2GPH;l~ugI!h_35ORYMpAO<_3dIk@Xjtm9V(6c7vQerdZK~cHH|Gv+l<# zyYA{qV{F)_f_!$Mt-g2$9ddbwIej?LG^edeFU{6=)Ws;AK7LH=z`o3HJuBq9rU9i&-N+ay6=yq!P)2a{GGh`FJsdxE0gxKtXuOMZ2r!V1HVvE=li>yno z61r_BnPHoI@nK~3-y72pO&XjkT{H9`=f70<%fzlcU9T^9SCAJ(g-r(IqmSv3f_iEvc=w%AO%|1yXRn5yPV0OFVe_8ag|Y zCd?}!P(b9fiXr5E1@rRqx=4SRfWVKD%g4JFeJ>}h4bG{MXCEG&cJQtfAh!w3BlExLM8DcSCh&BWcqjkuMDVfw(7^pf}BYXaCxM%%+A`K2BNiBofX zA33}~rB%Eyj=Yq;SSu5uYU@A^giu6fO0}aq2=z)5|5)lQX1I&0(AKF;int z3znQmMU#_~#(%w))ntz25fgif`GJEft^=?CZuz>^=-1t3KCBQJxTVGF#Ky+G*PL~}W%&HB<#N^-eUOvO{nrpeec3)QYS#KR&I>Kx)%(lhVhR+*e9W`yzLd$B;I+gV{_vRK6xKez=Kx6u1cRo=90H zK{Szu^^6fk?n+a1pL}@eX&XcJ)uTb%DRo;WwO#$1Ni!<C27aXqM!W9kP zrHQ>pPCha#E6E-6RoA%4x1EN-Vs|iGuWM0i!b86_iy>#Rnjj$ z8~7t^lX}z1Q}9%ow86~P&3(T#p;<}%bi`0ik0E>1=cL8!{#VFUtfiLN3ao>Gnn%oV ztPJ^JSlG4ZJ+F89_f!LMCKXi#HiUyqN*n9mzTiewMfs}OKVGg(Q9n!_@Aw*ey;l+> zPC#Wrvd~vHHFsYF1(z%5V9Qsvbx#K=$#`Yr;e91-ZQ@Ee@IxofLd}azBH#BHp%~MN zLVp(Nn84?!*$1(Va(^y$%Ohw8Ehh?ch4N)33PpQV)n;>SM6 z=F`F>4>GdcJ{8K7hp>x1!?@-9eYkhqdu4Kp*P?YHCp(_q2`@k~DzW*@`Ff z1m|*UT@>V5F;_knx!QutW*%3CT}~pKTo9pHmpyHFENn^T#ks zzZjqxe}&bexId26otcV%Ir_!v=o41Yl0xgstsTed}yabEQbuA}d4ZEs86 zle2@zUZ{#Zheq5q(B1t~Qc($(#3vFtk)W7t=jCkfamo!kAgW$-5<8MpT$sqDInoG3} zv^HoM-L>2eAd-&)J5bPReC*`*pdQ3y$+^u1Z>UNUKTxoz`<*eF@!9L0BD`+t{Pq|x zwINrGEV48CKSQ&5_6N-`8cR~9evcIN{CE=s28MMMHET{dWM<_ve&x|G-uqYxt2mp% zm|D$aLolw~c@Ye$Snd23q!n}r#sEKE`ly6TYeTHUinIn4%#*cr60eN4rcC5*(+;VU zS(p!rr++E39NvA^z)4F^occ08QzEw^R-DckzpWQZBb=A+Ju|vzN_V&OWAu^*@cD@$ zkG9n zdlCXMG)&LS%j-m<2}lrPl{-dJ%$9~XZ_pl({Y#0v34Xg9h{F}XA+b|U_Fr%EJkPbL za|ueO+1S+Uj9E5pk`1CG1(ROV#r58YidVf-hWK*gobAoXU)#lfjZ`EOr&kt;{8Tmh z*P6JrZL;KBvJX)$P@Z|T!ROXbma+NxMu;F3NN;uJi^sd-ylCueub`l*Yhru$Q6a1? zrmf(Qx)!NHH~p8Z{ulG^`z6MZ9xkf3tnKLcu180Q@Z+`{GUR-wD1yB#QSKCO4{p1J*eF5U82zP9Hh5_0d2xU6didJpi@GRIzE z1O#{mk?PkL>J0u#jo;^Gqn!USk*&*0s>vQ#y=n#j^XHc1TYqYSjZooMEs$PKZ5*M? z0{0lX(eHIKdm@4HJ8^mg2r;4n1G+h(fY~3f@b@YGc-+_PjJtw4mavAihIX%n+s=i( zb`=f1o?q>>_3{Qj#kM5g6q9&GR`1k6b0jJxM0G!hwMbEjTkS}L!s20pAF3Z|_TnGT z_2F$z`iMd#oFIk3K|fl{e+yr;eabJyQzzNPP|ds>uI(eL<5m+7=5(= zlaS(MxlzR{Lek-t88*gz>S2kx=5OBSVN_BuR==6|>^r6{jy8%j<#6k~*5f+SKhUVD z46XSy#Wu>5Y0%r#X>kzPI!M0{S*(?ubCQR6A5@EZUev!7^7<(IA$;fWMfJ!qsY@+r z@;hGlFDI=&GBQnlg`KU#6xWH`OT^K#o$@{8cv|-HqO96ym3^vFSPQ7{tzp^HERuV$ z7)XYTSIVRnuvyX!a(}Hi?c(^k=PWDU+O;h&a7!^PIbBaJS67=oY+N*+h1ro32VDS7 zv7L!@||!0>%kvOP~G2mi^vZ2;H>r z^%8mVXEY9&2T%HXdHr)DhY;S#bI>Y4_^Ru@?XhWYm$pA zewb={+`|^m?kQt#{;0fpUhZp*nV=s$rWD>LDnhmrEH{l(3x{rUAC_KSLR7r|j^;iu z_Mi9q!z{%wP~6u1&h-}YG^4}@(nUoeQswp{xZbnLOm0M-XO)!ZAW%@#h zU#K)QNiae?-o@jKrlBA=>^XV#1mWW{k*^Gq&p}t?NkUuMCj6U~A2m1L#Jq*H8y;>S zK$}U2v*BGN(RjANCR3y-Rp~_s9;^dhW3d|{FrCztRz{P1Tpp)Cd_!hynznJK#;YC+^ z9OVrHJDuMp?W&*K{brbQ+*Qm!l+`HMxb4n%ql=Av3`HAx)^8L<**9+Eo?y&w%%OKu z10+p8d3bjhX+CGS{CtRVo}!)uuiaDcl9!*7*`Dj&8^_NL(Zr`Sxr^LR<0c63kypI; zyzXW45ud18zgL#pSft$3jAe5JIo`+;Wu8vDv}o+e+hi^j*2%jCx7_{jt-cz7|N zY8tir(pb&%K#BVj^edQogc9+U05XW#nY)*zpe&!pY^q#f-GOVB)l9+aFdF}@ONm@D znWvwi>5CiciIqtF0qBdUd%*I296Sc&kSe0UxsfMEq{Z@+a25Ei3#2L$`YnHacOSjMQty%GvIu<3Y~*_Wkb1mra_;K8 zKz1N*JDYOlwzzGMhdG!aPlvdM+dmY%$u6s@{`#cKn@uLE1dn9qn{T^1 z*??W|uHD^=%Ca@i3u9G=M=Xr}ROm6*3{^*WC#tG+VT?TcR%)SEaI-HJU;DZHhZFXw zTzt%fWn8v@(;|k`{hI9NU3dN8S9G8G;JhL!u>o+PIr3ziL@d-;Yg?c{g|KSnM|Gc$JZqldtui>i-v&7WW5s3FkWpXu&}=DqIm(`u@$aqrt)O$+NuCHIab{a2@YMpsXl+IlXKssLW zziT-U$xTNvviO%e6V?-djE*s9-vkbFYpsD=C0hr}wOk8@^bIvt-9u`x-1p;`Rd{tg z!<9y{taX-hvp%fAyo%(r!_3$iQaYg+$zmmyJR2OU;i`KZvXT2AD`3-Lg#cSNpizm? z?g=?=Rij$67AZ&Zq^3U15cXPMMWC&^IY(8@6WsQo4at8zR29o1@;iF&T3y43j-rn6 zUO`C!!IuPRV*tSn0yT`C^1$bjl26+y^&)Y?!T^yDN>4vR1e0a6Pe7wk4wwU!X6$OX zUGLpuz-JiU<4s?eh=bwnfc8g7Bgn)v^GRA*7Lr{`r}rn%7pOBQ4&D&=&)0_lo=mQR z>LcjV@N!-*lldubW1!sqc)m9ka+aNLzsV(t_Bky9UXd?@7Ja<}gBBVrU{a>7l*<#41k^lVV;9cR0~%vGMoeA3{aM?+EV z7X@3DLf4>gxGy$@IJxqiV`7riQa+C~c#q>+j4y227%&;e44X|R4?o$A&B>dD!;Yq5 zgsP)EnWuAO`18xD+S=@8(_hrf_yB|+DfYU+Gp?t$H9WYJm3xYVLZY16k2n5N`-aGl z^xbnJ(U&9tLP+2KjEp_whi@%tBCFL-U^Q$@Rf4S%QzEQ1!iaFEi#a4WV&5W?9)G$K zdnZevl@#r&{D_li5prk$s2oB>M|h1QZ@2Y6+FhpWmvSHQCZ=&l)KHD+rXjty32dbz z{PDI49HcQciZ7L>whMk&Z&EAgP7dl(mVJdJYc*Lq@!k+n{uMjkAEjyrbh)w-?mLv7 z-L{C?^$lDre?2eIjJTzIX!{`l*q1KwrPMkIy!FaULoWVWB_U~ z7{o?@!dWBxuASO8I=2;m_<)tnp#1Nq*M=Avh8T~NFDNNEH z&wH|2MSz`BA`qTEJHHH)Hcml1y1K;Vwa+Ke0c{Q}ClgE^o!B2;-m+t4Y-AVsaOVbb zkPtl13=O>3x-xCx>b$Fu)noh1HkkXZ#BCL)x2{4|?#}X14QhQD8paHN4eI}C@x2w`hF!D*O%|vUslNUxEFP3Jv}I=|uzmZr+ZdzYmu05=Oa!|S!gL+}L^V}LNmb~b{8SocqDGtL?PW@utxIRQ(J3^BC0<(r5@|QBB{hJWJfZPqZQxD2& z>H6!E7qkI3|7@7hF|rUUSon8eXD5V7669$7Vi*=_J`2F2sm$u47RnJm5MMe*cNeCo#j<&P0o)*Xw6g3T{6jWMAds~HD5 zil)+l>VxDxMFl-UUcsOgV&#E%69eXznMpjalXbb1*~##I>De@`2Ly+%rsIv&%j(Nt zCb8^U$81osXz>`9ve-WT^L`q2bMsF;Oy4m%JpJ;zo%HReio`Xn95e$c6&d$Es!)!U zcN^nuB%Vu9a>xj*_pZx`e6Giyob5}Nr4w9_d=+hrA}QHE+I4$LYcL^~Yq>W=>U`aJ zyXJ5(W;vy%EB%YRj1wUx_~5nrS-ZJ4_27y?w1_t{Z4G3_DZsvt{_$$b$lQuNe5avp z8N(WaD?HzwW{8`lkY%4x0c~qk*gXGKn^1fWJ$aiCbSZc7^37ac4q-K~wY_Xu?Ujp_ zJ)}t&jw8w^oF4+?0h3LpRiZm(Db2)d7HCnE@p?~|!1m&btTulrlnUj-mG@k=3MU3E z^|ceMR5nR)S$|Dt#SJIME!or%vw9+vT;79*mYln%|V%A=BZc(P&OliFzQ#mVq-m=YjccBlOzgs0`I;F(X0Wx4BmA=?H64z zK}mGlx^L3vj)nS`FyI|_mgWi7g74j0i@YV`TG7JNVmuXiwlt_)F0>fi{7+qIMt}?!Rbm z6*4;1AH^C|_1MVKjvC2WzLT+}(FcG=eggGM_t$nQeQkUc3_&LV<(f9cg);r;n$EAcfCJmyu5<8(i$4`?Fp)UmX(bK zOe|I5in@Btg@h}Qiyt{b`;C>Y+P;52?9Y|4pp=eOfVx1k+#I^;g)PQ1^nC_SC($yn~y8I)v){odjrUro!vv_+3rWMk04J<$UWM zFMQvStzJU~yq*^A+u4Nz+k-l((DBbe=7>>yU&%o!VGdS8jzE>lE+7KKVZIYh>82jGgU> zb%L?|2=2cS@u4+C3k$@lPI@nBpcJoqZVQNL%1JwDGO+$We|gg>J!rG!GNCNwuqpw% z`2F-SJJh=ZhGt)!Nt7X#l{byOEbwngg^e8uT9-F+*gG)GXIH^z7^nis`it=2XROSp z(kAE3^^qEO^^Y=j%`aBFhWXpimy_p_J0>m|G9S(@LAhsKd3NzaXzj*N2OWAdu;Lfc zsh$U8FHJEV4hg^PQyhHG&dy{l<)ND3T%3#MaJBi?gUQ{uzmjij!pqXUn9sbDUCz}t z-^ohZJXSex@~@RjVQoQJ6wROUx3g#ea8Eq^g0d^}0(zW6*@zTxFhQwQ`l zz^D7SYeMUcG(h2X*&7+ha1=^nVY5&wqQt<2VPF=NU>4v7!>ZSQ;CSu!_1;#BpIMq4 zOIS}|y2@m$%;}^B=4mh-(Dv%iyk5F4!mMr>p(BH>)Jo6SPt6FtkxAk5s`>LEu~=W$ z<(0s<(@;<_cfGQH2fuF|UtlL++LtLQGt=oY8g(nqJtb|94C;(MTTyCZx}Yxbc>Gid z;|9K2O8QzdDAdOoukT$L=9qbo3Hu`yv%?U3@s!7teez%%J$cV+!qn%1?m z^I}R}KE=!1#bM^_FeSDuja?~NYiWk#4U?IAF(Z7>)^cVf64;VkrYK%ss#xa%$tN{k z*ZIJT8g8*hXd4*ltj79btN5tUVU|1ocDRwf`^C#Hq)I2$emIsXg*Na8WGanC^E3F< zv`C;wJ=V-R=n!qT%Mo()Z>*``sHdcSvBQh!Lex7dJyv}-SB=CfQHfrLyGH;MDDPB- z!m_{Xhq&u-HqSw(`}Fs7j~i4Q4}Wg(C2HZjgkg#xx|aL`Vh>}3X(CC>e^*gTHmGje!N1Ynl-pnP#Y$bbx zd6Oi`mQ;f#ajI}SMJrda`-va^yGR0}`Qp1i0MWK90!(zj= z^%gG7ekiGs{wd!Z@~<#0as~JpO^I-@01Mm{PyNrUsZ^e!OT~pdDvf|@MB)^2ecqPC z`IYt2&nxOhwC|HE)M?PS zRu1lbPnPKD*9RvT-(DIP?8L?qoVQIS$Mf#mYPh#u9nNJ66CP@W6$_9iL`!4LXQ2_k zGEFM!VjS7=?@OON#JaLVHX5XrPV-uAPNvDT9AoXs zVGkhjooTiDt_S*)6{RXi)bP?}sJ-TIIq%)*R{|#`;YvLr3urb3{Ab3r2BYfwxb;=bY;92e0=*;hjyG=&1tT*e z&VP}9_?Jz~=h1Y2ZVmia*(a8wq&J3CxhO6_EJMDb(lSOu$5AjD#&3UX_}Vr&sVoW5 zRDzwD;~w2}bjXlbLjE;(Vmn9GM+>o&@NVXmciWj7lcw#sDLb|`jyp+TrtHg_=d=Ii&#AZqX z#_c*!)GoxcDY+}d(VXx(0F@3<6lQjXQu%>Z>G)Y~pj5B;d3bWh$H&L8BAvVYRs;L! zAR?gi?+N|{2QRdF)_KuI);Ax<<+PqWf=UTJ`-GP>#Sz)!@DE6M-{nFZGSL?|;>AoN zz)rBK1+zlgO{3I5iYg04n>m;v^T;E%GW22w&Spj%FNu?XQk!LLhUvui;-WU`Vw&; z@E_a&TI&PB5%6{U*$;d7K05UKkBah_eXj%~}ITaorukj}{CI(^B|%d$8P!mlW(WD@nf29m(+qU223B zhY4x9;h?D2EhU8or`@rT%r1#O+1Iw@DCgab6XmTJT6B3OnjMt1Jctg+m8^l*gtGfQ z1z=Q{573HwX>M$6tO-Hc=CLXx>BLJ53k%1(y+%c^0}G`szUESr-e8jOcNPH#6$`Qt07V@$%uNpl2?FO(Em0_D=n&Dxoj^%y*UbOWoyDy0Z zS(&|j9`9#^$z3N&tz4dFwK0^aYC1?c6|G-1S&1quWwj9sb)_14C5Anyx<<;qXK^_F zpr108FluS-onml=#9+h89TCsft0S_~cMplSG(1k>q?gw)kq?>It0}ILW-I(cQLpCu z9GpJ9lNZUiG?_f3+z{SC(~xHZi?kdq);ebQ{Kj8Xmuk3u=?A^>9%`r#hRglJvM z5?rp2L?c+JwZ-!TEm_3V|90LUu|y&+JH)MmBcS2?<#1_^KHrltOr8@ZR8hCj}7Y$}0D>nC42Y#jC>ZVQNuBI%tq6|nt_HLz(o~x&1!U~5ofL24t(_3#G zc4WE|knU8AM~e(gHFIViQ1?5+F1@x%6OQa%5?Ru(WpO=hhw613c)t5S$Asctc75Js ze%FZKwA1$@rm)YYCTpAEfs$x|LjvxLhJ8r|pizR3Bn(tNwomkmK#VqCoWWkVVf z(Y5)MbyO%kssdo5QO<@^PxB2(^YjDS!;%Hj>an}u;L#R;FyhQkihEux?0z)4Z*VQt zvZqys3y@tAfZ2mFhNP$YYk&1be$`7h^cTC&j-&l9{_P+2+|fg~y-E6}X#STo7QRAt zYPfbT9?vCc!6Y<0oi}Vg+ebe^d%3-)REjIiQe$Lf#+Dmu%TaB+cU}i+x)@!4gS;nF zux}h*-Eww6jwU>a9Sq{pu;Eci@LAzp*@tzEy3!mvr#8{bPMuu+#PD%c@aoRM9<$YD zVyIAQ@S-;-fX7k*ek2VoWbbc|*SINRCPP(AA-vt{($fJwB<)R1c@<+l_W?ITjA<%GI zDKo3#*5v)_Q;bEht$fu~}C%EVIn0B~32EQNqc$A62wEqjT!?tk%o5l-*tg zs-EPeTOOvafeZmO(jn}_FwcR&))LR%_$9caeXN1A8lqxGvF&6ux2{Ef??Mo0G+Qs( zc+RkER;O6jswz}phGr-|bKKWU-7d$?`~qWC_=s@}-VSjY56#YB*m3S2TG6ziV) zUX)E|$<-p?z~>I_aIQsvfzMvknISt1tz?UNw)6g@8oMHCpLR)+rom*Y;=;{=PaXDg%{Dqs0d=|uYtF{QhRHb3uw7&%&_Do)@q>yK#4L1I>Q zh!ti}nROSL`z=e-PG6EmP){mI_ibkZdA^T#3$4H55?uOjzPwgXcY2wFAHG~BIP?I! zP+;{8?vTXf6F7M)jurKMaEhrpW$-_^xzt?pGAEiqT9dE7#tgo9<~XetyZL3HCWnFE zzd>5tdQX}@nlg>DcsDfWg^)N`rnA&rFGN3+K1B-dPieZkPeS>EU5mHl^2#4#wF)R! zLEYYNBfQM<-S(5oWBe$J@A`$6Yx6`HFKwFMm|SMjS`R&e_I{=su{|u=qydipUizfR z?}^ptHlB9E6ZCQ@zfrg(9;?@=g~pawS7qusH=$%zOh3(ic};+j{T?v#z8nFEugH;fSi7YUkQ=yC2%@M53^oiA&=vo$Ny7CEBZ!2BA6QzoVxlsJZ1rj@tOtp;M?kn4uunD@>);6G= z_w*IGc$oFM-v;HCgLFTt{?;F>sG2B6BjptlEasM)Nd9fjgH+OK0HaQ-s%&lJq%d)= zlwDqsbQ=`*G-SL?K4CfDWgy5;-uwq%a^=V5ubKBnq$?&`ylnwb_^v^v>J4H8*emGm zI2IVuP^KpqVPw@&t&}{@=-SV197RM$X<;@u6Xpcs3~RL1)OgzhnD=kqW87rjWP3j+ z@9Pf0*n^531&E_O=A{KU&h6^DJV`2v(K{*A);D|HT9>YJ`oRr>RosRbm6<<++|;T- zhbmEueFPS!+?e80R4=Z6a}K5P%+l7RaFoY^^OF)|^Jwz^TI(O<#ES%abCoguYC0k@ ziU}&K0C`Ui^a}MA|-+`=k^g7Bf)&2wX%_>%+^w`sCS}`BcvG>Z|#qB6WEd1Rj z!h(vJ)v7o}^{v2LvCcK9Ah^0-)QA$z;NRrcF=n*o5OLMBJ=lppIuU`I(pyp-Fn(x*C3Z=)5rRR*_o{k`|TzBeG;5!8f}Jhs%uK5 z_2z}Uu+yB+rSp~41-0Y0SV}wV+sEfz#s|zz1_1FOH)SlAPYM%wVM1aex#t={F=bEX zMone5q5b#;qN1icjThWi$;KrU3d*vXLE{P%n8`xD$*;$I?Ix8DWo zbs1$UL6z_SVi8JJS`^5Mk+nTJ}mm!eT=g9pP->BA$S{rwi2=rOI_g-N>dTdE6 z-#1B#pZnN@$colAI)5-eijiP?ymj=<-jj+o|u`UvLCg4!|s%DE zXh*+xeN|&3Bu69_^y{n>6XQSpph`h2<6= zQonfUr83*C@-%^OV>XK7gHt1Cy9CoT+-JOOb=!0ThMATo)5fo+bNuc9II{2jovmMf zY@g6|!7*F?DCeSG$1KJ!?1wAT$1~oD%_nnBiO@ax5r#ub;A$prI=3K1&r{0XbB2zq zod2tap!^4v04)o^eUBy@=-W9lbJ!V#*P$D1SdEUMXzTd0AASBiIUdz;f%)iff?IKP zfm#%^&Son?+uqOp3=2x3v++;3jwJ}rOnXS=#*J%O1#R;j`zG^^nqF4YIRv8{#hf}0 zY%>s8f#ws7e!PI{heqC#4I443ny&Yt}UU!J$t7m}^I}*?er`TZ-{%!)VuuUMRnGVtM!#VKoDZkON6Dlt->^KD*b^f2 z8g+G3Vvj9nC=(W6vsoeIU9JA)T~3*96BzQ=Y;q4!c2amIS5&5OhY#N_D`VD-RKNb^ z{ds4PmlXLupoFclp-hTaY&1oI>P%fk4wX|S&|}f$pr0pwegF(^&@_M1jdgQA0I2$u zh`ynu>{@JCd2$8OqOsuHNR51zAdl?0m)}_`8!JYU+Ts{v!P%5GO-;f#&h~-c(h{h* z#zymh(%yb*3q-?RPIn?RydWzkAN*25ELGsHcjGgGMQAiYRq7+3DCajT(bL@un)rwp zfpii}+Wk2~xwv;2@yEMKFSksLK0?SR%pYGOXE#pF!3;2Zoi|FYXdWa<5ElK;~U>PMFoO;_Fjl!9gdwA}J8THm0Lf}imvDAs?H5->dynejN0l2_d zlq?ZB9d=1u$_g|sO7gh*z}DIV%~CDXD*E9eI$j>@nW?Cz0uKgNBbQDMr*y!YZ4n4Q6D;fMQkrpD z;(54Zp7ev8QIYn z|H7T|262Y@j@5R#kE<7K?~GZQwHDEVKQt^uZJxN*MYv<4QV;83KHkp*o6^+^b<``D zs@|!_AY)n`Mj3WnQ>bTrPORbNQ3$H7`14y>7h?sTN1IIZ&if{}00V96UVvrRE8mEo zQ`*N=%kdsY(DIIK-}%XX1vsKIT8Dj?YF&Hjr{dGl#hUl@U6Xe|%tQ?ogK=EA3%AC? zla+abUe8_lDrV&1Ybpb=aWGcY`DlkG*eM&`IDWx>dI^PB6#aHt^$!ccn_74!(^9p zYsS8|Ho9H8mAwO6%JnXzdXCb#Z1KfXr9E}eH#uuTe*kY+S*Vbd{_0PhZJuYaRJKY3 z`Co+zt~f|gsmZ4DxV{v0`b3A^k##-$`v>3#Rs}Bx_^$UNdd1=e5%m=a^y}@H_+Cce zjMnK@j&O#g{}cB*HBC^!MK)&cL~vH54Mvg0ROT(oE4<-svAiI98k8BbiwzBY)n(;o ztRm!I%h{)wdmsQ~8Izs}Zn}m@BHWMT9X*##@kK5u8JHJA01VGY))J&WnZfiO9=b>rd^+l_KoQc*5qsdnk$_6S1cYY()aPC!L zI`&u);yqAz>liS%E~V$mubiE(#5unID7gW2!7F7S*FL85rLO=>6L%~e)~&ouX7GPa z)81`_mJx0$fn(I^h;QLYs7O)XU@*j~WGcyitsl&7)&{m07Yp$3L4{@x=1OQ#*0xBnS@HrcWfPgLrXP zQj%tH-JzP>qQLp0r@T#*B>MDJNo6}jC<9x#s-;J1L63`yzU|@Opz)NmWJR5Z z_060k?l1`^CHVQ4<`pTL<`WGlNqZ0H%yV*-^#sTRvk#ZZor~E=6V3SP@EQC|I_l1W zz^Lz%MwWwoDTZyvkr1Wheh3s8d^%U7KVQ+~WftRNn$UI_oL4y|JH>vi%12iH^hLb* zrZJl)tNqhEowO%Listx65((FO_&`A^iW6D7I_`~#DazS0U5$x%^V?bdjYT6@#yE03 zq}CbJzTulh=LQ`!_Rh^P@AeqB<|0;H%2F$S+Gld9WTUdDFP)fx%vGpu7W9;Wk(1qi%)41h|l9`qS{2 z7~RfJBacA=8*biW&1v5BW;Bixn^K*_;Bq%@XD-Z+@VJSrICyi|(BfHiw6gr>XA3_D zsAEHemhJvD(>VO-TM{tgcq!!qiHw@peZEQJ%ZWk_p2QN^a}Cb5S*N zL38}I8DX8hf_jm9wC-U3wYF1E40(>JQu#hC#B6v@44=lRWr9wQUPiC-r!{^9br$TT<9VcKytu`?bj9@2v@ zE1zMne~n4!&WEY)a^q3AAxi=N?D&doHp=W!5g^buG1zD=J+GVWy3;jm%?B&$%rx@HR=?HQb=Ffl$Cf7}$hz3{{jd>_r5!_zCkRpyuh-1_wHbIizeI!w6Mf&+_*8E2gGm@ zXaP_F9v}x$0cBX$u(DxALpZ;>c@!WcUi^^AKbro7xT0&Ked1!U!#(%h6YhgV&8vh# zVt9+?g%GmYhIP{3ozldNG(K)9q6yQGwXWa5W;i;xC_QR`yWlOQpcXZkq5}+DuB)r; z^P{Z9;4sbp^jmF&zH`pf)*aSa_Ukn4bs4^}poQhK>|b$&6D0+G0vKX>W7-g=AHgEm zVQ_2RZ%A}to)w7O!i6iv1Ne*O>K9(qrQB-qE4T2LFXz7DlC>vkSBVbn-K#>e=E64O z8c&;ATYAk(HO;xYGTGLrvsOl9^sh-IRq^khuhm`qVKl09zyIemY4x?Yxxh4OXl71x z#Zu@ihjlHNN-K+%z%a%{y_p2fuZBRElxc^Xr~rj7h*m^BRz3-*DaQ$4vz*LoY@wtl z&YV%g$}23K?`u0&lv^xUxvc%-5d`>Q0$c!&=1l+&OBeml03`aHMg8FbG{6afihwMF zD1Z)#!=C{LG@nLcu*d;@2;n2(BA%#k8~ahSX%uI)&tM3klIu8*&U3ul#S+W&C1fuw zW;gCxnUigVO1Xi_@Wx<*ai5KFBFw-Uq>zNQ>Mms{(yZ^sVx4Q$ca*abDaD5Jr}0@ zSgJ3%U^WG7TgG_C5?S+=vc?LAH=nb&`t#|~$1Uw7uUnaglI+y&vBIlOjA;4%3Ehy- z82VT?tFB$0ZMt>OPNlPHWw4FM63g=>Ko4NGXOFd#g?ZZs1_lDW(Ht5rG7sQw-MaN+ zvnAI=7CH8D#Kn2S0%BOz073wba7YAv#KC#uj^YE>u&_n(Md9$LIrp=l{cMN}!5^rk zS(Wn<=&gpi$i))Nw<}RLqTgFOEBW)U?jE_K;|hrhimuaG+(!Iq&l%3bNK}IsYnsxe zI%}Zvq4kN+c?UpS-|33I;cwPi`rNbuaNgk^7VThm3*g2`(|MmCH@uBIews}Ce18Tg z1bDN40Sbw{&lj~gOzpIv(_RRDQrH5~ z1t?rnM{!157YjO6D{9@$n5qWIx=ZLpTc0u;cWP5#uXbIW)>mP1vYAW1=eV+N?!Gl`Ae!aGD->x05tkK-d^D~Ho_=q2go-dYI z{;}dZwt5Y1MZ;Qa8|B8ZX3An2t*WyZBTbVv1C+N3fAWDXnmmCvMxwH`mWxJ!vk1_z zj^!K{2rC%APR|$wXJtJi&C!nza3fBKrR&b1pMi;b${Rfaz}AW<#L;a{e7bJvmNF2*o5g@5gd=3pf!ZytqrawJv!k z1~q!63}hXh1InZ_q2EAYDCc^bGpm&;%}$?lp)4xj+NXrWEHBo4KB0yBk|wQi7(_r{ zwz2u5=883SWzyQ-)vj&b9qPwY;gWKGuApIq;w-BzSRwTq+}0WN^4@1DdRBJ`OLIYT zjxSjDe7s~RWSWqI&xcN?>7-gVZFP{0`Zn&-ZTG!FFMZA1^rCxSse5j@Q{TO3pI&Um zwWTGiUZ&&5<{rxxk!zuhAV3=+26#oyrU(E9T;V@HJ{|@(uupix08xZti?t4kfG&bK z;sX%b0#TfctY}elX#{$lCtmiWXIOoVmuS>7EYh2H$(I)Aw*#K5i6| zxR|`~LXuz3t(N4!wv3OAYN^$4?Go~tkA6f)4nMwfwDzQVOgtIXRJ^P%H=!kat@bNCY=pdII9z57b8gPVO>-#4($fF-WJ#F9o=HR78ajh zat^1KLuZ4W;j7&WsW+1~xHW42&xkKV2{~R`P;=hTa_qN)Vj^_XXJ;JeVMkbBEUUMx zOWC$A-G0kH-MfFI-m+^zFYUl77SgoQFp}-baFCdFN51cVkE@7EO7vKV-uy6ru_>+Ix zMi#Zmnily-a2WFWibag`gv0N-WHrnY7s4^m7CGRMXX1CUC*tNwPFoL)lQ~u)0wO@U*IPA+W#q zUA9Af;hg_cd#m{QhEr*M<=i<{7p65kKd%FAY5n?d{HpfcdArtirPMjltJc9@b@X(p zzoT7See+u~Ila_hmmMuRRjnXL9Z&r-86cMLoUC<#LjWHvRgvWlFpCBikcVmdWFlUm z2m!_bH2{cf5W+@Kg+I_oxYaI{2g4K47C|6>IIfKx!WRE$OB9yp1ISnap8*#%vGOcG z_j5m|+itrpKqAek039v@JnjpiaxGz^`$rCmo*|a+j9g0qmo?nbY#Qny&L+Sbqe%fc z)-tUal9={`^W>(`gz5A6ac#}}fE*C#`oGJHA<5UV#MP{~Sk7Q2rUmJ;>jVHzVqyK% zmE!OX+0(~(F~taW3aobo<(dM`u)Wfe8EM8zU-3O&>^NOawC)B2pNns&&$n%qI<{C} z8crDvyT$i>v9~v4l{3iAIX?Q*0hy7+#pL;r2Ea|0pBvx?o(*tI*DSzwg%5VQTv0yP z?nGe}ot<;yQoZQHRaQ3V9O~S@Y)Gs7e5lpCl%+DPJ(^I_%A&TMRNYmoSu3J~ferI@ zvlT)|CavyVMx6#Xno6z0{I3CSG|&tKP zTVJADVaAGcPWNuwsykdNiwiRv?CnybXM@t4cByUm4eGw>fI2tzs>#Z!+MM%msc5sG z?PfpA9bFx2_4A+i&9g3%jBZ7##`2w&YX#sKw19OA$1#8gbOB>g-#1300y&XC<%DCJ zCh?;Q6{{MML|!8d0w@s%$GOM?#`Q?FA3;vTISk5RN)*mvIpjR|=UH*=1EoAO_vbh|A4v>z-@aTc07p)s-&@9D!vX!) ztY@7E{fqo*b)DIH#{h&O+gX+HWO0CvtlQ-93|_zzW_6;Z!_5PL6ay;^rV{B+8{C*S z#M`XHggCg?+aTVjRH1+Pnuq4waKd3I9vpj%zM1oVW(=FXxA~b2ZKGq-0jv3!t@nM$ zrA^x$+{ep4ZuWlX9y@QbGA`Snx76!6`h0@}H0?UBvY&C;&)0A0vw8~8hQ!6>J0&X? zxNj2RR`P{EoR|5x)`bF8b@ix1HEEe<6B32FrJ9s+j!&i1E|87MLg^fr(;v_zj=$=np<4JavErDR)0(0;FeL&3JHrE3Wk~$_BIlh23Ra^X_x4%uPO2z zSpMuA`qV8k@0^`saUVPwBBw{k^7UXTxaOruIQ^ zH!C|hsIFZ*wdaT4p`Ke0sk(EkCimT-v0Z!AzHX!b*}nbynH^hn=avn+^F?>6)$w*HCi?BGpCy*-SI7m_N$M4;M8`6JMky*l6jO`vS!EG= z0MBX!bz^d0umMWx{ib~ApyVf&vocrA2G>u+_O2C^>iGgzX1!65gPbo60q+AQ5~3~^8>N%pF5K=oHc4+9l>X%o>E-MEygqJr_yPN2U8IP_#pF9F5rKW< zztyq=aM!LZEiG!nrL9~+DJ;2=F(=is@&>p>qOKGpL|a;%39X>4WSU&^l4*aq9j;|?TxD+Dg|w=%b3;1y&?Bl19hb(=_y)dn zy{7Tu5&gk$|KIxLNJSfe`j_>ZU;GKZ?zJz{owx1QaAsMbn_tkABNKY;@yC>H&8xky zM?LG;DdYX{tc_1YEZ4Dwd~7(vvc(oyS`Bds);GWgzyfRlvIyR=)^QzB$38H{xd{3q z2)dMI4S()MSOAXuaBXCzV?P2%9QUV*6gZ3~3&yd;7thant3cf2k3Syfj05-x$KZ>P zeB>hm=zio!enda<6F(6kkWs7LgXd=Y6c*JdZY*}dA-@@kEpf3ReA-L?^g7Eq?>69y z{%cGLLP?>SlsVzD2BK)N9NU_~3QL`{S%`$i>Pu5no6m<;6!5k`mb2)S97>?~$$EHk ziean7o$;1I6j;fMj+>@h1}w0`19Qn8beJ2rI(U1dSvP9pP58N4#%t`H^w7tpkq4yN zM}6H%Y4W`E=oh8;{Gs%LCk$ubFTL)6lwSP?xvV&><9yGU^py$e>8A~TeyA2d|2jil zbB~|P;EL{xi^>ZpnQ=Zs#{0+aAo%PNiyDN!mo(=tKtiWSWzhou1=n~)4 z(yaAet?FxQSF;sEPd2UF40iV#-ZuG5P;tDKaz)j0L6c)6O8GdK%&6=aQJr1T$N%de z>$iU4=k&`z_p|!BxBrmd`nDg__rLRhRr-co^^RZqWgWcX2DNY7u8jw8)Zb6f>fJ|< z>r|O$t)%wmvU;V#^p&$SdZpvNZ_75d^sZ}s6k@rq1prz-=`I$q$f_2B6i@?Xu?47r zE!!gn(?je5&qgD_#P9nGrDv&NPtP#`COTcL4X z(azqgO*_27D(MKYo>?l=?cPcEcR~`zN?c(e4%S4pq=C(@C5=_mfBbn79h zcXOi&_G^B|-+sz~=j-aeevj|>PkW`G^!5iG@0&JCyY@Dq^D;#~mTy6L&zEznr2%l) zuv82%OZA57u~x5!a=?#vrQ1p&p}b34I+X|`eDVgWq^l5#rJ7pp-)d#i?b6ui4|-cB z6|8VeOLk609AzsiUs1CuM_9EY%T@`i22DbalZk$5V`UJ;$lSb|3MFlDZf9V{w9m0F z>mpXSg?XJDI2nV}s`~M# zl$#s2_Hwn#u=@9hkL!0n`0+4Gwkx02O}V^&;nth<&-U-r_wL=TAL;GUPd3%`rna1z z+b)*tS0Z@Adc{6qv>M(b0iYt71Fj-C1Dx;&dZMP%2zD+9JkdQPAzWm|BVLY)n_~ti z@ND=4iTL9p%NoFl1q~ry{IRw%;qNCt@rht{d+@;rgJtd5v10+$u+kClQ%^mm4}bW> zAzq#xN546mQE6($S_h!=Y|%X~_nTt5KE!qTAmm^GiL8u4fRzuO^+OY?>&aogQU*I@ z4OK}47l75~{hk)7%lj>cJQN09ZlZo^v>I*H{LAWxUzbBUO zyo5mvqa&lLU1ku&XFv849bLnN82H$?v~(KOn$?nTwX!iv(9IU9@6Z zu%f8e>R}KB%Bg0+>dU3WlqGZNtV-Se0SxD+CUoM=X>D=o&s%YPd1gUhD_Oz$i!ji) zPKR#0UElNC@70U%d6|+eZTjOs|1+I`^g-?GY1RFo{i1&TcmGh!wPm$tbK36e-5U?y zpiKvMtK#2MGn7`nP5BvK)RNa=N1LW#tZ{3EynY@+5=@^S=wtD!F{ACVvMbFsXvY$HG7V#{+xFc*Ox!o4|1Tz!3gVawnJ z(mjGmEP_}OvDDEt${+{khI{$TUmjL#`13#ibA8|g9|*tYp@$yQfBmojHT({KGxxjs z=9>e&0jM~B8^0GJ49^omWc2*8{KIn13}WyrXRrcYq&p2U3=E*fhU|^ijg=T^ac?of zV3kA9%rbn_KeyfI$x6h5!+M8=>sS0JCEhgl{VB#DNQ?Tw;aCWt`_cbx+C)XuDb22F zJF6457$9>FIO#A821xpFGy3%8S!tV}!C6Y7CC$xG zyY`fJVa$aE>lfw=I&*$Rb(hjE=T<)0HG>Yo7BZs$@Zm;TU}3umZ1{Pf!j@v6ur8qq zm*RB3S#yT3xoSljD}-z+t$8b%9R|n+gVV4};-+*$lbJS6`Z*WpXEbv9m`Vn|ysKGX znlGx<-mTm3xJw83AJVq%yVT!5poB|ewOG*Rt=rW)xKSVe+!yq~XTGSDhUeRuon&-<(`4 z07qV@&li2aA~*q>02?5OrbY5SPy;B@R0+rcI$XMbt1j%HDy3s}Q~ z#xd7&95vTQmbs{Tl_pNooG`=%STVTaMK5|$00x%);`eYZVHmgo_&xH-BLUvn0)T)S z*8+76XyE?D7d6*1qLslC{C3iYeSns0qTdqBbIY|fjfk^g7@*M8Wl%D}ApV4gc|rZD z4i+~+hZPlArF9R9+9k9#$M@oh`UsLk_h+W0^9yWwo{V3-+j@xw`(wGNsn*q*6CIBbRvI~Az z24M7=4Oj)@?>{R&a>TIb-(fYWUSE5M@0sgx*n+{a>a_4Ntyfdte#*b?gzrD#``_Iw zZFF4c41o)dn{-1~HsSZhaw%DvM#KPjxlyRA0gk+~A|&ml+}aoVtT7;=$t8qZ5&hrN zDR8V|MdM?9?8I43%~!N}&187qrR?QRScFiCjpwpQc0ysNmC<3>UG?=-g%D>&n@W4&}dM8$x>0fn_IQuXE}HL zv@Se#Qe(ptnjD|f1vTq$Ctb4UC)7VXrRwyo_Lyk@<+e?Fd3R^9O2%^i36KP^0X@JH z`6yIYTt?WE_nvs-i2yqr`~l$@2k2c2WQb$65C*^1{>00DfkeOzP>d{q(K7&3grRR8 zjy`L^8%?N$0jjXFz4*m14s*XDSmFRJ;Eii&HU;SJz4zX5jySk?WP#%z_|eo#vn+GV zVUgrKO|oo>him9d$2kBm`aP>%EZ4R$T?muyvV0c$B!C>G#W};u)QmyusMOgl?Y^M_ zc$5C5lQS?JgpU5c4lqMsAOX-g_I_~e+Yy$i*)KZGf@|nq@8|B_X5e!i zKv-QeE=u#MYq^tb`hU^sEXTSxys`!)NXxB1~SP6Moww>L{a>o|70NMg0*r(%iZ z_r%5I9}*Jia_$)+uY|JU1h;a2At66joI2#>6|8Krgh@%s3u*) zwr$;}ZLXGdx3{{$G^u2;1NNfkQsN7LoiM?|781vP-ZToF6&+jqV!B>Trd-%kYIC)W z$$P=RpIxnu63D=O2v{EX67|JX+S(bq?yrKefaE< z3*SlYU8-r>4?Jx*uC(Sh3@W>AhtpuE_U_)J?|Q{O`rhxpPaD?t`4`1(kFp)(MI0Mp%A9!P*QLMlmVWa2e zIe=HDDdFBUxxVBjFVQ{s+!Kxw!bag^xi+McqZ_j90~V*}yHB>H4;-(X)fI+E?IxXv zO(|QqNFR#z%a81fu;#@n5~FAMfBIQ+EZz0hfp?DusWrOlh9m%K=N(`%$# zZfRI4Nwc_^Tw0>Exs+QiS`y*d1YRDhyFgZ_C zTFtE;y6x6Ox@q45XXH{a3Aeb4g8rCiZ4(NS4=5L5f2DK*a7m|Vl5JFwdDES}WWpf>9aEB^ zaBy`iCm&RDG zDFHOFaxu~pu%eSc&4~C#YoA6!t4 z^U<|Yz9-J;emFpqDMbJ_O$Vj*uJ@uBSf4al%e-Y-E3_`UBB&9^3#0{oLRkFqYXl4XeBtSD z#;OWTVY0MxeYni`EZC1{tJq`2`z1dEO}Vr98O#O^kvAP`fF@Qql)8&Z!+9SITd*qn zM!=hkmWuvuqeG70zmI2}_qpP{!}%E&eXio)%rq!9pMUh6f3y9jodz=w!}C-Iq{B{! zCmh$be>>^%+VAqSxMt7j7{qJsS_$;1GhBiTQtzys~h&*=nwpYs^tZxU4>b4j!wJ8u`)xszAW-R zlK_VT)+l<7)T9)7hfL04*(G?Ko;1bBt_q*;mYZM0-M9yQG<5>VS?#Z)6^WG7bV;phv+nI9?AY>w7U=FJti~Hk99|lg)?<@LE zv8;XZtJmiB(%ar3?b_7$*sZVph2%NWAHKLFhZ4&J`?l? zI0BaJ#eM)W`?C$Ygne%iMyN8PbkWySna4WT;(Z1{EEC9n#0jijaD;rz89cxmtSn@Y zMUeGOw*X2G2dFJOzK|symfu8NL`4z8xj2P+;wBBKSlApqJl_TT-ETPKxl2A~^$0+* zsm=HDmT5^C;KBK_b;v`r2D=l!z9{Y7Ann<1Ju~Sv%K3ToP9vw~o8KsX&#M~g78jF$ zoCHlw+L3^la%)*w$i7lbsiQfouJ&dFNmZvur!>7#QFCjz%9XM<4sOt)8~3U(Kc?Yx zN0oQ(Vs!@ALv1pQwUPz_lBjK@!pIQ%)RE~EE>)afa%n2sf8Hfv$=7Fd*#K|M{g!l? z(9eMj{6R+v?doU?Ksm(9$1Z_P?ptwbZEb1Q)*XASyz*hx>aycRX#i@GVj|Aq=hDCE zICz$Yg@Puj5<33MVnrRD-Rju9OJB2+8Ffh=^YhOxCpEWhn46w;A#2i8RmYcX^G711 zN5^J#c6>rJv$N{$>QGaqNkiv{HCI?vs-;i88}};JJgC+V1MzELr@QZTb#9fQaAR4U z0w`fI!rDahA|Qhm3Mc_i044J5&Ye3$GbR1RrlzI>{NN7&MX-bgi~R`9I0wY6&L02~ z$6QbR5ljJ2_(zVofvAw@oqsvcKIeE&ru*R80V(`(QC!h}1aDl=KEI2mV8Q@%SQKdj z<$UB2o(n4+&(1yQR|m}T`=aJs!VnLT%QMg?4tR@}?YjH!yTd40ekb8#S-TRwKAHi? z4I%z~t0gN<8)(iL;7W!Yzvf`Y+P=lGveV#Z|Hvwra#-TP!q)6 z^?MhIu*H$(U0i|-mN#^3^ zZ@MfU_Vram9rx_@^NpN0=*{`O<7(@Z+WY-m40AW`liu(>Qcw3Y=^WDHIv0|H^thB; zEdk)JMIjR;Q_VUvJg0mrsh8eyNIjh_TAxKxpJ?K;Yo zegO$9hL*Gy683k8DK90$Gj(+KDCaLhsay#bxZqsEm`4s+tKnSwP(HO<)qV|En6pwT zEV%GnX*z!H+tc2nO14?2tcWKnC5=~0nsN2(tii0mr&mW^T8pkeRa0#S&y0T?>!l_f zPgQ3xOsH5Wgr?@It7C~&i+VTg@rS6>zca5pZo56j=JbnXU3a~~^sf0g!_H6Kvz!E3} z+PH7@oWLA$aBT#<03m(kc+UO%_v_9(?~Ebt8W5~#023BBmcP31ZbQvFgNVVc-GD;B zwEc#W0fR^wp=tf%*RG_21&dgK8;XXrj=hz4c0iwmzHL6H{~A_2T*=o0I$47kD=q+6 zfE#c%=fD)L(CljAnS6;_h~JJWMHcMA$kHA|Qg^T8GjuuIH=KdY*}Gwo@D^wbi}BlG zdE-3IsLsv-1aVbxmc;~!_&qF~LUIkH?@Rx;hG zO&iwh9sl(EwQJj!P!MSD80ha(Qih|2d8LwRgO)$m&UJ}&Ue)FS7lxE;OEoc!fq@4Z zgB;6S%@*h+E7t7itAP<*HSEWJL2WVjmhlrpi*gGRa<5QiHB8 z_G{YL&6np5s7Z~EjOq08Cv@cSS9Id&W9sbNpthbtr&CThAK0gt-ED|l`H=Vru54pj zO9FI|KLIo>O<3dT*9Cw89thAvKQO=sU<0ZE5-ed{0}KINKpVh=L~upC_;H?nt|vVA z#1S|C08kV!Pz2}^E^=JYv+_(>yC@ep?i0Ca5CYGNMG;_I?eLF4if5%<(OgWtJZlsu zaR95QPMr$zvMd+R%EZ1*M?!Nl@lQ`rhxmy5@ZrPy>Q}$2kAM8*AwI&frFoh@axuiM zZJ}SAvt;N~MkRTpVdI~EAKi4E*X_r^{hoGz1r0UJ1~{vd#NjK9li&p^QUCMJpdyZITX;0?X%Hf|6P=paF0wg?U5ToU1O=Gjp1p zoYKVPq_-2AVmmdZ>8a_UL|7^;VVLA8vBK31nH87VjK3nwhS4#9VP=nDlI#%v9WQTJ@J$t`RZphKFYjV zZOU62&HMTH@7u0J`}g{;q!=G8|NgKS%UTg21DF9YBFh+127r(+fesw~zQ}8<1+W6N z02<;T|Kf)LZGaeHgg*lG;1|Igz=or#6l)od@Z2w&&kcYgPQvj#OkqMeAPZm%<;45M zP17#()8PO!Ty#v6C}AT=WXrvXi)V>Or}BI_Kn@_okKe-Fam2y10Fa~+X~I2-4>+WM z9pR22KOWK(2nG^KR|Z=|=^4w~6PC&Xq_C!804Nz)fU9+eBrI~g)Rf(^lJjtwD^|pO6jgm~@wLfFWuo<#Ws@$6yC%%P^XiZz}sNnMFw&F|9f42FMB{ zJZ~}}IsCkzi3TKAgRtsxUTs*uoCU+y^?%LyT8go=a+s&ipZBnJ5efa?@F$!<{Q*?5 zBvO1?0RmX?=U)@vrwMh!{-^E!{+OSi=|`M4A3h^}*=fMlww$ zymS{%N@si>7DgBUFf9uzn~|!lC(HHxW?U?nmx}Y%0peKc0BFIFW=i~t7hxX=B0jEN?GG$r zmBVpw@+^Y`qV-9+CvkEOaqx@)8DRl8pezD8V2|@S?!$8cVjOc00j8q+<4@cOV8}Bx z%1$C&7g-^rDN`PP_+dT$^wS|Oo`q|GMl5w)gYcUkee_X%;R|2T(W6I$ANM3KEO-cE zVp&_l_mAerXs`hgcigXce9Qo5;FvJ904Y^Ig@!tsM`@NMI2NpMt$~K~<<eF-zkTRhGTb>)3;=Ty|^bn3|noU?`&Jy0wJnn0fhOlk+-)?{U z2c9t`VzFDK51cPz)GU)3pE&Jpp#dGF#|6XQ6A$|N&pQt5Fn%|K9JZ{JdVPNY6n8xe z+2OfdB#kc_a;Z?bH7dM!I{YuXq%AGextZE_*#(bMeE zkeTTTXZ{PSFP1~?sNUA6Qs;om2Dz%QT{JN{7K4q!(5^X?YFN!&DgZxVtl%mInY^`Q zP;K4oRZX|K#y+f*M-FRhY$Wt+!}>wZpL;TB0mVQQYbY;tZm`l3XoKbEUnBH#jeA|W3}$8^#M;D9gkD;B5-&KQx21qwfahQ4VL><}h$(KY0A zV2ijpj_%7o*J8;7ihxI~TYwtR0w6~H(IPk_P9)0zY>7W|QQ2TWI*+5gu|~0gyZo*KwY8R(am&{(v3UH{#)0*^-9LA4gnK+QhOpB=jxz zN1qiC(2D>XN`U4^<}{l$EFC>(FP}?9@ZqqGhXa_V4S0(NBeLGIA*W>EDjEFj6`DDT zmgZ2bYK$}muCSafIRHRKKQ;QlEg8;w`W)WdNrTy`6NaTxKZgO$+0ps8(S%Cz1;}__ zo{8^qP$}N>4A?8M*3oZ{(tw_e&a)b9K9jCL)4mN|-} zpMA5R{YFD7oiB=hzOsRhbl{#$?t9j0bke|PRT7>T$fRE!MhMbi&hc&XZ}{PD(#sqV z8iiuQPq?mh3P1RAZk2?kv_^&v6s=SpzEM8^@xRltC)VClTA55vJ>Bb+Nj0WHNT>Ml zFKE`(k;!(d)4A^84ZGFaoHEoDTqCHf=A2e>b>sZ`*)T1~Vzq2wN;tDy0k|aAvpJPW zK*J6I#e8m9wrVbU1y{3*{_vMv@(T5=5}lh=Pq(O4GtezpRjpVN1dG9(W@qXA+^|Go zeM_er{fe5J05tmHH53EZLz-k83W#PW3Q(h&mge3B&*jHqutkcJ=xwlm*pJD6nF@u{ zLo;o*rA^C@cd=4XhfD90tCA%{ZPwQf%ei10K<$Crx4=mB=4&VVK0ViN6 z%-LuM1aVEs^LE6IBRty(pm5ReiNZ$v(RqY>tab>`!f)Z;02P+92;!o9v*kCFKENrz z0kGpYuphxF&rVuIVYx>%@B_Kwh8w~F5#lFa;)ozFdgg0Uu7#zv{My4LxIhp4fEk}x znk2DSZ5ib_td{vLnF65(+FcwdC*|G6e`tp;+t~g^Hz( zi3-Zj8W}^IPlcvNXSEQ&vrn+7(ImQ(#ZU`D$pc3&A_2w%q_MT*Jok!VjbpaKt@s5i zqwf=@J#oBIQz~=7F)1)%fW*83ujXf=;|0@Cp&OY4wb}bD+`r)X!lc0tLsN42&Azr^ z7-ZpnfR1N#@zTVkGz8>2%!_W6w)RMSoX)!qlH7a9=@2Ou?tkq{V=1li;^o&u&CE}K z{aUEQ2Drs*4B*I(eZA|#0`_UvK27_BY*3q9Fr4Jt!sLouHn%Ey{+L0>@Mbt+vfR1Z zg2skt)od_HxP;Cu(067|rQ*DbE-fW;pFg@Rw^eeLgb_N<*Qz;JRSY%@E?Jd&O`Yw{ zF6>Jx&Km?PGlyFl7=EIIYZO@Q}X@m!HU+fx>4w`Agp9qO4V}RVQMZAhEZ3E zDg}d;6-LojnBv@YP((Gqbfy*oNGq(?P%5g#^E(VneC1l(eXgc_a@p|a-{F$Fu;g&u z!lgdpXRlSPm}<=PWq3`eluai?KfCeC2@Q>mYj|{A7sjSEGCZUodGinIdw<}^mF`=o zs!QmN?QOcNIj0M*{!Lg3UhA^f*FwdzMV~If1Tey~1Xz&w05PmZ>_`2&01m)}a6k(X z1;|7ohXZ6HAR>=P4gplSK7zT(sz-Av*8z&F;VBA_ANL`TbKmG(6eljaAJ^gt%WsJW zHL%5Tj&Q__BOH$V6Q1~^=Zb`D2+KXV7thW!;qZ?JcMuP05CJIn!TaD zWtY$#YJavkpa)$N#QQ-UHdwsu!q(xceh#c`W!J?DIGS304?xk`JA?%s8KH`0utf8y zkD1f0;&UVk;D*3jJU1h6L(e2%&vOETJPXsJpw4L0rRmwlB&=R>!P}SYm)`Z`QqNY$ z%Sc+g+dHkRea!_cTC9+(Wl?Fz`PNr{m%jJA-=fa`%^KXYPpv&Y z{&*$S>gx}*w3?w4n)W>gH*cf=Z(~)7YfRQUz+ow(`4Y<$c`yPfni?q&^zXuI6j`y5 z2-pZq-bDZqpoX%*bw~tOT+5dDDJR?$a3L&lumyU6pQxOy2AC+G2+nvuK#ObG0zeUb z0ZzbW1a0^cFVDoa5x6}YnD`yID1KaYzv%v)#~(lT`3+Hg9P>Nz!{JAI;Fv@2M}Fi- z!iZF+X~C+AKfsJNjxEne6D`(Iegm>P4c4+;3jha1F+$Sy(*{fdXa1Z4IKbP}M|{qJ zm+$a5*T=vUu;y$S`pHpx=;PJmb6BH*8sfkL#>Wxs7gj7Lb$9j(qgv_QPk%PD0nM<^ z#$hxm`v8%%TsXH{qGRHq7y@&QFa^l?`~qb-7md(#iDl5mC_HPphp!1EQ2l&i07Fzv zyj^zO{5JeJ7hsWR@|Kaf{KlOBIC;W(=CMPtT87Vuj{^|;9D_3$Y=J-1jsS!A-XQ(x z8=cOB(z?ywZt{7jH|??OoDR1dQFu1_J?z~p%}h7$ja(x|#nJ${v0}vlw~~O*lI*1c z?$Q>xwF+=dFQM^zSP>{SpSi8#~+e+Cw+%MYr6pT2m@C0Vk7nEBzL=cXg=lw5PA$et+%u8R~Z4 zakp-N>32Dvl7@%QhUd6eWUT`npcc&q2UGw)02-DzzygRM4AwXR1!%&LFu+U%Z3yRA zLrkMm)0j(+JjXH9_i!)#fkwhnMu>;}{anz(xyT_AU~zo}UBrVwj(oiOnrFM!^6Yb5 z&+|lOm@R(MZ(JQezl(h!lIJ0907rf|&8AH13#jp1xejX`me0s)M_O~tny#$kz;&#I z!S9KlZ7s{S0C0@o=^HSdb&(5m&jC4BDPc>$JK_LZxW{v4i)Wwbd7XuWqquVH zEZc~1C0umPvt8q4_}_3V{uj?#0JeF*)&)Ns>W<|odCO=ktZg)v24KU_C$K^?=N*&% zZngh?JEfQ0?BD9&ZFL&1!FtCWd(HN%8)%91CS%GDr}d88rJehvNvAFK9e&H|=UKb* z48UEk3H2KRZfHc+mdiAuer#<5+{O)q>gw!KBGv4SK>w7fFh82p#UIGLc5fb3e|K7o zD;@Ib>|Zs|oIg9Mnn8|5;jvlM-z#KR=XCm>rIP-D`>15FnIw^&Gdm1#DT544Trm)W zQo$+>^l77cl#lk+DrG<0Y{kkm6tbjigjQhwosHZx?_OflTCx6Bj{F1XozyfH%$& z4q*%Y;0N$T*MGA@*ni0VW98ZLkVX+4^4suZ8%dN7_yNRN()jI!!^#NwVi{y@Sq6Ua zdjUGq7?34hNq=CO)f^CDI)X>yS*vm_0NkD(h7LbARxw6QV#Q)51}sW>1K0LV-ue~M z|Ba?kV1+Q}&p1p3T&MxN@G9-dK0peGbG~lUZrPxMm5(saQjL$W1xp=%SkCbOR=XrZDqiFpahE_{Op zZuFXKLfyN2mrk5LZ%|lr0jv217d7idDOH&+0jpR+?PI5$<6LXcxl}s0Ei9CE{@l0= zi=l1VxdM2jUs`}Mf3%$us}48e>*q{7+15_A*>R4{O06L@VR{D(RJ~4TXXnPNT>Qjg z^eYy>phWHCU+)i9U9F^rf6HJ~T^sT(8ocFR9dj{!`0&H(#3nyHq?uz!R2Xqum;80{ z$2nu=-_p^k4#UA>OPkK-IyE+5)S1&KwA_?bZ~ve_Tuqu|2+|stwF+>&=LjBXvWvDf zHv()}$N(>(;^fJb;W)B10eHX#&;nfG$2I~R9Ot;6eIOAF6yYM_Jc6}}a6r!03@Op? z;&%d7Jb(1OQ5r<)fV<<4I|BSZ_~3&9ih(yqs-8c8J{%JVznydi_DFx=4L{PG@X@GT z_9H9j)tp9`leG+RWLa~zwr-P#SYFDHPBSM14OnY5^nvzgkf}CWXZ_2_)`kqS_qp@Z zsILLkf^JyJ{=7kS35O-eGiC6o`q-aJwKRtyjgzakfKZtJ!}q$HvjESABN?#7U9|T3 zn8qWP5?eOK!F1jJ-F>az*(^O`RWN+gX=rsh=k2V)w`!P68O)e%tJUe!*5&K%PCK3< z-{brR$tpgiTUWQ^u@nt{r1KgqCM)tySl||B3~=ki{xu139qk>u`M?3q%oTNhWJdXP zU8ziy7RrVyzulRsG0iU&)jrUtCVxPi=xAVI8y%h1*4>!Beys=!C^SjcTuA@PF3f4D zT-UIxqEk~7A&r3Bw$5(l@_83(I+0V&TAQ+#EpT+Gr+*vMfkZ$8q+tyr-?DBfTju)1 z4~r4UOqYQLiEGF&5x@X&_yH&E1BkfQ0|K}QVc8OfYXL{LYXMH8XXJO27W~#|ANb-o z0m0-4w)|F_LjhqdZ&=`%CIyfKT1iu^pIi?(1IL_;(v;?A2APn4Sn-gxkWQ;*ty|!_ z4F|W~XmA*kCJkvcfd&XkH!Ntb!-giowm~abg9iP(7`=JwY{Q??oCpKw02g!wR;n2T zAYVT02W?irSnVh#%RUeAMFIeF4gKkyE&To){#O9xKpVfaM32bt;h6zm(u7g8`0=~v zd^|Vh-{mZ^>~tHp=ShRy={f01r|FXhxu>6!p7M6i=}5EkgrV;d)|oZj4H@8~`vADi zh1cq|1ajA|HSSvjxR9U7Fyxm@xwR+*{rzgmwyVFZEqp|W#|<8HRuD}oe-vxsqnXMy z>+zGLsw6WmEOk}N)i8Ak>lp;Ao52lpFP%1L{gIw5`(xeItd_0;XOv!N82X1%+MG#N z5{k@1exb8J{nnUxcP$Fdz_m(MCI5yA!`SHDye`bl>5N)+Hr=mt=f>35+@s|BeR{fm zlb-5YuP0jj^o7#0{@({5*Z=f~f2`m9qxa|oUwvAW%NdpCrb9ov&dv_kF!Rb7n62*B zw{Ej`-Ehd;hB-Kv7gm5ZAP#_t1_1zSScNDzz!zbFim3k<(1G<0z`!8@1?RX1KP*ZN zjKHGAb%4mFB!V3vXstmB&&%%u-gthjh@>B&MjA#?27EDafrajwj)k>SNgp8igCG2$ z-t(UK7;3&8;Ei{{h*hj{IKUSu1hTpI=9_O0{+CP7Se_rDj~ei`Z@cw@0c648vx)170j=5I&G9L zIBgy?*nQr>_WyiV`cu0vo^m=k4H>`y(6zNmd50m5pqVbmvG0Hxzf`@0Tk?%1cj`)>(@KVo_Qg?vihji$1=>86_kDDZxODgXq3U;!|r zzZ<{-tVGvF79zGl9s7U@3%LV+#D`xbQCT6rC=LK8vMQ}r;dexi-^K3(=prx$>Tm!W zX-gUdWQs#NdSG?jC;aV(qq%#8=aD*fM={v}EfHY7D#F56_Yjyg@@|_ow*RMu& z51+CAU=-@U9sXuvDa$lqi(VwGkdUz>D<40pFoEuv_Zi>-jG-Tx;mC z6RbsHU;_0Z?&VEzyjGkKuq>f&SRj9`3u%c%P$x7;r~TWV9AUy@CjO;A9COWuv^UtX zdc~fbr8{5gcVLBaf@ae*jdXL-2z?S=RKk|^UlAJAw(gX6?6sf4dQBD+KQqsLDYsez zz=d2Ba?Iu2+LEfPGPrcQO9yVeRlSC_TD9zgRdFp~F?7_gFQRZdbYXZz!=sa$nqAUT zQ@d)0uaXtjc(JU4lPBNSZ#d~u$_fn$3dmb}im}tkkd0>4FzK%W@7eRcT`R(rCd{SB zz=f)-Oy$zND#baK40Cg{PQ&Rrm0dkrH?TpK(wxqmeo8|Z&S=r;&d6F65zVG3sC;u? zS(i8zJx$IXU47cU@1VCUof%?zUIoh-phkYC)BVAN2LmjSZ=*hGKu2Veqd67p62Jyb zuq7Oz!u!PH2EauYw`ape1UGDX=fq3=KpX&btq8vbOB!Grr4PrX9hOTfb{zBD0bSA& zcw@Sjs972z&3PZVsQ)45hhwaHEM11g5cm8FS<~4yK-99e<*w~gw}CEnkv4qz6}v*; z2;0&nT^N@}#|$HewJ=GoUk{V=GG83?rLpQDmZPW-9R0#BzQL89u*dt0xw^ks;~asYZH`Mx;CM#zi7P99w@@ zLb?&apEYgv`y$NRD<(YfW!$QKz)xL$d`(C~Nf)4kp=`>TBCVVD?$P>z&MfP*&fqk3NsC6v|QyX3Uy`kiJ z56c_XEHX*j)g>mpjplK>)&%7;KR2tTT2)Q{-OM|;ST7rnt#~R0*P@ClqO9iUtf(ek zI7^1Sie_f#To@b5nzYRL9kjXn+0xvwHe|CozcZEtYt_!Z2Q{#MjYY+WM7V&4=6&H~JcpZl7oqHO7*J3dV0Eh6~F6GvmP)n^YmUZ&fUw{(t~)3x{=2l%6v+O}%=9(=LFKoFiHTiJ0%b{_B`c_U9i>xJabZ?0w9k|* zD58o>AQnknF+iM4qy^=a4ORv#rd(E;Fg-Vknbel;`!o>ui+f%Ls}7y&pM3I3J@Ld7 zI&$PlfH{DGcY`&J5s-idkd4JBvdRG&lrz8$fCDywKFUJ`ZqfegvcNUK2XO)|To;AE z)`Z_pIss)7*aC8qMULMOya9IjML>s(;4cDcz>RkhHL2oEo;Mbb zR~*Wi=|Z??1R(Up0aC8fV&Z(OFx?7(OoBA@$%@s@juj$irtIgszl)8(K`YMtVIDqj z86ZKL(RAAEv~F9m#&OL1=`c9&xuJ1xtrQa;^Kx#r1b_=C{O3|`tqIC!YHCJvg>vZY zhIMSQp3v4U8?l$t=Q)Q<+lxgl*8OT7vK@kCQ z9IQB}SmPWc@5m%83s_H2`h)C}dyNZoxXsN^Ymv!!tdy4P4dp~Bo1dM~(9k(GyR@WT z-DLFz6c*MNrc_}chZ4sUhh?G7a1AUp)2y0Kg__ugZF{ths?*gjYp{$Uysv1jQSvSz z0R%)-e$cFnKadx}T4V*HOkkA*0=b^+@B>m-%WA0LJn<11_vQZAD!d_4dH`clA3=`U zl4ewH7@dlcrU2V&D;(Phm`O95oJoHmjxC@~JPeXxvf$Vf_xuPWL;YC~-z4T|^D7EK zwQZBEQ>0Dn8z6Ch*viylI~}Ik*}BV6mmzx_+On+%GVexeB+?^6`8IYN&K^OZ*ikfC zFBGfOHC#+C{x+JNfwEv?vjgJjBL~n;8{jb3JL?C)B`nrW7n9(y$l*s+*Z}~wuwsU{ zG&GRTEX>cuzpeJCm|X2eg~yAY^Vx2-tmL6^#7K&*%x{H{51V|l$vZ4N z#h?Y2&zha1Z;Nx&bs{JW`V;1?OzPOX{Z*qmxn3*l+}V>VxZ2v?(WCCpZl@2^QdC_C zXIEFnQUrU8CqLfyk!m9q*J;{y#t%Ie$&>V_+$Cb3Te-Tu{6VxzLXmv z0jmy3L>BESB^iE>= z&WYXy%)WZ@7)RDG~R^72j8r;xG_qAJ8p2-yauIHRa1hv%!sH;EjFY4U5$E0B@1- zTY)be*0KoV5Fi>re)Q2t!$1b04$CK_O-ZlF8pnG9<_J%Gz#CJv;3zY^4@R~EalqW_ z_YuptC(in^Itf-Fn(-OHve(q=o-`f4pIJl5sh>P+FEfZ%hf zpt!XqeZ2#r+0OMJZLpH+Ff?pfw?TK_dAmy0w7U8>X*u2DLe`_#+;^+iyZUtSwj2Gj z)6O(B>ok^;dM1^;bq)&6r7MnH#X{{#=boyoWY^M^A>WFiD8@$5E0xY`IoY8_?A|L7 z$C6_#Np}AHDLwu4Q#y6>j0;y$-JQMa?&?#{Fg@ol)wI8ig@wk#=ztv4v~;-Cp^#`& zC5v|U3~Kw%8~l#^wT$K479ao^qX`diqaPK^Trr3Np*au0i!4L8-g;|zA2;53V|eGl z+iHMA@Q)l4H5YO(00a17@Wb^4Z;_A=Oc4UOk=`7$Pg>xBI!5_E_0&@V{Nu$*smTfN_|vv+oBaM<`C0F6rvbd$ zm=1(yQi{&YUTj!!`27d{)iOXZ0yO}fvwScL@H*J8IEoN1ocES~bj+3J*L+|zz+p*aDvRFkO)hZjRH`M^+T5k7$tksW z_Nl9Tg9}Tudb`?`NM%%NYSq@A+ZzDFN7bnkDVi|yG`wNp&G7?sz6Z^Ib^3n!RFdAQ z*QsEIo0&Kh(tW9^&5DVpRGdN1wWCxrp_(Cg!e7B-N1xIYPd*wfZ*8sZT0gisC?2c} z^LA0w8dHq4`#j1ipQlMXss8m_v~%x%$4NammVd0E)KUI4>tVrR`VZ0>YaBqp#J<2Z zbB$pQ!XFsJ8pOG6)?&a%)U16uAmbj9rH=4ydDqt?R78-*5?Q1p7Eb&~7n(q$G>Os* zE9e6cJP-z#@SXr`-qUJ`<2uq4c;o%??szA>FCY&1Lt=>g_JrJx^#&^(^+{YUEA?+M zZ24qMYh$D%edU-QWWMY$hPdtfeBSEBuhj3l5f{^tAROY~&?i3jKGq>DN$4)(x?Tm# z+2pw4%jw7`hUrCEl8b(Eq!pike^$bT$s=RZV~@&uMFIL~TE!|yGdC0cVv(y>8sT|w z?S{AxzaxN-xS8yC*X{;5uhn9*lBF6y;8N~cz<1Tl{Ja&-_8kVVRIu<&OigKedR$LG z^{`mVY0+0N8PiM1VlN z00`xSeYQB>B~7Q%`Ri4pbjQ+0dH`OePXuwmE@=+v5soeW4ryL}{PD*_Ix}?$?+l2G z21oGz=obgXVcp{$aZFeo_lP0xA20OFN*dB=AEj>_OE^soOQ(-XqeBfHxn-NQZqRzd zV891~DJ%9KYybjNjf89usI357ii)k{3ox>>&wZvQ8vu75+XF%xk%om?r%9oac2*6c zNfl!W=}3C9gc(o=_>yilz5Y9YLX^`~e4=_JB49 zDlnA@(_3K0p>sVjK(pzVEn7mqr|Aw$5SBQA1XF5OBm7}xWl>!9C9sv80i;B z(<<*M2DxutfPncasbnxXaZ(x?moA)_hR*mM86f;B!}^}N9hy~1UnHHCI{mIX{f<(;mS*t1HyXGN;mFlcOag*j z#;ulXP6IBw%JbCIPaEPGg3m~TS|8LMdvg+GOA-o9}oTH2G{R2fp2xGshYrZ z>geiJR|`!Wi(zm=0%ep=hHX$#h7krkOpFW}N@^hkv9>BoES8283)2R&VSk)5N+g?A zDi>8L&%0*H=tY`~R#s%N!b&@BM@G)-^Pm5u4nO{g2Kv^Arf&v>OifONX4MGfXiB9| z77DMqwM~0&yhXeA+!PdDEZ@4I%z-s5Z&*Ho0jxR9^3VH;7F`Ft00FFS02weqpE-bo z@Pwso0C*9^u^+`r*}$K77z2crj?)51MTK=wfno+FkWTmkain?lO#$wJG1C6Q2Oo4< z`Fu!6pqh683^K?e0yzYLqj{XNK~p%(Z^bJ&{Nse_DQJ!Z)aYPOXZbM$T}OYTL;Td3 zG<>cB8|$(B7zXHg&$J`A-!2{8OP|t)q6Izx4i^#UfrjJUtm9yz_s}29uRT^L4IzE# z6UYDt=7YoPM!GV470uIRWzvgnlhf73Bs5c>GH8G4D~JZ#q(ayttF{rEA?BQo}KrLDr=#*pqA!tZ5Z5S zn5g>2VT-MWrnvFuRjueyJ}p*ujB3TQ*VDgV_r3D<8r-lsD8(x!YpskRz!y-3;*UDU zGuIndq39fNhee208aPK7=5WKxLl~?`QHOeE;xC(IYeAPEMg%oz%KcfhoZgj)SRnfQ12e(8;GYm8F8lCOb#U(PiW zr6Dkg#oiR^OSv^Be4LhQW%c&-s=cj8n>X#!hV`2ac6EQu{FauiU}k5+)FPu}Bg*Dl zweyBM{qdX9)aZGKNou)X(cH|a!%aBD_=D(NQuW7l((pztm*k7ma1AEW3nDZv&(Dr( zVQxfAPP=5P-5;rzMxiqt(4>l`bJ<_3lob>1%$ei*+~@vY<5p^0x9n6d-zt{mnzeG8 znwkzuDoX#hmYlY3-Kj0xc3V-kDMu;1BC_@}Km=K&B~5Zz*RZP51V{P7;sZDU1B4qI z8Va4_vBUuotlj{8a6JG5Ot2610d~EVYT4lvET!H@Sq8ENz-oe$Rngi*6A00_)QamVrxh_fu+X)*?_POFY} z1Ja(|jpjEjW^{dK4!2qQ#u@HTpKXjzWrQP^x?@kt-!g^8?im%y^cM?rt~?nWTzve_ z3~vUHa38z)ja)`n(ucGn{R(#MGXfQh8(EpAXI!Z7_!#RN>B;j0|e2jFpOItTItgeh_bWsa&UlHNH-275*RZa;2=d`(|Wb;x%>B4k?f;97# zxfGVlvwoRRXn1r;v;M&G(VUzd3nL#py9dqcNsARLqlr=1kVjN7 zuGg`=VQp}{AsttQDMezrv;cC2g>eJ+@&;#;-5aJ_#&I1of6fWAv^7TTx z0B8UhfXtTki~6#0o;1fI2N*{H_Sj>O=~JKjRJfMGCJ0tI$_EffJY0j7j^^5!|;~%rzaK{Dw6KNF!Zw2;cx1eCR_T(tF?gUVZV4UkuPdcm^*-(~EHJYAYLp zAMXj+qdAbckY|UB2#&OhEON{ThouwCC@_ZrWTY<=^@02BXFnU>BLhx=W~`yS zPvVKJs9g7scf3QdeeG+*I|Kk@E8KGn);HiQXGOle(6HP@i>fm)o`vc^{2A%Lysh&>vwG}HIb*E78bP?foph*CaSS~N9ThfVU z(<1N15XM@nG@)931dwCE0}zK`DP=m5haYZC222w;)>N9VT~u5~8bFRUZg@=E?sxV* zZ;=i$ub;2I62*jDUe2wS)ptk_wisiT8g%QM=vew|L7KB4=+`c<7eb=)%+sZ77jcy2U`3ae-UX67S5_ zRjdpE_T=P*@~$#P+BjP{Pa8D zrH_5|qdLjgdkqPrNlAMYJ6m9YEfzKafQfwpFh-_6^w2}W!p5`|EVBhb0~c5qZAf$Du=q^u{;7QJ?t434Qs?tp7@7?dlhlC7@O@2#mXa z!xCCd@d1#*(=_4H?6zftbndM5=ws4@4>mf*yO=obSAfE*VszT^dCRpp`e238sCIP9 z_p1s}!W?kC4kihXi^)GMq!H-~j4iJ8MI_Cs%VRwQn0b%Uv?FABT&Le#s~rHywWK%U z=oiO)@%wIS^rh?Ydp>d8m-2pIkoS?%ul?GKHRPH?DNKO(jnZ10*T7(7$ZP<(q0upw zF9UF2USj~4G5}1vsx)mRpDUnXs$sAKAHQ@aVG^(F&Rg%$?j4)N!uC%*`J_Jn@s9=o z>+kDTCW(Sc>io!*3*NG!piK+JQ>%s-7$)!a$1_^50XRg_F<>H=Z%yj;stZra&S__8 zOQ*3}CtX-5N``3{I+W4O)TDu;*B?#hz?upc(q)&-l)t8PwqU>3Lfx!#(pJD{$2S51102II)Rw00aW;)^n zENE(tfDOO^I@re#h`<4L&o?k!D?&O&X$g=4)}%SaM`)_E2hD*%-HMSlmTyLQ zPb*oMdJVRe$l^&8ID;MnfH`fkZWjC=8I{`X_d_#xXwG(c^bJ-!nz`viciSzE>&Fdz zIIa(%{rnV@75QcWZthtCH!`j=%k^JE)*8TNn$qgPqSLlf+5Divs_vIp3rlgdwzX(< zY}7Q~qPEsfZQr^{FMY|qdfB}%*HcHH&?o->qyC_kwRP*pU`ZG@#8qp21ev{_dCitg z`>cc77`4lLWXt-i^yNZP#qzBS(~H!qB^Nx_MO`##=Us!!h7TkwIba!OEE9LkD;|xdR3f ze9@;07z5t0#L>SBrO%fBaZ$5sWbFa?*aC3?AlFB6MS`UW%N*bk%eN+^WAt7CHS#}O z(wK@V>B>3MIBGU!a06)##LEpGKY`%cN41GrU5&}EqF z$oo5l#iq?qZk>w7hGx)a!`S)r4Jf0}+xh|Nj@x8?Vz8TO%t9!*L!Efz6+X*KtitNH04;42v8N%Nv$c{8=DBgzj^^h9#9jDm(Z4yyLlXPk3c6ODl0aFR}t~^Uncr1IyR!y$nWSSDXV5PobP#YU7V`uojrd+4?X;-_U+!Pw!A-#hBun{n&`9Dlrdan)#Q(#QOj@PSE~A+ z*S$5h|WebF6Ow9V^t0(UvYd-*+=2n_z z&-h)QIMv7puYH5xvFn~MzNgX8@%b($&k)!*{#z~N!H{v*vP6&H=aQ2!PEXaXBwPY& zE)7_8GRsNT7s?u*m~$a)OdiWbzY7IMnU3kyu_rZTaJ%icm+BXP@n37x#;tntiKq3$ zZ-0Z{{)2A`pip0~85$hF{{>|f_8N(q%I3xTsIh!!gu+oS&1!D?g385dE!CL(*U-FJ z*6Cx9sc4vBKDmjR1tnWHXvT2<*keca=%a`A&_j=Fwp3B?;6_bMj%)L_eM(c}VhPF_ zr4C$0FolqxqjLZpK)^WwjwU%QI_J-y52F}?gSWipEqd3x-W9+A%g)>1{`O#HAsl{O z%NB=3abuxl8_TyZ5zJv_1J)vtivW*h(HIE9yQcpl7FQfF&Kj#YR(N1-TL2u;W6jlA zEKWkcLF-*NWPicgIP|mfA={MC6=xd?9Y8Y+tKfaCH&YXaf-ytgq%_bkz3t7?-7jja z+xfP)O8@i+8qIY`Xx?*i@M{Z-IJ`OkNl_*bmaCZ$D=}0S>sqYVRrK2#9zJR1G^*Z# z&AR=LyLDjyL9JWIB)@f~(plvUX>_o!R7=3S6K*-wni#~u0_%JPWBJZXu*f;R7v?7o za!gV>?*doW)?9v8E zB(m*F=Gy#mTvXasSSAFX7`dQQf&PlMus-dXvlk2*3+n9ZutMt5CWBm0-@299u3w>< z4Y)$t189*2hhw%VeH;TEn6?7z8V>6WVX?4b!C^!rO|UGxML)U+9(W*t0pTL6UL?Q( z@CGyzhO$Onv3zGG0y)wbiz@pxh4Vh}qwMf5vDR@Om;=@UINlBK6S(J{l25Kzh~ro1 z4N={BJ40NPp$iG{X0U7Whrh|&yt8n#!EMaSpS4f{fsC&UeXpzvQ2wq{1{lL)v&D|l zjEA2z*x8TfSeoW23XU#JeZhQiEYHP)>;PO`Ouq9%nuauYqGK@)(Cl|jGb$!l`ou9Z zbz<1(4dl%0$2+~?_eq~@7b*2IJE80_P~FnBw`kdTVdsV9izjH?6JHclFhdps8de=cBRsJ zC2C~@$b!oLDyHnuXyUQ43wr3muLa2K?_a0>b?dZk*M4<%_piiqeM$sk^m$_d0?-D~ z0cJQr4n+^Z0eDztXllcf0|=nhX=0@h6^freRdj^!>goz3Rk;o@0N5DC3fut(#Kk%8 z6~o)}D^dDlVFS!~=e$SW9Rp2x2fQC3j`#ie*CWL7lbJ?h(aO4D zMcul|iro8Qno2A4Bmn1K`oZyn05E-od{zmUd2bSmwAzf^#X{)kHkH>_}gI3P_k>oLFI zqer9`zb_XPVGr27ukr54GcRaSS(P~&0JoOKKPI2cXui0l2cJHz(?ip$`i&GzqpFoB z)wHrh#`4`?V5C>dG z@CHQl4gfkF0~i2s@&mB;)vtav^oQd;VuhnAl?Ct#OMKT0$ni7D{#FGVo0UHQB`fWx zd~D!iWFk|DFo2fD>QVf3d){WK>$B3Iv)|Vqk%mXDl6=lZ%jJx35|-~qlN){Fx;mtn z-sXGxCN#fd-9d-B$b{)d!gQjm6m%@lpOC8=Y06Y0SV~h >chjukR!?hY$G_+9SY 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 D_JlOr literal 0 HcmV?d00001 diff --git a/docs/source/gui_tool/user_guide/media/image436.png b/docs/source/gui_tool/user_guide/media/image436.png new file mode 100644 index 0000000000000000000000000000000000000000..21302739ca6cd7fbc210187dd0e06f3ad05927d8 GIT binary patch literal 13321 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 CfKu-O literal 17704 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 nE<)CAjK2)n6vI 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 X>FN$cLQY^$F@&V3oJf_Bp8x*?G(_k0 literal 18704 zcmV)BK*PU@P)Pyo%1J~)RCr$PT?b$lRo0&O(gP`2R$Y}|1FnJ;Mc0lBtRky`s0c1fT_K<-C@Lxl ziXcU4YXRMrs<^uX0_u+|i&6v;j39(6D=LseP5)23ckaCRlDt4j@{$>bmzg`ao_oG~ z&ONtz=Dz=rjEs!*^o;z1f~TH(DlYNdyu94}yuAGUJor~okY8X~RssIFmrH?4Ljez! z#3TRU559Tuot*GR9<)EgfQ<%tViyL7fsnvpabSp(mxLfCP7N>0hU9{C0#{;mLU?#Y zSa^7tH!>_N%rkx3w8MuFWn^Z~`Nx9DaPPps{=;|%aIP{ZZ)CZsY;D75qSr^}^2K;y_Y?^E|ai3)?*Dfl?}eOEq_4HO43 zN1Cs~utvtogYC!gu0A-b(|+;yX|ShH-TQKcO0*bn`f~t|DwN5bIrJ;30M)Y2IrrQ? zci%Z@$@0_U(^3x9v*T^E7Jocx($p!FCm%b0;+)uXVbNKhF1(kr1OLLjo-lm&gn7I@{?ha1CzD4Fd*bB2Jz+=z z$|v1GMgM;GnUPmsecif^TaFw_%gxEo z%gxQp&4FLw62JTi9qo!oKCrJauQv?-!xe_|JRHP%IQ$L6SN|J47*YS#hY!4KN z!@u?n?wk%KWj{;Y?2a$ufghBEJn4WO6K7ZQPtkH1DjAiDZ+w%B6XBwAJ}8(xU>s0D zdShx(T;bvP0}K-eHKq_nAR!V9z%?#y(EC3TUKzO53<&2yG=zQssx+H6Zcvy71E>%R zw_17N*ePCMJmxkh|4Kx5+iv=SAe7GnqgB`KJM%h&%Lkttn{8F!fnDeW_~EGe6)Tl4L=p*Ps1W*!e@S{4 zK<%Lj&Ck!x!wV}Ae!;ZBFZ2qRNLrG%ES(J*YrvHsDLEFHwzSnRByY9UWr>OYsp1GK zB6YPem8cA9%)*#cVG#<|&4q<{I1+sEh`WPr42jAlbp&yWTtD1Wq;&az()~bF{Fx$G zvO%m1#YK5bMSxZlT2GL)qlXTBap>d6_v{QOukaN;Sr&>}WMq86GEy$6ohy)HbDoR~ z>R!oYbR|~6C?E_x!vjcEYAZpslCT9hld_TLv8vZ@@XYwJ&%N=^2Oq6K;#*dS+pqsq z$J@O*mZxdcrv3h!oYdl)pZ@p1U_h%$ueBp3WPt=waHKwn8mL8f*V>h>VnO-=T5?Be zi|fmop6$3CI9JYEt9PQ3W6+)$ND{5!O@kA^X(&=KLxraJ@kih)07eHf2T;8-NL66T zDWMZl3GjnQL}@NM^G%mWWgx&x5^ty)d}n^FtmxxoaCh75SOfV<--Bht`nc6*~olSZ18Xd(*%A#(FNT*JSP2U$5P=GczNtSkRh?<*H?5Dt#E?%KzAf`9nWo@ZtrmAV>WPpw#gv zA5rWhD#ec-nZ#o>2inDfSgryQrvgq#9@uDvU-&C4t^!C*Pf1fdhm2?G_fnR)-+?_> z>b_!s1aO<1!@@Qv2Osb^2hq#F^bft`FNK0c;Sa-Aq$_1bXFwr`z~Lu#wXjtH*2rau zk`!f`$wC>Qq@&BdJtZCq07}@J5*0oGGZ2^Zk0e1cDMS~CTW($`2t{J+YeC3cm9(U& zvcioP_i3RdAY*|~L9{;DyXUBZkGwEs=l|S=+^$J@6r~{ zpUE6|Pq)5p&W&7qINNj?^M@KHTv570U=ITrfr`CZDlEiay+PAw#=kuJt+@hPXja0! zo$k2hsy1y9v|BfC{A}&_M>Eq+(7rtCDVyPgczWXLXQb<<`glMF#4ZY82C%B7!ec8G zhyFnf9QoN?(YMo4;nNw9pGxcmw#KEd7RDY$qTnpJY;*@19YRew9NRgOjiH!h; z>iqan-nxte6Q$;Jt9a{A=Y_FE1!NLGV6PsqiijoI3(=$aC51Vm*%i;e`pVe%KG2|r ztoH78Ps4_d3)9AffT`0MQ$r(67>&79Sh{1-Mqbb)3p-O`UK2j0!y)y-Iw}<{=MKH<+)6Ndmka`g?5x^_pa#mwAiTrS zx8yvM(uW^<*O5|gI5`zL(FRe9P$jPs5%h@u(9*1I+-w|RafppNiE3@f63EclI>S{1 z7@?)0OGEE$#UhB2J9!|(mIAkG1f;voV%u&I)HAmSsza`!%ZTMf_`fo}6@XfS>Vafi zkghx_?S+~n2BDjS-h<6Ic)#R@#+a_h*F(%Jod15Ub9&q5zOh(~5tS z@d<`-t#BBj^@4g_5a!88J=p^;q$ew#S+^<>=NIQT*%DXtjVUj1W(E#=w%O5r*B|<+@+<@Izf11fw4J zdg!4|o4uq zB`M0ono5e|UllX6HunHTkzr@LA(nh0;|d_pOt3~A2#IrN@+3-=7H#A#TZ2& ziBr5u63R0PSaL+M36d7>RW%v{Vkb>_2Afv8;CX9YO>oztd>#UeS%?#7NdUl&T&pq^ zuO2_~2yXB^vH-7UCL{qC)E&Z$Fm62NJQLpEx4U^V)fAPKBYf&3p zyW_kCi-ufz4zi`bmGN2dDviJC!+y45LhZDDE3^~KGwRe=^ApJpNl1KJ>%QCvtqA8k zbIU&wvusVImY!zJ@SGh|PHVLt(%MSRzz_A>{t`uOo%n$Uy^u8n;R+ zv86^2Vv8kJOhh;)XiFI?5oe2yGt`kJz86hbDZ@$?Ccub-Dk?LBL=P_M7sMe!S1HU& ze^wf^>fG|clme;!%+NKF%zCZTy2YriI=qBTW3HG$o8hQbV(RXS=pv+QBRnRhzBoJ> zxD5b8(Q6D9&)Jhzz@?4{F*B2&J0!#cRIzkpEdx6sN9bRa!gzmi;bmJB>Wm*ZoZ*`M z*o*uCF5f5C`Ok%y(ZG=k%Ok{cvxS;Dm-t2vk7)AQ4|`PO|&wQPyOY)bdOrT5(34el;?cU!U-v$yZ#Ze2cJw)CER zyW+iUAA>7n^CWS(_q9zK5I$qm^h7W(>>v3!MQKfh@_WkGU2c8(-MuDDfq+A%m zA3c)1Q*p5(*8Q>MR&$qZ$wZIPR&Af#GI>Z>u6ys%t+O8P*XQ8@eIFUncV7_*iy#Bd}mK+v+TM*}+| z5Vc?vxbg=UX41`7-An1&YN;!IPbF7bTvKP4d%R|ER;GG59gR0kgM)C+Me=jMfNfWF zF?1PA8n-5ZP24IIyau+2Tn%iWZZI)q@m*Zo>LoS;gONcUiVU}^MS5c*t>{Q^bfhOX z+KP$x#Kc%JvEHayD>BxKjxC6ewa_Jo++(ch=&kX!$Bup+P5-&M!-kJ*c6>kl@+-K1 zA77iip=SykYemI+qhmerfbLP=*r+f_i&C^=BVlQP6~^;8bhnFne7AI7tF|?32w4i^FYEi(jK94#eb$75S8jM?!%rM!vDcFU7BVe1fN#*7%Mgg!%_?$xT{5b zV*$OGgg2&|O1GLPA{ugH8jUqO29~s1qp&&0`2-$5S@ubQuO_*E_0^cabh*28m%E3|wyYnIq>-E}^~OAUS%Ro>rWG^(4|jB1 zKVjYeg7m##Zn*c3X7P3e>074vd!%>&K99WtIp<*x|MV}Y-fm;+MawWSwnr-XA7?^5}lxNm8G*~%m4PBZj z%Fs2q*SCwS`NUxjRhBD`E!-RFtyV2EF)_S)^|0#IBjT%v!<`W4jgJeDO9-o;;Ehj+ zNQk%M;=$b$Up>%k**ZKz|zbpaSRvP#4XnV^8r_d1~y?n0B#lVnSTuFDh?dKU^gKyCO8Y$!an<8|pxdTR>l?1W z{*QnBqwTKYNe}t)%{ty|%^f-A+cYI8n8@*UZn%2Q*ta$uF|{rk=acrG{MAb8X}vq_ zU%REz7v^R00qmM~c)NA=^)=(ceb3f8He{Jy^W$p)8ee{A<6(zXz?lPbA=2QAZ+2&o zweP*V_Wn-IsxyQzeq>or2uD-oNyb;mxGHd>nS`+H1Fqk<9Nc35yoFZ|zVi3<8QCrG z$dOd5#Z?zq1DAc<79oz&Fj-)2nzn8~dP+}OvDmt!?gfm(*!rEG>T|vI>AqhvDRL4i zOB|oaAf^RaY~LiN=K@Ltqo^eBO1iSND_rU_#5o>r78tRTyf~U2T}F%J(ols5pU$uX zs2HfY*^{P(w0*1%n$#g}JB?v+qrn&?;FAI(Dg;l@;3ix>^GCuh%VZ9$MS;!U1W^Zo z5wN(v6dap0YO*gq{k*1^SG(Z%(I{`@qT}MC;^PrxAR(*AMN~(T3$NjE@ev>uK^m*u ze8x|4aHD_rz+WL0g-yhGs(~ZEM#aZP!z-kfm;mWQ>iZ5KN@~(f@{igFmjy2va8j4; z=s@U_kC(js%FENI&p3JVGo@i*I>* z;zcutK6=0M1EQ}c)X^V$jkv1sOZ`Bn#S0vVTp_I^RRrru`T4clztUsQ6T`a!BJt`m zZhrd&4Q?y$^1&}1xOc$4I`agOJ>%8=2rM9D~8RmFIsXA!v-G`o(Qp zSOtJkM3XpIZ_<0%{{}ogP-l|X0~A=7U!MBv;1AIC@<*O*dqHmA{;!^$x`KQ)=-mJ2 z8aT>;BkOt)7v~@2(gB={OhW;PFM_bZQ#ZuIu96~TktW&UxF7x?FXOycbi_aQGuyYo zM28>p(Kv`KjAk#KE>5O98f#(`s@IdBk6!rKHb5#p6Q-{sbPH&@pZyu<#^*z15{v@l z^u2t}2hH=8b-9QjoPx_JW`hYtNx8vL7=U<<4KDaBRzoNwd0?9zh0#VCoHoeOaa=U- zm|WPzSWxiQ7pvPnI&|{mgF1HpbHk)7E&`iSZZ>$p5nb{QL$zE=HHT4HO4V_ISIRmw z9$vCeKq&-yxaff9HgX;!A`hL)*_xWVWbxwUDYMqC-9#-6)*Y=eB67go<1IAz_*h2z zF<6?HnRfE{F*U40eIL5vNfo1=u|Jsv5;B=Bt}_;6Zj31Qs(ogn3P=T` zBAJjpi$KEa6>C26CE2}+;qiz7 zCTbf9qMSpV6pdMdUz&T2!|j*Ws`-Z&m;d+maXWYK`t|rp3P59iLJ=m1O(#rLz@(T{ zwGtxG)vM~>*@^&4I#eXE3i$`3_+7P_dUfkxlRWZ=lphcNa+p>haoXNTdfy9^D+&Sa z$rG=(&XiT#bR_bn4~);50*NJo{<}Hdq->5{NrFeEI52 zYBrl-N<)(cmnd%J>J+f7d?_4q#H`UVYy{OB%cS6OBu9UoJ7!ah5nUQUkHZEntr0-n z;zEI(T*N1!lz#}W@Pn@S5ob-|QXf&CxmRbjAx5L6Sd~DjZTs}b?8`9L&}H;l0fYrz~pliG76y!l#2WP_}!|pnZ0yGD134sPy9naEB)u3h~(ArqM za8waRD9*V7SeUr2*RXz@Yuhr#M3L4Mb8@h>+=|~=EpV`_$#zCVZ>qb>KUW4! zY6^jbV@HfFbLXN7-(5bmQ@v`aU891FIy0@oqdAPUq6eog(o{wSE&`Zba{;zEHyt!& z(4MjX3FasxEmH|&5DQkh%)$*Mnh2-NVEim_HIQfuJwxDX4&N#7zXKR4LKO~^VNHItVcdoqWM(Lj3X9Kw_)l2vR+!XOLy2X`~U9ZoP`GQR;q z+ua|uX|Qv}>4Em~r=F&rRcJkmD8;g>R^}lo&TxR#c+n!1P^Uj=Jc^4cIJFZ|M_q=1 zV<%A{RlKC)N(iOFvM5zp7yU)@fz;(%5`|eCK`3GsyLw`gYtM}52EgeiAg5j_uE;}u zHy^sP1YLz(5Uw&2nPe~`o2MM3RfgB*=`^JU(gN3}tAJDMi^9$Up=(NAc{UMd>&iBC zMOT)oDB8Dx*A7kW1;8 zv|`*TDuSk&%4Ap4>+OC3B2nv!KyBzn)2=XF)4U%A6J4!36g(4WDPuev51GBl+Q#|f z2W-K3HZ0GSMZ^N6D0k=zzkMFQ&jDJ1>+wt*w9u{1I(0&A>?V9GT>{mV5{+qot&*rR zDOat6H)rs1{W%cpP+ZL~euz~*{Shk~hsG@9PE3m!k8aQmp~pbh+OirOWbvr9UU?Am zsy3Bd01nGGP-zrMXv<_EawD2iFi~GMyp^g`-{=aNyQAg!M@7RDo8B3Ccbdo2DSEFhOi8=oPOSNl^P@m(3RMG#4n1FM#kP9v z6u(-2Dp@SjhUKLJB>}V&rHn2n9Qh-?MjLb(jj0WC{i>doOzxFw2EP-0JkC=a)>++-ygy{V;3q9K!vB{pSFP-EZtLb2Jp z9VJ4o1hsIK3s74lSWl+BVDr-Vk^37o(0y7bXtPe9bZ((4%>#i!aBFvqbUVr|Dq&TO z7L*57#nKi!5Nl|)wy<=HO^q&2oi78i0#R^OYO6^Q?pf$PCA5GQiYc{Mdb6Sei*Bn_ zVjgy-L0qXM7NTqe(i3R4^b`5Y+c+tFQE06tYri~ALZ{jRLnL zHVFd2m5Hn+OhD-R2;rg?W*_Sm>zX1X4I7myD2dMuf$Ar6nK|ruOp(j-S9;sdo~_cs zZTYVh2)&Ifm7R+(fIz8CY5uX-=enER(8fJucS;cWW8X zgbi*J@JA4C&TL`is*ChuJ2+*Sy04@sV~(U-%Vsh}a=%9&2mo5xDT;@DG-yS}7z!t_ zq7OGPf9T8SRapkm*+i@=uUTlp7{gNG(}t?Jh%{7^SAb!|SyNFuqiP^twUjLDh}RYB z=v@RT(NzR2!9$c`yBEn|RG{VA4dO!13@A-H{gsmyDJe0q&V;^62c}r5uZzzhh(+n9 z#8tC|(%ztvK`X6|@Ph`Xw8>f~m2l zn&y72pjL@x+Nh2DNIW@&*#*4|I|z?cQ1DE&rOo6Bp9EU^C8^8isJ?w3XxKmx`H-YN zmXW5%X&JQ8nbM4`0i!VlrPu7jJRlioOe?k!ggf+L8V8n_pqJ4^jYByMn z79ADChuNFyB@LhmQ9aYuRyXm9(MCfl^dtRKfu;kZjCDc0su(*2tHlCS(=R*XL?^CO z?(@ik0M+*q1;H;cCYTVitrRzllRcb3}=o(700Mx&us|Q+@c}d~hp)HHG z$%L^jYzgR&UT(=mu4;dbfn4cX6LXRj#hYzApnU=+=oi63DB)32u|R=OrTU`iR4Anh zO4Qyf{u0a)1jO>gO}zvm?<{7aa#6$*Pf+A20!c3-^_1-ZVu<457uMMky#!iQJ6&jx^X!{hL%&>JAMBW5i@#iPxITKXD^+K)EqnI?7< zz-^gT8idEtLrE~xw&nRa?n36Q!>7q6EkD;KK}3}#!YHiVGk9^oM+DSQNvO+KnG63$9a z*sLUh43;)3OJ|z;9^MnjPkz4YOOpKvT>x&sz7IOpV+Gn)mnVJp>B@KCeGjadJQB!> zyO&FW5T$?ypctaayvy>IZnijS+_HD-nzB~RZ6zZ?b(Od+w*`YzGnqsjO7uJOrqyC; zy(voF90*~#p^_emw?~irufO4@|6BDXnvqFi9?-X^G2{d4u>$S&S6|uvz6UmMkjcRz zT2F4aT?&Lc1(KRx4u@L)d&zR?)_TJR^m_o>2^m8hX!)o%VP4+YrZfm?Qw5sTLv z_-;4jLbmc$xLcBtrvU5EG`he>EY`e5()w7K0^5;7o(66KTndB|1yr}0hbv^j51?B7 z#%`fhN;j`A1u7i{$eKlCMQSQXAkexxa;2+yw?Lst0U0A?yE83MV9T^xxzA%)G=lE5 zU58>}-8{P#s2mgk*y2PC?7h+v+>Smi7g6S&xu~riHSU(85>P;!m+YB0BxzZ=>S<-p z2{V*E_s7w(H(Q>-kb}3MKD1cMAWaXTi*GcE%5_V;d zG*76mJY{xE;!+@_DL}KN+1|tl|6sqCkGkCS#fBeXwZQSC8OJg+u4#RF*L%BvyS|ud zj8mr14ry|@nRh8rz7!bLzgN*2Xq?s}ZoPV*ngH7GHvQ+D zZO1b+jvvbe=(cTn`90mb1q0e*Q!QVjxT(7osIU|OnO7`)0JMu1&i{DHGG2fL|NHlQ zq;B1MNoIRZ1B>?X#%)(CZXw*9xfG}}6aa+cwpDn+){F>V zv`E2%0iJV#fTj~Y0MEQVk9)Zka4Ask6aZ0*n2n5#h>QSu@_rq>L!=cR=0PzFa+VI@ zDfhg(8E`4!QUE9b9o9&|EZzuQus4}~TGoxj0M%-*XP0O$1zZZ0GX+4>0$2gCeDM}s zeBKvWp~OTL2`As-X$kJ-QoyA^`BMPY-l)hZfGdjHC>yjmJqnD;pci9*HxjpNF8|EB zIdCaZYR)2 zY+a{DTnzNc^bX49pR==^16Zt%$B|m$fB)>etRp{vI^&r$1nuh8|4I2_8*R3VpuP5* zw&$P!JI^I`8}0mI>z)I@;&G^W-gwrfzrWy`>u+>HdzS0BGDm-H^7I}zG`zA|le%?k zLwBr7fi^2EYxkagJNEwc#=pN=J*od0g7&Mg{<~wxP7<_ef@>--~PFVaNvu zd$3O%8HoqXR%V8B%T@*y2#cs+^OCxCY5`M~L24B$2{7r_sa^ATH4?0_Qs^200i&a% zqT!-$Eu9}GO zI5I@p7Z1nckPpq4qER1$t~*X!D&(%>k-#2gz`<1tvD`3msd)>wGrzC};0aN5Q%6HB zg6Fe3c_o26jj_bE-ZlN!Tc_{x4F;}>iODNV7Mp)mfg>+jtR?ncbfPPh6B7dz*h~eE z=*+3YHx$2AE$Bhzit`lM3P%MWrQ_$P&=z`{iJo57$~u3Y)qiWQ$`W@bXPXpG9q%{zD~?ZCl9dw)8(d*A-_4E#{a7paPLk-n(gY;&&EYj4PMSZr%AdY%tn&?3J>UROi&~NyCOE=@$#WJLo_$wivOFv#yKz{$Y4u z4hpGFKAoDOr#i%V;RWWZd=JaES)sa-Fhc6GD@F_F6m^p;v z7sX}4e)AwO;mXg?&CSb&!=foxbpV%2;;ft=jVz3+A~GYi&j%|a{7>&B1CPWG`V%y>Q}5$uYGN$E=>OLrq!Q!sS_*JR!L8PKSdA<+8qGB6c8c2a~G=> zrE_7k7W@@3=c(ICCokazp$u)oC-Cl!oRmXZ_MMh{A~$yjyTm=x?TM|!F23yJo8D*| zSBM`oY(;&Zxj79=4Zn_(Qf{I6C8TB`@31Ht*YsrUa{+WgSg3|wnRa{&idZaC8rxmXLP+YL0rW`iO0vRi zU_Xus(F^ym@j|r%%E~aUTi;sTj|;9Y!#C1LyBAhWM^Y=3yI2d0HRf`V!g3B7G_qS1 zw;5SN@mzSzLsqDK`^H07LDLhL8kaPIRKTSOKaNjkR2Av@^^J!M$!963#|n$QyNrDy zXiFh_|9ZANRG`^n*9&ww(s`2>GOU1k*P$(jA7`gDVzZ6*iN3)`0^g)-rZhn6WI|-c z3>2DK(wPDN_O~Y&l2yq7b334Rwx#rLzfAfU-w6XyD^yvjkGsxCM75g)BPi4X?YBKi z6kuIU9|hhAv58P5(nm0cVh4o_%ekEaWeE#}TnFW`c`01bTENO#x#o@MM=6(;{}@#8 z@Va>yzS{rO-G%w_2_9mvQ2e6WzLI@o#Hyq5VBx^UwHs{t?%QSm*$6ux!%pQ#GtzFp zu1${zdsdzW1JHl%XU!<{u^&hlZE8h#!%J3qW#StQ(n~rPpKb6qzLAgROXu&}$KJf@ z##R?W^F-I-`G0zK(guERJ@I{O=zZ42gPpIotgnWR7;5$UYCQOJ`k}9(eEpod{qz4@ zzk0&Mg%u*0rFl&+yJF6q*$Wnaz*|EBZl4iQ1&OP^Ye8};S5+5!AX zKpjKKJwWl#ymGV(EQeewoftqX>T>DWsLwp7r5?j+&mTC`oRGDvSOA=9Dy0*i&3$wQ zkz@2-jOvn|o1dMNS9R~w04;*lhNa!J^&9dz8-}RT;VUR9cZ+8dP-S2!;Zl0)#8tigWWq!U$ZNnWTb4@qq}z(5e_eB5ci` zE3K)ei!jZ&b}yF#XGVdG<07_g{{FM)!Rpo}l=H)@bq4+?AGovD4k)$kN z{KiJ)OX@WIZu6$4|Jej~V=y2e%}l%Hk5@g|vl3a4&yaF%@RC!&JqDF&R>Zk@!B~%* zBwhCIyR+xbUqmP7!D>A3QzM2oY|tQFHlN{rquk4-Kq*n+pXUso`HiRVl?7GycWIqT zXGlv{jbU1f$I7REELx<|9t9h_ZTS`??Z!+Pr_D$Md3(!sZ655|tMZJ~o+0Jj;3cO3 zh>;GvlL4_M4_bLbbUHHm3?*$NkhF8&nLBqO9w`l59(z0^pMJ7IgNCY4O9#YP9)h_g zDQ^l?am0dtQ3^GHJy{U8GzFCG0z6U+8%1I4_^YN#l|o`}6jh!A{#-k;i&f~>-HKen z_z4NpmSUR)kcA6TTR;;w;HXQMuLXRNG^;aJTB!*MmI#4Nnq1qsrM01tbzPCU4#rOi z6kBDH+pJ^9j={3^oSYmqpr6*b3>%IiFbiDl<;uoxhLm*y8$z5>k+W(_+HD)GmW!;t zTAcQ=K4?iV=U5-~u^v4KAAS5(TAt~J=l^F$!vcw#*p5ADU>SNuoyK*8H zh|F!)FTeZ(lJ>-j6Q?I>)sj3VN*VXqr_~p?1SM|{%8U=(ghGe{MO$LFrG-#YPA99} zW&vE`Xdf1{ry*&1uNj(F;amjIZ`8qc5qmn-6AsyPvM?-G2 zPTT&(1flVq0E0S)ddy=&u6A7cIH){IGN?h4XkcO@dkF)9kV<-g@iJ_urofk`$yajQzYYYGi|k z4ZT{hD&z6#n7OKW^|-}%DR8zZASPl|g1K{xQdbgF>)&1V_-s+%jn}2XX;FaB=`qKQ z9DQ0Bv8P22+D(;(Sy!TJvZQ%wR;+!Y^I+}Do`u1dpxt3ssij(3BEqFTgjbrV$g>sYgaKp5hKt(DQr{J% z9PMY$Y|mL~kkl4_m-W>m7m+Hmwf^A$7VMaW-`z`5fXqv>x<}?|kU;g>tej_8xR*0$Y<+Q?NM~qJdSuh%Flasa{Q)iY$afQjVn~_RG z0Z@rYCmBMue#2&!rfg@sARuYEFROPbcdYV}p;{NWXS*J%RQ%X+bs!eIwH>UbMcSpR z6orZ^EXr71GmGm6c+rnJjZq=iC6`Nq(xd?NWY<`0k{jqzpSSvY0J315YCjXq$qo80 zqtp=E4U*T8M{z36T!eC5gj-O9_3}K^5XEz$+`-yVuHcH7cQgwd*MA51o>@>J_T={V zqv=gqn=4*n$~H$NT6r0dqtmJn;9^(7ZQ16{P0gi%OMyZupyon(aIBUt$K(oP`2HyxD=?86ktH&Hsv5>>Bp3`EMvJl>vn3ZB!jqx zu5c8Pbpi-iIE)WQWAnKCYV>JkGL)+ySGdZn92qjEFZveLa!V=;+CQ!Q2=LGSbYkeTDI*J)d;Kh9GuBQ!|N)3gK;)E!f55*InU@2bU1p_?2;;xU1T`C_6 z#V)Np0w@8o1Xo%YK!zuKyBXVn?8@5GBDot6tmx4bqF}jf+ey4?pzs9)?eFTdCS`|9 zIH3TWndpF8zg0e}E9(x_y6jR06!2Xzuyb->Wqexp(X6&f4ay)7p^$_JmI}!3Q1&DW z*{qxgY1L4zy9_=Q>N9ln2!<%%1q1aPT53fBd~%n?8%As>5Aw`)BPT|iXm zoP(x}cr;cC=tq2z%Uvy?D^Z=hMW^P`f?7F8@6virzTLQC;fi%>hkr{y@@v|mUpu$I zvHySp!E9Dup;^SuT)9%f-K-ooD_^sERYGEo^UuE^Au%x`BGT)1?CvzEf3Kpes7cey zr%s(bZTjB;y7-_&Ie8gd40;nq^3=6=#xR&9j6V3n64j}0%bx0UQSDg z_?Yu2>@8Fw7l2z{GEgQtauabW;8LJ?3g8(&`T4YY1$tK7&4}q(Eh=y^TRbIQAHh>V zF9s-bDS-0jo-tq>qVNgDC?PiRdRkg7Acm^otHkx`QlJP5z~n^S#0{^dR)@k6$-_Y8 z%N_7>P!lC7idw=i&#$oeJr;wz(vV$7bSY3O6o3T)eeU8D24u2RfCU4~KH55Ihdb8e zOK=|Gkr|9AQJrV`=oRqPp>_4+QsGxO5~je`ALZCBI2Ht+{TB?tEGT=sMt3=_$v1k{ zohZTd4TpR-eY4^HrE6e>_Q>I1j{NpZmpj@G8uVzf~S=Q7!7u2@LX+c4Tr2>+(K1*p454)+E$RK;w;vW2vGO|)E^noFt zPnUfd7Z-QVIp@G{L_`D(w+2&Fx)R|~>DMMsm^yX3Hw^y4h>vI5v?|8}tJ9k&h|9297$&zKP#6Vt6*xAV?BuN+y{P5RKGL-Xg)KXLM8|9<@f zQlpG(>ZS3o2bf%8F4DtJ?5qHwML0WG@`Y^e0%&6+axcB~(k4xsTyn`J)v8s4li-W3 zDYLi$+KCeYw9{Z#22HN;PM+usKHR-~cW6jXxL3JS0IC2g0;;1h z4lm=H3X}~vZYvqj-@7- zB&)`0Wif$c1yHeV9OMZyaq85mVtu+E&MF0<3ZNpOItojrJZnnEa^ScvUX~YN!2on~ zyDp$J2+&t$wn!CQQ~otbXjKC{^x2lQJlIpxoh!_izVEzv6~XIr*YV2nX!VKP!SXeh84<;fWc&@D)%T0>p8PD?#o*@0xvV(e#{Yp%{BuClDD67gjv_ROI5soG*| z$|Q*1CKWfG!IMFZJ}u%_9}7}4?N|jXvu50xQ0nyCTYl7O9hlPbTGwg&C#h#CC%EY> zH+B{A3WoK|EZ@OorYiV3b4d%ZR5oJ~vp$9>-f>0_8VH`IWyT7D68iM@k=E$f`__?f zb^1=A=hPYPVeA;M_A>y)@Y;UHjG^K*^i%R(s2}`pf8-dzVc^L1>qoZc7=6N)D+xI9 zVCt*PvKc5d%lhhx$Gll8Ud65sFhbFW$)R`(fe+b(sVTz`2v`=^_`{PG_0l{;&IZK* z5^Zj7Ze}@NTfX1g;`X-Z`Q+eVgW7!GFO{y(Z~w6Wlr6`z^45?1{(5qGtNXjJOxcoI zgrDbHWoDk&eI7@U`d0mslm3McFF(_w#WSC$rq1iW)i?ffq<~5qDxzp^D9f7i$q9k9 z!z^)y0b3Z?Ax?%vrF#~7oPaYF3>AgmDFiUU17owtj~_pJ^r+^MVrwd(dMvml)areY zX-h~NyLRo`Zq4mYZ}o0^apT4vyTDtEr~h&t z<+Enp77(OpKSq4@de61C@7hHH)Cep0Cm=~${$b0H06OJh+V3*UrcY*Y8&%jp)jx|c z85tb|ojCy8tonos!BYrW#`=&wn3}TvV0hAg`Hyd(7PRM}6)i_&P-a?}e?qnDG}yRo z)o%DutLMBQe_Zi&3toWO;^`GX>g$E;RCivNuNXhOXLoD2tUX5|aSFvG2H*J0kpz-- zFsx8!xhyg>0LGCiG?N0SQp};P-gZo~lE*KDIgBWTix{d6u~P^F5?O&cM81v-p-JB{Dsb;MP8^^r0%E*Y>;sxoa`R&)$#29w3 zTDp-73BMb6?m=1Y7k}B7IbdqK87_<`%Ce@4=1ZQ$h-YoJU4u}CEAW{E{?UZkDFhm{ zW|b9NQ_f;DTD8DceOma7j(n&OUZJBN=jUJgz&}2@ec5e|>ALMD>-Os|&A;}!Pe%OM zhn*TX?mlM^7hD0Go{RKz=>z>5yu@)1-`Su!hXu#3^C8ymb87m=UjaZHO#5AC*(@qE zJ139cI%ycLumuF#XXF)r=74`>A#)0WDJ)9Vk@3Z?U{;L8#!;69+!8he6=IzBlKbD= zs;>9$zXYk{;{>nrfj;qb5q|RXuX$EteDB$3by#-9&;gPxGeiVVsMw0DD3TV?spP9= zFAwlNbHE>|p?C^GK$;g@Q_j984*95+0rG`G%Xk$7a!EHUFK2FblP3yPWJ!xXQ9gak z@B?p+fK!0!?!fjOx8a_~Ik;*nG?Z9fqh9 z0@i}X_-Scr7him_EA7gc0#F4|5l|h4aYGr`RPan$xqbTgX^X8X2SIp~D8ehtT7Ji! zD>kfO_vVMI4*z=a@UQyWBDCiN}N4Zf3@e8L)jumo8lr5)vvm^(%PkGBPq2 zE?fxft@{|9Z7O_`i_S+mQ>y!6t` z&Hfd>zM z+W*0Cuda7I@x-uTByGtH;)bXa6mZY-foeYUvwRS=W5$d{(1Mf=1Jur2fS|RfF-nKF zL}sWe!GUfe&yWIvCi)k9o`Aimz*tpKY^XELX$1?eC-PCh*0vGbhA8<^(gB|eR!%oN z6^;V_6a9lZPhdw%Gd^)ZuPO*8RD~=2nP!M_OGCAR<{slm{%@P9&NN9kyh{NXs!=<& z!D$1y)1u#b0)Z9`*rC*_f~t860q>70lZ=g}dCXWczruyAi?hi7=E}R-g$AA>NU|?BaSqTK5 z{6bYeI{B1HiCG%2uf?HJh>0Q^PG_c~HP_qJRC4UGa1u%uApJ1E4mKPV!%= zXM`$`Qj~d`_G~D>^|Y=9s?2MnN+jqv(LdmM0)7hyP@y&vTWL=r2$Bg5YPI0t0T);kNi<5!P|XVF&w+d4UQpQ zUqMhHz(jw&VxZ6k1Lhh7;8ybI6Do+LL*9#YVwIQ`gOh!hkJyi0yoP*Dy9KCJ6yRA; zgj-oaU@wB#1$ALtsp{MZ`Z;Rq!(RuQH>hsz#>*S722#t*u2AdML5wr0~W6% zX=xibZu9_x%{X>6 zBRlI9Dbu_N*M@0Hm&=C|$GLeuV+xEIK2n1g#%^G%89PdtA~Ebo?W7Ka_fL|}iWg`0 zWw<~j75)@k0PVz=M~8)lhet$)dBZ$mVY-mEqKm*)OWWzQ{sbq#<10y6xZt%><3?At zZ8>_>bKWM68^1Pj;+0Km2QN5X#GWx%SD^?@JMhvzR@K~F<>akj{n^#c8=Ita zky(X+RW?P1%~NOf8A+RyD>2E70Ro>e;G3xsJYj%wXB32ILWql=7NJ+@IQB6nyz0PR zy8L5J$Xq?g&VkZn3pko;;OXz62us~98*CwJ>0iD+hz#DA7J?e#L6Kced z8Z|1SAQ#udzxcxQX=&*{A3XTk7ypfn2pc$XV5d%<=y;qwG#$f1U9w~^c=fsR@ta$s zl2PE}PgeOFqOfb5GSbl#bxbYhu5X|&uKx_=LgplwERSd1H}q+b9yxH~g%{SXTlazs zE{u(d4P-zztWSmH=%bH5nmTnV$k@n&ym08+LNhjT!mAmXnMX5^re|cl{r1~&iRXgF z7~o2vg;s!cYIkzuS+C+smB6Uculj)24`+cKI?M_LT7Yv;YmVc99|-)>T@~FF1)zO? zPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!~g&e!~vBn4jTXf1tCd9K~#8N?V2%9 zB3l&4|GY1tUtk*_xCx=Muz(s3&`2RIRI*ygG!!ITno!Vyg|1Z6vc=L2kNiERJ~x3&<@}Tx$+znR&)MNO4I#)>@p^b+T#6cxunG?elqaJKyE;2{NTUom1)xK=@I=z1E z3ZrHSO4|7|{Z8J_T)P=$v=S~&g^*xM+Aenacgt`Dt3u|lHR`H4LH@pwq$#}^VPWx;&r8mf6wTg7!IfvA(e za9b9G#~#9|1ck~<74aEmu4SY;3U~{RY8Oht@a17t${4OlBv4A^LH%61j#S^sfGn&u zYA!RJ)A|Pf?GU!q&}h^|NJ&pN&X%lY zU@4&_s9rovk|6v!)XF^)X#n+9O&1nj47J`xR|yzm2`K1x6$EDVgO2Mnfgr-Y$EEaL zrl1xT7UNP}6!_C3^;sDtDBcURTf_rrq46k_e2M5OikeEfstr}Mh}Fsemk~g4s-C`N zEgSp^q{2Ozm5MXVB!1*NLMTEk;cyxKPHrl5Ixw|9`Wm{SZFkqYU>U+|lKV!6oarxc(R00pURvtv%YQ#L(BslGP*LxO zux&=~Ro05(;HZkTaWiH0r@l*X#BH*lYi6xaCYN-+saP@Gi|Z{&06S*sIt0F(cahe4 zy+Mzn{7{5;XwK2FJg(T0pLu2Ky4$FCQ z&6(p1@NGpNOT72RH3v1IeHG8SJ_!7X;wFR+Jplm&7xqxFnt-#U=3!DK3d;NO4I# a0{9Px*hDk(0RA@u(T5n8KR}_CPF)Cl|kR>y*bQFrCA55qLmq181)&jc3@S*EA6`REf z#c1jhR47)1{4qf?f|1t6CB(;H?ym_8{Jl0faT3A>Z92{I+S~Atu z#qBkvv(?tt*3i%(lgXYud6McePTv03uU`)hud}n$VzI=rzLPvF$!B{0{CQ?(=3A92 z2qO3kUW4z#fTnb|V7tuQDxzmU)wA_>CM;2#N||g0Z91JTD7MX&Ro6_fP9`(ERtA1O zD3@6#)Ai0Ifm~X)z;T(Gj_A;68@6DgfF=&~>ThM>dYBcL`CTGr7dI98JJVa&R4{nI zt2`WzzyJxc3x^?(tRlWd!o!G=#jqnN(Tno16FeTwQr>lcu%M}qGa}gJ$|t~UDV?y` zX!LcmM?Glk^c5F_w$kKsnUt%~fJ6pzR_UV#W&j>;7d)*#!%;f+0Mk7ds`XGgJ4w}G zAc(W}gBhFY+HMcN8Yr-sZ0P9I!-NWxox@Y&LLc39j;Sf4m@upd?i8?EglNWA$!=+3 zfH72Jn({-fl%*$bcLMHBV!}sBt1XJ!x~rPHI*K|I-P5S9u15N#hR|=b8C4$=UIp?@ z0ES)1-3?tvfKaG-9EXW$qxqPgh}{?)xm>dorz1zZyXenAOoU;vXty?p_=|~nu17C( z@{7nyc~=*k*`WVcL0-<-H(6O#c1$^Aui_26@_!#Wl+W@-wFnXDvRh)xch=nU2NUd0 zco6R0yBC)am|0D>s9aNny0M2wh1GGSB9`3a+iHi81fkH7(V-XSi#BWSG~o4+xB6=hs{ zfAJoYitb538~SUfcHrwBS9I+L4mR0c0aMq?rcZ7UZ9hp{mYcu59-8bnUD46~0D@)H zQac(wT3TLSdc-pqn*907GS~w{Q@6nal1*|AI~MGYuNfUrPftfYhH)GSyq3}l5mvhk za-nHdHwe04adic0hybB{f%#bGV8BN?^rW^ljULaP8N!9rMGr&rwUii!Wr2PhifOo{ zV(<@JJ-l(GoFYfFti?~{lB(^vW0r9@A7C!QrOTr6wq0^_IK}<}nEaGh834m5_Mf%* z52&=xsKYd+?-*Ny-Uvq{e`)5 z-YuJWE4YWta|;>fAIAlM)7Pc2n7yxmw8?+LG0O**mK(f*IW&{4P?3&@mjV12E2B+? zg-xUV`?Ase_{sbWx!gq*g)rQf<^o=o0)zisz=S9W9ZxZN=@UFU<~Y`g1lk(ZS=^V4 z$~?6PdT5-EvZC5IZ03?Bj=?H|`xf@6^pnpA$J}T%H}0HJDfrovD^wHCaUAn-*lo39 z7>oM|*4n@4&&b1bebzn>JvLaVM4@$C4>A{h7gg;ixFs%(C~!E3bA4ZaXjA_B(_X%9 zY^m==J5^amrsrn;gmE?UDz%j`NuEXxX>@vOh%~Lp40?jlc6&Q~Y%9c%{+I}*%RzB% zRU9L<7%7?(tBt8ugH(3p*{mfou_*aiKjFMV5+Kj8QjQ zbuDB2Vb2!#DKvm3Fp}3%d5R=ABBb8tZa62X2~JV{6=W6kuqEIH7LqJLPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!~g&e!U4l^sht1-3@=GUK~#8N?VUku z8%Y+&pXLiByXOpHJyIqJpOTP+ybOWC3AQdav+-%cCI@*Om{=Pytl7XYtQQ{)K~5kZ z`>~5=Vy&qIsRjRJ; zxc++Yb-ivW_W1GRCNNCEd?vOG6DTY!CKOm$OenChm{4G0F`>Z1VnQLdx3|a34NTy$ z#bQFiW3dW_0!&X&!*9R+CR&abbpA~ExHaO|&9M~LlP6C^jR}M02JI>)yS4 zqJ?pDkhxu4T=XR@jcC!bN&LGzSkK&cTrNMzpa7F%BD|?xhSXS%BOEiAs`z zQnLw7tL(sh93VcwBkFc3WdKsiggxow(CWWKoqa%}TEgR`M4ix{t7-hP1;{}bRt{)n zPm%#Kl>O4h6@CfnNr0dKV=HU>&LSiyv4M~l$=YTid%1lwE+glJ`nOK}vMpMxEeq}4 zwjnF)y8bHG`;CB zy|Dj?;<`vcOUXLyEF|p)f$m)O;}da6Po<#x>R>i&Z*Nb$a$;)tL)RGVi@60zF5ZD{ zoWPaZGTgW#-cWg3gD-#Fvrc61{tRqhRYjtLf;smKEKolxFDBvVZ&93R{T606p(uCS zF2d$a8b(e1MYo`6ysAQKDvi^mvH{~ATwB>TaX8s!C~Q<9o3Q&*vAyFMtfN36yMjG1 zew*Hcsk&|XNL161_i5Qo7q$K47STaOMfeEbMju7qV@V5yV?KTQ3;c?2jiynMLRs6z8Z6%!?byEsx#YcvFc+v# zMo|d2(`=StI6LhOk`-%Zqqt2JQ+0e6<$%QJA)l+FKohVvmA6ENJ411+0+npS7D^%u zl}Z*89pXRZ_(UQJsp_jh;w&kqgNlmqAuOEq8_O71%|IIG`oLUVh8JXgS|x|lMMiJH za;=CqY8#3*MX09!heiUDE+5*qRvO}Im`YVadMC0dPU0}XBSa#El1k;EzBBK#mnDfc%47|S*&@^?uh^Ec zV}F5<7^@qWJJ~9>KlK8)Rk8ze2bZe}hZ#s>S zRdVBW0TD6$39B18`RAm&x(aY#UM;HaRxmyaxfjb&yDHnTC`%GUS0nyB3p>sUtwT83Ck z`gfxOnp?{K^_r%Ue_KvM_dt9;54r02(OP*QuR<>0o2VrDeBKg6FQpgwwwVFs`t|GL z)&3aR`>`e_CPa-1i-8js{fC+GA*)B0OEM6`diU;~s4-#i=+UEA5HIN}tfP(;)~Qpc zL<a zYI;5+pLm=~XEZm^De%pHz48A2d-(9-1AP4W5k7tTWaSyvd7v2U*c3u%vFf>$-6xep zyQHJk%s4*%RL|hQohfCYnWCL6>(RZx8*BeerkPUCOM9-y@og)6m_}AwEcNg1E&~%| z@y&jR3kUB4zS%EWSXf2|^Jc%GU}5=Uux|DX0v48lZ}tlwZB&0$3(@haP!#V~lMzHkF_nEyS9WwP-BqFE*t+qFXkHa!(Ka60LhN^e)SA%4 zoS`5fu^N|9!hx3?dSuXU_6rjhmIDz~AC=paY2{r!r5!a`SWGCeupIC=&IH9^2+K;P zv{$W731UYQ+O&_OU71uXGca6H0%e&D{@T!P?T6ygC84$FWG9;o zVw^^C%NC63xMDv}LLWjQ+%c9rsY}HhPf-lhj$MrVSTU1qbH`Hdb~`T8C85pdq{dQ8 zyk$DB6}xc~I`l$t5;|Nsc<&KTLI(#6%K;~$gMx*{gaQl80Vkn@VnBpN>qRnwYZi|4 zNoWb~Yj}D_{{*wSU5|Tw^<4GeS=t((lhA#pTxcbB@!HS1xY)w`vnTfB^!lGl(fwQr zVQSZ*8(*JtN;^D@1yGaC!xL2uSJ6O=P|re-L$^veQj+`w0b$V7Hhwv`tB;gv%~!f*EV0^u`_rMzZdlg;`i@5 za2AVxe)xxz0Dry@@KUr%&Rzic>38Txx5evo;tk0geD68HgFgYB7EQY^0KR!9Dh6(& zSiTakiTu9-?&4NE4v7not2@6az!u9Os%yC2wW}U7IH7qI4)GEvnc@jqzDH5*e!@3U zc)O3-9}DxnGS=PyVS{O$U-2AplR<$+5x*Ae*b0k2St?|KuAwl#5yJ6+6ZsXs zzfF_w1}FU59{~PI76m8%rt&lTZ%AwiS)~Vf3=8c4pqooGj6-arh)<*NqKNzNYGNQP z7K!Q~xb5V!&XNVe3Dvou^m+UTT~*Ppwn5=s!VQV>Cb~C3tCPmfcV7cMCU>WZ^am6g z#ZN8qsVL(RFHvMs6&718<&!mv*5))$I!){|ifeGve!?LAta!g8s{%(|#SsvD~r`k@f?&SUx{lEijP&FHY#F6a+%ClG|FH9YO)(b{gH6 zPx#32;bRa{vGi!~g&e!vWfAnA`vW7U)StK~#8N?OlCz6~}d-Cn154T`FNafnv}X zGC~2|2nE|XHUbP(U}6Y1PR_B9a7iV=H9Qn(tAsc-rltuW79utX8y5lzHJD?tjRYt_ zjz4GvvIt1X7(Gd-J}EJ7K!4OV@TB*8XJ$WUcW3v_`_%5!?%DIszTG=_X6~JPXZE*u z?|QnsyOFy&M*IhYX!FF(>mcZc9%~^LRWlWp6uX!C>%~-`|S{{QCI$o7evI+uENv zOkv+%zV+#n{0ZX<^K!D&)BA_O?_QLTLjBrVL06AWv@)^#p>+y+a>SN=j;4 zI`lf^V6dse0ti7j`h&LUSfrhWUWeTJT45bqXUy2zVQ6M=0QrS>-;NpSdCQN85exDR zlkC{iyo|{^d|{U2^$vO+a?>bmMn*=4w?Qe>;LXSYX`09snVk<=rvO0@?oW{H^*QgXTPIEg2DwX)9b1wc zl2c3xz)Sdh@qTi}?F}w1C7h?MScz7?(O}AuRy6LefySL}1N1ti6m>3f^z3=)?W5R5 zHPloU&YOqJQ%l0XUN5#myfJpjQ~@;7b>immUxg6Nl^pA6l+yUT>Kw7c@P6dv?nMum z;NzjwzB}%dOP%Zx{_Q&liog3{mNc$mbOxBKy}@8F{hL)VE{Nv|f5|WZfw1xi+S<<) z-jj9Y#jg)4+vdY(+P9Q_y<~SgzS_C1`5!Kdx^X%0dXRqG+AJpr^ z2RF{%o|Vyf;UqmK`_XgFxl^ZpA^io)9o}KL-9PNM@4wu7_UiD_J1QF|;lKA!+CS!} zKaj~qp;yke z9{&ZVy0eY%9P0T_=Iu~X^QlJKh7oChEggSxS3z?#)T=g+d%W%3b*0B}tfPTmhihR# zKYcc+qo}HImB9^8oHvttMj%10zR$GGJAAEZ=abaJimFbZ{AB4_=i_Kv+}|(@0)Pu(N`ZH4ouq59XZ|ciwTSG%UnF?M;(9o z(F@PKdPU-YZ1mq^sHY~4hJ%^~b<;<_ekVR);?41iZFXbkivE89rJs|GrAs|R+hFh4#B-0E!m zkAAR{r-YjohTmpLXWu5#rE0 z@aK|2uS3e5x2(s5orv3P1OEorrUM{zM-`q1XR0fcJ9A#^VXv$&J`E|Nox}XSpb6MZ zM$i4#`48~%mG=)`&dnW+PfGjX;RTl)j$WaEKd5|@klpG$diH!lMp5q9&aXbso*CAA z<=2%bJ89X5^KTd5ITRnfxjeY=)Nj7F={fn5!XMkOA2ZxqGjYS6Z@zo~FtuvJr2XWY z)xjx`(k5N$Xw4d#t+Xw&1L$=~g@pr$pROv(s3IH}?(bfb+p!gC&Qw=7t(#GlkwNPO zOClZ)Ype?9Hp!XlX}0lO2v&8o2e&@Ea(DA1LrJYqKmGn>dQ#fHHDkgLH@9>xdSEz0 zf0cLT`;-h#SpUcD9+bUz%?{~I@!#w{lnn*%3BLE#(D!7T;$aPMO?n1V;`McFo?4ml z{?A7#%mc83L0A(XzSD&oyX1l+p8s&s$Zcy@?Eyl3JbwgfQfJHY#=FYsapW8HI%HvC zv>q&AG1``p_5HUiU;EaB-yE8IZ^+hPJ20=dYgtw0l#rD1B{*HRZclzi&EF1*u9`n} zTsrd~XFuHZcNT2=8tE`;T94!sbzVsS_m0OmOzA9JxAAynE+{soPW|J$s*6*8Sat{F zt|Q|Ny$-2yVG9e%DRu-8uc-aTgmGYD_&ze(ABIU{3kFj>2rTTv$ITzST;VVa8$Niz z#d8<_@2ju;@K_JF0IcAXddHpYf&N0Tq3_ls-;Nemvvp_e%+-0EmMvLm(P%rybb!JR z4Ow3Mv8Q4}u)qC2er>ZO6*jBW3)*s?bwt-<7@c)F)z%8L=_*lNThBj<8Y2K@qLZwq zbRKFfN6$yOq9+_hdTmpM!B|s-vZ)+P1op=aQZ6V)W!oZcBgD~dY_H8Y*)f9bXX~nA4x=pw!A6t31I#rO(3o4D@4d zDbN#Cho+&NCc;PIOsbPkMyE5F!(OQJTJW&Dg-y*tCy0nIcA%~d?I_Mek6cF$L|azd zb<{;Kw@ccB!<Cz;tRX&->R`)$l=B4frB9&W9aqWloL7#h zEmnfL6k$*M{514CVyU35e?e$|BTuwLPzfl8jE@|7#sU@63ZdM zR801+B5W(**1#pg-(A;H*#wmbC=yOirS^0U{kRE%noBw*zdAh!Wfe(k28-E4r!lf^ zk`)hXE}=uA9F-AE#T(eM&H)Ivn;}as3mby6t|Om-6tLqOjGoOv+lk`>4eiR%ui{R0 zV$Vh`gWae)@%|R-x=v<3sx2~m(a93jJfESFnv{vMx;5r5&{91~GFTW$c4{8(SV68{ zBG?P%$QA$#3PJ{px)K=mm$CmWXU`CKrz{s?YNQj=rfF!6Y?OQvlj%*>7DEJ%6v;jr z`)950BT!f}SlD~8WS@qe&z>T*M)!1-%=y#lbOOpFWH4iO9hc@OSeHM+xowaJ3Q`N=36k7+!48g8neD)rMz1gt3&YF$G=xLgarA^X_7pEj zQc~baUIc?Bnj)~Q7eqR^YyD`dj<77VzK@UtDMpcw(93#1>V>^GMXwkVIF@K_(GF7- zk@msjcSWrS<|h%#xY9IELm&T>2WGn{YZ0+9WG->q%b zruSjdIF{rt%P5TR{6asp?o$EzHb3O?RMhun8|o`Oo`*Jz+;GIAl~xu86IdTOM1HU8 zD|j;TZ7%bUtqazcBBD6D$%I&tWYg)B&q}J6!-rnsl#(=6S{tkz>n%%=%0jqAJ9tbY z!&%!oimYGnMb)cH@s+k#P#oE)@SxBvM9eDHa0DbUthR6dE8D_vEM zyz3KU=T;gSG-!|=o*|A}{!y^4S`rPbw)%tHCoPyw!PnASFf+2{7h=-3)>Gwk>6y~G z<)>QP$f1@MMMZ$1Las1NYul({fM?yepzWjOY8WY)mBL5gTTpHp+NGsNqwXCm zkBaz8#|}O(hCQsW$lteM7f2qIv0z_5ox}3Ir^epPDnqGo)bd@`EwemQ8KxZyA7k4< zlc!F332R^sN7Ub%jfxm{j;fTNNO1-n18fjga4m~T=oScc(> zmXH3j!k2QcmMR1vY%(fh+hGS=81_Q@4omX}o`e`}F)k7ol9G?)NUpvDFHM+5o++OY zV#og3bX3GvW%lEyLps<6N4c~Vloo{L2u8SMMQlIt**ZXW@g`Bo#-@;!*I^2Yp?s)Y z+|aV74WJe#*RO3`gg{M5q{DvdLP#8%!lI0coFWvjTnzNhj*BR`g;bb>T_TJ%Y0=Q< zNXM*}YP`}(#O7>;(K{=QboAi{NJmh&$d6-doUX7qipHL-ovtt!{I%P-GDTx?Lsjrs zYPLBSU&hGbLB#` z2yLIGJ~Oife~qm(;Jd7WI|KZ^iS7tf>JgJ$ps`$@T!{1s@YYF>oc z7T|BFW_wWlU81yD;NpZ9?K6RBr4sa+8Qyr{uL)Z%T&S5pp@bvA-!#vcp>!Jcb#pXU z1OAHCQ(qxLBG~*Wv~B=@^?cyz=k|l-QLQERl=Gav{9X z5G_MN7KW!(wf}E)BTM5uu{Xr>!7o)FhweisE%_|3xwL*pSLv&AORGnX`B1OnMp=m8nc^up3k1g7@P%z_>siCO6Ld z&?Gg;Oo>wB01lc`6zkvxI2q%fm@}N3q6mz#EE*E&5H=d{h(W!Pr67My2Zc2==A{M` zLW6nfV|dM>UKI|Oc{9}|3fL79rm({ByR)g}g)w#BO2F%5zReZ#LIY1(Y;dg}{M7-n z9MsqV^%~${Zgma;c16@39~zBIRG?Sp@jegy)dR8y)cAE{oG%;G%Y*<+y^@!YF#~o* z+-OLb7$1>G$JJioZ|tIaW1=u|ml&NA;!`^A;4e&q35@C$5t6eN7Wc^C#76b@2Zf3B zF{x;rZecF?YuDk(#m$+>Umiv*pEJ#(!3BufQVs@x`&5yd91Chom>XpyXp7ei5^ii7 z!*o#}0icHus>EyhueVl(!fJk~|nn zL0yT8>J>2wA%%e$bi6=?ptJa^%{qXW+d%_H%(%2Ll02}E(3#+`MmpppQFs#>6mg$|n=U;+J9(1LoP~e26 zDsaJH5yaJ!#);ssanT`SC<&l(!C$_RVFV^*i4lQ{;~e?xT=17JF_dMY5-J#hjNKy9 zNI+;=pOz*C+bGLI#p_2?cPz@f;4e>#9>1T)a*)X^{;`OfHdy6quuA)&0=pqID07oM zkX*qo_#2T`ndNG*Nc%vOjA~wJ=2orG=bIdnzp1HUTPd&;$ip^otY{;DO;ZC)5o{t! z9@w0}PLBM&*^7FD0odsvjXxr#GX`UDE*Jb|OM&`Ux6%}{YEoJA48NKRZ5{b5wCD(U z@a#o5=vrbCDMPLXF|R6TumR-al$4>|G{disqYgxNAo!b_l1i6_RAFFYeQ_&o49+!I z9R!uO(2VvVef71MFp@m5+0z{d{sPoXWusV_5x*B3_u*4i6318p5%R;mR}!Cy|22NMw4dEhUFrdUrnfNb$l zpBwpWAyuJ82N1#ge6V}SlT2!7J!FfA!o+#H;4fcRu$-sMdb+MxrZjQM#*NYif8!=v zmvKxaAmkEbQd4}GL;?QVFQtQsh@7A>^8j96U0p?(=TOI9~@u^(KMBB-BEahMdza z-sjyoP_NyidXqz8axbjkN#K3n%@pdjYgBI%D2yM9OFPB`fAO+W{=Azm$%7l!D>5D; zqS0cB#!y@Z|K&vf%2{r>`F#60s#heY)s6hMQB-eC6y}5*XLB=+3jDQSN(T{tp$S@7489l|WhgRP@_ zMHFU78cx&{FUchH(z7m`rIglJD8p|8^ZE9p8)r_gY|TjHXNNG4^R;O{UlHrE`=Wz9 z_|kt}%^dt?yR6Ufn;cQS_E#7I;Axz^Zo_u6@34Pm6utO`qh!1%E}xNQ5-TSDg7d ze<__?oQaU(aVf^e3Qol5jzapP^r=;L^4oCn1002ovPDHLkV1jQkj-mhn diff --git a/docs/source/gui_tool/user_guide/media/image58.png b/docs/source/gui_tool/user_guide/media/image58.png index 3498075b6b6e9d41dae77dad190df7e6f8d0c09b..46d92a0b726f60ea9b4574e4b0f2b00f27b183d5 100644 GIT binary patch literal 37192 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 ASO5S3 literal 28307 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 PqdKR4Uajn`-HrbOdx0h= diff --git a/docs/source/gui_tool/user_guide/media/image85.png b/docs/source/gui_tool/user_guide/media/image85.png index f5526fde945a97e070f3fc3a6ec5548ff9afef96..8eaa01b5dbb0ac22d4c0b5f5d15a39d30ffcf4f2 100644 GIT binary patch literal 29459 zcmaHSWmsEXm~F7qLa}1SDHOL-JjGp#7q=D)#VL^B*5Y2IxVyV+Tig@0cyW^89`vT) zH*@dIGxOXZJmH*>%{eD~uf5*&t`)AKrhtb-i30!t@RSr~wEzILHUI#f)X?oGEK??YBaq>9hb68@!Ul=M1IbpY2YXd0%S96&)+-=vDG=vxEq7en|!y zyD?}o+t>)rpIcdMWV+tCP3&hKWM!$3p56LaFt<6muT~$lK8WwEbS*dS;5Ju3LtQeV zb|OR!nLTjM{P%#)@y+b)IO5N1_0=16PZBCre18mP0h)Uu=e1p_It1EvCF%n71UsWM0F!0 zs?L*P4zF^;d6t1h=^^RMU>?7?`&GI-{|4^~$Lr z48}AB*2{E7(G*+RdP=Q5ynE-5deXBgcL)H0*~escGiLsWt9t$px4-h}DfQ~)n*Hv) zyzb8VZ14Q*Dp{;=!5o&0>7Qt9iZGg@&LVKR3toGLC9c@cc-1W9O&F9e4j` zdeM|6c$mWw+lXV3jD17jYsMskA#yTZ?HJ^<57`T6hGOwq;0{g-2dS}-Cm#yXw?w;N zF>+hRZN=7ZxWrHm3E!M}^3_frE$>7aRfs$J{YH0l@IKXhNg#h)8b`Dt`f!Z~2x$D# zv_4$BX+K=I`|FP|%zOS2R{5Ayed^`8miyN^;^huOCC7Znn2?ZgW?@sujPQ`_Xz`L7 zUq8+%>re5_-mdJ~bx}6B>|$09^^zJ^J}DbYQz;eN5&8h@8F9!96^x-5@D%8d^Yy8T z)x+~0hundM0d}AeXt4M}b#hSi@T1l_bgP|}$+3!5NiS2KUo`D(^HYW5eNc{Q%c=NJ z@PSV|gLha6(5k}K<2afO07#1}!!g;BK(d?#lA9Gg+-YY|zF5X;GMeU^-ha zQ}pt)j5x3LzWUS2`%!AH&1rmQ`)mS+GFOTP3G2e`Ulb&h-<{5S^iA_TlTvAo9^alc zP3S^t#a*(JeB^|s0_!>1m025qm$n!b#Cv^Ho)aDmCgThX5LLV_C8~30dUSazuIj>3 z*xz14%xDMz*hRsr_?J_*H0mIC>rVbshc{!FFfJiI6EKtSb@sh>+r@$%Sn;_5ke`1b zDZ~;w7{BR2nSE007i4LeHdl@nw9oN1N_USTO;h)6jJy`GBdbP3?enh{EzUiN4jrSygWef975wW^PHPFU91ADf~+z!F0#0&J_STH4N`NkgR%+M zDYsfeJ7*(ASY`Y2rF)d@i}0K7GJNXv@ngxAR#n`lKZCia_*pU*hFc-d&>*FYUAG)|oSM@q50$BUACBi-r&~ zF;)0~0a8bDtBO(VOLM+*nB*^7j@G@NEY{`pC4h}Jyl-b&j4yMt*KO6QY!UgulC68a zavQY8^EAevS!M%xcsHHyZbEr@&J+r~UG4w?G-R3_7Io=e1e<;j@fJc*=$Jag@^G56 za#lPUp6cIv&C#igbFzN<%3*9w>w`B{6G387i(>+AgMXuJ?jt||^PgEB{CDoJaID4a zLPM4gPf>6*8USgeK1mDS%1SJ>6P4ks_c~(-0D8YER;i$nFo)s`0)@Yld;fu`guwrT zp_03(gI>zVG=E_j<6jt__6Eg9-C}n>`TJIAd&oU0=iiSU{|D^;Zy)*Jko&*=pN4&; zxz%}G540GNo|ZOghZ;}P7j}49?7r%&=R~?w)%j2+n(KYsM}Cf4YXA*8d|FY(L;r^x z1Pcf?p}*s!HUofdq{GmR$?p4k)Io_r5mPg>^yP1hSeV$~(aw3Xs;F~&O8(yRf9??f z?NYV*a!39@7#~lQ(V?&W?OSUZ3{NI28~F^0xz30-to6 zJPUb`L)u3g5N13l8ojA&1J+5RK38LAj4+`&LG4Uj@~T0Oo?h`K?sWSIrnC%4CfARt z@}UE4d=?SfUmvl^hlSwRM*Y19ApycROD*7BW{zyL77)J{N~WS~>|)w%`dgehc7}0% zGS!`UVMS{TIP)P1S7#iN=bVseTWB1NJUz+M^IfuDVS=SMZ`q_}jA*DEIxW6z zD-{w@SAi$yxXiNq-=CxQ!b<7$U^Pl_IMb|1`IjB8X3YUS_LxUplj(Wk zA^55BKk$&fIgJv$iZ^^kfS~s@Ke;JEFP9lE|igRwO4&fqh}D`Y~wS$di^2N z-o~_=jIgQt@%lRA{{h@AEBThNswZwaW^16}&91ZDRunq!I_F#u=6MhJ3BX#Zi~}^! zMyVdHq1HZBAs5tdf4g&n@eb#@;nxK+C)o10UF9a3^|;%MGLLuG!r^1nKbx`>)G0)O zrZ4Z%pYgXy$ZVKkgwz;uD+o5T(&>b6k+6@N{ROCstJAK(0M(m}+C?t$(ctj%@?wYQmoL6g3=e+-KRlc>`q^c z3&>T(O2s1%b%^R0=UI9&0J>*jzmuS!o{)spY~{{fXX<6as#wGg89%n_d#H&T!T4R! zc$Wl8PYHT@6DT-uhZ!op7E>hyeQc$OH8lVeVWqJ)m)%Hfh*dL^ zja@~w7&v;};&rNjZnvU#FZN11e?lJRDISwKAr)QypJ+kI>uu zjP&N9$<^DvCmATdurc<0HG&~YsxKm?o_&xHs`ruuuqL!!KeY zRwY~bo}FNr~V>8n?BzG6ez=mt;F*z1fEy9bLRC+zXlOXC zrcKHgT26$$$|Q5AVbu2l1=wIyI?U_z0xRc9X}R2|cAm&Pu*C(`H}m9F^z#*s;Y+ur z!C$qC)sfb);@Qq-Gu!=o1Cbtu=j!$Q&Cr$QYDhdV^wQytq&wVq*wUgBe)_a_|BXCP zdBW3$1W?~P#Z0m@L%U-8#mcC%``6~xoSiJ@1#l({PuFKp+OFt#>E=n;0k$qc1zP{$ z(A!SGa}1;1(v9mZ%$#tWBhBP!X*#jOAYGmq(juExrS{)AK8{-?mgLAM9-;QGfed*n z9t@B0zAhCn#%KzHHeDcrmTw$Ziu4?&W&V&|KR997`HHE#a*{R-YU!k+p@%75sSb6Tyoblj5({QOVAsutxv|!IdL~!RLRe<~- zt>L}>d`92kyQz)bbrxe~y2W3!S-d!>y%yNJTgyU}=8q*UCNaq}*94>Z6$AZ82eJ#e z2#gUK7I!7hQ!-^{Y6FGW%X=)kEAFGVfRC#t_Q4`mD!#N7$E}aPSua$#r|yDQRM{3I z705A`k`~!Ijgm)LxB<5PG1Z8B8(*Fg7a{05(W8<*(KBDKZ}^az>kcC_0ti9J3Lb#+ zTa!_Dih=KQGadlF+aClLJ@>nl^RMm4|EzIm7C+y=+@_7krKd&@jux2VmqtQG01Fc79GQsuKHQ7$E*HV?eE9u3y;?$n4~XimEC&ADXoz zYDGh&GelgOCr^%J{f)EHBu}S4^X0dAe(&;KJ#BD4q<47uhJS*`k-utm?^sMw${#Zs3wm{!LzQusw*#ATT2)XujT82{3hCs3E^zwA)1C}u;^!hnbGyU> z($8vEkA6lzQDzojcaL?qkvG>AB%EHHR~BVw-4lX0J*!)-&WAzx zcIv5o_{l~Yhq7dWrkEl&f#JwO^e5OSuVyAk-K!OUsq_eSz)&aBH?V&uJC!)Ed^3AJYHed z#kL$V-JE%)pjywenc?K`J^h5Zx!m{OmpyR7M9~RaAG-glNZ-Ux@Sr6|P~3a+_r%-* zvL4hEvpT!@)rJ>-u6KIeKKxTQa;ln>o&V*1zzq%nk~qDb$8pj8(o{@o!f|Q8<-Fp6 z((%Vz_f^Nd9o#!3S-KTPy6N)CVhaJ=d&kyhbuo2`>m!LG^>>Bc3MVW{Y2&LaRJ(hc z51^jY=elZS`SsnEJrYVzBIbmC`+)Wzf?>uWf@6f|zTE=d772BqzCdV5YvKA#zVuM`x- z$VL`HvrRzR=P`A7F0;jYh)N%`xzHv8!|0rqrojQ{tNG}Lc0&xwclS?K_GK1fZR&M{ zSKyA|E#8IT;bX$aDuL|ZKlAQ8X6BG^C9)et z#0*>V#_4rRfL8>`r}qZv>zD1-0D!7W;3d@eo1mY5^+HKMn9*0!kVU{_jovrwUQytq zC;1eMO|u`RUgTw5hWd)N(EXQ9?AYJBF>-ODI+2Q=oLXvT3>l0EgZTxqiz-4MBZ#~p zQJ-C6`@hrW8qVf*zlZnH&Y6FusCfdelVI*F&mBqR|Hl^#U@cGC5qltWmK$x)`<#(> ztMsl?{l3XgGDQE4cPrK#jf&QNER6-Dm6GS9O z{D9)t8R2^E)G}0rl?O?lUZ|Ime@6CEmGgwlk`Z}uSV&8wq=~K%*Zp^ zZbTP4fS(J`5$f#vAmWEp{0Ua$BDSocfGqW`b`TKeW*h1B$fV4=dUBlscM{p#8zGP! z%s2kh9v-tWu;iXNzH)?Alv$KHeMiFK|0xy@(xFR!P#iuImFFl<58LrESLpKb?`43B0b7$4X5MhiF2F%_=Z!0p$;pmdo0dm-siKq$5ppLDJ-xxy&fUv$>Qeg2%(USl}=+u<9CY?!-Z|9%=X1}sI z0O0#K-)+o7r=`+nfVJt$4b`Dt%uiMnbpQZr$o|1dlv*W+Eg4zU0xBxl3Z#0-6(iZZUOj#ctU&;-miu!(kOhx1^1BDCar4N$dK56UZaU(Kw+gm$>A^x zE2mX9B;MSZfgc0x6i`rU!y2hZ`NziGxjo@xxb72;iT>=3wHWvSy0)z3<#27_;wFOE zyfQH|hTwryFv$8SPTJ<+;rI^H6xTO;)Bb8ker>Un((@@g?D0f1X; zm&lmDo!|K&k7+|I`%@)O9vU}~>4CCmXm2hVRJ#+KHGI|{-8bSrQ4Xsy@yIlNlUy*S zS&1WZS;wJL^QhPI!XXX0(wr$8i3{akTsxd6O9C&cj38etxAJtn+%Z^G72FKkg$Zt71!)ZkM ztxvz(Vj-+<&eWy<HtZJ-bDZuFxj_v|`_tr=ud6F^Ag+F}8`(;CSZ=ES|K`+*o=! z>yS5=Y=f&4E*v`K82m;8c@`Mv5{+4(^_F+@2-n8}XOcM1JHk|DWt}a(fMvEtO}+Iy z9va^T*!8VdcTRc^NF^KtMegCY(D{QA)ZH+0H_g>$vDlSMu<;0sIM&=zLJ1(>eckdu zMMNKvuOH#XVCAnRtGoWLt*q z43_6SZbWswLIk=TsZ3l`$z0}}*Vx+(4~^WnzGvClK1+0Da|Qt5s(;};0H=#AkCJO& zP&*K7^T{>E%=_&=WqHl*;Ezr@mVKY8m**><#=8)4}B{9VLEkCcBN1?K z4!&fik?@yk8}in{-aX~$Z^(aCD~3Lku3|omMxCn zQg4FMfF&op%F}pcoQ?gTJ^M5>g+U*-r3tsU%NO*9QO~%xSlRq|>VzoJ^p4SJoLC~e z{pB-=|J=Dr^AnV~OwyWyag(Aqq^M?>`TjS&Jz;Ag+x%y{>7lhCH{qQtcMqeNw9R8G zePUU_U-iJKa7M>26{eq)v7(xoN7Tt9B+WnVge#jGHV7Vj<6Kp>UE==Qz^InPP#&Vp zZ$h`MXK?1Xq&8jIj0PyId7FPHGtJAV64-CF53A)>Mt~D~|M(4$s!oFD(|TcevD*FL zO;LK6tQ0}5gAT4T2V)5xxd}+~kCsYWb!<#!uEdQ9bUYU3u#ewhl3oN@IiL5r5<-u0 z|Al2?_9c@4B~5O6m?HuW9_n;u5c51!c_MaPlf8%Cpgt);O55Om9xVemuAl>fn<8NSqim zc19t-a_`29yTZiTpFO*1v{}7_%V)Obai8(P-n{3kn*w=YQ!^@dep21zf~CXK_&weW z?Af4=8|TGEg`gKDkBR}`Uy;xW}^duR6Ta%N@JxG!FfhFH( zTsjti0+M0#B1uDD!>l7u*Dj72?g%Blq54irgiltYaur(_(TigaZIczR>|e{??tXwZ zP}}6hO61C^SM)sen*XkvAabUhyCFjw6EVnX0GWLbV&|_{ew8nC+WTL~nA$WqRqu5B zZ(p_w&@t=(A+roVeF1Y?Z$K+ycSUxU_5R_+?R$Fc;P3-IzM-OEQv-W!6kk`+y*Mr_ zox}l5@=A`87?{~hzdvD3WQ^)(FiJR>Y*1&?P-WMvr#rxCxdRfH&)_CcuuFJfsV{k9uCFk|%unN7dXkM0@qc4*lx2|{zaOjQhdgwZ_e0a?dzN0> zLxO2`5~!)GH|AIQ(m@rbL&YRZGds6%r@=k!oOa^Y&vt1J@f>O3uht`_NYnI$j_4C* ze=K+MZ3SjyMv_3q*sZ@RVd=zv8eOh2FD{3e?l;yj}(F>Pf-MZPqtp-6G>zI^ym7e(Z;FGN3ncI~AcN zuYo@`n{fjMKyC*)S+>-6m@c`t+b{vWm`w}1FI=2unXSlvvVSUNMk#n z5-mh-Kw9XB^yLk(M0g7)Qy--Hr~swBm*6viW+*Wq{V;SMpmD!ydzJ5Ux#Dlq3K=p3 zNLnyRUruG;zjH!^7-tAOOT$Lfa7`My+L~8?#XbwLMG@{GMiAZIt^$}9Fc!EgFA!7( zTlvel0m^9k+$f&jOQ{b6G;ogaFQoe0v~AOwVdcENzwv)KJQMOi6*_bGzjQwL(OW6OuT>I)OZ1-&ixfTfb?kA$O(!(-GPyJUt!&yBzI5eLTx@r z9*``b#zorX*Dkqg9pcE2!*Y*FI}Lczjw?l_gmeTlDWRN5&Qx~Oo!_&XgcqSodP4%< zdgk9A`C6Tp-ENcv-t{A@yQ?1Q+|8KXeXoNnH}9glrqOJxdF>gr88=TdeG^bBAE@Ba z6qtKi!G;ph+*dR5-nOqezDr}u2KO<>SL!NUy<)R|J)R|2&e_&|kLc!J&a;TNs}gUc z5t^hmESNLe9-<1MB)a0AmM%Lv)&a0fVyZ1AJs|w=cm`%namHp8+)pTgb^BK5FSLVQ zMCKjG04fvUsKs+nb2D+DjV^6qBJX%B>^MN3prr!6)9FaJtNjN>rA{} z`~IVl@0tc7nLkPOdqLD|yGn zz&~G##6p}MN%DUnMcM-onSCB4n$$%{D_U!X`Uy8chtpR zBVxE;A}iBo!`LrHe@XuA>{R!bV}g!lVYD{mmU+2*ODXd(8zS>&+A;8`P4~RMcTT+! zQAL2g${+FFQlmB#SfJ<8P7#gGvf{Fbh8U%vHDaH0K%)CHfoTtO^# zgt$w7eBLeKV0x!vReUhooJ2UA!0v-4_w0zx95}g542yeCYbshxSE;8oTRYKYbYP2+ zMBO{SN?(+5HPrHpae;}qlU3oHpNmcqSM>xPe39+l%AaI!Yul2pTV*1Zzu_+MM6+5F z0Ijw##eCahFIK9mWk||2V?qY*pW9NkitjgxD(A~doa>Gc=%MKwr4`43=F1mUFYJ|?C}gj!JN04qyRw;dN2+%&k}3YD zrn~fY-tUIw?vGva)H)(d_!qoQ*4Rdt%^SkY@Mke%E14&hZ@tJV0=>PP z&-O&)*O}NTl~<+lnDeU~Jb~{*f2kjkUsp?B>jGCiWD(z}D{O zlS*5F0cs3d&CKVDvFzK~6&DoQ;cl|dYvpFt(rwcm9%dccdrmX&lCRT`{&ho>VJOe8 z-N@4Wc-MU84&GwKQ&diOL_GPeEhfT=QgfIVG+LM_a{p3-daH+v!}(32yPe)Aw#%o0 z95$Qwa}|to0)GrM9&6#=zSLppiV$@0>&cHfT*-RIwiiHGREJgjb?Q5RiTmT&^0}ve z`*VJ(bfn|UW_@$Glu5*~G=uHJXeETmoMR3QhpWhL)$?Y16iDD9WJ0FYM@X7-RjvIq*J0|H1law$Mt( z>jZx(Q20tgHu3&FK9r%_k5T6Ajc$qgdTcmt>-)-)E>a7lBV3L@7APKE+)VfIZ8!T3 zZ4{+uL6qF-)7eB@0}$BN1RVg6TsEPXp5xn~h|0tIGZ7Wib>nq^D3WJ$ZxIm?^;CR; zmYYycP9`#gEydk8X?Kc4fTaD|@MB7X+TCsK)v^ePz>GHt5O8wT_T|7FiadVe&L~ld z^<#tq7nyQb+dz5a?z(#hB=o`~Cln`C-r&ZoN*H-OSY=o*(w#o4 zUPK~8+@ywUP95wEeTN8t`iZaNclS0Bs<1Lzi~jAYDd*)t-#fqGPxZ6@RkHsZ5YZ+=u(~EeD8l#%k@PZC1)s@@9`|xvK`1 zL*El2c^{lHUjQhC^EjDINBy|8Q%DTM#xV_i7UWu|%0WI^E#!gYi=a4D=&eyEd824C zMqrq}Jfx)|Nv@c(^Cj1w5ObX6SO)jCNSuJYY6_JguVWx23|XO^H4A@H!h0-8xgpWO zS>W>MYldg&gk`yH}Rj;o}Cm}_?vj)CDFsFi+7TSm^yUnWL#4w z6v4{Fk7u*U>UMv}Bk1yCQgvK>v4W_6N%@ds1`K*o;cX4SkvcmBb${nfu6ZR(Vavlv zzBT9bF8lKE_sN3E=r8l1Cq*oD>StYsM;2s4joDbr@^2Ryu5^|^eaiMWG$m%0PJ9hK z&@eR)H2aY@1teQO;a45EEMX}tVMFj7u8;b^o7}$!{W2LKsKL`vbffn-O-FEi?>lmK zvO0w+y&K8TR9ngnff1z?#jTD*HJkgM8IqOXR_%SlZovI*fxPNvL=I&mTssyhGzRd4 z!QXi58=c-u!eu6(j%a@{J=vL8^dOI(qQ+xFQr`sq@OY~{#6nDoLW0cI+CDfxuq!$w zEOrf_Uv8(ogNE5F)@MLi$o3(VrL#Jsar?v8_kg>MWPisDyLRqQBLufvAn^h6))Gzj!a#>nl{TQYdxo+;z-F;Rp(C&0yz3Aja=*jqM9Fb6ay|+J53HPnmQ*N!turD$+MNBQGpI;j%=v z!C&8Wt`eQbvMgI1BE;cu%WpcQKvHlvZ0Y@L-ZIB>1jM zLiN%~Mcb#&BA9)Q=R)1RszTW!0_vSN-f}Px?^M~HcOtj&7C=7!C?fwAyyxX!8$0^= zsTMzNLKyOCq_#KXh4&lE9{6t~|2Q~5<)rm*bG?ynb0*JExvQjWqHF#e8^PuSYfg4| zl{l}le!n^FAofJgHg`2%f3NoGLsF57bb(yT8QBcSMt8^1t#l1u=^{%r31|#dHA7@h zYH~M$LPGKJ3YZ+DJ6Gt&1B#{~L#wtba6*lRbW`AD;?eZu(BGJu#8HV7eJsEIx$=4~ zb^>E{Jij@JSJA8MQmWCkA0^FqG_Clqc@CTD9eOfn`Q{#{Bz{AE0fF$>ti~I9{y)K# zF?Z=VL+XBc_vjG&-@sGv?o03TRphw4uK!UWT?I-IX)oWI$iAFm>6YU^5Yw3+)j;QJ z(r6f|9GQ}+MRAbrrf#&rFT92tBYesbzb*W8eW|-eD~akRWUab>=j5ivfPh!B8c`N7B9x$_g{zV#Nz+$Z`2TyhrO3fNDV4&Q;J zKUrJN-gp53nt!}Y&5Y|Dot0vd?bb7oS6h|p+POFeTvm%RG=DTQbvgxYhjQg6VfV&- zowJo(`XV>Sub8cV4{Ja2t(Om_YPL#wR%vr$fToF!KJ!dWe}Hn(!tlea*O3ZxyBDxR z4&O4)4oClOp0S%*a6hgF&r6M1QNSEiO26V!;JEtLa*GAvXI@u|Ei=mGQSVZem9lma7+Bf+J;uM*>-Ec zmkRap_Yy}lwVXI2jM7P-C<4E8Gd*rltk^}$3USOw;fR}) z|I$X1t${F+TX&CeXs`;7BRCRsDR%TFezGz%_}$3uCN99wMjN6nbWB4({&30~eOh^7 zbix++C!0LA-!Wo@SG-#XSJ3)V858I)6PeLCJfYw_zB0$B`00z z(K4QR2~80+wdHjwP@H}Bh2SEwYPN($tBXM@kUv2km-=e0QhXSZ_yJ_HRz`783fnWJ zXeW6D*IX{K;%G;{eg?zmPMQY@)8fcH<7F| zrT|nkxsW7e3$OM$`Q*x@jPUido<+ z-K?DSi0ujCt|ZxE6%4INA0ayve$m&2vPLH3=t3{_z0-1H4_w;Jou`X6fpW3pM-iq@ z8VgSOhg~9ascxN}Nz+_BI%Efio9%H`Diz<{38SSLZNni1*pksU5#nuJMqycg_jsL! zBFLvS^UDg}XMa8Fe-yN%+Ln9<=p8)&q=TfLh&!b=Ldju2-#AV$Qf|e!r3k{evJ!wD zKZaj5i&Ulijyv~(tK*uKezfARfbyv)Zf=bm`-U&BI>)=xX)i@dSQ)kp^FW&^MzF`7 z(wD_o&)ccLI4|gXItOQOf*k$6>UJP6TDe&+kegGYT{+@o7H8K(J{`}Jx5%2C#$1#Q z-C7Vp3S=AXm?dJH0-z!Bx8s8H4mrI*`9yA>TkiMUqv|?Up{aB3IbtFGQk13XxBBj0 zddKKr&rp8tll4s@*gcoJ;m*+s1pi55OUzW22+bUCUJq7u8~^y+kJ`M}7% zQTWS;&?dyIbaP9K4+d?}1^0?rQ#Ub^E26|?<*9i!f==X1qxs~iKrQz&YR#$}39Jf^QcQ#tVs5!ZH_S0hD&fj!FQ4!f>gyL!+h&=i# z$#v&NW>ey6$-b6*@-Y!`+}vp+`x8L&uH-*FNaq$iG)t1SNi05&m~r3X=%`5P+#*M*tK+$WoRnCu|C{Sh6gZ^h@gOkYwehH zBoB2*sA_m4hRVR240c}Qg1uF^0DG)i9og%CM#05e%-Ze|aBwy&2LZ>~wRrTm3Dre5 z*d2};=dyU*;CUU`Q_+?%{BS>EjrO{y7EhT zI$|2TDxOT*bW`onyfgQ~licTP#_FySea)d%>|X3+SG`N<&$n{K4|trXP{*o{`kGAHi{bVk*Sl=zn^no^9OY?)IcXzQ#c0OFO;ed>M(<|F zhd1&YTI`Ak(&~>}xi3&b5A>m=d;IxvD23TbH+nDDyaMp_SCo7u*=nN+0z<(WqlJ#AKA@t}plLQqoW)2}C5 z`=Fp_>-ODhoV8Aq`RY!`%{~~_BZZ(l?%D{4FNynd!{YtXb~*2pek#Y9t|S$x+L~0D zliF?}&d|KxC2IyPw05JPb|g(B`NW1Qv(!&M+SvL;cV__%BerXIgJ{gQi+AZSSKYi6 z&mXMGFobw;*1-^RwYgiXw%Cl4+IGJ5$0xla@4%0TU8rU67qQaPb+QqH>(!j$$ zIp3a#)+Fjr6DL~h!!E#t3%2sNq?bcAdVRg)i`#!kjK)5~A3sggJe)Xd5g+RLSZuwN z`g#-*AGMyBXt6*&SKJ~`BUHYDKXV*~FMNZuRGee^W0du8L(93yrQvc+nO2^i@SP;|b00K{kTJ=;Lve97q6&Bk;`ayefjbde0%%9bEQL+U>!4qaB^7f~I#b+Qqc zGfMDM2!C5QI3%=d%)r7>R3UgeC5?`IDen8eEp$Y0FA|TwieD48XGBMw)8_1_b!=JF ziGi1HY-KTO$+BTYmrSU3k6YTqVc|jDyQw;$35HQGP$wyElTU$hr33B94^+8pUNABB zT&K04dt|+$?~~WFm-5RRbA*~s%^zqYK99%cn@su_>)|SC9`SM3jjIl1Q34~Ll*U<`?Yo)U z9QE35GbZGYUim1}WmX{0lqUC!Y~Jv&i6=XZRAQNNC5J_Rf5Oi&^N*F41m!}n#SU2G z6U#BMY5T|2L4Oc7QxJ-LgDXCTF-v=fDzD2hQNL0qgm|Oua2hmwtJEEAIjN-~h-WB6 zT`x(yKSqF6u}x5wXk=9qW1*ovi7q5eh|s8`Tg2$f@8Qxvd&TEeb$H7Ke)w=j_YmlJ zUTy}<=UnmJp=sOAi##I(*o~~-c>Bi+IxNW2-5-Ub@GR@L#jERc1j>JF7bzNcm+yZTyL)%& zFJa;`#B|o|f72Yz2CCWpWqsD~$#tUVm=msFPx#R`IsBZsr_C$_eDcS;gL^~~8xPMQwcZpm=G?-o zkezB4Ebdg4%-p`$(QdYfH(nmc;$$MvJ@r&mPyar}+=eW%^^du{^3+3H8u-XhXJY)b z8~ADK$bK1i=Ydi4nEE%!-2m!|!jijhneI?wuFPu$smNOQUGsx{_qkDbVKYGA=pJ5u zA2C~LRE(*1KP+R&Cau5#I_x-A+uSl*y`F{Zb)AszIKA7vV)b7ht*~Nk1jbLL<3Cd? zU&=B38`%0XFu!)->cYr&55^uC!^^65pcYDi-ux}FPKDERHYz>ju=YMXhKsy&Xqj-% zoluGX91&GsF)QwF8VK)u+mm2gYU!fMIhOO9O0k9bi)t64cZ$Tof&vpsY9nVZ8rfdpB$dpb&sI zz7LGiOMH_K%ofTm);~#Vq_2K)Zmw;c`inKqD(H1Nfh*-H`6Hs&DxKQcOvJj)$Gv4e zf-?gzBBM-uz81!j-NbOf9M!CF` zuFdN=6e^4Wl2iZKZOu3OT$~DRIEAE7O@PLobVboOZ=rfAbC(KZtl< zoUiAdy~f(%u}ym+%&mlA%uYf~Cs_RfpRbQ`B-{Akw>7JVbJ;y>bVLJ;;aJJrc!=gR zjmWR3=FxrvF_oAu3o`rVbGsiz-4}}b90T}q4!druyh6ussYofn6y4&$Kd~))_H%UB zO0s9N9eLb<-r()Md<)E2r>YK z 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 bBvw3+v_B+=gMfcOxuE=1L!n&G^!0xLul;FG diff --git a/docs/source/gui_tool/user_guide/media/image86.png b/docs/source/gui_tool/user_guide/media/image86.png index 2b03b49153b7d940b17e5f9b26319a50b9ffa8b9..e21c10036e81235f719ca345278e0c2a6cfe86db 100644 GIT binary patch literal 29972 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 F{sYe~)29Fc diff --git a/docs/source/gui_tool/user_guide/media/psnrdisplayfeature.png b/docs/source/gui_tool/user_guide/media/psnrdisplayfeature.png new file mode 100644 index 0000000000000000000000000000000000000000..184bca5561869a56df9e6a3a7e0ce94a58d846e4 GIT binary patch literal 3919 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 + `Signed channel support for BC4 and BC5 `__ + + +- GPU Encoding + + `GPU Based Compression `__ + +- Mip Map Generation + + `MIP Map Filter Support Using either GPU or CPU `__ + + `GPU Based MIP Map generation `__ + +- Image View + + `PSNR Display Feature for GUI Image Views `__ - SNORM support for BC4 and BC5 +- Test Analysis + `CSV File Update to Support Automation `__ + +- `KTX2 File Support `__ + + A KTX version 2 plugin has been added to the SDK, that supports saving and loading multichannel images, BCn, ETCn, and ASTC codecs. + + +**Known issues and limitations** + +- When using GPU encoding, all source image width and height must be divisible by 4. +- GPU HW based encoding feature is only available on the Windows platform. Encoding is set only for BCn codecs and quality is limited when compared to CPU encoding. +- BC6H is not supported with GPU HW based encoding. +- KTX2 file formats for ATIn and DXT5 swizzled formats are not supported. +- ATI1n, ATI2n processed images save as BC4 and BC5 formats. +- Transcoding to ARGB_16F, ARGB_32F, and ARGB_8888 image formats is supported, all other channel formats have various data issues and have been removed until a fix is available. +- Viewing glTF and OBJ models using Vulkan(TM) rendering shows blank views. +- PSNR and Image diff analysis for mismatched channel source and destination types (F16, F32, HalfFloat, and 8bit) needs improvement. +- BC6H for OpenCL is not available in this release. +- If user-requested MIP Map level generation for GPU texture sizes in the GUI and CLI applications are invalid, they will automatically be adjusted to the lowest settable limits. +- Limited CubeMap support. V4.0 @@ -26,6 +61,7 @@ May 2020 - `CMP_Core GPU encoding support `__ + Supports GPU based encoding with OpenCL and DX11 - `Analysis Views `__ @@ -404,3 +440,4 @@ views NotUsing Level3 Disabled - NO_LEGACY_BEHAVIOR;BUILD_MTd;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MTd;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework; MultiThreadedDebug $(IntDir)vc$(PlatformToolsetVersion).pdb @@ -344,7 +344,7 @@ NotUsing Level3 Disabled - NO_LEGACY_BEHAVIOR;BUILD_MDd;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MDd;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework; MultiThreadedDebugDLL $(IntDir)vc$(PlatformToolsetVersion).pdb @@ -364,7 +364,7 @@ Level3 Disabled - NO_LEGACY_BEHAVIOR;BUILD_MTd;WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MTd;WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework; MultiThreadedDebug @@ -385,7 +385,7 @@ Level3 Disabled - NO_LEGACY_BEHAVIOR;BUILD_MDd;WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MDd;WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework; MultiThreadedDebugDLL @@ -408,7 +408,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MT;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MT;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework; MultiThreaded @@ -429,7 +429,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MT_DLL;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MT_DLL;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework; MultiThreaded @@ -450,7 +450,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MTd_DLL;WIN32;_CONSOLE;_DEBUG;%(PreprocessorDefinitions) + BUILD_MTd_DLL;WIN32;_CONSOLE;_DEBUG;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework; MultiThreadedDebug @@ -471,7 +471,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MD;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MD;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework; MultiThreadedDLL @@ -492,7 +492,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MD_DLL;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MD_DLL;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework; MultiThreadedDLL @@ -513,7 +513,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MDd_DLL;WIN32;_CONSOLE;_DEBUG;%(PreprocessorDefinitions) + BUILD_MDd_DLL;WIN32;_CONSOLE;_DEBUG;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework; MultiThreadedDebugDLL @@ -534,7 +534,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MT;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MT;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework; MultiThreaded @@ -559,7 +559,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MT_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MT_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework; MultiThreaded @@ -584,7 +584,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MTd_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MTd_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework; MultiThreadedDebug @@ -609,7 +609,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MD;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MD;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework; MultiThreadedDLL @@ -634,7 +634,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MD_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MD_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework; MultiThreadedDLL @@ -659,7 +659,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MDd_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MDd_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework; MultiThreadedDebugDLL diff --git a/examples/framework_example2/framework_example2.cpp b/examples/framework_example2/framework_example2.cpp index 4e77da9f7..3c532de53 100644 --- a/examples/framework_example2/framework_example2.cpp +++ b/examples/framework_example2/framework_example2.cpp @@ -56,7 +56,7 @@ bool CompressionCallback(CMP_FLOAT fProgress, CMP_DWORD_PTR pUser1, CMP_DWORD_PT (pUser1); (pUser2); - std::printf("\rCompression progress = %2.0f ", fProgress); + std::printf("\rCompression progress = %3.0f ", fProgress); return g_bAbortCompression; } diff --git a/examples/framework_example2/sdk_example2.sln b/examples/framework_example2/sdk_example2.sln index 612f1d1af..cd5897a80 100644 --- a/examples/framework_example2/sdk_example2.sln +++ b/examples/framework_example2/sdk_example2.sln @@ -7,14 +7,56 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Example2", "SDK_Example2.vc EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug_DLL|x64 = Debug_DLL|x64 + Debug_DLL|x86 = Debug_DLL|x86 + Debug_MD_DLL|x64 = Debug_MD_DLL|x64 + Debug_MD_DLL|x86 = Debug_MD_DLL|x86 Debug_MD|x64 = Debug_MD|x64 + Debug_MD|x86 = Debug_MD|x86 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release_DLL|x64 = Release_DLL|x64 + Release_DLL|x86 = Release_DLL|x86 + Release_MD_DLL|x64 = Release_MD_DLL|x64 + Release_MD_DLL|x86 = Release_MD_DLL|x86 Release_MD|x64 = Release_MD|x64 + Release_MD|x86 = Release_MD|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_DLL|x64.ActiveCfg = Debug_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_DLL|x64.Build.0 = Debug_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_DLL|x86.ActiveCfg = Debug_DLL|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_DLL|x86.Build.0 = Debug_DLL|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD_DLL|x64.ActiveCfg = Debug_MD_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD_DLL|x64.Build.0 = Debug_MD_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD_DLL|x86.ActiveCfg = Debug_MD_DLL|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD_DLL|x86.Build.0 = Debug_MD_DLL|Win32 {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD|x64.ActiveCfg = Debug_MD|x64 {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD|x64.Build.0 = Debug_MD|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD|x86.ActiveCfg = Debug_MD|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD|x86.Build.0 = Debug_MD|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug|x64.ActiveCfg = Debug|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug|x64.Build.0 = Debug|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug|x86.ActiveCfg = Debug|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug|x86.Build.0 = Debug|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_DLL|x64.ActiveCfg = Release_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_DLL|x64.Build.0 = Release_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_DLL|x86.ActiveCfg = Release_DLL|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_DLL|x86.Build.0 = Release_DLL|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD_DLL|x64.ActiveCfg = Release_MD_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD_DLL|x64.Build.0 = Release_MD_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD_DLL|x86.ActiveCfg = Release_MD_DLL|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD_DLL|x86.Build.0 = Release_MD_DLL|Win32 {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD|x64.ActiveCfg = Release_MD|x64 {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD|x64.Build.0 = Release_MD|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD|x86.ActiveCfg = Release_MD|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD|x86.Build.0 = Release_MD|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release|x64.ActiveCfg = Release|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release|x64.Build.0 = Release|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release|x86.ActiveCfg = Release|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/examples/framework_example2/sdk_example2.vcxproj b/examples/framework_example2/sdk_example2.vcxproj index f70ddca35..448877ccc 100644 --- a/examples/framework_example2/sdk_example2.vcxproj +++ b/examples/framework_example2/sdk_example2.vcxproj @@ -325,7 +325,7 @@ NotUsing Level3 Disabled - NO_LEGACY_BEHAVIOR;BUILD_MTd;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MTd;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework; MultiThreadedDebug $(IntDir)vc$(PlatformToolsetVersion).pdb @@ -348,7 +348,7 @@ NotUsing Level3 Disabled - NO_LEGACY_BEHAVIOR;BUILD_MDd;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MDd;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework; MultiThreadedDebugDLL $(IntDir)vc$(PlatformToolsetVersion).pdb @@ -372,7 +372,7 @@ Level3 Disabled - NO_LEGACY_BEHAVIOR;BUILD_MTd;WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MTd;WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework; MultiThreadedDebug @@ -397,7 +397,7 @@ Level3 Disabled - NO_LEGACY_BEHAVIOR;BUILD_MDd;WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MDd;WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework; MultiThreadedDebugDLL @@ -424,7 +424,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MT;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MT;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework; MultiThreaded @@ -449,7 +449,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MT_DLL;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MT_DLL;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework; MultiThreaded @@ -474,7 +474,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MTd_DLL;WIN32;_CONSOLE;_DEBUG;%(PreprocessorDefinitions) + BUILD_MTd_DLL;WIN32;_CONSOLE;_DEBUG;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework; MultiThreadedDebug @@ -499,7 +499,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MD;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MD;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework; MultiThreadedDLL @@ -524,7 +524,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MD_DLL;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MD_DLL;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework; MultiThreadedDLL @@ -549,7 +549,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MDd_DLL;WIN32;_CONSOLE;_DEBUG;%(PreprocessorDefinitions) + BUILD_MDd_DLL;WIN32;_CONSOLE;_DEBUG;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework; MultiThreadedDebugDLL @@ -574,7 +574,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MT;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MT;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework; MultiThreaded @@ -603,7 +603,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MT_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MT_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework; MultiThreaded @@ -632,7 +632,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MTd_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MTd_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework; MultiThreadedDebug @@ -661,7 +661,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MD;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MD;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework; MultiThreadedDLL @@ -690,7 +690,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MD_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MD_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework; MultiThreadedDLL @@ -719,7 +719,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MDd_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MDd_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework; MultiThreadedDebugDLL diff --git a/examples/framework_example3/framework_example3.cpp b/examples/framework_example3/framework_example3.cpp index 9bbeb2835..188330bea 100644 --- a/examples/framework_example3/framework_example3.cpp +++ b/examples/framework_example3/framework_example3.cpp @@ -55,7 +55,7 @@ bool CompressionCallback(CMP_FLOAT fProgress, CMP_DWORD_PTR pUser1, CMP_DWORD_PT (pUser1); (pUser2); - std::printf("\rCompression progress = %2.0f ", fProgress); + std::printf("\rCompression progress = %3.0f ", fProgress); return g_bAbortCompression; } diff --git a/examples/framework_example3/sdk_example3.sln b/examples/framework_example3/sdk_example3.sln index 73ae782db..fc221724a 100644 --- a/examples/framework_example3/sdk_example3.sln +++ b/examples/framework_example3/sdk_example3.sln @@ -7,14 +7,56 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Example3", "SDK_Example3.vc EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug_DLL|x64 = Debug_DLL|x64 + Debug_DLL|x86 = Debug_DLL|x86 + Debug_MD_DLL|x64 = Debug_MD_DLL|x64 + Debug_MD_DLL|x86 = Debug_MD_DLL|x86 Debug_MD|x64 = Debug_MD|x64 + Debug_MD|x86 = Debug_MD|x86 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release_DLL|x64 = Release_DLL|x64 + Release_DLL|x86 = Release_DLL|x86 + Release_MD_DLL|x64 = Release_MD_DLL|x64 + Release_MD_DLL|x86 = Release_MD_DLL|x86 Release_MD|x64 = Release_MD|x64 + Release_MD|x86 = Release_MD|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_DLL|x64.ActiveCfg = Debug_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_DLL|x64.Build.0 = Debug_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_DLL|x86.ActiveCfg = Debug_DLL|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_DLL|x86.Build.0 = Debug_DLL|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD_DLL|x64.ActiveCfg = Debug_MD_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD_DLL|x64.Build.0 = Debug_MD_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD_DLL|x86.ActiveCfg = Debug_MD_DLL|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD_DLL|x86.Build.0 = Debug_MD_DLL|Win32 {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD|x64.ActiveCfg = Debug_MD|x64 {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD|x64.Build.0 = Debug_MD|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD|x86.ActiveCfg = Debug_MD|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD|x86.Build.0 = Debug_MD|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug|x64.ActiveCfg = Debug|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug|x64.Build.0 = Debug|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug|x86.ActiveCfg = Debug|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug|x86.Build.0 = Debug|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_DLL|x64.ActiveCfg = Release_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_DLL|x64.Build.0 = Release_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_DLL|x86.ActiveCfg = Release_DLL|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_DLL|x86.Build.0 = Release_DLL|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD_DLL|x64.ActiveCfg = Release_MD_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD_DLL|x64.Build.0 = Release_MD_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD_DLL|x86.ActiveCfg = Release_MD_DLL|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD_DLL|x86.Build.0 = Release_MD_DLL|Win32 {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD|x64.ActiveCfg = Release_MD|x64 {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD|x64.Build.0 = Release_MD|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD|x86.ActiveCfg = Release_MD|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD|x86.Build.0 = Release_MD|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release|x64.ActiveCfg = Release|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release|x64.Build.0 = Release|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release|x86.ActiveCfg = Release|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/examples/framework_example3/sdk_example3.vcxproj b/examples/framework_example3/sdk_example3.vcxproj index cd2322cf5..41eacc123 100644 --- a/examples/framework_example3/sdk_example3.vcxproj +++ b/examples/framework_example3/sdk_example3.vcxproj @@ -323,7 +323,7 @@ NotUsing Level3 Disabled - NO_LEGACY_BEHAVIOR;BUILD_MTd;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MTd;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework;;../../header/Compute_lib MultiThreadedDebug $(IntDir)vc$(PlatformToolsetVersion).pdb @@ -346,7 +346,7 @@ NotUsing Level3 Disabled - NO_LEGACY_BEHAVIOR;BUILD_MDd;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MDd;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework;;../../header/Compute_lib MultiThreadedDebugDLL $(IntDir)vc$(PlatformToolsetVersion).pdb @@ -370,7 +370,7 @@ Level3 Disabled - NO_LEGACY_BEHAVIOR;BUILD_MTd;WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MTd;WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework;;../../header/Compute_lib MultiThreadedDebug @@ -395,7 +395,7 @@ Level3 Disabled - NO_LEGACY_BEHAVIOR;BUILD_MDd;WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MDd;WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework;;../../header/Compute_lib MultiThreadedDebugDLL ProgramDatabase @@ -423,7 +423,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MT;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MT;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework;;../../header/Compute_lib MultiThreaded @@ -448,7 +448,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MT_DLL;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MT_DLL;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework;;../../header/Compute_lib MultiThreaded @@ -473,7 +473,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MTd_DLL;WIN32;_CONSOLE;_DEBUG;%(PreprocessorDefinitions) + BUILD_MTd_DLL;WIN32;_CONSOLE;_DEBUG;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework;;../../header/Compute_lib MultiThreadedDebug @@ -498,7 +498,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MD;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MD;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework;;../../header/Compute_lib MultiThreadedDLL @@ -523,7 +523,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MD_DLL;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MD_DLL;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework;;../../header/Compute_lib MultiThreadedDLL @@ -548,7 +548,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MDd_DLL;WIN32;_CONSOLE;_DEBUG;%(PreprocessorDefinitions) + BUILD_MDd_DLL;WIN32;_CONSOLE;_DEBUG;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework;;../../header/Compute_lib MultiThreadedDebugDLL @@ -573,7 +573,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MT;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MT;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework;;../../header/Compute_lib MultiThreaded @@ -602,7 +602,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MT_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MT_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework;;../../header/Compute_lib MultiThreaded @@ -631,7 +631,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MTd_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MTd_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework;;../../header/Compute_lib MultiThreadedDebug @@ -660,7 +660,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MD;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MD;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework;../common;;../../header/Compute_lib MultiThreadedDLL @@ -689,7 +689,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MD_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MD_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework;;../../header/Compute_lib MultiThreadedDLL @@ -718,7 +718,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MDd_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MDd_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../include;$(COMPRESSONATOR_ROOT)/SDK/include/;../common;$(COMPRESSONATOR_ROOT)/CMP_Framework;../../CMP_Framework;;../../header/Compute_lib MultiThreadedDebugDLL diff --git a/examples/framework_example4/cmakelists.txt b/examples/framework_example4/cmakelists.txt index b9ff666be..1a676ca0f 100644 --- a/examples/framework_example4/cmakelists.txt +++ b/examples/framework_example4/cmakelists.txt @@ -24,6 +24,7 @@ set_target_properties(framework_example4 ) -# WIP to add pipeline dlls in proper folders -# include(CopyFiles.cmake) - +# ToDo WIP to add pipeline dlls in proper folders, users will have to add manually as instructed in documentation +if (EXISTS "./plugins") + include(CopyFiles.cmake) +endif() diff --git a/examples/framework_example4/copyfiles.cmake b/examples/framework_example4/copyfiles.cmake index 3ef6e0463..bf4e36546 100644 --- a/examples/framework_example4/copyfiles.cmake +++ b/examples/framework_example4/copyfiles.cmake @@ -6,11 +6,10 @@ endmacro() if (CMP_HOST_APPLE) # Following: https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html#//apple_ref/doc/uid/10000123i-CH101-SW1 - set(PLUGINS_PATH $/PlugIns) + set(PLUGINS_PATH $/plugins) else() set(PLUGINS_PATH $/plugins) endif() -#OpenCL pipeline dll -cmp_app_copy_to_output($/Plugin_CMP_GPU_OCL.dll ${PLUGINS_PATH}/Plugin_CMP_GPU_OCL.dll) +# cmp_app_copy_to_output(plugins/*.dll ${PLUGINS_PATH}/*.dll) diff --git a/examples/framework_example4/framework_example4.cpp b/examples/framework_example4/framework_example4.cpp index 97114ef25..b28650ef6 100644 --- a/examples/framework_example4/framework_example4.cpp +++ b/examples/framework_example4/framework_example4.cpp @@ -32,7 +32,7 @@ // This example uses GPU framework (DLL's) and compiles encode shaders (.cpp or hlsl) // // Using OpenCL (OCL) -// CMP_GPU_OCL.dll +// EncodeWith_OCL.dll Note: Only 64bit MD DLL versions available in the Framework SDK // Encode Kernel files in plugins/compute folder // BC1_Encode_Kernel.cpp // BC1_Encode_Kernel.h @@ -40,18 +40,17 @@ // Common_Def.h // // Using DirectX (DXC) -// CMP_GPU_DXC.dll +// EncodeWith_DXC.dll Note: Only 64bit MD DLL versions available in the Framework SDK // Encode Kernel files in plugins/compute folder // BC1_Encode_Kernel.hlsl // BCn_Common_kernel.h // Common_Def.h // -// Can also use any GPU Hardware which support encode extension in OpenGL. This only requires the plugin and no shaders -// CMP_GPU_HW.dll -// // When using DXC or OCL the shaders are compiled into .cmp binaries which takes some processing time. // subsequent runs will use .cmp files which will provide faster encoding // +// When using EncodeWith_GPU No plugins/compute shaders are required +// #include #include @@ -80,7 +79,7 @@ bool g_bAbortCompression = false; // If set true current compression will abor bool CompressionCallback(CMP_FLOAT fProgress, CMP_DWORD_PTR pUser1, CMP_DWORD_PTR pUser2) { (pUser1); (pUser2); - std::printf("\rCompression progress = %2.0f", fProgress); + std::printf("\rCompression progress = %3.0f", fProgress); return g_bAbortCompression; } diff --git a/examples/prototype/cmakelists.txt b/examples/prototype/cmakelists.txt index b4893b452..c250a1b2f 100644 --- a/examples/prototype/cmakelists.txt +++ b/examples/prototype/cmakelists.txt @@ -11,10 +11,12 @@ target_sources(cmp_prototype target_include_directories(cmp_prototype PUBLIC ./ - ${PROJECT_SOURCE_DIR}/cmp_framework # compute_base.h - ${PROJECT_SOURCE_DIR}/cmp_core/shaders # common_def.h - ${PROJECT_SOURCE_DIR}/cmp_core/source # cmp_math_vec4.h - ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_math #cmp_math_common.h + ${PROJECT_SOURCE_DIR}/cmp_framework + ${PROJECT_SOURCE_DIR}/cmp_framework/common/half + ${PROJECT_SOURCE_DIR}/cmp_core/shaders + ${PROJECT_SOURCE_DIR}/cmp_core/source + ${PROJECT_SOURCE_DIR}/cmp_compressonatorlib + ${PROJECT_SOURCE_DIR}/applications/_libs/cmp_math ${PROJECT_SOURCE_DIR}/applications/_plugins/common # stb_image ${PROJECT_SOURCE_DIR}/external/glad/include/ ${PROJECT_SOURCE_DIR}/external/glad/include/glad @@ -25,7 +27,7 @@ target_link_libraries(cmp_prototype ExtGLM ExtGLFW CMP_Framework - CompressonatorLIB + CMP_Compressonator d3d11.lib ) diff --git a/examples/prototype/cmp_prototype.cpp b/examples/prototype/cmp_prototype.cpp index 4f3800742..07006d868 100644 --- a/examples/prototype/cmp_prototype.cpp +++ b/examples/prototype/cmp_prototype.cpp @@ -21,7 +21,15 @@ // THE SOFTWARE. // +// The cmake sets up include paths to the SDK libs +// It also includes some external libs that were used in building CLI and GUI apps +// This is a sample include used for both CPU and GPU shared code -int main() { - // add prototype code here...that uses CMP dev code +#include "compressonator.h" +#include "common.h" +#include "common_def.h" + +// add prototype code here...that uses CMP dev code +int main() +{ } diff --git a/examples/sdk_example1/cmakelists.txt b/examples/sdk_example1/cmakelists.txt index eba7497da..5efaeb2b1 100644 --- a/examples/sdk_example1/cmakelists.txt +++ b/examples/sdk_example1/cmakelists.txt @@ -22,7 +22,7 @@ target_include_directories(sdk_example1 PUBLIC target_link_libraries(sdk_example1 PRIVATE dds_helper - CompressonatorLIB + CMP_Compressonator ) set_target_properties(sdk_example1 PROPERTIES FOLDER ${FOLDER_NAME}) diff --git a/examples/sdk_example1/sdk_example1.cpp b/examples/sdk_example1/sdk_example1.cpp index aa9d180db..6552f12f8 100644 --- a/examples/sdk_example1/sdk_example1.cpp +++ b/examples/sdk_example1/sdk_example1.cpp @@ -67,7 +67,7 @@ bool g_bAbortCompression = false; // If set true current compression will abor // Sample loop back code called for each compression block been processed //--------------------------------------------------------------------------- bool CompressionCallback(float fProgress, CMP_DWORD_PTR pUser1, CMP_DWORD_PTR pUser2) { - std::printf("\rCompression progress = %2.0f ", fProgress); + std::printf("\rCompression progress = %3.0f ", fProgress); return g_bAbortCompression; } diff --git a/examples/sdk_example1/sdk_example1.sln b/examples/sdk_example1/sdk_example1.sln index eb67d3a65..745cdbbed 100644 --- a/examples/sdk_example1/sdk_example1.sln +++ b/examples/sdk_example1/sdk_example1.sln @@ -7,14 +7,56 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Example1", "SDK_Example1.vc EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug_DLL|x64 = Debug_DLL|x64 + Debug_DLL|x86 = Debug_DLL|x86 + Debug_MD_DLL|x64 = Debug_MD_DLL|x64 + Debug_MD_DLL|x86 = Debug_MD_DLL|x86 Debug_MD|x64 = Debug_MD|x64 + Debug_MD|x86 = Debug_MD|x86 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release_DLL|x64 = Release_DLL|x64 + Release_DLL|x86 = Release_DLL|x86 + Release_MD_DLL|x64 = Release_MD_DLL|x64 + Release_MD_DLL|x86 = Release_MD_DLL|x86 Release_MD|x64 = Release_MD|x64 + Release_MD|x86 = Release_MD|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_DLL|x64.ActiveCfg = Debug_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_DLL|x64.Build.0 = Debug_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_DLL|x86.ActiveCfg = Debug_DLL|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_DLL|x86.Build.0 = Debug_DLL|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD_DLL|x64.ActiveCfg = Debug_MD_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD_DLL|x64.Build.0 = Debug_MD_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD_DLL|x86.ActiveCfg = Debug_MD_DLL|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD_DLL|x86.Build.0 = Debug_MD_DLL|Win32 {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD|x64.ActiveCfg = Debug_MD|x64 {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD|x64.Build.0 = Debug_MD|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD|x86.ActiveCfg = Debug_MD|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD|x86.Build.0 = Debug_MD|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug|x64.ActiveCfg = Debug|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug|x64.Build.0 = Debug|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug|x86.ActiveCfg = Debug|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug|x86.Build.0 = Debug|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_DLL|x64.ActiveCfg = Release_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_DLL|x64.Build.0 = Release_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_DLL|x86.ActiveCfg = Release_DLL|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_DLL|x86.Build.0 = Release_DLL|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD_DLL|x64.ActiveCfg = Release_MD_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD_DLL|x64.Build.0 = Release_MD_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD_DLL|x86.ActiveCfg = Release_MD_DLL|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD_DLL|x86.Build.0 = Release_MD_DLL|Win32 {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD|x64.ActiveCfg = Release_MD|x64 {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD|x64.Build.0 = Release_MD|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD|x86.ActiveCfg = Release_MD|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD|x86.Build.0 = Release_MD|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release|x64.ActiveCfg = Release|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release|x64.Build.0 = Release|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release|x86.ActiveCfg = Release|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/examples/sdk_example1/sdk_example1.vcxproj b/examples/sdk_example1/sdk_example1.vcxproj index c015f06cd..b536615cd 100644 --- a/examples/sdk_example1/sdk_example1.vcxproj +++ b/examples/sdk_example1/sdk_example1.vcxproj @@ -327,7 +327,7 @@ NotUsing Level3 Disabled - NO_LEGACY_BEHAVIOR;BUILD_MTd;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MTd;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDebug $(IntDir)vc$(PlatformToolsetVersion).pdb @@ -346,7 +346,7 @@ NotUsing Level3 Disabled - NO_LEGACY_BEHAVIOR;BUILD_MDd;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MDd;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDebugDLL $(IntDir)vc$(PlatformToolsetVersion).pdb @@ -366,7 +366,7 @@ Level3 Disabled - NO_LEGACY_BEHAVIOR;BUILD_MTd;WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MTd;WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDebug @@ -387,7 +387,7 @@ Level3 Disabled - NO_LEGACY_BEHAVIOR;BUILD_MDd;WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MDd;WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDebugDLL @@ -410,7 +410,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MT;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MT;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreaded @@ -431,7 +431,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MT_DLL;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MT_DLL;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreaded @@ -452,7 +452,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MTd_DLL;WIN32;_CONSOLE;_DEBUG;%(PreprocessorDefinitions) + BUILD_MTd_DLL;WIN32;_CONSOLE;_DEBUG;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDebug @@ -473,7 +473,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MD;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MD;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDLL @@ -494,7 +494,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MD_DLL;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MD_DLL;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDLL @@ -515,7 +515,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MDd_DLL;WIN32;_CONSOLE;_DEBUG;%(PreprocessorDefinitions) + BUILD_MDd_DLL;WIN32;_CONSOLE;_DEBUG;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDebugDLL @@ -536,7 +536,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MT;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MT;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreaded @@ -561,7 +561,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MT_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MT_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreaded @@ -586,7 +586,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MTd_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MTd_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDebug @@ -611,7 +611,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MD;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MD;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDLL @@ -636,7 +636,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MD_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MD_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDLL @@ -661,7 +661,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MDd_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MDd_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDebugDLL diff --git a/examples/sdk_example2/cmakelists.txt b/examples/sdk_example2/cmakelists.txt index d0287e076..24524457e 100644 --- a/examples/sdk_example2/cmakelists.txt +++ b/examples/sdk_example2/cmakelists.txt @@ -22,7 +22,7 @@ target_include_directories(sdk_example2 PUBLIC target_link_libraries(sdk_example2 PRIVATE dds_helper - CompressonatorLIB + CMP_Compressonator ) set_target_properties(sdk_example2 PROPERTIES FOLDER ${FOLDER_NAME}) diff --git a/examples/sdk_example2/sdk_example2.cpp b/examples/sdk_example2/sdk_example2.cpp index d346bbb90..9391f5c8b 100644 --- a/examples/sdk_example2/sdk_example2.cpp +++ b/examples/sdk_example2/sdk_example2.cpp @@ -69,7 +69,7 @@ bool g_bAbortCompression = false; // If set true current compression will abor // Sample loop back code called for each compression block been processed //--------------------------------------------------------------------------- bool CompressionCallback(float fProgress, CMP_DWORD_PTR pUser1, CMP_DWORD_PTR pUser2) { - std::printf("\rCompression progress = %2.0f ", fProgress); + std::printf("\rCompression progress = %3.0f ", fProgress); return g_bAbortCompression; } diff --git a/examples/sdk_example2/sdk_example2.sln b/examples/sdk_example2/sdk_example2.sln index adb09a482..9d0b9dded 100644 --- a/examples/sdk_example2/sdk_example2.sln +++ b/examples/sdk_example2/sdk_example2.sln @@ -7,14 +7,56 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Example2", "SDK_Example2.vc EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug_DLL|x64 = Debug_DLL|x64 + Debug_DLL|x86 = Debug_DLL|x86 + Debug_MD_DLL|x64 = Debug_MD_DLL|x64 + Debug_MD_DLL|x86 = Debug_MD_DLL|x86 Debug_MD|x64 = Debug_MD|x64 + Debug_MD|x86 = Debug_MD|x86 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release_DLL|x64 = Release_DLL|x64 + Release_DLL|x86 = Release_DLL|x86 + Release_MD_DLL|x64 = Release_MD_DLL|x64 + Release_MD_DLL|x86 = Release_MD_DLL|x86 Release_MD|x64 = Release_MD|x64 + Release_MD|x86 = Release_MD|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_DLL|x64.ActiveCfg = Debug_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_DLL|x64.Build.0 = Debug_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_DLL|x86.ActiveCfg = Debug_DLL|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_DLL|x86.Build.0 = Debug_DLL|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD_DLL|x64.ActiveCfg = Debug_MD_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD_DLL|x64.Build.0 = Debug_MD_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD_DLL|x86.ActiveCfg = Debug_MD_DLL|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD_DLL|x86.Build.0 = Debug_MD_DLL|Win32 {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD|x64.ActiveCfg = Debug_MD|x64 {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD|x64.Build.0 = Debug_MD|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD|x86.ActiveCfg = Debug_MD|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD|x86.Build.0 = Debug_MD|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug|x64.ActiveCfg = Debug|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug|x64.Build.0 = Debug|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug|x86.ActiveCfg = Debug|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug|x86.Build.0 = Debug|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_DLL|x64.ActiveCfg = Release_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_DLL|x64.Build.0 = Release_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_DLL|x86.ActiveCfg = Release_DLL|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_DLL|x86.Build.0 = Release_DLL|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD_DLL|x64.ActiveCfg = Release_MD_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD_DLL|x64.Build.0 = Release_MD_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD_DLL|x86.ActiveCfg = Release_MD_DLL|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD_DLL|x86.Build.0 = Release_MD_DLL|Win32 {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD|x64.ActiveCfg = Release_MD|x64 {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD|x64.Build.0 = Release_MD|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD|x86.ActiveCfg = Release_MD|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD|x86.Build.0 = Release_MD|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release|x64.ActiveCfg = Release|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release|x64.Build.0 = Release|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release|x86.ActiveCfg = Release|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/examples/sdk_example2/sdk_example2.vcxproj b/examples/sdk_example2/sdk_example2.vcxproj index 93ec20df4..b26b045a4 100644 --- a/examples/sdk_example2/sdk_example2.vcxproj +++ b/examples/sdk_example2/sdk_example2.vcxproj @@ -327,7 +327,7 @@ NotUsing Level3 Disabled - NO_LEGACY_BEHAVIOR;BUILD_MTd;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MTd;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDebug $(IntDir)vc$(PlatformToolsetVersion).pdb @@ -346,7 +346,7 @@ NotUsing Level3 Disabled - NO_LEGACY_BEHAVIOR;BUILD_MDd;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MDd;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDebugDLL $(IntDir)vc$(PlatformToolsetVersion).pdb @@ -366,7 +366,7 @@ Level3 Disabled - NO_LEGACY_BEHAVIOR;BUILD_MTd;WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MTd;WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDebug @@ -387,7 +387,7 @@ Level3 Disabled - NO_LEGACY_BEHAVIOR;BUILD_MDd;WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MDd;WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDebugDLL @@ -410,7 +410,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MT;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MT;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreaded @@ -431,7 +431,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MT_DLL;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MT_DLL;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreaded @@ -452,7 +452,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MTd_DLL;WIN32;_CONSOLE;_DEBUG;%(PreprocessorDefinitions) + BUILD_MTd_DLL;WIN32;_CONSOLE;_DEBUG;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDebug @@ -473,7 +473,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MD;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MD;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDLL @@ -494,7 +494,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MD_DLL;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MD_DLL;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDLL @@ -515,7 +515,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MDd_DLL;WIN32;_CONSOLE;_DEBUG;%(PreprocessorDefinitions) + BUILD_MDd_DLL;WIN32;_CONSOLE;_DEBUG;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDebugDLL @@ -536,7 +536,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MT;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MT;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreaded @@ -561,7 +561,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MT_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MT_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreaded @@ -586,7 +586,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MTd_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MTd_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDebug @@ -611,7 +611,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MD;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MD;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDLL @@ -636,7 +636,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MD_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MD_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDLL @@ -661,7 +661,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MDd_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MDd_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDebugDLL diff --git a/examples/sdk_example3/cmakelists.txt b/examples/sdk_example3/cmakelists.txt index d5b68a6aa..5c971f709 100644 --- a/examples/sdk_example3/cmakelists.txt +++ b/examples/sdk_example3/cmakelists.txt @@ -21,7 +21,7 @@ target_include_directories(sdk_example3 PUBLIC target_link_libraries(sdk_example3 PRIVATE dds_helper - CompressonatorLIB + CMP_Compressonator ) set_target_properties(sdk_example3 PROPERTIES FOLDER ${FOLDER_NAME}) \ No newline at end of file diff --git a/examples/sdk_example3/sdk_example3.cpp b/examples/sdk_example3/sdk_example3.cpp index 9aff425ef..cf82b3fe0 100644 --- a/examples/sdk_example3/sdk_example3.cpp +++ b/examples/sdk_example3/sdk_example3.cpp @@ -61,7 +61,7 @@ bool CompressionCallback(float fProgress, CMP_DWORD_PTR pUser1, CMP_DWORD_PTR pU UNREFERENCED_PARAMETER(pUser1); UNREFERENCED_PARAMETER(pUser2); - std::printf("\rCompression progress = %2.0f ", fProgress); + std::printf("\rCompression progress = %3.0f ", fProgress); return g_bAbortCompression; } @@ -214,7 +214,7 @@ int main(int argc, const char* argv[]) { // Show Progress float fProgress = 100.f * (yBlock * dwBlocksX) / dwBlocksXY; - std::printf("\rCompression progress = %2.0f", fProgress); + std::printf("\rCompression progress = %3.0f", fProgress); } } diff --git a/examples/sdk_example3/sdk_example3.sln b/examples/sdk_example3/sdk_example3.sln index 06d323ffb..03b786f1f 100644 --- a/examples/sdk_example3/sdk_example3.sln +++ b/examples/sdk_example3/sdk_example3.sln @@ -7,14 +7,56 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Example3", "SDK_Example3.vc EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug_DLL|x64 = Debug_DLL|x64 + Debug_DLL|x86 = Debug_DLL|x86 + Debug_MD_DLL|x64 = Debug_MD_DLL|x64 + Debug_MD_DLL|x86 = Debug_MD_DLL|x86 Debug_MD|x64 = Debug_MD|x64 + Debug_MD|x86 = Debug_MD|x86 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release_DLL|x64 = Release_DLL|x64 + Release_DLL|x86 = Release_DLL|x86 + Release_MD_DLL|x64 = Release_MD_DLL|x64 + Release_MD_DLL|x86 = Release_MD_DLL|x86 Release_MD|x64 = Release_MD|x64 + Release_MD|x86 = Release_MD|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_DLL|x64.ActiveCfg = Debug_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_DLL|x64.Build.0 = Debug_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_DLL|x86.ActiveCfg = Debug_DLL|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_DLL|x86.Build.0 = Debug_DLL|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD_DLL|x64.ActiveCfg = Debug_MD_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD_DLL|x64.Build.0 = Debug_MD_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD_DLL|x86.ActiveCfg = Debug_MD_DLL|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD_DLL|x86.Build.0 = Debug_MD_DLL|Win32 {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD|x64.ActiveCfg = Debug_MD|x64 {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD|x64.Build.0 = Debug_MD|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD|x86.ActiveCfg = Debug_MD|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug_MD|x86.Build.0 = Debug_MD|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug|x64.ActiveCfg = Debug|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug|x64.Build.0 = Debug|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug|x86.ActiveCfg = Debug|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Debug|x86.Build.0 = Debug|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_DLL|x64.ActiveCfg = Release_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_DLL|x64.Build.0 = Release_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_DLL|x86.ActiveCfg = Release_DLL|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_DLL|x86.Build.0 = Release_DLL|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD_DLL|x64.ActiveCfg = Release_MD_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD_DLL|x64.Build.0 = Release_MD_DLL|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD_DLL|x86.ActiveCfg = Release_MD_DLL|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD_DLL|x86.Build.0 = Release_MD_DLL|Win32 {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD|x64.ActiveCfg = Release_MD|x64 {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD|x64.Build.0 = Release_MD|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD|x86.ActiveCfg = Release_MD|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release_MD|x86.Build.0 = Release_MD|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release|x64.ActiveCfg = Release|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release|x64.Build.0 = Release|x64 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release|x86.ActiveCfg = Release|Win32 + {B06F1D70-67F1-4491-B479-1B98175768E6}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/examples/sdk_example3/sdk_example3.vcxproj b/examples/sdk_example3/sdk_example3.vcxproj index a6ecb10d2..7bed84ed6 100644 --- a/examples/sdk_example3/sdk_example3.vcxproj +++ b/examples/sdk_example3/sdk_example3.vcxproj @@ -327,7 +327,7 @@ NotUsing Level3 Disabled - NO_LEGACY_BEHAVIOR;BUILD_MTd;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MTd;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDebug $(IntDir)vc$(PlatformToolsetVersion).pdb @@ -346,7 +346,7 @@ NotUsing Level3 Disabled - NO_LEGACY_BEHAVIOR;BUILD_MDd;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MDd;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDebugDLL $(IntDir)vc$(PlatformToolsetVersion).pdb @@ -366,7 +366,7 @@ Level3 Disabled - NO_LEGACY_BEHAVIOR;BUILD_MTd;WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MTd;WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDebug @@ -387,7 +387,7 @@ Level3 Disabled - NO_LEGACY_BEHAVIOR;BUILD_MDd;WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MDd;WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDebugDLL @@ -410,7 +410,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MT;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MT;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreaded @@ -431,7 +431,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MT_DLL;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MT_DLL;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreaded @@ -452,7 +452,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MTd_DLL;WIN32;_CONSOLE;_DEBUG;%(PreprocessorDefinitions) + BUILD_MTd_DLL;WIN32;_CONSOLE;_DEBUG;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDebug @@ -473,7 +473,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MD;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MD;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDLL @@ -494,7 +494,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MD_DLL;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MD_DLL;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDLL @@ -515,7 +515,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MDd_DLL;WIN32;_CONSOLE;_DEBUG;%(PreprocessorDefinitions) + BUILD_MDd_DLL;WIN32;_CONSOLE;_DEBUG;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDebugDLL @@ -536,7 +536,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MT;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MT;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreaded @@ -561,7 +561,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MT_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MT_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreaded @@ -586,7 +586,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MTd_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MTd_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDebug @@ -611,7 +611,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MD;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MD;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDLL @@ -636,7 +636,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MD_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MD_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDLL @@ -661,7 +661,7 @@ MaxSpeed true true - NO_LEGACY_BEHAVIOR;BUILD_MDd_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BUILD_MDd_DLL;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../common;../../include;../../CMP_CompressonatorLib;$(COMPRESSONATOR_ROOT)/SDK/include/;$(COMPRESSONATOR_ROOT)/CMP_CompressonatorLib;../../ MultiThreadedDebugDLL diff --git a/external/cmakelists.txt b/external/cmakelists.txt index 6a8ff6aa3..43ed291ce 100644 --- a/external/cmakelists.txt +++ b/external/cmakelists.txt @@ -10,9 +10,13 @@ include(${CMAKE_CURRENT_LIST_DIR}/opengl/CMakeLists.txt) include(${CMAKE_CURRENT_LIST_DIR}/qt5/CMakeLists.txt) include(${CMAKE_CURRENT_LIST_DIR}/rapidxml/CMakeLists.txt) include(${CMAKE_CURRENT_LIST_DIR}/vulkan/CMakeLists.txt) -include(${CMAKE_CURRENT_LIST_DIR}/ktx/CMakeLists.txt) include(${CMAKE_CURRENT_LIST_DIR}/glfw/CMakeLists.txt) +# Enable KTX2 features +if(${CMAKE_VERSION} VERSION_GREATER "3.14.0") + include(${CMAKE_CURRENT_LIST_DIR}/ktx/CMakeLists.txt) +endif() + list(APPEND CMAKE_FIND_ROOT_PATH ${DEPENDENCIES_INSTALL_DIR}) #aqppend know paths to cmake search path diff --git a/external/glm/cmakelists.txt b/external/glm/cmakelists.txt index 62e44ac83..86e664cf4 100644 --- a/external/glm/cmakelists.txt +++ b/external/glm/cmakelists.txt @@ -13,6 +13,8 @@ target_include_directories(ExtGLEW INTERFACE ${PROJECT_SOURCE_DIR}/../common/lib/ext/glm ) -target_link_libraries(ExtGLM INTERFACE - glm::glm -) + +# Enable this for newer GLM libs (using older version 0.9.8.0 is been used) +# target_link_libraries(ExtGLM INTERFACE +# glm::glm +# ) diff --git a/external/opencv/cmakelists.txt b/external/opencv/cmakelists.txt index 529b949c7..d359e3622 100644 --- a/external/opencv/cmakelists.txt +++ b/external/opencv/cmakelists.txt @@ -8,208 +8,11 @@ set(ExtOpenCV_BIN_PATH ${PROJECT_SOURCE_DIR}/../common/lib/ext/opencv/2.49/x64/V #set_property(GLOBAL PROPERTY ExtOpenCV_BIN_PATH "${ExtOpenCV_BIN_PATH}") #list(APPEND CMAKE_FIND_ROOT_PATH ${ExtOpenCV_BIN_PATH}) +if (CMP_HOST_WINDOWS) + set(OpenCV_INCLUDE_DIRS ${CMAKE_CURRENT_LIST_DIR}/../../../common/lib/ext/opencv/2.49/include) +endif() + target_include_directories(ExtOpenCV INTERFACE ${CMAKE_CURRENT_LIST_DIR}/../../../common/lib/ext/opencv/2.49/include ) -# if(POLICY CMP0091) -# cmake_policy(SET CMP0091 NEW) -# endif() -# -# project(CompressonatorOpenCV) -# -# set (OPENCV_CONFIG_FILE_INCLUDE_DIR ${CMAKE_CURRENT_LIST_DIR}/opencv2/build) -# set (PROJECT_BINARY_DIR ${CMAKE_CURRENT_LIST_DIR}/opencv2/build) -# set (PROJECT_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/opencv2/) -# -# # =========== OPENCV v2.9.0 options -# if(NOT DEFINED CMAKE_SUPPRESS_DEVELOPER_WARNINGS) -# set(CMAKE_SUPPRESS_DEVELOPER_WARNINGS 1 CACHE INTERNAL "No dev warnings") -# endif() -# -# #Create build rules for OpenCV Documentation -# option( BUILD_DOCS OFF) -# -# #Build all examples -# option( BUILD_EXAMPLES OFF) -# -# #Enables 'make package_source' command -# option( BUILD_PACKAGE OFF) -# -# #Build performance tests -# option( BUILD_PERF_TESTS OFF) -# -# #Build shared libraries (.dll/.so) instead of static ones (.lib/.a) -# option( BUILD_SHARED_LIBS OFF) -# -# # #Download and build TBB from source -# # option( BUILD_TBB OFF) -# -# #Build accuracy & regression tests -# option( BUILD_TESTS OFF) -# -# # #Include debug info into debug libs (not MSCV only) -# # option( BUILD_WITH_DEBUG_INFO OFF) -# -# # #Enables use of staticaly linked CRT for staticaly linked OpenCV -# # option( BUILD_WITH_STATIC_CRT OFF) -# -# # ================ Media I/O ===================== -# -# #Build libjpeg from source -# option( BUILD_JPEG OFF) -# -# #Build openexr from source -# option( BUILD_OPENEXR OFF) -# -# #Build libpng from source -# option( BUILD_PNG OFF) -# -# #Build libtiff from source -# option( BUILD_TIFF OFF) -# -# #Include JPEG2K support -# option( WITH_JASPER OFF) -# -# #Include JPEG support -# option( WITH_JPEG OFF) -# -# -# option( BUILD_opencv_calib3d OFF) -# option( BUILD_opencv_contrib OFF) -# option( BUILD_opencv_features2d OFF) -# option( BUILD_opencv_flann OFF) -# option( BUILD_opencv_gpu OFF) -# option( BUILD_opencv_legacy OFF) -# option( BUILD_opencv_ml OFF) -# option( BUILD_opencv_nonfree OFF) -# option( BUILD_opencv_objdetect OFF) -# option( BUILD_opencv_ocl OFF) -# option( BUILD_opencv_photo OFF) -# option( BUILD_opencv_stitching OFF) -# option( BUILD_opencv_superres OFF) -# option( BUILD_opencv_ts OFF) -# option( BUILD_opencv_video OFF) -# option( BUILD_opencv_videostab OFF) -# -# option( BUILD_LIST "core, highgui, imgproc") -# -# #Use precompiled headers -# option( ENABLE_PRECOMPILED_HEADERS OFF) -# -# #Build with Win32 UI Backend support -# option( WITH_WIN32UI OFF) -# -# -# -# # ================ Video I/O ===================== -# #Include Video for Windows support -# option(WITH_VFW OFF) -# -# #Build HighGUI with DirectShow support -# option( WITH_DSHOW OFF) -# -# #Include FFMPEG support -# option( WITH_FFMPEG OFF) -# -# # ================ third party ===================== -# #Include OpenCL Runtime support -# option( WITH_OPENCL OFF) -# -# -# # ================ Current Source and build folders, other then that of root CMakeList ===================== -# unset(OpenCV_DIR CACHE) -# -# -# # is the directory where the compiled or generated files from the current CMakeLists.txt will go to -# set (CMAKE_CURRENT_BINARY_DIR ${CMAKE_CURRENT_LIST_DIR}/opencv2/build) -# -# # this is the directory where the currently processed CMakeLists.txt is located in -# set (CMAKE_CURRENT_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/opencv2 ) -# -# #Installation Directory -# set( CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_LIST_DIR}/opencv2/build/install) -# -# #Output directory for applications -# set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_LIST_DIR}/opencv2/build/bin) -# -# set( CMAKE_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/opencv2) -# set( CMAKE_BINARY_DIR ${CMAKE_CURRENT_LIST_DIR}/opencv2/build) -# -# # ================ OpenCV CMakeList.txt location ===================== -# add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/opencv2) -# -# # CMakeLists.txt -# message(STATUS "(1:1)---------------------- OpenCV CmakeList for ${CMAKE_CURRENT_LIST_DIR}" ) -# -# # if you are building in-source, this is the same as CMAKE_SOURCE_DIR, otherwise -# # this is the top level directory of your build tree -# message( STATUS "CMAKE_BINARY_DIR: " ${CMAKE_BINARY_DIR} ) -# -# # if you are building in-source, this is the same as CMAKE_CURRENT_SOURCE_DIR, otherwise this -# # is the directory where the compiled or generated files from the current CMakeLists.txt will go to -# message( STATUS "CMAKE_CURRENT_BINARY_DIR: " ${CMAKE_CURRENT_BINARY_DIR} ) -# -# # this is the directory, from which cmake was started, i.e. the top level source directory -# message( STATUS "CMAKE_SOURCE_DIR: " ${CMAKE_SOURCE_DIR} ) -# -# # this is the directory where the currently processed CMakeLists.txt is located in -# message( STATUS "CMAKE_CURRENT_SOURCE_DIR: " ${CMAKE_CURRENT_SOURCE_DIR} ) -# -# # contains the full path to the top level directory of your build tree -# message( STATUS "PROJECT_BINARY_DIR: " ${PROJECT_BINARY_DIR} ) -# -# # contains the full path to the root of your project source directory, -# # i.e. to the nearest directory where CMakeLists.txt contains the PROJECT() command -# message( STATUS "PROJECT_SOURCE_DIR: " ${PROJECT_SOURCE_DIR} ) -# -# # set this variable to specify a common place where CMake should put all executable files -# # (instead of CMAKE_CURRENT_BINARY_DIR) -# message( STATUS "EXECUTABLE_OUTPUT_PATH: " ${EXECUTABLE_OUTPUT_PATH} ) -# -# # set this variable to specify a common place where CMake should put all libraries -# # (instead of CMAKE_CURRENT_BINARY_DIR) -# message( STATUS "LIBRARY_OUTPUT_PATH: " ${LIBRARY_OUTPUT_PATH} ) -# -# # tell CMake to search first in directories listed in CMAKE_MODULE_PATH -# # when you use FIND_PACKAGE() or INCLUDE() -# message( STATUS "CMAKE_MODULE_PATH: " ${CMAKE_MODULE_PATH} ) -# -# # this is the complete path of the cmake which runs currently (e.g. /usr/local/bin/cmake) -# message( STATUS "CMAKE_COMMAND: " ${CMAKE_COMMAND} ) -# -# # this is the CMake installation directory -# message( STATUS "CMAKE_ROOT: " ${CMAKE_ROOT} ) -# -# # this is the filename including the complete path of the file where this variable is used. -# message( STATUS "CMAKE_CURRENT_LIST_FILE: " ${CMAKE_CURRENT_LIST_FILE} ) -# -# # this is linenumber where the variable is used -# message( STATUS "CMAKE_CURRENT_LIST_LINE: " ${CMAKE_CURRENT_LIST_LINE} ) -# -# # this is used when searching for include files e.g. using the FIND_PATH() command. -# message( STATUS "CMAKE_INCLUDE_PATH: " ${CMAKE_INCLUDE_PATH} ) -# -# # this is used when searching for libraries e.g. using the FIND_LIBRARY() command. -# message( STATUS "CMAKE_LIBRARY_PATH: " ${CMAKE_LIBRARY_PATH} ) -# -# # the complete system name, e.g. "Linux-2.4.22", "FreeBSD-5.4-RELEASE" or "Windows 5.1" -# message( STATUS "CMAKE_SYSTEM: " ${CMAKE_SYSTEM} ) -# -# # the short system name, e.g. "Linux", "FreeBSD" or "Windows" -# message( STATUS "CMAKE_SYSTEM_NAME: " ${CMAKE_SYSTEM_NAME} ) -# -# # only the version part of CMAKE_SYSTEM -# message( STATUS "CMAKE_SYSTEM_VERSION: " ${CMAKE_SYSTEM_VERSION} ) -# -# # the processor name (e.g. "Intel(R) Pentium(R) M processor 2.00GHz") -# message( STATUS "CMAKE_SYSTEM_PROCESSOR: " ${CMAKE_SYSTEM_PROCESSOR} ) -# -# # is TRUE on all UNIX-like OS's, including Apple OS X and CygWin -# message( STATUS "UNIX: " ${UNIX} ) -# -# # is TRUE on Windows, including CygWin -# message( STATUS "WIN32: " ${WIN32} ) -# -# include(${CMAKE_CURRENT_LIST_DIR}/opencv-include.cmake) - diff --git a/external/openexr/cmakelists.txt b/external/openexr/cmakelists.txt index ead31328a..56c7f772a 100644 --- a/external/openexr/cmakelists.txt +++ b/external/openexr/cmakelists.txt @@ -6,12 +6,19 @@ message(STATUS "++++++++++++++++++External OpenEXR CMakeList called" ) add_library(ExtOpenEXR INTERFACE) if (CMP_HOST_WINDOWS) -target_include_directories(ExtOpenEXR INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/../../../common/lib/ext/openexr/openexr-2.2.0/VS2015/x64/include/OpenEXR - ${CMAKE_CURRENT_LIST_DIR}/../../../common/lib/ext/openexr/ilmbase-2.2.0/VS2015/x64/include/OpenEXR -) + target_include_directories(ExtOpenEXR INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/../../../common/lib/ext/openexr/openexr-2.2.0/VS2015/x64/include/OpenEXR + ${CMAKE_CURRENT_LIST_DIR}/../../../common/lib/ext/openexr/ilmbase-2.2.0/VS2015/x64/include/OpenEXR + ) + set(OpenEXR_INCLUDE_DIRS + ${CMAKE_CURRENT_LIST_DIR}/../../../common/lib/ext/openexr/openexr-2.2.0/VS2015/x64/include/OpenEXR + ${CMAKE_CURRENT_LIST_DIR}/../../../common/lib/ext/openexr/ilmbase-2.2.0/VS2015/x64/include/OpenEXR + ) else() -target_include_directories(ExtOpenEXR INTERFACE ${OpenEXR_INCLUDE_DIRS}) + set(OpenEXR_INCLUDE_DIRS + /usr/include/OpenEXR + ) + target_include_directories(ExtOpenEXR INTERFACE ${OpenEXR_INCLUDE_DIRS}) endif() diff --git a/installer/amdcompresscli_64.aip b/installer/amdcompresscli_64.aip index b81694783..b72fe7527 100644 --- a/installer/amdcompresscli_64.aip +++ b/installer/amdcompresscli_64.aip @@ -1,5 +1,8 @@ - + + + + @@ -33,15 +36,13 @@ + + + + - - - - - - - + @@ -49,8 +50,9 @@ - - + + + @@ -63,9 +65,12 @@ + + + @@ -82,8 +87,10 @@ + + - + @@ -93,32 +100,42 @@ - - + + + + + + - - - - + + + + + + + + + + + - - - - + + + @@ -145,9 +162,13 @@ - - - + + + + + + + @@ -159,72 +180,49 @@ - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - - - + + - - - - + + + - - - + + + + + + - @@ -233,36 +231,65 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + - + @@ -273,6 +300,9 @@ + + + @@ -304,17 +334,12 @@ - - - - - - - - + + + + - - + @@ -351,7 +376,9 @@ - + + + @@ -449,28 +476,34 @@ - - + + + - - + + + + + - + + - + + - + @@ -491,15 +524,14 @@ - - + + - @@ -513,6 +545,9 @@ + + + @@ -521,6 +556,7 @@ + @@ -579,6 +615,9 @@ + + + @@ -589,6 +628,7 @@ + @@ -596,6 +636,7 @@ + @@ -622,34 +663,40 @@ - + - + - + + + - - + + - + + + + + @@ -679,8 +726,8 @@ - - + + @@ -698,6 +745,17 @@ + + + + + + + + + + + diff --git a/installer/amdcompresscore_64.aip b/installer/amdcompresscore_64.aip index 286f974f9..de2bf2d41 100644 --- a/installer/amdcompresscore_64.aip +++ b/installer/amdcompresscore_64.aip @@ -1,5 +1,8 @@ - + + + + @@ -32,6 +35,10 @@ + + + + @@ -64,11 +71,11 @@ - + - + @@ -93,8 +100,8 @@ - - + + @@ -122,10 +129,10 @@ - + @@ -135,7 +142,6 @@ - @@ -235,6 +241,7 @@ + @@ -261,7 +268,7 @@ - + @@ -277,18 +284,22 @@ + - - + + + + + diff --git a/installer/amdcompressframework_64.aip b/installer/amdcompressframework_64.aip index 48df19e02..89d6d1ed6 100644 --- a/installer/amdcompressframework_64.aip +++ b/installer/amdcompressframework_64.aip @@ -1,5 +1,8 @@ - + + + + @@ -32,6 +35,10 @@ + + + + @@ -44,7 +51,6 @@ - @@ -75,13 +81,15 @@ - + + + + + + - - - @@ -90,7 +98,7 @@ - + @@ -100,7 +108,7 @@ - + @@ -152,22 +160,20 @@ - - - - - - + + + + + + + - - - @@ -185,7 +191,7 @@ - + @@ -195,7 +201,6 @@ - @@ -296,6 +301,7 @@ + @@ -322,7 +328,7 @@ - + @@ -338,18 +344,22 @@ + - - + + + + + diff --git a/installer/amdcompressgui_64.aip b/installer/amdcompressgui_64.aip index 5ba7c306f..5a5d619e4 100644 --- a/installer/amdcompressgui_64.aip +++ b/installer/amdcompressgui_64.aip @@ -1,5 +1,8 @@ - + + + + @@ -34,29 +37,27 @@ + + + + - - + - - - - - - + - - - - - - - - - + + + + + + + + + @@ -65,37 +66,39 @@ - - + + - - + + + + + - - - + + - + - - + + + + - - - + + - - - + + @@ -103,169 +106,135 @@ - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - + + - - - - - - - - - - - - - - - - + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - + - + + + + + - + @@ -285,21 +254,25 @@ + + - + - + - - - - - + + + + + + + @@ -310,33 +283,38 @@ - + - + - + + + - - + - - + + + + + + + - + - - - + + @@ -368,17 +346,12 @@ - - - - - - - - + + + + - - + @@ -415,7 +388,9 @@ - + + + @@ -474,117 +449,125 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + - - - - - - + + + + + + + - - - + + + + + + + + - + - - - - - - + - - - + + - - - - - + + + + + - + - + - + - + - - - - + + + + + - + + + - - + + + + - @@ -598,6 +581,9 @@ + + + @@ -606,6 +592,7 @@ + @@ -615,13 +602,8 @@ - - - - - @@ -669,9 +651,9 @@ - - - + + + @@ -684,24 +666,20 @@ - - + - - - @@ -709,13 +687,12 @@ - - + @@ -723,7 +700,7 @@ - + @@ -733,31 +710,30 @@ - + - + - - + + - - - - + + + @@ -770,8 +746,6 @@ - - @@ -789,8 +763,8 @@ - - + + @@ -809,15 +783,14 @@ - - - + + + - - - - - + + + + diff --git a/installer/amdcompresssdk_64.aip b/installer/amdcompresssdk_64.aip index 6e56babfa..721998c5e 100644 --- a/installer/amdcompresssdk_64.aip +++ b/installer/amdcompresssdk_64.aip @@ -1,5 +1,8 @@ - + + + + @@ -32,6 +35,10 @@ + + + + @@ -132,7 +139,6 @@ - @@ -233,6 +239,7 @@ + @@ -259,7 +266,7 @@ - + @@ -275,18 +282,22 @@ + - - + + + + + diff --git a/license/license.txt b/license/license.txt index 929068f4e..982ce4b95 100644 --- a/license/license.txt +++ b/license/license.txt @@ -1,4 +1,4 @@ -Copyright (c) 2019 Advanced Micro Devices, Inc. All rights reserved. +Copyright (c) 2020 Advanced Micro Devices, Inc. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"), to deal diff --git a/runtime/ktx.dll b/runtime/ktx.dll new file mode 100644 index 000000000..e69de29bb diff --git a/scripts/buildcli_linux_package.sh b/scripts/buildcli_linux_package.sh new file mode 100644 index 000000000..770a5c07a --- /dev/null +++ b/scripts/buildcli_linux_package.sh @@ -0,0 +1,97 @@ +#please make sure all the prerequite packages - initsetup_ubuntu.sh are installed and build-buildCLI_ubuntu_cmake.sh is run successfully before running this script. +set -x +set -e + +cd $WORKSPACE/compressonator + +rm -rf compressonatorcli_linux_x86_64_4.1* +mkdir compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER +mkdir compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/documents +mkdir compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/images +mkdir compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/license +mkdir compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/qt +mkdir compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs + +cp scripts/compressonatorcli compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER +cp bin/compressonatorcli-bin compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER + +cp -r docs/build/html compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/documents +cp -r runtime/images compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER +cp license/license.txt compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/license + +# Qt +cp $QT_ROOT/lib/libQt5Core.so* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/qt +cp $QT_ROOT/lib/libQt5Core.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/qt +cp $QT_ROOT/lib/libQt5Gui.so* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/qt +cp $QT_ROOT/lib/libQt5Gui.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/qt +cp $QT_ROOT/lib/libicui18n.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/qt +cp $QT_ROOT/lib/libicuuc.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/qt +cp $QT_ROOT/lib/libicudata.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/qt +cp $QT_ROOT/plugins/imageformats/* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/qt + +# OpenCV +cp /usr/lib/x86_64-linux-gnu/libopencv_core.so compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +cp /usr/lib/x86_64-linux-gnu/libopencv_core.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +cp /usr/lib/x86_64-linux-gnu/libopencv_highgui.so compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +cp /usr/lib/x86_64-linux-gnu/libopencv_highgui.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +cp /usr/lib/x86_64-linux-gnu/libopencv_imgproc.so compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +cp /usr/lib/x86_64-linux-gnu/libopencv_imgproc.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs + +# Misc +cp /usr/lib/x86_64-linux-gnu/libtbb.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs + +# Optional User pkg update +cp scripts/initsetup_ubuntu.sh compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs + +# GLEW if needed +# cp /usr/lib/x86_64-linux-gnu/libGLEW.so compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +# cp /usr/lib/x86_64-linux-gnu/libGLEW.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs + +# OpenEXR if needed +cp /usr/local/ilmbase22build/lib/libHalf.so compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +cp /usr/local/ilmbase22build/lib/libHalf.la compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +cp /usr/local/ilmbase22build/lib/libHalf.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +cp /usr/local/ilmbase22build/lib/libIex-2_2.la compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +cp /usr/local/ilmbase22build/lib/libIex-2_2.so compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +cp /usr/local/ilmbase22build/lib/libIex-2_2.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +cp /usr/local/ilmbase22build/lib/libIexMath-2_2.la compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +cp /usr/local/ilmbase22build/lib/libIexMath-2_2.so compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +cp /usr/local/ilmbase22build/lib/libIexMath-2_2.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +cp /usr/local/ilmbase22build/lib/libImath-2_2.la compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +cp /usr/local/ilmbase22build/lib/libImath-2_2.so compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +cp /usr/local/ilmbase22build/lib/libImath-2_2.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +cp /usr/local/ilmbase22build/lib/libIlmThread-2_2.la compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +cp /usr/local/ilmbase22build/lib/libIlmThread-2_2.so compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +cp /usr/local/ilmbase22build/lib/libIlmThread-2_2.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +cp /usr/local/openexr22build/lib/libIlmImf-2_2.so compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +cp /usr/local/openexr22build/lib/libIlmImf-2_2.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs + + +# Misc runtime enable if needed in pkglibs +# cp /usr/lib/x86_64-linux-gnu/libwebp.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +# cp /usr/lib/x86_64-linux-gnu/libvpx.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +# cp /usr/lib/x86_64-linux-gnu/libdc1394.so* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +# cp /usr/lib/x86_64-linux-gnu/libva.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +# cp /usr/lib/x86_64-linux-gnu/libzvbi.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +# cp /usr/lib/x86_64-linux-gnu/libxvidcore.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +# cp /usr/lib/x86_64-linux-gnu/libx265.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +# cp /usr/lib/x86_64-linux-gnu/libx264.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +# cp /usr/lib/x86_64-linux-gnu/libtwolame.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +# cp /usr/lib/x86_64-linux-gnu/libsnappy.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +# cp /usr/lib/x86_64-linux-gnu/libshine.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +# cp /usr/lib/x86_64-linux-gnu/libmp3lame.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +# cp /usr/lib/x86_64-linux-gnu/libgsm.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +# cp /usr/lib/x86_64-linux-gnu/libcrystalhd.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +# cp /usr/lib/x86_64-linux-gnu/libssh-gcrypt.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +# cp /usr/lib/x86_64-linux-gnu/libgme.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +# cp /usr/lib/x86_64-linux-gnu/libbluray.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +# cp /usr/lib/x86_64-linux-gnu/libsoxr.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +# cp /usr/lib/x86_64-linux-gnu/libstdc++.so.* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +# cp /usr/lib/x86_64-linux-gnu/libjpeg* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +# cp /usr/lib/x86_64-linux-gnu/libpng* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +# cp /usr/lib/x86_64-linux-gnu/libpangox* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +# cp /usr/lib/x86_64-linux-gnu/libpcre16* compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +# cp /usr/lib/x86_64-linux-gnu/libz.a compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs +# cp /usr/lib/x86_64-linux-gnu/libpthread.so compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER/pkglibs + +tar -zcvf compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER.tar.gz compressonatorcli_linux_x86_64_4.1.$BUILD_NUMBER diff --git a/scripts/compressonatorcli b/scripts/compressonatorcli new file mode 100644 index 000000000..d8af4a7fd --- /dev/null +++ b/scripts/compressonatorcli @@ -0,0 +1,46 @@ + +#!/bin/bash + +# ---------------------------------------------------------------------------------------------------------------------------------- +# This script launches the compressonatorcli executable after configuring the environment. +# (c) 2017 Advanced Micro Devices Inc. All Rights Reserved. +# ---------------------------------------------------------------------------------------------------------------------------------- + +if echo "$0" | grep '^/' ; then + thisScriptFullPath="$0" +else + thisScriptFullPath=`pwd`/$0 +fi + +# Enable the use of symbolic links to the script +if [ -h ${thisScriptFullPath} ] +then + LINKTARGET=`readlink -f "$thisScriptFullPath"` + thisScriptFullPath=${LINKTARGET} +fi + +CMPCLI_BIN_PATH=`dirname "$thisScriptFullPath"` + +# Add compressonatorcli's binaries directory to PATH: +export PATH="${CMPCLI_BIN_PATH}:$PATH" + +# Add compressonatorcli's binaries directory to LD_LIBRARY_PATH: +if [ -z "$LD_LIBRARY_PATH" ]; then + export LD_LIBRARY_PATH="${CMPCLI_BIN_PATH}" +else + export LD_LIBRARY_PATH="${CMPCLI_BIN_PATH}:$LD_LIBRARY_PATH" +fi + +# Add Qt lib directory to LD_LIBRARY_PATH: +export LD_LIBRARY_PATH="${CMPCLI_BIN_PATH}/qt:$LD_LIBRARY_PATH" + +# Add pkg libs directory to LD_LIBRARY_PATH: +export LD_LIBRARY_PATH="${CMPCLI_BIN_PATH}/pkglibs:$LD_LIBRARY_PATH" + + +# Call compressonatorcli executable +if [ -e $CMPCLI_BIN_PATH/compressonatorcli-bin ]; then + $CMPCLI_BIN_PATH/compressonatorcli-bin "$@" +else + echo "Error:Cannot find compressonatorcli-bin" +fi diff --git a/scripts/fetch_dependencies.py b/scripts/fetch_dependencies.py index 7756b15c0..105a33554 100644 --- a/scripts/fetch_dependencies.py +++ b/scripts/fetch_dependencies.py @@ -65,12 +65,12 @@ ghRoot = "https://github.com/GPUOpen-Tools/" # source reviews for use -# "https://github.com/opencv/opencv.git" : ["../common/lib/ext/opencv", "master"], -# "https://github.com/openexr/openexr.git" : ["../common/lib/ext/openexr", "master"], -# "https://github.com/madler/zlib" : ["../common/lib/ext/zlib", "master"], -# "https://github.com/catchorg/Catch2.git" : ["../common/lib/ext/catch", "master"], -# "https://github.com/syoyo/tinyexr" : ["../common/lib/ext/tinyexr", "master"], -# "https://github.com/google/draco/tree/master/src/draco" : : ["../common/lib/ext/draco", "master"], +# "https://github.com/opencv/opencv.git" : ["../common/lib/ext/opencv2", "2.4.9"] +# "https://github.com/openexr/openexr.git" : ["../common/lib/ext/openexr", "master"], +# "https://github.com/madler/zlib" : ["../common/lib/ext/zlib", "master"], +# "https://github.com/catchorg/Catch2.git" : ["../common/lib/ext/catch", "master"], +# "https://github.com/syoyo/tinyexr" : ["../common/lib/ext/tinyexr", "master"], +# "https://github.com/google/draco/tree/master/src/draco" : : ["../common/lib/ext/draco", "master"], # Libs. gitMapping = { @@ -81,9 +81,9 @@ ghRoot+"common_lib_ext_opengl.git" : ["../common/lib/ext/opengl", "master"], ghRoot+"common_lib_ext_tinyxml_2.6.2.git" : ["../common/lib/ext/tinyxml", "master"], ghRoot+"common_lib_ext_zlib_1.2.10.git" : ["../common/lib/ext/zlib", "master"], - "https://github.com/g-truc/glm.git" : ["../common/lib/ext/glm", "master"], + "https://github.com/g-truc/glm.git" : ["../common/lib/ext/glm", "0.9.8.0"], "https://github.com/discord/rapidxml.git" : ["../common/lib/ext/rapidxml", "master"], - "https://github.com/KhronosGroup/KTX-Software.git" : ["../common/lib/ext/ktx", "master"], + "https://github.com/KhronosGroup/KTX-Software.git" : ["../common/lib/ext/ktx", "v4.0.0-beta4"], "https://github.com/apitrace/dxsdk" : ["../common/lib/ext/apitrace/dxsdk", "master"], } diff --git a/scripts/get_version.py b/scripts/get_version.py index 469fb3a8f..05445df86 100644 --- a/scripts/get_version.py +++ b/scripts/get_version.py @@ -3,7 +3,7 @@ # simple script to read version information from Compressonator\Header\Version.h # Usage # export WORKSPACE= -# set =`python $WORKSPACE/main/Compressonator/Scripts/GetVersion.py --major|--minor` +# set =`python $WORKSPACE/compressonator/scripts/getversion.py --major|--minor` # import os import argparse @@ -18,7 +18,7 @@ args = parser.parse_args() # initialize file for search -gpaVersionFile = os.path.join(os.environ['WORKSPACE'], 'compressonator/cmp_compressonatorLib/version.h') +gpaVersionFile = os.path.join(os.environ['WORKSPACE'], 'compressonator/cmp_compressonatorlib/version.h') gpaVersionData = open(gpaVersionFile) # get major, minor, and update values diff --git a/scripts/initsetup_mac.sh b/scripts/initsetup_mac.sh new file mode 100644 index 000000000..391b30b30 --- /dev/null +++ b/scripts/initsetup_mac.sh @@ -0,0 +1,39 @@ +#run the following command to install prerequisite to build CompressonatorCLI binary for mac (tested only on macOS Mojave v10.14.3) +#assuming you already have homebrew installed +brew update +brew install cmake + +#need >gcc-6 and >g++-6 for the build +brew install gcc +export CC=/usr/local/bin/gcc-9 +export CXX=/usr/local/bin/g++-9 + +brew install mesa +echo 'export PATH="$(brew --prefix glew)/bin:$PATH"' >> ~/.bashrc + +brew install glew +echo 'export PATH="$(brew --prefix glew)/bin:$PATH"' >> ~/.bashrc + +brew install qt +echo 'export PATH="$(brew --prefix qt)/bin:$PATH"' >> ~/.bashrc +export QT5DIR="$(brew --prefix qt)" +export CMAKE_MODULE_PATH=${QT5DIR}/lib/cmake:${CMAKE_MODULE_PATH} +export CMAKE_PREFIX_PATH=${QT5DIR} + +#openexr v2.2. is needed else have to build from source +#brew install openexr +#echo 'export PATH="$(brew --prefix openexr)/bin:$PATH"' >> ~/.bashrc +#to build from source, uncomment below and remember to symlink by export path +wget http://download.savannah.nongnu.org/releases/openexr/ilmbase-2.2.0.tar.gz +(tar xvfz ilmbase-2.2.0.tar.gz && cd ilmbase-2.2.0 && ./configure && sudo make install) +wget http://download.savannah.nongnu.org/releases/openexr/openexr-2.2.0.tar.gz +(tar xvfz openexr-2.2.0.tar.gz && cd openexr-2.2.0 && ./configure --disable-ilmbasetest && sudo make install) + +brew install opencv +echo 'export PATH="$(brew --prefix opencv@2)/lib:$PATH"' >> ~/.bashrc + +brew install boost +echo 'export PATH="$(brew --prefix boost)/bin:$PATH"' >> ~/.bashrc + +source ~/.bashrc + diff --git a/scripts/initsetup_ubuntu.sh b/scripts/initsetup_ubuntu.sh index 5623e0510..87550301e 100644 --- a/scripts/initsetup_ubuntu.sh +++ b/scripts/initsetup_ubuntu.sh @@ -1,24 +1,66 @@ -#run the following command to install prerequisite to build CompressonatorCLI for unix then only run buildCLI_unix.sh -# openexr v2.2 -# If you want to build from source use this -# LD_LIBRARY_PATH=/usr/local/lib -# export LD_LIBRARY_PATH -# wget http://download.savannah.nongnu.org/releases/openexr/ilmbase-2.2.0.tar.gz -# (tar xvfz ilmbase-2.2.0.tar.gz && cd ilmbase-2.2.0 && sudo cmake -DCMAKE_INSTALL_PREFIX=/usr/local/ilmbase22build/ . && sudo make -j 4 && sudo make install) -# wget http://download.savannah.nongnu.org/releases/openexr/openexr-2.2.0.tar.gz -# (tar xvfz openexr-2.2.0.tar.gz && cd openexr-2.2.0 && sudo cmake -DILMBASE_PACKAGE_PREFIX=/usr/local/ilmbase22build/ -DCMAKE_INSTALL_PREFIX=/usr/local/openexr22build/ . && sudo make -j 4 && sudo make install) - +#run the following command to install any missing prerequisite to build or run Compressonatorcli version=`lsb_release --release | cut -f2` -if [ $version = "18.04" ];then +if [ $version = "20.04" ];then + echo "You have Ubuntu 20.04" + sudo apt-get install libglew-dev libegl1-mesa-dev + # sudo apt-get install qtdeclarative5-dev + # openexr v2.2. is needed else have to build from source + sudo apt-get install libopenexr-dev + sudo apt-get install libilmbase-dev + sudo apt-get install libopencv-dev +elif [ $version = "18.04" ];then echo "You have Ubuntu 18.04" - sudo apt-get install cmake sudo apt-get install libglew-dev libegl1-mesa-dev sudo apt-get install qtdeclarative5-dev #openexr v2.2. is needed else have to build from source sudo apt-get install libopenexr-dev sudo apt-get install libilmbase-dev sudo apt-get install libopencv-dev +elif [ $version = "16.04" ];then + echo "You have Ubuntu 16.04" + #need gcc-6 and g++-6 for the build + sudo add-apt-repository ppa:ubuntu-toolchain-r/test + sudo apt-get update + sudo apt-get install gcc-6 g++-6 + export CC=/usr/bin/gcc-6 + export CXX=/usr/bin/g++-6 + sudo apt-get install libglew-dev libegl1-mesa-dev + sudo apt-get install qtdeclarative5-dev + #openexr v2.2. is needed else have to build from source + wget http://download.savannah.nongnu.org/releases/openexr/ilmbase-2.2.0.tar.gz + (tar xvfz ilmbase-2.2.0.tar.gz && cd ilmbase-2.2.0 && sudo cmake -DCMAKE_INSTALL_PREFIX=/usr/local/ilmbase22build/ . && sudo make -j 4 && sudo make install) + wget http://download.savannah.nongnu.org/releases/openexr/openexr-2.2.0.tar.gz + (tar xvfz openexr-2.2.0.tar.gz && cd openexr-2.2.0 && sudo cmake -DILMBASE_PACKAGE_PREFIX=/usr/local/ilmbase22build/ -DCMAKE_INSTALL_PREFIX=/usr/local/openexr22build/ . && sudo make -j 4 && sudo make install) + +elif [ $version = "14.04" ];then + echo "You have Ubuntu 14.04" + #need gcc-6 and g++-6 for the build + sudo add-apt-repository ppa:ubuntu-toolchain-r/test + sudo apt-get update + sudo apt-get install gcc-6 g++-6 + export CC=/usr/bin/gcc-6 + export CXX=/usr/bin/g++-6 + sudo apt-get install libglew-dev libegl1-mesa-dev + #need qt>=5.4 to run the program + sudo add-apt-repository ppa:kvirc/kvirc-qt5.5 + sudo apt-get update + sudo apt-get install qtdeclarative5-dev + + #need to build openexr v2.2 + LD_LIBRARY_PATH=/usr/local/lib + export LD_LIBRARY_PATH + wget http://download.savannah.nongnu.org/releases/openexr/ilmbase-2.2.0.tar.gz + (tar xvfz ilmbase-2.2.0.tar.gz && cd ilmbase-2.2.0 && sudo cmake -DCMAKE_INSTALL_PREFIX=/usr/local/ilmbase22build/ . && sudo make -j 4 && sudo make install) + wget http://download.savannah.nongnu.org/releases/openexr/openexr-2.2.0.tar.gz + (tar xvfz openexr-2.2.0.tar.gz && cd openexr-2.2.0 && sudo cmake -DILMBASE_PACKAGE_PREFIX=/usr/local/ilmbase22build/ -DCMAKE_INSTALL_PREFIX=/usr/local/openexr22build/ . && sudo make -j 4 && sudo make install) + + sudo apt-get install libopencv-dev + sudo apt-get install libboost-filesystem-dev + sudo apt-get install libboost-system-dev + else - echo "Version other than 18.04 detected. This script only support 18.04. You might need to install the prerequisite packages with the specific version as listed in initsetup_unix.sh above manually." + echo "Version other than 14.04 and higher. This script only supports 14.04 build and higher. You might need to install the prerequisite packages with the specific version as listed in initsetup_unix.sh above manually." fi + + diff --git a/scripts/linux_build_apps.sh b/scripts/linux_build_apps.sh index 9e9f2dd89..d696c9021 100644 --- a/scripts/linux_build_apps.sh +++ b/scripts/linux_build_apps.sh @@ -1,6 +1,12 @@ -cd .. -export VULKAN_SDK=~/vulkan/1.2.141.2 -QT_ROOT=~/Qt5.12.6/5.12.6/gcc_64 +# Build documentation +set -x +cd $WORKSPACE/compressonator/scripts +python3 fetch_dependencies.py --no-hooks + +cd $WORKSPACE/compressonator +export VULKAN_SDK_VER=1.2.141.2 +export VULKAN_SDK=/opt/VulkanSDK/$VULKAN_SDK_VER/ +QT_ROOT=/opt/Qt/Qt5.9.2/5.9.2/gcc_64 cmake -DQT_PACKAGE_ROOT=$QT_ROOT . make diff --git a/scripts/update_version.py b/scripts/update_version.py index 62d39d1a1..2719d672d 100644 --- a/scripts/update_version.py +++ b/scripts/update_version.py @@ -4,7 +4,7 @@ # # Usage # export WORKSPACE= -# python $WORKSPACE/main/Compressonator/CMP_CompressonatorLib/UpdateVersion.py +# python $WORKSPACE/main/Compressonator/CMP_CompressonatorLib/UpdateVersion.py # import os import argparse @@ -21,7 +21,7 @@ updateArgs = parser.parse_args() # initialize file for search -cmpVersionFile = os.path.join(os.environ['WORKSPACE'], 'compressonator/cmp_compressonatorLib/version.h') +cmpVersionFile = os.path.join(os.environ['WORKSPACE'], 'compressonator/cmp_compressonatorlib/version.h') cmpVersionData = open(cmpVersionFile, 'r') # replace version string in data and write back out to file diff --git a/scripts/windows_build_install.bat b/scripts/windows_build_install.bat index 0ea76d70c..be866ee33 100644 --- a/scripts/windows_build_install.bat +++ b/scripts/windows_build_install.bat @@ -6,7 +6,8 @@ set WORKDIR=%ROOTDIR%\ PATH=C:\OpenJDK\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\Program Files\doxygen\bin;C:\Strawberry\c\bin;C:\Strawberry\perl\site\bin;C:\Strawberry\perl\bin;C:\Program Files\MiKTeX 2.9\miktex\bin\x64\;C:\Program Files (x86)\Graphviz2.38\bin;C:\Program Files\Microsoft DNX\Dnvm\;C:\Program Files\Microsoft SQL Server\130\Tools\Binn\;C:\Program Files\Perforce;C:\Program Files\Perforce\DVCS\;C:\Program Files (x86)\Windows Kits\10\Windows Performance Toolkit\;C:\Users\adtbld\.dnx\bin;C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE;C:\Program Files (x86)\MSBuild\14.0\Bin;C:\Program Files\CMake\bin;C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin;C:\Users\adtbld\AppData\Local\Microsoft\WindowsApps;C:\Python27;C:\Python27\Scripts;C:\Python27\Tools\Scripts -set ADVANCED_INSTALLER="C:\Program Files (x86)\Caphyon\Advanced Installer 12.4.2\bin\x86\AdvancedInstaller.com" +REM set ADVANCED_INSTALLER="C:\Program Files (x86)\Caphyon\Advanced Installer 12.4.2\bin\x86\AdvancedInstaller.com" +set ADVANCED_INSTALLER="C:\Program Files (x86)\Caphyon\Advanced Installer 13.5\bin\x86\AdvancedInstaller.com" echo on diff --git a/scripts/windows_build_sdk.bat b/scripts/windows_build_sdk.bat index bf0cbe8a2..a2a3cf444 100644 --- a/scripts/windows_build_sdk.bat +++ b/scripts/windows_build_sdk.bat @@ -409,29 +409,29 @@ if not %ERRORLEVEL%==0 ( REM Qt dir only required for Framework Plugins build -set QTDIR=C:\Qt\Qt5.12.6\5.12.6\msvc2017_64 - -REM Vulkan SDK only required for Framework Plugin build -set VULKAN_SDK=C:\VulkanSDK\1.2.141.2 - +REM set QTDIR=C:\Qt\Qt5.12.6\5.12.6\msvc2017_64 +REM +REM REM Vulkan SDK only required for Framework Plugin build +REM set VULKAN_SDK=C:\VulkanSDK\1.2.141.2 +REM REM ################################################################## REM cmp_frameworkPlugins - VS 2017 Release Reserved for V4.0 REM ################################################################## -msbuild /m:4 /t:build /p:Configuration=Release_MD_DLL /p:Platform=x64 %WORKDIR%\compressonator\VS2017\cmp_frameworkPlugins.sln -if not %ERRORLEVEL%==0 ( - echo on - echo cmp_frameworkPlugins build release_md_dll x64 FAILED - cd %WORKDIR% - exit 1 -) - -msbuild /m:4 /t:build /p:Configuration=Debug_MD_DLL /p:Platform=x64 %WORKDIR%\compressonator\VS2017\cmp_frameworkPlugins.sln -if not %ERRORLEVEL%==0 ( - echo on - echo cmp_frameworkPlugins build Debug_MD_dll x64 FAILED - cd %WORKDIR% - exit 1 -) +REM msbuild /m:4 /t:build /p:Configuration=Release_MD_DLL /p:Platform=x64 %WORKDIR%\compressonator\VS2017\cmp_frameworkPlugins.sln +REM if not %ERRORLEVEL%==0 ( +REM echo on +REM echo cmp_frameworkPlugins build release_md_dll x64 FAILED +REM cd %WORKDIR% +REM exit 1 +REM ) +REM +REM msbuild /m:4 /t:build /p:Configuration=Debug_MD_DLL /p:Platform=x64 %WORKDIR%\compressonator\VS2017\cmp_frameworkPlugins.sln +REM if not %ERRORLEVEL%==0 ( +REM echo on +REM echo cmp_frameworkPlugins build Debug_MD_dll x64 FAILED +REM cd %WORKDIR% +REM exit 1 +REM ) REM ################# REM CLEAN TMP FOLDER diff --git a/vs2017/cmp_compressonatorlib.vcxproj b/vs2017/cmp_compressonatorlib.vcxproj index 0008e548f..5f621426f 100644 --- a/vs2017/cmp_compressonatorlib.vcxproj +++ b/vs2017/cmp_compressonatorlib.vcxproj @@ -404,8 +404,8 @@ Level4 Disabled - USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;USE_GTC;USE_APC;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN32;_DEBUG;_LIB;HALF_NO_STD;_ITERATOR_DEBUG_LEVEL=2;%(PreprocessorDefinitions) - ../;../CMP_CompressonatorLib;../CMP_CompressonatorLib/ASTC;../CMP_CompressonatorLib/ASTC/ARM;../CMP_CompressonatorLib/ATC;../CMP_CompressonatorLib/ATI;../CMP_CompressonatorLib/BASIS;../CMP_CompressonatorLib/BC6H;../CMP_CompressonatorLib/BC7;../CMP_CompressonatorLib/Block;../CMP_CompressonatorLib/Buffer;../CMP_CompressonatorLib/Common;../CMP_CompressonatorLib/DXT;../CMP_CompressonatorLib/DXTC;../CMP_CompressonatorLib/ETC;../CMP_CompressonatorLib/ETC/etcpack;../CMP_CompressonatorLib/GT;../CMP_CompressonatorLib/APC;../CMP_Framework/Common;../CMP_Framework/Common/half;../Source/CMP_Transcoder/basis_universal;../Header/CMP_Math;../../Common/Src;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common + USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN32;_DEBUG;_LIB;HALF_NO_STD;_ITERATOR_DEBUG_LEVEL=2;%(PreprocessorDefinitions) + ../;../CMP_CompressonatorLib;../CMP_CompressonatorLib/ASTC;../CMP_CompressonatorLib/ASTC/ARM;../CMP_CompressonatorLib/ATC;../CMP_CompressonatorLib/ATI;../CMP_CompressonatorLib/BASIS;../CMP_CompressonatorLib/BC6H;../CMP_CompressonatorLib/BC7;../CMP_CompressonatorLib/Block;../CMP_CompressonatorLib/Buffer;../CMP_CompressonatorLib/Common;../CMP_CompressonatorLib/DXT;../CMP_CompressonatorLib/DXTC;../CMP_CompressonatorLib/ETC;../CMP_CompressonatorLib/ETC/etcpack;../CMP_CompressonatorLib/GT;../CMP_CompressonatorLib/APC;../CMP_Framework/Common;../CMP_Framework/Common/half;../Source/CMP_Transcoder/basis_universal;../Header/CMP_Math;../../Common/Src;../cmp_core/shaders;../cmp_core/source;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common true MultiThreadedDebug false @@ -443,8 +443,8 @@ Level4 Disabled - USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;USE_GTC;USE_APC;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN32;_DEBUG;_LIB;HALF_NO_STD;_ITERATOR_DEBUG_LEVEL=2;%(PreprocessorDefinitions) - ../;../CMP_CompressonatorLib;../CMP_CompressonatorLib/ASTC;../CMP_CompressonatorLib/ASTC/ARM;../CMP_CompressonatorLib/ATC;../CMP_CompressonatorLib/ATI;../CMP_CompressonatorLib/BASIS;../CMP_CompressonatorLib/BC6H;../CMP_CompressonatorLib/BC7;../CMP_CompressonatorLib/Block;../CMP_CompressonatorLib/Buffer;../CMP_CompressonatorLib/Common;../CMP_CompressonatorLib/DXT;../CMP_CompressonatorLib/DXTC;../CMP_CompressonatorLib/ETC;../CMP_CompressonatorLib/ETC/etcpack;../CMP_CompressonatorLib/GT;../CMP_CompressonatorLib/APC;../CMP_Framework/Common;../CMP_Framework/Common/half;../Source/CMP_Transcoder/basis_universal;../Header/CMP_Math;../../Common/Src;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common + USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN32;_DEBUG;_LIB;HALF_NO_STD;_ITERATOR_DEBUG_LEVEL=2;%(PreprocessorDefinitions) + ../;../CMP_CompressonatorLib;../CMP_CompressonatorLib/ASTC;../CMP_CompressonatorLib/ASTC/ARM;../CMP_CompressonatorLib/ATC;../CMP_CompressonatorLib/ATI;../CMP_CompressonatorLib/BASIS;../CMP_CompressonatorLib/BC6H;../CMP_CompressonatorLib/BC7;../CMP_CompressonatorLib/Block;../CMP_CompressonatorLib/Buffer;../CMP_CompressonatorLib/Common;../CMP_CompressonatorLib/DXT;../CMP_CompressonatorLib/DXTC;../CMP_CompressonatorLib/ETC;../CMP_CompressonatorLib/ETC/etcpack;../CMP_CompressonatorLib/GT;../CMP_CompressonatorLib/APC;../CMP_Framework/Common;../CMP_Framework/Common/half;../Source/CMP_Transcoder/basis_universal;../Header/CMP_Math;../../Common/Src;../cmp_core/shaders;../cmp_core/source;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common true MultiThreadedDebug false @@ -482,8 +482,8 @@ Level4 Disabled - USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;USE_GTC;USE_APC;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN32;_DEBUG;_LIB;HALF_NO_STD;_ITERATOR_DEBUG_LEVEL=2;%(PreprocessorDefinitions) - ../;../CMP_CompressonatorLib;../CMP_CompressonatorLib/ASTC;../CMP_CompressonatorLib/ASTC/ARM;../CMP_CompressonatorLib/ATC;../CMP_CompressonatorLib/ATI;../CMP_CompressonatorLib/BASIS;../CMP_CompressonatorLib/BC6H;../CMP_CompressonatorLib/BC7;../CMP_CompressonatorLib/Block;../CMP_CompressonatorLib/Buffer;../CMP_CompressonatorLib/Common;../CMP_CompressonatorLib/DXT;../CMP_CompressonatorLib/DXTC;../CMP_CompressonatorLib/ETC;../CMP_CompressonatorLib/ETC/etcpack;../CMP_CompressonatorLib/GT;../CMP_CompressonatorLib/APC;../CMP_Framework/Common;../CMP_Framework/Common/half;../Source/CMP_Transcoder/basis_universal;../Header/CMP_Math;../../Common/Src;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common + USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN32;_DEBUG;_LIB;HALF_NO_STD;_ITERATOR_DEBUG_LEVEL=2;%(PreprocessorDefinitions) + ../;../CMP_CompressonatorLib;../CMP_CompressonatorLib/ASTC;../CMP_CompressonatorLib/ASTC/ARM;../CMP_CompressonatorLib/ATC;../CMP_CompressonatorLib/ATI;../CMP_CompressonatorLib/BASIS;../CMP_CompressonatorLib/BC6H;../CMP_CompressonatorLib/BC7;../CMP_CompressonatorLib/Block;../CMP_CompressonatorLib/Buffer;../CMP_CompressonatorLib/Common;../CMP_CompressonatorLib/DXT;../CMP_CompressonatorLib/DXTC;../CMP_CompressonatorLib/ETC;../CMP_CompressonatorLib/ETC/etcpack;../CMP_CompressonatorLib/GT;../CMP_CompressonatorLib/APC;../CMP_Framework/Common;../CMP_Framework/Common/half;../Source/CMP_Transcoder/basis_universal;../Header/CMP_Math;../../Common/Src;../cmp_core/shaders;../cmp_core/source;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common true MultiThreadedDebugDLL false @@ -522,8 +522,8 @@ Level4 Disabled - USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;USE_GTC;USE_APC;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN32;_DEBUG;_LIB;HALF_NO_STD;_ITERATOR_DEBUG_LEVEL=2;%(PreprocessorDefinitions) - ../;../CMP_CompressonatorLib;../CMP_CompressonatorLib/ASTC;../CMP_CompressonatorLib/ASTC/ARM;../CMP_CompressonatorLib/ATC;../CMP_CompressonatorLib/ATI;../CMP_CompressonatorLib/BASIS;../CMP_CompressonatorLib/BC6H;../CMP_CompressonatorLib/BC7;../CMP_CompressonatorLib/Block;../CMP_CompressonatorLib/Buffer;../CMP_CompressonatorLib/Common;../CMP_CompressonatorLib/DXT;../CMP_CompressonatorLib/DXTC;../CMP_CompressonatorLib/ETC;../CMP_CompressonatorLib/ETC/etcpack;../CMP_CompressonatorLib/GT;../CMP_CompressonatorLib/APC;../CMP_Framework/Common;../CMP_Framework/Common/half;../Source/CMP_Transcoder/basis_universal;../Header/CMP_Math;../../Common/Src;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common + USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN32;_DEBUG;_LIB;HALF_NO_STD;_ITERATOR_DEBUG_LEVEL=2;%(PreprocessorDefinitions) + ../;../CMP_CompressonatorLib;../CMP_CompressonatorLib/ASTC;../CMP_CompressonatorLib/ASTC/ARM;../CMP_CompressonatorLib/ATC;../CMP_CompressonatorLib/ATI;../CMP_CompressonatorLib/BASIS;../CMP_CompressonatorLib/BC6H;../CMP_CompressonatorLib/BC7;../CMP_CompressonatorLib/Block;../CMP_CompressonatorLib/Buffer;../CMP_CompressonatorLib/Common;../CMP_CompressonatorLib/DXT;../CMP_CompressonatorLib/DXTC;../CMP_CompressonatorLib/ETC;../CMP_CompressonatorLib/ETC/etcpack;../CMP_CompressonatorLib/GT;../CMP_CompressonatorLib/APC;../CMP_Framework/Common;../CMP_Framework/Common/half;../Source/CMP_Transcoder/basis_universal;../Header/CMP_Math;../../Common/Src;../cmp_core/shaders;../cmp_core/source;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common true MultiThreadedDebugDLL false @@ -561,8 +561,8 @@ Level4 Disabled - USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;USE_GTC;USE_APC;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN64;_DEBUG;_LIB;HALF_NO_STD;_ITERATOR_DEBUG_LEVEL=2;%(PreprocessorDefinitions) - ../;../CMP_CompressonatorLib;../CMP_CompressonatorLib/ASTC;../CMP_CompressonatorLib/ASTC/ARM;../CMP_CompressonatorLib/ATC;../CMP_CompressonatorLib/ATI;../CMP_CompressonatorLib/BASIS;../CMP_CompressonatorLib/BC6H;../CMP_CompressonatorLib/BC7;../CMP_CompressonatorLib/Block;../CMP_CompressonatorLib/Buffer;../CMP_CompressonatorLib/Common;../CMP_CompressonatorLib/DXT;../CMP_CompressonatorLib/DXTC;../CMP_CompressonatorLib/ETC;../CMP_CompressonatorLib/ETC/etcpack;../CMP_CompressonatorLib/GT;../CMP_CompressonatorLib/APC;../CMP_Framework/Common;../CMP_Framework/Common/half;../Source/CMP_Transcoder/basis_universal;../Header/CMP_Math;../../Common/Src;../Header/Codec/BASIS;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common + USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN64;_DEBUG;_LIB;HALF_NO_STD;_ITERATOR_DEBUG_LEVEL=2;%(PreprocessorDefinitions) + ../;../CMP_CompressonatorLib;../CMP_CompressonatorLib/ASTC;../CMP_CompressonatorLib/ASTC/ARM;../CMP_CompressonatorLib/ATC;../CMP_CompressonatorLib/ATI;../CMP_CompressonatorLib/BASIS;../CMP_CompressonatorLib/BC6H;../CMP_CompressonatorLib/BC7;../CMP_CompressonatorLib/Block;../CMP_CompressonatorLib/Buffer;../CMP_CompressonatorLib/Common;../CMP_CompressonatorLib/DXT;../CMP_CompressonatorLib/DXTC;../CMP_CompressonatorLib/ETC;../CMP_CompressonatorLib/ETC/etcpack;../CMP_CompressonatorLib/GT;../CMP_CompressonatorLib/APC;../CMP_Framework/Common;../CMP_Framework/Common/half;../Source/CMP_Transcoder/basis_universal;../Header/CMP_Math;../../Common/Src;../cmp_core/shaders;../cmp_core/source;../Header/Codec/BASIS;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common ProgramDatabase true MultiThreadedDebug @@ -594,8 +594,8 @@ Level4 Disabled - USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;USE_GTC;USE_APC;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN64;_DEBUG;_LIB;HALF_NO_STD;_ITERATOR_DEBUG_LEVEL=2;%(PreprocessorDefinitions) - ../;../CMP_CompressonatorLib;../CMP_CompressonatorLib/ASTC;../CMP_CompressonatorLib/ASTC/ARM;../CMP_CompressonatorLib/ATC;../CMP_CompressonatorLib/ATI;../CMP_CompressonatorLib/BASIS;../CMP_CompressonatorLib/BC6H;../CMP_CompressonatorLib/BC7;../CMP_CompressonatorLib/Block;../CMP_CompressonatorLib/Buffer;../CMP_CompressonatorLib/Common;../CMP_CompressonatorLib/DXT;../CMP_CompressonatorLib/DXTC;../CMP_CompressonatorLib/ETC;../CMP_CompressonatorLib/ETC/etcpack;../CMP_CompressonatorLib/GT;../CMP_CompressonatorLib/APC;../CMP_Framework/Common;../CMP_Framework/Common/half;../Source/CMP_Transcoder/basis_universal;../Header/CMP_Math;../../Common/Src;../Header/Codec/BASIS;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common + USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN64;_DEBUG;_LIB;HALF_NO_STD;_ITERATOR_DEBUG_LEVEL=2;%(PreprocessorDefinitions) + ../;../CMP_CompressonatorLib;../CMP_CompressonatorLib/ASTC;../CMP_CompressonatorLib/ASTC/ARM;../CMP_CompressonatorLib/ATC;../CMP_CompressonatorLib/ATI;../CMP_CompressonatorLib/BASIS;../CMP_CompressonatorLib/BC6H;../CMP_CompressonatorLib/BC7;../CMP_CompressonatorLib/Block;../CMP_CompressonatorLib/Buffer;../CMP_CompressonatorLib/Common;../CMP_CompressonatorLib/DXT;../CMP_CompressonatorLib/DXTC;../CMP_CompressonatorLib/ETC;../CMP_CompressonatorLib/ETC/etcpack;../CMP_CompressonatorLib/GT;../CMP_CompressonatorLib/APC;../CMP_Framework/Common;../CMP_Framework/Common/half;../Source/CMP_Transcoder/basis_universal;../Header/CMP_Math;../../Common/Src;../cmp_core/shaders;../cmp_core/source;../Header/Codec/BASIS;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common ProgramDatabase true MultiThreadedDebug @@ -625,8 +625,8 @@ Level4 Disabled - USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;USE_GTC;USE_APC;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN64;_DEBUG;_LIB;HALF_NO_STD;_ITERATOR_DEBUG_LEVEL=2;%(PreprocessorDefinitions) - ../;../CMP_CompressonatorLib;../CMP_CompressonatorLib/ASTC;../CMP_CompressonatorLib/ASTC/ARM;../CMP_CompressonatorLib/ATC;../CMP_CompressonatorLib/ATI;../CMP_CompressonatorLib/BASIS;../CMP_CompressonatorLib/BC6H;../CMP_CompressonatorLib/BC7;../CMP_CompressonatorLib/Block;../CMP_CompressonatorLib/Buffer;../CMP_CompressonatorLib/Common;../CMP_CompressonatorLib/DXT;../CMP_CompressonatorLib/DXTC;../CMP_CompressonatorLib/ETC;../CMP_CompressonatorLib/ETC/etcpack;../CMP_CompressonatorLib/GT;../CMP_CompressonatorLib/APC;../CMP_Framework/Common;../CMP_Framework/Common/half;../Source/CMP_Transcoder/basis_universal;../Header/CMP_Math;../../Common/Src;../Header/Codec/BASIS;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common + USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN64;_DEBUG;_LIB;HALF_NO_STD;_ITERATOR_DEBUG_LEVEL=2;%(PreprocessorDefinitions) + ../;../CMP_CompressonatorLib;../CMP_CompressonatorLib/ASTC;../CMP_CompressonatorLib/ASTC/ARM;../CMP_CompressonatorLib/ATC;../CMP_CompressonatorLib/ATI;../CMP_CompressonatorLib/BASIS;../CMP_CompressonatorLib/BC6H;../CMP_CompressonatorLib/BC7;../CMP_CompressonatorLib/Block;../CMP_CompressonatorLib/Buffer;../CMP_CompressonatorLib/Common;../CMP_CompressonatorLib/DXT;../CMP_CompressonatorLib/DXTC;../CMP_CompressonatorLib/ETC;../CMP_CompressonatorLib/ETC/etcpack;../CMP_CompressonatorLib/GT;../CMP_CompressonatorLib/APC;../CMP_Framework/Common;../CMP_Framework/Common/half;../Source/CMP_Transcoder/basis_universal;../Header/CMP_Math;../../Common/Src;../cmp_core/shaders;../cmp_core/source;../Header/Codec/BASIS;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common ProgramDatabase true MultiThreadedDebugDLL @@ -659,8 +659,8 @@ Level4 Disabled - USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;USE_GTC;USE_APC;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN64;_DEBUG;_LIB;HALF_NO_STD;_ITERATOR_DEBUG_LEVEL=2;%(PreprocessorDefinitions) - ../;../CMP_CompressonatorLib;../CMP_CompressonatorLib/ASTC;../CMP_CompressonatorLib/ASTC/ARM;../CMP_CompressonatorLib/ATC;../CMP_CompressonatorLib/ATI;../CMP_CompressonatorLib/BASIS;../CMP_CompressonatorLib/BC6H;../CMP_CompressonatorLib/BC7;../CMP_CompressonatorLib/Block;../CMP_CompressonatorLib/Buffer;../CMP_CompressonatorLib/Common;../CMP_CompressonatorLib/DXT;../CMP_CompressonatorLib/DXTC;../CMP_CompressonatorLib/ETC;../CMP_CompressonatorLib/ETC/etcpack;../CMP_CompressonatorLib/GT;../CMP_CompressonatorLib/APC;../CMP_Framework/Common;../CMP_Framework/Common/half;../Source/CMP_Transcoder/basis_universal;../Header/CMP_Math;../../Common/Src;../Header/Codec/BASIS;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common + USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN64;_DEBUG;_LIB;HALF_NO_STD;_ITERATOR_DEBUG_LEVEL=2;%(PreprocessorDefinitions) + ../;../CMP_CompressonatorLib;../CMP_CompressonatorLib/ASTC;../CMP_CompressonatorLib/ASTC/ARM;../CMP_CompressonatorLib/ATC;../CMP_CompressonatorLib/ATI;../CMP_CompressonatorLib/BASIS;../CMP_CompressonatorLib/BC6H;../CMP_CompressonatorLib/BC7;../CMP_CompressonatorLib/Block;../CMP_CompressonatorLib/Buffer;../CMP_CompressonatorLib/Common;../CMP_CompressonatorLib/DXT;../CMP_CompressonatorLib/DXTC;../CMP_CompressonatorLib/ETC;../CMP_CompressonatorLib/ETC/etcpack;../CMP_CompressonatorLib/GT;../CMP_CompressonatorLib/APC;../CMP_Framework/Common;../CMP_Framework/Common/half;../Source/CMP_Transcoder/basis_universal;../Header/CMP_Math;../../Common/Src;../cmp_core/shaders;../cmp_core/source;../Header/Codec/BASIS;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common ProgramDatabase true MultiThreadedDebugDLL @@ -693,8 +693,8 @@ false - USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;USE_GTC;USE_APC;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN32;NDEBUG;_LIB;HALF_NO_STD;%(PreprocessorDefinitions) - ../;../CMP_CompressonatorLib;../CMP_CompressonatorLib/ASTC;../CMP_CompressonatorLib/ASTC/ARM;../CMP_CompressonatorLib/ATC;../CMP_CompressonatorLib/ATI;../CMP_CompressonatorLib/BASIS;../CMP_CompressonatorLib/BC6H;../CMP_CompressonatorLib/BC7;../CMP_CompressonatorLib/Block;../CMP_CompressonatorLib/Buffer;../CMP_CompressonatorLib/Common;../CMP_CompressonatorLib/DXT;../CMP_CompressonatorLib/DXTC;../CMP_CompressonatorLib/ETC;../CMP_CompressonatorLib/ETC/etcpack;../CMP_CompressonatorLib/GT;../CMP_CompressonatorLib/APC;../CMP_Framework/Common;../CMP_Framework/Common/half;../Source/CMP_Transcoder/basis_universal;../Header/CMP_Math;../../Common/Src;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common + USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN32;NDEBUG;_LIB;HALF_NO_STD;%(PreprocessorDefinitions) + ../;../CMP_CompressonatorLib;../CMP_CompressonatorLib/ASTC;../CMP_CompressonatorLib/ASTC/ARM;../CMP_CompressonatorLib/ATC;../CMP_CompressonatorLib/ATI;../CMP_CompressonatorLib/BASIS;../CMP_CompressonatorLib/BC6H;../CMP_CompressonatorLib/BC7;../CMP_CompressonatorLib/Block;../CMP_CompressonatorLib/Buffer;../CMP_CompressonatorLib/Common;../CMP_CompressonatorLib/DXT;../CMP_CompressonatorLib/DXTC;../CMP_CompressonatorLib/ETC;../CMP_CompressonatorLib/ETC/etcpack;../CMP_CompressonatorLib/GT;../CMP_CompressonatorLib/APC;../CMP_Framework/Common;../CMP_Framework/Common/half;../Source/CMP_Transcoder/basis_universal;../Header/CMP_Math;../../Common/Src;../cmp_core/shaders;../cmp_core/source;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common MultiThreaded true ProgramDatabase @@ -741,8 +741,8 @@ false - USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;USE_GTC;USE_APC;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN32;NDEBUG;_LIB;HALF_NO_STD;%(PreprocessorDefinitions) - ../;../CMP_CompressonatorLib;../CMP_CompressonatorLib/ASTC;../CMP_CompressonatorLib/ASTC/ARM;../CMP_CompressonatorLib/ATC;../CMP_CompressonatorLib/ATI;../CMP_CompressonatorLib/BASIS;../CMP_CompressonatorLib/BC6H;../CMP_CompressonatorLib/BC7;../CMP_CompressonatorLib/Block;../CMP_CompressonatorLib/Buffer;../CMP_CompressonatorLib/Common;../CMP_CompressonatorLib/DXT;../CMP_CompressonatorLib/DXTC;../CMP_CompressonatorLib/ETC;../CMP_CompressonatorLib/ETC/etcpack;../CMP_CompressonatorLib/GT;../CMP_CompressonatorLib/APC;../CMP_Framework/Common;../CMP_Framework/Common/half;../Source/CMP_Transcoder/basis_universal;../Header/CMP_Math;../../Common/Src;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common + USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN32;NDEBUG;_LIB;HALF_NO_STD;%(PreprocessorDefinitions) + ../;../CMP_CompressonatorLib;../CMP_CompressonatorLib/ASTC;../CMP_CompressonatorLib/ASTC/ARM;../CMP_CompressonatorLib/ATC;../CMP_CompressonatorLib/ATI;../CMP_CompressonatorLib/BASIS;../CMP_CompressonatorLib/BC6H;../CMP_CompressonatorLib/BC7;../CMP_CompressonatorLib/Block;../CMP_CompressonatorLib/Buffer;../CMP_CompressonatorLib/Common;../CMP_CompressonatorLib/DXT;../CMP_CompressonatorLib/DXTC;../CMP_CompressonatorLib/ETC;../CMP_CompressonatorLib/ETC/etcpack;../CMP_CompressonatorLib/GT;../CMP_CompressonatorLib/APC;../CMP_Framework/Common;../CMP_Framework/Common/half;../Source/CMP_Transcoder/basis_universal;../Header/CMP_Math;../../Common/Src;../cmp_core/shaders;../cmp_core/source;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common MultiThreaded true ProgramDatabase @@ -789,8 +789,8 @@ false - USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;USE_GTC;USE_APC;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN32;NDEBUG;_LIB;HALF_NO_STD;%(PreprocessorDefinitions) - ../;../CMP_CompressonatorLib;../CMP_CompressonatorLib/ASTC;../CMP_CompressonatorLib/ASTC/ARM;../CMP_CompressonatorLib/ATC;../CMP_CompressonatorLib/ATI;../CMP_CompressonatorLib/BASIS;../CMP_CompressonatorLib/BC6H;../CMP_CompressonatorLib/BC7;../CMP_CompressonatorLib/Block;../CMP_CompressonatorLib/Buffer;../CMP_CompressonatorLib/Common;../CMP_CompressonatorLib/DXT;../CMP_CompressonatorLib/DXTC;../CMP_CompressonatorLib/ETC;../CMP_CompressonatorLib/ETC/etcpack;../CMP_CompressonatorLib/GT;../CMP_CompressonatorLib/APC;../CMP_Framework/Common;../CMP_Framework/Common/half;../Source/CMP_Transcoder/basis_universal;../Header/CMP_Math;../../Common/Src;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common + USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN32;NDEBUG;_LIB;HALF_NO_STD;%(PreprocessorDefinitions) + ../;../CMP_CompressonatorLib;../CMP_CompressonatorLib/ASTC;../CMP_CompressonatorLib/ASTC/ARM;../CMP_CompressonatorLib/ATC;../CMP_CompressonatorLib/ATI;../CMP_CompressonatorLib/BASIS;../CMP_CompressonatorLib/BC6H;../CMP_CompressonatorLib/BC7;../CMP_CompressonatorLib/Block;../CMP_CompressonatorLib/Buffer;../CMP_CompressonatorLib/Common;../CMP_CompressonatorLib/DXT;../CMP_CompressonatorLib/DXTC;../CMP_CompressonatorLib/ETC;../CMP_CompressonatorLib/ETC/etcpack;../CMP_CompressonatorLib/GT;../CMP_CompressonatorLib/APC;../CMP_Framework/Common;../CMP_Framework/Common/half;../Source/CMP_Transcoder/basis_universal;../Header/CMP_Math;../../Common/Src;../cmp_core/shaders;../cmp_core/source;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common MultiThreadedDLL true ProgramDatabase @@ -837,8 +837,8 @@ false - USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;USE_GTC;USE_APC;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN32;NDEBUG;_LIB;HALF_NO_STD;%(PreprocessorDefinitions) - ../;../CMP_CompressonatorLib;../CMP_CompressonatorLib/ASTC;../CMP_CompressonatorLib/ASTC/ARM;../CMP_CompressonatorLib/ATC;../CMP_CompressonatorLib/ATI;../CMP_CompressonatorLib/BASIS;../CMP_CompressonatorLib/BC6H;../CMP_CompressonatorLib/BC7;../CMP_CompressonatorLib/Block;../CMP_CompressonatorLib/Buffer;../CMP_CompressonatorLib/Common;../CMP_CompressonatorLib/DXT;../CMP_CompressonatorLib/DXTC;../CMP_CompressonatorLib/ETC;../CMP_CompressonatorLib/ETC/etcpack;../CMP_CompressonatorLib/GT;../CMP_CompressonatorLib/APC;../CMP_Framework/Common;../CMP_Framework/Common/half;../Source/CMP_Transcoder/basis_universal;../Header/CMP_Math;../../Common/Src;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common + USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN32;NDEBUG;_LIB;HALF_NO_STD;%(PreprocessorDefinitions) + ../;../CMP_CompressonatorLib;../CMP_CompressonatorLib/ASTC;../CMP_CompressonatorLib/ASTC/ARM;../CMP_CompressonatorLib/ATC;../CMP_CompressonatorLib/ATI;../CMP_CompressonatorLib/BASIS;../CMP_CompressonatorLib/BC6H;../CMP_CompressonatorLib/BC7;../CMP_CompressonatorLib/Block;../CMP_CompressonatorLib/Buffer;../CMP_CompressonatorLib/Common;../CMP_CompressonatorLib/DXT;../CMP_CompressonatorLib/DXTC;../CMP_CompressonatorLib/ETC;../CMP_CompressonatorLib/ETC/etcpack;../CMP_CompressonatorLib/GT;../CMP_CompressonatorLib/APC;../CMP_Framework/Common;../CMP_Framework/Common/half;../Source/CMP_Transcoder/basis_universal;../Header/CMP_Math;../../Common/Src;../cmp_core/shaders;../cmp_core/source;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common MultiThreadedDLL true ProgramDatabase @@ -884,8 +884,8 @@ MaxSpeed true true - USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;USE_GTC;USE_APC;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN64;NDEBUG;_LIB;%(PreprocessorDefinitions) - ../;../CMP_CompressonatorLib;../CMP_CompressonatorLib/ASTC;../CMP_CompressonatorLib/ASTC/ARM;../CMP_CompressonatorLib/ATC;../CMP_CompressonatorLib/ATI;../CMP_CompressonatorLib/BASIS;../CMP_CompressonatorLib/BC6H;../CMP_CompressonatorLib/BC7;../CMP_CompressonatorLib/Block;../CMP_CompressonatorLib/Buffer;../CMP_CompressonatorLib/Common;../CMP_CompressonatorLib/DXT;../CMP_CompressonatorLib/DXTC;../CMP_CompressonatorLib/ETC;../CMP_CompressonatorLib/ETC/etcpack;../CMP_CompressonatorLib/GT;../CMP_CompressonatorLib/APC;../CMP_Framework/Common;../CMP_Framework/Common/half;../Source/CMP_Transcoder/basis_universal;../Header/CMP_Math;../../Common/Src;../Header/Codec/BASIS;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common + USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN64;NDEBUG;_LIB;%(PreprocessorDefinitions) + ../;../CMP_CompressonatorLib;../CMP_CompressonatorLib/ASTC;../CMP_CompressonatorLib/ASTC/ARM;../CMP_CompressonatorLib/ATC;../CMP_CompressonatorLib/ATI;../CMP_CompressonatorLib/BASIS;../CMP_CompressonatorLib/BC6H;../CMP_CompressonatorLib/BC7;../CMP_CompressonatorLib/Block;../CMP_CompressonatorLib/Buffer;../CMP_CompressonatorLib/Common;../CMP_CompressonatorLib/DXT;../CMP_CompressonatorLib/DXTC;../CMP_CompressonatorLib/ETC;../CMP_CompressonatorLib/ETC/etcpack;../CMP_CompressonatorLib/GT;../CMP_CompressonatorLib/APC;../CMP_Framework/Common;../CMP_Framework/Common/half;../Source/CMP_Transcoder/basis_universal;../Header/CMP_Math;../../Common/Src;../cmp_core/shaders;../cmp_core/source;../Header/Codec/BASIS;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common MultiThreaded true ProgramDatabase @@ -919,8 +919,8 @@ MaxSpeed true true - USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;USE_GTC;USE_APC;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN64;NDEBUG;_LIB;%(PreprocessorDefinitions) - ../;../CMP_CompressonatorLib;../CMP_CompressonatorLib/ASTC;../CMP_CompressonatorLib/ASTC/ARM;../CMP_CompressonatorLib/ATC;../CMP_CompressonatorLib/ATI;../CMP_CompressonatorLib/BASIS;../CMP_CompressonatorLib/BC6H;../CMP_CompressonatorLib/BC7;../CMP_CompressonatorLib/Block;../CMP_CompressonatorLib/Buffer;../CMP_CompressonatorLib/Common;../CMP_CompressonatorLib/DXT;../CMP_CompressonatorLib/DXTC;../CMP_CompressonatorLib/ETC;../CMP_CompressonatorLib/ETC/etcpack;../CMP_CompressonatorLib/GT;../CMP_CompressonatorLib/APC;../CMP_Framework/Common;../CMP_Framework/Common/half;../Source/CMP_Transcoder/basis_universal;../Header/CMP_Math;../../Common/Src;../Header/Codec/BASIS;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common + USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN64;NDEBUG;_LIB;%(PreprocessorDefinitions) + ../;../CMP_CompressonatorLib;../CMP_CompressonatorLib/ASTC;../CMP_CompressonatorLib/ASTC/ARM;../CMP_CompressonatorLib/ATC;../CMP_CompressonatorLib/ATI;../CMP_CompressonatorLib/BASIS;../CMP_CompressonatorLib/BC6H;../CMP_CompressonatorLib/BC7;../CMP_CompressonatorLib/Block;../CMP_CompressonatorLib/Buffer;../CMP_CompressonatorLib/Common;../CMP_CompressonatorLib/DXT;../CMP_CompressonatorLib/DXTC;../CMP_CompressonatorLib/ETC;../CMP_CompressonatorLib/ETC/etcpack;../CMP_CompressonatorLib/GT;../CMP_CompressonatorLib/APC;../CMP_Framework/Common;../CMP_Framework/Common/half;../Source/CMP_Transcoder/basis_universal;../Header/CMP_Math;../../Common/Src;../cmp_core/shaders;../cmp_core/source;../Header/Codec/BASIS;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common MultiThreaded true ProgramDatabase @@ -954,8 +954,8 @@ MaxSpeed true true - USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;USE_GTC;USE_APC;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN64;NDEBUG;_LIB;%(PreprocessorDefinitions) - ../;../CMP_CompressonatorLib;../CMP_CompressonatorLib/ASTC;../CMP_CompressonatorLib/ASTC/ARM;../CMP_CompressonatorLib/ATC;../CMP_CompressonatorLib/ATI;../CMP_CompressonatorLib/BASIS;../CMP_CompressonatorLib/BC6H;../CMP_CompressonatorLib/BC7;../CMP_CompressonatorLib/Block;../CMP_CompressonatorLib/Buffer;../CMP_CompressonatorLib/Common;../CMP_CompressonatorLib/DXT;../CMP_CompressonatorLib/DXTC;../CMP_CompressonatorLib/ETC;../CMP_CompressonatorLib/ETC/etcpack;../CMP_CompressonatorLib/GT;../CMP_CompressonatorLib/APC;../CMP_Framework/Common;../CMP_Framework/Common/half;../Source/CMP_Transcoder/basis_universal;../Header/CMP_Math;../../Common/Src;../Header/Codec/BASIS;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common + USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN64;NDEBUG;_LIB;%(PreprocessorDefinitions) + ../;../CMP_CompressonatorLib;../CMP_CompressonatorLib/ASTC;../CMP_CompressonatorLib/ASTC/ARM;../CMP_CompressonatorLib/ATC;../CMP_CompressonatorLib/ATI;../CMP_CompressonatorLib/BASIS;../CMP_CompressonatorLib/BC6H;../CMP_CompressonatorLib/BC7;../CMP_CompressonatorLib/Block;../CMP_CompressonatorLib/Buffer;../CMP_CompressonatorLib/Common;../CMP_CompressonatorLib/DXT;../CMP_CompressonatorLib/DXTC;../CMP_CompressonatorLib/ETC;../CMP_CompressonatorLib/ETC/etcpack;../CMP_CompressonatorLib/GT;../CMP_CompressonatorLib/APC;../CMP_Framework/Common;../CMP_Framework/Common/half;../Source/CMP_Transcoder/basis_universal;../Header/CMP_Math;../../Common/Src;../cmp_core/shaders;../cmp_core/source;../Header/Codec/BASIS;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common MultiThreadedDLL true ProgramDatabase @@ -989,8 +989,8 @@ MaxSpeed true true - USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;USE_GTC;USE_APC;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN64;NDEBUG;_LIB;%(PreprocessorDefinitions) - ../;../CMP_CompressonatorLib;../CMP_CompressonatorLib/ASTC;../CMP_CompressonatorLib/ASTC/ARM;../CMP_CompressonatorLib/ATC;../CMP_CompressonatorLib/ATI;../CMP_CompressonatorLib/BASIS;../CMP_CompressonatorLib/BC6H;../CMP_CompressonatorLib/BC7;../CMP_CompressonatorLib/Block;../CMP_CompressonatorLib/Buffer;../CMP_CompressonatorLib/Common;../CMP_CompressonatorLib/DXT;../CMP_CompressonatorLib/DXTC;../CMP_CompressonatorLib/ETC;../CMP_CompressonatorLib/ETC/etcpack;../CMP_CompressonatorLib/GT;../CMP_CompressonatorLib/APC;../CMP_Framework/Common;../CMP_Framework/Common/half;../Source/CMP_Transcoder/basis_universal;../Header/CMP_Math;../../Common/Src;../Header/Codec/BASIS;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common + USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN64;NDEBUG;_LIB;%(PreprocessorDefinitions) + ../;../CMP_CompressonatorLib;../CMP_CompressonatorLib/ASTC;../CMP_CompressonatorLib/ASTC/ARM;../CMP_CompressonatorLib/ATC;../CMP_CompressonatorLib/ATI;../CMP_CompressonatorLib/BASIS;../CMP_CompressonatorLib/BC6H;../CMP_CompressonatorLib/BC7;../CMP_CompressonatorLib/Block;../CMP_CompressonatorLib/Buffer;../CMP_CompressonatorLib/Common;../CMP_CompressonatorLib/DXT;../CMP_CompressonatorLib/DXTC;../CMP_CompressonatorLib/ETC;../CMP_CompressonatorLib/ETC/etcpack;../CMP_CompressonatorLib/GT;../CMP_CompressonatorLib/APC;../CMP_Framework/Common;../CMP_Framework/Common/half;../Source/CMP_Transcoder/basis_universal;../Header/CMP_Math;../../Common/Src;../cmp_core/shaders;../cmp_core/source;../Header/Codec/BASIS;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common MultiThreadedDLL true ProgramDatabase @@ -1066,12 +1066,15 @@ + + + @@ -1079,6 +1082,7 @@ + @@ -1169,12 +1173,15 @@ + + + @@ -1182,6 +1189,7 @@ + @@ -1217,6 +1225,7 @@ + diff --git a/vs2017/cmp_compressonatorlib.vcxproj.filters b/vs2017/cmp_compressonatorlib.vcxproj.filters index 1b2dc10a7..fba47009e 100644 --- a/vs2017/cmp_compressonatorlib.vcxproj.filters +++ b/vs2017/cmp_compressonatorlib.vcxproj.filters @@ -352,6 +352,18 @@ Source Files\Codec\APC + + Source Files\Codec\Buffer + + + Source Files\Codec\Buffer + + + Source Files\Codec\Buffer + + + Source Files\Codec\Buffer + @@ -681,6 +693,21 @@ Source Files\Codec\APC + + Source Files\Codec\Buffer + + + Source Files\Codec\Buffer + + + Source Files\Codec\Buffer + + + Source Files\Codec\Buffer + + + Common + diff --git a/vs2017/cmp_framework.vcxproj b/vs2017/cmp_framework.vcxproj index b0bfabc2f..597e8b03f 100644 --- a/vs2017/cmp_framework.vcxproj +++ b/vs2017/cmp_framework.vcxproj @@ -490,7 +490,7 @@ Level4 Disabled - USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN32;_DEBUG;_LIB;HALF_NO_STD;_ITERATOR_DEBUG_LEVEL=2;%(PreprocessorDefinitions) + USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;CMP_USE_XMMINTRIN;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN32;_DEBUG;_LIB;HALF_NO_STD;_ITERATOR_DEBUG_LEVEL=2;%(PreprocessorDefinitions) ./;../;../Applications/_Libs/CMP_Math;../CMP_Core/source;../CMP_Core/shaders;../CMP_Framework;../CMP_Framework/Common;../CMP_Framework/Common/half;../Applications/_Plugins/Common;../CMP_CompressonatorLib;../CMP_CompressonatorLib/Common;../CMP_Core/source../CMP_Core/source../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common true MultiThreadedDebug @@ -532,7 +532,7 @@ Level4 Disabled - USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN32;_DEBUG;_LIB;HALF_NO_STD;_ITERATOR_DEBUG_LEVEL=2;%(PreprocessorDefinitions) + USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;CMP_USE_XMMINTRIN;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN32;_DEBUG;_LIB;HALF_NO_STD;_ITERATOR_DEBUG_LEVEL=2;%(PreprocessorDefinitions) ./;../;../Applications/_Libs/CMP_Math;../CMP_Core/source;../CMP_Core/shaders;../CMP_Framework;../CMP_Framework/Common;../CMP_Framework/Common/half;../Applications/_Plugins/Common;../CMP_CompressonatorLib;../CMP_CompressonatorLib/Common;../CMP_Core/source../CMP_Core/source../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common true MultiThreadedDebug @@ -574,7 +574,7 @@ Level4 Disabled - USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN32;_DEBUG;_LIB;HALF_NO_STD;_ITERATOR_DEBUG_LEVEL=2;%(PreprocessorDefinitions) + USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;CMP_USE_XMMINTRIN;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN32;_DEBUG;_LIB;HALF_NO_STD;_ITERATOR_DEBUG_LEVEL=2;%(PreprocessorDefinitions) ./;../;../Applications/_Libs/CMP_Math;../CMP_Core/source;../CMP_Core/shaders;../CMP_Framework;../CMP_Framework/Common;../CMP_Framework/Common/half;../Applications/_Plugins/Common;../CMP_CompressonatorLib;../CMP_CompressonatorLib/Common;../CMP_Core/source../CMP_Core/source../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common true MultiThreadedDebugDLL @@ -617,7 +617,7 @@ Level4 Disabled - USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN32;_DEBUG;_LIB;HALF_NO_STD;_ITERATOR_DEBUG_LEVEL=2;%(PreprocessorDefinitions) + USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;CMP_USE_XMMINTRIN;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN32;_DEBUG;_LIB;HALF_NO_STD;_ITERATOR_DEBUG_LEVEL=2;%(PreprocessorDefinitions) ./;../;../Applications/_Libs/CMP_Math;../CMP_Core/source;../CMP_Core/shaders;../CMP_Framework;../CMP_Framework/Common;../CMP_Framework/Common/half;../Applications/_Plugins/Common;../CMP_CompressonatorLib;../CMP_CompressonatorLib/Common;../CMP_Core/source../CMP_Core/source../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common true MultiThreadedDebugDLL @@ -659,7 +659,7 @@ Level4 Disabled - USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN64;_DEBUG;_LIB;HALF_NO_STD;_ITERATOR_DEBUG_LEVEL=2;%(PreprocessorDefinitions) + USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;CMP_USE_XMMINTRIN;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN64;_DEBUG;_LIB;HALF_NO_STD;_ITERATOR_DEBUG_LEVEL=2;%(PreprocessorDefinitions) ./;../;../Applications/_Libs/CMP_Math;../CMP_Core/source;../CMP_Core/shaders;../CMP_Framework;../CMP_Framework/Common;../CMP_Framework/Common/half;../Applications/_Plugins/Common;../CMP_CompressonatorLib;../CMP_CompressonatorLib/Common;../CMP_Core/source../CMP_Core/source../Header/Codec/BASIS;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common ProgramDatabase true @@ -695,7 +695,7 @@ Level4 Disabled - USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN64;_DEBUG;_LIB;HALF_NO_STD;_ITERATOR_DEBUG_LEVEL=2;%(PreprocessorDefinitions) + USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;CMP_USE_XMMINTRIN;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN64;_DEBUG;_LIB;HALF_NO_STD;_ITERATOR_DEBUG_LEVEL=2;%(PreprocessorDefinitions) ./;../;../Applications/_Libs/CMP_Math;../CMP_Core/source;../CMP_Core/shaders;../CMP_Framework;../CMP_Framework/Common;../CMP_Framework/Common/half;../Applications/_Plugins/Common;../CMP_CompressonatorLib;../CMP_CompressonatorLib/Common;../CMP_Core/source../CMP_Core/source../Header/Codec/BASIS;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common ProgramDatabase true @@ -729,7 +729,7 @@ Level4 Disabled - USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN64;_DEBUG;_LIB;HALF_NO_STD;_ITERATOR_DEBUG_LEVEL=2;%(PreprocessorDefinitions) + USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;CMP_USE_XMMINTRIN;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN64;_DEBUG;_LIB;HALF_NO_STD;_ITERATOR_DEBUG_LEVEL=2;%(PreprocessorDefinitions) ./;../;../Applications/_Libs/CMP_Math;../CMP_Core/source;../CMP_Core/shaders;../CMP_Framework;../CMP_Framework/Common;../CMP_Framework/Common/half;../Applications/_Plugins/Common;../CMP_CompressonatorLib;../CMP_CompressonatorLib/Common;../CMP_Core/source../CMP_Core/source../Header/Codec/BASIS;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common ProgramDatabase true @@ -766,7 +766,7 @@ Level4 Disabled - USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN64;_DEBUG;_LIB;HALF_NO_STD;_ITERATOR_DEBUG_LEVEL=2;%(PreprocessorDefinitions) + USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;CMP_USE_XMMINTRIN;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN64;_DEBUG;_LIB;HALF_NO_STD;_ITERATOR_DEBUG_LEVEL=2;%(PreprocessorDefinitions) ./;../;../Applications/_Libs/CMP_Math;../CMP_Core/source;../CMP_Core/shaders;../CMP_Framework;../CMP_Framework/Common;../CMP_Framework/Common/half;../Applications/_Plugins/Common;../CMP_CompressonatorLib;../CMP_CompressonatorLib/Common;../CMP_Core/source../CMP_Core/source../Header/Codec/BASIS;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common ProgramDatabase true @@ -803,7 +803,7 @@ false - USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN32;NDEBUG;_LIB;HALF_NO_STD;%(PreprocessorDefinitions) + USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;CMP_USE_XMMINTRIN;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN32;NDEBUG;_LIB;HALF_NO_STD;%(PreprocessorDefinitions) ./;../;../Applications/_Libs/CMP_Math;../CMP_Core/source;../CMP_Core/shaders;../CMP_Framework;../CMP_Framework/Common;../CMP_Framework/Common/half;../Applications/_Plugins/Common;../CMP_CompressonatorLib;../CMP_CompressonatorLib/Common;../CMP_Core/source../CMP_Core/source../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common MultiThreaded true @@ -854,7 +854,7 @@ false - USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN32;NDEBUG;_LIB;HALF_NO_STD;%(PreprocessorDefinitions) + USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;CMP_USE_XMMINTRIN;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN32;NDEBUG;_LIB;HALF_NO_STD;%(PreprocessorDefinitions) ./;../;../Applications/_Libs/CMP_Math;../CMP_Core/source;../CMP_Core/shaders;../CMP_Framework;../CMP_Framework/Common;../CMP_Framework/Common/half;../Applications/_Plugins/Common;../CMP_CompressonatorLib;../CMP_CompressonatorLib/Common;../CMP_Core/source../CMP_Core/source../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common MultiThreaded true @@ -905,7 +905,7 @@ false - USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN32;NDEBUG;_LIB;HALF_NO_STD;%(PreprocessorDefinitions) + USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;CMP_USE_XMMINTRIN;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN32;NDEBUG;_LIB;HALF_NO_STD;%(PreprocessorDefinitions) ./;../;../Applications/_Libs/CMP_Math;../CMP_Core/source;../CMP_Core/shaders;../CMP_Framework;../CMP_Framework/Common;../CMP_Framework/Common/half;../Applications/_Plugins/Common;../CMP_CompressonatorLib;../CMP_CompressonatorLib/Common;../CMP_Core/source../CMP_Core/source../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common MultiThreadedDLL true @@ -956,7 +956,7 @@ false - USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN32;NDEBUG;_LIB;HALF_NO_STD;%(PreprocessorDefinitions) + USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;CMP_USE_XMMINTRIN;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN32;NDEBUG;_LIB;HALF_NO_STD;%(PreprocessorDefinitions) ./;../;../Applications/_Libs/CMP_Math;../CMP_Core/source;../CMP_Core/shaders;../CMP_Framework;../CMP_Framework/Common;../CMP_Framework/Common/half;../Applications/_Plugins/Common;../CMP_CompressonatorLib;../CMP_CompressonatorLib/Common;../CMP_Core/source../CMP_Core/source../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common MultiThreadedDLL true @@ -1006,7 +1006,7 @@ MaxSpeed true true - USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN64;NDEBUG;_LIB;%(PreprocessorDefinitions) + USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;CMP_USE_XMMINTRIN;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN64;NDEBUG;_LIB;%(PreprocessorDefinitions) ./;../;../Applications/_Libs/CMP_Math;../CMP_Core/source;../CMP_Core/shaders;../CMP_Framework;../CMP_Framework/Common;../CMP_Framework/Common/half;../Applications/_Plugins/Common;../CMP_CompressonatorLib;../CMP_CompressonatorLib/Common;../CMP_Core/source../CMP_Core/source../Header/Codec/BASIS;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common MultiThreaded true @@ -1044,7 +1044,7 @@ MaxSpeed true true - USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN64;NDEBUG;_LIB;%(PreprocessorDefinitions) + USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;CMP_USE_XMMINTRIN;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN64;NDEBUG;_LIB;%(PreprocessorDefinitions) ./;../;../Applications/_Libs/CMP_Math;../CMP_Core/source;../CMP_Core/shaders;../CMP_Framework;../CMP_Framework/Common;../CMP_Framework/Common/half;../Applications/_Plugins/Common;../CMP_CompressonatorLib;../CMP_CompressonatorLib/Common;../CMP_Core/source../CMP_Core/source../Header/Codec/BASIS;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common MultiThreaded true @@ -1082,7 +1082,7 @@ MaxSpeed true true - USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN64;NDEBUG;_LIB;%(PreprocessorDefinitions) + USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;CMP_USE_XMMINTRIN;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN64;NDEBUG;_LIB;%(PreprocessorDefinitions) ./;../;../Applications/_Libs/CMP_Math;../CMP_Core/source;../CMP_Core/shaders;../CMP_Framework;../CMP_Framework/Common;../CMP_Framework/Common/half;../Applications/_Plugins/Common;../CMP_CompressonatorLib;../CMP_CompressonatorLib/Common;../CMP_Core/source../CMP_Core/source../Header/Codec/BASIS;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common MultiThreadedDLL true @@ -1120,7 +1120,7 @@ MaxSpeed true true - USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN64;NDEBUG;_LIB;%(PreprocessorDefinitions) + USE_SSE;USE_SSE2;BASISU_NO_ITERATOR_DEBUG_LEVEL;CMP_USE_XMMINTRIN;BASISU_NO_ITERATOR_DEBUG_LEVELBASISU_NO_ITERATOR_DEBUG_LEVELWIN64;NDEBUG;_LIB;%(PreprocessorDefinitions) ./;../;../Applications/_Libs/CMP_Math;../CMP_Core/source;../CMP_Core/shaders;../CMP_Framework;../CMP_Framework/Common;../CMP_Framework/Common/half;../Applications/_Plugins/Common;../CMP_CompressonatorLib;../CMP_CompressonatorLib/Common;../CMP_Core/source../CMP_Core/source../Header/Codec/BASIS;../Header/Codec/Block;../Header/Codec/Buffer;../Header/Codec/DXT;../Header/Codec/DXTC;../Header/Codec/ETC;../Header/Codec/ETC/etcpack;../Header/Codec/GT;../Header/Internal;../Utils;../Source/Common MultiThreadedDLL true