Bug 1707590 - Part 2: Implement nsJXLDecoder r=tnikkel

Differential Revision: https://phabricator.services.mozilla.com/D113359
This commit is contained in:
Kagami Sascha Rosylight
2021-05-06 01:14:21 +00:00
parent d7d62ba1aa
commit b5bd9f27ae
26 changed files with 325 additions and 5 deletions

View File

@@ -23,6 +23,9 @@
#ifdef MOZ_AV1 #ifdef MOZ_AV1
# include "nsAVIFDecoder.h" # include "nsAVIFDecoder.h"
#endif #endif
#ifdef MOZ_JXL
# include "nsJXLDecoder.h"
#endif
namespace mozilla { namespace mozilla {
@@ -88,6 +91,11 @@ DecoderType DecoderFactory::GetDecoderType(const char* aMimeType) {
type = DecoderType::AVIF; type = DecoderType::AVIF;
} }
#endif #endif
#ifdef MOZ_JXL
else if (!strcmp(aMimeType, IMAGE_JXL) && StaticPrefs::image_jxl_enabled()) {
type = DecoderType::JXL;
}
#endif
return type; return type;
} }
@@ -130,6 +138,11 @@ already_AddRefed<Decoder> DecoderFactory::GetDecoder(DecoderType aType,
case DecoderType::AVIF: case DecoderType::AVIF:
decoder = new nsAVIFDecoder(aImage); decoder = new nsAVIFDecoder(aImage);
break; break;
#endif
#ifdef MOZ_JXL
case DecoderType::JXL:
decoder = new nsJXLDecoder(aImage);
break;
#endif #endif
default: default:
MOZ_ASSERT_UNREACHABLE("Unknown decoder type"); MOZ_ASSERT_UNREACHABLE("Unknown decoder type");

View File

@@ -15,8 +15,7 @@
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
#include "SurfaceFlags.h" #include "SurfaceFlags.h"
namespace mozilla { namespace mozilla::image {
namespace image {
class Decoder; class Decoder;
class IDecodingTask; class IDecodingTask;
@@ -39,6 +38,7 @@ enum class DecoderType {
ICON, ICON,
WEBP, WEBP,
AVIF, AVIF,
JXL,
UNKNOWN UNKNOWN
}; };
@@ -201,7 +201,6 @@ class DecoderFactory {
bool aIsRedecode); bool aIsRedecode);
}; };
} // namespace image } // namespace mozilla::image
} // namespace mozilla
#endif // mozilla_image_DecoderFactory_h #endif // mozilla_image_DecoderFactory_h

View File

@@ -58,10 +58,14 @@ nsresult mozilla::image::EnsureModuleInitialized() {
static ImageEnablementCookie kAVIFCookie = { static ImageEnablementCookie kAVIFCookie = {
mozilla::StaticPrefs::image_avif_enabled, "image/avif"_ns}; mozilla::StaticPrefs::image_avif_enabled, "image/avif"_ns};
static ImageEnablementCookie kJXLCookie = {
mozilla::StaticPrefs::image_jxl_enabled, "image/jxl"_ns};
static ImageEnablementCookie kWebPCookie = { static ImageEnablementCookie kWebPCookie = {
mozilla::StaticPrefs::image_webp_enabled, "image/webp"_ns}; mozilla::StaticPrefs::image_webp_enabled, "image/webp"_ns};
Preferences::RegisterCallbackAndCall(UpdateContentViewerRegistration, Preferences::RegisterCallbackAndCall(UpdateContentViewerRegistration,
"image.avif.enabled", &kAVIFCookie); "image.avif.enabled", &kAVIFCookie);
Preferences::RegisterCallbackAndCall(UpdateContentViewerRegistration,
"image.jxl.enabled", &kJXLCookie);
Preferences::RegisterCallbackAndCall(UpdateContentViewerRegistration, Preferences::RegisterCallbackAndCall(UpdateContentViewerRegistration,
"image.webp.enabled", &kWebPCookie); "image.webp.enabled", &kWebPCookie);

View File

@@ -36,6 +36,11 @@ if CONFIG["MOZ_AV1"]:
"nsAVIFDecoder.cpp", "nsAVIFDecoder.cpp",
] ]
if CONFIG["MOZ_JXL"]:
UNIFIED_SOURCES += [
"nsJXLDecoder.cpp",
]
include("/ipc/chromium/chromium-config.mozbuild") include("/ipc/chromium/chromium-config.mozbuild")
LOCAL_INCLUDES += [ LOCAL_INCLUDES += [

View File

@@ -0,0 +1,163 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ImageLogging.h" // Must appear first
#include "gfxPlatform.h"
#include "jxl/codestream_header.h"
#include "jxl/decode_cxx.h"
#include "jxl/types.h"
#include "mozilla/TelemetryHistogramEnums.h"
#include "mozilla/gfx/Point.h"
#include "nsJXLDecoder.h"
#include "RasterImage.h"
#include "SurfacePipeFactory.h"
using namespace mozilla::gfx;
namespace mozilla::image {
#define JXL_TRY(expr) \
do { \
JxlDecoderStatus status = (expr); \
if (status != JXL_DEC_SUCCESS) { \
return Transition::TerminateFailure(); \
} \
} while (0);
#define JXL_TRY_BOOL(expr) \
do { \
bool succeeded = (expr); \
if (!succeeded) { \
return Transition::TerminateFailure(); \
} \
} while (0);
static LazyLogModule sJXLLog("JXLDecoder");
nsJXLDecoder::nsJXLDecoder(RasterImage* aImage)
: Decoder(aImage),
mLexer(Transition::ToUnbuffered(State::FINISHED_JXL_DATA, State::JXL_DATA,
SIZE_MAX),
Transition::TerminateSuccess()),
mDecoder(JxlDecoderMake(nullptr)),
mParallelRunner(
JxlThreadParallelRunnerMake(nullptr, PreferredThreadCount())) {
JxlDecoderSubscribeEvents(mDecoder.get(),
JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE);
JxlDecoderSetParallelRunner(mDecoder.get(), JxlThreadParallelRunner,
mParallelRunner.get());
MOZ_LOG(sJXLLog, LogLevel::Debug,
("[this=%p] nsJXLDecoder::nsJXLDecoder", this));
}
nsJXLDecoder::~nsJXLDecoder() {
MOZ_LOG(sJXLLog, LogLevel::Debug,
("[this=%p] nsJXLDecoder::~nsJXLDecoder", this));
}
size_t nsJXLDecoder::PreferredThreadCount() {
if (IsMetadataDecode()) {
return 0; // no additional worker thread
}
return JxlThreadParallelRunnerDefaultNumWorkerThreads();
}
LexerResult nsJXLDecoder::DoDecode(SourceBufferIterator& aIterator,
IResumable* aOnResume) {
// return LexerResult(TerminalState::FAILURE);
MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
return mLexer.Lex(aIterator, aOnResume,
[=](State aState, const char* aData, size_t aLength) {
switch (aState) {
case State::JXL_DATA:
return ReadJXLData(aData, aLength);
case State::FINISHED_JXL_DATA:
return FinishedJXLData();
}
MOZ_CRASH("Unknown State");
});
};
LexerTransition<nsJXLDecoder::State> nsJXLDecoder::ReadJXLData(
const char* aData, size_t aLength) {
const uint8_t* input = (const uint8_t*)aData;
size_t length = aLength;
if (mBuffer.length() != 0) {
JXL_TRY_BOOL(mBuffer.append(aData, aLength));
input = mBuffer.begin();
length = mBuffer.length();
}
JXL_TRY(JxlDecoderSetInput(mDecoder.get(), input, length));
while (true) {
JxlDecoderStatus status = JxlDecoderProcessInput(mDecoder.get());
switch (status) {
case JXL_DEC_ERROR:
default:
return Transition::TerminateFailure();
case JXL_DEC_NEED_MORE_INPUT: {
size_t remaining = JxlDecoderReleaseInput(mDecoder.get());
mBuffer.clear();
JXL_TRY_BOOL(mBuffer.append(aData + aLength - remaining, remaining));
return Transition::ContinueUnbuffered(State::JXL_DATA);
}
case JXL_DEC_BASIC_INFO: {
JXL_TRY(JxlDecoderGetBasicInfo(mDecoder.get(), &mInfo));
PostSize(mInfo.xsize, mInfo.ysize);
if (mInfo.alpha_bits > 0) {
PostHasTransparency();
}
if (IsMetadataDecode()) {
return Transition::TerminateSuccess();
}
break;
}
case JXL_DEC_NEED_IMAGE_OUT_BUFFER: {
size_t size = 0;
JxlPixelFormat format{4, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, 0};
JXL_TRY(JxlDecoderImageOutBufferSize(mDecoder.get(), &format, &size));
mOutBuffer.clear();
JXL_TRY_BOOL(mOutBuffer.growBy(size));
JXL_TRY(JxlDecoderSetImageOutBuffer(mDecoder.get(), &format,
mOutBuffer.begin(), size));
break;
}
case JXL_DEC_FULL_IMAGE: {
gfx::IntSize size(mInfo.xsize, mInfo.ysize);
Maybe<SurfacePipe> pipe = SurfacePipeFactory::CreateSurfacePipe(
this, size, OutputSize(), FullFrame(), SurfaceFormat::R8G8B8A8,
SurfaceFormat::OS_RGBA, Nothing(), nullptr, SurfacePipeFlags());
for (uint8_t* rowPtr = mOutBuffer.begin(); rowPtr < mOutBuffer.end();
rowPtr += mInfo.xsize * 4) {
pipe->WriteBuffer(reinterpret_cast<uint32_t*>(rowPtr));
}
if (Maybe<SurfaceInvalidRect> invalidRect = pipe->TakeInvalidRect()) {
PostInvalidation(invalidRect->mInputSpaceRect,
Some(invalidRect->mOutputSpaceRect));
}
PostFrameStop();
PostDecodeDone();
return Transition::TerminateSuccess();
}
}
}
}
LexerTransition<nsJXLDecoder::State> nsJXLDecoder::FinishedJXLData() {
MOZ_ASSERT_UNREACHABLE("Read the entire address space?");
return Transition::TerminateFailure();
}
} // namespace mozilla::image

View File

@@ -0,0 +1,55 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_image_decoders_nsJXLDecoder_h
#define mozilla_image_decoders_nsJXLDecoder_h
#include "Decoder.h"
#include "mp4parse.h"
#include "SurfacePipe.h"
#include "jxl/decode_cxx.h"
#include "jxl/thread_parallel_runner_cxx.h"
#include "mozilla/Telemetry.h"
namespace mozilla::image {
class RasterImage;
class nsJXLDecoder final : public Decoder {
public:
virtual ~nsJXLDecoder();
DecoderType GetType() const override { return DecoderType::JXL; }
protected:
LexerResult DoDecode(SourceBufferIterator& aIterator,
IResumable* aOnResume) override;
private:
friend class DecoderFactory;
// Decoders should only be instantiated via DecoderFactory.
explicit nsJXLDecoder(RasterImage* aImage);
size_t PreferredThreadCount();
enum class State { JXL_DATA, FINISHED_JXL_DATA };
LexerTransition<State> ReadJXLData(const char* aData, size_t aLength);
LexerTransition<State> FinishedJXLData();
StreamingLexer<State> mLexer;
JxlDecoderPtr mDecoder;
JxlThreadParallelRunnerPtr mParallelRunner;
Vector<uint8_t> mBuffer;
Vector<uint8_t> mOutBuffer;
JxlBasicInfo mInfo{};
};
} // namespace mozilla::image
#endif // mozilla_image_decoders_nsJXLDecoder_h

View File

@@ -2758,6 +2758,11 @@ nsresult imgLoader::GetMimeTypeFromContent(const char* aContents,
detected) && detected) &&
detected.Equals(IMAGE_AVIF)) { detected.Equals(IMAGE_AVIF)) {
aContentType.AssignLiteral(IMAGE_AVIF); aContentType.AssignLiteral(IMAGE_AVIF);
} else if ((aLength >= 2 && !memcmp(aContents, "\xFF\x0A", 2)) ||
(aLength >= 12 &&
!memcmp(aContents, "\x00\x00\x00\x0CJXL \x0D\x0A\x87\x0A", 12))) {
// Each version is for containerless and containerful files respectively.
aContentType.AssignLiteral(IMAGE_JXL);
} else { } else {
/* none of the above? I give up */ /* none of the above? I give up */
return NS_ERROR_NOT_AVAILABLE; return NS_ERROR_NOT_AVAILABLE;

View File

@@ -129,8 +129,13 @@ static int RunDecodeToSurfaceFuzzingAVIF(nsCOMPtr<nsIInputStream> inputStream) {
return RunDecodeToSurfaceFuzzing(inputStream, "image/avif"); return RunDecodeToSurfaceFuzzing(inputStream, "image/avif");
} }
static int RunDecodeToSurfaceFuzzingJXL(nsCOMPtr<nsIInputStream> inputStream) {
return RunDecodeToSurfaceFuzzing(inputStream, "image/jxl");
}
int FuzzingInitImage(int* argc, char*** argv) { int FuzzingInitImage(int* argc, char*** argv) {
Preferences::SetBool("image.avif.enabled", true); Preferences::SetBool("image.avif.enabled", true);
Preferences::SetBool("image.jxl.enabled", true);
nsCOMPtr<imgITools> imgTools = nsCOMPtr<imgITools> imgTools =
do_CreateInstance("@mozilla.org/image/tools;1"); do_CreateInstance("@mozilla.org/image/tools;1");
@@ -162,3 +167,6 @@ MOZ_FUZZING_INTERFACE_STREAM(FuzzingInitImage, RunDecodeToSurfaceFuzzingWebP,
MOZ_FUZZING_INTERFACE_STREAM(FuzzingInitImage, RunDecodeToSurfaceFuzzingAVIF, MOZ_FUZZING_INTERFACE_STREAM(FuzzingInitImage, RunDecodeToSurfaceFuzzingAVIF,
ImageAVIF); ImageAVIF);
MOZ_FUZZING_INTERFACE_STREAM(FuzzingInitImage, RunDecodeToSurfaceFuzzingJXL,
ImageJXL);

View File

@@ -47,6 +47,10 @@ AutoInitializeImageLib::AutoInitializeImageLib() {
rv = Preferences::SetBool("image.avif.enabled", true); rv = Preferences::SetBool("image.avif.enabled", true);
EXPECT_TRUE(rv == NS_OK); EXPECT_TRUE(rv == NS_OK);
// Ensure JXL is enabled to run decoder tests.
rv = Preferences::SetBool("image.jxl.enabled", true);
EXPECT_TRUE(rv == NS_OK);
// Ensure that ImageLib services are initialized. // Ensure that ImageLib services are initialized.
nsCOMPtr<imgITools> imgTools = nsCOMPtr<imgITools> imgTools =
do_CreateInstance("@mozilla.org/image/tools;1"); do_CreateInstance("@mozilla.org/image/tools;1");
@@ -438,6 +442,10 @@ ImageTestCase GreenAVIFTestCase() {
.WithSurfaceFlags(SurfaceFlags::TO_SRGB_COLORSPACE); .WithSurfaceFlags(SurfaceFlags::TO_SRGB_COLORSPACE);
} }
ImageTestCase GreenJXLTestCase() {
return ImageTestCase("green.jxl", "image/jxl", IntSize(100, 100));
}
// Forcing sRGB is required until nsAVIFDecoder supports ICC profiles // Forcing sRGB is required until nsAVIFDecoder supports ICC profiles
// See bug 1634741 // See bug 1634741
ImageTestCase Transparent10bit420AVIFTestCase() { ImageTestCase Transparent10bit420AVIFTestCase() {
@@ -565,6 +573,11 @@ ImageTestCase LargeAVIFTestCase() {
TEST_CASE_IGNORE_OUTPUT); TEST_CASE_IGNORE_OUTPUT);
} }
ImageTestCase LargeJXLTestCase() {
return ImageTestCase("large.jxl", "image/jxl", IntSize(1200, 660),
TEST_CASE_IGNORE_OUTPUT);
}
ImageTestCase GreenWebPIccSrgbTestCase() { ImageTestCase GreenWebPIccSrgbTestCase() {
return ImageTestCase("green.icc_srgb.webp", "image/webp", IntSize(100, 100)); return ImageTestCase("green.icc_srgb.webp", "image/webp", IntSize(100, 100));
} }
@@ -661,6 +674,11 @@ ImageTestCase TransparentWebPTestCase() {
return test; return test;
} }
ImageTestCase TransparentJXLTestCase() {
return ImageTestCase("transparent.jxl", "image/jxl", IntSize(1200, 1200),
TEST_CASE_IS_TRANSPARENT);
}
ImageTestCase TransparentNoAlphaHeaderWebPTestCase() { ImageTestCase TransparentNoAlphaHeaderWebPTestCase() {
ImageTestCase test("transparent-no-alpha-header.webp", "image/webp", ImageTestCase test("transparent-no-alpha-header.webp", "image/webp",
IntSize(100, 100), TEST_CASE_IS_FUZZY); IntSize(100, 100), TEST_CASE_IS_FUZZY);
@@ -750,6 +768,11 @@ ImageTestCase DownscaledAVIFTestCase() {
IntSize(20, 20)); IntSize(20, 20));
} }
ImageTestCase DownscaledJXLTestCase() {
return ImageTestCase("downscaled.jxl", "image/jxl", IntSize(100, 100),
IntSize(20, 20));
}
ImageTestCase DownscaledTransparentICOWithANDMaskTestCase() { ImageTestCase DownscaledTransparentICOWithANDMaskTestCase() {
// This test case is an ICO with AND mask transparency. We want to ensure that // This test case is an ICO with AND mask transparency. We want to ensure that
// we can downscale it without crashing or triggering ASAN failures, but its // we can downscale it without crashing or triggering ASAN failures, but its

View File

@@ -476,6 +476,7 @@ ImageTestCase GreenICOTestCase();
ImageTestCase GreenIconTestCase(); ImageTestCase GreenIconTestCase();
ImageTestCase GreenWebPTestCase(); ImageTestCase GreenWebPTestCase();
ImageTestCase GreenAVIFTestCase(); ImageTestCase GreenAVIFTestCase();
ImageTestCase GreenJXLTestCase();
ImageTestCase Transparent10bit420AVIFTestCase(); ImageTestCase Transparent10bit420AVIFTestCase();
ImageTestCase Transparent10bit422AVIFTestCase(); ImageTestCase Transparent10bit422AVIFTestCase();
@@ -490,6 +491,7 @@ ImageTestCase Transparent8bit444AVIFTestCase();
ImageTestCase StackCheckAVIFTestCase(); ImageTestCase StackCheckAVIFTestCase();
ImageTestCase LargeWebPTestCase(); ImageTestCase LargeWebPTestCase();
ImageTestCase LargeJXLTestCase();
ImageTestCase GreenWebPIccSrgbTestCase(); ImageTestCase GreenWebPIccSrgbTestCase();
ImageTestCase GreenFirstFrameAnimatedGIFTestCase(); ImageTestCase GreenFirstFrameAnimatedGIFTestCase();
@@ -509,6 +511,7 @@ ImageTestCase CorruptICOWithBadBppTestCase();
ImageTestCase TransparentPNGTestCase(); ImageTestCase TransparentPNGTestCase();
ImageTestCase TransparentGIFTestCase(); ImageTestCase TransparentGIFTestCase();
ImageTestCase TransparentWebPTestCase(); ImageTestCase TransparentWebPTestCase();
ImageTestCase TransparentJXLTestCase();
ImageTestCase TransparentNoAlphaHeaderWebPTestCase(); ImageTestCase TransparentNoAlphaHeaderWebPTestCase();
ImageTestCase FirstFramePaddingGIFTestCase(); ImageTestCase FirstFramePaddingGIFTestCase();
ImageTestCase TransparentIfWithinICOBMPTestCase(TestCaseFlags aFlags); ImageTestCase TransparentIfWithinICOBMPTestCase(TestCaseFlags aFlags);
@@ -526,6 +529,7 @@ ImageTestCase DownscaledBMPTestCase();
ImageTestCase DownscaledICOTestCase(); ImageTestCase DownscaledICOTestCase();
ImageTestCase DownscaledIconTestCase(); ImageTestCase DownscaledIconTestCase();
ImageTestCase DownscaledWebPTestCase(); ImageTestCase DownscaledWebPTestCase();
ImageTestCase DownscaledJXLTestCase();
ImageTestCase DownscaledTransparentICOWithANDMaskTestCase(); ImageTestCase DownscaledTransparentICOWithANDMaskTestCase();
ImageTestCase TruncatedSmallGIFTestCase(); ImageTestCase TruncatedSmallGIFTestCase();

View File

@@ -680,6 +680,7 @@ IMAGE_GTEST_DECODER_BASE_F(BMP)
IMAGE_GTEST_DECODER_BASE_F(ICO) IMAGE_GTEST_DECODER_BASE_F(ICO)
IMAGE_GTEST_DECODER_BASE_F(Icon) IMAGE_GTEST_DECODER_BASE_F(Icon)
IMAGE_GTEST_DECODER_BASE_F(WebP) IMAGE_GTEST_DECODER_BASE_F(WebP)
IMAGE_GTEST_DECODER_BASE_F(JXL)
TEST_F(ImageDecoders, ICOWithANDMaskDownscaleDuringDecode) { TEST_F(ImageDecoders, ICOWithANDMaskDownscaleDuringDecode) {
CheckDownscaleDuringDecode(DownscaledTransparentICOWithANDMaskTestCase()); CheckDownscaleDuringDecode(DownscaledTransparentICOWithANDMaskTestCase());
@@ -768,6 +769,10 @@ TEST_F(ImageDecoders, AVIFDownscaleDuringDecode) {
CheckDownscaleDuringDecode(DownscaledAVIFTestCase()); CheckDownscaleDuringDecode(DownscaledAVIFTestCase());
} }
TEST_F(ImageDecoders, JXLLargeMultiChunk) {
CheckDecoderMultiChunk(LargeJXLTestCase(), /* aChunkSize */ 64);
}
TEST_F(ImageDecoders, AnimatedGIFSingleChunk) { TEST_F(ImageDecoders, AnimatedGIFSingleChunk) {
CheckDecoderSingleChunk(GreenFirstFrameAnimatedGIFTestCase()); CheckDecoderSingleChunk(GreenFirstFrameAnimatedGIFTestCase());
} }

View File

@@ -87,6 +87,19 @@ TEST_F(ImageLoader, DetectAVIFCompatibleBrand) {
CheckMimeType(buffer, sizeof(buffer), IMAGE_AVIF); CheckMimeType(buffer, sizeof(buffer), IMAGE_AVIF);
} }
TEST_F(ImageLoader, DetectJXLCodestream) {
const char buffer[] = "\xff\x0a";
CheckMimeType(buffer, sizeof(buffer), IMAGE_JXL);
}
TEST_F(ImageLoader, DetectJXLContainer) {
const char buffer[] =
"\x00\x00\x00\x0c"
"JXL "
"\x0d\x0a\x87\x0a";
CheckMimeType(buffer, sizeof(buffer), IMAGE_JXL);
}
TEST_F(ImageLoader, DetectNonImageMP4) { TEST_F(ImageLoader, DetectNonImageMP4) {
const char buffer[] = const char buffer[] =
"\x00\x00\x00\x1c" // box length "\x00\x00\x00\x1c" // box length

View File

@@ -153,6 +153,10 @@ TEST_F(ImageDecoderMetadata, BMP) { CheckMetadata(GreenBMPTestCase()); }
TEST_F(ImageDecoderMetadata, ICO) { CheckMetadata(GreenICOTestCase()); } TEST_F(ImageDecoderMetadata, ICO) { CheckMetadata(GreenICOTestCase()); }
TEST_F(ImageDecoderMetadata, Icon) { CheckMetadata(GreenIconTestCase()); } TEST_F(ImageDecoderMetadata, Icon) { CheckMetadata(GreenIconTestCase()); }
TEST_F(ImageDecoderMetadata, WebP) { CheckMetadata(GreenWebPTestCase()); } TEST_F(ImageDecoderMetadata, WebP) { CheckMetadata(GreenWebPTestCase()); }
TEST_F(ImageDecoderMetadata, JXL) { CheckMetadata(GreenJXLTestCase()); }
TEST_F(ImageDecoderMetadata, TransparentJXL) {
CheckMetadata(TransparentJXLTestCase());
}
TEST_F(ImageDecoderMetadata, AnimatedGIF) { TEST_F(ImageDecoderMetadata, AnimatedGIF) {
CheckMetadata(GreenFirstFrameAnimatedGIFTestCase()); CheckMetadata(GreenFirstFrameAnimatedGIFTestCase());

View File

View File

View File

View File

@@ -60,6 +60,7 @@ TEST_HARNESS_FILES.gtest += [
"downscaled.ico", "downscaled.ico",
"downscaled.icon", "downscaled.icon",
"downscaled.jpg", "downscaled.jpg",
"downscaled.jxl",
"downscaled.png", "downscaled.png",
"downscaled.webp", "downscaled.webp",
"exif_resolution.jpg", "exif_resolution.jpg",
@@ -78,10 +79,12 @@ TEST_HARNESS_FILES.gtest += [
"green.ico", "green.ico",
"green.icon", "green.icon",
"green.jpg", "green.jpg",
"green.jxl",
"green.png", "green.png",
"green.webp", "green.webp",
"invalid-truncated-metadata.bmp", "invalid-truncated-metadata.bmp",
"large.avif", "large.avif",
"large.jxl",
"large.webp", "large.webp",
"multilayer.avif", "multilayer.avif",
"no-frame-delay.gif", "no-frame-delay.gif",
@@ -114,6 +117,7 @@ TEST_HARNESS_FILES.gtest += [
"transparent-no-alpha-header.webp", "transparent-no-alpha-header.webp",
"transparent.avif", "transparent.avif",
"transparent.gif", "transparent.gif",
"transparent.jxl",
"transparent.png", "transparent.png",
"transparent.webp", "transparent.webp",
] ]

View File

View File

@@ -0,0 +1,3 @@
# JXL tests
pref(image.jxl.enabled,true) == jxl-size-33x33.jxl jxl-size-33x33.png

View File

@@ -28,6 +28,9 @@ skip-if(Android&&webrender) include ico/reftest.list
# JPEG tests # JPEG tests
include jpeg/reftest.list include jpeg/reftest.list
# JXL tests
skip-if(Android) include jxl/reftest.list
# GIF tests # GIF tests
include gif/reftest.list include gif/reftest.list

View File

@@ -5488,6 +5488,12 @@
value: true value: true
mirror: always mirror: always
# Whether we attempt to decode JXL images or not.
- name: image.jxl.enabled
type: RelaxedAtomicBool
value: false
mirror: always
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Prefs starting with "intl." # Prefs starting with "intl."
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------

View File

@@ -159,6 +159,7 @@
#define IMAGE_SVG_XML "image/svg+xml" #define IMAGE_SVG_XML "image/svg+xml"
#define IMAGE_WEBP "image/webp" #define IMAGE_WEBP "image/webp"
#define IMAGE_AVIF "image/avif" #define IMAGE_AVIF "image/avif"
#define IMAGE_JXL "image/jxl"
#define MESSAGE_EXTERNAL_BODY "message/external-body" #define MESSAGE_EXTERNAL_BODY "message/external-body"
#define MESSAGE_NEWS "message/news" #define MESSAGE_NEWS "message/news"

View File

@@ -1136,6 +1136,7 @@ const kImageExtensions = new Set([
"svg", "svg",
"webp", "webp",
"avif", "avif",
"jxl",
]); ]);
function getNormalizedLeafName(aFile, aDefaultExtension) { function getNormalizedLeafName(aFile, aDefaultExtension) {

View File

@@ -535,6 +535,7 @@ static const nsExtraMimeTypeEntry extraMimeEntries[] = {
{IMAGE_SVG_XML, "svg", "Scalable Vector Graphics"}, {IMAGE_SVG_XML, "svg", "Scalable Vector Graphics"},
{IMAGE_WEBP, "webp", "WebP Image"}, {IMAGE_WEBP, "webp", "WebP Image"},
{IMAGE_AVIF, "avif", "AV1 Image File"}, {IMAGE_AVIF, "avif", "AV1 Image File"},
{IMAGE_JXL, "jxl", "JPEG XL Image File"},
{MESSAGE_RFC822, "eml", "RFC-822 data"}, {MESSAGE_RFC822, "eml", "RFC-822 data"},
{TEXT_PLAIN, "txt,text", "Text File"}, {TEXT_PLAIN, "txt,text", "Text File"},
@@ -605,7 +606,7 @@ static const char* forcedExtensionMimetypes[] = {
* NOTE: These MUST be lower-case and ASCII. * NOTE: These MUST be lower-case and ASCII.
*/ */
static const char* descriptionOverwriteExtensions[] = { static const char* descriptionOverwriteExtensions[] = {
"avif", "pdf", "svg", "webp", "xml", "avif", "jxl", "pdf", "svg", "webp", "xml",
}; };
static StaticRefPtr<nsExternalHelperAppService> sExtHelperAppSvcSingleton; static StaticRefPtr<nsExternalHelperAppService> sExtHelperAppSvcSingleton;