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/. */
6 #include "OrientedImage.h"
10 #include "gfx2DGlue.h"
11 #include "gfxDrawable.h"
12 #include "gfxPlatform.h"
14 #include "ImageRegion.h"
15 #include "SVGImageContext.h"
22 using layers::ImageContainer
;
23 using layers::LayerManager
;
28 OrientedImage::GetWidth(int32_t* aWidth
) {
29 if (mOrientation
.SwapsWidthAndHeight()) {
30 return InnerImage()->GetHeight(aWidth
);
32 return InnerImage()->GetWidth(aWidth
);
37 OrientedImage::GetHeight(int32_t* aHeight
) {
38 if (mOrientation
.SwapsWidthAndHeight()) {
39 return InnerImage()->GetWidth(aHeight
);
41 return InnerImage()->GetHeight(aHeight
);
45 nsresult
OrientedImage::GetNativeSizes(nsTArray
<IntSize
>& aNativeSizes
) const {
46 nsresult rv
= InnerImage()->GetNativeSizes(aNativeSizes
);
48 if (mOrientation
.SwapsWidthAndHeight()) {
49 auto i
= aNativeSizes
.Length();
52 swap(aNativeSizes
[i
].width
, aNativeSizes
[i
].height
);
60 OrientedImage::GetIntrinsicSize(nsSize
* aSize
) {
61 nsresult rv
= InnerImage()->GetIntrinsicSize(aSize
);
63 if (mOrientation
.SwapsWidthAndHeight()) {
64 swap(aSize
->width
, aSize
->height
);
71 OrientedImage::GetIntrinsicRatio(nsSize
* aRatio
) {
72 nsresult rv
= InnerImage()->GetIntrinsicRatio(aRatio
);
74 if (mOrientation
.SwapsWidthAndHeight()) {
75 swap(aRatio
->width
, aRatio
->height
);
81 NS_IMETHODIMP_(already_AddRefed
<SourceSurface
>)
82 OrientedImage::GetFrame(uint32_t aWhichFrame
, uint32_t aFlags
) {
85 if (mOrientation
.IsIdentity()) {
86 return InnerImage()->GetFrame(aWhichFrame
, aFlags
);
89 // Get the underlying dimensions.
91 rv
= InnerImage()->GetWidth(&size
.width
);
92 NS_ENSURE_SUCCESS(rv
, nullptr);
93 rv
= InnerImage()->GetHeight(&size
.height
);
94 NS_ENSURE_SUCCESS(rv
, nullptr);
96 // Determine an appropriate format for the surface.
97 gfx::SurfaceFormat surfaceFormat
;
98 if (InnerImage()->WillDrawOpaqueNow()) {
99 surfaceFormat
= gfx::SurfaceFormat::B8G8R8X8
;
101 surfaceFormat
= gfx::SurfaceFormat::B8G8R8A8
;
104 // Create a surface to draw into.
105 RefPtr
<DrawTarget
> target
=
106 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
107 size
, surfaceFormat
);
108 if (!target
|| !target
->IsValid()) {
109 NS_ERROR("Could not create a DrawTarget");
113 // Create our drawable.
114 RefPtr
<SourceSurface
> innerSurface
=
115 InnerImage()->GetFrame(aWhichFrame
, aFlags
);
116 NS_ENSURE_TRUE(innerSurface
, nullptr);
117 RefPtr
<gfxDrawable
> drawable
= new gfxSurfaceDrawable(innerSurface
, size
);
120 RefPtr
<gfxContext
> ctx
= gfxContext::CreateOrNull(target
);
121 MOZ_ASSERT(ctx
); // already checked the draw target above
122 ctx
->Multiply(OrientationMatrix(size
));
123 gfxUtils::DrawPixelSnapped(ctx
, drawable
, SizeDouble(size
),
124 ImageRegion::Create(size
), surfaceFormat
,
125 SamplingFilter::LINEAR
);
127 return target
->Snapshot();
130 NS_IMETHODIMP_(already_AddRefed
<SourceSurface
>)
131 OrientedImage::GetFrameAtSize(const IntSize
& aSize
, uint32_t aWhichFrame
,
133 // XXX(seth): It'd be nice to support downscale-during-decode for this case,
134 // but right now we just fall back to the intrinsic size.
135 return GetFrame(aWhichFrame
, aFlags
);
139 OrientedImage::IsImageContainerAvailable(LayerManager
* aManager
,
141 if (mOrientation
.IsIdentity()) {
142 return InnerImage()->IsImageContainerAvailable(aManager
, aFlags
);
147 NS_IMETHODIMP_(already_AddRefed
<ImageContainer
>)
148 OrientedImage::GetImageContainer(LayerManager
* aManager
, uint32_t aFlags
) {
149 // XXX(seth): We currently don't have a way of orienting the result of
150 // GetImageContainer. We work around this by always returning null, but if it
151 // ever turns out that OrientedImage is widely used on codepaths that can
152 // actually benefit from GetImageContainer, it would be a good idea to fix
153 // that method for performance reasons.
155 if (mOrientation
.IsIdentity()) {
156 return InnerImage()->GetImageContainer(aManager
, aFlags
);
163 OrientedImage::IsImageContainerAvailableAtSize(LayerManager
* aManager
,
164 const IntSize
& aSize
,
166 if (mOrientation
.IsIdentity()) {
167 return InnerImage()->IsImageContainerAvailableAtSize(aManager
, aSize
,
173 NS_IMETHODIMP_(ImgDrawResult
)
174 OrientedImage::GetImageContainerAtSize(
175 layers::LayerManager
* aManager
, const gfx::IntSize
& aSize
,
176 const Maybe
<SVGImageContext
>& aSVGContext
, uint32_t aFlags
,
177 layers::ImageContainer
** aOutContainer
) {
178 // XXX(seth): We currently don't have a way of orienting the result of
179 // GetImageContainer. We work around this by always returning null, but if it
180 // ever turns out that OrientedImage is widely used on codepaths that can
181 // actually benefit from GetImageContainer, it would be a good idea to fix
182 // that method for performance reasons.
184 if (mOrientation
.IsIdentity()) {
185 return InnerImage()->GetImageContainerAtSize(aManager
, aSize
, aSVGContext
,
186 aFlags
, aOutContainer
);
189 return ImgDrawResult::NOT_SUPPORTED
;
192 struct MatrixBuilder
{
193 explicit MatrixBuilder(bool aInvert
) : mInvert(aInvert
) {}
195 gfxMatrix
Build() { return mMatrix
; }
197 void Scale(gfxFloat aX
, gfxFloat aY
) {
199 mMatrix
*= gfxMatrix::Scaling(1.0 / aX
, 1.0 / aY
);
201 mMatrix
.PreScale(aX
, aY
);
205 void Rotate(gfxFloat aPhi
) {
207 mMatrix
*= gfxMatrix::Rotation(-aPhi
);
209 mMatrix
.PreRotate(aPhi
);
213 void Translate(gfxPoint aDelta
) {
215 mMatrix
*= gfxMatrix::Translation(-aDelta
);
217 mMatrix
.PreTranslate(aDelta
);
227 * OrientationMatrix() computes a matrix that applies the rotation and
228 * reflection specified by mOrientation, or that matrix's inverse if aInvert is
231 * @param aSize The scaled size of the inner image. (When outside code specifies
232 * the scaled size, as with imgIContainer::Draw and its aSize
233 * parameter, it's necessary to swap the width and height if
234 * mOrientation.SwapsWidthAndHeight() is true.)
235 * @param aInvert If true, compute the inverse of the orientation matrix. Prefer
236 * this approach to OrientationMatrix(..).Invert(), because it's
237 * more numerically accurate.
239 gfxMatrix
OrientedImage::OrientationMatrix(const nsIntSize
& aSize
,
240 bool aInvert
/* = false */) {
241 MatrixBuilder
builder(aInvert
);
243 // Apply reflection, if present. (This logically happens second, but we
244 // apply it first because these transformations are all premultiplied.) A
245 // translation is necessary to place the image back in the first quadrant.
246 switch (mOrientation
.flip
) {
247 case Flip::Unflipped
:
249 case Flip::Horizontal
:
250 if (mOrientation
.SwapsWidthAndHeight()) {
251 builder
.Translate(gfxPoint(aSize
.height
, 0));
253 builder
.Translate(gfxPoint(aSize
.width
, 0));
255 builder
.Scale(-1.0, 1.0);
258 MOZ_ASSERT(false, "Invalid flip value");
261 // Apply rotation, if present. Again, a translation is used to place the
262 // image back in the first quadrant.
263 switch (mOrientation
.rotation
) {
267 builder
.Translate(gfxPoint(aSize
.height
, 0));
268 builder
.Rotate(-1.5 * M_PI
);
271 builder
.Translate(gfxPoint(aSize
.width
, aSize
.height
));
272 builder
.Rotate(-1.0 * M_PI
);
275 builder
.Translate(gfxPoint(0, aSize
.width
));
276 builder
.Rotate(-0.5 * M_PI
);
279 MOZ_ASSERT(false, "Invalid rotation value");
282 return builder
.Build();
285 NS_IMETHODIMP_(ImgDrawResult
)
286 OrientedImage::Draw(gfxContext
* aContext
, const nsIntSize
& aSize
,
287 const ImageRegion
& aRegion
, uint32_t aWhichFrame
,
288 SamplingFilter aSamplingFilter
,
289 const Maybe
<SVGImageContext
>& aSVGContext
, uint32_t aFlags
,
291 if (mOrientation
.IsIdentity()) {
292 return InnerImage()->Draw(aContext
, aSize
, aRegion
, aWhichFrame
,
293 aSamplingFilter
, aSVGContext
, aFlags
, aOpacity
);
296 // Update the image size to match the image's coordinate system. (This could
297 // be done using TransformBounds but since it's only a size a swap is enough.)
298 nsIntSize
size(aSize
);
299 if (mOrientation
.SwapsWidthAndHeight()) {
300 swap(size
.width
, size
.height
);
303 // Update the matrix so that we transform the image into the orientation
304 // expected by the caller before drawing.
305 gfxMatrix
matrix(OrientationMatrix(size
));
306 gfxContextMatrixAutoSaveRestore
saveMatrix(aContext
);
307 aContext
->Multiply(matrix
);
309 // The region is already in the orientation expected by the caller, but we
310 // need it to be in the image's coordinate system, so we transform it using
311 // the inverse of the orientation matrix.
312 gfxMatrix
inverseMatrix(OrientationMatrix(size
, /* aInvert = */ true));
313 ImageRegion
region(aRegion
);
314 region
.TransformBoundsBy(inverseMatrix
);
316 auto orientViewport
= [&](const SVGImageContext
& aOldContext
) {
317 SVGImageContext
context(aOldContext
);
318 auto oldViewport
= aOldContext
.GetViewportSize();
319 if (oldViewport
&& mOrientation
.SwapsWidthAndHeight()) {
320 // Swap width and height:
321 CSSIntSize
newViewport(oldViewport
->height
, oldViewport
->width
);
322 context
.SetViewportSize(Some(newViewport
));
327 return InnerImage()->Draw(aContext
, size
, region
, aWhichFrame
,
328 aSamplingFilter
, aSVGContext
.map(orientViewport
),
332 nsIntSize
OrientedImage::OptimalImageSizeForDest(const gfxSize
& aDest
,
333 uint32_t aWhichFrame
,
334 SamplingFilter aSamplingFilter
,
336 if (!mOrientation
.SwapsWidthAndHeight()) {
337 return InnerImage()->OptimalImageSizeForDest(aDest
, aWhichFrame
,
338 aSamplingFilter
, aFlags
);
341 // Swap the size for the calculation, then swap it back for the caller.
342 gfxSize
destSize(aDest
.height
, aDest
.width
);
343 nsIntSize
innerImageSize(InnerImage()->OptimalImageSizeForDest(
344 destSize
, aWhichFrame
, aSamplingFilter
, aFlags
));
345 return nsIntSize(innerImageSize
.height
, innerImageSize
.width
);
348 NS_IMETHODIMP_(nsIntRect
)
349 OrientedImage::GetImageSpaceInvalidationRect(const nsIntRect
& aRect
) {
350 nsIntRect
rect(InnerImage()->GetImageSpaceInvalidationRect(aRect
));
352 if (mOrientation
.IsIdentity()) {
357 nsresult rv
= InnerImage()->GetWidth(&innerSize
.width
);
358 rv
= NS_FAILED(rv
) ? rv
: InnerImage()->GetHeight(&innerSize
.height
);
360 // Fall back to identity if the width and height aren't available.
364 // Transform the invalidation rect into the correct orientation.
365 gfxMatrix
matrix(OrientationMatrix(innerSize
));
366 gfxRect
invalidRect(matrix
.TransformBounds(
367 gfxRect(rect
.X(), rect
.Y(), rect
.Width(), rect
.Height())));
369 return IntRect::RoundOut(invalidRect
.X(), invalidRect
.Y(),
370 invalidRect
.Width(), invalidRect
.Height());
374 } // namespace mozilla