Blob Blame History Raw
/* -*- 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 "DynamicImage.h"
#include "gfxPlatform.h"
#include "gfxUtils.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/Logging.h"
#include "mozilla/RefPtr.h"
#include "ImageRegion.h"
#include "Orientation.h"
#include "SVGImageContext.h"

#include "mozilla/MemoryReporting.h"

using namespace mozilla;
using namespace mozilla::gfx;
using mozilla::layers::ImageContainer;
using mozilla::layers::LayerManager;

namespace mozilla {
namespace image {

// Inherited methods from Image.

already_AddRefed<ProgressTracker> DynamicImage::GetProgressTracker() {
  return nullptr;
}

size_t DynamicImage::SizeOfSourceWithComputedFallback(
    SizeOfState& aState) const {
  return 0;
}

void DynamicImage::CollectSizeOfSurfaces(
    nsTArray<SurfaceMemoryCounter>& aCounters,
    MallocSizeOf aMallocSizeOf) const {
  // We can't report anything useful because gfxDrawable doesn't expose this
  // information.
}

void DynamicImage::IncrementAnimationConsumers() {}

void DynamicImage::DecrementAnimationConsumers() {}

#ifdef DEBUG
uint32_t DynamicImage::GetAnimationConsumers() { return 0; }
#endif

nsresult DynamicImage::OnImageDataAvailable(nsIRequest* aRequest,
                                            nsISupports* aContext,
                                            nsIInputStream* aInStr,
                                            uint64_t aSourceOffset,
                                            uint32_t aCount) {
  return NS_OK;
}

nsresult DynamicImage::OnImageDataComplete(nsIRequest* aRequest,
                                           nsISupports* aContext,
                                           nsresult aStatus, bool aLastPart) {
  return NS_OK;
}

void DynamicImage::OnSurfaceDiscarded(const SurfaceKey& aSurfaceKey) {}

void DynamicImage::SetInnerWindowID(uint64_t aInnerWindowId) {}

uint64_t DynamicImage::InnerWindowID() const { return 0; }

bool DynamicImage::HasError() { return !mDrawable; }

void DynamicImage::SetHasError() {}

ImageURL* DynamicImage::GetURI() { return nullptr; }

// Methods inherited from XPCOM interfaces.

NS_IMPL_ISUPPORTS(DynamicImage, imgIContainer)

NS_IMETHODIMP
DynamicImage::GetWidth(int32_t* aWidth) {
  *aWidth = mDrawable->Size().width;
  return NS_OK;
}

NS_IMETHODIMP
DynamicImage::GetHeight(int32_t* aHeight) {
  *aHeight = mDrawable->Size().height;
  return NS_OK;
}

nsresult DynamicImage::GetNativeSizes(nsTArray<IntSize>& aNativeSizes) const {
  return NS_ERROR_NOT_IMPLEMENTED;
}

size_t DynamicImage::GetNativeSizesLength() const { return 0; }

NS_IMETHODIMP
DynamicImage::GetIntrinsicSize(nsSize* aSize) {
  IntSize intSize(mDrawable->Size());
  *aSize = nsSize(intSize.width, intSize.height);
  return NS_OK;
}

NS_IMETHODIMP
DynamicImage::GetIntrinsicRatio(nsSize* aSize) {
  IntSize intSize(mDrawable->Size());
  *aSize = nsSize(intSize.width, intSize.height);
  return NS_OK;
}

NS_IMETHODIMP_(Orientation)
DynamicImage::GetOrientation() { return Orientation(); }

NS_IMETHODIMP
DynamicImage::GetType(uint16_t* aType) {
  *aType = imgIContainer::TYPE_RASTER;
  return NS_OK;
}

NS_IMETHODIMP
DynamicImage::GetAnimated(bool* aAnimated) {
  *aAnimated = false;
  return NS_OK;
}

NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
DynamicImage::GetFrame(uint32_t aWhichFrame, uint32_t aFlags) {
  IntSize size(mDrawable->Size());
  return GetFrameAtSize(IntSize(size.width, size.height), aWhichFrame, aFlags);
}

NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
DynamicImage::GetFrameAtSize(const IntSize& aSize, uint32_t aWhichFrame,
                             uint32_t aFlags) {
  RefPtr<DrawTarget> dt =
      gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
          aSize, SurfaceFormat::B8G8R8A8);
  if (!dt || !dt->IsValid()) {
    gfxWarning()
        << "DynamicImage::GetFrame failed in CreateOffscreenContentDrawTarget";
    return nullptr;
  }
  RefPtr<gfxContext> context = gfxContext::CreateOrNull(dt);
  MOZ_ASSERT(context);  // already checked the draw target above

  auto result = Draw(context, aSize, ImageRegion::Create(aSize), aWhichFrame,
                     SamplingFilter::POINT, Nothing(), aFlags, 1.0);

  return result == ImgDrawResult::SUCCESS ? dt->Snapshot() : nullptr;
}

NS_IMETHODIMP_(bool)
DynamicImage::WillDrawOpaqueNow() { return false; }

NS_IMETHODIMP_(bool)
DynamicImage::IsImageContainerAvailable(LayerManager* aManager,
                                        uint32_t aFlags) {
  return false;
}

NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
DynamicImage::GetImageContainer(LayerManager* aManager, uint32_t aFlags) {
  return nullptr;
}

NS_IMETHODIMP_(bool)
DynamicImage::IsImageContainerAvailableAtSize(LayerManager* aManager,
                                              const IntSize& aSize,
                                              uint32_t aFlags) {
  return false;
}

NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
DynamicImage::GetImageContainerAtSize(LayerManager* aManager,
                                      const IntSize& aSize,
                                      const Maybe<SVGImageContext>& aSVGContext,
                                      uint32_t aFlags) {
  return nullptr;
}

NS_IMETHODIMP_(ImgDrawResult)
DynamicImage::Draw(gfxContext* aContext, const nsIntSize& aSize,
                   const ImageRegion& aRegion, uint32_t aWhichFrame,
                   SamplingFilter aSamplingFilter,
                   const Maybe<SVGImageContext>& aSVGContext, uint32_t aFlags,
                   float aOpacity) {
  MOZ_ASSERT(!aSize.IsEmpty(), "Unexpected empty size");

  IntSize drawableSize(mDrawable->Size());

  if (aSize == drawableSize) {
    gfxUtils::DrawPixelSnapped(aContext, mDrawable, SizeDouble(drawableSize),
                               aRegion, SurfaceFormat::B8G8R8A8,
                               aSamplingFilter, aOpacity);
    return ImgDrawResult::SUCCESS;
  }

  gfxSize scale(double(aSize.width) / drawableSize.width,
                double(aSize.height) / drawableSize.height);

  ImageRegion region(aRegion);
  region.Scale(1.0 / scale.width, 1.0 / scale.height);

  gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
  aContext->Multiply(gfxMatrix::Scaling(scale.width, scale.height));

  gfxUtils::DrawPixelSnapped(aContext, mDrawable, SizeDouble(drawableSize),
                             region, SurfaceFormat::B8G8R8A8, aSamplingFilter,
                             aOpacity);
  return ImgDrawResult::SUCCESS;
}

NS_IMETHODIMP
DynamicImage::StartDecoding(uint32_t aFlags) { return NS_OK; }

bool DynamicImage::StartDecodingWithResult(uint32_t aFlags) { return true; }

NS_IMETHODIMP
DynamicImage::RequestDecodeForSize(const nsIntSize& aSize, uint32_t aFlags) {
  return NS_OK;
}

NS_IMETHODIMP
DynamicImage::LockImage() { return NS_OK; }

NS_IMETHODIMP
DynamicImage::UnlockImage() { return NS_OK; }

NS_IMETHODIMP
DynamicImage::RequestDiscard() { return NS_OK; }

NS_IMETHODIMP_(void)
DynamicImage::RequestRefresh(const mozilla::TimeStamp& aTime) {}

NS_IMETHODIMP
DynamicImage::GetAnimationMode(uint16_t* aAnimationMode) {
  *aAnimationMode = kNormalAnimMode;
  return NS_OK;
}

NS_IMETHODIMP
DynamicImage::SetAnimationMode(uint16_t aAnimationMode) { return NS_OK; }

NS_IMETHODIMP
DynamicImage::ResetAnimation() { return NS_OK; }

NS_IMETHODIMP_(float)
DynamicImage::GetFrameIndex(uint32_t aWhichFrame) { return 0; }

NS_IMETHODIMP_(int32_t)
DynamicImage::GetFirstFrameDelay() { return 0; }

NS_IMETHODIMP_(void)
DynamicImage::SetAnimationStartTime(const mozilla::TimeStamp& aTime) {}

nsIntSize DynamicImage::OptimalImageSizeForDest(const gfxSize& aDest,
                                                uint32_t aWhichFrame,
                                                SamplingFilter aSamplingFilter,
                                                uint32_t aFlags) {
  IntSize size(mDrawable->Size());
  return nsIntSize(size.width, size.height);
}

NS_IMETHODIMP_(nsIntRect)
DynamicImage::GetImageSpaceInvalidationRect(const nsIntRect& aRect) {
  return aRect;
}

already_AddRefed<imgIContainer> DynamicImage::Unwrap() {
  nsCOMPtr<imgIContainer> self(this);
  return self.forget();
}

void DynamicImage::PropagateUseCounters(nsIDocument*) {
  // No use counters.
}

}  // namespace image
}  // namespace mozilla