gecko-dev/image/DecoderFactory.cpp
Gurzau Raul 967bc2a754 Backed out 31 changesets (bug 1552643, bug 1550422) for xpcshell crash on a CLOSED TREE.
Backed out changeset e30c1aa75529 (bug 1552643)
Backed out changeset caadcd7e02d3 (bug 1552643)
Backed out changeset aa7086ab09be (bug 1552643)
Backed out changeset 0b4029671710 (bug 1550422)
Backed out changeset a16295296035 (bug 1550422)
Backed out changeset 3b70307c0db5 (bug 1550422)
Backed out changeset 69df7818d4a3 (bug 1550422)
Backed out changeset d98dfc565927 (bug 1550422)
Backed out changeset 6f0997976944 (bug 1550422)
Backed out changeset 0edd264464c2 (bug 1550422)
Backed out changeset 9ea6da7a74ec (bug 1550422)
Backed out changeset f855f9309c8b (bug 1550422)
Backed out changeset 1033546224a7 (bug 1550422)
Backed out changeset ade7384c6186 (bug 1550422)
Backed out changeset 75b04de7e99c (bug 1550422)
Backed out changeset 91c3acdb2454 (bug 1550422)
Backed out changeset 77d2f80257d1 (bug 1550422)
Backed out changeset e0cd10d35327 (bug 1550422)
Backed out changeset 097091082423 (bug 1550422)
Backed out changeset 2f328853c1ab (bug 1550422)
Backed out changeset f92f2cc29cb1 (bug 1550422)
Backed out changeset 6dc82f88333d (bug 1550422)
Backed out changeset c20f66494d69 (bug 1550422)
Backed out changeset 2ba22cddeb6f (bug 1550422)
Backed out changeset 3aa72f89e295 (bug 1550422)
Backed out changeset ab4c4e806977 (bug 1550422)
Backed out changeset 72e5de040dda (bug 1550422)
Backed out changeset 7d3c2d486706 (bug 1550422)
Backed out changeset 132e0b8d8468 (bug 1550422)
Backed out changeset 54c85ac75dd0 (bug 1550422)
Backed out changeset d7ba4a18dd54 (bug 1550422)
2019-05-25 09:07:49 +03:00

398 lines
12 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 "gfxPrefs.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"
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) && gfxPrefs::ImageWebPEnabled()) {
type = DecoderType::WEBP;
}
return type;
}
/* 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;
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;
}
// 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(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;
}
MOZ_ASSERT(aType == DecoderType::GIF || aType == DecoderType::PNG ||
aType == DecoderType::WEBP,
"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,
"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<IDecodingTask> DecoderFactory::CreateMetadataDecoder(
DecoderType aType, NotNull<RasterImage*> aImage,
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->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<IntSize>& 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;
}
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(*aOutputSize);
}
if (NS_FAILED(decoder->Init())) {
return nullptr;
}
return decoder.forget();
}
/* static */
already_AddRefed<Decoder> DecoderFactory::CreateAnonymousMetadataDecoder(
DecoderType aType, NotNull<SourceBuffer*> aSourceBuffer) {
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(DecoderFlags::FIRST_FRAME_ONLY);
if (NS_FAILED(decoder->Init())) {
return nullptr;
}
return decoder.forget();
}
} // namespace image
} // namespace mozilla