forked from mirrors/gecko-dev
		
	 0e3ad61a5d
			
		
	
	
		0e3ad61a5d
		
	
	
	
	
		
			
			Backed out changeset 45e3f5d8e294 (bug 1446309) Backed out changeset 69a4c2a0aac6 (bug 1446309)
		
			
				
	
	
		
			404 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			404 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 "OrientedImage.h"
 | |
| 
 | |
| #include <algorithm>
 | |
| 
 | |
| #include "gfx2DGlue.h"
 | |
| #include "gfxDrawable.h"
 | |
| #include "gfxPlatform.h"
 | |
| #include "gfxUtils.h"
 | |
| #include "ImageRegion.h"
 | |
| #include "SVGImageContext.h"
 | |
| 
 | |
| using std::swap;
 | |
| 
 | |
| namespace mozilla {
 | |
| 
 | |
| using namespace gfx;
 | |
| using layers::LayerManager;
 | |
| using layers::ImageContainer;
 | |
| 
 | |
| namespace image {
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| OrientedImage::GetWidth(int32_t* aWidth)
 | |
| {
 | |
|   if (mOrientation.SwapsWidthAndHeight()) {
 | |
|     return InnerImage()->GetHeight(aWidth);
 | |
|   } else {
 | |
|     return InnerImage()->GetWidth(aWidth);
 | |
|   }
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| OrientedImage::GetHeight(int32_t* aHeight)
 | |
| {
 | |
|   if (mOrientation.SwapsWidthAndHeight()) {
 | |
|     return InnerImage()->GetWidth(aHeight);
 | |
|   } else {
 | |
|     return InnerImage()->GetHeight(aHeight);
 | |
|   }
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| OrientedImage::GetNativeSizes(nsTArray<IntSize>& aNativeSizes) const
 | |
| {
 | |
|   nsresult rv = InnerImage()->GetNativeSizes(aNativeSizes);
 | |
| 
 | |
|   if (mOrientation.SwapsWidthAndHeight()) {
 | |
|     auto i = aNativeSizes.Length();
 | |
|     while (i > 0) {
 | |
|       --i;
 | |
|       swap(aNativeSizes[i].width, aNativeSizes[i].height);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| OrientedImage::GetIntrinsicSize(nsSize* aSize)
 | |
| {
 | |
|   nsresult rv = InnerImage()->GetIntrinsicSize(aSize);
 | |
| 
 | |
|   if (mOrientation.SwapsWidthAndHeight()) {
 | |
|     swap(aSize->width, aSize->height);
 | |
|   }
 | |
| 
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| OrientedImage::GetIntrinsicRatio(nsSize* aRatio)
 | |
| {
 | |
|   nsresult rv = InnerImage()->GetIntrinsicRatio(aRatio);
 | |
| 
 | |
|   if (mOrientation.SwapsWidthAndHeight()) {
 | |
|     swap(aRatio->width, aRatio->height);
 | |
|   }
 | |
| 
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
 | |
| OrientedImage::GetFrame(uint32_t aWhichFrame,
 | |
|                         uint32_t aFlags)
 | |
| {
 | |
|   nsresult rv;
 | |
| 
 | |
|   if (mOrientation.IsIdentity()) {
 | |
|     return InnerImage()->GetFrame(aWhichFrame, aFlags);
 | |
|   }
 | |
| 
 | |
|   // Get the underlying dimensions.
 | |
|   IntSize size;
 | |
|   rv = InnerImage()->GetWidth(&size.width);
 | |
|   NS_ENSURE_SUCCESS(rv, nullptr);
 | |
|   rv = InnerImage()->GetHeight(&size.height);
 | |
|   NS_ENSURE_SUCCESS(rv, nullptr);
 | |
| 
 | |
|   // Determine an appropriate format for the surface.
 | |
|   gfx::SurfaceFormat surfaceFormat;
 | |
|   if (InnerImage()->WillDrawOpaqueNow()) {
 | |
|     surfaceFormat = gfx::SurfaceFormat::B8G8R8X8;
 | |
|   } else {
 | |
|     surfaceFormat = gfx::SurfaceFormat::B8G8R8A8;
 | |
|   }
 | |
| 
 | |
|   // Create a surface to draw into.
 | |
|   RefPtr<DrawTarget> target =
 | |
|     gfxPlatform::GetPlatform()->
 | |
|       CreateOffscreenContentDrawTarget(size, surfaceFormat);
 | |
|   if (!target || !target->IsValid()) {
 | |
|     NS_ERROR("Could not create a DrawTarget");
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
| 
 | |
|   // Create our drawable.
 | |
|   RefPtr<SourceSurface> innerSurface =
 | |
|     InnerImage()->GetFrame(aWhichFrame, aFlags);
 | |
|   NS_ENSURE_TRUE(innerSurface, nullptr);
 | |
|   RefPtr<gfxDrawable> drawable =
 | |
|     new gfxSurfaceDrawable(innerSurface, size);
 | |
| 
 | |
|   // Draw.
 | |
|   RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(target);
 | |
|   MOZ_ASSERT(ctx); // already checked the draw target above
 | |
|   ctx->Multiply(OrientationMatrix(size));
 | |
|   gfxUtils::DrawPixelSnapped(ctx, drawable, SizeDouble(size), ImageRegion::Create(size),
 | |
|                              surfaceFormat, SamplingFilter::LINEAR);
 | |
| 
 | |
|   return target->Snapshot();
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
 | |
| OrientedImage::GetFrameAtSize(const IntSize& aSize,
 | |
|                               uint32_t aWhichFrame,
 | |
|                               uint32_t aFlags)
 | |
| {
 | |
|   // XXX(seth): It'd be nice to support downscale-during-decode for this case,
 | |
|   // but right now we just fall back to the intrinsic size.
 | |
|   return GetFrame(aWhichFrame, aFlags);
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP_(bool)
 | |
| OrientedImage::IsImageContainerAvailable(LayerManager* aManager, uint32_t aFlags)
 | |
| {
 | |
|   if (mOrientation.IsIdentity()) {
 | |
|     return InnerImage()->IsImageContainerAvailable(aManager, aFlags);
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
 | |
| OrientedImage::GetImageContainer(LayerManager* aManager, uint32_t aFlags)
 | |
| {
 | |
|   // XXX(seth): We currently don't have a way of orienting the result of
 | |
|   // GetImageContainer. We work around this by always returning null, but if it
 | |
|   // ever turns out that OrientedImage is widely used on codepaths that can
 | |
|   // actually benefit from GetImageContainer, it would be a good idea to fix
 | |
|   // that method for performance reasons.
 | |
| 
 | |
|   if (mOrientation.IsIdentity()) {
 | |
|     return InnerImage()->GetImageContainer(aManager, aFlags);
 | |
|   }
 | |
| 
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP_(bool)
 | |
| OrientedImage::IsImageContainerAvailableAtSize(LayerManager* aManager,
 | |
|                                                const IntSize& aSize,
 | |
|                                                uint32_t aFlags)
 | |
| {
 | |
|   if (mOrientation.IsIdentity()) {
 | |
|     return InnerImage()->IsImageContainerAvailableAtSize(aManager, aSize, aFlags);
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
 | |
| OrientedImage::GetImageContainerAtSize(LayerManager* aManager,
 | |
|                                        const IntSize& aSize,
 | |
|                                        const Maybe<SVGImageContext>& aSVGContext,
 | |
|                                        uint32_t aFlags)
 | |
| {
 | |
|   // XXX(seth): We currently don't have a way of orienting the result of
 | |
|   // GetImageContainer. We work around this by always returning null, but if it
 | |
|   // ever turns out that OrientedImage is widely used on codepaths that can
 | |
|   // actually benefit from GetImageContainer, it would be a good idea to fix
 | |
|   // that method for performance reasons.
 | |
| 
 | |
|   if (mOrientation.IsIdentity()) {
 | |
|     return InnerImage()->GetImageContainerAtSize(aManager, aSize,
 | |
|                                                  aSVGContext, aFlags);
 | |
|   }
 | |
| 
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| struct MatrixBuilder
 | |
| {
 | |
|   explicit MatrixBuilder(bool aInvert) : mInvert(aInvert) { }
 | |
| 
 | |
|   gfxMatrix Build() { return mMatrix; }
 | |
| 
 | |
|   void Scale(gfxFloat aX, gfxFloat aY)
 | |
|   {
 | |
|     if (mInvert) {
 | |
|       mMatrix *= gfxMatrix::Scaling(1.0 / aX, 1.0 / aY);
 | |
|     } else {
 | |
|       mMatrix.PreScale(aX, aY);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void Rotate(gfxFloat aPhi)
 | |
|   {
 | |
|     if (mInvert) {
 | |
|       mMatrix *= gfxMatrix::Rotation(-aPhi);
 | |
|     } else {
 | |
|       mMatrix.PreRotate(aPhi);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void Translate(gfxPoint aDelta)
 | |
|   {
 | |
|     if (mInvert) {
 | |
|       mMatrix *= gfxMatrix::Translation(-aDelta);
 | |
|     } else {
 | |
|       mMatrix.PreTranslate(aDelta);
 | |
|     }
 | |
|   }
 | |
| 
 | |
| private:
 | |
|   gfxMatrix mMatrix;
 | |
|   bool      mInvert;
 | |
| };
 | |
| 
 | |
| /*
 | |
|  * OrientationMatrix() computes a matrix that applies the rotation and
 | |
|  * reflection specified by mOrientation, or that matrix's inverse if aInvert is
 | |
|  * true.
 | |
|  *
 | |
|  * @param aSize The scaled size of the inner image. (When outside code specifies
 | |
|  *              the scaled size, as with imgIContainer::Draw and its aSize
 | |
|  *              parameter, it's necessary to swap the width and height if
 | |
|  *              mOrientation.SwapsWidthAndHeight() is true.)
 | |
|  * @param aInvert If true, compute the inverse of the orientation matrix. Prefer
 | |
|  *                this approach to OrientationMatrix(..).Invert(), because it's
 | |
|  *                more numerically accurate.
 | |
|  */
 | |
| gfxMatrix
 | |
| OrientedImage::OrientationMatrix(const nsIntSize& aSize,
 | |
|                                  bool aInvert /* = false */)
 | |
| {
 | |
|   MatrixBuilder builder(aInvert);
 | |
| 
 | |
|   // Apply reflection, if present. (This logically happens second, but we
 | |
|   // apply it first because these transformations are all premultiplied.) A
 | |
|   // translation is necessary to place the image back in the first quadrant.
 | |
|   switch (mOrientation.flip) {
 | |
|     case Flip::Unflipped:
 | |
|       break;
 | |
|     case Flip::Horizontal:
 | |
|       if (mOrientation.SwapsWidthAndHeight()) {
 | |
|         builder.Translate(gfxPoint(aSize.height, 0));
 | |
|       } else {
 | |
|         builder.Translate(gfxPoint(aSize.width, 0));
 | |
|       }
 | |
|       builder.Scale(-1.0, 1.0);
 | |
|       break;
 | |
|     default:
 | |
|       MOZ_ASSERT(false, "Invalid flip value");
 | |
|   }
 | |
| 
 | |
|   // Apply rotation, if present. Again, a translation is used to place the
 | |
|   // image back in the first quadrant.
 | |
|   switch (mOrientation.rotation) {
 | |
|     case Angle::D0:
 | |
|       break;
 | |
|     case Angle::D90:
 | |
|       builder.Translate(gfxPoint(aSize.height, 0));
 | |
|       builder.Rotate(-1.5 * M_PI);
 | |
|       break;
 | |
|     case Angle::D180:
 | |
|       builder.Translate(gfxPoint(aSize.width, aSize.height));
 | |
|       builder.Rotate(-1.0 * M_PI);
 | |
|       break;
 | |
|     case Angle::D270:
 | |
|       builder.Translate(gfxPoint(0, aSize.width));
 | |
|       builder.Rotate(-0.5 * M_PI);
 | |
|       break;
 | |
|     default:
 | |
|       MOZ_ASSERT(false, "Invalid rotation value");
 | |
|   }
 | |
| 
 | |
|   return builder.Build();
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP_(ImgDrawResult)
 | |
| OrientedImage::Draw(gfxContext* aContext,
 | |
|                     const nsIntSize& aSize,
 | |
|                     const ImageRegion& aRegion,
 | |
|                     uint32_t aWhichFrame,
 | |
|                     SamplingFilter aSamplingFilter,
 | |
|                     const Maybe<SVGImageContext>& aSVGContext,
 | |
|                     uint32_t aFlags,
 | |
|                     float aOpacity)
 | |
| {
 | |
|   if (mOrientation.IsIdentity()) {
 | |
|     return InnerImage()->Draw(aContext, aSize, aRegion,
 | |
|                               aWhichFrame, aSamplingFilter,
 | |
|                               aSVGContext, aFlags, aOpacity);
 | |
|   }
 | |
| 
 | |
|   // Update the image size to match the image's coordinate system. (This could
 | |
|   // be done using TransformBounds but since it's only a size a swap is enough.)
 | |
|   nsIntSize size(aSize);
 | |
|   if (mOrientation.SwapsWidthAndHeight()) {
 | |
|     swap(size.width, size.height);
 | |
|   }
 | |
| 
 | |
|   // Update the matrix so that we transform the image into the orientation
 | |
|   // expected by the caller before drawing.
 | |
|   gfxMatrix matrix(OrientationMatrix(size));
 | |
|   gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
 | |
|   aContext->Multiply(matrix);
 | |
| 
 | |
|   // The region is already in the orientation expected by the caller, but we
 | |
|   // need it to be in the image's coordinate system, so we transform it using
 | |
|   // the inverse of the orientation matrix.
 | |
|   gfxMatrix inverseMatrix(OrientationMatrix(size, /* aInvert = */ true));
 | |
|   ImageRegion region(aRegion);
 | |
|   region.TransformBoundsBy(inverseMatrix);
 | |
| 
 | |
|   auto orientViewport = [&](const SVGImageContext& aOldContext) {
 | |
|     SVGImageContext context(aOldContext);
 | |
|     auto oldViewport = aOldContext.GetViewportSize();
 | |
|     if (oldViewport && mOrientation.SwapsWidthAndHeight()) {
 | |
|       // Swap width and height:
 | |
|       CSSIntSize newViewport(oldViewport->height, oldViewport->width);
 | |
|       context.SetViewportSize(Some(newViewport));
 | |
|     }
 | |
|     return context;
 | |
|   };
 | |
| 
 | |
|   return InnerImage()->Draw(aContext, size, region, aWhichFrame,
 | |
|                             aSamplingFilter,
 | |
|                             aSVGContext.map(orientViewport), aFlags,
 | |
|                             aOpacity);
 | |
| }
 | |
| 
 | |
| nsIntSize
 | |
| OrientedImage::OptimalImageSizeForDest(const gfxSize& aDest,
 | |
|                                        uint32_t aWhichFrame,
 | |
|                                        SamplingFilter aSamplingFilter,
 | |
|                                        uint32_t aFlags)
 | |
| {
 | |
|   if (!mOrientation.SwapsWidthAndHeight()) {
 | |
|     return InnerImage()->OptimalImageSizeForDest(aDest, aWhichFrame,
 | |
|                                                  aSamplingFilter, aFlags);
 | |
|   }
 | |
| 
 | |
|   // Swap the size for the calculation, then swap it back for the caller.
 | |
|   gfxSize destSize(aDest.height, aDest.width);
 | |
|   nsIntSize innerImageSize(InnerImage()->OptimalImageSizeForDest(destSize,
 | |
|                                                                  aWhichFrame,
 | |
|                                                                  aSamplingFilter,
 | |
|                                                                  aFlags));
 | |
|   return nsIntSize(innerImageSize.height, innerImageSize.width);
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP_(nsIntRect)
 | |
| OrientedImage::GetImageSpaceInvalidationRect(const nsIntRect& aRect)
 | |
| {
 | |
|   nsIntRect rect(InnerImage()->GetImageSpaceInvalidationRect(aRect));
 | |
| 
 | |
|   if (mOrientation.IsIdentity()) {
 | |
|     return rect;
 | |
|   }
 | |
| 
 | |
|   nsIntSize innerSize;
 | |
|   nsresult rv = InnerImage()->GetWidth(&innerSize.width);
 | |
|   rv = NS_FAILED(rv) ? rv : InnerImage()->GetHeight(&innerSize.height);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     // Fall back to identity if the width and height aren't available.
 | |
|     return rect;
 | |
|   }
 | |
| 
 | |
|   // Transform the invalidation rect into the correct orientation.
 | |
|   gfxMatrix matrix(OrientationMatrix(innerSize));
 | |
|   gfxRect invalidRect(matrix.TransformBounds(gfxRect(rect.X(), rect.Y(),
 | |
|                                                      rect.Width(), rect.Height())));
 | |
| 
 | |
|   return IntRect::RoundOut(invalidRect.X(), invalidRect.Y(),
 | |
|                            invalidRect.Width(), invalidRect.Height());
 | |
| }
 | |
| 
 | |
| } // namespace image
 | |
| } // namespace mozilla
 |