LCOV - code coverage report
Current view: top level - image - OrientedImage.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 147 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 22 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "OrientedImage.h"
       7             : 
       8             : #include <algorithm>
       9             : 
      10             : #include "gfx2DGlue.h"
      11             : #include "gfxDrawable.h"
      12             : #include "gfxPlatform.h"
      13             : #include "gfxUtils.h"
      14             : #include "ImageRegion.h"
      15             : #include "SVGImageContext.h"
      16             : 
      17             : using std::swap;
      18             : 
      19             : namespace mozilla {
      20             : 
      21             : using namespace gfx;
      22             : using layers::LayerManager;
      23             : using layers::ImageContainer;
      24             : 
      25             : namespace image {
      26             : 
      27           0 : NS_IMPL_ISUPPORTS_INHERITED0(OrientedImage, ImageWrapper)
      28             : 
      29             : NS_IMETHODIMP
      30           0 : OrientedImage::GetWidth(int32_t* aWidth)
      31             : {
      32           0 :   if (mOrientation.SwapsWidthAndHeight()) {
      33           0 :     return InnerImage()->GetHeight(aWidth);
      34             :   } else {
      35           0 :     return InnerImage()->GetWidth(aWidth);
      36             :   }
      37             : }
      38             : 
      39             : NS_IMETHODIMP
      40           0 : OrientedImage::GetHeight(int32_t* aHeight)
      41             : {
      42           0 :   if (mOrientation.SwapsWidthAndHeight()) {
      43           0 :     return InnerImage()->GetWidth(aHeight);
      44             :   } else {
      45           0 :     return InnerImage()->GetHeight(aHeight);
      46             :   }
      47             : }
      48             : 
      49             : nsresult
      50           0 : OrientedImage::GetNativeSizes(nsTArray<IntSize>& aNativeSizes) const
      51             : {
      52           0 :   nsresult rv = InnerImage()->GetNativeSizes(aNativeSizes);
      53             : 
      54           0 :   if (mOrientation.SwapsWidthAndHeight()) {
      55           0 :     auto i = aNativeSizes.Length();
      56           0 :     while (i > 0) {
      57           0 :       --i;
      58           0 :       swap(aNativeSizes[i].width, aNativeSizes[i].height);
      59             :     }
      60             :   }
      61             : 
      62           0 :   return rv;
      63             : }
      64             : 
      65             : NS_IMETHODIMP
      66           0 : OrientedImage::GetIntrinsicSize(nsSize* aSize)
      67             : {
      68           0 :   nsresult rv = InnerImage()->GetIntrinsicSize(aSize);
      69             : 
      70           0 :   if (mOrientation.SwapsWidthAndHeight()) {
      71           0 :     swap(aSize->width, aSize->height);
      72             :   }
      73             : 
      74           0 :   return rv;
      75             : }
      76             : 
      77             : NS_IMETHODIMP
      78           0 : OrientedImage::GetIntrinsicRatio(nsSize* aRatio)
      79             : {
      80           0 :   nsresult rv = InnerImage()->GetIntrinsicRatio(aRatio);
      81             : 
      82           0 :   if (mOrientation.SwapsWidthAndHeight()) {
      83           0 :     swap(aRatio->width, aRatio->height);
      84             :   }
      85             : 
      86           0 :   return rv;
      87             : }
      88             : 
      89             : NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
      90           0 : OrientedImage::GetFrame(uint32_t aWhichFrame,
      91             :                         uint32_t aFlags)
      92             : {
      93             :   nsresult rv;
      94             : 
      95           0 :   if (mOrientation.IsIdentity()) {
      96           0 :     return InnerImage()->GetFrame(aWhichFrame, aFlags);
      97             :   }
      98             : 
      99             :   // Get the underlying dimensions.
     100           0 :   IntSize size;
     101           0 :   rv = InnerImage()->GetWidth(&size.width);
     102           0 :   NS_ENSURE_SUCCESS(rv, nullptr);
     103           0 :   rv = InnerImage()->GetHeight(&size.height);
     104           0 :   NS_ENSURE_SUCCESS(rv, nullptr);
     105             : 
     106             :   // Determine an appropriate format for the surface.
     107             :   gfx::SurfaceFormat surfaceFormat;
     108           0 :   if (InnerImage()->WillDrawOpaqueNow()) {
     109           0 :     surfaceFormat = gfx::SurfaceFormat::B8G8R8X8;
     110             :   } else {
     111           0 :     surfaceFormat = gfx::SurfaceFormat::B8G8R8A8;
     112             :   }
     113             : 
     114             :   // Create a surface to draw into.
     115             :   RefPtr<DrawTarget> target =
     116             :     gfxPlatform::GetPlatform()->
     117           0 :       CreateOffscreenContentDrawTarget(size, surfaceFormat);
     118           0 :   if (!target || !target->IsValid()) {
     119           0 :     NS_ERROR("Could not create a DrawTarget");
     120           0 :     return nullptr;
     121             :   }
     122             : 
     123             : 
     124             :   // Create our drawable.
     125             :   RefPtr<SourceSurface> innerSurface =
     126           0 :     InnerImage()->GetFrame(aWhichFrame, aFlags);
     127           0 :   NS_ENSURE_TRUE(innerSurface, nullptr);
     128             :   RefPtr<gfxDrawable> drawable =
     129           0 :     new gfxSurfaceDrawable(innerSurface, size);
     130             : 
     131             :   // Draw.
     132           0 :   RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(target);
     133           0 :   MOZ_ASSERT(ctx); // already checked the draw target above
     134           0 :   ctx->Multiply(OrientationMatrix(size));
     135           0 :   gfxUtils::DrawPixelSnapped(ctx, drawable, SizeDouble(size), ImageRegion::Create(size),
     136           0 :                              surfaceFormat, SamplingFilter::LINEAR);
     137             : 
     138           0 :   return target->Snapshot();
     139             : }
     140             : 
     141             : NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
     142           0 : OrientedImage::GetFrameAtSize(const IntSize& aSize,
     143             :                               uint32_t aWhichFrame,
     144             :                               uint32_t aFlags)
     145             : {
     146             :   // XXX(seth): It'd be nice to support downscale-during-decode for this case,
     147             :   // but right now we just fall back to the intrinsic size.
     148           0 :   return GetFrame(aWhichFrame, aFlags);
     149             : }
     150             : 
     151             : NS_IMETHODIMP_(bool)
     152           0 : OrientedImage::IsImageContainerAvailable(LayerManager* aManager, uint32_t aFlags)
     153             : {
     154           0 :   if (mOrientation.IsIdentity()) {
     155           0 :     return InnerImage()->IsImageContainerAvailable(aManager, aFlags);
     156             :   }
     157           0 :   return false;
     158             : }
     159             : 
     160             : NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
     161           0 : OrientedImage::GetImageContainer(LayerManager* aManager, uint32_t aFlags)
     162             : {
     163             :   // XXX(seth): We currently don't have a way of orienting the result of
     164             :   // GetImageContainer. We work around this by always returning null, but if it
     165             :   // ever turns out that OrientedImage is widely used on codepaths that can
     166             :   // actually benefit from GetImageContainer, it would be a good idea to fix
     167             :   // that method for performance reasons.
     168             : 
     169           0 :   if (mOrientation.IsIdentity()) {
     170           0 :     return InnerImage()->GetImageContainer(aManager, aFlags);
     171             :   }
     172             : 
     173           0 :   return nullptr;
     174             : }
     175             : 
     176             : struct MatrixBuilder
     177             : {
     178           0 :   explicit MatrixBuilder(bool aInvert) : mInvert(aInvert) { }
     179             : 
     180           0 :   gfxMatrix Build() { return mMatrix; }
     181             : 
     182           0 :   void Scale(gfxFloat aX, gfxFloat aY)
     183             :   {
     184           0 :     if (mInvert) {
     185           0 :       mMatrix *= gfxMatrix::Scaling(1.0 / aX, 1.0 / aY);
     186             :     } else {
     187           0 :       mMatrix.PreScale(aX, aY);
     188             :     }
     189           0 :   }
     190             : 
     191           0 :   void Rotate(gfxFloat aPhi)
     192             :   {
     193           0 :     if (mInvert) {
     194           0 :       mMatrix *= gfxMatrix::Rotation(-aPhi);
     195             :     } else {
     196           0 :       mMatrix.PreRotate(aPhi);
     197             :     }
     198           0 :   }
     199             : 
     200           0 :   void Translate(gfxPoint aDelta)
     201             :   {
     202           0 :     if (mInvert) {
     203           0 :       mMatrix *= gfxMatrix::Translation(-aDelta);
     204             :     } else {
     205           0 :       mMatrix.PreTranslate(aDelta);
     206             :     }
     207           0 :   }
     208             : 
     209             : private:
     210             :   gfxMatrix mMatrix;
     211             :   bool      mInvert;
     212             : };
     213             : 
     214             : /*
     215             :  * OrientationMatrix() computes a matrix that applies the rotation and
     216             :  * reflection specified by mOrientation, or that matrix's inverse if aInvert is
     217             :  * true.
     218             :  *
     219             :  * @param aSize The scaled size of the inner image. (When outside code specifies
     220             :  *              the scaled size, as with imgIContainer::Draw and its aSize
     221             :  *              parameter, it's necessary to swap the width and height if
     222             :  *              mOrientation.SwapsWidthAndHeight() is true.)
     223             :  * @param aInvert If true, compute the inverse of the orientation matrix. Prefer
     224             :  *                this approach to OrientationMatrix(..).Invert(), because it's
     225             :  *                more numerically accurate.
     226             :  */
     227             : gfxMatrix
     228           0 : OrientedImage::OrientationMatrix(const nsIntSize& aSize,
     229             :                                  bool aInvert /* = false */)
     230             : {
     231           0 :   MatrixBuilder builder(aInvert);
     232             : 
     233             :   // Apply reflection, if present. (This logically happens second, but we
     234             :   // apply it first because these transformations are all premultiplied.) A
     235             :   // translation is necessary to place the image back in the first quadrant.
     236           0 :   switch (mOrientation.flip) {
     237             :     case Flip::Unflipped:
     238           0 :       break;
     239             :     case Flip::Horizontal:
     240           0 :       if (mOrientation.SwapsWidthAndHeight()) {
     241           0 :         builder.Translate(gfxPoint(aSize.height, 0));
     242             :       } else {
     243           0 :         builder.Translate(gfxPoint(aSize.width, 0));
     244             :       }
     245           0 :       builder.Scale(-1.0, 1.0);
     246           0 :       break;
     247             :     default:
     248           0 :       MOZ_ASSERT(false, "Invalid flip value");
     249             :   }
     250             : 
     251             :   // Apply rotation, if present. Again, a translation is used to place the
     252             :   // image back in the first quadrant.
     253           0 :   switch (mOrientation.rotation) {
     254             :     case Angle::D0:
     255           0 :       break;
     256             :     case Angle::D90:
     257           0 :       builder.Translate(gfxPoint(aSize.height, 0));
     258           0 :       builder.Rotate(-1.5 * M_PI);
     259           0 :       break;
     260             :     case Angle::D180:
     261           0 :       builder.Translate(gfxPoint(aSize.width, aSize.height));
     262           0 :       builder.Rotate(-1.0 * M_PI);
     263           0 :       break;
     264             :     case Angle::D270:
     265           0 :       builder.Translate(gfxPoint(0, aSize.width));
     266           0 :       builder.Rotate(-0.5 * M_PI);
     267           0 :       break;
     268             :     default:
     269           0 :       MOZ_ASSERT(false, "Invalid rotation value");
     270             :   }
     271             : 
     272           0 :   return builder.Build();
     273             : }
     274             : 
     275             : NS_IMETHODIMP_(DrawResult)
     276           0 : OrientedImage::Draw(gfxContext* aContext,
     277             :                     const nsIntSize& aSize,
     278             :                     const ImageRegion& aRegion,
     279             :                     uint32_t aWhichFrame,
     280             :                     SamplingFilter aSamplingFilter,
     281             :                     const Maybe<SVGImageContext>& aSVGContext,
     282             :                     uint32_t aFlags,
     283             :                     float aOpacity)
     284             : {
     285           0 :   if (mOrientation.IsIdentity()) {
     286           0 :     return InnerImage()->Draw(aContext, aSize, aRegion,
     287             :                               aWhichFrame, aSamplingFilter,
     288           0 :                               aSVGContext, aFlags, aOpacity);
     289             :   }
     290             : 
     291             :   // Update the image size to match the image's coordinate system. (This could
     292             :   // be done using TransformBounds but since it's only a size a swap is enough.)
     293           0 :   nsIntSize size(aSize);
     294           0 :   if (mOrientation.SwapsWidthAndHeight()) {
     295           0 :     swap(size.width, size.height);
     296             :   }
     297             : 
     298             :   // Update the matrix so that we transform the image into the orientation
     299             :   // expected by the caller before drawing.
     300           0 :   gfxMatrix matrix(OrientationMatrix(size));
     301           0 :   gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
     302           0 :   aContext->Multiply(matrix);
     303             : 
     304             :   // The region is already in the orientation expected by the caller, but we
     305             :   // need it to be in the image's coordinate system, so we transform it using
     306             :   // the inverse of the orientation matrix.
     307           0 :   gfxMatrix inverseMatrix(OrientationMatrix(size, /* aInvert = */ true));
     308           0 :   ImageRegion region(aRegion);
     309           0 :   region.TransformBoundsBy(inverseMatrix);
     310             : 
     311           0 :   auto orientViewport = [&](const SVGImageContext& aOldContext) {
     312           0 :     SVGImageContext context(aOldContext);
     313           0 :     auto oldViewport = aOldContext.GetViewportSize();
     314           0 :     if (oldViewport && mOrientation.SwapsWidthAndHeight()) {
     315             :       // Swap width and height:
     316           0 :       CSSIntSize newViewport(oldViewport->height, oldViewport->width);
     317           0 :       context.SetViewportSize(Some(newViewport));
     318             :     }
     319           0 :     return context;
     320           0 :   };
     321             : 
     322           0 :   return InnerImage()->Draw(aContext, size, region, aWhichFrame,
     323             :                             aSamplingFilter,
     324           0 :                             aSVGContext.map(orientViewport), aFlags,
     325           0 :                             aOpacity);
     326             : }
     327             : 
     328             : nsIntSize
     329           0 : OrientedImage::OptimalImageSizeForDest(const gfxSize& aDest,
     330             :                                        uint32_t aWhichFrame,
     331             :                                        SamplingFilter aSamplingFilter,
     332             :                                        uint32_t aFlags)
     333             : {
     334           0 :   if (!mOrientation.SwapsWidthAndHeight()) {
     335           0 :     return InnerImage()->OptimalImageSizeForDest(aDest, aWhichFrame,
     336           0 :                                                  aSamplingFilter, aFlags);
     337             :   }
     338             : 
     339             :   // Swap the size for the calculation, then swap it back for the caller.
     340           0 :   gfxSize destSize(aDest.height, aDest.width);
     341           0 :   nsIntSize innerImageSize(InnerImage()->OptimalImageSizeForDest(destSize,
     342             :                                                                  aWhichFrame,
     343             :                                                                  aSamplingFilter,
     344           0 :                                                                  aFlags));
     345           0 :   return nsIntSize(innerImageSize.height, innerImageSize.width);
     346             : }
     347             : 
     348             : NS_IMETHODIMP_(nsIntRect)
     349           0 : OrientedImage::GetImageSpaceInvalidationRect(const nsIntRect& aRect)
     350             : {
     351           0 :   nsIntRect rect(InnerImage()->GetImageSpaceInvalidationRect(aRect));
     352             : 
     353           0 :   if (mOrientation.IsIdentity()) {
     354           0 :     return rect;
     355             :   }
     356             : 
     357           0 :   nsIntSize innerSize;
     358           0 :   nsresult rv = InnerImage()->GetWidth(&innerSize.width);
     359           0 :   rv = NS_FAILED(rv) ? rv : InnerImage()->GetHeight(&innerSize.height);
     360           0 :   if (NS_FAILED(rv)) {
     361             :     // Fall back to identity if the width and height aren't available.
     362           0 :     return rect;
     363             :   }
     364             : 
     365             :   // Transform the invalidation rect into the correct orientation.
     366           0 :   gfxMatrix matrix(OrientationMatrix(innerSize));
     367           0 :   gfxRect invalidRect(matrix.TransformBounds(gfxRect(rect.x, rect.y,
     368           0 :                                                      rect.width, rect.height)));
     369             : 
     370           0 :   return IntRect::RoundOut(invalidRect.x, invalidRect.y,
     371           0 :                            invalidRect.width, invalidRect.height);
     372             : }
     373             : 
     374             : } // namespace image
     375             : } // namespace mozilla

Generated by: LCOV version 1.13