mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-06 07:48:38 +09:00
Issue #1769 - Part 2: Implement JPEG-XL decoder and about:config and MIME plumbing.
Backported from Mozilla bug 1707590 whereever possible.
This commit is contained in:
parent
7983f5d8ba
commit
51ea0e4f3a
15 changed files with 257 additions and 4 deletions
|
|
@ -448,6 +448,7 @@ private:
|
|||
DECL_GFX_PREF(Live, "image.mozsamplesize.enabled", ImageMozSampleSizeEnabled, bool, false);
|
||||
DECL_GFX_PREF(Once, "image.multithreaded_decoding.limit", ImageMTDecodingLimit, int32_t, -1);
|
||||
DECL_GFX_PREF(Live, "image.webp.enabled", ImageWebPEnabled, bool, true);
|
||||
DECL_GFX_PREF(Live, "image.jxl.enabled", ImageJXLEnabled, bool, true);
|
||||
|
||||
DECL_GFX_PREF(Once, "layers.acceleration.enabled", LayersAccelerationEnabledDoNotUseDirectly, bool, true);
|
||||
DECL_GFX_PREF(Live, "layers.acceleration.draw-fps", LayersDrawFPS, bool, false);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@
|
|||
#include "nsICODecoder.h"
|
||||
#include "nsIconDecoder.h"
|
||||
#include "nsWebPDecoder.h"
|
||||
#ifdef MOZ_JXL
|
||||
# include "nsJXLDecoder.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
|
@ -77,6 +80,12 @@ DecoderFactory::GetDecoderType(const char* aMimeType)
|
|||
gfxPrefs::ImageWebPEnabled()) {
|
||||
type = DecoderType::WEBP;
|
||||
}
|
||||
#ifdef MOZ_JXL
|
||||
else if (!strcmp(aMimeType, IMAGE_JXL) &&
|
||||
gfxPrefs::ImageJXLEnabled()) {
|
||||
type = DecoderType::JXL;
|
||||
}
|
||||
#endif
|
||||
return type;
|
||||
}
|
||||
|
||||
|
|
@ -116,6 +125,11 @@ DecoderFactory::GetDecoder(DecoderType aType,
|
|||
case DecoderType::WEBP:
|
||||
decoder = new nsWebPDecoder(aImage);
|
||||
break;
|
||||
#ifdef MOZ_JXL
|
||||
case DecoderType::JXL:
|
||||
decoder = new nsJXLDecoder(aImage);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Unknown decoder type");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
#include "nsCOMPtr.h"
|
||||
#include "SurfaceFlags.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace image {
|
||||
namespace mozilla::image {
|
||||
|
||||
class Decoder;
|
||||
class IDecodingTask;
|
||||
|
|
@ -38,6 +37,7 @@ enum class DecoderType
|
|||
ICO,
|
||||
ICON,
|
||||
WEBP,
|
||||
JXL,
|
||||
UNKNOWN
|
||||
};
|
||||
|
||||
|
|
@ -200,7 +200,6 @@ private:
|
|||
bool aIsRedecode);
|
||||
};
|
||||
|
||||
} // namespace image
|
||||
} // namespace mozilla
|
||||
} // namespace mozilla::image
|
||||
|
||||
#endif // mozilla_image_DecoderFactory_h
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ static const mozilla::Module::CategoryEntry kImageCategories[] = {
|
|||
{ "Gecko-Content-Viewers", IMAGE_APNG, "@mozilla.org/content/document-loader-factory;1" },
|
||||
{ "Gecko-Content-Viewers", IMAGE_X_PNG, "@mozilla.org/content/document-loader-factory;1" },
|
||||
{ "Gecko-Content-Viewers", IMAGE_WEBP, "@mozilla.org/content/document-loader-factory;1" },
|
||||
{ "Gecko-Content-Viewers", IMAGE_JXL, "@mozilla.org/content/document-loader-factory;1" },
|
||||
{ "content-sniffing-services", "@mozilla.org/image/loader;1", "@mozilla.org/image/loader;1" },
|
||||
{ nullptr }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -28,6 +28,11 @@ UNIFIED_SOURCES += [
|
|||
'nsWebPDecoder.cpp',
|
||||
]
|
||||
|
||||
if CONFIG["MOZ_JXL"]:
|
||||
UNIFIED_SOURCES += [
|
||||
"nsJXLDecoder.cpp",
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
|
|
|
|||
162
image/decoders/nsJXLDecoder.cpp
Normal file
162
image/decoders/nsJXLDecoder.cpp
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
/* -*- 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/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: {
|
||||
OrientedIntSize 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
|
||||
53
image/decoders/nsJXLDecoder.h
Normal file
53
image/decoders/nsJXLDecoder.h
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/* -*- 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"
|
||||
|
||||
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
|
||||
|
|
@ -2550,6 +2550,12 @@ imgLoader::GetMimeTypeFromContent(const char* aContents,
|
|||
!memcmp(aContents + 8, "WEBP", 4)) {
|
||||
aContentType.AssignLiteral(IMAGE_WEBP);
|
||||
|
||||
} 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 {
|
||||
/* none of the above? I give up */
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
|
|
|||
BIN
image/test/reftest/jxl/jxl-size-33x33.jxl
Normal file
BIN
image/test/reftest/jxl/jxl-size-33x33.jxl
Normal file
Binary file not shown.
BIN
image/test/reftest/jxl/jxl-size-33x33.png
Normal file
BIN
image/test/reftest/jxl/jxl-size-33x33.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.2 KiB |
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) include ico/reftest.list
|
|||
# JPEG tests
|
||||
include jpeg/reftest.list
|
||||
|
||||
# JXL tests
|
||||
skip-if(Android) include jxl/reftest.list
|
||||
|
||||
# GIF tests
|
||||
include gif/reftest.list
|
||||
|
||||
|
|
|
|||
|
|
@ -4262,6 +4262,10 @@ pref("image.multithreaded_decoding.limit", -1);
|
|||
// Whether we attempt to decode WebP images or not.
|
||||
pref("image.webp.enabled", true);
|
||||
|
||||
#ifdef MOZ_JXL
|
||||
pref("image.jxl.enabled", false);
|
||||
#endif
|
||||
|
||||
// Limit for the canvas image cache. 0 means we don't limit the size of the
|
||||
// cache.
|
||||
pref("canvas.image.cache.limit", 0);
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@
|
|||
#define IMAGE_JNG "image/x-jng"
|
||||
#define IMAGE_SVG_XML "image/svg+xml"
|
||||
#define IMAGE_WEBP "image/webp"
|
||||
#define IMAGE_JXL "image/jxl"
|
||||
|
||||
#define MESSAGE_EXTERNAL_BODY "message/external-body"
|
||||
#define MESSAGE_NEWS "message/news"
|
||||
|
|
|
|||
|
|
@ -503,6 +503,7 @@ static const nsExtraMimeTypeEntry extraMimeEntries[] =
|
|||
{ IMAGE_XBM, "xbm", "XBM Image" },
|
||||
{ IMAGE_SVG_XML, "svg", "Scalable Vector Graphics" },
|
||||
{ IMAGE_WEBP, "webp", "WebP Image" },
|
||||
{ IMAGE_JXL, "jxl", "JPEG XL Image File" },
|
||||
{ MESSAGE_RFC822, "eml", "RFC-822 data" },
|
||||
{ TEXT_PLAIN, "txt,text", "Text File" },
|
||||
{ TEXT_HTML, "html,htm,shtml,ehtml", "HyperText Markup Language" },
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue