gecko-dev/image/DecoderFactory.cpp
Andrew Osmond 874ce61a83 Bug 1901078 - Implement promise based anonymous image decoder. r=tnikkel
This bypasses any caching used on the display pipeline and is
intended to be used by layers that want asynchronous decoding
that fits well into the MozPromise style.

This also fixes an issue with the frame count metadata decoder in the
GIF decoder. The code only began being used with this patch, and the WPT
exposed an overflow bug caused by not clearing nsGIFDecoder2::mColormap
and nsGIFDecoder2::mColormapSize.

Differential Revision: https://phabricator.services.mozilla.com/D212833
2024-07-24 03:16:16 +00:00

488 lines
15 KiB
C++

/* -*- 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 "DecoderFactory.h"
#include "nsMimeTypes.h"
#include "mozilla/RefPtr.h"
#include "AnimationSurfaceProvider.h"
#include "Decoder.h"
#include "DecodedSurfaceProvider.h"
#include "IDecodingTask.h"
#include "ImageOps.h"
#include "nsPNGDecoder.h"
#include "nsGIFDecoder2.h"
#include "nsJPEGDecoder.h"
#include "nsBMPDecoder.h"
#include "nsICODecoder.h"
#include "nsIconDecoder.h"
#include "nsWebPDecoder.h"
#ifdef MOZ_AV1
# include "nsAVIFDecoder.h"
#endif
#ifdef MOZ_JXL
# include "nsJXLDecoder.h"
#endif
namespace mozilla {
using namespace gfx;
namespace image {
/* static */
DecoderType DecoderFactory::GetDecoderType(const char* aMimeType) {
// By default we don't know.
DecoderType type = DecoderType::UNKNOWN;
// PNG
if (!strcmp(aMimeType, IMAGE_PNG)) {
type = DecoderType::PNG;
} else if (!strcmp(aMimeType, IMAGE_X_PNG)) {
type = DecoderType::PNG;
} else if (!strcmp(aMimeType, IMAGE_APNG)) {
type = DecoderType::PNG;
// GIF
} else if (!strcmp(aMimeType, IMAGE_GIF)) {
type = DecoderType::GIF;
// JPEG
} else if (!strcmp(aMimeType, IMAGE_JPEG)) {
type = DecoderType::JPEG;
} else if (!strcmp(aMimeType, IMAGE_PJPEG)) {
type = DecoderType::JPEG;
} else if (!strcmp(aMimeType, IMAGE_JPG)) {
type = DecoderType::JPEG;
// BMP
} else if (!strcmp(aMimeType, IMAGE_BMP)) {
type = DecoderType::BMP;
} else if (!strcmp(aMimeType, IMAGE_BMP_MS)) {
type = DecoderType::BMP;
// BMP_CLIPBOARD
} else if (!strcmp(aMimeType, IMAGE_BMP_MS_CLIPBOARD)) {
type = DecoderType::BMP_CLIPBOARD;
// ICO
} else if (!strcmp(aMimeType, IMAGE_ICO)) {
type = DecoderType::ICO;
} else if (!strcmp(aMimeType, IMAGE_ICO_MS)) {
type = DecoderType::ICO;
// Icon
} else if (!strcmp(aMimeType, IMAGE_ICON_MS)) {
type = DecoderType::ICON;
// WebP
} else if (!strcmp(aMimeType, IMAGE_WEBP)) {
type = DecoderType::WEBP;
// AVIF
}
#ifdef MOZ_AV1
else if (!strcmp(aMimeType, IMAGE_AVIF) &&
StaticPrefs::image_avif_enabled()) {
type = DecoderType::AVIF;
}
#endif
#ifdef MOZ_JXL
else if (!strcmp(aMimeType, IMAGE_JXL) && StaticPrefs::image_jxl_enabled()) {
type = DecoderType::JXL;
}
#endif
return type;
}
/* static */
DecoderFlags DecoderFactory::GetDefaultDecoderFlagsForType(DecoderType aType) {
auto flags = DefaultDecoderFlags();
#ifdef MOZ_AV1
if (aType == DecoderType::AVIF) {
if (StaticPrefs::image_avif_sequence_enabled()) {
flags |= DecoderFlags::AVIF_SEQUENCES_ENABLED;
}
if (StaticPrefs::image_avif_sequence_animate_avif_major_branded_images()) {
flags |= DecoderFlags::AVIF_ANIMATE_AVIF_MAJOR;
}
}
#endif
return flags;
}
/* static */
already_AddRefed<Decoder> DecoderFactory::GetDecoder(DecoderType aType,
RasterImage* aImage,
bool aIsRedecode) {
RefPtr<Decoder> decoder;
switch (aType) {
case DecoderType::PNG:
decoder = new nsPNGDecoder(aImage);
break;
case DecoderType::GIF:
decoder = new nsGIFDecoder2(aImage);
break;
case DecoderType::JPEG:
// If we have all the data we don't want to waste cpu time doing
// a progressive decode.
decoder = new nsJPEGDecoder(
aImage, aIsRedecode ? Decoder::SEQUENTIAL : Decoder::PROGRESSIVE);
break;
case DecoderType::BMP:
decoder = new nsBMPDecoder(aImage);
break;
case DecoderType::BMP_CLIPBOARD:
decoder = new nsBMPDecoder(aImage, /* aForClipboard */ true);
break;
case DecoderType::ICO:
decoder = new nsICODecoder(aImage);
break;
case DecoderType::ICON:
decoder = new nsIconDecoder(aImage);
break;
case DecoderType::WEBP:
decoder = new nsWebPDecoder(aImage);
break;
#ifdef MOZ_AV1
case DecoderType::AVIF:
decoder = new nsAVIFDecoder(aImage);
break;
#endif
#ifdef MOZ_JXL
case DecoderType::JXL:
decoder = new nsJXLDecoder(aImage);
break;
#endif
default:
MOZ_ASSERT_UNREACHABLE("Unknown decoder type");
}
return decoder.forget();
}
/* static */
nsresult DecoderFactory::CreateDecoder(
DecoderType aType, NotNull<RasterImage*> aImage,
NotNull<SourceBuffer*> aSourceBuffer, const IntSize& aIntrinsicSize,
const IntSize& aOutputSize, DecoderFlags aDecoderFlags,
SurfaceFlags aSurfaceFlags, IDecodingTask** aOutTask) {
if (aType == DecoderType::UNKNOWN) {
return NS_ERROR_INVALID_ARG;
}
// Only can use COUNT_FRAMES with metadata decoders.
if (NS_WARN_IF(bool(aDecoderFlags & DecoderFlags::COUNT_FRAMES))) {
return NS_ERROR_INVALID_ARG;
}
// Create an anonymous decoder. Interaction with the SurfaceCache and the
// owning RasterImage will be mediated by DecodedSurfaceProvider.
RefPtr<Decoder> decoder = GetDecoder(
aType, nullptr, bool(aDecoderFlags & DecoderFlags::IS_REDECODE));
MOZ_ASSERT(decoder, "Should have a decoder now");
// Initialize the decoder.
decoder->SetMetadataDecode(false);
decoder->SetIterator(aSourceBuffer->Iterator());
decoder->SetOutputSize(OrientedIntSize::FromUnknownSize(aOutputSize));
decoder->SetDecoderFlags(aDecoderFlags | DecoderFlags::FIRST_FRAME_ONLY);
decoder->SetSurfaceFlags(aSurfaceFlags);
nsresult rv = decoder->Init();
if (NS_FAILED(rv)) {
return NS_ERROR_FAILURE;
}
// Create a DecodedSurfaceProvider which will manage the decoding process and
// make this decoder's output available in the surface cache.
SurfaceKey surfaceKey =
RasterSurfaceKey(aOutputSize, aSurfaceFlags, PlaybackType::eStatic);
auto provider = MakeNotNull<RefPtr<DecodedSurfaceProvider>>(
aImage, surfaceKey, WrapNotNull(decoder));
if (aDecoderFlags & DecoderFlags::CANNOT_SUBSTITUTE) {
provider->Availability().SetCannotSubstitute();
}
// Attempt to insert the surface provider into the surface cache right away so
// we won't trigger any more decoders with the same parameters.
switch (SurfaceCache::Insert(provider)) {
case InsertOutcome::SUCCESS:
break;
case InsertOutcome::FAILURE_ALREADY_PRESENT:
return NS_ERROR_ALREADY_INITIALIZED;
default:
return NS_ERROR_FAILURE;
}
// Return the surface provider in its IDecodingTask guise.
RefPtr<IDecodingTask> task = provider.get();
task.forget(aOutTask);
return NS_OK;
}
/* static */
nsresult DecoderFactory::CreateAnimationDecoder(
DecoderType aType, NotNull<RasterImage*> aImage,
NotNull<SourceBuffer*> aSourceBuffer, const IntSize& aIntrinsicSize,
DecoderFlags aDecoderFlags, SurfaceFlags aSurfaceFlags,
size_t aCurrentFrame, IDecodingTask** aOutTask) {
if (aType == DecoderType::UNKNOWN) {
return NS_ERROR_INVALID_ARG;
}
// Only can use COUNT_FRAMES with metadata decoders.
if (NS_WARN_IF(bool(aDecoderFlags & DecoderFlags::COUNT_FRAMES))) {
return NS_ERROR_INVALID_ARG;
}
MOZ_ASSERT(aType == DecoderType::GIF || aType == DecoderType::PNG ||
aType == DecoderType::WEBP || aType == DecoderType::AVIF,
"Calling CreateAnimationDecoder for non-animating DecoderType");
// Create an anonymous decoder. Interaction with the SurfaceCache and the
// owning RasterImage will be mediated by AnimationSurfaceProvider.
RefPtr<Decoder> decoder =
GetDecoder(aType, nullptr, /* aIsRedecode = */ true);
MOZ_ASSERT(decoder, "Should have a decoder now");
// Initialize the decoder.
decoder->SetMetadataDecode(false);
decoder->SetIterator(aSourceBuffer->Iterator());
decoder->SetDecoderFlags(aDecoderFlags | DecoderFlags::IS_REDECODE);
decoder->SetSurfaceFlags(aSurfaceFlags);
nsresult rv = decoder->Init();
if (NS_FAILED(rv)) {
return NS_ERROR_FAILURE;
}
// Create an AnimationSurfaceProvider which will manage the decoding process
// and make this decoder's output available in the surface cache.
SurfaceKey surfaceKey =
RasterSurfaceKey(aIntrinsicSize, aSurfaceFlags, PlaybackType::eAnimated);
auto provider = MakeNotNull<RefPtr<AnimationSurfaceProvider>>(
aImage, surfaceKey, WrapNotNull(decoder), aCurrentFrame);
// Attempt to insert the surface provider into the surface cache right away so
// we won't trigger any more decoders with the same parameters.
switch (SurfaceCache::Insert(provider)) {
case InsertOutcome::SUCCESS:
break;
case InsertOutcome::FAILURE_ALREADY_PRESENT:
return NS_ERROR_ALREADY_INITIALIZED;
default:
return NS_ERROR_FAILURE;
}
// Return the surface provider in its IDecodingTask guise.
RefPtr<IDecodingTask> task = provider.get();
task.forget(aOutTask);
return NS_OK;
}
/* static */
already_AddRefed<Decoder> DecoderFactory::CloneAnimationDecoder(
Decoder* aDecoder) {
MOZ_ASSERT(aDecoder);
// In an ideal world, we would assert aDecoder->HasAnimation() but we cannot.
// The decoder may not have detected it is animated yet (e.g. it did not even
// get scheduled yet, or it has only decoded the first frame and has yet to
// rediscover it is animated).
DecoderType type = aDecoder->GetType();
MOZ_ASSERT(type == DecoderType::GIF || type == DecoderType::PNG ||
type == DecoderType::WEBP || type == DecoderType::AVIF,
"Calling CloneAnimationDecoder for non-animating DecoderType");
RefPtr<Decoder> decoder = GetDecoder(type, nullptr, /* aIsRedecode = */ true);
MOZ_ASSERT(decoder, "Should have a decoder now");
// Initialize the decoder.
decoder->SetMetadataDecode(false);
decoder->SetIterator(aDecoder->GetSourceBuffer()->Iterator());
decoder->SetDecoderFlags(aDecoder->GetDecoderFlags());
decoder->SetSurfaceFlags(aDecoder->GetSurfaceFlags());
decoder->SetFrameRecycler(aDecoder->GetFrameRecycler());
if (NS_FAILED(decoder->Init())) {
return nullptr;
}
return decoder.forget();
}
/* static */
already_AddRefed<Decoder> DecoderFactory::CloneAnonymousMetadataDecoder(
Decoder* aDecoder, const Maybe<DecoderFlags>& aDecoderFlags) {
MOZ_ASSERT(aDecoder);
DecoderType type = aDecoder->GetType();
RefPtr<Decoder> decoder =
GetDecoder(type, nullptr, /* aIsRedecode = */ false);
MOZ_ASSERT(decoder, "Should have a decoder now");
// Initialize the decoder.
decoder->SetMetadataDecode(true);
decoder->SetIterator(aDecoder->GetSourceBuffer()->Iterator());
if (aDecoderFlags) {
decoder->SetDecoderFlags(*aDecoderFlags);
} else {
decoder->SetDecoderFlags(aDecoder->GetDecoderFlags());
}
if (NS_FAILED(decoder->Init())) {
return nullptr;
}
return decoder.forget();
}
/* static */
already_AddRefed<IDecodingTask> DecoderFactory::CreateMetadataDecoder(
DecoderType aType, NotNull<RasterImage*> aImage, DecoderFlags aFlags,
NotNull<SourceBuffer*> aSourceBuffer) {
if (aType == DecoderType::UNKNOWN) {
return nullptr;
}
RefPtr<Decoder> decoder =
GetDecoder(aType, aImage, /* aIsRedecode = */ false);
MOZ_ASSERT(decoder, "Should have a decoder now");
// Initialize the decoder.
decoder->SetMetadataDecode(true);
decoder->SetDecoderFlags(aFlags);
decoder->SetIterator(aSourceBuffer->Iterator());
if (NS_FAILED(decoder->Init())) {
return nullptr;
}
RefPtr<IDecodingTask> task = new MetadataDecodingTask(WrapNotNull(decoder));
return task.forget();
}
/* static */
already_AddRefed<Decoder> DecoderFactory::CreateDecoderForICOResource(
DecoderType aType, SourceBufferIterator&& aIterator,
NotNull<nsICODecoder*> aICODecoder, bool aIsMetadataDecode,
const Maybe<OrientedIntSize>& aExpectedSize,
const Maybe<uint32_t>& aDataOffset
/* = Nothing() */) {
// Create the decoder.
RefPtr<Decoder> decoder;
switch (aType) {
case DecoderType::BMP:
MOZ_ASSERT(aDataOffset);
decoder =
new nsBMPDecoder(aICODecoder->GetImageMaybeNull(), *aDataOffset);
break;
case DecoderType::PNG:
MOZ_ASSERT(!aDataOffset);
decoder = new nsPNGDecoder(aICODecoder->GetImageMaybeNull());
break;
default:
MOZ_ASSERT_UNREACHABLE("Invalid ICO resource decoder type");
return nullptr;
}
MOZ_ASSERT(decoder);
// Initialize the decoder, copying settings from @aICODecoder.
decoder->SetMetadataDecode(aIsMetadataDecode);
decoder->SetIterator(std::forward<SourceBufferIterator>(aIterator));
if (!aIsMetadataDecode) {
decoder->SetOutputSize(aICODecoder->OutputSize());
}
if (aExpectedSize) {
decoder->SetExpectedSize(*aExpectedSize);
}
decoder->SetDecoderFlags(aICODecoder->GetDecoderFlags());
decoder->SetSurfaceFlags(aICODecoder->GetSurfaceFlags());
decoder->SetFinalizeFrames(false);
if (NS_FAILED(decoder->Init())) {
return nullptr;
}
return decoder.forget();
}
/* static */
already_AddRefed<Decoder> DecoderFactory::CreateAnonymousDecoder(
DecoderType aType, NotNull<SourceBuffer*> aSourceBuffer,
const Maybe<IntSize>& aOutputSize, DecoderFlags aDecoderFlags,
SurfaceFlags aSurfaceFlags) {
if (aType == DecoderType::UNKNOWN) {
return nullptr;
}
// Only can use COUNT_FRAMES with metadata decoders.
if (NS_WARN_IF(bool(aDecoderFlags & DecoderFlags::COUNT_FRAMES))) {
return nullptr;
}
RefPtr<Decoder> decoder =
GetDecoder(aType, /* aImage = */ nullptr, /* aIsRedecode = */ false);
MOZ_ASSERT(decoder, "Should have a decoder now");
// Initialize the decoder.
decoder->SetMetadataDecode(false);
decoder->SetIterator(aSourceBuffer->Iterator());
// Anonymous decoders are always transient; we don't want to optimize surfaces
// or do any other expensive work that might be wasted.
DecoderFlags decoderFlags = DecoderFlags::IMAGE_IS_TRANSIENT;
decoder->SetDecoderFlags(aDecoderFlags | decoderFlags);
decoder->SetSurfaceFlags(aSurfaceFlags);
// Set an output size for downscale-during-decode if requested.
if (aOutputSize) {
decoder->SetOutputSize(OrientedIntSize::FromUnknownSize(*aOutputSize));
}
if (NS_FAILED(decoder->Init())) {
return nullptr;
}
return decoder.forget();
}
/* static */
already_AddRefed<Decoder> DecoderFactory::CreateAnonymousMetadataDecoder(
DecoderType aType, NotNull<SourceBuffer*> aSourceBuffer,
DecoderFlags aDecoderFlags) {
if (aType == DecoderType::UNKNOWN) {
return nullptr;
}
RefPtr<Decoder> decoder =
GetDecoder(aType, /* aImage = */ nullptr, /* aIsRedecode = */ false);
MOZ_ASSERT(decoder, "Should have a decoder now");
// Initialize the decoder.
decoder->SetMetadataDecode(true);
decoder->SetIterator(aSourceBuffer->Iterator());
decoder->SetDecoderFlags(aDecoderFlags);
if (NS_FAILED(decoder->Init())) {
return nullptr;
}
return decoder.forget();
}
} // namespace image
} // namespace mozilla