gecko-dev/image/DecoderFactory.cpp
Andrew Osmond c35d734095 Bug 1389988 - Handle a data race between a new sync decode request and a pending decoder. r=tnikkel
If there is an active provider which has yet to produce a frame, any
calls to SurfaceCache::Lookup will return MatchType::PENDING. If
RasterImage::Lookup gets the above result while given FLAG_SYNC_DECODE,
it will attempt to start a new decoder. It is entirely possible that
when we try to insert the new provider into the SurfaceCache, it cannot
because the original provider finally did produce something. In that
case we should abandon attempting to redecode and retry our lookup.
2018-02-09 08:51:28 -05:00

371 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 "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"
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;
// 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;
}
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::ICO:
decoder = new nsICODecoder(aImage);
break;
case DecoderType::ICON:
decoder = new nsIconDecoder(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,
IDecodingTask** aOutTask)
{
if (aType == DecoderType::UNKNOWN) {
return NS_ERROR_INVALID_ARG;
}
MOZ_ASSERT(aType == DecoderType::GIF || aType == DecoderType::PNG,
"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));
// 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<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(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,
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;
// Without an image, the decoder can't store anything in the SurfaceCache, so
// callers will only be able to retrieve the most recent frame via
// Decoder::GetCurrentFrame(). That means that anonymous decoders should
// always be first-frame-only decoders, because nobody ever wants the *last*
// frame.
decoderFlags |= DecoderFlags::FIRST_FRAME_ONLY;
decoder->SetDecoderFlags(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