2729 lines
112 KiB
Diff
2729 lines
112 KiB
Diff
From: uazo <uazo@users.noreply.github.com>
|
|
Date: Tue, 24 Oct 2023 10:02:00 +0000
|
|
Subject: Add support to jxl
|
|
|
|
Partial revert of https://chromium-review.googlesource.com/c/chromium/src/+/4095497
|
|
Enabled by default
|
|
---
|
|
DEPS | 7 +
|
|
build/linux/unbundle/libjxl.gn | 34 +
|
|
build/linux/unbundle/replace_gn_files.py | 1 +
|
|
cc/base/devtools_instrumentation.cc | 3 +
|
|
cc/base/devtools_instrumentation.h | 2 +-
|
|
cc/paint/paint_image.h | 2 +-
|
|
cc/tiles/image_decode_cache.h | 2 +
|
|
chrome/browser/about_flags.cc | 6 +
|
|
chrome/browser/flag-metadata.json | 5 +
|
|
chrome/browser/flag_descriptions.cc | 7 +
|
|
chrome/browser/flag_descriptions.h | 5 +
|
|
content/common/content_constants_internal.cc | 11 +-
|
|
content/common/content_constants_internal.h | 3 +-
|
|
content/public/browser/frame_accept_header.cc | 13 +-
|
|
media/BUILD.gn | 1 +
|
|
media/media_options.gni | 3 +
|
|
net/base/mime_util.cc | 2 +
|
|
net/base/mime_util_unittest.cc | 3 +
|
|
third_party/.gitignore | 1 +
|
|
third_party/blink/common/features.cc | 3 +
|
|
.../blink/common/loader/network_utils.cc | 16 +-
|
|
.../blink/common/mime_util/mime_util.cc | 7 +
|
|
.../common/mime_util/mime_util_unittest.cc | 6 +
|
|
third_party/blink/public/common/features.h | 2 +
|
|
.../devtools_protocol/browser_protocol.pdl | 1 +
|
|
.../inspector/inspector_emulation_agent.cc | 7 +-
|
|
.../inspector_emulation_agent_test.cc | 37 +
|
|
.../generate_image_corpus.py | 1 +
|
|
.../modules/webcodecs/image_decoder_fuzzer.cc | 5 +
|
|
third_party/blink/renderer/platform/BUILD.gn | 5 +
|
|
.../platform/graphics/bitmap_image_metrics.cc | 9 +-
|
|
.../platform/graphics/bitmap_image_metrics.h | 4 +-
|
|
.../renderer/platform/image-decoders/BUILD.gn | 9 +
|
|
.../platform/image-decoders/image_decoder.cc | 23 +
|
|
.../image-decoders/jxl/jxl_image_decoder.cc | 683 ++++++++++++++++++
|
|
.../image-decoders/jxl/jxl_image_decoder.h | 123 ++++
|
|
.../jxl/jxl_image_decoder_test.cc | 626 ++++++++++++++++
|
|
.../blink/tools/commit_stats/git-dirs.txt | 1 +
|
|
third_party/blink/web_tests/TestExpectations | 6 +
|
|
third_party/blink/web_tests/VirtualTestSuites | 9 +
|
|
...-set-disabled-image-types-jxl-expected.txt | 13 +
|
|
.../emulation-set-disabled-image-types-jxl.js | 50 ++
|
|
.../resources/image-jxl-fallback-img.html | 1 +
|
|
.../resources/image-jxl-fallback-picture.html | 4 +
|
|
.../web_tests/images/jxl/jxl-images.html | 22 +
|
|
.../web_tests/images/jxl/progressive.html | 6 +
|
|
.../web_tests/images/resources/jxl/README.md | 79 ++
|
|
.../web_tests/virtual/jxl-enabled/README.md | 5 +
|
|
third_party/libjxl/BUILD.gn | 79 ++
|
|
third_party/libjxl/DIR_METADATA | 4 +
|
|
third_party/libjxl/LICENSE | 27 +
|
|
third_party/libjxl/OWNERS | 9 +
|
|
third_party/libjxl/README.chromium | 15 +
|
|
.../libjxl/gen_headers/jxl/jxl_export.h | 11 +
|
|
tools/metrics/histograms/enums.xml | 3 +-
|
|
55 files changed, 2005 insertions(+), 17 deletions(-)
|
|
create mode 100644 build/linux/unbundle/libjxl.gn
|
|
create mode 100644 third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder.cc
|
|
create mode 100644 third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder.h
|
|
create mode 100644 third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder_test.cc
|
|
create mode 100644 third_party/blink/web_tests/http/tests/inspector-protocol/emulation/emulation-set-disabled-image-types-jxl-expected.txt
|
|
create mode 100644 third_party/blink/web_tests/http/tests/inspector-protocol/emulation/emulation-set-disabled-image-types-jxl.js
|
|
create mode 100644 third_party/blink/web_tests/http/tests/inspector-protocol/emulation/resources/image-jxl-fallback-img.html
|
|
create mode 100644 third_party/blink/web_tests/http/tests/inspector-protocol/emulation/resources/image-jxl-fallback-picture.html
|
|
create mode 100644 third_party/blink/web_tests/images/jxl/jxl-images.html
|
|
create mode 100644 third_party/blink/web_tests/images/jxl/progressive.html
|
|
create mode 100644 third_party/blink/web_tests/images/resources/jxl/README.md
|
|
create mode 100644 third_party/blink/web_tests/virtual/jxl-enabled/README.md
|
|
create mode 100644 third_party/libjxl/BUILD.gn
|
|
create mode 100644 third_party/libjxl/DIR_METADATA
|
|
create mode 100644 third_party/libjxl/LICENSE
|
|
create mode 100644 third_party/libjxl/OWNERS
|
|
create mode 100644 third_party/libjxl/README.chromium
|
|
create mode 100644 third_party/libjxl/gen_headers/jxl/jxl_export.h
|
|
|
|
diff --git a/DEPS b/DEPS
|
|
--- a/DEPS
|
|
+++ b/DEPS
|
|
@@ -515,6 +515,10 @@ vars = {
|
|
# Three lines of non-changing comments so that
|
|
# the commit queue can handle CLs rolling feed
|
|
# and whatever else without interference from each other.
|
|
+ 'libjxl_revision': '954b460768c08a147abf47689ad69b0e7beff65e',
|
|
+ # Three lines of non-changing comments so that
|
|
+ # the commit queue can handle CLs rolling feed
|
|
+ # and whatever else without interference from each other.
|
|
'highway_revision': '8f20644eca693cfb74aa795b0006b6779c370e7a',
|
|
# Three lines of non-changing comments so that
|
|
# the commit queue can handle CLs rolling ffmpeg
|
|
@@ -1205,6 +1209,9 @@ deps = {
|
|
'src/third_party/dawn':
|
|
Var('dawn_git') + '/dawn.git' + '@' + Var('dawn_revision'),
|
|
|
|
+ 'src/third_party/libjxl/src':
|
|
+ Var('chromium_git') + '/external/github.com/libjxl/libjxl.git' + '@' + Var('libjxl_revision'),
|
|
+
|
|
'src/third_party/highway/src':
|
|
Var('chromium_git') + '/external/github.com/google/highway.git' + '@' + Var('highway_revision'),
|
|
|
|
diff --git a/build/linux/unbundle/libjxl.gn b/build/linux/unbundle/libjxl.gn
|
|
new file mode 100644
|
|
--- /dev/null
|
|
+++ b/build/linux/unbundle/libjxl.gn
|
|
@@ -0,0 +1,34 @@
|
|
+import("//build/config/linux/pkg_config.gni")
|
|
+import("//build/shim_headers.gni")
|
|
+
|
|
+pkg_config("system_libjxl") {
|
|
+ packages = [ "libjxl" ]
|
|
+}
|
|
+
|
|
+shim_headers("jxl_shim") {
|
|
+ root_path = "src/lib/include"
|
|
+ headers = [
|
|
+ "jxl/butteraugli.h",
|
|
+ "jxl/butteraugli_cxx.h",
|
|
+ "jxl/codestream_header.h",
|
|
+ "jxl/color_encoding.h",
|
|
+ "jxl/decode.h",
|
|
+ "jxl/decode_cxx.h",
|
|
+ "jxl/encode.h",
|
|
+ "jxl/encode_cxx.h",
|
|
+ "jxl/jxl_export.h",
|
|
+ "jxl/jxl_threads_export.h",
|
|
+ "jxl/memory_manager.h",
|
|
+ "jxl/parallel_runner.h",
|
|
+ "jxl/resizable_parallel_runner.h",
|
|
+ "jxl/resizable_parallel_runner_cxx.h",
|
|
+ "jxl/thread_parallel_runner.h",
|
|
+ "jxl/thread_parallel_runner_cxx.h",
|
|
+ "jxl/types.h",
|
|
+ ]
|
|
+}
|
|
+
|
|
+source_set("libjxl") {
|
|
+ deps = [ ":jxl_shim" ]
|
|
+ public_configs = [ ":system_libjxl" ]
|
|
+}
|
|
diff --git a/build/linux/unbundle/replace_gn_files.py b/build/linux/unbundle/replace_gn_files.py
|
|
--- a/build/linux/unbundle/replace_gn_files.py
|
|
+++ b/build/linux/unbundle/replace_gn_files.py
|
|
@@ -53,6 +53,7 @@ REPLACEMENTS = {
|
|
'libdrm': 'third_party/libdrm/BUILD.gn',
|
|
'libevent': 'third_party/libevent/BUILD.gn',
|
|
'libjpeg': 'third_party/libjpeg.gni',
|
|
+ 'libjxl' : 'third_party/libjxl/BUILD.gn',
|
|
'libpng': 'third_party/libpng/BUILD.gn',
|
|
'libvpx': 'third_party/libvpx/BUILD.gn',
|
|
'libwebp': 'third_party/libwebp/BUILD.gn',
|
|
diff --git a/cc/base/devtools_instrumentation.cc b/cc/base/devtools_instrumentation.cc
|
|
--- a/cc/base/devtools_instrumentation.cc
|
|
+++ b/cc/base/devtools_instrumentation.cc
|
|
@@ -90,6 +90,9 @@ ScopedImageDecodeTask::~ScopedImageDecodeTask() {
|
|
auto duration = base::TimeTicks::Now() - start_time_;
|
|
const char* histogram_name = nullptr;
|
|
switch (image_type_) {
|
|
+ case ImageType::kJxl:
|
|
+ histogram_name = "Renderer4.ImageUploadTaskDurationUs.Jxl";
|
|
+ break;
|
|
case ImageType::kAvif:
|
|
histogram_name = "Renderer4.ImageDecodeTaskDurationUs.Avif";
|
|
break;
|
|
diff --git a/cc/base/devtools_instrumentation.h b/cc/base/devtools_instrumentation.h
|
|
--- a/cc/base/devtools_instrumentation.h
|
|
+++ b/cc/base/devtools_instrumentation.h
|
|
@@ -72,7 +72,7 @@ class CC_BASE_EXPORT ScopedLayerTask {
|
|
|
|
class CC_BASE_EXPORT ScopedImageTask {
|
|
public:
|
|
- enum ImageType { kAvif, kBmp, kGif, kIco, kJpeg, kPng, kWebP, kOther };
|
|
+ enum ImageType { kJxl, kAvif, kBmp, kGif, kIco, kJpeg, kPng, kWebP, kOther };
|
|
|
|
explicit ScopedImageTask(ImageType image_type)
|
|
: image_type_(image_type), start_time_(base::TimeTicks::Now()) {}
|
|
diff --git a/cc/paint/paint_image.h b/cc/paint/paint_image.h
|
|
--- a/cc/paint/paint_image.h
|
|
+++ b/cc/paint/paint_image.h
|
|
@@ -40,7 +40,7 @@ class PaintImageGenerator;
|
|
class PaintWorkletInput;
|
|
class TextureBacking;
|
|
|
|
-enum class ImageType { kPNG, kJPEG, kWEBP, kGIF, kICO, kBMP, kAVIF, kInvalid };
|
|
+enum class ImageType { kPNG, kJPEG, kWEBP, kGIF, kICO, kBMP, kAVIF, kJXL, kInvalid };
|
|
|
|
enum class AuxImage : size_t { kDefault = 0, kGainmap = 1 };
|
|
static constexpr std::array<AuxImage, 2> kAllAuxImages = {AuxImage::kDefault,
|
|
diff --git a/cc/tiles/image_decode_cache.h b/cc/tiles/image_decode_cache.h
|
|
--- a/cc/tiles/image_decode_cache.h
|
|
+++ b/cc/tiles/image_decode_cache.h
|
|
@@ -84,6 +84,8 @@ class CC_EXPORT ImageDecodeCache {
|
|
using ScopedImageType =
|
|
devtools_instrumentation::ScopedImageDecodeTask::ImageType;
|
|
switch (image_type) {
|
|
+ case ImageType::kJXL:
|
|
+ return ScopedImageType::kJxl;
|
|
case ImageType::kAVIF:
|
|
return ScopedImageType::kAvif;
|
|
case ImageType::kBMP:
|
|
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
|
|
--- a/chrome/browser/about_flags.cc
|
|
+++ b/chrome/browser/about_flags.cc
|
|
@@ -8918,6 +8918,12 @@ const FeatureEntry kFeatureEntries[] = {
|
|
FEATURE_VALUE_TYPE(download::features::kSmartSuggestionForLargeDownloads)},
|
|
#endif // BUILDFLAG(IS_ANDROID)
|
|
|
|
+#if BUILDFLAG(ENABLE_JXL_DECODER)
|
|
+ {"enable-jxl", flag_descriptions::kEnableJXLName,
|
|
+ flag_descriptions::kEnableJXLDescription, kOsAll,
|
|
+ FEATURE_VALUE_TYPE(blink::features::kJXL)},
|
|
+#endif // BUILDFLAG(ENABLE_JXL_DECODER)
|
|
+
|
|
#if BUILDFLAG(IS_ANDROID)
|
|
{"messages-for-android-ads-blocked",
|
|
flag_descriptions::kMessagesForAndroidAdsBlockedName,
|
|
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
|
|
--- a/chrome/browser/flag-metadata.json
|
|
+++ b/chrome/browser/flag-metadata.json
|
|
@@ -3095,6 +3095,11 @@
|
|
// This flag is used by web developers to test upcoming javascript features.
|
|
"expiry_milestone": -1
|
|
},
|
|
+ {
|
|
+ "name": "enable-jxl",
|
|
+ "owners": [ "eustas@chromium.org", "firsching", "sboukortt", "veluca" ],
|
|
+ "expiry_milestone": 150
|
|
+ },
|
|
{
|
|
"name": "enable-keyboard-backlight-toggle",
|
|
"owners": [ "rtinkoff" ],
|
|
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
|
|
--- a/chrome/browser/flag_descriptions.cc
|
|
+++ b/chrome/browser/flag_descriptions.cc
|
|
@@ -7714,6 +7714,13 @@ const char kDcheckIsFatalDescription[] =
|
|
"rather than crashing. If enabled, DCHECKs will crash the calling process.";
|
|
#endif // BUILDFLAG(DCHECK_IS_CONFIGURABLE)
|
|
|
|
+#if BUILDFLAG(ENABLE_JXL_DECODER)
|
|
+const char kEnableJXLName[] = "Enable JXL image format";
|
|
+const char kEnableJXLDescription[] =
|
|
+ "Adds image decoding support for the JPEG XL image format. NOTE: JPEG XL "
|
|
+ "format will be removed in Chrome 110 release.";
|
|
+#endif // BUILDFLAG(ENABLE_JXL_DECODER)
|
|
+
|
|
#if BUILDFLAG(ENABLE_CARDBOARD)
|
|
const char kEnableCardboardName[] = "Enable Cardboard VR WebXR Runtime";
|
|
const char kEnableCardboardDescription[] =
|
|
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
|
|
--- a/chrome/browser/flag_descriptions.h
|
|
+++ b/chrome/browser/flag_descriptions.h
|
|
@@ -4459,6 +4459,11 @@ extern const char kDcheckIsFatalName[];
|
|
extern const char kDcheckIsFatalDescription[];
|
|
#endif // BUILDFLAG(DCHECK_IS_CONFIGURABLE)
|
|
|
|
+#if BUILDFLAG(ENABLE_JXL_DECODER)
|
|
+extern const char kEnableJXLName[];
|
|
+extern const char kEnableJXLDescription[];
|
|
+#endif // BUILDFLAG(ENABLE_JXL_DECODER)
|
|
+
|
|
#if BUILDFLAG(ENABLE_CARDBOARD)
|
|
extern const char kEnableCardboardName[];
|
|
extern const char kEnableCardboardDescription[];
|
|
diff --git a/content/common/content_constants_internal.cc b/content/common/content_constants_internal.cc
|
|
--- a/content/common/content_constants_internal.cc
|
|
+++ b/content/common/content_constants_internal.cc
|
|
@@ -19,13 +19,16 @@ const int kTraceEventGpuProcessSortIndex = -1;
|
|
|
|
const int kTraceEventRendererMainThreadSortIndex = -1;
|
|
|
|
+const char kFrameAcceptHeaderValue_Prefix[] =
|
|
+ "text/html,application/xhtml+xml,application/xml;q=0.9,";
|
|
+
|
|
#if BUILDFLAG(ENABLE_AV1_DECODER)
|
|
-const char kFrameAcceptHeaderValue[] =
|
|
- "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,"
|
|
+const char kFrameAcceptHeaderValue_Suffix[] =
|
|
+ "image/avif,"
|
|
"image/webp,image/apng,*/*;q=0.8";
|
|
#else
|
|
-const char kFrameAcceptHeaderValue[] =
|
|
- "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,"
|
|
+const char kFrameAcceptHeaderValue_Suffix[] =
|
|
+ "image/webp,"
|
|
"image/apng,*/*;q=0.8";
|
|
#endif
|
|
|
|
diff --git a/content/common/content_constants_internal.h b/content/common/content_constants_internal.h
|
|
--- a/content/common/content_constants_internal.h
|
|
+++ b/content/common/content_constants_internal.h
|
|
@@ -39,7 +39,8 @@ CONTENT_EXPORT extern const int kTraceEventGpuProcessSortIndex;
|
|
CONTENT_EXPORT extern const int kTraceEventRendererMainThreadSortIndex;
|
|
|
|
// Accept header used for frame requests.
|
|
-CONTENT_EXPORT extern const char kFrameAcceptHeaderValue[];
|
|
+CONTENT_EXPORT extern const char kFrameAcceptHeaderValue_Prefix[];
|
|
+CONTENT_EXPORT extern const char kFrameAcceptHeaderValue_Suffix[];
|
|
|
|
// Constants for attaching message pipes to the mojo invitation used to
|
|
// initialize child processes.
|
|
diff --git a/content/public/browser/frame_accept_header.cc b/content/public/browser/frame_accept_header.cc
|
|
--- a/content/public/browser/frame_accept_header.cc
|
|
+++ b/content/public/browser/frame_accept_header.cc
|
|
@@ -7,13 +7,22 @@
|
|
#include "content/browser/web_package/signed_exchange_consts.h"
|
|
#include "content/browser/web_package/signed_exchange_utils.h"
|
|
#include "content/common/content_constants_internal.h"
|
|
+#include "third_party/blink/public/common/buildflags.h"
|
|
+#include "third_party/blink/public/common/features.h"
|
|
|
|
namespace content {
|
|
|
|
std::string FrameAcceptHeaderValue(bool allow_sxg_responses,
|
|
BrowserContext* browser_context) {
|
|
- std::string header_value = kFrameAcceptHeaderValue;
|
|
-
|
|
+ std::string header_value = kFrameAcceptHeaderValue_Prefix;
|
|
+#if BUILDFLAG(ENABLE_JXL_DECODER)
|
|
+ // In case the buildflag and the runtime flag are enables, we insert
|
|
+ // "image/jxl," into the header value at the correct place.
|
|
+ if (base::FeatureList::IsEnabled(blink::features::kJXL)) {
|
|
+ header_value.append("image/jxl,");
|
|
+ }
|
|
+#endif // BUILDFLAG(ENABLE_JXL_DECODER)
|
|
+ header_value.append(kFrameAcceptHeaderValue_Suffix);
|
|
if (allow_sxg_responses &&
|
|
content::signed_exchange_utils::IsSignedExchangeHandlingEnabled(
|
|
browser_context)) {
|
|
diff --git a/media/BUILD.gn b/media/BUILD.gn
|
|
--- a/media/BUILD.gn
|
|
+++ b/media/BUILD.gn
|
|
@@ -37,6 +37,7 @@ buildflag_header("media_buildflags") {
|
|
"ENABLE_CAST_AUDIO_RENDERER=$enable_cast_audio_renderer",
|
|
"ENABLE_DAV1D_DECODER=$enable_dav1d_decoder",
|
|
"ENABLE_AV1_DECODER=$enable_av1_decoder",
|
|
+ "ENABLE_JXL_DECODER=$enable_jxl_decoder",
|
|
"ENABLE_PLATFORM_DOLBY_VISION=$enable_platform_dolby_vision",
|
|
"ENABLE_PLATFORM_ENCRYPTED_DOLBY_VISION=$enable_platform_encrypted_dolby_vision",
|
|
"ENABLE_FFMPEG=$media_use_ffmpeg",
|
|
diff --git a/media/media_options.gni b/media/media_options.gni
|
|
--- a/media/media_options.gni
|
|
+++ b/media/media_options.gni
|
|
@@ -125,6 +125,9 @@ declare_args() {
|
|
# `enable_libaom` should likely also be overriddent to false.
|
|
enable_av1_decoder = enable_dav1d_decoder
|
|
|
|
+ # If true, adds support for JPEG XL image decoding.
|
|
+ enable_jxl_decoder = is_android || is_win
|
|
+
|
|
# Enable HEVC/H265 demuxing. Actual decoding must be provided by the
|
|
# platform. Always enable this for Lacros, it determines support at runtime.
|
|
# TODO(crbug.com/1336055): Revisit the default value for this setting as it
|
|
diff --git a/net/base/mime_util.cc b/net/base/mime_util.cc
|
|
--- a/net/base/mime_util.cc
|
|
+++ b/net/base/mime_util.cc
|
|
@@ -163,6 +163,7 @@ static const MimeInfo kPrimaryMappings[] = {
|
|
{"image/avif", "avif"},
|
|
{"image/gif", "gif"},
|
|
{"image/jpeg", "jpeg,jpg"},
|
|
+ {"image/jxl", "jxl"},
|
|
{"image/png", "png"},
|
|
{"image/apng", "png,apng"},
|
|
{"image/svg+xml", "svg,svgz"},
|
|
@@ -666,6 +667,7 @@ static const char* const kStandardImageTypes[] = {"image/avif",
|
|
"image/gif",
|
|
"image/ief",
|
|
"image/jpeg",
|
|
+ "image/jxl",
|
|
"image/webp",
|
|
"image/pict",
|
|
"image/pipeg",
|
|
diff --git a/net/base/mime_util_unittest.cc b/net/base/mime_util_unittest.cc
|
|
--- a/net/base/mime_util_unittest.cc
|
|
+++ b/net/base/mime_util_unittest.cc
|
|
@@ -39,6 +39,7 @@ TEST(MimeUtilTest, GetWellKnownMimeTypeFromExtension) {
|
|
{FILE_PATH_LITERAL("webm"), "video/webm"},
|
|
{FILE_PATH_LITERAL("weba"), "audio/webm"},
|
|
{FILE_PATH_LITERAL("avif"), "image/avif"},
|
|
+ {FILE_PATH_LITERAL("jxl"), "image/jxl"},
|
|
{FILE_PATH_LITERAL("epub"), "application/epub+zip"},
|
|
{FILE_PATH_LITERAL("apk"), "application/vnd.android.package-archive"},
|
|
{FILE_PATH_LITERAL("cer"), "application/x-x509-ca-cert"},
|
|
@@ -80,6 +81,7 @@ TEST(MimeUtilTest, ExtensionTest) {
|
|
{FILE_PATH_LITERAL("webm"), {"video/webm"}},
|
|
{FILE_PATH_LITERAL("weba"), {"audio/webm"}},
|
|
{FILE_PATH_LITERAL("avif"), {"image/avif"}},
|
|
+ {FILE_PATH_LITERAL("jxl"), {"image/jxl"}},
|
|
#if BUILDFLAG(IS_CHROMEOS_ASH)
|
|
// These are test cases for testing platform mime types on ChromeOS.
|
|
{FILE_PATH_LITERAL("epub"), {"application/epub+zip"}},
|
|
@@ -497,6 +499,7 @@ TEST(MimeUtilTest, TestGetExtensionsForMimeType) {
|
|
{"MeSsAge/*", 1, "eml"},
|
|
{"message/", 0, nullptr, true},
|
|
{"image/avif", 1, "avif"},
|
|
+ {"image/jxl", 1, "jxl"},
|
|
{"image/bmp", 1, "bmp"},
|
|
{"video/*", 6, "mp4"},
|
|
{"video/*", 6, "mpeg"},
|
|
diff --git a/third_party/.gitignore b/third_party/.gitignore
|
|
--- a/third_party/.gitignore
|
|
+++ b/third_party/.gitignore
|
|
@@ -97,6 +97,7 @@
|
|
/libgifcodec
|
|
/libjingle/source
|
|
/libupnp
|
|
+#/libjxl/src
|
|
/llvm
|
|
/llvm-allocated-type
|
|
/llvm-bootstrap
|
|
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
|
|
--- a/third_party/blink/common/features.cc
|
|
+++ b/third_party/blink/common/features.cc
|
|
@@ -422,6 +422,9 @@ BASE_FEATURE(kCORSErrorsIssueOnly,
|
|
"CORSErrorsIssueOnly",
|
|
base::FEATURE_DISABLED_BY_DEFAULT);
|
|
|
|
+// Enables the JPEG XL Image File Format (JXL).
|
|
+BASE_FEATURE(kJXL, "JXL", base::FEATURE_ENABLED_BY_DEFAULT);
|
|
+
|
|
// When enabled, code cache is produced asynchronously from the script execution
|
|
// (https://crbug.com/1260908).
|
|
BASE_FEATURE(kCacheCodeOnIdle,
|
|
diff --git a/third_party/blink/common/loader/network_utils.cc b/third_party/blink/common/loader/network_utils.cc
|
|
--- a/third_party/blink/common/loader/network_utils.cc
|
|
+++ b/third_party/blink/common/loader/network_utils.cc
|
|
@@ -9,6 +9,7 @@
|
|
#include "services/network/public/cpp/constants.h"
|
|
#include "services/network/public/mojom/fetch_api.mojom.h"
|
|
#include "third_party/blink/public/common/buildflags.h"
|
|
+#include "third_party/blink/public/common/features.h"
|
|
|
|
namespace blink {
|
|
namespace network_utils {
|
|
@@ -33,7 +34,20 @@ bool AlwaysAccessNetwork(
|
|
}
|
|
|
|
const char* ImageAcceptHeader() {
|
|
-#if BUILDFLAG(ENABLE_AV1_DECODER)
|
|
+#if BUILDFLAG(ENABLE_JXL_DECODER) && BUILDFLAG(ENABLE_AV1_DECODER)
|
|
+ if (base::FeatureList::IsEnabled(blink::features::kJXL)) {
|
|
+ return "image/jxl,image/avif,image/webp,image/apng,image/svg+xml,image/*,*/"
|
|
+ "*;q=0.8";
|
|
+ } else {
|
|
+ return "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8";
|
|
+ }
|
|
+#elif BUILDFLAG(ENABLE_JXL_DECODER)
|
|
+ if (base::FeatureList::IsEnabled(blink::features::kJXL)) {
|
|
+ return "image/jxl,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8";
|
|
+ } else {
|
|
+ return "image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8";
|
|
+ }
|
|
+#elif BUILDFLAG(ENABLE_AV1_DECODER)
|
|
return "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8";
|
|
#else
|
|
return "image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8";
|
|
diff --git a/third_party/blink/common/mime_util/mime_util.cc b/third_party/blink/common/mime_util/mime_util.cc
|
|
--- a/third_party/blink/common/mime_util/mime_util.cc
|
|
+++ b/third_party/blink/common/mime_util/mime_util.cc
|
|
@@ -14,6 +14,7 @@
|
|
#include "media/media_buildflags.h"
|
|
#include "net/base/mime_util.h"
|
|
#include "third_party/blink/public/common/buildflags.h"
|
|
+#include "third_party/blink/public/common/features.h"
|
|
|
|
#if !BUILDFLAG(IS_IOS)
|
|
// iOS doesn't use and must not depend on //media
|
|
@@ -144,6 +145,12 @@ MimeUtil::MimeUtil() {
|
|
non_image_types_.insert(type);
|
|
for (const char* type : kSupportedImageTypes)
|
|
image_types_.insert(type);
|
|
+#if BUILDFLAG(ENABLE_JXL_DECODER)
|
|
+ // TODO(firsching): Add "image/jxl" to the kSupportedImageTypes array when the
|
|
+ // JXL feature is shipped.
|
|
+ if (base::FeatureList::IsEnabled(features::kJXL))
|
|
+ image_types_.insert("image/jxl");
|
|
+#endif
|
|
for (const char* type : kUnsupportedTextTypes)
|
|
unsupported_text_types_.insert(type);
|
|
for (const char* type : kSupportedJavascriptTypes) {
|
|
diff --git a/third_party/blink/common/mime_util/mime_util_unittest.cc b/third_party/blink/common/mime_util/mime_util_unittest.cc
|
|
--- a/third_party/blink/common/mime_util/mime_util_unittest.cc
|
|
+++ b/third_party/blink/common/mime_util/mime_util_unittest.cc
|
|
@@ -9,6 +9,7 @@
|
|
#include "net/base/mime_util.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
#include "third_party/blink/public/common/buildflags.h"
|
|
+#include "third_party/blink/public/common/features.h"
|
|
|
|
namespace blink {
|
|
|
|
@@ -18,7 +19,12 @@ TEST(MimeUtilTest, LookupTypes) {
|
|
|
|
EXPECT_TRUE(IsSupportedImageMimeType("image/jpeg"));
|
|
EXPECT_TRUE(IsSupportedImageMimeType("Image/JPEG"));
|
|
+#if BUILDFLAG(ENABLE_JXL_DECODER)
|
|
+ EXPECT_EQ(IsSupportedImageMimeType("image/jxl"),
|
|
+ base::FeatureList::IsEnabled(features::kJXL));
|
|
+#else
|
|
EXPECT_FALSE(IsSupportedImageMimeType("image/jxl"));
|
|
+#endif
|
|
EXPECT_EQ(IsSupportedImageMimeType("image/avif"),
|
|
BUILDFLAG(ENABLE_AV1_DECODER));
|
|
EXPECT_FALSE(IsSupportedImageMimeType("image/lolcat"));
|
|
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
|
|
--- a/third_party/blink/public/common/features.h
|
|
+++ b/third_party/blink/public/common/features.h
|
|
@@ -145,6 +145,8 @@ BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kBackgroundResourceFetch);
|
|
|
|
BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kShowAlwaysContextMenuOnLinks);
|
|
|
|
+BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kJXL);
|
|
+
|
|
// Used to configure a per-origin allowlist of performance.mark events that are
|
|
// permitted to be included in slow reports traces. See crbug.com/1181774.
|
|
BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kBackgroundTracingPerformanceMark);
|
|
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
|
|
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
|
|
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
|
|
@@ -4133,6 +4133,7 @@ domain Emulation
|
|
experimental type DisabledImageType extends string
|
|
enum
|
|
avif
|
|
+ jxl
|
|
webp
|
|
|
|
experimental command setDisabledImageTypes
|
|
diff --git a/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc b/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc
|
|
--- a/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc
|
|
+++ b/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc
|
|
@@ -495,10 +495,10 @@ AtomicString InspectorEmulationAgent::OverrideAcceptImageHeader(
|
|
String header(network_utils::ImageAcceptHeader());
|
|
for (String type : *disabled_image_types) {
|
|
// The header string is expected to be like
|
|
- // `image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8`
|
|
+ // `image/jxl,image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8`
|
|
// and is expected to be always ending with `image/*,*/*;q=xxx`, therefore,
|
|
- // to remove a type we replace `image/x,` with empty string. Only webp and
|
|
- // avif types can be disabled.
|
|
+ // to remove a type we replace `image/x,` with empty string. Only webp, avif
|
|
+ // and jxl types can be disabled.
|
|
header.Replace(String(type + ","), "");
|
|
}
|
|
return AtomicString(header);
|
|
@@ -842,6 +842,7 @@ protocol::Response InspectorEmulationAgent::setDisabledImageTypes(
|
|
namespace DisabledImageTypeEnum = protocol::Emulation::DisabledImageTypeEnum;
|
|
for (protocol::Emulation::DisabledImageType type : *disabled_types) {
|
|
if (DisabledImageTypeEnum::Avif == type ||
|
|
+ DisabledImageTypeEnum::Jxl == type ||
|
|
DisabledImageTypeEnum::Webp == type) {
|
|
disabled_image_types_.Set(prefix + type, true);
|
|
continue;
|
|
diff --git a/third_party/blink/renderer/core/inspector/inspector_emulation_agent_test.cc b/third_party/blink/renderer/core/inspector/inspector_emulation_agent_test.cc
|
|
--- a/third_party/blink/renderer/core/inspector/inspector_emulation_agent_test.cc
|
|
+++ b/third_party/blink/renderer/core/inspector/inspector_emulation_agent_test.cc
|
|
@@ -7,6 +7,7 @@
|
|
#include "media/media_buildflags.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
#include "third_party/blink/public/common/buildflags.h"
|
|
+#include "third_party/blink/public/common/features.h"
|
|
|
|
namespace blink {
|
|
|
|
@@ -22,6 +23,8 @@ TEST_F(InspectorEmulationAgentTest, ModifiesAcceptHeader) {
|
|
"image/apng,image/svg+xml,image/*,*/*;q=0.8";
|
|
String expected_no_avif =
|
|
"image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8";
|
|
+ String expected_no_jxl =
|
|
+ "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8";
|
|
#else
|
|
String expected_default =
|
|
"image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8";
|
|
@@ -30,8 +33,38 @@ TEST_F(InspectorEmulationAgentTest, ModifiesAcceptHeader) {
|
|
"image/apng,image/svg+xml,image/*,*/*;q=0.8";
|
|
String expected_no_avif =
|
|
"image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8";
|
|
+ String expected_no_jxl =
|
|
+ "image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8";
|
|
#endif
|
|
|
|
+#if BUILDFLAG(ENABLE_JXL_DECODER)
|
|
+ bool jxl_enabled = base::FeatureList::IsEnabled(features::kJXL);
|
|
+ if (jxl_enabled) {
|
|
+#if BUILDFLAG(ENABLE_AV1_DECODER)
|
|
+ expected_default =
|
|
+ "image/jxl,image/avif,image/webp,image/apng,image/svg+xml,image/*,*/"
|
|
+ "*;q=0.8";
|
|
+ expected_no_webp =
|
|
+ "image/jxl,image/avif,image/apng,image/svg+xml,image/*,*/*;q=0.8";
|
|
+ expected_no_webp_and_avif =
|
|
+ "image/jxl,image/apng,image/svg+xml,image/*,*/*;q=0.8";
|
|
+ expected_no_avif =
|
|
+ "image/jxl,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8";
|
|
+ expected_no_jxl =
|
|
+ "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8";
|
|
+#else // BUILDFLAG(ENABLE_AV1_DECODER)
|
|
+ expected_default =
|
|
+ "image/jxl,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8";
|
|
+ expected_no_webp = "image/jxl,image/apng,image/svg+xml,image/*,*/*;q=0.8";
|
|
+ expected_no_webp_and_avif =
|
|
+ "image/jxl,image/apng,image/svg+xml,image/*,*/*;q=0.8";
|
|
+ expected_no_avif =
|
|
+ "image/jxl,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8";
|
|
+ expected_no_jxl = "image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8";
|
|
+#endif // BUILDFLAG(ENABLE_AV1_DECODER)
|
|
+ }
|
|
+#endif // BUILDFLAG(ENABLE_JXL_DECODER)
|
|
+
|
|
HashSet<String> disabled_types;
|
|
EXPECT_EQ(InspectorEmulationAgent::OverrideAcceptImageHeader(&disabled_types),
|
|
expected_default);
|
|
@@ -44,6 +77,10 @@ TEST_F(InspectorEmulationAgentTest, ModifiesAcceptHeader) {
|
|
disabled_types.erase("image/webp");
|
|
EXPECT_EQ(InspectorEmulationAgent::OverrideAcceptImageHeader(&disabled_types),
|
|
expected_no_avif);
|
|
+ disabled_types.erase("image/avif");
|
|
+ disabled_types.insert("image/jxl");
|
|
+ EXPECT_EQ(InspectorEmulationAgent::OverrideAcceptImageHeader(&disabled_types),
|
|
+ expected_no_jxl);
|
|
}
|
|
|
|
} // namespace blink
|
|
diff --git a/third_party/blink/renderer/modules/webcodecs/fuzzer_seed_corpus/generate_image_corpus.py b/third_party/blink/renderer/modules/webcodecs/fuzzer_seed_corpus/generate_image_corpus.py
|
|
--- a/third_party/blink/renderer/modules/webcodecs/fuzzer_seed_corpus/generate_image_corpus.py
|
|
+++ b/third_party/blink/renderer/modules/webcodecs/fuzzer_seed_corpus/generate_image_corpus.py
|
|
@@ -32,6 +32,7 @@ EXTENSIONS_MAP = {
|
|
"ico": "image/x-icon",
|
|
"bmp": "image/bmp",
|
|
"jpg": "image/jpeg",
|
|
+ "jxl": "image/jxl",
|
|
"gif": "image/gif",
|
|
"cur": "image/x-icon",
|
|
"webp": "image/webp",
|
|
diff --git a/third_party/blink/renderer/modules/webcodecs/image_decoder_fuzzer.cc b/third_party/blink/renderer/modules/webcodecs/image_decoder_fuzzer.cc
|
|
--- a/third_party/blink/renderer/modules/webcodecs/image_decoder_fuzzer.cc
|
|
+++ b/third_party/blink/renderer/modules/webcodecs/image_decoder_fuzzer.cc
|
|
@@ -3,8 +3,10 @@
|
|
// found in the LICENSE file.
|
|
|
|
#include "base/run_loop.h"
|
|
+#include "base/test/scoped_feature_list.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
#include "testing/libfuzzer/proto/lpm_interface.h"
|
|
+#include "third_party/blink/public/common/features.h"
|
|
#include "third_party/blink/renderer/bindings/core/v8/to_v8_traits.h"
|
|
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
|
|
#include "third_party/blink/renderer/bindings/core/v8/v8_union_arraybufferallowshared_arraybufferviewallowshared_readablestream.h"
|
|
@@ -89,6 +91,9 @@ DEFINE_BINARY_PROTO_FUZZER(
|
|
auto scoped_gc =
|
|
MakeScopedGarbageCollectionRequest(test_support.GetIsolate());
|
|
|
|
+ base::test::ScopedFeatureList scoped_feature_list;
|
|
+ scoped_feature_list.InitAndEnableFeature(features::kJXL);
|
|
+
|
|
//
|
|
// NOTE: GC objects that need to survive iterations of the loop below
|
|
// must be Persistent<>!
|
|
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
|
|
--- a/third_party/blink/renderer/platform/BUILD.gn
|
|
+++ b/third_party/blink/renderer/platform/BUILD.gn
|
|
@@ -2232,6 +2232,10 @@ source_set("blink_platform_unittests_sources") {
|
|
sources += [ "text/locale_icu_test.cc" ]
|
|
}
|
|
|
|
+ if (enable_jxl_decoder) {
|
|
+ sources += [ "image-decoders/jxl/jxl_image_decoder_test.cc" ]
|
|
+ }
|
|
+
|
|
sources += [ "testing/run_all_tests.cc" ]
|
|
|
|
configs += [
|
|
@@ -2275,6 +2279,7 @@ source_set("blink_platform_unittests_sources") {
|
|
"//third_party/blink/renderer/platform/scheduler:unit_tests",
|
|
"//third_party/blink/renderer/platform/wtf",
|
|
"//third_party/libavif:libavif",
|
|
+ "//third_party/libjxl:libjxl",
|
|
"//third_party/libyuv",
|
|
"//third_party/webrtc/api/task_queue:task_queue_test",
|
|
"//third_party/webrtc_overrides:metronome_like_task_queue_test",
|
|
diff --git a/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.cc b/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.cc
|
|
--- a/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.cc
|
|
+++ b/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.cc
|
|
@@ -6,7 +6,6 @@
|
|
|
|
#include "base/metrics/histogram_base.h"
|
|
#include "base/metrics/histogram_macros.h"
|
|
-#include "base/notreached.h"
|
|
#include "base/numerics/safe_conversions.h"
|
|
#include "media/media_buildflags.h"
|
|
#include "third_party/blink/public/common/buildflags.h"
|
|
@@ -37,6 +36,10 @@ BitmapImageMetrics::StringToDecodedImageType(const String& type) {
|
|
#if BUILDFLAG(ENABLE_AV1_DECODER)
|
|
if (type == "avif")
|
|
return BitmapImageMetrics::DecodedImageType::kAVIF;
|
|
+#endif
|
|
+#if BUILDFLAG(ENABLE_JXL_DECODER)
|
|
+ if (type == "jxl")
|
|
+ return BitmapImageMetrics::DecodedImageType::kJXL;
|
|
#endif
|
|
return BitmapImageMetrics::DecodedImageType::kUnknown;
|
|
}
|
|
@@ -55,6 +58,10 @@ void BitmapImageMetrics::CountDecodedImageType(const String& type,
|
|
} else if (type == "avif") {
|
|
use_counter->CountUse(WebFeature::kAVIFImage);
|
|
#endif
|
|
+// #if BUILDFLAG(ENABLE_JXL_DECODER)
|
|
+// } else if (type == "jxl") {
|
|
+// use_counter->CountUse(WebFeature::kJXLImage);
|
|
+// #endif
|
|
}
|
|
}
|
|
}
|
|
diff --git a/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h b/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h
|
|
--- a/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h
|
|
+++ b/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h
|
|
@@ -29,8 +29,8 @@ class PLATFORM_EXPORT BitmapImageMetrics {
|
|
kICO = 5,
|
|
kBMP = 6,
|
|
kAVIF = 7,
|
|
- kREMOVED_JXL = 8,
|
|
- kMaxValue = kREMOVED_JXL,
|
|
+ kJXL = 8,
|
|
+ kMaxValue = kJXL,
|
|
};
|
|
|
|
// Categories for the JPEG color space histogram. Synced with 'JpegColorSpace'
|
|
diff --git a/third_party/blink/renderer/platform/image-decoders/BUILD.gn b/third_party/blink/renderer/platform/image-decoders/BUILD.gn
|
|
--- a/third_party/blink/renderer/platform/image-decoders/BUILD.gn
|
|
+++ b/third_party/blink/renderer/platform/image-decoders/BUILD.gn
|
|
@@ -70,6 +70,15 @@ component("image_decoders") {
|
|
"//third_party/libyuv",
|
|
]
|
|
|
|
+ if (enable_jxl_decoder) {
|
|
+ sources += [
|
|
+ "jxl/jxl_image_decoder.cc",
|
|
+ "jxl/jxl_image_decoder.h",
|
|
+ ]
|
|
+
|
|
+ deps += [ "//third_party/libjxl:libjxl" ]
|
|
+ }
|
|
+
|
|
if (enable_av1_decoder) {
|
|
sources += [
|
|
"avif/avif_image_decoder.cc",
|
|
diff --git a/third_party/blink/renderer/platform/image-decoders/image_decoder.cc b/third_party/blink/renderer/platform/image-decoders/image_decoder.cc
|
|
--- a/third_party/blink/renderer/platform/image-decoders/image_decoder.cc
|
|
+++ b/third_party/blink/renderer/platform/image-decoders/image_decoder.cc
|
|
@@ -23,6 +23,7 @@
|
|
#include <memory>
|
|
|
|
#include "base/logging.h"
|
|
+#include "base/feature_list.h"
|
|
#include "base/numerics/safe_conversions.h"
|
|
#include "base/sys_byteorder.h"
|
|
#include "base/trace_event/trace_event.h"
|
|
@@ -30,6 +31,7 @@
|
|
#include "media/media_buildflags.h"
|
|
#include "skia/ext/cicp.h"
|
|
#include "third_party/blink/public/common/buildflags.h"
|
|
+#include "third_party/blink/public/common/features.h"
|
|
#include "third_party/blink/public/platform/platform.h"
|
|
#include "third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_decoder.h"
|
|
#include "third_party/blink/renderer/platform/image-decoders/exif_reader.h"
|
|
@@ -47,6 +49,9 @@
|
|
#include "third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.h"
|
|
#endif
|
|
|
|
+#if BUILDFLAG(ENABLE_JXL_DECODER)
|
|
+#include "third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder.h"
|
|
+#endif
|
|
namespace blink {
|
|
|
|
namespace {
|
|
@@ -74,6 +79,11 @@ cc::ImageType FileExtensionToImageType(String image_extension) {
|
|
if (image_extension == "avif") {
|
|
return cc::ImageType::kAVIF;
|
|
}
|
|
+#endif
|
|
+#if BUILDFLAG(ENABLE_JXL_DECODER)
|
|
+ if (image_extension == "jxl") {
|
|
+ return cc::ImageType::kJXL;
|
|
+ }
|
|
#endif
|
|
return cc::ImageType::kInvalid;
|
|
}
|
|
@@ -191,6 +201,12 @@ String SniffMimeTypeInternal(scoped_refptr<SegmentReader> reader) {
|
|
return "image/avif";
|
|
}
|
|
#endif
|
|
+#if BUILDFLAG(ENABLE_JXL_DECODER)
|
|
+ if (base::FeatureList::IsEnabled(blink::features::kJXL) &&
|
|
+ JXLImageDecoder::MatchesJXLSignature(fast_reader)) {
|
|
+ return "image/jxl";
|
|
+ }
|
|
+#endif
|
|
|
|
return String();
|
|
}
|
|
@@ -296,6 +312,13 @@ std::unique_ptr<ImageDecoder> ImageDecoder::CreateByMimeType(
|
|
decoder = std::make_unique<AVIFImageDecoder>(
|
|
alpha_option, high_bit_depth_decoding_option, color_behavior,
|
|
max_decoded_bytes, animation_option);
|
|
+#endif
|
|
+#if BUILDFLAG(ENABLE_JXL_DECODER)
|
|
+ } else if (base::FeatureList::IsEnabled(blink::features::kJXL) &&
|
|
+ mime_type == "image/jxl") {
|
|
+ decoder = std::make_unique<JXLImageDecoder>(
|
|
+ alpha_option, high_bit_depth_decoding_option, color_behavior,
|
|
+ max_decoded_bytes);
|
|
#endif
|
|
}
|
|
|
|
diff --git a/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder.cc b/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder.cc
|
|
new file mode 100644
|
|
--- /dev/null
|
|
+++ b/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder.cc
|
|
@@ -0,0 +1,683 @@
|
|
+// Copyright 2021 The Chromium Authors
|
|
+// Use of this source code is governed by a BSD-style license that can be
|
|
+// found in the LICENSE file.
|
|
+
|
|
+#include "third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder.h"
|
|
+#include "base/logging.h"
|
|
+#include "base/time/time.h"
|
|
+#include "third_party/blink/renderer/platform/image-decoders/fast_shared_buffer_reader.h"
|
|
+#include "third_party/skia/include/core/SkColorSpace.h"
|
|
+
|
|
+namespace blink {
|
|
+
|
|
+namespace {
|
|
+// Returns transfer function which approximates HLG with linear range 0..1,
|
|
+// while skcms_TransferFunction_makeHLGish uses linear range 0..12.
|
|
+void MakeTransferFunctionHLG01(skcms_TransferFunction* tf) {
|
|
+ skcms_TransferFunction_makeScaledHLGish(
|
|
+ tf, 1 / 12.0f, 2.0f, 2.0f, 1 / 0.17883277f, 0.28466892f, 0.55991073f);
|
|
+}
|
|
+
|
|
+// The input profile must outlive the output one as they will share their
|
|
+// buffers.
|
|
+skcms_ICCProfile ReplaceTransferFunction(skcms_ICCProfile profile,
|
|
+ const skcms_TransferFunction& tf) {
|
|
+ // Override the transfer function with a known parametric curve.
|
|
+ profile.has_trc = true;
|
|
+ for (int c = 0; c < 3; c++) {
|
|
+ profile.trc[c].table_entries = 0;
|
|
+ profile.trc[c].parametric = tf;
|
|
+ }
|
|
+ return profile;
|
|
+}
|
|
+
|
|
+// Computes whether the transfer function from the ColorProfile, that was
|
|
+// created from a parsed ICC profile, approximately matches the given parametric
|
|
+// transfer function.
|
|
+bool ApproximatelyMatchesTF(const ColorProfile& profile,
|
|
+ const skcms_TransferFunction& tf) {
|
|
+ skcms_ICCProfile parsed_copy =
|
|
+ ReplaceTransferFunction(*profile.GetProfile(), tf);
|
|
+ return skcms_ApproximatelyEqualProfiles(profile.GetProfile(), &parsed_copy);
|
|
+}
|
|
+
|
|
+std::unique_ptr<ColorProfile> NewColorProfileWithSameBuffer(
|
|
+ const ColorProfile& buffer_donor,
|
|
+ skcms_ICCProfile new_profile) {
|
|
+ // The input ColorProfile owns the buffer memory, make a new copy for
|
|
+ // the newly created one and pass the ownership of the new copy to the new
|
|
+ // color profile.
|
|
+ std::unique_ptr<uint8_t[]> owned_buffer(
|
|
+ new uint8_t[buffer_donor.GetProfile()->size]);
|
|
+ memcpy(owned_buffer.get(), buffer_donor.GetProfile()->buffer,
|
|
+ buffer_donor.GetProfile()->size);
|
|
+ new_profile.buffer = owned_buffer.get();
|
|
+ return std::make_unique<ColorProfile>(new_profile, std::move(owned_buffer));
|
|
+}
|
|
+} // namespace
|
|
+
|
|
+JXLImageDecoder::JXLImageDecoder(
|
|
+ AlphaOption alpha_option,
|
|
+ HighBitDepthDecodingOption high_bit_depth_decoding_option,
|
|
+ const ColorBehavior& color_behavior,
|
|
+ wtf_size_t max_decoded_bytes)
|
|
+ : ImageDecoder(alpha_option,
|
|
+ high_bit_depth_decoding_option,
|
|
+ color_behavior,
|
|
+ max_decoded_bytes) {
|
|
+ info_.have_animation = false;
|
|
+}
|
|
+
|
|
+// Use the provisional Mime type "image/jxl" for JPEG XL images. See
|
|
+// https://www.iana.org/assignments/provisional-standard-media-types/provisional-standard-media-types.xhtml.
|
|
+const AtomicString& JXLImageDecoder::MimeType() const {
|
|
+ DEFINE_STATIC_LOCAL(const AtomicString, jxl_mime_type, ("image/jxl"));
|
|
+ return jxl_mime_type;
|
|
+}
|
|
+
|
|
+bool JXLImageDecoder::ReadBytes(size_t remaining,
|
|
+ wtf_size_t* offset,
|
|
+ WTF::Vector<uint8_t>* segment,
|
|
+ FastSharedBufferReader* reader,
|
|
+ const uint8_t** jxl_data,
|
|
+ size_t* jxl_size) {
|
|
+ *offset -= remaining;
|
|
+ if (*offset + remaining >= reader->size()) {
|
|
+ segment->clear();
|
|
+ if (IsAllDataReceived()) {
|
|
+ DVLOG(1) << "need more input but all data received";
|
|
+ SetFailed();
|
|
+ return false;
|
|
+ }
|
|
+ // Return because we need more input from the reader, to continue
|
|
+ // decoding in the next call.
|
|
+ return false;
|
|
+ }
|
|
+ const char* buffer = nullptr;
|
|
+ size_t read = reader->GetSomeData(buffer, *offset);
|
|
+
|
|
+ if (read > remaining) {
|
|
+ // Sufficient data present in the segment from the
|
|
+ // FastSharedBufferReader, no need to copy to segment_.
|
|
+ *jxl_data = reinterpret_cast<const uint8_t*>(buffer);
|
|
+ *jxl_size = read;
|
|
+ *offset += read;
|
|
+ segment->clear();
|
|
+ } else {
|
|
+ if (segment->size() == remaining) {
|
|
+ // Keep reading from the end of the segment_ we already are
|
|
+ // appending to. The above read is ignored, and start reading after the
|
|
+ // end of the data we already have.
|
|
+ *offset += remaining;
|
|
+ read = 0;
|
|
+ } else {
|
|
+ // segment_->size() could be greater than or smaller than remaining.
|
|
+ // Typically, it'll be smaller than. If it is greater than, then we could
|
|
+ // do something similar as in the segment->size() == remaining case but
|
|
+ // remove the non-remaining bytes from the beginning of the segment_
|
|
+ // vector. This would avoid re-reading, however the case where
|
|
+ // segment->size() > remaining is rare since normally if the JXL decoder
|
|
+ // returns a positive value for remaining, it will be consistent, making
|
|
+ // the sizes match exactly, so this more complex case is not implemented.
|
|
+ // Clear the segment, the bytes from the GetSomeData above will be
|
|
+ // appended and then we continue reading from the position after the
|
|
+ // above GetSomeData read.
|
|
+ segment->clear();
|
|
+ }
|
|
+
|
|
+ for (;;) {
|
|
+ if (read) {
|
|
+ *offset += read;
|
|
+ segment->Append(buffer, base::checked_cast<wtf_size_t>(read));
|
|
+ }
|
|
+ if (segment->size() > remaining) {
|
|
+ *jxl_data = segment->data();
|
|
+ *jxl_size = segment->size();
|
|
+ // Have enough data, break and continue JXL decoding, rather than
|
|
+ // copy more input than needed into segment_.
|
|
+ break;
|
|
+ }
|
|
+ read = reader->GetSomeData(buffer, *offset);
|
|
+ if (read == 0) {
|
|
+ // We tested above that *offset + remaining >= reader.size() so
|
|
+ // should be able to read all data.
|
|
+ DVLOG(1) << "couldn't read all available data";
|
|
+ SetFailed();
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return true;
|
|
+}
|
|
+
|
|
+void JXLImageDecoder::DecodeImpl(wtf_size_t index, bool only_size) {
|
|
+ if (Failed())
|
|
+ return;
|
|
+
|
|
+ if (IsDecodedSizeAvailable() && only_size) {
|
|
+ // Also SetEmbeddedProfile is done already if the size was set.
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ DCHECK_LE(num_decoded_frames_, frame_buffer_cache_.size());
|
|
+ if (num_decoded_frames_ > index &&
|
|
+ frame_buffer_cache_[index].GetStatus() == ImageFrame::kFrameComplete) {
|
|
+ // Frame already complete
|
|
+ return;
|
|
+ }
|
|
+ if ((index < num_decoded_frames_) && dec_ &&
|
|
+ frame_buffer_cache_[index].GetStatus() != ImageFrame::kFramePartial) {
|
|
+ // An animation frame that already has been decoded, but does not have
|
|
+ // status ImageFrame::kFrameComplete, was requested.
|
|
+ // This can mean two things:
|
|
+ // (1) an earlier animation frame was purged but is to be re-decoded now.
|
|
+ // Rewind the decoder and skip to the requested frame.
|
|
+ // (2) During progressive decoding the frame has the status
|
|
+ // ImageFrame::kFramePartial.
|
|
+ JxlDecoderRewind(dec_.get());
|
|
+ offset_ = 0;
|
|
+ // No longer subscribe to JXL_DEC_BASIC_INFO or JXL_DEC_COLOR_ENCODING.
|
|
+ if (JXL_DEC_SUCCESS !=
|
|
+ JxlDecoderSubscribeEvents(
|
|
+ dec_.get(), JXL_DEC_FULL_IMAGE | JXL_DEC_FRAME_PROGRESSION)) {
|
|
+ SetFailed();
|
|
+ return;
|
|
+ }
|
|
+ JxlDecoderSkipFrames(dec_.get(), index);
|
|
+ num_decoded_frames_ = index;
|
|
+ }
|
|
+
|
|
+ if (!dec_) {
|
|
+ dec_ = JxlDecoderMake(nullptr);
|
|
+ // Subscribe to color encoding event even when only getting size, because
|
|
+ // SetSize must be called after SetEmbeddedColorProfile
|
|
+ const int events = JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING |
|
|
+ JXL_DEC_FULL_IMAGE | JXL_DEC_FRAME_PROGRESSION;
|
|
+
|
|
+ if (JXL_DEC_SUCCESS != JxlDecoderSubscribeEvents(dec_.get(), events)) {
|
|
+ SetFailed();
|
|
+ return;
|
|
+ }
|
|
+ if (JXL_DEC_SUCCESS !=
|
|
+ JxlDecoderSetProgressiveDetail(dec_.get(), JxlProgressiveDetail::kDC)) {
|
|
+ SetFailed();
|
|
+ return;
|
|
+ }
|
|
+ } else {
|
|
+ offset_ -= JxlDecoderReleaseInput(dec_.get());
|
|
+ }
|
|
+
|
|
+ FastSharedBufferReader reader(data_.get());
|
|
+
|
|
+ const JxlPixelFormat format = {4, JXL_TYPE_FLOAT, JXL_NATIVE_ENDIAN, 0};
|
|
+
|
|
+ const bool size_available = IsDecodedSizeAvailable();
|
|
+
|
|
+ if (have_color_info_) {
|
|
+ xform_ = ColorTransform();
|
|
+ }
|
|
+
|
|
+ // The JXL API guarantees that we eventually get JXL_DEC_ERROR,
|
|
+ // JXL_DEC_SUCCESS or JXL_DEC_NEED_MORE_INPUT, and we exit the loop below in
|
|
+ // each case.
|
|
+ for (;;) {
|
|
+ if (only_size && have_color_info_)
|
|
+ return;
|
|
+ JxlDecoderStatus status = JxlDecoderProcessInput(dec_.get());
|
|
+ switch (status) {
|
|
+ case JXL_DEC_ERROR: {
|
|
+ DVLOG(1) << "Decoder error " << status;
|
|
+ SetFailed();
|
|
+ return;
|
|
+ }
|
|
+ case JXL_DEC_NEED_MORE_INPUT: {
|
|
+ // The decoder returns how many bytes it has not yet processed, and
|
|
+ // must be included in the next JxlDecoderSetInput call.
|
|
+ const size_t remaining = JxlDecoderReleaseInput(dec_.get());
|
|
+ const uint8_t* jxl_data = nullptr;
|
|
+ size_t jxl_size = 0;
|
|
+ if (!ReadBytes(remaining, &offset_, &segment_, &reader, &jxl_data,
|
|
+ &jxl_size)) {
|
|
+ if (IsAllDataReceived()) {
|
|
+ // Happens only if a partial image file was transferred, otherwise
|
|
+ // status will be JXL_DEC_FULL_IMAGE or JXL_DEC_SUCCESS. In
|
|
+ // this case we flush one more time in order to get the progressive
|
|
+ // image plus everything known so far. The progressive image was not
|
|
+ // flushed when status was JXL_DEC_FRAME_PROGRESSION because all
|
|
+ // data seemed to have been received (not knowing then that it was
|
|
+ // only a partial file).
|
|
+ if (JXL_DEC_SUCCESS != JxlDecoderFlushImage(dec_.get())) {
|
|
+ DVLOG(1) << "JxlDecoderSetImageOutCallback failed";
|
|
+ SetFailed();
|
|
+ return;
|
|
+ }
|
|
+ ImageFrame& frame = frame_buffer_cache_[num_decoded_frames_ - 1];
|
|
+ frame.SetPixelsChanged(true);
|
|
+ frame.SetStatus(ImageFrame::kFramePartial);
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (JXL_DEC_SUCCESS !=
|
|
+ JxlDecoderSetInput(dec_.get(), jxl_data, jxl_size)) {
|
|
+ DVLOG(1) << "JxlDecoderSetInput failed";
|
|
+ SetFailed();
|
|
+ return;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case JXL_DEC_BASIC_INFO: {
|
|
+ if (JXL_DEC_SUCCESS != JxlDecoderGetBasicInfo(dec_.get(), &info_)) {
|
|
+ DVLOG(1) << "JxlDecoderGetBasicInfo failed";
|
|
+ SetFailed();
|
|
+ return;
|
|
+ }
|
|
+ if (!size_available && !SetSize(info_.xsize, info_.ysize)) {
|
|
+ return;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case JXL_DEC_COLOR_ENCODING: {
|
|
+ if (IgnoresColorSpace()) {
|
|
+ have_color_info_ = true;
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ // If the decoder was used before with only_size == true, the color
|
|
+ // encoding is already decoded as well, and SetEmbeddedColorProfile
|
|
+ // should not be called a second time anymore.
|
|
+ if (size_available) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ // Detect whether the JXL image is intended to be an HDR image: when it
|
|
+ // uses more than 8 bits per pixel, or when it has explicitly marked
|
|
+ // PQ or HLG color profile.
|
|
+ if (info_.bits_per_sample > 8) {
|
|
+ is_hdr_ = true;
|
|
+ }
|
|
+ JxlColorEncoding color_encoding;
|
|
+ if (JXL_DEC_SUCCESS == JxlDecoderGetColorAsEncodedProfile(
|
|
+ dec_.get(), &format,
|
|
+ JXL_COLOR_PROFILE_TARGET_ORIGINAL,
|
|
+ &color_encoding)) {
|
|
+ if (color_encoding.transfer_function == JXL_TRANSFER_FUNCTION_PQ ||
|
|
+ color_encoding.transfer_function == JXL_TRANSFER_FUNCTION_HLG) {
|
|
+ is_hdr_ = true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ std::unique_ptr<ColorProfile> profile;
|
|
+
|
|
+ if (is_hdr_ &&
|
|
+ high_bit_depth_decoding_option_ == kHighBitDepthToHalfFloat) {
|
|
+ decode_to_half_float_ = true;
|
|
+ }
|
|
+
|
|
+ bool have_data_profile = false;
|
|
+ if (JXL_DEC_SUCCESS ==
|
|
+ JxlDecoderGetColorAsEncodedProfile(dec_.get(), &format,
|
|
+ JXL_COLOR_PROFILE_TARGET_DATA,
|
|
+ &color_encoding)) {
|
|
+ bool known_transfer_function = true;
|
|
+ bool known_gamut = true;
|
|
+ gfx::ColorSpace::PrimaryID gamut;
|
|
+ gfx::ColorSpace::TransferID transfer;
|
|
+ if (color_encoding.transfer_function == JXL_TRANSFER_FUNCTION_PQ) {
|
|
+ transfer = gfx::ColorSpace::TransferID::PQ;
|
|
+ } else if (color_encoding.transfer_function ==
|
|
+ JXL_TRANSFER_FUNCTION_HLG) {
|
|
+ transfer = gfx::ColorSpace::TransferID::HLG;
|
|
+ } else if (color_encoding.transfer_function ==
|
|
+ JXL_TRANSFER_FUNCTION_LINEAR) {
|
|
+ transfer = gfx::ColorSpace::TransferID::LINEAR;
|
|
+ } else if (color_encoding.transfer_function ==
|
|
+ JXL_TRANSFER_FUNCTION_SRGB) {
|
|
+ transfer = gfx::ColorSpace::TransferID::SRGB;
|
|
+ } else {
|
|
+ known_transfer_function = false;
|
|
+ }
|
|
+
|
|
+ if (color_encoding.white_point == JXL_WHITE_POINT_D65 &&
|
|
+ color_encoding.primaries == JXL_PRIMARIES_2100) {
|
|
+ gamut = gfx::ColorSpace::PrimaryID::BT2020;
|
|
+ } else if (color_encoding.white_point == JXL_WHITE_POINT_D65 &&
|
|
+ color_encoding.primaries == JXL_PRIMARIES_SRGB) {
|
|
+ gamut = gfx::ColorSpace::PrimaryID::BT709;
|
|
+ } else if (color_encoding.white_point == JXL_WHITE_POINT_D65 &&
|
|
+ color_encoding.primaries == JXL_PRIMARIES_P3) {
|
|
+ gamut = gfx::ColorSpace::PrimaryID::P3;
|
|
+ } else {
|
|
+ known_gamut = false;
|
|
+ }
|
|
+
|
|
+ have_data_profile = known_transfer_function && known_gamut;
|
|
+
|
|
+ if (have_data_profile) {
|
|
+ skcms_ICCProfile dataProfile;
|
|
+ gfx::ColorSpace(gamut, transfer)
|
|
+ .ToSkColorSpace()
|
|
+ ->toProfile(&dataProfile);
|
|
+ profile = std::make_unique<ColorProfile>(dataProfile);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // Did not handle exact enum values, get as ICC profile instead.
|
|
+ if (!have_data_profile) {
|
|
+ size_t icc_size;
|
|
+ bool got_size =
|
|
+ JXL_DEC_SUCCESS == JxlDecoderGetICCProfileSize(
|
|
+ dec_.get(), &format,
|
|
+ JXL_COLOR_PROFILE_TARGET_DATA, &icc_size);
|
|
+ std::vector<uint8_t> icc_profile(icc_size);
|
|
+ if (got_size &&
|
|
+ JXL_DEC_SUCCESS == JxlDecoderGetColorAsICCProfile(
|
|
+ dec_.get(), &format,
|
|
+ JXL_COLOR_PROFILE_TARGET_DATA,
|
|
+ icc_profile.data(), icc_profile.size())) {
|
|
+ profile =
|
|
+ ColorProfile::Create(icc_profile.data(), icc_profile.size());
|
|
+ have_data_profile = true;
|
|
+
|
|
+ // Detect whether the ICC profile approximately equals PQ or HLG,
|
|
+ // and set the profile to one that indicates this transfer function
|
|
+ // more clearly than a raw ICC profile does, so Chrome considers
|
|
+ // the profile as HDR.
|
|
+ skcms_TransferFunction tf_pq;
|
|
+ skcms_TransferFunction tf_hlg01;
|
|
+ skcms_TransferFunction tf_hlg12;
|
|
+ skcms_TransferFunction_makePQ(&tf_pq);
|
|
+ MakeTransferFunctionHLG01(&tf_hlg01);
|
|
+ skcms_TransferFunction_makeHLG(&tf_hlg12);
|
|
+
|
|
+ if (ApproximatelyMatchesTF(*profile, tf_pq)) {
|
|
+ is_hdr_ = true;
|
|
+ auto hdr10 = gfx::ColorSpace::CreateHDR10().ToSkColorSpace();
|
|
+ skcms_TransferFunction pq;
|
|
+ hdr10->transferFn(&pq);
|
|
+ profile = NewColorProfileWithSameBuffer(
|
|
+ *profile,
|
|
+ ReplaceTransferFunction(*profile->GetProfile(), pq));
|
|
+ } else {
|
|
+ for (skcms_TransferFunction tf : {tf_hlg01, tf_hlg12}) {
|
|
+ if (ApproximatelyMatchesTF(*profile, tf)) {
|
|
+ is_hdr_ = true;
|
|
+ auto hlg_colorspace =
|
|
+ gfx::ColorSpace::CreateHLG().ToSkColorSpace();
|
|
+ skcms_TransferFunction hlg;
|
|
+ hlg_colorspace->transferFn(&hlg);
|
|
+ profile = NewColorProfileWithSameBuffer(
|
|
+ *profile,
|
|
+ ReplaceTransferFunction(*profile->GetProfile(), hlg));
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (is_hdr_ &&
|
|
+ high_bit_depth_decoding_option_ == kHighBitDepthToHalfFloat) {
|
|
+ decode_to_half_float_ = true;
|
|
+ }
|
|
+
|
|
+ if (have_data_profile) {
|
|
+ if (profile->GetProfile()->data_color_space == skcms_Signature_RGB) {
|
|
+ SetEmbeddedColorProfile(std::move(profile));
|
|
+ }
|
|
+ }
|
|
+ have_color_info_ = true;
|
|
+ break;
|
|
+ }
|
|
+ case JXL_DEC_NEED_IMAGE_OUT_BUFFER: {
|
|
+ const wtf_size_t frame_index = num_decoded_frames_++;
|
|
+ ImageFrame& frame = frame_buffer_cache_[frame_index];
|
|
+ // This is guaranteed to occur after JXL_DEC_BASIC_INFO so the size
|
|
+ // is correct.
|
|
+ if (!InitFrameBuffer(frame_index)) {
|
|
+ DVLOG(1) << "InitFrameBuffer failed";
|
|
+ SetFailed();
|
|
+ return;
|
|
+ }
|
|
+ frame.SetHasAlpha(info_.alpha_bits != 0);
|
|
+
|
|
+ size_t buffer_size;
|
|
+ if (JXL_DEC_SUCCESS !=
|
|
+ JxlDecoderImageOutBufferSize(dec_.get(), &format, &buffer_size)) {
|
|
+ DVLOG(1) << "JxlDecoderImageOutBufferSize failed";
|
|
+ SetFailed();
|
|
+ return;
|
|
+ }
|
|
+ if (buffer_size != info_.xsize * info_.ysize * 16) {
|
|
+ DVLOG(1) << "Unexpected buffer size";
|
|
+ SetFailed();
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // TODO(http://crbug.com/1210465): Add Munsell chart color accuracy
|
|
+ // tests for JXL
|
|
+ xform_ = ColorTransform();
|
|
+ auto callback = [](void* opaque, size_t x, size_t y, size_t num_pixels,
|
|
+ const void* pixels) {
|
|
+ JXLImageDecoder* self = reinterpret_cast<JXLImageDecoder*>(opaque);
|
|
+ ImageFrame& frame =
|
|
+ self->frame_buffer_cache_[self->num_decoded_frames_ - 1];
|
|
+ void* row_dst = self->decode_to_half_float_
|
|
+ ? reinterpret_cast<void*>(frame.GetAddrF16(
|
|
+ static_cast<int>(x), static_cast<int>(y)))
|
|
+ : reinterpret_cast<void*>(frame.GetAddr(
|
|
+ static_cast<int>(x), static_cast<int>(y)));
|
|
+
|
|
+ bool dst_premultiply = frame.PremultiplyAlpha();
|
|
+
|
|
+ const skcms_PixelFormat kSrcFormat = skcms_PixelFormat_RGBA_ffff;
|
|
+ const skcms_PixelFormat kDstFormat = self->decode_to_half_float_
|
|
+ ? skcms_PixelFormat_RGBA_hhhh
|
|
+ : XformColorFormat();
|
|
+
|
|
+ if (self->xform_ || (kDstFormat != kSrcFormat) ||
|
|
+ (dst_premultiply && frame.HasAlpha())) {
|
|
+ skcms_AlphaFormat src_alpha = skcms_AlphaFormat_Unpremul;
|
|
+ skcms_AlphaFormat dst_alpha =
|
|
+ (dst_premultiply && self->info_.alpha_bits)
|
|
+ ? skcms_AlphaFormat_PremulAsEncoded
|
|
+ : skcms_AlphaFormat_Unpremul;
|
|
+ const auto* src_profile =
|
|
+ self->xform_ ? self->xform_->SrcProfile() : nullptr;
|
|
+ const auto* dst_profile =
|
|
+ self->xform_ ? self->xform_->DstProfile() : nullptr;
|
|
+ bool color_conversion_successful = skcms_Transform(
|
|
+ pixels, kSrcFormat, src_alpha, src_profile, row_dst, kDstFormat,
|
|
+ dst_alpha, dst_profile, num_pixels);
|
|
+ DCHECK(color_conversion_successful);
|
|
+ }
|
|
+ };
|
|
+ if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutCallback(
|
|
+ dec_.get(), &format, callback, this)) {
|
|
+ DVLOG(1) << "JxlDecoderSetImageOutCallback failed";
|
|
+ SetFailed();
|
|
+ return;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case JXL_DEC_FRAME_PROGRESSION: {
|
|
+ if (IsAllDataReceived()) {
|
|
+ break;
|
|
+ } else {
|
|
+ if (JXL_DEC_SUCCESS != JxlDecoderFlushImage(dec_.get())) {
|
|
+ DVLOG(1) << "JxlDecoderSetImageOutCallback failed";
|
|
+ SetFailed();
|
|
+ return;
|
|
+ }
|
|
+ ImageFrame& frame = frame_buffer_cache_[num_decoded_frames_ - 1];
|
|
+ frame.SetPixelsChanged(true);
|
|
+ frame.SetStatus(ImageFrame::kFramePartial);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ case JXL_DEC_FULL_IMAGE: {
|
|
+ ImageFrame& frame = frame_buffer_cache_[num_decoded_frames_ - 1];
|
|
+ frame.SetPixelsChanged(true);
|
|
+ frame.SetStatus(ImageFrame::kFrameComplete);
|
|
+ // All required frames were decoded.
|
|
+ if (num_decoded_frames_ > index) {
|
|
+ return;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case JXL_DEC_SUCCESS: {
|
|
+ // Finished decoding entire image, with all frames in case of animation.
|
|
+ // Don't reset dec_, since we may want to rewind it if an earlier
|
|
+ // animation frame has to be decoded again.
|
|
+ segment_.clear();
|
|
+ return;
|
|
+ }
|
|
+ default: {
|
|
+ DVLOG(1) << "Unexpected decoder status " << status;
|
|
+ SetFailed();
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+bool JXLImageDecoder::MatchesJXLSignature(
|
|
+ const FastSharedBufferReader& fast_reader) {
|
|
+ char buffer[12];
|
|
+ if (fast_reader.size() < sizeof(buffer))
|
|
+ return false;
|
|
+ const char* contents = reinterpret_cast<const char*>(
|
|
+ fast_reader.GetConsecutiveData(0, sizeof(buffer), buffer));
|
|
+ // Direct codestream
|
|
+ if (!memcmp(contents, "\xFF\x0A", 2))
|
|
+ return true;
|
|
+ // Box format container
|
|
+ if (!memcmp(contents, "\0\0\0\x0CJXL \x0D\x0A\x87\x0A", 12))
|
|
+ return true;
|
|
+ return false;
|
|
+}
|
|
+
|
|
+void JXLImageDecoder::InitializeNewFrame(wtf_size_t index) {
|
|
+ auto& buffer = frame_buffer_cache_[index];
|
|
+ if (decode_to_half_float_)
|
|
+ buffer.SetPixelFormat(ImageFrame::PixelFormat::kRGBA_F16);
|
|
+ buffer.SetHasAlpha(info_.alpha_bits != 0);
|
|
+ buffer.SetPremultiplyAlpha(premultiply_alpha_);
|
|
+}
|
|
+
|
|
+bool JXLImageDecoder::FrameIsReceivedAtIndex(wtf_size_t index) const {
|
|
+ return IsAllDataReceived() ||
|
|
+ (index < num_decoded_frames_ &&
|
|
+ frame_buffer_cache_[index].GetStatus() == ImageFrame::kFrameComplete);
|
|
+}
|
|
+
|
|
+int JXLImageDecoder::RepetitionCount() const {
|
|
+ if (!info_.have_animation)
|
|
+ return kAnimationNone;
|
|
+
|
|
+ if (info_.animation.num_loops == 0)
|
|
+ return kAnimationLoopInfinite;
|
|
+
|
|
+ if (info_.animation.num_loops == 1)
|
|
+ return kAnimationLoopOnce;
|
|
+
|
|
+ return info_.animation.num_loops;
|
|
+}
|
|
+
|
|
+base::TimeDelta JXLImageDecoder::FrameDurationAtIndex(wtf_size_t index) const {
|
|
+ if (index < frame_durations_.size())
|
|
+ return base::Seconds(frame_durations_[index]);
|
|
+
|
|
+ return base::TimeDelta();
|
|
+}
|
|
+
|
|
+wtf_size_t JXLImageDecoder::DecodeFrameCount() {
|
|
+ DecodeSize();
|
|
+ if (!info_.have_animation) {
|
|
+ frame_durations_.resize(1);
|
|
+ frame_durations_[0] = 0;
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ FastSharedBufferReader reader(data_.get());
|
|
+ if (has_full_frame_count_ || size_at_last_frame_count_ == reader.size()) {
|
|
+ return frame_buffer_cache_.size();
|
|
+ }
|
|
+ size_at_last_frame_count_ = reader.size();
|
|
+
|
|
+ // Decode the metadata of every frame that is available.
|
|
+ if (frame_count_dec_ == nullptr) {
|
|
+ frame_durations_.clear();
|
|
+ frame_count_dec_ = JxlDecoderMake(nullptr);
|
|
+ frame_count_offset_ = 0;
|
|
+ if (JXL_DEC_SUCCESS !=
|
|
+ JxlDecoderSubscribeEvents(frame_count_dec_.get(), JXL_DEC_FRAME)) {
|
|
+ SetFailed();
|
|
+ return frame_buffer_cache_.size();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ for (;;) {
|
|
+ JxlDecoderStatus status = JxlDecoderProcessInput(frame_count_dec_.get());
|
|
+ switch (status) {
|
|
+ case JXL_DEC_ERROR: {
|
|
+ DVLOG(1) << "Decoder error " << status;
|
|
+ SetFailed();
|
|
+ return frame_buffer_cache_.size();
|
|
+ }
|
|
+ case JXL_DEC_NEED_MORE_INPUT: {
|
|
+ // The decoder returns how many bytes it has not yet processed, and
|
|
+ // must be included in the next JxlDecoderSetInput call.
|
|
+ const size_t remaining = JxlDecoderReleaseInput(frame_count_dec_.get());
|
|
+ const uint8_t* jxl_data = nullptr;
|
|
+ size_t jxl_size = 0;
|
|
+ if (!ReadBytes(remaining, &frame_count_offset_, &frame_count_segment_,
|
|
+ &reader, &jxl_data, &jxl_size)) {
|
|
+ if (Failed()) {
|
|
+ return frame_buffer_cache_.size();
|
|
+ }
|
|
+ return frame_durations_.size();
|
|
+ }
|
|
+
|
|
+ if (JXL_DEC_SUCCESS !=
|
|
+ JxlDecoderSetInput(frame_count_dec_.get(), jxl_data, jxl_size)) {
|
|
+ DVLOG(1) << "JxlDecoderSetInput failed";
|
|
+ SetFailed();
|
|
+ return frame_buffer_cache_.size();
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case JXL_DEC_FRAME: {
|
|
+ JxlFrameHeader frame_header;
|
|
+ if (JxlDecoderGetFrameHeader(frame_count_dec_.get(), &frame_header) !=
|
|
+ JXL_DEC_SUCCESS) {
|
|
+ DVLOG(1) << "GetFrameHeader failed";
|
|
+ SetFailed();
|
|
+ return frame_buffer_cache_.size();
|
|
+ }
|
|
+ if (frame_header.is_last) {
|
|
+ has_full_frame_count_ = true;
|
|
+ }
|
|
+ frame_durations_.push_back(1.0f * frame_header.duration *
|
|
+ info_.animation.tps_denominator /
|
|
+ info_.animation.tps_numerator);
|
|
+ break;
|
|
+ }
|
|
+ case JXL_DEC_SUCCESS: {
|
|
+ // If the file is fully processed, we won't need to run the decoder
|
|
+ // anymore: we can free the memory.
|
|
+ frame_count_dec_ = nullptr;
|
|
+ DCHECK(has_full_frame_count_);
|
|
+ frame_count_segment_.clear();
|
|
+ return frame_durations_.size();
|
|
+ }
|
|
+ default: {
|
|
+ DVLOG(1) << "Unexpected decoder status " << status;
|
|
+ SetFailed();
|
|
+ return frame_buffer_cache_.size();
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+} // namespace blink
|
|
diff --git a/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder.h b/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder.h
|
|
new file mode 100644
|
|
--- /dev/null
|
|
+++ b/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder.h
|
|
@@ -0,0 +1,123 @@
|
|
+/*
|
|
+ * Copyright (c) 2021, Google Inc. 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.
|
|
+ * * Redistributions in binary form must reproduce the above
|
|
+ * copyright notice, this list of conditions and the following disclaimer
|
|
+ * in the documentation and/or other materials provided with the
|
|
+ * distribution.
|
|
+ * * Neither the name of Google Inc. nor the names of its
|
|
+ * contributors may be used to endorse or promote products derived from
|
|
+ * this software without specific prior written permission.
|
|
+ *
|
|
+ * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_JXL_JXL_IMAGE_DECODER_H_
|
|
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_JXL_JXL_IMAGE_DECODER_H_
|
|
+
|
|
+#include "third_party/blink/renderer/platform/image-decoders/fast_shared_buffer_reader.h"
|
|
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
|
|
+
|
|
+#include "third_party/libjxl/src/lib/include/jxl/decode.h"
|
|
+#include "third_party/libjxl/src/lib/include/jxl/decode_cxx.h"
|
|
+
|
|
+namespace blink {
|
|
+
|
|
+// This class decodes the JXL image format.
|
|
+class PLATFORM_EXPORT JXLImageDecoder final : public ImageDecoder {
|
|
+ public:
|
|
+ JXLImageDecoder(AlphaOption,
|
|
+ HighBitDepthDecodingOption high_bit_depth_decoding_option,
|
|
+ const ColorBehavior&,
|
|
+ wtf_size_t max_decoded_bytes);
|
|
+
|
|
+ // ImageDecoder:
|
|
+ String FilenameExtension() const override { return "jxl"; }
|
|
+ const AtomicString& MimeType() const override;
|
|
+ bool ImageIsHighBitDepth() override { return is_hdr_; }
|
|
+
|
|
+ // Returns true if the data in fast_reader begins with
|
|
+ static bool MatchesJXLSignature(const FastSharedBufferReader& fast_reader);
|
|
+
|
|
+ private:
|
|
+ // ImageDecoder:
|
|
+ void DecodeSize() override { DecodeImpl(0, true); }
|
|
+ wtf_size_t DecodeFrameCount() override;
|
|
+ void Decode(wtf_size_t frame) override { DecodeImpl(frame); }
|
|
+ void InitializeNewFrame(wtf_size_t) override;
|
|
+
|
|
+ // Decodes up to a given frame. If |only_size| is true, stops decoding after
|
|
+ // calculating the image size. If decoding fails but there is no more
|
|
+ // data coming, sets the "decode failure" flag.
|
|
+ void DecodeImpl(wtf_size_t frame, bool only_size = false);
|
|
+
|
|
+ bool FrameIsReceivedAtIndex(wtf_size_t) const override;
|
|
+ base::TimeDelta FrameDurationAtIndex(wtf_size_t) const override;
|
|
+ int RepetitionCount() const override;
|
|
+ bool CanReusePreviousFrameBuffer(wtf_size_t) const override { return false; }
|
|
+
|
|
+ // Reads bytes from the segment reader, after releasing input from the JXL
|
|
+ // decoder, which required `remaining` previous bytes to still be available.
|
|
+ // Starts reading from *offset - remaining, and ensures more than remaining
|
|
+ // bytes are read, if possible. Returns false if not enough bytes are
|
|
+ // available or if Failed() was set.
|
|
+ bool ReadBytes(size_t remaining,
|
|
+ wtf_size_t* offset,
|
|
+ WTF::Vector<uint8_t>* segment,
|
|
+ FastSharedBufferReader* reader,
|
|
+ const uint8_t** jxl_data,
|
|
+ size_t* jxl_size);
|
|
+
|
|
+ JxlDecoderPtr dec_ = nullptr;
|
|
+ wtf_size_t offset_ = 0;
|
|
+
|
|
+ JxlDecoderPtr frame_count_dec_ = nullptr;
|
|
+ wtf_size_t frame_count_offset_ = 0;
|
|
+
|
|
+ // The image is considered to be HDR, such as using PQ or HLG transfer
|
|
+ // function in the color space.
|
|
+ bool is_hdr_ = false;
|
|
+ bool decode_to_half_float_ = false;
|
|
+
|
|
+ JxlBasicInfo info_;
|
|
+ bool have_color_info_ = false;
|
|
+
|
|
+ // Preserved for JXL pixel callback. Not owned.
|
|
+ ColorProfileTransform* xform_;
|
|
+
|
|
+ // Fields for animation support.
|
|
+
|
|
+ // The amount of frames the JXL decoder has decoded. This can be reset to
|
|
+ // an earlier amount if frame buffers were cleared and decoding was
|
|
+ // restarted from an earlier frame. This is used to keep track of the index
|
|
+ // in the frame_buffer_cache_.
|
|
+ wtf_size_t num_decoded_frames_ = 0;
|
|
+ bool has_full_frame_count_ = false;
|
|
+ size_t size_at_last_frame_count_ = 0;
|
|
+ WTF::Vector<float> frame_durations_;
|
|
+ // Multiple concatenated segments from the FastSharedBufferReader, these are
|
|
+ // only used when a single segment did not contain enough data for the JXL
|
|
+ // parser.
|
|
+ WTF::Vector<uint8_t> segment_;
|
|
+ WTF::Vector<uint8_t> frame_count_segment_;
|
|
+};
|
|
+
|
|
+} // namespace blink
|
|
+
|
|
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_JXL_JXL_IMAGE_DECODER_H_
|
|
diff --git a/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder_test.cc b/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder_test.cc
|
|
new file mode 100644
|
|
--- /dev/null
|
|
+++ b/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder_test.cc
|
|
@@ -0,0 +1,626 @@
|
|
+// Copyright 2021 The Chromium Authors
|
|
+// Use of this source code is governed by a BSD-style license that can be
|
|
+// found in the LICENSE file.
|
|
+
|
|
+#include "third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder.h"
|
|
+
|
|
+#include <memory>
|
|
+#include "testing/gtest/include/gtest/gtest.h"
|
|
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.h"
|
|
+#include "third_party/skia/include/core/SkColorSpace.h"
|
|
+#include "ui/gfx/geometry/point.h"
|
|
+
|
|
+namespace blink {
|
|
+
|
|
+namespace {
|
|
+
|
|
+std::unique_ptr<ImageDecoder> CreateJXLDecoderWithArguments(
|
|
+ const char* jxl_file,
|
|
+ ImageDecoder::AlphaOption alpha_option,
|
|
+ ImageDecoder::HighBitDepthDecodingOption high_bit_depth_decoding_option,
|
|
+ ColorBehavior color_behavior) {
|
|
+ auto decoder = std::make_unique<JXLImageDecoder>(
|
|
+ alpha_option, high_bit_depth_decoding_option, color_behavior,
|
|
+ ImageDecoder::kNoDecodedImageByteLimit);
|
|
+ scoped_refptr<SharedBuffer> data = ReadFile(jxl_file);
|
|
+ EXPECT_FALSE(data->empty());
|
|
+ decoder->SetData(data.get(), true);
|
|
+ return decoder;
|
|
+}
|
|
+
|
|
+std::unique_ptr<ImageDecoder> CreateJXLDecoder() {
|
|
+ return std::make_unique<JXLImageDecoder>(
|
|
+ ImageDecoder::kAlphaNotPremultiplied, ImageDecoder::kDefaultBitDepth,
|
|
+ ColorBehavior::Tag(), ImageDecoder::kNoDecodedImageByteLimit);
|
|
+}
|
|
+
|
|
+std::unique_ptr<ImageDecoder> CreateJXLDecoderWithData(const char* jxl_file) {
|
|
+ auto decoder = CreateJXLDecoder();
|
|
+ scoped_refptr<SharedBuffer> data = ReadFile(jxl_file);
|
|
+ EXPECT_FALSE(data->empty());
|
|
+ decoder->SetData(data.get(), true);
|
|
+ return decoder;
|
|
+}
|
|
+
|
|
+// expected_color must match the expected top left pixel
|
|
+void TestColorProfile(const char* jxl_file,
|
|
+ ColorBehavior color_behavior,
|
|
+ SkColor expected_color) {
|
|
+ auto decoder = CreateJXLDecoderWithArguments(
|
|
+ jxl_file, ImageDecoder::AlphaOption::kAlphaNotPremultiplied,
|
|
+ ImageDecoder::kDefaultBitDepth, color_behavior);
|
|
+ EXPECT_EQ(1u, decoder->FrameCount());
|
|
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0);
|
|
+ ASSERT_TRUE(frame);
|
|
+ EXPECT_EQ(ImageFrame::kFrameComplete, frame->GetStatus());
|
|
+ EXPECT_FALSE(decoder->Failed());
|
|
+ const SkBitmap& bitmap = frame->Bitmap();
|
|
+ SkColor frame_color = bitmap.getColor(0, 0);
|
|
+ for (int i = 0; i < 4; ++i) {
|
|
+ int frame_comp = (frame_color >> (8 * i)) & 255;
|
|
+ int expected_comp = (expected_color >> (8 * i)) & 255;
|
|
+ EXPECT_GE(1, abs(frame_comp - expected_comp));
|
|
+ }
|
|
+}
|
|
+
|
|
+// Convert from float16 bits in a uint16_t, to 32-bit float, for testing
|
|
+static float FromFloat16(uint16_t a) {
|
|
+ // 5 bits exponent
|
|
+ int exp = (a >> 10) & 31;
|
|
+ // 10 bits fractional part
|
|
+ float frac = a & 1023;
|
|
+ // 1 bit sign
|
|
+ int sign = (a & 32768) ? 1 : 0;
|
|
+ bool subnormal = exp == 0;
|
|
+ // Infinity and NaN are not supported here.
|
|
+ exp -= 15;
|
|
+ if (subnormal)
|
|
+ exp++;
|
|
+ frac /= 1024.0;
|
|
+ if (!subnormal)
|
|
+ frac++;
|
|
+ frac *= std::pow(2, exp);
|
|
+ if (sign)
|
|
+ frac = -frac;
|
|
+ return frac;
|
|
+}
|
|
+
|
|
+// expected_color must match the expected top left pixel
|
|
+void TestHDR(const char* jxl_file,
|
|
+ ColorBehavior color_behavior,
|
|
+ bool expect_f16,
|
|
+ float expected_r,
|
|
+ float expected_g,
|
|
+ float expected_b,
|
|
+ float expected_a) {
|
|
+ auto decoder = CreateJXLDecoderWithArguments(
|
|
+ jxl_file, ImageDecoder::AlphaOption::kAlphaPremultiplied,
|
|
+ ImageDecoder::kHighBitDepthToHalfFloat, color_behavior);
|
|
+ EXPECT_TRUE(decoder->IsSizeAvailable());
|
|
+ EXPECT_EQ(1u, decoder->FrameCount());
|
|
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0);
|
|
+ ASSERT_TRUE(frame);
|
|
+ EXPECT_EQ(ImageFrame::kFrameComplete, frame->GetStatus());
|
|
+ EXPECT_FALSE(decoder->Failed());
|
|
+ float r, g, b, a;
|
|
+ if (expect_f16) {
|
|
+ EXPECT_EQ(ImageFrame::kRGBA_F16, frame->GetPixelFormat());
|
|
+ } else {
|
|
+ EXPECT_EQ(ImageFrame::kN32, frame->GetPixelFormat());
|
|
+ }
|
|
+ if (ImageFrame::kRGBA_F16 == frame->GetPixelFormat()) {
|
|
+ uint64_t first_pixel = *frame->GetAddrF16(0, 0);
|
|
+ r = FromFloat16(first_pixel >> 0);
|
|
+ g = FromFloat16(first_pixel >> 16);
|
|
+ b = FromFloat16(first_pixel >> 32);
|
|
+ a = FromFloat16(first_pixel >> 48);
|
|
+ } else {
|
|
+ uint32_t first_pixel = *frame->GetAddr(0, 0);
|
|
+ a = ((first_pixel >> SK_A32_SHIFT) & 255) / 255.0;
|
|
+ r = ((first_pixel >> SK_R32_SHIFT) & 255) / 255.0;
|
|
+ g = ((first_pixel >> SK_G32_SHIFT) & 255) / 255.0;
|
|
+ b = ((first_pixel >> SK_B32_SHIFT) & 255) / 255.0;
|
|
+ }
|
|
+ constexpr float eps = 0.01;
|
|
+ EXPECT_NEAR(expected_r, r, eps);
|
|
+ EXPECT_NEAR(expected_g, g, eps);
|
|
+ EXPECT_NEAR(expected_b, b, eps);
|
|
+ EXPECT_NEAR(expected_a, a, eps);
|
|
+}
|
|
+
|
|
+void TestSize(const char* jxl_file, gfx::Size expected_size) {
|
|
+ auto decoder = CreateJXLDecoderWithData(jxl_file);
|
|
+ EXPECT_TRUE(decoder->IsSizeAvailable());
|
|
+ EXPECT_EQ(expected_size, decoder->Size());
|
|
+}
|
|
+
|
|
+struct FramePoint {
|
|
+ size_t frame;
|
|
+ gfx::Point point;
|
|
+};
|
|
+
|
|
+void TestPixel(const char* jxl_file,
|
|
+ gfx::Size expected_size,
|
|
+ const WTF::Vector<FramePoint>& coordinates,
|
|
+ const WTF::Vector<SkColor>& expected_colors,
|
|
+ ImageDecoder::AlphaOption alpha_option,
|
|
+ ColorBehavior color_behavior,
|
|
+ int accuracy,
|
|
+ size_t num_frames = 1) {
|
|
+ SCOPED_TRACE(testing::Message()
|
|
+ << "TestPixel jxl_file: " << jxl_file
|
|
+ << ", alpha_option:" << static_cast<int>(alpha_option));
|
|
+ EXPECT_EQ(coordinates.size(), expected_colors.size());
|
|
+ auto decoder = CreateJXLDecoderWithArguments(
|
|
+ jxl_file, alpha_option, ImageDecoder::kDefaultBitDepth, color_behavior);
|
|
+ EXPECT_TRUE(decoder->IsSizeAvailable());
|
|
+ EXPECT_EQ(expected_size, decoder->Size());
|
|
+ ASSERT_EQ(num_frames, decoder->FrameCount());
|
|
+ for (size_t i = 0; i < num_frames; ++i) {
|
|
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(i);
|
|
+ ASSERT_TRUE(frame);
|
|
+ EXPECT_EQ(ImageFrame::kFrameComplete, frame->GetStatus());
|
|
+ }
|
|
+ EXPECT_FALSE(decoder->Failed());
|
|
+ for (size_t i = 0; i < coordinates.size(); ++i) {
|
|
+ SCOPED_TRACE(testing::Message() << "Coordinate: " << i);
|
|
+ const SkBitmap& bitmap =
|
|
+ decoder->DecodeFrameBufferAtIndex(coordinates[i].frame)->Bitmap();
|
|
+ EXPECT_TRUE(SkColorSpace::Equals(bitmap.colorSpace(),
|
|
+ decoder->ColorSpaceForSkImages().get()));
|
|
+ int x = coordinates[i].point.x();
|
|
+ int y = coordinates[i].point.y();
|
|
+ SkColor frame_color = bitmap.getColor(x, y);
|
|
+ int r_expected = (expected_colors[i] >> 16) & 255;
|
|
+ int g_expected = (expected_colors[i] >> 8) & 255;
|
|
+ int b_expected = (expected_colors[i] >> 0) & 255;
|
|
+ int a_expected = (expected_colors[i] >> 24) & 255;
|
|
+ int r_actual = (frame_color >> 16) & 255;
|
|
+ int g_actual = (frame_color >> 8) & 255;
|
|
+ int b_actual = (frame_color >> 0) & 255;
|
|
+ int a_actual = (frame_color >> 24) & 255;
|
|
+ EXPECT_NEAR(r_expected, r_actual, accuracy);
|
|
+ EXPECT_NEAR(g_expected, g_actual, accuracy);
|
|
+ EXPECT_NEAR(b_expected, b_actual, accuracy);
|
|
+ // Alpha is always lossless.
|
|
+ EXPECT_EQ(a_expected, a_actual);
|
|
+ }
|
|
+}
|
|
+
|
|
+// SegmentReader implementation for testing, which always returns segments
|
|
+// of size 1. This allows to test whether the decoder handles streaming
|
|
+// correctly in the most fine-grained case.
|
|
+class PerByteSegmentReader : public SegmentReader {
|
|
+ public:
|
|
+ PerByteSegmentReader(SharedBuffer& buffer) : buffer_(buffer) {}
|
|
+ size_t size() const override { return buffer_.size(); }
|
|
+ size_t GetSomeData(const char*& data, size_t position) const override {
|
|
+ if (position >= buffer_.size()) {
|
|
+ return 0;
|
|
+ }
|
|
+ data = buffer_.Data() + position;
|
|
+ return 1;
|
|
+ }
|
|
+ sk_sp<SkData> GetAsSkData() const override { return nullptr; }
|
|
+
|
|
+ private:
|
|
+ SharedBuffer& buffer_;
|
|
+};
|
|
+
|
|
+// Tests whether the decoder successfully parses the file without errors or
|
|
+// infinite loop in the worst case of the reader returning 1-byte segments.
|
|
+void TestSegmented(const char* jxl_file, gfx::Size expected_size) {
|
|
+ auto decoder = std::make_unique<JXLImageDecoder>(
|
|
+ ImageDecoder::kAlphaNotPremultiplied, ImageDecoder::kDefaultBitDepth,
|
|
+ ColorBehavior::Tag(), ImageDecoder::kNoDecodedImageByteLimit);
|
|
+ scoped_refptr<SharedBuffer> data = ReadFile(jxl_file);
|
|
+ EXPECT_FALSE(data->empty());
|
|
+
|
|
+ scoped_refptr<SegmentReader> reader =
|
|
+ base::AdoptRef(new PerByteSegmentReader(*data.get()));
|
|
+ decoder->SetData(reader, true);
|
|
+
|
|
+ ImageFrame* frame;
|
|
+ for (;;) {
|
|
+ frame = decoder->DecodeFrameBufferAtIndex(0);
|
|
+ if (decoder->Failed())
|
|
+ break;
|
|
+ if (frame)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ EXPECT_TRUE(decoder->IsSizeAvailable());
|
|
+ EXPECT_LE(1u, decoder->FrameCount());
|
|
+ EXPECT_TRUE(!!frame);
|
|
+ EXPECT_EQ(ImageFrame::kFrameComplete, frame->GetStatus());
|
|
+ EXPECT_FALSE(decoder->Failed());
|
|
+ EXPECT_EQ(expected_size, decoder->Size());
|
|
+}
|
|
+
|
|
+TEST(JXLTests, SegmentedTest) {
|
|
+ TestSegmented("/images/resources/jxl/alpha-lossless.jxl", gfx::Size(2, 10));
|
|
+ TestSegmented("/images/resources/jxl/3x3_srgb_lossy.jxl", gfx::Size(3, 3));
|
|
+ TestSegmented("/images/resources/jxl/pq_gradient_icc_lossy.jxl",
|
|
+ gfx::Size(16, 16));
|
|
+ TestSegmented("/images/resources/jxl/animated.jxl", gfx::Size(16, 16));
|
|
+}
|
|
+
|
|
+TEST(JXLTests, SizeTest) {
|
|
+ TestSize("/images/resources/jxl/alpha-lossless.jxl", gfx::Size(2, 10));
|
|
+}
|
|
+
|
|
+TEST(JXLTests, PixelTest) {
|
|
+ TestPixel("/images/resources/jxl/red-10-default.jxl", gfx::Size(10, 10),
|
|
+ {{0, {0, 0}}}, {SkColorSetARGB(255, 255, 0, 0)},
|
|
+ ImageDecoder::AlphaOption::kAlphaNotPremultiplied,
|
|
+ ColorBehavior::Tag(), 0);
|
|
+ TestPixel("/images/resources/jxl/red-10-lossless.jxl", gfx::Size(10, 10),
|
|
+ {{0, {0, 1}}}, {SkColorSetARGB(255, 255, 0, 0)},
|
|
+ ImageDecoder::AlphaOption::kAlphaNotPremultiplied,
|
|
+ ColorBehavior::Tag(), 0);
|
|
+ TestPixel("/images/resources/jxl/red-10-container.jxl", gfx::Size(10, 10),
|
|
+ {{0, {1, 0}}}, {SkColorSetARGB(255, 255, 0, 0)},
|
|
+ ImageDecoder::AlphaOption::kAlphaNotPremultiplied,
|
|
+ ColorBehavior::Tag(), 0);
|
|
+ TestPixel("/images/resources/jxl/green-10-lossless.jxl", gfx::Size(10, 10),
|
|
+ {{0, {2, 3}}}, {SkColorSetARGB(255, 0, 255, 0)},
|
|
+ ImageDecoder::AlphaOption::kAlphaNotPremultiplied,
|
|
+ ColorBehavior::Tag(), 0);
|
|
+ TestPixel("/images/resources/jxl/blue-10-lossless.jxl", gfx::Size(10, 10),
|
|
+ {{0, {9, 9}}}, {SkColorSetARGB(255, 0, 0, 255)},
|
|
+ ImageDecoder::AlphaOption::kAlphaNotPremultiplied,
|
|
+ ColorBehavior::Tag(), 0);
|
|
+ TestPixel("/images/resources/jxl/alpha-lossless.jxl", gfx::Size(2, 10),
|
|
+ {{0, {0, 1}}}, {SkColorSetARGB(0, 255, 255, 255)},
|
|
+ ImageDecoder::AlphaOption::kAlphaNotPremultiplied,
|
|
+ ColorBehavior::Tag(), 0);
|
|
+ TestPixel("/images/resources/jxl/alpha-lossless.jxl", gfx::Size(2, 10),
|
|
+ {{0, {0, 1}}}, {SkColorSetARGB(0, 0, 0, 0)},
|
|
+ ImageDecoder::AlphaOption::kAlphaPremultiplied,
|
|
+ ColorBehavior::Tag(), 0);
|
|
+
|
|
+ WTF::Vector<FramePoint> coordinates_3x3 = {
|
|
+ {0, {0, 0}}, {0, {1, 0}}, {0, {2, 0}}, {0, {0, 1}}, {0, {1, 1}},
|
|
+ {0, {2, 1}}, {0, {0, 2}}, {0, {1, 2}}, {0, {2, 2}},
|
|
+ };
|
|
+
|
|
+ TestPixel("/images/resources/jxl/3x3_srgb_lossless.jxl", gfx::Size(3, 3),
|
|
+ coordinates_3x3,
|
|
+ {
|
|
+ SkColorSetARGB(255, 255, 0, 0),
|
|
+ SkColorSetARGB(255, 0, 255, 0),
|
|
+ SkColorSetARGB(255, 0, 0, 255),
|
|
+ SkColorSetARGB(255, 128, 64, 64),
|
|
+ SkColorSetARGB(255, 64, 128, 64),
|
|
+ SkColorSetARGB(255, 64, 64, 128),
|
|
+ SkColorSetARGB(255, 255, 255, 255),
|
|
+ SkColorSetARGB(255, 128, 128, 128),
|
|
+ SkColorSetARGB(255, 0, 0, 0),
|
|
+ },
|
|
+ ImageDecoder::AlphaOption::kAlphaPremultiplied,
|
|
+ ColorBehavior::TransformToSRGB(), 0);
|
|
+
|
|
+ TestPixel("/images/resources/jxl/3x3_srgb_lossy.jxl", gfx::Size(3, 3),
|
|
+ coordinates_3x3,
|
|
+ {
|
|
+ SkColorSetARGB(255, 255, 0, 0),
|
|
+ SkColorSetARGB(255, 0, 255, 0),
|
|
+ SkColorSetARGB(255, 0, 0, 255),
|
|
+ SkColorSetARGB(255, 128, 64, 64),
|
|
+ SkColorSetARGB(255, 64, 128, 64),
|
|
+ SkColorSetARGB(255, 64, 64, 128),
|
|
+ SkColorSetARGB(255, 255, 255, 255),
|
|
+ SkColorSetARGB(255, 128, 128, 128),
|
|
+ SkColorSetARGB(255, 0, 0, 0),
|
|
+ },
|
|
+ ImageDecoder::AlphaOption::kAlphaPremultiplied,
|
|
+ ColorBehavior::TransformToSRGB(), 15);
|
|
+
|
|
+ TestPixel("/images/resources/jxl/3x3a_srgb_lossless.jxl", gfx::Size(3, 3),
|
|
+ coordinates_3x3,
|
|
+ {
|
|
+ SkColorSetARGB(128, 255, 0, 0),
|
|
+ SkColorSetARGB(128, 0, 255, 0),
|
|
+ SkColorSetARGB(128, 0, 0, 255),
|
|
+ SkColorSetARGB(128, 128, 64, 64),
|
|
+ SkColorSetARGB(128, 64, 128, 64),
|
|
+ SkColorSetARGB(128, 64, 64, 128),
|
|
+ SkColorSetARGB(128, 255, 255, 255),
|
|
+ SkColorSetARGB(128, 128, 128, 128),
|
|
+ SkColorSetARGB(128, 0, 0, 0),
|
|
+ },
|
|
+ ImageDecoder::AlphaOption::kAlphaPremultiplied,
|
|
+ ColorBehavior::TransformToSRGB(), 0);
|
|
+
|
|
+ TestPixel("/images/resources/jxl/3x3a_srgb_lossy.jxl", gfx::Size(3, 3),
|
|
+ coordinates_3x3,
|
|
+ {
|
|
+ SkColorSetARGB(128, 255, 0, 0),
|
|
+ SkColorSetARGB(128, 0, 255, 0),
|
|
+ SkColorSetARGB(128, 0, 0, 255),
|
|
+ SkColorSetARGB(128, 128, 64, 64),
|
|
+ SkColorSetARGB(128, 64, 128, 64),
|
|
+ SkColorSetARGB(128, 64, 64, 128),
|
|
+ SkColorSetARGB(128, 255, 255, 255),
|
|
+ SkColorSetARGB(128, 128, 128, 128),
|
|
+ SkColorSetARGB(128, 0, 0, 0),
|
|
+ },
|
|
+ ImageDecoder::AlphaOption::kAlphaPremultiplied,
|
|
+ ColorBehavior::TransformToSRGB(), 15);
|
|
+
|
|
+ // Lossless, but allow some inaccuracy due to the color profile conversion.
|
|
+ TestPixel("/images/resources/jxl/3x3_gbr_lossless.jxl", gfx::Size(3, 3),
|
|
+ coordinates_3x3,
|
|
+ {
|
|
+ SkColorSetARGB(255, 0, 255, 0),
|
|
+ SkColorSetARGB(255, 0, 0, 255),
|
|
+ SkColorSetARGB(255, 255, 0, 0),
|
|
+ SkColorSetARGB(255, 64, 128, 64),
|
|
+ SkColorSetARGB(255, 64, 64, 128),
|
|
+ SkColorSetARGB(255, 128, 64, 64),
|
|
+ SkColorSetARGB(255, 255, 255, 255),
|
|
+ SkColorSetARGB(255, 128, 128, 128),
|
|
+ SkColorSetARGB(255, 0, 0, 0),
|
|
+ },
|
|
+ ImageDecoder::AlphaOption::kAlphaPremultiplied,
|
|
+ ColorBehavior::TransformToSRGB(), 3);
|
|
+
|
|
+ TestPixel("/images/resources/jxl/3x3_gbr_lossy.jxl", gfx::Size(3, 3),
|
|
+ coordinates_3x3,
|
|
+ {
|
|
+ SkColorSetARGB(255, 0, 255, 0),
|
|
+ SkColorSetARGB(255, 0, 0, 255),
|
|
+ SkColorSetARGB(255, 255, 0, 0),
|
|
+ SkColorSetARGB(255, 64, 128, 64),
|
|
+ SkColorSetARGB(255, 64, 64, 128),
|
|
+ SkColorSetARGB(255, 128, 64, 64),
|
|
+ SkColorSetARGB(255, 255, 255, 255),
|
|
+ SkColorSetARGB(255, 128, 128, 128),
|
|
+ SkColorSetARGB(255, 0, 0, 0),
|
|
+ },
|
|
+ ImageDecoder::AlphaOption::kAlphaPremultiplied,
|
|
+ ColorBehavior::TransformToSRGB(), 35);
|
|
+
|
|
+ // Lossless, but allow some inaccuracy due to the color profile conversion.
|
|
+ TestPixel("/images/resources/jxl/3x3a_gbr_lossless.jxl", gfx::Size(3, 3),
|
|
+ coordinates_3x3,
|
|
+ {
|
|
+ SkColorSetARGB(128, 0, 255, 0),
|
|
+ SkColorSetARGB(128, 0, 0, 255),
|
|
+ SkColorSetARGB(128, 255, 0, 0),
|
|
+ SkColorSetARGB(128, 64, 128, 64),
|
|
+ SkColorSetARGB(128, 64, 64, 128),
|
|
+ SkColorSetARGB(128, 128, 64, 64),
|
|
+ SkColorSetARGB(128, 255, 255, 255),
|
|
+ SkColorSetARGB(128, 128, 128, 128),
|
|
+ SkColorSetARGB(128, 0, 0, 0),
|
|
+ },
|
|
+ ImageDecoder::AlphaOption::kAlphaPremultiplied,
|
|
+ ColorBehavior::TransformToSRGB(), 3);
|
|
+
|
|
+ TestPixel("/images/resources/jxl/3x3a_gbr_lossy.jxl", gfx::Size(3, 3),
|
|
+ coordinates_3x3,
|
|
+ {
|
|
+ SkColorSetARGB(128, 0, 255, 0),
|
|
+ SkColorSetARGB(128, 0, 0, 255),
|
|
+ SkColorSetARGB(128, 255, 0, 0),
|
|
+ SkColorSetARGB(128, 64, 128, 64),
|
|
+ SkColorSetARGB(128, 64, 64, 128),
|
|
+ SkColorSetARGB(128, 128, 64, 64),
|
|
+ SkColorSetARGB(128, 255, 255, 255),
|
|
+ SkColorSetARGB(128, 128, 128, 128),
|
|
+ SkColorSetARGB(128, 0, 0, 0),
|
|
+ },
|
|
+ ImageDecoder::AlphaOption::kAlphaPremultiplied,
|
|
+ ColorBehavior::TransformToSRGB(), 35);
|
|
+
|
|
+ // Lossless, but allow some inaccuracy due to the color profile conversion.
|
|
+ TestPixel("/images/resources/jxl/3x3_pq_lossless.jxl", gfx::Size(3, 3),
|
|
+ coordinates_3x3,
|
|
+ {
|
|
+ SkColorSetARGB(255, 255, 0, 0),
|
|
+ SkColorSetARGB(255, 0, 255, 0),
|
|
+ SkColorSetARGB(255, 0, 0, 255),
|
|
+ SkColorSetARGB(255, 128, 64, 64),
|
|
+ SkColorSetARGB(255, 64, 128, 64),
|
|
+ SkColorSetARGB(255, 64, 64, 128),
|
|
+ SkColorSetARGB(255, 255, 255, 255),
|
|
+ SkColorSetARGB(255, 128, 128, 128),
|
|
+ SkColorSetARGB(255, 0, 0, 0),
|
|
+ },
|
|
+ ImageDecoder::AlphaOption::kAlphaPremultiplied,
|
|
+ ColorBehavior::Tag(), 2);
|
|
+
|
|
+ TestPixel("/images/resources/jxl/3x3_pq_lossy.jxl", gfx::Size(3, 3),
|
|
+ coordinates_3x3,
|
|
+ {
|
|
+ SkColorSetARGB(255, 255, 0, 0),
|
|
+ SkColorSetARGB(255, 64, 255, 64),
|
|
+ SkColorSetARGB(255, 39, 76, 255),
|
|
+ SkColorSetARGB(255, 128, 64, 64),
|
|
+ SkColorSetARGB(255, 64, 128, 64),
|
|
+ SkColorSetARGB(255, 64, 64, 128),
|
|
+ SkColorSetARGB(255, 255, 255, 255),
|
|
+ SkColorSetARGB(255, 128, 128, 128),
|
|
+ SkColorSetARGB(255, 0, 0, 0),
|
|
+ },
|
|
+ ImageDecoder::AlphaOption::kAlphaPremultiplied,
|
|
+ ColorBehavior::Tag(), 15);
|
|
+
|
|
+ TestPixel("/images/resources/jxl/3x3a_pq_lossless.jxl", gfx::Size(3, 3),
|
|
+ coordinates_3x3,
|
|
+ {
|
|
+ SkColorSetARGB(128, 255, 0, 0),
|
|
+ SkColorSetARGB(128, 0, 255, 0),
|
|
+ SkColorSetARGB(128, 0, 0, 255),
|
|
+ SkColorSetARGB(128, 128, 64, 64),
|
|
+ SkColorSetARGB(128, 64, 128, 64),
|
|
+ SkColorSetARGB(128, 64, 64, 128),
|
|
+ SkColorSetARGB(128, 255, 255, 255),
|
|
+ SkColorSetARGB(128, 128, 128, 128),
|
|
+ SkColorSetARGB(128, 0, 0, 0),
|
|
+ },
|
|
+ ImageDecoder::AlphaOption::kAlphaPremultiplied,
|
|
+ ColorBehavior::Tag(), 2);
|
|
+
|
|
+ TestPixel("/images/resources/jxl/3x3a_pq_lossy.jxl", gfx::Size(3, 3),
|
|
+ coordinates_3x3,
|
|
+ {
|
|
+ SkColorSetARGB(128, 255, 0, 0),
|
|
+ SkColorSetARGB(128, 64, 255, 64),
|
|
+ SkColorSetARGB(128, 40, 82, 255),
|
|
+ SkColorSetARGB(128, 128, 64, 64),
|
|
+ SkColorSetARGB(128, 64, 128, 64),
|
|
+ SkColorSetARGB(128, 64, 64, 128),
|
|
+ SkColorSetARGB(128, 255, 255, 255),
|
|
+ SkColorSetARGB(128, 128, 128, 128),
|
|
+ SkColorSetARGB(128, 0, 0, 0),
|
|
+ },
|
|
+ ImageDecoder::AlphaOption::kAlphaPremultiplied,
|
|
+ ColorBehavior::Tag(), 15);
|
|
+
|
|
+ TestPixel("/images/resources/jxl/3x3_hlg_lossless.jxl", gfx::Size(3, 3),
|
|
+ coordinates_3x3,
|
|
+ {
|
|
+ SkColorSetARGB(255, 255, 0, 0),
|
|
+ SkColorSetARGB(255, 0, 255, 0),
|
|
+ SkColorSetARGB(255, 0, 0, 255),
|
|
+ SkColorSetARGB(255, 86, 46, 46),
|
|
+ SkColorSetARGB(255, 46, 86, 46),
|
|
+ SkColorSetARGB(255, 46, 46, 86),
|
|
+ SkColorSetARGB(255, 255, 255, 255),
|
|
+ SkColorSetARGB(255, 85, 85, 85),
|
|
+ SkColorSetARGB(255, 0, 0, 0),
|
|
+ },
|
|
+ ImageDecoder::AlphaOption::kAlphaPremultiplied,
|
|
+ ColorBehavior::Tag(), 2);
|
|
+
|
|
+ TestPixel("/images/resources/jxl/3x3_hlg_lossy.jxl", gfx::Size(3, 3),
|
|
+ coordinates_3x3,
|
|
+ {
|
|
+ SkColorSetARGB(255, 255, 13, 13),
|
|
+ SkColorSetARGB(255, 13, 255, 13),
|
|
+ SkColorSetARGB(255, 13, 13, 255),
|
|
+ SkColorSetARGB(255, 128, 64, 64),
|
|
+ SkColorSetARGB(255, 64, 128, 64),
|
|
+ SkColorSetARGB(255, 64, 64, 128),
|
|
+ SkColorSetARGB(255, 255, 255, 255),
|
|
+ SkColorSetARGB(255, 128, 128, 128),
|
|
+ SkColorSetARGB(255, 0, 0, 0),
|
|
+ },
|
|
+ ImageDecoder::AlphaOption::kAlphaPremultiplied,
|
|
+ ColorBehavior::Tag(), 15);
|
|
+
|
|
+ TestPixel("/images/resources/jxl/3x3a_hlg_lossless.jxl", gfx::Size(3, 3),
|
|
+ coordinates_3x3,
|
|
+ {
|
|
+ SkColorSetARGB(128, 255, 0, 0),
|
|
+ SkColorSetARGB(128, 0, 255, 0),
|
|
+ SkColorSetARGB(128, 0, 0, 255),
|
|
+ SkColorSetARGB(128, 86, 46, 46),
|
|
+ SkColorSetARGB(128, 46, 86, 46),
|
|
+ SkColorSetARGB(128, 46, 46, 86),
|
|
+ SkColorSetARGB(128, 255, 255, 255),
|
|
+ SkColorSetARGB(128, 85, 85, 85),
|
|
+ SkColorSetARGB(128, 0, 0, 0),
|
|
+ },
|
|
+ ImageDecoder::AlphaOption::kAlphaPremultiplied,
|
|
+ ColorBehavior::Tag(), 6);
|
|
+
|
|
+ TestPixel("/images/resources/jxl/3x3a_hlg_lossy.jxl", gfx::Size(3, 3),
|
|
+ coordinates_3x3,
|
|
+ {
|
|
+ SkColorSetARGB(128, 255, 13, 13),
|
|
+ SkColorSetARGB(128, 13, 255, 13),
|
|
+ SkColorSetARGB(128, 13, 13, 255),
|
|
+ SkColorSetARGB(128, 128, 64, 64),
|
|
+ SkColorSetARGB(128, 64, 128, 64),
|
|
+ SkColorSetARGB(128, 74, 64, 128),
|
|
+ SkColorSetARGB(128, 255, 255, 255),
|
|
+ SkColorSetARGB(128, 128, 128, 128),
|
|
+ SkColorSetARGB(128, 0, 0, 0),
|
|
+ },
|
|
+ ImageDecoder::AlphaOption::kAlphaPremultiplied,
|
|
+ ColorBehavior::Tag(), 15);
|
|
+}
|
|
+
|
|
+TEST(JXLTests, ColorProfileTest) {
|
|
+ TestColorProfile("/images/resources/jxl/icc-v2-gbr.jxl", ColorBehavior::Tag(),
|
|
+ SkColorSetARGB(255, 0xaf, 0xfe, 0x6b));
|
|
+ TestColorProfile("/images/resources/jxl/icc-v2-gbr.jxl",
|
|
+ ColorBehavior::TransformToSRGB(),
|
|
+ SkColorSetARGB(255, 0x6b, 0xb1, 0xfe));
|
|
+ TestColorProfile("/images/resources/jxl/icc-v2-gbr.jxl",
|
|
+ ColorBehavior::Ignore(),
|
|
+ SkColorSetARGB(255, 0xaf, 0xfe, 0x6b));
|
|
+}
|
|
+
|
|
+TEST(JXLTests, AnimatedPixelTest) {
|
|
+ TestPixel(
|
|
+ "/images/resources/jxl/animated.jxl", gfx::Size(16, 16),
|
|
+ {{0, {0, 0}}, {1, {0, 0}}},
|
|
+ {SkColorSetARGB(255, 204, 0, 153), SkColorSetARGB(255, 0, 102, 102)},
|
|
+ ImageDecoder::AlphaOption::kAlphaNotPremultiplied, ColorBehavior::Tag(),
|
|
+ 0, 2);
|
|
+}
|
|
+
|
|
+TEST(JXLTests, JXLHDRTest) {
|
|
+ // PQ tests
|
|
+ // PQ values, as expected
|
|
+ TestHDR("/images/resources/jxl/pq_gradient_lossy.jxl",
|
|
+ ColorBehavior::Ignore(), false, 0.58039218187332153,
|
|
+ 0.73333334922790527, 0.43921568989753723, 1);
|
|
+ // sRGB as expected, but not an exact match
|
|
+ TestHDR("/images/resources/jxl/pq_gradient_lossy.jxl",
|
|
+ ColorBehavior::TransformToSRGB(), true, -0.9248046875, 1.943359375,
|
|
+ -0.4443359375, 1);
|
|
+
|
|
+ // linear sRGB as expected.
|
|
+ TestHDR("/images/resources/jxl/pq_gradient_lossy.jxl", ColorBehavior::Tag(),
|
|
+ true, 0.58039218187332153, 0.73333334922790527, 0.43921568989753723,
|
|
+ 1);
|
|
+
|
|
+ // correct, original PQ values
|
|
+ TestHDR("/images/resources/jxl/pq_gradient_lossless.jxl",
|
|
+ ColorBehavior::Ignore(), false, 0.58039218187332153,
|
|
+ 0.73725491762161255, 0.45098039507865906, 1);
|
|
+ TestHDR("/images/resources/jxl/pq_gradient_lossless.jxl",
|
|
+ ColorBehavior::TransformToSRGB(), true, -0.95751953125, 1.9677734375,
|
|
+ -0.416748046875, 1);
|
|
+ // correct, original PQ values
|
|
+ TestHDR("/images/resources/jxl/pq_gradient_lossless.jxl",
|
|
+ ColorBehavior::Tag(), true, 0.58056640625, 0.7373046875,
|
|
+ 0.450927734375, 1);
|
|
+
|
|
+ // with ICC
|
|
+ // clipped linear sRGB, as expected from current JXL implementation
|
|
+ TestHDR("/images/resources/jxl/pq_gradient_icc_lossy.jxl",
|
|
+ ColorBehavior::Ignore(), false, 0, 0.0930381, 0, 1);
|
|
+
|
|
+ TestHDR("/images/resources/jxl/pq_gradient_icc_lossy.jxl",
|
|
+ ColorBehavior::TransformToSRGB(), false, 0, 0.338623046875, 0, 1);
|
|
+ TestHDR("/images/resources/jxl/pq_gradient_icc_lossy.jxl",
|
|
+ ColorBehavior::Tag(), false, 0, 0.0930381, 0, 1);
|
|
+
|
|
+ TestHDR("/images/resources/jxl/pq_gradient_icc_lossless.jxl",
|
|
+ ColorBehavior::Ignore(), false, 0.58039218187332153,
|
|
+ 0.73725491762161255, 0.45098039507865906, 1);
|
|
+ TestHDR("/images/resources/jxl/pq_gradient_icc_lossless.jxl",
|
|
+ ColorBehavior::TransformToSRGB(), true, -0.95751953125, 1.9677734375,
|
|
+ -0.416748046875, 1);
|
|
+ TestHDR("/images/resources/jxl/pq_gradient_icc_lossless.jxl",
|
|
+ ColorBehavior::Tag(), true, 0.58039218187332153, 0.73725491762161255,
|
|
+ 0.45098039507865906, 1);
|
|
+}
|
|
+
|
|
+TEST(JXLTests, RandomFrameDecode) {
|
|
+ TestRandomFrameDecode(&CreateJXLDecoder, "/images/resources/jxl/count.jxl");
|
|
+}
|
|
+
|
|
+TEST(JXLTests, RandomDecodeAfterClearFrameBufferCache) {
|
|
+ TestRandomDecodeAfterClearFrameBufferCache(&CreateJXLDecoder,
|
|
+ "/images/resources/jxl/count.jxl");
|
|
+}
|
|
+
|
|
+} // namespace
|
|
+} // namespace blink
|
|
diff --git a/third_party/blink/tools/commit_stats/git-dirs.txt b/third_party/blink/tools/commit_stats/git-dirs.txt
|
|
--- a/third_party/blink/tools/commit_stats/git-dirs.txt
|
|
+++ b/third_party/blink/tools/commit_stats/git-dirs.txt
|
|
@@ -75,6 +75,7 @@
|
|
./third_party/angle/third_party/glmark2/src,ANGLE
|
|
./third_party/openh264/src,OpenH264
|
|
./third_party/googletest/src,GoogleTest
|
|
+./third_party/libjxl/src,libjxl
|
|
./third_party/highway/src,highway
|
|
./third_party/wuffs/src,wuffs
|
|
./third_party/catapult,catapult
|
|
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
|
|
--- a/third_party/blink/web_tests/TestExpectations
|
|
+++ b/third_party/blink/web_tests/TestExpectations
|
|
@@ -4781,6 +4781,12 @@ crbug.com/1199522 http/tests/devtools/layers/layers-3d-view-hit-testing.js [ Fai
|
|
# Started failing after rolling new version of check-layout-th.js
|
|
css3/flexbox/perpendicular-writing-modes-inside-flex-item.html [ Failure ]
|
|
|
|
+# JXL tests fail due to rounding error differences.
|
|
+# TODO(https://crbug.com/1274220): Rebaseline all images once new tone mapping
|
|
+# lands.
|
|
+crbug.com/1210658 virtual/jxl-enabled/images/jxl/jxl-images.html [ Crash Failure Pass Timeout ]
|
|
+crbug.com/1358616 virtual/jxl-enabled/images/jxl/progressive.html [ Crash Failure Pass Timeout ]
|
|
+
|
|
# Temporarily disabled to unblock https://crrev.com/c/3099011
|
|
crbug.com/1199701 http/tests/devtools/console/console-big-array.js [ Crash Failure Pass Timeout ]
|
|
|
|
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
|
|
--- a/third_party/blink/web_tests/VirtualTestSuites
|
|
+++ b/third_party/blink/web_tests/VirtualTestSuites
|
|
@@ -447,6 +447,15 @@
|
|
"--disable-threaded-compositing", "--disable-threaded-animation"],
|
|
"expires": "Jul 1, 2023"
|
|
},
|
|
+ {
|
|
+ "prefix": "jxl-enabled",
|
|
+ "platforms": ["Linux", "Mac", "Win"],
|
|
+ "bases": ["http/tests/inspector-protocol/emulation/emulation-set-disabled-image-types-jxl.js",
|
|
+ "images/jxl"],
|
|
+ "exclusive_tests": ["http/tests/inspector-protocol/emulation/emulation-set-disabled-image-types-jxl.js"],
|
|
+ "args": ["--enable-features=JXL"],
|
|
+ "expires": "Jul 1, 2023"
|
|
+ },
|
|
{
|
|
"prefix": "scalefactor150",
|
|
"platforms": ["Linux", "Win"],
|
|
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/emulation/emulation-set-disabled-image-types-jxl-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/emulation/emulation-set-disabled-image-types-jxl-expected.txt
|
|
new file mode 100644
|
|
--- /dev/null
|
|
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/emulation/emulation-set-disabled-image-types-jxl-expected.txt
|
|
@@ -0,0 +1,13 @@
|
|
+Tests the Emulation.setDisabledImageTypes method for JPEG XL.
|
|
+With emulation (jxl enabled):
|
|
+Expected jxl image: http://127.0.0.1:8000/inspector-protocol/emulation/resources/test.jxl
|
|
+Image request Accept header: image/jxl,image/avif,image/apng,image/svg+xml,image/*,*/*;q=0.8
|
|
+With emulation (jxl disabled):
|
|
+Expected png image: http://127.0.0.1:8000/inspector-protocol/emulation/resources/test.png
|
|
+Image request Accept header: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
|
|
+With emulation (jxl enabled):
|
|
+Expected jxl image: http://127.0.0.1:8000/inspector-protocol/emulation/resources/test.jxl
|
|
+Image request Accept header: image/jxl,image/avif,image/apng,image/svg+xml,image/*,*/*;q=0.8
|
|
+With emulation (jxl disabled):
|
|
+Expected png image: http://127.0.0.1:8000/inspector-protocol/emulation/resources/test.png
|
|
+Image request Accept header: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
|
|
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/emulation/emulation-set-disabled-image-types-jxl.js b/third_party/blink/web_tests/http/tests/inspector-protocol/emulation/emulation-set-disabled-image-types-jxl.js
|
|
new file mode 100644
|
|
--- /dev/null
|
|
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/emulation/emulation-set-disabled-image-types-jxl.js
|
|
@@ -0,0 +1,50 @@
|
|
+(async function(testRunner) {
|
|
+ const {page, session, dp} =
|
|
+ await testRunner.startBlank('Tests the Emulation.setDisabledImageTypes method for JPEG XL.');
|
|
+
|
|
+ await dp.Page.enable();
|
|
+ await dp.Network.enable();
|
|
+
|
|
+ let requestEvents = [];
|
|
+ dp.Network.onRequestWillBeSent(event => requestEvents.push(event));
|
|
+
|
|
+ await dp.Emulation.setDisabledImageTypes({ imageTypes: ['webp'] });
|
|
+
|
|
+ testRunner.log('With emulation (jxl enabled):');
|
|
+ await page.navigate(testRunner.url('resources/image-jxl-fallback-img.html'));
|
|
+ testRunner.log('Expected jxl image: ' + await session.evaluate(() => document.querySelector('img').currentSrc));
|
|
+ let jxlRequest = requestEvents.map(event => event.params.request).find(request => request.url.endsWith('test.jxl'));
|
|
+ testRunner.log('Image request Accept header: ' + jxlRequest.headers.Accept);
|
|
+
|
|
+ requestEvents = [];
|
|
+
|
|
+ testRunner.log('With emulation (jxl disabled):');
|
|
+ await dp.Emulation.setDisabledImageTypes({ imageTypes: ['jxl'] });
|
|
+ dp.Page.reload({ ignoreCache: true });
|
|
+ await dp.Page.onceLoadEventFired();
|
|
+ testRunner.log('Expected png image: ' + await session.evaluate(() => document.querySelector('img').currentSrc));
|
|
+ let pngRequest = requestEvents.map(event => event.params.request).find(request => request.url.endsWith('test.png'));
|
|
+ testRunner.log('Image request Accept header: ' + pngRequest.headers.Accept);
|
|
+
|
|
+ requestEvents = [];
|
|
+
|
|
+ await dp.Emulation.setDisabledImageTypes({ imageTypes: ['webp'] });
|
|
+
|
|
+ testRunner.log('With emulation (jxl enabled):');
|
|
+ await page.navigate(testRunner.url('resources/image-jxl-fallback-picture.html'));
|
|
+ testRunner.log('Expected jxl image: ' + await session.evaluate(() => document.querySelector('img').currentSrc));
|
|
+ jxlRequest = requestEvents.map(event => event.params.request).find(request => request.url.endsWith('test.jxl'));
|
|
+ testRunner.log('Image request Accept header: ' + jxlRequest.headers.Accept);
|
|
+
|
|
+ requestEvents = [];
|
|
+
|
|
+ testRunner.log('With emulation (jxl disabled):');
|
|
+ await dp.Emulation.setDisabledImageTypes({ imageTypes: ['jxl'] });
|
|
+ dp.Page.reload({ ignoreCache: true });
|
|
+ await dp.Page.onceLoadEventFired();
|
|
+ testRunner.log('Expected png image: ' + await session.evaluate(() => document.querySelector('img').currentSrc));
|
|
+ pngRequest = requestEvents.map(event => event.params.request).find(request => request.url.endsWith('test.png'));
|
|
+ testRunner.log('Image request Accept header: ' + pngRequest.headers.Accept);
|
|
+
|
|
+ testRunner.completeTest();
|
|
+})
|
|
\ No newline at end of file
|
|
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/emulation/resources/image-jxl-fallback-img.html b/third_party/blink/web_tests/http/tests/inspector-protocol/emulation/resources/image-jxl-fallback-img.html
|
|
new file mode 100644
|
|
--- /dev/null
|
|
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/emulation/resources/image-jxl-fallback-img.html
|
|
@@ -0,0 +1 @@
|
|
+<img src="test.jxl" onerror="src='test.png'">
|
|
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/emulation/resources/image-jxl-fallback-picture.html b/third_party/blink/web_tests/http/tests/inspector-protocol/emulation/resources/image-jxl-fallback-picture.html
|
|
new file mode 100644
|
|
--- /dev/null
|
|
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/emulation/resources/image-jxl-fallback-picture.html
|
|
@@ -0,0 +1,4 @@
|
|
+<picture>
|
|
+ <source srcset="test.jxl" type="image/jxl">
|
|
+ <img src="test.png">
|
|
+</picture>
|
|
diff --git a/third_party/blink/web_tests/images/jxl/jxl-images.html b/third_party/blink/web_tests/images/jxl/jxl-images.html
|
|
new file mode 100644
|
|
--- /dev/null
|
|
+++ b/third_party/blink/web_tests/images/jxl/jxl-images.html
|
|
@@ -0,0 +1,22 @@
|
|
+<img src="../resources/jxl/3x3a_gbr_lossless.jxl">
|
|
+<img src="../resources/jxl/3x3a_gbr_lossy.jxl">
|
|
+<img src="../resources/jxl/3x3a_pq_lossless.jxl">
|
|
+<img src="../resources/jxl/3x3a_pq_lossy.jxl">
|
|
+<img src="../resources/jxl/3x3a_srgb_lossless.jxl">
|
|
+<img src="../resources/jxl/3x3a_srgb_lossy.jxl">
|
|
+<img src="../resources/jxl/3x3_gbr_lossless.jxl">
|
|
+<img src="../resources/jxl/3x3_gbr_lossy.jxl">
|
|
+<img src="../resources/jxl/3x3_pq_lossless.jxl">
|
|
+<img src="../resources/jxl/3x3_pq_lossy.jxl">
|
|
+<img src="../resources/jxl/3x3_srgb_lossless.jxl">
|
|
+<img src="../resources/jxl/3x3_srgb_lossy.jxl">
|
|
+<img src="../resources/jxl/alpha-large-dice.jxl">
|
|
+<img src="../resources/jxl/alpha-lossless.jxl">
|
|
+<img src="../resources/jxl/blue-10-lossless.jxl">
|
|
+<img src="../resources/jxl/green-10-lossless.jxl">
|
|
+<img src="../resources/jxl/icc-grb2.jxl">
|
|
+<img src="../resources/jxl/icc-grb.jxl">
|
|
+<img src="../resources/jxl/icc-v2-gbr.jxl">
|
|
+<img src="../resources/jxl/red-10-container.jxl">
|
|
+<img src="../resources/jxl/red-10-default.jxl">
|
|
+<img src="../resources/jxl/red-10-lossless.jxl">
|
|
diff --git a/third_party/blink/web_tests/images/jxl/progressive.html b/third_party/blink/web_tests/images/jxl/progressive.html
|
|
new file mode 100644
|
|
--- /dev/null
|
|
+++ b/third_party/blink/web_tests/images/jxl/progressive.html
|
|
@@ -0,0 +1,6 @@
|
|
+<!DOCTYPE html>
|
|
+<html>
|
|
+<body>
|
|
+<img src="../resources/jxl/partial_black.jxl">
|
|
+</body>
|
|
+</html>
|
|
diff --git a/third_party/blink/web_tests/images/resources/jxl/README.md b/third_party/blink/web_tests/images/resources/jxl/README.md
|
|
new file mode 100644
|
|
--- /dev/null
|
|
+++ b/third_party/blink/web_tests/images/resources/jxl/README.md
|
|
@@ -0,0 +1,79 @@
|
|
+# JPEG XL Test files
|
|
+
|
|
+## How to generate the test set
|
|
+
|
|
+We assume to have a the following images (from
|
|
+`third_party/blink/web_tests/images/resources/`) available:
|
|
+```
|
|
+red-10.png
|
|
+green-10.png
|
|
+blue-10.png
|
|
+png_per_row_alpha.png
|
|
+icc-v2-gbr.jpg
|
|
+dice.png
|
|
+animated.gif
|
|
+jxl/3x3.png
|
|
+jxl/3x3a.png
|
|
+```
|
|
+Then we run:
|
|
+```
|
|
+cjxl red-10.png red-10-default.jxl
|
|
+cjxl --container red-10.png red-10-container.jxl
|
|
+cjxl -d 0 red-10.png red-10-lossless.jxl
|
|
+cjxl -d 0 green-10.png green-10-lossless.jxl
|
|
+cjxl -d 0 blue-10.png blue-10-lossless.jxl
|
|
+cjxl -d 0 png_per_row_alpha.png alpha-lossless.jxl
|
|
+cjxl icc-v2-gbr.jpg icc-v2-gbr.jxl
|
|
+cjxl -d 0 dice.png alpha-large-dice.jxl
|
|
+
|
|
+cjxl 3x3.png temp.jxl -d 0
|
|
+djxl temp.jxl 3x3_srgb.png
|
|
+cjxl 3x3_srgb.png 3x3_srgb_lossy.jxl -d 0.1 -e 7
|
|
+cjxl 3x3_srgb.png 3x3_srgb_lossless.jxl -d 0
|
|
+
|
|
+cjxl 3x3a.png temp.jxl -d 0
|
|
+djxl temp.jxl 3x3a_srgb.png
|
|
+cjxl 3x3a_srgb.png 3x3a_srgb_lossy.jxl -d 0.1 -e 7
|
|
+cjxl 3x3a_srgb.png 3x3a_srgb_lossless.jxl -d 0
|
|
+
|
|
+cjxl 3x3.png temp.jxl -x color_space=RGB_D65_202_Rel_PeQ -d 0
|
|
+djxl temp.jxl 3x3_pq.png
|
|
+cjxl 3x3_pq.png 3x3_pq_lossy.jxl -d 0.1 -e 7
|
|
+cjxl 3x3_pq.png 3x3_pq_lossless.jxl -d 0
|
|
+
|
|
+cjxl 3x3a.png temp.jxl -x color_space=RGB_D65_202_Rel_PeQ -d 0
|
|
+djxl temp.jxl 3x3a_pq.png
|
|
+cjxl 3x3a_pq.png 3x3a_pq_lossy.jxl -d 0.1 -e 7
|
|
+cjxl 3x3a_pq.png 3x3a_pq_lossless.jxl -d 0
|
|
+
|
|
+cjxl 3x3.png temp.jxl -x color_space=RGB_D65_202_Rel_HLG -d 0
|
|
+djxl temp.jxl 3x3_hlg.png
|
|
+cjxl 3x3_hlg.png 3x3_hlg_lossy.jxl -d 0.1 -e 7
|
|
+cjxl 3x3_hlg.png 3x3_hlg_lossless.jxl -d 0
|
|
+
|
|
+cjxl 3x3a.png temp.jxl -x color_space=RGB_D65_202_Rel_HLG -d 0
|
|
+djxl temp.jxl 3x3a_hlg.png
|
|
+cjxl 3x3a_hlg.png 3x3a_hlg_lossy.jxl -d 0.1 -e 7
|
|
+cjxl 3x3a_hlg.png 3x3a_hlg_lossless.jxl -d 0
|
|
+
|
|
+convert icc-v2-gbr.jpg icc-v2-gbr.icc
|
|
+cjxl 3x3.png temp.jxl -x icc_pathname=icc-v2-gbr.icc -d 0
|
|
+djxl temp.jxl 3x3_gbr.png
|
|
+cjxl 3x3_gbr.png 3x3_gbr_lossy.jxl -d 0.1 -e 7
|
|
+cjxl 3x3_gbr.png 3x3_gbr_lossless.jxl -d 0
|
|
+
|
|
+cjxl 3x3a.png temp.jxl -x icc_pathname=icc-v2-gbr.icc -d 0
|
|
+djxl temp.jxl 3x3a_gbr.png
|
|
+cjxl 3x3a_gbr.png 3x3a_gbr_lossy.jxl -d 0.1 -e 7
|
|
+cjxl 3x3a_gbr.png 3x3a_gbr_lossless.jxl -d 0
|
|
+
|
|
+cjxl animated.gif animated.jxl
|
|
+
|
|
+for i in $(seq 0 9); do J=$(printf '%03d' $i); convert -fill black -size 500x500 -font 'Courier' -pointsize 72 -gravity center label:$J $J.png; done
|
|
+convert -delay 20 *.png count.gif
|
|
+cjxl count.gif count.jxl
|
|
+
|
|
+convert -size 680x420 xc:black black.png
|
|
+cjxl --group_order 1 -d 0 black.png black.jxl
|
|
+dd bs=1 count=46 if=black.jxl of=partial_black.jxl
|
|
+```
|
|
diff --git a/third_party/blink/web_tests/virtual/jxl-enabled/README.md b/third_party/blink/web_tests/virtual/jxl-enabled/README.md
|
|
new file mode 100644
|
|
--- /dev/null
|
|
+++ b/third_party/blink/web_tests/virtual/jxl-enabled/README.md
|
|
@@ -0,0 +1,5 @@
|
|
+This suite runs the tests with
|
|
+--enable-features=JXL
|
|
+
|
|
+See the issue for more details:
|
|
+https://crbug.com/1178058
|
|
diff --git a/third_party/libjxl/BUILD.gn b/third_party/libjxl/BUILD.gn
|
|
new file mode 100644
|
|
--- /dev/null
|
|
+++ b/third_party/libjxl/BUILD.gn
|
|
@@ -0,0 +1,79 @@
|
|
+# Copyright 2020 The Chromium Authors
|
|
+# Use of this source code is governed by a BSD-style license that can be
|
|
+# found in the LICENSE file.
|
|
+
|
|
+# Import list of source files and flags from the jpeg-xl project.
|
|
+import("src/lib/lib.gni")
|
|
+import("//build/util/process_version.gni")
|
|
+
|
|
+# This config is applied to targets that depend on libjxl.
|
|
+config("libjxl_external_config") {
|
|
+ include_dirs = [
|
|
+ # Path to the public headers.
|
|
+ "src/lib/include",
|
|
+
|
|
+ # Configuration headers normally generated by the cmake build system.
|
|
+ "gen_headers",
|
|
+
|
|
+ "${target_gen_dir}/",
|
|
+ ]
|
|
+}
|
|
+
|
|
+source_set("libjxl") {
|
|
+ sources = rebase_path(libjxl_dec_sources, ".", "src/lib")
|
|
+
|
|
+ cflags_cc = [
|
|
+ "-Wno-shadow",
|
|
+ "-Wno-unused-function",
|
|
+ ]
|
|
+
|
|
+ defines = [
|
|
+ "JPEGXL_ENABLE_SKCMS=1",
|
|
+
|
|
+ # Disabling decode-to-JPEG bytes in the library removes about 20%
|
|
+ # of the binary size (as measured in android arm builds).
|
|
+ # Transcoding back to JPEG is not used in Chrome, only decoding to
|
|
+ # pixels is used even for files that were originally transcoded
|
|
+ # *from* JPEG.
|
|
+ "JPEGXL_ENABLE_TRANSCODE_JPEG=0",
|
|
+ ]
|
|
+
|
|
+ if (is_official_build) {
|
|
+ # Disable assertion messages, saving about 6 kB in android.
|
|
+ defines += [ "JXL_DEBUG_ON_ABORT=0" ]
|
|
+ }
|
|
+
|
|
+ include_dirs = [
|
|
+ "src",
|
|
+ "src/lib/include/",
|
|
+ "${target_gen_dir}/",
|
|
+ "//third_party/skia/include/third_party/skcms", # for "skcms.h"
|
|
+ ]
|
|
+
|
|
+ deps = [
|
|
+ ":libjxml_version",
|
|
+ "//skia:skcms",
|
|
+ "//third_party/brotli:dec",
|
|
+ "//third_party/highway:libhwy",
|
|
+ ]
|
|
+
|
|
+ public_configs = [ ":libjxl_external_config" ]
|
|
+}
|
|
+
|
|
+process_version("libjxml_version") {
|
|
+ write_file("$target_gen_dir/jxl/version", libjxl_version_defines)
|
|
+
|
|
+ template_file = "src/lib/jxl/version.h.in"
|
|
+ output = "$target_gen_dir/jxl/version.h"
|
|
+
|
|
+ sources = [ "$target_gen_dir/jxl/version" ]
|
|
+
|
|
+ extra_args = [
|
|
+ "-e",
|
|
+ "JPEGXL_MAJOR_VERSION=\"%s\"%(JPEGXL_MAJOR_VERSION)",
|
|
+ "-e",
|
|
+ "JPEGXL_MINOR_VERSION=\"%s\"%(JPEGXL_MINOR_VERSION)",
|
|
+ "-e",
|
|
+ "JPEGXL_PATCH_VERSION=\"%s\"%(JPEGXL_PATCH_VERSION)",
|
|
+ ]
|
|
+}
|
|
diff --git a/third_party/libjxl/DIR_METADATA b/third_party/libjxl/DIR_METADATA
|
|
new file mode 100644
|
|
--- /dev/null
|
|
+++ b/third_party/libjxl/DIR_METADATA
|
|
@@ -0,0 +1,4 @@
|
|
+
|
|
+monorail: {
|
|
+ component: "Internals>Images>Codecs"
|
|
+}
|
|
diff --git a/third_party/libjxl/LICENSE b/third_party/libjxl/LICENSE
|
|
new file mode 100644
|
|
--- /dev/null
|
|
+++ b/third_party/libjxl/LICENSE
|
|
@@ -0,0 +1,27 @@
|
|
+Copyright (c) the JPEG XL Project Authors.
|
|
+All rights reserved.
|
|
+
|
|
+Redistribution and use in source and binary forms, with or without
|
|
+modification, are permitted provided that the following conditions are met:
|
|
+
|
|
+1. Redistributions of source code must retain the above copyright notice, this
|
|
+ list of conditions and the following disclaimer.
|
|
+
|
|
+2. Redistributions in binary form must reproduce the above copyright notice,
|
|
+ this list of conditions and the following disclaimer in the documentation
|
|
+ and/or other materials provided with the distribution.
|
|
+
|
|
+3. Neither the name of the copyright holder nor the names of its
|
|
+ contributors may be used to endorse or promote products derived from
|
|
+ this software without specific prior written permission.
|
|
+
|
|
+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 HOLDER 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.
|
|
diff --git a/third_party/libjxl/OWNERS b/third_party/libjxl/OWNERS
|
|
new file mode 100644
|
|
--- /dev/null
|
|
+++ b/third_party/libjxl/OWNERS
|
|
@@ -0,0 +1,9 @@
|
|
+# Owners:
|
|
+noel@chromium.org
|
|
+scroggo@google.com
|
|
+
|
|
+# Reviewers:
|
|
+# eustas@chromium.org
|
|
+# firsching@google.com
|
|
+# sboukortt@google.com
|
|
+# veluca@google.com
|
|
diff --git a/third_party/libjxl/README.chromium b/third_party/libjxl/README.chromium
|
|
new file mode 100644
|
|
--- /dev/null
|
|
+++ b/third_party/libjxl/README.chromium
|
|
@@ -0,0 +1,15 @@
|
|
+Name: JPEG XL image decoder library
|
|
+Short Name: libjxl
|
|
+URL: https://github.com/libjxl/libjxl
|
|
+Version: 0.7rc
|
|
+Date: 2022-08-24
|
|
+Revision: 3e246a860ea6d510d91ceca5413dcc50e8c41dd9
|
|
+License: BSD 3-Clause
|
|
+Security Critical: yes
|
|
+CPEPrefix: cpe:/a:libjxl_project:libjxl:0.7rc
|
|
+
|
|
+Description:
|
|
+The reference implementation for the JPEG XL image encoder/decoder.
|
|
+
|
|
+Local Modifications:
|
|
+None. Only decoder-side is compiled.
|
|
diff --git a/third_party/libjxl/gen_headers/jxl/jxl_export.h b/third_party/libjxl/gen_headers/jxl/jxl_export.h
|
|
new file mode 100644
|
|
--- /dev/null
|
|
+++ b/third_party/libjxl/gen_headers/jxl/jxl_export.h
|
|
@@ -0,0 +1,11 @@
|
|
+// Copyright 2020 The Chromium Authors
|
|
+// Use of this source code is governed by a BSD-style license that can be
|
|
+// found in the LICENSE file.
|
|
+
|
|
+#ifndef THIRD_PARTY_LIBJXL_GEN_HEADERS_JXL_JXL_EXPORT_H_
|
|
+#define THIRD_PARTY_LIBJXL_GEN_HEADERS_JXL_JXL_EXPORT_H_
|
|
+
|
|
+#define JXL_EXPORT
|
|
+#define JXL_DEPRECATED
|
|
+
|
|
+#endif // THIRD_PARTY_LIBJXL_GEN_HEADERS_JXL_JXL_EXPORT_H_
|
|
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
|
|
--- a/tools/metrics/histograms/enums.xml
|
|
+++ b/tools/metrics/histograms/enums.xml
|
|
@@ -26495,7 +26495,7 @@ Called by update_debug_scenarios.py.-->
|
|
<int value="5" label="kImageICO"/>
|
|
<int value="6" label="kImageBMP"/>
|
|
<int value="7" label="kImageAVIF"/>
|
|
- <int value="8" label="(obsolete) kImageJXL"/>
|
|
+ <int value="8" label="kImageJXL"/>
|
|
</enum>
|
|
|
|
<enum name="DecodeSwapChainNotUsedReason">
|
|
@@ -65579,6 +65579,7 @@ from previous Chrome versions.
|
|
<int value="188610022" label="NewMessageListView:enabled"/>
|
|
<int value="189728101" label="FasterLocationReload:disabled"/>
|
|
<int value="189777537" label="DisableInitialMostVisitedFadeIn:enabled"/>
|
|
+ <int value="190282969" label="JXL:disabled"/>
|
|
<int value="190395448" label="SearchWebInSidePanel:enabled"/>
|
|
<int value="191737931" label="enable-mark-http-as"/>
|
|
<int value="191891628" label="AutocompleteExtendedSuggestions:enabled"/>
|
|
--
|
|
2.25.1
|