diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e03bed..203cba7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## v0.7.1 + +- Bump the versions of the libavif and libheif dependencies. + ## v0.7.0 - Replace deps.sh by cmake/Modules. diff --git a/CMakeLists.txt b/CMakeLists.txt index d056d84..e9a1ec3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ cmake_minimum_required(VERSION 3.20) project( codec-compare-gen LANGUAGES CXX - VERSION 0.7.0) + VERSION 0.7.1) set(CMAKE_CXX_STANDARD 17) # Options @@ -256,7 +256,6 @@ endif() if(CCGEN_ENABLE_WEBP2) include(LocalLibwebp2) - target_compile_definitions(libccgen PRIVATE HAS_WEBP2) target_link_libraries(libccgen webp2 imageio) if(CCGEN_ENABLE_WEBP) # libccgen also uses WebP encoding and decoding through libwebp2. @@ -397,12 +396,10 @@ if(NOT CCGEN_WASM) target_include_directories(are_images_equivalent PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(are_images_equivalent libccgen) - target_compile_definitions(are_images_equivalent PRIVATE HAS_WEBP2) add_executable(strip_metadata tools/strip_metadata.cc) target_include_directories(strip_metadata PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(strip_metadata libccgen) - target_compile_definitions(strip_metadata PRIVATE HAS_WEBP2) endif() # Tests @@ -435,7 +432,6 @@ if(CCGEN_BUILD_TESTING) target_include_directories(${TEST_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(${TEST_NAME} PRIVATE libccgen GTest::gtest) - target_compile_definitions(${TEST_NAME} PRIVATE HAS_WEBP2) if(${ARGC} EQUAL 3) add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/${ARGV1}/ diff --git a/cmake/Modules/LocalLibavif.cmake b/cmake/Modules/LocalLibavif.cmake index 63a1bde..f3616d8 100644 --- a/cmake/Modules/LocalLibavif.cmake +++ b/cmake/Modules/LocalLibavif.cmake @@ -4,10 +4,9 @@ include(CcgenFetchContent) FetchContent_Declare( libavif GIT_REPOSITORY "https://github.com/AOMediaCodec/libavif.git" - # GIT_TAG v1.4.1 does not contain the latest AVM tag. - GIT_TAG 257b1e45f979491d905786d80691ba33a7597290 + GIT_TAG v1.4.2 GIT_PROGRESS ON - GIT_SHALLOW OFF + GIT_SHALLOW ON UPDATE_COMMAND "") set(AVIF_BUILD_APPS diff --git a/cmake/Modules/LocalLibheif.cmake b/cmake/Modules/LocalLibheif.cmake index 70c9cf4..8cc8b05 100644 --- a/cmake/Modules/LocalLibheif.cmake +++ b/cmake/Modules/LocalLibheif.cmake @@ -4,7 +4,7 @@ include(CcgenFetchContent) FetchContent_Declare( libheif GIT_REPOSITORY "https://github.com/strukturag/libheif.git" - GIT_TAG v1.21.2 + GIT_TAG v1.23.0 GIT_PROGRESS ON GIT_SHALLOW ON UPDATE_COMMAND "") diff --git a/src/base.h b/src/base.h index 99e97db..935f0ae 100644 --- a/src/base.h +++ b/src/base.h @@ -142,12 +142,10 @@ struct LogError { // Can be used as follows: // OK_WP2_OR_RETURN(FuncThatReturnsWP2Status(), quiet); -#if defined(HAS_WEBP2) #define OK_WP2_OR_RETURN(STATUS, QUIET) \ const WP2Status CONCAT(checked_status, __LINE__) = (STATUS); \ CHECK_OR_RETURN(CONCAT(checked_status, __LINE__) == WP2_STATUS_OK, QUIET) \ << WP2GetStatusMessage(CONCAT(checked_status, __LINE__)) << " " -#endif // HAS_WEBP2 //------------------------------------------------------------------------------ diff --git a/src/codec.cc b/src/codec.cc index 7e2c984..b734472 100644 --- a/src/codec.cc +++ b/src/codec.cc @@ -26,6 +26,7 @@ #include #include +#include "imageio/anim_image_dec.h" #include "src/base.h" #include "src/codec_avif.h" #include "src/codec_avif_libheif.h" @@ -45,453 +46,70 @@ #include "src/framework.h" #include "src/task.h" #include "src/timer.h" - -#if defined(HAS_WEBP2) -#include "imageio/anim_image_dec.h" #include "src/wp2/base.h" -#endif // HAS_WEBP2 namespace codec_compare_gen { -std::string CodecName(Codec codec) { - switch (codec) { - case Codec::kWebp: - return "webp"; - case Codec::kWebp2: - return "webp2"; - case Codec::kJpegXl: - return "jpegxl"; - case Codec::kAvif: - return "avif"; - case Codec::kAvifSsim: - return "avifssim"; - case Codec::kAvifIq: - return "avifiq"; - case Codec::kAvifExp: - return "avifexp"; - case Codec::kAvifAvm: - return "avifavm"; - case Codec::kAvifLibheif: - return "aviflibheif"; - case Codec::kCombination: - return "combination"; - case Codec::kJpegturbo: - return "jpegturbo"; - case Codec::kJpegli: - return "jpegli"; - case Codec::kJpegsimple: - return "jpegsimple"; - case Codec::kJpegmoz: - return "jpegmoz"; - case Codec::kJp2: - return "jp2"; - case Codec::kFfv1: - return "ffv1"; - case Codec::kBasis: - return "basis"; - case Codec::kNumCodecs: - break; - } - assert(false); - return "unknown"; -} - -std::string CodecPrettyName(Codec codec, bool lossless, Subsampling subsampling, - int effort) { - const std::string subsampling_str = - (lossless && (subsampling == Subsampling::kDefault || - subsampling == Subsampling::k444)) - ? "" - : (subsampling == Subsampling::k444 ? " 4:4:4" : " 4:2:0"); - switch (codec) { - case Codec::kWebp: - return (lossless ? "WebP z" : "WebP m") + std::to_string(effort) + - subsampling_str; - case Codec::kWebp2: - return "WebP2 e" + std::to_string(effort) + subsampling_str; - case Codec::kJpegXl: - return "JPEG XL e" + std::to_string(effort); // Only 4:4:4. - case Codec::kAvif: - return "AVIF s" + std::to_string(effort) + subsampling_str; - case Codec::kAvifSsim: - return "AVIF tune=SSIM s" + std::to_string(effort) + subsampling_str; - case Codec::kAvifIq: - return "AVIF tune=IQ s" + std::to_string(effort) + subsampling_str; - case Codec::kAvifExp: - return "AVIFmini" + std::string(lossless ? "YCgCo" : "") + - (" s" + std::to_string(effort)) + subsampling_str; - case Codec::kAvifAvm: - // YCgCo-Re is also used with AVM but save column width by omitting it. - return "AVIFminiAVM s" + std::to_string(effort) + subsampling_str; - case Codec::kAvifLibheif: - return "AVIF libheif s" + std::to_string(effort) + subsampling_str; - case Codec::kCombination: - return "combination e" + std::to_string(effort) + subsampling_str; - case Codec::kJpegturbo: - return "TurboJPEG" + subsampling_str; // No effort setting. - case Codec::kJpegli: - return "Jpegli" + subsampling_str; // No effort setting. - case Codec::kJpegsimple: - return "SimpleJPEG m" + std::to_string(effort) + subsampling_str; - case Codec::kJpegmoz: - return "MozJPEG" + subsampling_str; // No effort setting. - case Codec::kJp2: - return "JPEG2000" + subsampling_str; // No effort setting. - case Codec::kFfv1: - return "FFV1" + subsampling_str; // No effort setting. - case Codec::kBasis: - return "Basis"; // No effort setting, only 4:4:4. - case Codec::kNumCodecs: - break; - } - assert(false); - return "unknown" + subsampling_str; -} - -std::string CodecVersion(Codec codec) { +CodecMetadata GetCodecMetadata(Codec codec) { switch (codec) { case Codec::kWebp: - return WebpVersion(); + return GetWebpMetadata(); case Codec::kWebp2: - return Webp2Version(); + return GetWebp2Metadata(); case Codec::kJpegXl: - return JpegXLVersion(); + return GetJpegXlMetadata(); case Codec::kAvif: - return AvifVersion(); + return GetAvifMetadata(); case Codec::kAvifSsim: - return AvifVersion() + "_tunessim"; + return GetAvifSsimMetadata(); case Codec::kAvifIq: - return AvifVersion() + "_tuneiq"; + return GetAvifIqMetadata(); case Codec::kAvifExp: - return AvifVersion() + "_exp"; + return GetAvifExpMetadata(); case Codec::kAvifAvm: - return AvifVersion() + "_avm"; + return GetAvifAvmMetadata(); case Codec::kAvifLibheif: - return AvifLibheifVersion(); + return GetAvifLibheifMetadata(); case Codec::kCombination: - return CodecCombinationVersion(); + return GetCombinationMetadata(); case Codec::kJpegturbo: - return JpegturboVersion(); + return GetJpegturboMetadata(); case Codec::kJpegli: - return JpegliVersion(); + return GetJpegliMetadata(); case Codec::kJpegsimple: - return JpegsimpleVersion(); + return GetJpegsimpleMetadata(); case Codec::kJpegmoz: - return JpegmozVersion(); + return GetJpegmozMetadata(); case Codec::kJp2: - return OpenjpegVersion(); + return GetJp2Metadata(); case Codec::kFfv1: - return Ffv1Version(); + return GetFfv1Metadata(); case Codec::kBasis: - return BasisVersion(); + return GetBasisMetadata(); case Codec::kNumCodecs: break; } assert(false); - return "unknown"; + return CodecMetadata(); } StatusOr CodecFromName(const std::string& name, bool quiet) { - if (name == "webp") return Codec::kWebp; - if (name == "webp2") return Codec::kWebp2; - if (name == "jpegxl") return Codec::kJpegXl; - if (name == "avif") return Codec::kAvif; - if (name == "avifssim") return Codec::kAvifSsim; - if (name == "avifiq") return Codec::kAvifIq; - if (name == "avifexp") return Codec::kAvifExp; - if (name == "avifavm") return Codec::kAvifAvm; - if (name == "aviflibheif") return Codec::kAvifLibheif; - if (name == "combination") return Codec::kCombination; - if (name == "jpegturbo") return Codec::kJpegturbo; - if (name == "jpegli") return Codec::kJpegli; - if (name == "jpegsimple") return Codec::kJpegsimple; - if (name == "jpegmoz") return Codec::kJpegmoz; - if (name == "jp2") return Codec::kJp2; - if (name == "ffv1") return Codec::kFfv1; - CHECK_OR_RETURN(name == "basis", quiet) << "Unknown codec \"" << name << "\""; - return Codec::kBasis; -} - -std::vector CodecLossyQualities(Codec codec) { - switch (codec) { - case Codec::kWebp: - return WebpLossyQualities(); - case Codec::kWebp2: - return Webp2LossyQualities(); - case Codec::kJpegXl: - return JpegXLLossyQualities(); - case Codec::kAvif: - case Codec::kAvifSsim: - case Codec::kAvifExp: - case Codec::kAvifAvm: - return AvifLossyQualities(); - case Codec::kAvifIq: { - std::vector qualities = AvifLossyQualities(); - // Remove the lossless quality because it is rejected with tune=iq. - assert(qualities.back() == 100); - qualities.pop_back(); - return qualities; + for (int c = 0; c < static_cast(Codec::kNumCodecs); ++c) { + if (GetCodecMetadata(static_cast(c)).name == name) { + return static_cast(c); } - case Codec::kAvifLibheif: - return AvifLibheifLossyQualities(); - case Codec::kCombination: - return CodecCombinationLossyQualities(); - case Codec::kJpegturbo: - return JpegturboLossyQualities(); - case Codec::kJpegli: - return JpegliLossyQualities(); - case Codec::kJpegsimple: - return JpegsimpleLossyQualities(); - case Codec::kJpegmoz: - return JpegmozLossyQualities(); - case Codec::kJp2: - return OpenjpegLossyQualities(); - case Codec::kFfv1: - return {}; - case Codec::kBasis: - return BasisLossyQualities(); - case Codec::kNumCodecs: - break; } - assert(false); - return {}; + CHECK_OR_RETURN(false, quiet) << "Unknown codec \"" << name << "\""; + return Status::kUnknownError; } -std::string CodecExtension(Codec codec) { - switch (codec) { - case Codec::kWebp: - return "webp"; - case Codec::kWebp2: - return "wp2"; - case Codec::kJpegXl: - return "jxl"; - case Codec::kAvif: - return "avif"; - case Codec::kAvifSsim: - return "ssim.avif"; - case Codec::kAvifIq: - return "iq.avif"; - case Codec::kAvifExp: - // See "MIME type registration" Annex in - // "ISO/IEC 23008-12 3rd edition DAM 2 Low-overhead image file format" - // https://www.mpeg.org/wp-content/uploads/mpeg_meetings/149_Geneva/w24745.zip - return "hmg"; - case Codec::kAvifAvm: - return "avmf"; - case Codec::kAvifLibheif: - return "libheif.avif"; - case Codec::kCombination: - return "comb"; - case Codec::kJpegturbo: - return "turbo.jpg"; - case Codec::kJpegli: - return "li.jpg"; - case Codec::kJpegsimple: - return "s.jpg"; - case Codec::kJpegmoz: - return "moz.jpg"; - case Codec::kJp2: - return "jp2"; // Matches OPJ_CODEC_JP2 used in codec_openjpeg.cc. - case Codec::kFfv1: - return "ffv1"; - case Codec::kBasis: - return "basis"; - case Codec::kNumCodecs: - break; - } - assert(false); - return "unknown"; +std::string SubsamplingToPrettyString(bool lossless, Subsampling subsampling) { + return (lossless && (subsampling == Subsampling::kDefault || + subsampling == Subsampling::k444)) + ? "" + : (subsampling == Subsampling::k444 ? " 4:4:4" : " 4:2:0"); } -bool CodecIsSupportedByBrowsers(Codec codec) { - switch (codec) { - case Codec::kWebp: - case Codec::kAvif: - case Codec::kAvifSsim: - case Codec::kAvifIq: - case Codec::kAvifLibheif: - case Codec::kJpegturbo: - case Codec::kJpegli: - case Codec::kJpegsimple: - case Codec::kJpegmoz: - return true; - case Codec::kWebp2: - case Codec::kJpegXl: - case Codec::kAvifExp: - case Codec::kAvifAvm: - case Codec::kCombination: - case Codec::kJp2: - case Codec::kFfv1: - case Codec::kBasis: - return false; - case Codec::kNumCodecs: - break; - } - assert(false); - return false; -} - -#if defined(HAS_WEBP2) - -namespace { - -bool CodecSupportsBitDepth(Codec codec, uint32_t d) { - switch (codec) { - case Codec::kWebp: - return d == 8; - case Codec::kWebp2: - return d == 8 || d == 10; // 10 useless here. - case Codec::kJpegXl: - return d == 8 || d == 16; - case Codec::kAvif: - case Codec::kAvifSsim: - case Codec::kAvifIq: - case Codec::kAvifExp: - case Codec::kAvifAvm: - case Codec::kAvifLibheif: - return d == 8 || d == 10 || d == 12; // 10/12 useless here. - case Codec::kCombination: - return d == 8; - case Codec::kJpegturbo: - case Codec::kJpegli: - case Codec::kJpegsimple: - case Codec::kJpegmoz: - return d == 8; - case Codec::kJp2: - return d == 8 || d == 16; - case Codec::kFfv1: - return d == 8; - case Codec::kBasis: - return d == 8; - case Codec::kNumCodecs: - break; - } - assert(false); - return false; -} - -// Returns the 8-bit format layout required by the API of the given codec. -WP2SampleFormat CodecToNeededFormat(Codec codec, bool has_transparency) { - switch (codec) { - case Codec::kWebp: - return WebPPictureFormat(); - case Codec::kWebp2: - return WP2_ARGB_32; // Even for opaque images. - case Codec::kJpegXl: - return has_transparency ? WP2_RGBA_32 : WP2_RGB_24; - case Codec::kAvif: - case Codec::kAvifSsim: - case Codec::kAvifIq: - case Codec::kAvifExp: - case Codec::kAvifAvm: - case Codec::kAvifLibheif: - return has_transparency ? WP2_ARGB_32 : WP2_RGB_24; - case Codec::kCombination: - return WP2_ARGB_32; // Even for opaque images. - case Codec::kJpegturbo: - case Codec::kJpegli: - case Codec::kJpegsimple: - case Codec::kJpegmoz: - return WP2_RGB_24; - case Codec::kJp2: - return has_transparency ? WP2_RGBA_32 : WP2_RGB_24; - case Codec::kFfv1: - return WP2_BGRA_32; - case Codec::kBasis: - return has_transparency ? WP2_RGBA_32 : WP2_RGB_24; - case Codec::kNumCodecs: - break; - } - assert(false); - return WP2_FORMAT_NUM; -} - -// Variants of AVIF. -StatusOr EncodeAvifRegular(const TaskInput& input, - const Image& original_image, bool quiet) { - return EncodeAvif(input, original_image, /*minimized_image_box=*/false, - /*ycgco_re=*/false, /*tune=*/nullptr, /*avm=*/false, quiet); -} -StatusOr EncodeAvifSsim(const TaskInput& input, - const Image& original_image, bool quiet) { - return EncodeAvif(input, original_image, /*minimized_image_box=*/false, - /*ycgco_re=*/false, /*tune=*/"ssim", /*avm=*/false, quiet); -} -StatusOr EncodeAvifIq(const TaskInput& input, - const Image& original_image, bool quiet) { - return EncodeAvif(input, original_image, /*minimized_image_box=*/false, - /*ycgco_re=*/false, /*tune=*/"iq", /*avm=*/false, quiet); -} -StatusOr> DecodeAvifRegularOrExp( - const TaskInput& input, const WP2::Data& encoded_image, bool quiet) { - return DecodeAvif(input, encoded_image, /*avm=*/false, quiet); -} -StatusOr EncodeAvifExp(const TaskInput& input, - const Image& original_image, bool quiet) { - return EncodeAvif(input, original_image, /*minimized_image_box=*/true, - /*ycgco_re=*/true, /*tune=*/nullptr, /*avm=*/false, quiet); -} -StatusOr EncodeAvifAvm(const TaskInput& input, - const Image& original_image, bool quiet) { - return EncodeAvif(input, original_image, /*minimized_image_box=*/true, - /*ycgco_re=*/true, /*tune=*/nullptr, /*avm=*/true, quiet); -} -StatusOr> DecodeAvifAvm(const TaskInput& input, - const WP2::Data& encoded_image, - bool quiet) { - return DecodeAvif(input, encoded_image, /*avm=*/true, quiet); -} - -typedef StatusOr (*EncodeFunc)(const TaskInput&, const Image&, - bool quiet); -typedef StatusOr> (*DecodeFunc)(const TaskInput&, - const WP2::Data&, - bool quiet); - -EncodeFunc GetEncodeFunc(Codec codec) { - return codec == Codec::kWebp ? &EncodeWebp - : codec == Codec::kWebp2 ? &EncodeWebp2 - : codec == Codec::kJpegXl ? &EncodeJxl - : codec == Codec::kAvif ? &EncodeAvifRegular - : codec == Codec::kAvifSsim ? &EncodeAvifSsim - : codec == Codec::kAvifIq ? &EncodeAvifIq - : codec == Codec::kAvifExp ? &EncodeAvifExp - : codec == Codec::kAvifAvm ? &EncodeAvifAvm - : codec == Codec::kAvifLibheif ? &EncodeAvifLibheif - : codec == Codec::kCombination ? &EncodeCodecCombination - : codec == Codec::kJpegturbo ? &EncodeJpegturbo - : codec == Codec::kJpegli ? &EncodeJpegli - : codec == Codec::kJpegsimple ? &EncodeJpegsimple - : codec == Codec::kJpegmoz ? &EncodeJpegmoz - : codec == Codec::kJp2 ? &EncodeOpenjpeg - : codec == Codec::kFfv1 ? &EncodeFfv1 - : codec == Codec::kBasis ? &EncodeBasis - : nullptr; -} - -DecodeFunc GetDecodeFunc(Codec codec) { - return codec == Codec::kWebp ? &DecodeWebp - : codec == Codec::kWebp2 ? &DecodeWebp2 - : codec == Codec::kJpegXl ? &DecodeJxl - : codec == Codec::kAvif ? &DecodeAvifRegularOrExp - : codec == Codec::kAvifSsim ? &DecodeAvifRegularOrExp - : codec == Codec::kAvifIq ? &DecodeAvifRegularOrExp - : codec == Codec::kAvifExp ? &DecodeAvifRegularOrExp - : codec == Codec::kAvifAvm ? &DecodeAvifAvm - : codec == Codec::kAvifLibheif ? &DecodeAvifLibheif - : codec == Codec::kCombination ? &DecodeCodecCombination - : codec == Codec::kJpegturbo ? &DecodeJpegturbo - : codec == Codec::kJpegli ? &DecodeJpegli - : codec == Codec::kJpegsimple ? &DecodeJpegsimple - : codec == Codec::kJpegmoz ? &DecodeJpegmoz - : codec == Codec::kJp2 ? &DecodeOpenjpeg - : codec == Codec::kFfv1 ? &DecodeFfv1 - : codec == Codec::kBasis ? &DecodeBasis - : nullptr; -} - -} // namespace - StatusOr EncodeDecode(const TaskInput& input, const std::string& metric_binary_folder_path, size_t thread_id, EncodeMode encode_mode, @@ -499,8 +117,10 @@ StatusOr EncodeDecode(const TaskInput& input, TaskOutput task; task.task_input = input; - const WP2SampleFormat initial_format = CodecToNeededFormat( - input.codec_settings.codec, /*has_transparency=*/true); + const CodecMetadata& codec = GetCodecMetadata(input.codec_settings.codec); + const bool supports_transparency = codec.transparent_format != WP2_FORMAT_NUM; + const WP2SampleFormat initial_format = + supports_transparency ? codec.transparent_format : codec.opaque_format; ASSIGN_OR_RETURN(Image original_image, ReadStillImageOrAnimation(input.image_path.c_str(), initial_format, quiet)); @@ -510,7 +130,7 @@ StatusOr EncodeDecode(const TaskInput& input, has_transparency |= frame.pixels.HasTransparency(); } WP2SampleFormat needed_format = - CodecToNeededFormat(input.codec_settings.codec, has_transparency); + has_transparency ? codec.transparent_format : codec.opaque_format; if (initial_format != needed_format) { needed_format = WP2FormatAtbpc( needed_format, WP2Formatbpc(original_image.front().pixels.format())); @@ -520,16 +140,17 @@ StatusOr EncodeDecode(const TaskInput& input, CloneAs(original_image, needed_format, quiet)); } if (WP2Formatbpc(original_image.front().pixels.format()) == 16 && - !CodecSupportsBitDepth(input.codec_settings.codec, 16) && + !codec.supports_16bit && input.codec_settings.quality == kQualityLossless) { // The codec does not support 16-bit images. Consider the frames to be 8-bit // and twice as large. The compression rate is likely terrible. ASSIGN_OR_RETURN(original_image, SpreadTo8bit(original_image, quiet)); } - CHECK_OR_RETURN(CodecSupportsBitDepth( - input.codec_settings.codec, - WP2Formatbpc(original_image.front().pixels.format())), - quiet); + CHECK_OR_RETURN( + WP2Formatbpc(original_image.front().pixels.format()) == 8 || + (WP2Formatbpc(original_image.front().pixels.format()) == 16 && + codec.supports_16bit), + quiet); const Timer encoding_duration; WP2::Data encoded_image; @@ -543,9 +164,8 @@ StatusOr EncodeDecode(const TaskInput& input, file.read(reinterpret_cast(encoded_image.bytes), static_cast(length)); } else { - const EncodeFunc encode_func = GetEncodeFunc(input.codec_settings.codec); - CHECK_OR_RETURN(encode_func != nullptr, quiet); - ASSIGN_OR_RETURN(encoded_image, encode_func(input, original_image, quiet)); + CHECK_OR_RETURN(codec.encode != nullptr, quiet); + ASSIGN_OR_RETURN(encoded_image, codec.encode(input, original_image, quiet)); } task.encoding_duration = encoding_duration.seconds(); task.image_width = original_image.front().pixels.width(); @@ -556,11 +176,10 @@ StatusOr EncodeDecode(const TaskInput& input, const Timer decoding_duration; Image decoded_image; - const DecodeFunc decode_func = GetDecodeFunc(input.codec_settings.codec); - CHECK_OR_RETURN(decode_func != nullptr, quiet); + CHECK_OR_RETURN(codec.decode != nullptr, quiet); { ASSIGN_OR_RETURN(auto image_and_color_conversion_duration, - decode_func(input, encoded_image, quiet)); + codec.decode(input, encoded_image, quiet)); decoded_image = std::move(image_and_color_conversion_duration.first); task.decoding_color_conversion_duration = image_and_color_conversion_duration.second; @@ -575,7 +194,7 @@ StatusOr EncodeDecode(const TaskInput& input, encoded_image.size); // Some image formats are not supported by all major browsers. - if (!CodecIsSupportedByBrowsers(input.codec_settings.codec)) { + if (!codec.is_supported_by_browsers) { // Also write a PNG or WebP of the decoded image to disk for convenience. // Keep the PNG extension for the simplicity of the whole pipeline. decoded_path = input.encoded_path + ".png"; @@ -594,8 +213,7 @@ StatusOr EncodeDecode(const TaskInput& input, decoded_image, input, metric_binary_folder_path, DistortionMetric::kLibwebp2Psnr, thread_id, quiet)); CHECK_OR_RETURN(false, quiet) - << input.image_path << " encoded with " - << CodecName(task.task_input.codec_settings.codec) + << input.image_path << " encoded with " << codec.name << " was not decoded losslessly (PSNR " << psnr << "dB)"; } @@ -628,18 +246,20 @@ StatusOr> Encode(const uint8_t* argb, uint32_t width, Image original_image; original_image.emplace_back(std::move(buffer), /*duration_ms=*/0); - const bool has_transparency = original_image.front().pixels.HasTransparency(); + const bool supports_transparency = + GetCodecMetadata(codec).transparent_format != WP2_FORMAT_NUM; const WP2SampleFormat needed_format = - CodecToNeededFormat(codec, has_transparency); + supports_transparency && original_image.front().pixels.HasTransparency() + ? GetCodecMetadata(codec).transparent_format + : GetCodecMetadata(codec).opaque_format; if (original_image.front().pixels.format() != needed_format) { ASSIGN_OR_RETURN(original_image, CloneAs(original_image, needed_format, quiet)); } - const EncodeFunc encode_func = GetEncodeFunc(input.codec_settings.codec); - CHECK_OR_RETURN(encode_func != nullptr, quiet); - ASSIGN_OR_RETURN(WP2::Data encoded_image, - encode_func(input, original_image, quiet)); + CHECK_OR_RETURN(GetCodecMetadata(codec).encode != nullptr, quiet); + ASSIGN_OR_RETURN(WP2::Data encoded_image, GetCodecMetadata(codec).encode( + input, original_image, quiet)); return std::vector(encoded_image.bytes, encoded_image.bytes + encoded_image.size); @@ -665,24 +285,4 @@ StatusOr> DecodeToArgb(const uint8_t* encoded_image, return output; } -#else - -StatusOr EncodeDecode(const TaskInput&, const std::string&, size_t, - EncodeMode, bool quiet) { - CHECK_OR_RETURN(false, quiet) - << "Encoding/decoding images requires HAS_WEBP2"; -} - -StatusOr> Encode(const uint8_t*, uint32_t, uint32_t, Codec, - Subsampling, int, int, bool quiet) { - CHECK_OR_RETURN(false, quiet) << "Encoding images requires HAS_WEBP2"; -} - -StatusOr> DecodeToArgb(const uint8_t*, size_t, uint32_t*, - uint32_t*, bool quiet) { - CHECK_OR_RETURN(false, quiet) << "Decoding images requires HAS_WEBP2"; -} - -#endif // HAS_WEBP2 - } // namespace codec_compare_gen diff --git a/src/codec.h b/src/codec.h index 29d3f58..46516a1 100644 --- a/src/codec.h +++ b/src/codec.h @@ -18,21 +18,40 @@ #include #include #include +#include #include #include "src/base.h" +#include "src/frame.h" #include "src/task.h" +#include "src/wp2/base.h" namespace codec_compare_gen { -std::string CodecName(Codec codec); -std::string CodecPrettyName(Codec codec, bool lossless, Subsampling subsampling, - int effort); -std::string CodecVersion(Codec codec); +struct CodecMetadata { + const char* name; + + std::string (*pretty_name)(bool lossless, Subsampling subsampling, + int effort); + std::string (*version)(); + std::vector (*lossy_qualities)(); + const char* extension; + bool is_supported_by_browsers; + bool supports_16bit; + WP2SampleFormat opaque_format; + WP2SampleFormat transparent_format; // WP2_FORMAT_NUM if no alpha support. + + // Returns the encoded image bytes. + StatusOr (*encode)(const TaskInput&, const Image&, bool quiet); + // Returns the decoded image pixels and the color conversion duration if any. + StatusOr> (*decode)(const TaskInput&, + const WP2::Data&, bool quiet); +}; + +CodecMetadata GetCodecMetadata(Codec codec); StatusOr CodecFromName(const std::string& name, bool quiet); -std::vector CodecLossyQualities(Codec codec); -std::string CodecExtension(Codec codec); -bool CodecIsSupportedByBrowsers(Codec codec); + +std::string SubsamplingToPrettyString(bool lossless, Subsampling subsampling); enum class EncodeMode { kEncode, kEncodeAndSaveToDisk, kLoadFromDisk }; diff --git a/src/codec_avif.cc b/src/codec_avif.cc index 3836919..fee8b0c 100644 --- a/src/codec_avif.cc +++ b/src/codec_avif.cc @@ -22,14 +22,12 @@ #include #include "src/base.h" +#include "src/codec.h" #include "src/frame.h" #include "src/serialization.h" #include "src/task.h" #include "src/timer.h" - -#if defined(HAS_WEBP2) #include "src/wp2/base.h" -#endif #if defined(HAS_AVIF) #include "avif/avif.h" @@ -37,6 +35,36 @@ #endif namespace codec_compare_gen { +namespace { + +std::string AvifPrettyName(bool lossless, Subsampling subsampling, int effort) { + return "AVIF s" + std::to_string(effort) + + SubsamplingToPrettyString(lossless, subsampling); +} + +std::string AvifSsimPrettyName(bool lossless, Subsampling subsampling, + int effort) { + return "AVIF tune=SSIM s" + std::to_string(effort) + + SubsamplingToPrettyString(lossless, subsampling); +} + +std::string AvifIqPrettyName(bool lossless, Subsampling subsampling, + int effort) { + return "AVIF tune=IQ s" + std::to_string(effort) + + SubsamplingToPrettyString(lossless, subsampling); +} + +std::string AvifExpPrettyName(bool lossless, Subsampling subsampling, + int effort) { + return "AVIFmini s" + std::to_string(effort) + + SubsamplingToPrettyString(lossless, subsampling); +} + +std::string AvifAvmPrettyName(bool lossless, Subsampling subsampling, + int effort) { + return "AVIFminiAVM s" + std::to_string(effort) + + SubsamplingToPrettyString(lossless, subsampling); +} std::string AvifVersion() { #if defined(HAS_AVIF) @@ -48,23 +76,29 @@ std::string AvifVersion() { #endif } +std::string AvifSsimVersion() { return AvifVersion() + "_tunessim"; } +std::string AvifIqVersion() { return AvifVersion() + "_tuneiq"; } +std::string AvifExpVersion() { return AvifVersion() + "_exp"; } +std::string AvifAvmVersion() { return AvifVersion() + "_avm"; } + std::vector AvifLossyQualities() { std::vector qualities(64); for (int i = 0; i < qualities.size(); ++i) { - // Reverse avifQualityToQuantizer(): - // quantizer = ((100 - quality) * 63 + 50) / 100; qualities[i] = ((63 - i) * 100 + 63 / 2) / 63; } std::reverse(qualities.begin(), qualities.end()); - return qualities; // [0:63] (63 is lossless but in YUV so RGB is lossy). + return qualities; } -#if defined(HAS_WEBP2) +std::vector AvifIqLossyQualities() { + std::vector qualities = AvifLossyQualities(); + assert(!qualities.empty() && qualities.back() == 100); + qualities.pop_back(); + return qualities; +} #if defined(HAS_AVIF) -namespace { - StatusOr WP2SampleFormatToAvifRGBFormat(WP2SampleFormat format) { if (format == WP2_Argb_32) return AVIF_RGB_FORMAT_ARGB; if (format == WP2_ARGB_32) return AVIF_RGB_FORMAT_ARGB; @@ -157,8 +191,6 @@ class RwData : public avifRWData { ~RwData() { avifRWDataFree(this); } }; -} // namespace - StatusOr EncodeAvif(const TaskInput& input, const Image& original_image, bool minimized_image_box, bool ycgco_re, @@ -246,7 +278,6 @@ StatusOr> DecodeAvif(const TaskInput& input, } return std::pair(std::move(image), color_conversion_duration); } - #else StatusOr EncodeAvif(const TaskInput&, const Image&, bool, bool, const char*, bool, bool quiet) { @@ -259,6 +290,121 @@ StatusOr> DecodeAvif(const TaskInput&, } #endif // HAS_AVIF -#endif // HAS_WEBP2 +StatusOr EncodeAvifRegular(const TaskInput& input, + const Image& original_image, bool quiet) { + return EncodeAvif(input, original_image, /*minimized_image_box=*/false, + /*ycgco_re=*/false, /*tune=*/nullptr, /*avm=*/false, quiet); +} +StatusOr EncodeAvifSsim(const TaskInput& input, + const Image& original_image, bool quiet) { + return EncodeAvif(input, original_image, /*minimized_image_box=*/false, + /*ycgco_re=*/false, /*tune=*/"ssim", /*avm=*/false, quiet); +} +StatusOr EncodeAvifIq(const TaskInput& input, + const Image& original_image, bool quiet) { + return EncodeAvif(input, original_image, /*minimized_image_box=*/false, + /*ycgco_re=*/false, /*tune=*/"iq", /*avm=*/false, quiet); +} +StatusOr> DecodeAvifRegularOrExp( + const TaskInput& input, const WP2::Data& encoded_image, bool quiet) { + return DecodeAvif(input, encoded_image, /*avm=*/false, quiet); +} +StatusOr EncodeAvifExp(const TaskInput& input, + const Image& original_image, bool quiet) { + return EncodeAvif(input, original_image, /*minimized_image_box=*/true, + /*ycgco_re=*/true, /*tune=*/nullptr, /*avm=*/false, quiet); +} +StatusOr EncodeAvifAvm(const TaskInput& input, + const Image& original_image, bool quiet) { + return EncodeAvif(input, original_image, /*minimized_image_box=*/true, + /*ycgco_re=*/true, /*tune=*/nullptr, /*avm=*/true, quiet); +} +StatusOr> DecodeAvifAvm(const TaskInput& input, + const WP2::Data& encoded_image, + bool quiet) { + return DecodeAvif(input, encoded_image, /*avm=*/true, quiet); +} + +} // namespace + +CodecMetadata GetAvifMetadata() { + return CodecMetadata{ + "avif", + AvifPrettyName, + AvifVersion, + AvifLossyQualities, + "avif", + /*is_supported_by_browsers=*/true, + /*supports_16bit=*/false, + /*opaque_format=*/WP2_RGB_24, + /*transparent_format=*/WP2_ARGB_32, + EncodeAvifRegular, + DecodeAvifRegularOrExp, + }; +} + +CodecMetadata GetAvifSsimMetadata() { + return CodecMetadata{ + "avifssim", + AvifSsimPrettyName, + AvifSsimVersion, + AvifLossyQualities, + "ssim.avif", + /*is_supported_by_browsers=*/true, + /*supports_16bit=*/false, + /*opaque_format=*/WP2_RGB_24, + /*transparent_format=*/WP2_ARGB_32, + EncodeAvifSsim, + DecodeAvifRegularOrExp, + }; +} + +CodecMetadata GetAvifIqMetadata() { + return CodecMetadata{ + "avifiq", + AvifIqPrettyName, + AvifIqVersion, + AvifIqLossyQualities, + "iq.avif", + /*is_supported_by_browsers=*/true, + /*supports_16bit=*/false, + /*opaque_format=*/WP2_RGB_24, + /*transparent_format=*/WP2_ARGB_32, + EncodeAvifIq, + DecodeAvifRegularOrExp, + }; +} + +CodecMetadata GetAvifExpMetadata() { + return CodecMetadata{ + "avifexp", + AvifExpPrettyName, + AvifExpVersion, + AvifLossyQualities, + "hmg", + /*is_supported_by_browsers=*/false, + /*supports_16bit=*/false, + /*opaque_format=*/WP2_RGB_24, + /*transparent_format=*/WP2_ARGB_32, + EncodeAvifExp, + DecodeAvifRegularOrExp, + }; +} + +CodecMetadata GetAvifAvmMetadata() { + return CodecMetadata{ + "avifavm", + AvifAvmPrettyName, + AvifAvmVersion, + AvifLossyQualities, + "avmf", + /*is_supported_by_browsers=*/false, + /*supports_16bit=*/false, + /*opaque_format=*/WP2_RGB_24, + /*transparent_format=*/WP2_ARGB_32, + EncodeAvifAvm, + DecodeAvifAvm, + }; +} } // namespace codec_compare_gen diff --git a/src/codec_avif.h b/src/codec_avif.h index 45dd075..a9f93cb 100644 --- a/src/codec_avif.h +++ b/src/codec_avif.h @@ -15,34 +15,15 @@ #ifndef SRC_CODEC_AVIF_H_ #define SRC_CODEC_AVIF_H_ -#include -#include -#include - -#include "src/base.h" -#include "src/frame.h" -#include "src/task.h" - -#if defined(HAS_WEBP2) -#include "src/wp2/base.h" -#endif +#include "src/codec.h" namespace codec_compare_gen { -std::string AvifVersion(); - -std::vector AvifLossyQualities(); - -#if defined(HAS_WEBP2) -StatusOr EncodeAvif(const TaskInput& input, - const Image& original_image, - bool minimized_image_box, bool ycgco_re, - const char* tune, bool avm, bool quiet); -// Returns the decoded image and the color conversion duration. -StatusOr> DecodeAvif(const TaskInput& input, - const WP2::Data& encoded_image, - bool avm, bool quiet); -#endif // HAS_WEBP2 +CodecMetadata GetAvifMetadata(); +CodecMetadata GetAvifSsimMetadata(); +CodecMetadata GetAvifIqMetadata(); +CodecMetadata GetAvifExpMetadata(); +CodecMetadata GetAvifAvmMetadata(); } // namespace codec_compare_gen diff --git a/src/codec_avif_libheif.cc b/src/codec_avif_libheif.cc index db46688..7e843f1 100644 --- a/src/codec_avif_libheif.cc +++ b/src/codec_avif_libheif.cc @@ -28,13 +28,11 @@ #include #include "src/base.h" +#include "src/codec.h" #include "src/frame.h" #include "src/task.h" #include "src/timer.h" - -#if defined(HAS_WEBP2) #include "src/wp2/base.h" -#endif #if defined(HAS_HEIF) #include "libheif/heif_aux_images.h" @@ -48,6 +46,13 @@ #endif namespace codec_compare_gen { +namespace { + +std::string AvifLibheifPrettyName(bool lossless, Subsampling subsampling, + int effort) { + return "AVIF libheif s" + std::to_string(effort) + + SubsamplingToPrettyString(lossless, subsampling); +} std::string AvifLibheifVersion() { #if defined(HAS_HEIF) @@ -67,12 +72,8 @@ std::vector AvifLibheifLossyQualities() { return qualities; // [0:63] (63 is lossless but in YUV so RGB is lossy). } -#if defined(HAS_WEBP2) - #if defined(HAS_HEIF) -namespace { - // heif_cxx.h already provides a C++ API but it relies on throwing exceptions. using HeifImage = std::unique_ptr; using HeifContext = std::unique_ptr; @@ -131,8 +132,6 @@ heif_error writer_callback(heif_context* ctx, const void* data, size_t size, : heif_error_Memory_allocation_error}; } -} // namespace - StatusOr EncodeAvifLibheif(const TaskInput& input, const Image& original_image, bool quiet) { const bool lossless = input.codec_settings.quality == kQualityLossless; @@ -277,6 +276,22 @@ StatusOr> DecodeAvifLibheif(const TaskInput&, } #endif // HAS_HEIF -#endif // HAS_WEBP2 +} // namespace + +CodecMetadata GetAvifLibheifMetadata() { + return CodecMetadata{ + "aviflibheif", + AvifLibheifPrettyName, + AvifLibheifVersion, + AvifLibheifLossyQualities, + "libheif.avif", + /*is_supported_by_browsers=*/true, + /*supports_16bit=*/false, + /*opaque_format=*/WP2_RGB_24, + /*transparent_format=*/WP2_ARGB_32, + EncodeAvifLibheif, + DecodeAvifLibheif, + }; +} } // namespace codec_compare_gen diff --git a/src/codec_avif_libheif.h b/src/codec_avif_libheif.h index cfa1d9c..225b5a4 100644 --- a/src/codec_avif_libheif.h +++ b/src/codec_avif_libheif.h @@ -15,30 +15,11 @@ #ifndef SRC_CODEC_AVIF_LIBHEIF_H_ #define SRC_CODEC_AVIF_LIBHEIF_H_ -#include -#include -#include - -#include "src/base.h" -#include "src/frame.h" -#include "src/task.h" - -#if defined(HAS_WEBP2) -#include "src/wp2/base.h" -#endif +#include "src/codec.h" namespace codec_compare_gen { -std::string AvifLibheifVersion(); - -std::vector AvifLibheifLossyQualities(); - -#if defined(HAS_WEBP2) -StatusOr EncodeAvifLibheif(const TaskInput& input, - const Image& original_image, bool quiet); -StatusOr> DecodeAvifLibheif( - const TaskInput& input, const WP2::Data& encoded_image, bool quiet); -#endif // HAS_WEBP2 +CodecMetadata GetAvifLibheifMetadata(); } // namespace codec_compare_gen diff --git a/src/codec_basis.cc b/src/codec_basis.cc index 09de2b3..5df4ab3 100644 --- a/src/codec_basis.cc +++ b/src/codec_basis.cc @@ -21,13 +21,11 @@ #include #include "src/base.h" +#include "src/codec.h" #include "src/frame.h" #include "src/serialization.h" #include "src/task.h" - -#if defined(HAS_WEBP2) #include "src/wp2/base.h" -#endif #if defined(HAS_BASIS) #include "encoder/basisu_comp.h" @@ -37,6 +35,9 @@ #endif namespace codec_compare_gen { +namespace { + +std::string BasisPrettyName(bool, Subsampling, int) { return "Basis"; } std::string BasisVersion() { #if defined(HAS_BASIS) @@ -46,6 +47,21 @@ std::string BasisVersion() { #endif } +std::vector BasisLossyQualities() { +#if defined(HAS_BASIS) + const uint32_t kBasisQualityMin = basisu::BASISU_QUALITY_MIN; + const uint32_t kBasisQualityMax = basisu::BASISU_QUALITY_MAX; +#else + const uint32_t kBasisQualityMin = 1; + const uint32_t kBasisQualityMax = 255; +#endif + std::vector qualities(kBasisQualityMax - kBasisQualityMin + 1); + std::iota(qualities.begin(), qualities.end(), kBasisQualityMin); + return qualities; +} + +} // namespace + BasisContext::BasisContext(bool enabled) : enabled(enabled) { #if defined(HAS_BASIS) if (enabled) { @@ -61,20 +77,7 @@ BasisContext::~BasisContext() { #endif } -std::vector BasisLossyQualities() { -#if defined(HAS_BASIS) - const uint32_t kBasisQualityMin = basisu::BASISU_QUALITY_MIN; - const uint32_t kBasisQualityMax = basisu::BASISU_QUALITY_MAX; -#else - const uint32_t kBasisQualityMin = 1; - const uint32_t kBasisQualityMax = 255; -#endif - std::vector qualities(kBasisQualityMax - kBasisQualityMin + 1); - std::iota(qualities.begin(), qualities.end(), kBasisQualityMin); - return qualities; -} - -#if defined(HAS_WEBP2) +namespace { #if defined(HAS_BASIS) @@ -205,6 +208,22 @@ StatusOr> DecodeBasis(const TaskInput&, } #endif // HAS_BASIS -#endif // HAS_WEBP2 +} // namespace + +CodecMetadata GetBasisMetadata() { + return CodecMetadata{ + "basis", + BasisPrettyName, + BasisVersion, + BasisLossyQualities, + "basis", + /*is_supported_by_browsers=*/false, + /*supports_16bit=*/false, + /*opaque_format=*/WP2_RGB_24, + /*transparent_format=*/WP2_RGBA_32, + EncodeBasis, + DecodeBasis, + }; +} } // namespace codec_compare_gen diff --git a/src/codec_basis.h b/src/codec_basis.h index fc42f9c..002941b 100644 --- a/src/codec_basis.h +++ b/src/codec_basis.h @@ -15,22 +15,10 @@ #ifndef SRC_CODEC_BASIS_H_ #define SRC_CODEC_BASIS_H_ -#include -#include -#include - -#include "src/base.h" -#include "src/frame.h" -#include "src/task.h" - -#if defined(HAS_WEBP2) -#include "src/wp2/base.h" -#endif +#include "src/codec.h" namespace codec_compare_gen { -std::string BasisVersion(); - class BasisContext { public: BasisContext(bool enabled); @@ -40,15 +28,7 @@ class BasisContext { bool enabled; }; -std::vector BasisLossyQualities(); - -#if defined(HAS_WEBP2) -StatusOr EncodeBasis(const TaskInput& input, - const Image& original_image, bool quiet); -StatusOr> DecodeBasis(const TaskInput& input, - const WP2::Data& encoded_image, - bool quiet); -#endif // HAS_WEBP2 +CodecMetadata GetBasisMetadata(); } // namespace codec_compare_gen diff --git a/src/codec_combination.cc b/src/codec_combination.cc index d412957..30bd2c5 100644 --- a/src/codec_combination.cc +++ b/src/codec_combination.cc @@ -22,21 +22,27 @@ #include #include "src/base.h" +#include "src/codec.h" #include "src/codec_jpegxl.h" #include "src/codec_webp.h" #include "src/codec_webp2.h" #include "src/frame.h" #include "src/task.h" #include "src/timer.h" - -#if defined(HAS_WEBP2) #include "src/wp2/base.h" -#endif namespace codec_compare_gen { +namespace { + +std::string CodecCombinationPrettyName(bool lossless, Subsampling subsampling, + int effort) { + return "combination e" + std::to_string(effort) + + SubsamplingToPrettyString(lossless, subsampling); +} std::string CodecCombinationVersion() { - return WebpVersion() + "_" + Webp2Version() + "_" + JpegXLVersion(); + return GetWebpMetadata().version() + "_" + GetWebp2Metadata().version() + + "_" + GetJpegXlMetadata().version(); } std::vector CodecCombinationLossyQualities() { @@ -45,10 +51,6 @@ std::vector CodecCombinationLossyQualities() { return qualities; // [5:95] so that every quality works with each codec. } -#if defined(HAS_WEBP2) - -namespace { - bool HasTransparency(const Image& image) { for (const Frame& frame : image) { if (frame.pixels.HasTransparency()) return true; @@ -61,8 +63,6 @@ struct CodecEffort { int effort; }; -} // namespace - StatusOr EncodeCodecCombination(const TaskInput& input, const Image& original_image, bool quiet) { @@ -98,14 +98,18 @@ StatusOr EncodeCodecCombination(const TaskInput& input, using std::swap; if (specialized_input.codec_settings.codec == Codec::kWebp) { - ASSIGN_OR_RETURN(const Image image, - CloneAs(original_image, WebPPictureFormat(), quiet)); + ASSIGN_OR_RETURN( + const Image image, + CloneAs(original_image, + GetCodecMetadata(Codec::kWebp).transparent_format, quiet)); ASSIGN_OR_RETURN(WP2::Data candidate, - EncodeWebp(specialized_input, image, quiet)); + GetCodecMetadata(Codec::kWebp) + .encode(specialized_input, image, quiet)); if (data.IsEmpty() || candidate.size < data.size) swap(data, candidate); } else if (specialized_input.codec_settings.codec == Codec::kWebp2) { ASSIGN_OR_RETURN(WP2::Data candidate, - EncodeWebp2(specialized_input, original_image, quiet)); + GetCodecMetadata(Codec::kWebp2) + .encode(specialized_input, original_image, quiet)); if (data.IsEmpty() || candidate.size < data.size) swap(data, candidate); } else { assert(specialized_input.codec_settings.codec == Codec::kJpegXl); @@ -114,7 +118,8 @@ StatusOr EncodeCodecCombination(const TaskInput& input, ASSIGN_OR_RETURN(const Image image, CloneAs(original_image, jxl_format, quiet)); ASSIGN_OR_RETURN(WP2::Data candidate, - EncodeJxl(specialized_input, image, quiet)); + GetCodecMetadata(Codec::kJpegXl) + .encode(specialized_input, image, quiet)); if (data.IsEmpty() || candidate.size < data.size) swap(data, candidate); } } @@ -126,7 +131,9 @@ StatusOr> DecodeCodecCombination( if (encoded_image.size >= 12 && std::equal(encoded_image.bytes, encoded_image.bytes + 4, "RIFF") && std::equal(encoded_image.bytes + 8, encoded_image.bytes + 12, "WEBP")) { - ASSIGN_OR_RETURN(auto webp, DecodeWebp(input, encoded_image, quiet)); + ASSIGN_OR_RETURN( + auto webp, + GetCodecMetadata(Codec::kWebp).decode(input, encoded_image, quiet)); const Timer color_conversion_duration; ASSIGN_OR_RETURN(Image clone, CloneAs(webp.first, WP2_ARGB_32, quiet)); return std::pair( @@ -135,16 +142,37 @@ StatusOr> DecodeCodecCombination( if (encoded_image.size >= 3 && encoded_image.bytes[0] == 0xf4 && encoded_image.bytes[1] == 0xff && encoded_image.bytes[2] == 0x6f) { - return DecodeWebp2(input, encoded_image, quiet); + return GetCodecMetadata(Codec::kWebp2).decode(input, encoded_image, quiet); } - ASSIGN_OR_RETURN(auto jxl, DecodeJxl(input, encoded_image, quiet)); + ASSIGN_OR_RETURN( + auto jxl, + GetCodecMetadata(Codec::kJpegXl).decode(input, encoded_image, quiet)); const Timer color_conversion_duration; ASSIGN_OR_RETURN(Image clone, CloneAs(jxl.first, WP2_ARGB_32, quiet)); return std::pair( std::move(clone), jxl.second + color_conversion_duration.seconds()); } -#endif // HAS_WEBP2 +} // namespace + +CodecMetadata GetCombinationMetadata() { + return CodecMetadata{ + "combination", + CodecCombinationPrettyName, + CodecCombinationVersion, + CodecCombinationLossyQualities, + "comb", + GetWebpMetadata().is_supported_by_browsers && + GetWebp2Metadata().is_supported_by_browsers && + GetJpegXlMetadata().is_supported_by_browsers, + GetWebpMetadata().supports_16bit && GetWebp2Metadata().supports_16bit && + GetJpegXlMetadata().supports_16bit, + /*opaque_format=*/WP2_ARGB_32, + /*transparent_format=*/WP2_ARGB_32, + EncodeCodecCombination, + DecodeCodecCombination, + }; +} } // namespace codec_compare_gen diff --git a/src/codec_combination.h b/src/codec_combination.h index cf2aa2c..aee2fbd 100644 --- a/src/codec_combination.h +++ b/src/codec_combination.h @@ -15,39 +15,14 @@ #ifndef SRC_CODEC_COMBINATION_H_ #define SRC_CODEC_COMBINATION_H_ -#include -#include -#include - -#include "src/base.h" -#include "src/frame.h" -#include "src/task.h" - -#if defined(HAS_WEBP2) -#include "src/wp2/base.h" -#endif +#include "src/codec.h" namespace codec_compare_gen { -std::string CodecCombinationVersion(); - -std::vector CodecCombinationLossyQualities(); - -#if defined(HAS_WEBP2) - // Tries encoding the original_image as WebP, WebP2 and/or JpegXL at various // efforts depending on input.codec_settings.effort. Returns the smallest // encoded payload. -StatusOr EncodeCodecCombination(const TaskInput& input, - const Image& original_image, - bool quiet); - -// Returns the encoded_image decoded by the first successful codec among WebP, -// WebP2 and JpegXL and the color conversion duration. -StatusOr> DecodeCodecCombination( - const TaskInput& input, const WP2::Data& encoded_image, bool quiet); - -#endif // HAS_WEBP2 +CodecMetadata GetCombinationMetadata(); } // namespace codec_compare_gen diff --git a/src/codec_ffv1.cc b/src/codec_ffv1.cc index 818fea6..be60b5b 100644 --- a/src/codec_ffv1.cc +++ b/src/codec_ffv1.cc @@ -14,21 +14,20 @@ #include "src/codec_ffv1.h" -#if defined(HAS_FFV1) && defined(HAS_WEBP2) +#if defined(HAS_FFV1) #include #include #include #endif #include #include +#include #include "src/base.h" +#include "src/codec.h" #include "src/frame.h" #include "src/task.h" - -#if defined(HAS_WEBP2) #include "src/wp2/base.h" -#endif #if defined(HAS_FFV1) extern "C" { @@ -37,6 +36,11 @@ extern "C" { #endif namespace codec_compare_gen { +namespace { + +std::string Ffv1PrettyName(bool lossless, Subsampling subsampling, int) { + return "FFV1" + SubsamplingToPrettyString(lossless, subsampling); +} std::string Ffv1Version() { #if defined(HAS_FFV1) @@ -48,12 +52,10 @@ std::string Ffv1Version() { #endif } -#if defined(HAS_WEBP2) +std::vector Ffv1LossyQualities() { return {}; } #if defined(HAS_FFV1) -namespace { - struct Ffv1 { public: Ffv1(bool is_encoding) @@ -102,8 +104,6 @@ struct Ffv1Container { uint32_t extradata_size; }; -} // namespace - StatusOr EncodeFfv1(const TaskInput& input, const Image& original_image, bool quiet) { // TODO(yguyon): Support sequences. @@ -266,6 +266,22 @@ StatusOr> DecodeFfv1(const TaskInput&, } #endif // HAS_FFV1 -#endif // HAS_WEBP2 +} // namespace + +CodecMetadata GetFfv1Metadata() { + return CodecMetadata{ + "ffv1", + Ffv1PrettyName, + Ffv1Version, + Ffv1LossyQualities, + "ffv1", + /*is_supported_by_browsers=*/false, + /*supports_16bit=*/false, + /*opaque_format=*/WP2_BGRA_32, + /*transparent_format=*/WP2_FORMAT_NUM, // No alpha support. + EncodeFfv1, + DecodeFfv1, + }; +} } // namespace codec_compare_gen diff --git a/src/codec_ffv1.h b/src/codec_ffv1.h index 5a5e37b..d304ff1 100644 --- a/src/codec_ffv1.h +++ b/src/codec_ffv1.h @@ -15,31 +15,13 @@ #ifndef SRC_CODEC_FFV1_H_ #define SRC_CODEC_FFV1_H_ -#include -#include - -#include "src/base.h" -#include "src/frame.h" -#include "src/task.h" - -#if defined(HAS_WEBP2) -#include "src/wp2/base.h" -#endif +#include "src/codec.h" namespace codec_compare_gen { // FFV1 is a lossless intra-frame video coding format. The library libavcodec is // in the FFmpeg project. - -std::string Ffv1Version(); - -#if defined(HAS_WEBP2) -StatusOr EncodeFfv1(const TaskInput& input, - const Image& original_image, bool quiet); -StatusOr> DecodeFfv1(const TaskInput& input, - const WP2::Data& encoded_image, - bool quiet); -#endif // HAS_WEBP2 +CodecMetadata GetFfv1Metadata(); } // namespace codec_compare_gen diff --git a/src/codec_jpegli.cc b/src/codec_jpegli.cc index 84355ba..f17d3bb 100644 --- a/src/codec_jpegli.cc +++ b/src/codec_jpegli.cc @@ -20,19 +20,16 @@ #include #include #include -#include #include #include "src/base.h" +#include "src/codec.h" #include "src/codec_jpegturbo.h" #include "src/codec_jpegxl.h" #include "src/frame.h" #include "src/serialization.h" #include "src/task.h" - -#if defined(HAS_WEBP2) #include "src/wp2/base.h" -#endif #if defined(HAS_JPEGTURBO) #include "src/jpeglib.h" @@ -44,9 +41,14 @@ #endif namespace codec_compare_gen { +namespace { + +std::string JpegliPrettyName(bool lossless, Subsampling subsampling, int) { + return "Jpegli" + SubsamplingToPrettyString(lossless, subsampling); +} std::string JpegliVersion() { - return JpegXLVersion() + "_" + JpegturboVersion(); + return GetJpegXlMetadata().version() + "_" + GetJpegturboMetadata().version(); } std::vector JpegliLossyQualities() { @@ -55,12 +57,8 @@ std::vector JpegliLossyQualities() { return qualities; } -#if defined(HAS_WEBP2) - #if defined(HAS_JPEGLI) && defined(HAS_JPEGTURBO) -namespace { - void jpeg_catch_error(j_common_ptr cinfo) { // (*cinfo->err->output_message) (cinfo); // to print encountered errors jmp_buf* jpeg_jmpbuf = reinterpret_cast(cinfo->client_data); @@ -68,8 +66,6 @@ void jpeg_catch_error(j_common_ptr cinfo) { longjmp(*jpeg_jmpbuf, 1); } -} // namespace - StatusOr EncodeJpegli(const TaskInput& input, const Image& original_image, bool quiet) { CHECK_OR_RETURN(original_image.size() == 1, quiet); @@ -156,24 +152,29 @@ StatusOr EncodeJpegli(const TaskInput& input, return data; } -StatusOr> DecodeJpegli(const TaskInput& input, - const WP2::Data& encoded_image, - bool quiet) { - return DecodeJpegturbo(input, encoded_image, quiet); -} - #else StatusOr EncodeJpegli(const TaskInput&, const Image&, bool quiet) { CHECK_OR_RETURN(false, quiet) << "Encoding images requires HAS_JPEGLI and HAS_JPEGTURBO"; } -StatusOr> DecodeJpegli(const TaskInput&, - const WP2::Data&, bool quiet) { - CHECK_OR_RETURN(false, quiet) - << "Decoding images requires HAS_JPEGLI and HAS_JPEGTURBO"; -} #endif // HAS_JPEGLI && HAS_JPEGTURBO -#endif // HAS_WEBP2 +} // namespace + +CodecMetadata GetJpegliMetadata() { + return CodecMetadata{ + "jpegli", + JpegliPrettyName, + JpegliVersion, + JpegliLossyQualities, + "li.jpg", + /*is_supported_by_browsers=*/true, + /*supports_16bit=*/false, + /*opaque_format=*/WP2_RGB_24, + /*transparent_format=*/WP2_FORMAT_NUM, // No alpha support. + EncodeJpegli, + GetJpegturboMetadata().decode, + }; +} } // namespace codec_compare_gen diff --git a/src/codec_jpegli.h b/src/codec_jpegli.h index 6a9e035..d4628cd 100644 --- a/src/codec_jpegli.h +++ b/src/codec_jpegli.h @@ -15,31 +15,11 @@ #ifndef SRC_CODEC_JPEGLI_H_ #define SRC_CODEC_JPEGLI_H_ -#include -#include -#include - -#include "src/base.h" -#include "src/frame.h" -#include "src/task.h" - -#if defined(HAS_WEBP2) -#include "src/wp2/base.h" -#endif +#include "src/codec.h" namespace codec_compare_gen { -std::string JpegliVersion(); - -std::vector JpegliLossyQualities(); - -#if defined(HAS_WEBP2) -StatusOr EncodeJpegli(const TaskInput& input, - const Image& original_image, bool quiet); -StatusOr> DecodeJpegli(const TaskInput& input, - const WP2::Data& encoded_image, - bool quiet); -#endif // HAS_WEBP2 +CodecMetadata GetJpegliMetadata(); } // namespace codec_compare_gen diff --git a/src/codec_jpegmoz.cc b/src/codec_jpegmoz.cc index 9fd3b49..42b67ec 100644 --- a/src/codec_jpegmoz.cc +++ b/src/codec_jpegmoz.cc @@ -14,7 +14,7 @@ #include "src/codec_jpegmoz.h" -#if defined(HAS_JPEGMOZ) && defined(HAS_WEBP2) +#if defined(HAS_JPEGMOZ) #include #include #include @@ -25,21 +25,24 @@ #include #include "src/base.h" +#include "src/codec.h" #include "src/frame.h" -#if defined(HAS_JPEGMOZ) && defined(HAS_WEBP2) +#if defined(HAS_JPEGMOZ) #include "src/serialization.h" #endif #include "src/task.h" - -#if defined(HAS_WEBP2) #include "src/wp2/base.h" -#endif #if defined(HAS_JPEGMOZ) #include "jpeglib.h" #endif namespace codec_compare_gen { +namespace { + +std::string JpegmozPrettyName(bool lossless, Subsampling subsampling, int) { + return "MozJPEG" + SubsamplingToPrettyString(lossless, subsampling); +} std::string JpegmozVersion() { #if defined(HAS_JPEGMOZ) @@ -58,12 +61,8 @@ std::vector JpegmozLossyQualities() { return qualities; } -#if defined(HAS_WEBP2) - #if defined(HAS_JPEGMOZ) -namespace { - void jpeg_catch_error(j_common_ptr cinfo) { // (*cinfo->err->output_message) (cinfo); // to print encountered errors jmp_buf* jpeg_jmpbuf = reinterpret_cast(cinfo->client_data); @@ -71,8 +70,6 @@ void jpeg_catch_error(j_common_ptr cinfo) { longjmp(*jpeg_jmpbuf, 1); } -} // namespace - StatusOr EncodeJpegmoz(const TaskInput& input, const Image& original_image, bool quiet) { CHECK_OR_RETURN(original_image.size() == 1, quiet); @@ -214,6 +211,22 @@ StatusOr> DecodeJpegmoz(const TaskInput&, } #endif // HAS_JPEGMOZ -#endif // HAS_WEBP2 +} // namespace + +CodecMetadata GetJpegmozMetadata() { + return CodecMetadata{ + "jpegmoz", + JpegmozPrettyName, + JpegmozVersion, + JpegmozLossyQualities, + "moz.jpg", + /*is_supported_by_browsers=*/true, + /*supports_16bit=*/false, + /*opaque_format=*/WP2_RGB_24, + /*transparent_format=*/WP2_FORMAT_NUM, // No alpha support. + EncodeJpegmoz, + DecodeJpegmoz, + }; +} } // namespace codec_compare_gen diff --git a/src/codec_jpegmoz.h b/src/codec_jpegmoz.h index 5922e2a..d899202 100644 --- a/src/codec_jpegmoz.h +++ b/src/codec_jpegmoz.h @@ -15,31 +15,11 @@ #ifndef SRC_CODEC_JPEGMOZ_H_ #define SRC_CODEC_JPEGMOZ_H_ -#include -#include -#include - -#include "src/base.h" -#include "src/frame.h" -#include "src/task.h" - -#if defined(HAS_WEBP2) -#include "src/wp2/base.h" -#endif +#include "src/codec.h" namespace codec_compare_gen { -std::string JpegmozVersion(); - -std::vector JpegmozLossyQualities(); - -#if defined(HAS_WEBP2) -StatusOr EncodeJpegmoz(const TaskInput& input, - const Image& original_image, bool quiet); -StatusOr> DecodeJpegmoz(const TaskInput& input, - const WP2::Data& encoded_image, - bool quiet); -#endif // HAS_WEBP2 +CodecMetadata GetJpegmozMetadata(); } // namespace codec_compare_gen diff --git a/src/codec_jpegsimple.cc b/src/codec_jpegsimple.cc index fe231ad..291e401 100644 --- a/src/codec_jpegsimple.cc +++ b/src/codec_jpegsimple.cc @@ -22,26 +22,32 @@ #include #include "src/base.h" +#include "src/codec.h" #include "src/codec_jpegturbo.h" #include "src/frame.h" #include "src/serialization.h" #include "src/task.h" - -#if defined(HAS_WEBP2) #include "src/wp2/base.h" -#endif #if defined(HAS_JPEGSIMPLE) #include "src/sjpeg.h" #endif namespace codec_compare_gen { +namespace { + +std::string JpegsimplePrettyName(bool lossless, Subsampling subsampling, + int effort) { + return "SimpleJPEG m" + std::to_string(effort) + + SubsamplingToPrettyString(lossless, subsampling); +} std::string JpegsimpleVersion() { #if defined(HAS_JPEGSIMPLE) return std::to_string(SjpegVersion() / 1000000) + "." + std::to_string(SjpegVersion() % 10000 / 100) + "." + - std::to_string(SjpegVersion() % 100) + "_" + JpegturboVersion(); + std::to_string(SjpegVersion() % 100) + "_" + + GetJpegturboMetadata().version(); #else return "n/a"; #endif @@ -53,8 +59,6 @@ std::vector JpegsimpleLossyQualities() { return qualities; } -#if defined(HAS_WEBP2) - #if defined(HAS_JPEGSIMPLE) && defined(HAS_JPEGTURBO) StatusOr EncodeJpegsimple(const TaskInput& input, @@ -95,25 +99,30 @@ StatusOr EncodeJpegsimple(const TaskInput& input, return data; } -StatusOr> DecodeJpegsimple( - const TaskInput& input, const WP2::Data& encoded_image, bool quiet) { - return DecodeJpegturbo(input, encoded_image, quiet); -} - #else StatusOr EncodeJpegsimple(const TaskInput&, const Image&, bool quiet) { CHECK_OR_RETURN(false, quiet) << "Encoding images requires HAS_JPEGSIMPLE and HAS_JPEGTURBO"; } -StatusOr> DecodeJpegsimple(const TaskInput&, - const WP2::Data&, - bool quiet) { - CHECK_OR_RETURN(false, quiet) - << "Decoding images requires HAS_JPEGSIMPLE and HAS_JPEGTURBO"; -} #endif // HAS_JPEGSIMPLE && HAS_JPEGTURBO -#endif // HAS_WEBP2 +} // namespace + +CodecMetadata GetJpegsimpleMetadata() { + return CodecMetadata{ + "jpegsimple", + JpegsimplePrettyName, + JpegsimpleVersion, + JpegsimpleLossyQualities, + "s.jpg", + /*is_supported_by_browsers=*/true, + /*supports_16bit=*/false, + /*opaque_format=*/WP2_RGB_24, + /*transparent_format=*/WP2_FORMAT_NUM, // No alpha support. + EncodeJpegsimple, + GetJpegturboMetadata().decode, + }; +} } // namespace codec_compare_gen diff --git a/src/codec_jpegsimple.h b/src/codec_jpegsimple.h index 4759578..759130c 100644 --- a/src/codec_jpegsimple.h +++ b/src/codec_jpegsimple.h @@ -15,30 +15,11 @@ #ifndef SRC_CODEC_JPEGSIMPLE_H_ #define SRC_CODEC_JPEGSIMPLE_H_ -#include -#include -#include - -#include "src/base.h" -#include "src/frame.h" -#include "src/task.h" - -#if defined(HAS_WEBP2) -#include "src/wp2/base.h" -#endif +#include "src/codec.h" namespace codec_compare_gen { -std::string JpegsimpleVersion(); - -std::vector JpegsimpleLossyQualities(); - -#if defined(HAS_WEBP2) -StatusOr EncodeJpegsimple(const TaskInput& input, - const Image& original_image, bool quiet); -StatusOr> DecodeJpegsimple( - const TaskInput& input, const WP2::Data& encoded_image, bool quiet); -#endif // HAS_WEBP2 +CodecMetadata GetJpegsimpleMetadata(); } // namespace codec_compare_gen diff --git a/src/codec_jpegturbo.cc b/src/codec_jpegturbo.cc index f2de10b..7128e1f 100644 --- a/src/codec_jpegturbo.cc +++ b/src/codec_jpegturbo.cc @@ -21,13 +21,11 @@ #include #include "src/base.h" +#include "src/codec.h" #include "src/frame.h" #include "src/serialization.h" #include "src/task.h" - -#if defined(HAS_WEBP2) #include "src/wp2/base.h" -#endif #if defined(HAS_JPEGTURBO) #include "jconfigint.h" @@ -35,6 +33,11 @@ #endif namespace codec_compare_gen { +namespace { + +std::string JpegturboPrettyName(bool lossless, Subsampling subsampling, int) { + return "TurboJPEG" + SubsamplingToPrettyString(lossless, subsampling); +} std::string JpegturboVersion() { #if defined(HAS_JPEGTURBO) @@ -50,8 +53,6 @@ std::vector JpegturboLossyQualities() { return qualities; } -#if defined(HAS_WEBP2) - #if defined(HAS_JPEGTURBO) constexpr int kPitch = 0; @@ -141,6 +142,22 @@ StatusOr> DecodeJpegturbo(const TaskInput&, } #endif // HAS_JPEGTURBO -#endif // HAS_WEBP2 +} // namespace + +CodecMetadata GetJpegturboMetadata() { + return CodecMetadata{ + "jpegturbo", + JpegturboPrettyName, + JpegturboVersion, + JpegturboLossyQualities, + "turbo.jpg", + /*is_supported_by_browsers=*/true, + /*supports_16bit=*/false, + /*opaque_format=*/WP2_RGB_24, + /*transparent_format=*/WP2_FORMAT_NUM, // No alpha support. + EncodeJpegturbo, + DecodeJpegturbo, + }; +} } // namespace codec_compare_gen diff --git a/src/codec_jpegturbo.h b/src/codec_jpegturbo.h index 07359e9..bdd88a7 100644 --- a/src/codec_jpegturbo.h +++ b/src/codec_jpegturbo.h @@ -15,30 +15,11 @@ #ifndef SRC_CODEC_JPEGTURBO_H_ #define SRC_CODEC_JPEGTURBO_H_ -#include -#include -#include - -#include "src/base.h" -#include "src/frame.h" -#include "src/task.h" - -#if defined(HAS_WEBP2) -#include "src/wp2/base.h" -#endif +#include "src/codec.h" namespace codec_compare_gen { -std::string JpegturboVersion(); - -std::vector JpegturboLossyQualities(); - -#if defined(HAS_WEBP2) -StatusOr EncodeJpegturbo(const TaskInput& input, - const Image& original_image, bool quiet); -StatusOr> DecodeJpegturbo( - const TaskInput& input, const WP2::Data& encoded_image, bool quiet); -#endif // HAS_WEBP2 +CodecMetadata GetJpegturboMetadata(); } // namespace codec_compare_gen diff --git a/src/codec_jpegxl.cc b/src/codec_jpegxl.cc index 91acbed..7d1a47e 100644 --- a/src/codec_jpegxl.cc +++ b/src/codec_jpegxl.cc @@ -23,13 +23,11 @@ #include #include "src/base.h" +#include "src/codec.h" #include "src/frame.h" #include "src/task.h" #include "src/timer.h" - -#if defined(HAS_WEBP2) #include "src/wp2/base.h" -#endif #if defined(HAS_JPEGXL) #include "lib/include/jxl/codestream_header.h" @@ -42,6 +40,11 @@ #endif namespace codec_compare_gen { +namespace { + +std::string JpegXlPrettyName(bool, Subsampling, int effort) { + return "JPEG XL e" + std::to_string(effort); // Only 4:4:4. +} std::string JpegXLVersion() { #if defined(HAS_JPEGXL) @@ -59,8 +62,6 @@ std::vector JpegXLLossyQualities() { return qualities; // [0:99] because 100 is lossless. } -#if defined(HAS_WEBP2) - #if defined(HAS_JPEGXL) namespace { @@ -310,6 +311,22 @@ StatusOr> DecodeJxl(const TaskInput&, const WP2::Data&, } #endif // HAS_JPEGXL -#endif // HAS_WEBP2 +} // namespace + +CodecMetadata GetJpegXlMetadata() { + return CodecMetadata{ + "jpegxl", + JpegXlPrettyName, + JpegXLVersion, + JpegXLLossyQualities, + "jxl", + /*is_supported_by_browsers=*/false, + /*supports_16bit=*/true, + /*opaque_format=*/WP2_RGB_24, + /*transparent_format=*/WP2_RGBA_32, + EncodeJxl, + DecodeJxl, + }; +} } // namespace codec_compare_gen diff --git a/src/codec_jpegxl.h b/src/codec_jpegxl.h index 6c48486..a5f0c74 100644 --- a/src/codec_jpegxl.h +++ b/src/codec_jpegxl.h @@ -15,32 +15,11 @@ #ifndef SRC_CODEC_JPEGXL_H_ #define SRC_CODEC_JPEGXL_H_ -#include -#include -#include - -#include "src/base.h" -#include "src/frame.h" -#include "src/task.h" - -#if defined(HAS_WEBP2) -#include "src/wp2/base.h" -#endif +#include "src/codec.h" namespace codec_compare_gen { -std::string JpegXLVersion(); - -std::vector JpegXLLossyQualities(); - -#if defined(HAS_WEBP2) -StatusOr EncodeJxl(const TaskInput& input, - const Image& original_image, bool quiet); -// Returns the decoded image and the color conversion duration. -StatusOr> DecodeJxl(const TaskInput& input, - const WP2::Data& encoded_image, - bool quiet); -#endif // HAS_WEBP2 +CodecMetadata GetJpegXlMetadata(); } // namespace codec_compare_gen diff --git a/src/codec_openjpeg.cc b/src/codec_openjpeg.cc index 2ed1b64..6ef1913 100644 --- a/src/codec_openjpeg.cc +++ b/src/codec_openjpeg.cc @@ -14,7 +14,7 @@ #include "src/codec_openjpeg.h" -#if defined(HAS_OPENJPEG) && defined(HAS_WEBP2) +#if defined(HAS_OPENJPEG) #include #include #include @@ -32,18 +32,21 @@ #include #include "src/base.h" +#include "src/codec.h" #include "src/frame.h" #include "src/task.h" - -#if defined(HAS_WEBP2) #include "src/wp2/base.h" -#endif #if defined(HAS_OPENJPEG) #include "openjpeg.h" #endif namespace codec_compare_gen { +namespace { + +std::string OpenjpegPrettyName(bool lossless, Subsampling subsampling, int) { + return "JPEG2000" + SubsamplingToPrettyString(lossless, subsampling); +} std::string OpenjpegVersion() { #if defined(HAS_OPENJPEG) @@ -59,8 +62,6 @@ std::vector OpenjpegLossyQualities() { return qualities; } -#if defined(HAS_WEBP2) - #if defined(HAS_OPENJPEG) StatusOr EncodeOpenjpeg(const TaskInput& input, @@ -404,6 +405,22 @@ StatusOr> DecodeOpenjpeg(const TaskInput&, } #endif // HAS_OPENJPEG -#endif // HAS_WEBP2 +} // namespace + +CodecMetadata GetJp2Metadata() { + return CodecMetadata{ + "jp2", + OpenjpegPrettyName, + OpenjpegVersion, + OpenjpegLossyQualities, + "jp2", + /*is_supported_by_browsers=*/false, + /*supports_16bit=*/true, + /*opaque_format=*/WP2_RGB_24, + /*transparent_format=*/WP2_RGBA_32, + EncodeOpenjpeg, + DecodeOpenjpeg, + }; +} } // namespace codec_compare_gen diff --git a/src/codec_openjpeg.h b/src/codec_openjpeg.h index 9796e5a..528599d 100644 --- a/src/codec_openjpeg.h +++ b/src/codec_openjpeg.h @@ -15,32 +15,13 @@ #ifndef SRC_CODEC_OPENJPEG_H_ #define SRC_CODEC_OPENJPEG_H_ -#include -#include -#include - -#include "src/base.h" -#include "src/frame.h" -#include "src/task.h" - -#if defined(HAS_WEBP2) -#include "src/wp2/base.h" -#endif +#include "src/codec.h" namespace codec_compare_gen { // OpenJPEG is a codec for JPEG 2000 images. -std::string OpenjpegVersion(); - -std::vector OpenjpegLossyQualities(); - -#if defined(HAS_WEBP2) -StatusOr EncodeOpenjpeg(const TaskInput& input, - const Image& original_image, bool quiet); -StatusOr> DecodeOpenjpeg( - const TaskInput& input, const WP2::Data& encoded_image, bool quiet); -#endif // HAS_WEBP2 +CodecMetadata GetJp2Metadata(); } // namespace codec_compare_gen diff --git a/src/codec_webp.cc b/src/codec_webp.cc index b2101d6..fc1a2ab 100644 --- a/src/codec_webp.cc +++ b/src/codec_webp.cc @@ -24,13 +24,11 @@ #include #include "src/base.h" +#include "src/codec.h" #include "src/frame.h" #include "src/task.h" #include "src/timer.h" - -#if defined(HAS_WEBP2) #include "src/wp2/base.h" -#endif #if defined(HAS_WEBP) #include "src/webp/decode.h" @@ -41,14 +39,18 @@ #endif namespace codec_compare_gen { - namespace { + std::string VersionToString(int version) { return std::to_string((version >> 16) & 0xff) + "." + std::to_string((version >> 8) & 0xff) + "." + std::to_string(version & 0xff); } -} // namespace + +std::string WebpPrettyName(bool lossless, Subsampling subsampling, int effort) { + return (lossless ? "WebP z" : "WebP m") + std::to_string(effort) + + SubsamplingToPrettyString(lossless, subsampling); +} std::string WebpVersion() { #if defined(HAS_WEBP) @@ -68,8 +70,6 @@ std::vector WebpLossyQualities() { return qualities; // [0:100] } -#if defined(HAS_WEBP2) - WP2SampleFormat WebPPictureFormat() { const uint32_t is_little_endian = 1; return reinterpret_cast(&is_little_endian)[0] ? WP2_BGRA_32 @@ -78,8 +78,6 @@ WP2SampleFormat WebPPictureFormat() { #if defined(HAS_WEBP) -namespace { - // Returns a WebPPicture that points to the given ArgbBuffer. StatusOr ArgbBufferToWebPPicture(WP2::ArgbBuffer& buffer, bool quiet) { @@ -105,8 +103,6 @@ int WriterFunction(const uint8_t* data, size_t data_size, return bytes.Append(data, data_size) == WP2_STATUS_OK ? 1 : 0; } -} // namespace - StatusOr EncodeWebp(const TaskInput& input, const Image& original_image, bool quiet) { const bool lossless = input.codec_settings.quality == kQualityLossless; @@ -236,6 +232,22 @@ StatusOr> DecodeWebp(const TaskInput&, } #endif // HAS_WEBP -#endif // HAS_WEBP2 +} // namespace + +CodecMetadata GetWebpMetadata() { + return CodecMetadata{ + "webp", + WebpPrettyName, + WebpVersion, + WebpLossyQualities, + "webp", + /*is_supported_by_browsers=*/true, + /*supports_16bit=*/false, + WebPPictureFormat(), + WebPPictureFormat(), + EncodeWebp, + DecodeWebp, + }; +} } // namespace codec_compare_gen diff --git a/src/codec_webp.h b/src/codec_webp.h index 6a11cb5..bcd3115 100644 --- a/src/codec_webp.h +++ b/src/codec_webp.h @@ -15,34 +15,11 @@ #ifndef SRC_CODEC_WEBP_H_ #define SRC_CODEC_WEBP_H_ -#include -#include -#include - -#include "src/base.h" -#include "src/frame.h" -#include "src/task.h" - -#if defined(HAS_WEBP2) -#include "src/wp2/base.h" -#endif +#include "src/codec.h" namespace codec_compare_gen { -std::string WebpVersion(); - -std::vector WebpLossyQualities(); - -#if defined(HAS_WEBP2) -WP2SampleFormat WebPPictureFormat(); - -StatusOr EncodeWebp(const TaskInput& input, - const Image& original_image, bool quiet); -// Returns the decoded image and the color conversion duration. -StatusOr> DecodeWebp(const TaskInput& input, - const WP2::Data& encoded_image, - bool quiet); -#endif // HAS_WEBP2 +CodecMetadata GetWebpMetadata(); } // namespace codec_compare_gen diff --git a/src/codec_webp2.cc b/src/codec_webp2.cc index 2fb786c..133e3a8 100644 --- a/src/codec_webp2.cc +++ b/src/codec_webp2.cc @@ -22,44 +22,39 @@ #include #include "src/base.h" +#include "src/codec.h" #include "src/frame.h" #include "src/framework.h" #include "src/serialization.h" #include "src/task.h" - -#if defined(HAS_WEBP2) #include "src/wp2/base.h" #include "src/wp2/decode.h" #include "src/wp2/encode.h" #include "src/wp2/format_constants.h" -#endif // HAS_WEBP2 namespace codec_compare_gen { - namespace { + std::string VersionToString(int version) { return std::to_string((version >> 16) & 0xff) + "." + std::to_string((version >> 8) & 0xff) + "." + std::to_string(version & 0xff); } -} // namespace -std::string Webp2Version() { -#if defined(HAS_WEBP2) - return VersionToString(WP2GetVersion()); -#else - return "n/a"; -#endif // HAS_WEBP2 +std::string Webp2PrettyName(bool lossless, Subsampling subsampling, + int effort) { + return "WebP2 e" + std::to_string(effort) + + SubsamplingToPrettyString(lossless, subsampling); } +std::string Webp2Version() { return VersionToString(WP2GetVersion()); } + std::vector Webp2LossyQualities() { std::vector qualities(96); std::iota(qualities.begin(), qualities.end(), 0); return qualities; // [0:95] because [96:100] is near-lossless and lossless. } -#if defined(HAS_WEBP2) - StatusOr EncodeWebp2(const TaskInput& input, const Image& original_image, bool quiet) { WP2::Data data; @@ -138,6 +133,22 @@ StatusOr> DecodeWebp2(const TaskInput& input, return std::pair(std::move(image), 0); } -#endif // HAS_WEBP2 +} // namespace + +CodecMetadata GetWebp2Metadata() { + return CodecMetadata{ + "webp2", + Webp2PrettyName, + Webp2Version, + Webp2LossyQualities, + "wp2", + /*is_supported_by_browsers=*/false, + /*supports_16bit=*/false, + /*opaque_format=*/WP2_ARGB_32, + /*transparent_format=*/WP2_ARGB_32, + EncodeWebp2, + DecodeWebp2, + }; +} } // namespace codec_compare_gen diff --git a/src/codec_webp2.h b/src/codec_webp2.h index 3b25690..b51e1e6 100644 --- a/src/codec_webp2.h +++ b/src/codec_webp2.h @@ -15,32 +15,11 @@ #ifndef SRC_CODEC_WEBP2_H_ #define SRC_CODEC_WEBP2_H_ -#include -#include -#include - -#include "src/base.h" -#include "src/frame.h" -#include "src/task.h" - -#if defined(HAS_WEBP2) -#include "src/wp2/base.h" -#endif +#include "src/codec.h" namespace codec_compare_gen { -std::string Webp2Version(); - -std::vector Webp2LossyQualities(); - -#if defined(HAS_WEBP2) -StatusOr EncodeWebp2(const TaskInput& input, - const Image& original_image, bool quiet); -// Returns the decoded image and the color conversion duration. -StatusOr> DecodeWebp2(const TaskInput& input, - const WP2::Data& encoded_image, - bool quiet); -#endif // HAS_WEBP2 +CodecMetadata GetWebp2Metadata(); } // namespace codec_compare_gen diff --git a/src/distortion.cc b/src/distortion.cc index 26e3a89..4d901a2 100644 --- a/src/distortion.cc +++ b/src/distortion.cc @@ -29,22 +29,17 @@ #include #include +#include "imageio/image_enc.h" #include "src/base.h" #include "src/codec.h" #include "src/frame.h" #include "src/framework.h" #include "src/serialization.h" #include "src/task.h" - -#if defined(HAS_WEBP2) -#include "imageio/image_enc.h" #include "src/wp2/base.h" -#endif // HAS_WEBP2 namespace codec_compare_gen { -#if defined(HAS_WEBP2) - namespace { class FileDeleter { @@ -100,8 +95,8 @@ StatusOr GetLibwebp2Distortion(const WP2::ArgbBuffer& reference, CHECK_OR_RETURN(status == WP2_STATUS_OK, quiet) << "GetDistortion(" << metric << ") failed on " << task.image_path - << " and " << CodecName(task.codec_settings.codec) << " at effort " - << task.codec_settings.effort << ", chroma subsampling " + << " and " << GetCodecMetadata(task.codec_settings.codec).name + << " at effort " << task.codec_settings.effort << ", chroma subsampling " << SubsamplingToString(task.codec_settings.chroma_subsampling) << " and quality " << task.codec_settings.quality << ": " << WP2GetStatusMessage(status); @@ -115,8 +110,9 @@ StatusOr GetLibwebp2Distortion(const WP2::ArgbBuffer& reference, if (!quiet) { std::cerr << "Error: " << task.image_path << " was encoded or decoded with loss in " - << CodecName(task.codec_settings.codec) << " format at effort " - << task.codec_settings.effort << ", chroma subsampling " + << GetCodecMetadata(task.codec_settings.codec).name + << " format at effort " << task.codec_settings.effort + << ", chroma subsampling " << SubsamplingToString(task.codec_settings.chroma_subsampling) << " and quality " << task.codec_settings.quality << " (alpha " << distortion[0] << "dB, R " << distortion[1] << "dB, G " @@ -425,16 +421,4 @@ StatusOr PixelEquality(const Image& a, const Image& b, bool quiet) { return true; } -#else -StatusOr GetAverageDistortion(const std::string&, const Image&, - const std::string&, const Image&, - const TaskInput&, const std::string&, - DistortionMetric, size_t, bool quiet) { - CHECK_OR_RETURN(false, quiet) << "Computing distortions requires HAS_WEBP2"; -} -StatusOr PixelEquality(const Image&, const Image&, bool quiet) { - CHECK_OR_RETURN(false, quiet) << "Equality check requires HAS_WEBP2"; -} -#endif // HAS_WEBP2 - } // namespace codec_compare_gen diff --git a/src/distortion.h b/src/distortion.h index 650dbf5..a144156 100644 --- a/src/distortion.h +++ b/src/distortion.h @@ -21,10 +21,7 @@ #include "src/base.h" #include "src/frame.h" #include "src/task.h" - -#if defined(HAS_WEBP2) #include "src/wp2/base.h" -#endif namespace codec_compare_gen { @@ -40,10 +37,8 @@ StatusOr GetAverageDistortion( // They must have the same total duration. StatusOr PixelEquality(const Image& a, const Image& b, bool quiet); -#if defined(HAS_WEBP2) StatusOr PixelEquality(const WP2::ArgbBuffer& a, const WP2::ArgbBuffer& b, bool quiet); -#endif } // namespace codec_compare_gen diff --git a/src/frame.cc b/src/frame.cc index 156a32a..e27da87 100644 --- a/src/frame.cc +++ b/src/frame.cc @@ -15,8 +15,6 @@ #include "src/frame.h" #include - -#if defined(HAS_WEBP2) #include #include #include @@ -24,11 +22,11 @@ #include "imageio/anim_image_dec.h" #include "imageio/image_enc.h" #include "src/base.h" +#include "src/codec.h" #include "src/codec_webp.h" #include "src/distortion.h" #include "src/task.h" #include "src/wp2/base.h" -#endif // HAS_WEBP2 namespace codec_compare_gen { @@ -40,8 +38,6 @@ uint32_t GetDurationMs(const Image& image) { return duration_ms; } -#if defined(HAS_WEBP2) - StatusOr CloneAs(const Image& from, WP2SampleFormat format, bool quiet) { Image to; to.reserve(from.size()); @@ -173,7 +169,7 @@ Status WriteStillImageOrAnimation(const Image& image, const char* file_path, ? MakeView(image, quiet) : CloneAs(image, WP2_BGRA_32, quiet)); ASSIGN_OR_RETURN(const WP2::Data encoded_image, - EncodeWebp(input, bgra, quiet)); + GetCodecMetadata(Codec::kWebp).encode(input, bgra, quiet)); std::ofstream(file_path, std::ios::binary) .write(reinterpret_cast(encoded_image.bytes), encoded_image.size); @@ -181,6 +177,4 @@ Status WriteStillImageOrAnimation(const Image& image, const char* file_path, return Status::kOk; } -#endif // HAS_WEBP2 - } // namespace codec_compare_gen diff --git a/src/frame.h b/src/frame.h index c75c5c3..5fcded1 100644 --- a/src/frame.h +++ b/src/frame.h @@ -20,22 +20,17 @@ #include #include "src/base.h" - -#if defined(HAS_WEBP2) #include "src/wp2/base.h" -#endif namespace codec_compare_gen { struct Frame { -#if defined(HAS_WEBP2) Frame() = default; Frame(Frame&&) = default; Frame(WP2::ArgbBuffer&& pixels, uint32_t duration_ms) : pixels(std::move(pixels)), duration_ms(duration_ms) {}; WP2::ArgbBuffer pixels; -#endif uint32_t duration_ms; // 0 for still images. }; @@ -44,8 +39,6 @@ using Image = std::vector; uint32_t GetDurationMs(const Image& image); -#if defined(HAS_WEBP2) - inline constexpr WP2SampleFormat kARGB32 = WP2_ARGB_32; // Makes a deep copy of the given frame sequence and converts the pixels to the @@ -65,8 +58,6 @@ StatusOr ReadStillImageOrAnimation(const char* file_path, Status WriteStillImageOrAnimation(const Image& image, const char* file_path, bool quiet); -#endif - } // namespace codec_compare_gen #endif // SRC_FRAME_H_ diff --git a/src/framework.cc b/src/framework.cc index 83e2865..40d8283 100644 --- a/src/framework.cc +++ b/src/framework.cc @@ -174,7 +174,8 @@ StatusOr> LoadTasks( std::vector> qualities_per_codec( static_cast(Codec::kNumCodecs)); for (size_t i = 0; i < qualities_per_codec.size(); ++i) { - const std::vector q = CodecLossyQualities(static_cast(i)); + const std::vector q = + GetCodecMetadata(static_cast(i)).lossy_qualities(); qualities_per_codec[i] = std::unordered_set(q.begin(), q.end()); } @@ -429,12 +430,14 @@ Status Compare(const std::vector& image_paths, : "") + std::to_string(codec_settings.effort); const std::string batch_file_name = - CodecName(codec_settings.codec) + "_" + - SubsamplingToString(codec_settings.chroma_subsampling) + "_" + - effort_str; - const std::string batch_pretty_name = CodecPrettyName( - codec_settings.codec, codec_settings.quality == kQualityLossless, - codec_settings.chroma_subsampling, codec_settings.effort); + GetCodecMetadata(codec_settings.codec).name + + ("_" + SubsamplingToString(codec_settings.chroma_subsampling)) + + ("_" + effort_str); + const std::string batch_pretty_name = + GetCodecMetadata(codec_settings.codec) + .pretty_name(codec_settings.quality == kQualityLossless, + codec_settings.chroma_subsampling, + codec_settings.effort); OK_OR_RETURN(TasksToJson(batch_pretty_name, codec_settings, tasks, settings.quiet, std::filesystem::path(results_folder_path) / @@ -459,8 +462,8 @@ Status Compare(const std::vector& image_paths, const CodecSettings& codec_settings = input.codec_settings; std::cout << std::endl << "Input settings" << std::endl - << " Codec: " << CodecName(codec_settings.codec) - << std::endl + << " Codec: " + << GetCodecMetadata(codec_settings.codec).name << std::endl << " Chroma subsampling: " << SubsamplingToString(codec_settings.chroma_subsampling) << std::endl diff --git a/src/result_json.cc b/src/result_json.cc index 94e76e3..d05887f 100644 --- a/src/result_json.cc +++ b/src/result_json.cc @@ -115,7 +115,8 @@ Status TasksToJson(const std::string& batch_pretty_name, CodecSettings settings, // See EncodeDecode(). const bool has_decoded_path = - has_encoded_path && !CodecIsSupportedByBrowsers(settings.codec); + has_encoded_path && + !GetCodecMetadata(settings.codec).is_supported_by_browsers; std::ofstream file(results_file_path, std::ios::trunc); CHECK_OR_RETURN(file.is_open(), quiet) << "Failed to open results file at " @@ -156,7 +157,7 @@ Status TasksToJson(const std::string& batch_pretty_name, CodecSettings settings, : ""; const std::string build_cmd = - "git clone -b v0.7.0 --depth 1" + "git clone -b v0.7.1 --depth 1" " https://github.com/webmproject/codec-compare-gen.git ccgen" " && cmake -S ccgen -B ccgen/build" + build_option + @@ -172,7 +173,8 @@ Status TasksToJson(const std::string& batch_pretty_name, CodecSettings settings, ? " " + std::to_string(settings.effort) : ""; // kJpegturbo, kJpegli, and kJpegmoz have no effort setting. std::string encoding_cmd = - "ccgen/build/ccgen --codec " + CodecName(settings.codec) + " " + + "ccgen/build/ccgen --codec " + + std::string(GetCodecMetadata(settings.codec).name) + " " + SubsamplingToString(settings.chroma_subsampling) + effort_str; if (settings.quality == kQualityLossless) { encoding_cmd += " --lossless"; @@ -205,9 +207,9 @@ Status TasksToJson(const std::string& batch_pretty_name, CodecSettings settings, )json" << Escape(batch_pretty_name) << R"json(, )json" - << Escape(CodecName(settings.codec)) << R"json(, + << Escape(GetCodecMetadata(settings.codec).name) << R"json(, )json" - << Escape(CodecVersion(settings.codec) + "_" + + << Escape(GetCodecMetadata(settings.codec).version() + "_" + SubsamplingToString(settings.chroma_subsampling)) << R"json(, )json" diff --git a/src/task.cc b/src/task.cc index 964ee8c..80b9e32 100644 --- a/src/task.cc +++ b/src/task.cc @@ -58,7 +58,7 @@ std::string GetEncodedFilePath(const std::string& folder_path, } else { ext << "q" << std::setfill('0') << std::setw(3) << codec_settings.quality; } - ext << "." << CodecExtension(codec_settings.codec); + ext << "." << GetCodecMetadata(codec_settings.codec).extension; path.replace_extension(ext.str()); return path; } @@ -80,7 +80,7 @@ bool operator==(const TaskInput& a, const TaskInput& b) { std::string TaskInput::Serialize() const { std::stringstream ss; - ss << Escape(CodecName(codec_settings.codec)) << ", " + ss << Escape(GetCodecMetadata(codec_settings.codec).name) << ", " << SubsamplingToString(codec_settings.chroma_subsampling) << ", " << codec_settings.effort << ", " << codec_settings.quality << ", " << Escape(image_path); diff --git a/tests/test_codec_avif.cc b/tests/test_codec_avif.cc index a762cd4..491de3a 100644 --- a/tests/test_codec_avif.cc +++ b/tests/test_codec_avif.cc @@ -30,7 +30,8 @@ static int QualityToQuantizer(int quality) { } TEST(AvifTest, Qualities) { - const std::vector qualities = CodecLossyQualities(Codec::kAvif); + const std::vector qualities = + GetCodecMetadata(Codec::kAvif).lossy_qualities(); std::vector quantizers; quantizers.reserve(qualities.size()); for (int quality : qualities) { diff --git a/tools/ccgen_impl.cc b/tools/ccgen_impl.cc index a9554ef..d1a5490 100644 --- a/tools/ccgen_impl.cc +++ b/tools/ccgen_impl.cc @@ -274,11 +274,11 @@ int Main(int argc, const char* const argv[]) { std::vector> qualities( static_cast(Codec::kNumCodecs)); for (size_t i = 0; i < qualities.size(); ++i) { - qualities[i] = CodecLossyQualities(static_cast(i)); + qualities[i] = GetCodecMetadata(static_cast(i)).lossy_qualities(); } for (const CodecEffort& setting : codec_settings) { if (qualities.at(static_cast(setting.codec)).empty()) { - std::cerr << CodecName(setting.codec) + std::cerr << GetCodecMetadata(setting.codec).name << " does not support lossy encoding" << std::endl; return 1; } diff --git a/wasm/tests/test_wasm_test.ts b/wasm/tests/test_wasm_test.ts index f4231af..bb2f3eb 100644 --- a/wasm/tests/test_wasm_test.ts +++ b/wasm/tests/test_wasm_test.ts @@ -41,6 +41,10 @@ function fromByteVector(v: any): Uint8Array { return bytes; } +// Loading codec_wasm_bin.wasm can take a long time, especially if sanitizers +// were enabled during the build. See b/514217988. +jasmine.DEFAULT_TIMEOUT_INTERVAL = 60000; + describe('Codec WASM', () => { let module: any; let pngBytes: Uint8Array;