diff --git a/data/crunch.7z b/data/crunch.7z index e2175e03..716610f3 100644 Binary files a/data/crunch.7z and b/data/crunch.7z differ diff --git a/include/btu/tex/crunch_texture.hpp b/include/btu/tex/crunch_texture.hpp index cebbc5e1..6006f178 100644 --- a/include/btu/tex/crunch_texture.hpp +++ b/include/btu/tex/crunch_texture.hpp @@ -42,6 +42,8 @@ class CrunchTexture [[nodiscard]] auto get_texture_type() const noexcept -> TextureType; [[nodiscard]] auto get_format_as_dxgi() const noexcept -> DXGI_FORMAT; + [[nodiscard]] auto has_opaque_alpha() const noexcept -> bool; + [[nodiscard]] auto operator==(const CrunchTexture &) const noexcept -> bool = default; private: diff --git a/src/tex/crunch_texture.cpp b/src/tex/crunch_texture.cpp index 1b00a3e5..7690672a 100644 --- a/src/tex/crunch_texture.cpp +++ b/src/tex/crunch_texture.cpp @@ -111,6 +111,49 @@ auto CrunchTexture::get_format_as_dxgi() const noexcept -> DXGI_FORMAT } } +auto CrunchTexture::has_opaque_alpha() const noexcept -> bool +{ + // If format doesn't have alpha, or the image is already compressed. + if (!tex_.has_alpha()) + return true; + + // Check if there are any miplevels at all. + [[unlikely]] if (!tex_.get_num_levels()) + return true; + + // Should catch everything. + const uint8 threshold = 254; + + for (auto face = 0u; face < tex_.get_num_faces(); face++) + { + const auto *mip = tex_.get_level(face, 0); + + // Get uncompressed image to check. + crnlib::image_u8 *img; + if (mip->is_packed()) + { + img = crnlib::crnlib_new(); + mip->get_unpacked_image(*img, 1); + } + else + img = mip->get_image(); + + // One last safety check. + [[unlikely]] if (img == nullptr) + return true; + + const auto pixels = img->get_pixels(); + + for (auto i = 0u; i < img->get_total_pixels(); i++) + { + if (pixels[i].a < threshold) + return false; + } + } + + return true; +} + auto load_crunch(Path path) noexcept -> tl::expected { CrunchTexture tex; diff --git a/src/tex/optimize.cpp b/src/tex/optimize.cpp index 0e4404ea..5bb989aa 100644 --- a/src/tex/optimize.cpp +++ b/src/tex/optimize.cpp @@ -178,11 +178,9 @@ template const Settings &sets, bool force_alpha) noexcept -> DXGI_FORMAT { - const auto &tex = file.get(); - return guess_best_format(file.get_format_as_dxgi(), sets.output_format, - GuessBestFormatArgs{.opaque_alpha = !tex.has_alpha(), + GuessBestFormatArgs{.opaque_alpha = file.has_opaque_alpha(), .allow_compressed = sets.compress && can_be_compressed(file), .force_alpha = force_alpha}); diff --git a/tests/tex/crunch.cpp b/tests/tex/crunch.cpp index d467bd95..af0f97ec 100644 --- a/tests/tex/crunch.cpp +++ b/tests/tex/crunch.cpp @@ -73,6 +73,27 @@ TEST_CASE("Crunch Tex Memory IO", "[src]") REQUIRE(*mem_data == fs_data); } +TEST_CASE("Crunch Tex has_opaque_alpha", "[src]") +{ + const auto dir = Path("crunch"); + const auto opaque_file = dir / "in" / u8"opaque_alpha.dds"; + const auto opaque_file_bc = dir / "in" / u8"opaque_alpha_compressed.dds"; + const auto not_opaque_file = dir / "in" / u8"not_opaque_alpha.dds"; + const auto not_opaque_file_bc = dir / "in" / u8"not_opaque_alpha_compressed.dds"; + + // Load. + auto opaque = require_expected(btu::tex::load_crunch(opaque_file)); + auto opaque_bc = require_expected(btu::tex::load_crunch(opaque_file_bc)); + auto not_opaque = require_expected(btu::tex::load_crunch(not_opaque_file)); + auto not_opaque_bc = require_expected(btu::tex::load_crunch(not_opaque_file_bc)); + + // Test alpha. + CHECK(opaque.has_opaque_alpha()); + CHECK(opaque_bc.has_opaque_alpha()); + CHECK_FALSE(not_opaque.has_opaque_alpha()); + CHECK_FALSE(not_opaque_bc.has_opaque_alpha()); +} + TEST_CASE("Crunch resize", "[src]") { test_expected_dir_crunch(u8"crunch_resize", [](auto &&tex) {