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/. */
9 #include "gfxDrawable.h"
10 #include "gfxPlatform.h"
12 #include "ImageRegion.h"
13 #include "SVGImageContext.h"
15 #include "OrientedImage.h"
22 using layers::LayerManager
;
23 using layers::ImageContainer
;
27 NS_IMPL_ISUPPORTS_INHERITED0(OrientedImage
, ImageWrapper
)
30 OrientedImage::FrameRect(uint32_t aWhichFrame
)
34 // Retrieve the frame rect of the inner image.
35 nsIntRect innerRect
= InnerImage()->FrameRect(aWhichFrame
);
36 if (mOrientation
.IsIdentity()) {
40 // Get the underlying image's dimensions.
42 rv
= InnerImage()->GetWidth(&size
.width
);
43 NS_ENSURE_SUCCESS(rv
, innerRect
);
44 rv
= InnerImage()->GetHeight(&size
.height
);
45 NS_ENSURE_SUCCESS(rv
, innerRect
);
47 // Transform the frame rect.
48 gfxRect finalRect
= OrientationMatrix(size
).TransformBounds(innerRect
);
49 return nsIntRect(finalRect
.x
, finalRect
.y
, finalRect
.width
, finalRect
.height
);
53 OrientedImage::GetWidth(int32_t* aWidth
)
55 if (mOrientation
.SwapsWidthAndHeight()) {
56 return InnerImage()->GetHeight(aWidth
);
58 return InnerImage()->GetWidth(aWidth
);
63 OrientedImage::GetHeight(int32_t* aHeight
)
65 if (mOrientation
.SwapsWidthAndHeight()) {
66 return InnerImage()->GetWidth(aHeight
);
68 return InnerImage()->GetHeight(aHeight
);
73 OrientedImage::GetIntrinsicSize(nsSize
* aSize
)
75 nsresult rv
= InnerImage()->GetIntrinsicSize(aSize
);
77 if (mOrientation
.SwapsWidthAndHeight()) {
78 swap(aSize
->width
, aSize
->height
);
85 OrientedImage::GetIntrinsicRatio(nsSize
* aRatio
)
87 nsresult rv
= InnerImage()->GetIntrinsicRatio(aRatio
);
89 if (mOrientation
.SwapsWidthAndHeight()) {
90 swap(aRatio
->width
, aRatio
->height
);
96 NS_IMETHODIMP_(TemporaryRef
<SourceSurface
>)
97 OrientedImage::GetFrame(uint32_t aWhichFrame
,
102 if (mOrientation
.IsIdentity()) {
103 return InnerImage()->GetFrame(aWhichFrame
, aFlags
);
106 // Get the underlying dimensions.
108 rv
= InnerImage()->GetWidth(&size
.width
);
109 NS_ENSURE_SUCCESS(rv
, nullptr);
110 rv
= InnerImage()->GetHeight(&size
.height
);
111 NS_ENSURE_SUCCESS(rv
, nullptr);
113 // Determine an appropriate format for the surface.
114 gfx::SurfaceFormat surfaceFormat
;
115 if (InnerImage()->FrameIsOpaque(aWhichFrame
)) {
116 surfaceFormat
= gfx::SurfaceFormat::B8G8R8X8
;
118 surfaceFormat
= gfx::SurfaceFormat::B8G8R8A8
;
121 // Create a surface to draw into.
122 RefPtr
<DrawTarget
> target
=
123 gfxPlatform::GetPlatform()->
124 CreateOffscreenContentDrawTarget(ToIntSize(size
), surfaceFormat
);
126 NS_ERROR("Could not create a DrawTarget");
131 // Create our drawable.
132 RefPtr
<SourceSurface
> innerSurface
=
133 InnerImage()->GetFrame(aWhichFrame
, aFlags
);
134 NS_ENSURE_TRUE(innerSurface
, nullptr);
135 nsRefPtr
<gfxDrawable
> drawable
=
136 new gfxSurfaceDrawable(innerSurface
, size
);
139 nsRefPtr
<gfxContext
> ctx
= new gfxContext(target
);
140 ctx
->Multiply(OrientationMatrix(size
));
141 gfxUtils::DrawPixelSnapped(ctx
, drawable
, size
,
142 ImageRegion::Create(size
),
143 surfaceFormat
, GraphicsFilter::FILTER_FAST
);
145 return target
->Snapshot();
149 OrientedImage::GetImageContainer(LayerManager
* aManager
, ImageContainer
** _retval
)
151 // XXX(seth): We currently don't have a way of orienting the result of
152 // GetImageContainer. We work around this by always returning null, but if it
153 // ever turns out that OrientedImage is widely used on codepaths that can
154 // actually benefit from GetImageContainer, it would be a good idea to fix
155 // that method for performance reasons.
157 if (mOrientation
.IsIdentity()) {
158 return InnerImage()->GetImageContainer(aManager
, _retval
);
167 MatrixBuilder(bool aInvert
) : mInvert(aInvert
) { }
169 gfxMatrix
Build() { return mMatrix
; }
171 void Scale(gfxFloat aX
, gfxFloat aY
) {
173 mMatrix
*= gfxMatrix::Scaling(1.0 / aX
, 1.0 / aY
);
175 mMatrix
.Scale(aX
, aY
);
179 void Rotate(gfxFloat aPhi
) {
181 mMatrix
*= gfxMatrix::Rotation(-aPhi
);
183 mMatrix
.Rotate(aPhi
);
187 void Translate(gfxPoint aDelta
) {
189 mMatrix
*= gfxMatrix::Translation(-aDelta
);
191 mMatrix
.Translate(aDelta
);
201 * OrientationMatrix() computes a matrix that applies the rotation and
202 * reflection specified by mOrientation, or that matrix's inverse if aInvert is
205 * @param aSize The scaled size of the inner image. (When outside code specifies
206 * the scaled size, as with imgIContainer::Draw and its aSize
207 * parameter, it's necessary to swap the width and height if
208 * mOrientation.SwapsWidthAndHeight() is true.)
209 * @param aInvert If true, compute the inverse of the orientation matrix. Prefer
210 * this approach to OrientationMatrix(..).Invert(), because it's
211 * more numerically accurate.
214 OrientedImage::OrientationMatrix(const nsIntSize
& aSize
,
215 bool aInvert
/* = false */)
217 MatrixBuilder
builder(aInvert
);
219 // Apply reflection, if present. (This logically happens second, but we
220 // apply it first because these transformations are all premultiplied.) A
221 // translation is necessary to place the image back in the first quadrant.
222 switch (mOrientation
.flip
) {
223 case Flip::Unflipped
:
225 case Flip::Horizontal
:
226 if (mOrientation
.SwapsWidthAndHeight()) {
227 builder
.Translate(gfxPoint(aSize
.height
, 0));
229 builder
.Translate(gfxPoint(aSize
.width
, 0));
231 builder
.Scale(-1.0, 1.0);
234 MOZ_ASSERT(false, "Invalid flip value");
237 // Apply rotation, if present. Again, a translation is used to place the
238 // image back in the first quadrant.
239 switch (mOrientation
.rotation
) {
243 builder
.Translate(gfxPoint(aSize
.height
, 0));
244 builder
.Rotate(-1.5 * M_PI
);
247 builder
.Translate(gfxPoint(aSize
.width
, aSize
.height
));
248 builder
.Rotate(-1.0 * M_PI
);
251 builder
.Translate(gfxPoint(0, aSize
.width
));
252 builder
.Rotate(-0.5 * M_PI
);
255 MOZ_ASSERT(false, "Invalid rotation value");
258 return builder
.Build();
261 static SVGImageContext
262 OrientViewport(const SVGImageContext
& aOldContext
,
263 const Orientation
& aOrientation
)
265 nsIntSize
viewportSize(aOldContext
.GetViewportSize());
266 if (aOrientation
.SwapsWidthAndHeight()) {
267 swap(viewportSize
.width
, viewportSize
.height
);
269 return SVGImageContext(viewportSize
,
270 aOldContext
.GetPreserveAspectRatio());
274 OrientedImage::Draw(gfxContext
* aContext
,
275 const nsIntSize
& aSize
,
276 const ImageRegion
& aRegion
,
277 uint32_t aWhichFrame
,
278 GraphicsFilter aFilter
,
279 const Maybe
<SVGImageContext
>& aSVGContext
,
282 if (mOrientation
.IsIdentity()) {
283 return InnerImage()->Draw(aContext
, aSize
, aRegion
,
284 aWhichFrame
, aFilter
, aSVGContext
, aFlags
);
287 // Update the image size to match the image's coordinate system. (This could
288 // be done using TransformBounds but since it's only a size a swap is enough.)
289 nsIntSize
size(aSize
);
290 if (mOrientation
.SwapsWidthAndHeight()) {
291 swap(size
.width
, size
.height
);
294 // Update the matrix so that we transform the image into the orientation
295 // expected by the caller before drawing.
296 gfxMatrix
matrix(OrientationMatrix(size
));
297 gfxContextMatrixAutoSaveRestore
saveMatrix(aContext
);
298 aContext
->Multiply(matrix
);
300 // The region is already in the orientation expected by the caller, but we
301 // need it to be in the image's coordinate system, so we transform it using
302 // the inverse of the orientation matrix.
303 gfxMatrix
inverseMatrix(OrientationMatrix(size
, /* aInvert = */ true));
304 ImageRegion
region(aRegion
);
305 region
.TransformBoundsBy(inverseMatrix
);
307 return InnerImage()->Draw(aContext
, size
, region
, aWhichFrame
, aFilter
,
308 aSVGContext
.map(OrientViewport
, mOrientation
),
313 OrientedImage::OptimalImageSizeForDest(const gfxSize
& aDest
, uint32_t aWhichFrame
,
314 GraphicsFilter aFilter
, uint32_t aFlags
)
316 if (!mOrientation
.SwapsWidthAndHeight()) {
317 return InnerImage()->OptimalImageSizeForDest(aDest
, aWhichFrame
, aFilter
, aFlags
);
320 // Swap the size for the calculation, then swap it back for the caller.
321 gfxSize
destSize(aDest
.height
, aDest
.width
);
322 nsIntSize
innerImageSize(InnerImage()->OptimalImageSizeForDest(destSize
,
326 return nsIntSize(innerImageSize
.height
, innerImageSize
.width
);
329 NS_IMETHODIMP_(nsIntRect
)
330 OrientedImage::GetImageSpaceInvalidationRect(const nsIntRect
& aRect
)
332 nsIntRect
rect(InnerImage()->GetImageSpaceInvalidationRect(aRect
));
334 if (mOrientation
.IsIdentity()) {
339 nsresult rv
= InnerImage()->GetWidth(&innerSize
.width
);
340 rv
= NS_FAILED(rv
) ? rv
: InnerImage()->GetHeight(&innerSize
.height
);
342 // Fall back to identity if the width and height aren't available.
346 // Transform the invalidation rect into the correct orientation.
347 gfxMatrix
matrix(OrientationMatrix(innerSize
, /* aInvert = */ true));
348 gfxRect
invalidRect(matrix
.TransformBounds(gfxRect(rect
.x
, rect
.y
,
349 rect
.width
, rect
.height
)));
350 invalidRect
.RoundOut();
352 return nsIntRect(invalidRect
.x
, invalidRect
.y
,
353 invalidRect
.width
, invalidRect
.height
);
357 } // namespace mozilla