/* 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 "ImageRegion.h"
#include "Orientation.h"
#include "gfxContext.h"
#include "gfxPlatform.h"
#include "gfxUtils.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/RefPtr.h"
#include "mozilla/SVGImageContext.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/Logging.h"
#include "mozilla/image/Resolution.h"
#include "nsPresContext.h"

using namespace mozilla;
using namespace mozilla::gfx;

namespace mozilla::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,
                                            nsIInputStream* aInStr,
                                            uint64_t aSourceOffset,
                                            uint32_t aCount) {
  return NS_OK;
}

nsresult DynamicImage::OnImageDataComplete(nsIRequest* aRequest,
                                           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() {}

nsIURI* DynamicImage::GetURI() const { 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;
}

void DynamicImage::MediaFeatureValuesChangedAllDocuments(
    const mozilla::MediaFeatureChange& aChange) {}

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

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

NS_IMETHODIMP
DynamicImage::GetIntrinsicSize(ImageIntrinsicSize* aIntrinsicSize) {
  IntSize intSize(mDrawable->Size());
  aIntrinsicSize->mWidth = Some(intSize.width);
  aIntrinsicSize->mHeight = Some(intSize.height);

  return NS_OK;
}

NS_IMETHODIMP
DynamicImage::GetIntrinsicSizeInAppUnits(nsSize* aSize) {
  IntSize intSize(mDrawable->Size());

  *aSize = nsSize(nsPresContext::CSSPixelsToAppUnits(intSize.width),
                  nsPresContext::CSSPixelsToAppUnits(intSize.height));
  return NS_OK;
}

AspectRatio DynamicImage::GetIntrinsicRatio() {
  auto size = mDrawable->Size();
  return AspectRatio::FromSize(size.width, size.height);
}

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

NS_IMETHODIMP_(Resolution)
DynamicImage::GetResolution() { return {}; }

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

NS_IMETHODIMP
DynamicImage::GetProviderId(uint32_t* aId) {
  *aId = 0;
  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::OS_RGBA);
  if (!dt || !dt->IsValid()) {
    gfxWarning()
        << "DynamicImage::GetFrame failed in CreateOffscreenContentDrawTarget";
    return nullptr;
  }
  gfxContext context(dt);

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

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

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

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

NS_IMETHODIMP_(ImgDrawResult)
DynamicImage::GetImageProvider(WindowRenderer* aRenderer,
                               const gfx::IntSize& aSize,
                               const SVGImageContext& aSVGContext,
                               const Maybe<ImageIntRegion>& aRegion,
                               uint32_t aFlags,
                               WebRenderImageProvider** aProvider) {
  return ImgDrawResult::NOT_SUPPORTED;
}

NS_IMETHODIMP_(ImgDrawResult)
DynamicImage::Draw(gfxContext* aContext, const nsIntSize& aSize,
                   const ImageRegion& aRegion, uint32_t aWhichFrame,
                   SamplingFilter aSamplingFilter,
                   const 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::OS_RGBA, aSamplingFilter,
                               aOpacity);
    return ImgDrawResult::SUCCESS;
  }

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

  ImageRegion region(aRegion);
  region.Scale(1.0 / scale.xScale, 1.0 / scale.yScale);

  gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
  aContext->Multiply(gfxMatrix::Scaling(scale));

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

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

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

bool DynamicImage::HasDecodedPixels() { return true; }

imgIContainer::DecodeResult DynamicImage::RequestDecodeWithResult(
    uint32_t aFlags, uint32_t aWhichFrame) {
  return imgIContainer::DECODE_SURFACE_AVAILABLE;
}

NS_IMETHODIMP
DynamicImage::RequestDecodeForSize(const nsIntSize& aSize, uint32_t aFlags,
                                   uint32_t aWhichFrame) {
  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(dom::Document*) {
  // No use counters.
}

nsresult DynamicImage::GetHotspotX(int32_t* aX) {
  return Image::GetHotspotX(aX);
}

nsresult DynamicImage::GetHotspotY(int32_t* aY) {
  return Image::GetHotspotY(aY);
}

}  // namespace mozilla::image
