Bug 1707590 - Part 2: Implement nsJXLDecoder r=tnikkel
Differential Revision: https://phabricator.services.mozilla.com/D113359
This commit is contained in:
@@ -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");
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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 += [
|
||||||
|
|||||||
163
image/decoders/nsJXLDecoder.cpp
Normal file
163
image/decoders/nsJXLDecoder.cpp
Normal 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
|
||||||
55
image/decoders/nsJXLDecoder.h
Normal file
55
image/decoders/nsJXLDecoder.h
Normal 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
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
0
image/test/gtest/downscaled.jxl
Normal file
0
image/test/gtest/downscaled.jxl
Normal file
0
image/test/gtest/green.jxl
Normal file
0
image/test/gtest/green.jxl
Normal file
0
image/test/gtest/large.jxl
Normal file
0
image/test/gtest/large.jxl
Normal 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",
|
||||||
]
|
]
|
||||||
|
|||||||
0
image/test/gtest/transparent.jxl
Normal file
0
image/test/gtest/transparent.jxl
Normal file
0
image/test/reftest/jxl/jxl-size-33x33.jxl
Normal file
0
image/test/reftest/jxl/jxl-size-33x33.jxl
Normal file
0
image/test/reftest/jxl/jxl-size-33x33.png
Normal file
0
image/test/reftest/jxl/jxl-size-33x33.png
Normal file
3
image/test/reftest/jxl/reftest.list
Normal file
3
image/test/reftest/jxl/reftest.list
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# JXL tests
|
||||||
|
|
||||||
|
pref(image.jxl.enabled,true) == jxl-size-33x33.jxl jxl-size-33x33.png
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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."
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -1136,6 +1136,7 @@ const kImageExtensions = new Set([
|
|||||||
"svg",
|
"svg",
|
||||||
"webp",
|
"webp",
|
||||||
"avif",
|
"avif",
|
||||||
|
"jxl",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
function getNormalizedLeafName(aFile, aDefaultExtension) {
|
function getNormalizedLeafName(aFile, aDefaultExtension) {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user