image/DecoderFactory.cpp
author Morten Stenshorne <mstensho@chromium.org>
Tue, 09 Oct 2018 04:14:13 +0000
changeset 495959 13844f50ee32e17f513b7c1013c2348200e21b8c
parent 492583 a826a94ae5dd12bfb01edde50dc9a37e4c1e6e3b
child 498813 6ef29bb729309b6c5fefa615a72cdcb897c7c473
permissions -rw-r--r--
Bug 1496274 [wpt PR 13345] - [LayoutNG] Correct clip-path reference box calculation., a=testonly Automatic update from web-platform-tests[LayoutNG] Correct clip-path reference box calculation. We used coordinates relatively to the line box, while we were expected by the caller to be relative to the containing block. Flipping for writing mode was bogus for NG (but needed by legacy), since NG uses truly physical coordinates. Hardened tests to contain a leading line and padding, and leading content on the first line of the clipped child. Bug: 641907 Change-Id: I2b1b9ff4ea92a6405fcdffcf139842458b46442f Cq-Include-Trybots: luci.chromium.try​:linux_layout_tests_layout_ng Reviewed-on: https://chromium-review.googlesource.com/c/1257913 Reviewed-by: Koji Ishii <kojii@chromium.org> Reviewed-by: Fredrik Söderquist <fs@opera.com> Commit-Queue: Morten Stenshorne <mstensho@chromium.org> Cr-Commit-Position: refs/heads/master@{#596554} -- wpt-commits: e9a0828c85819340f721f121aac19ab8eefa3439 wpt-pr: 13345

/* -*- 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,
                                       size_t aCurrentFrame,
                                       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), 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,
             "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());

  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