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 "gfxContext.h"
12 #include "gfxDrawable.h"
13 #include "gfxPlatform.h"
15 #include "ImageRegion.h"
16 #include "mozilla/SVGImageContext.h"
23 using layers::ImageContainer
;
24 using layers::LayerManager
;
29 OrientedImage::GetWidth(int32_t* aWidth
) {
30 if (mOrientation
.SwapsWidthAndHeight()) {
31 return InnerImage()->GetHeight(aWidth
);
33 return InnerImage()->GetWidth(aWidth
);
38 OrientedImage::GetHeight(int32_t* aHeight
) {
39 if (mOrientation
.SwapsWidthAndHeight()) {
40 return InnerImage()->GetWidth(aHeight
);
42 return InnerImage()->GetHeight(aHeight
);
46 nsresult
OrientedImage::GetNativeSizes(nsTArray
<IntSize
>& aNativeSizes
) const {
47 nsresult rv
= InnerImage()->GetNativeSizes(aNativeSizes
);
49 if (mOrientation
.SwapsWidthAndHeight()) {
50 auto i
= aNativeSizes
.Length();
53 swap(aNativeSizes
[i
].width
, aNativeSizes
[i
].height
);
61 OrientedImage::GetIntrinsicSize(nsSize
* aSize
) {
62 nsresult rv
= InnerImage()->GetIntrinsicSize(aSize
);
64 if (mOrientation
.SwapsWidthAndHeight()) {
65 swap(aSize
->width
, aSize
->height
);
71 Maybe
<AspectRatio
> OrientedImage::GetIntrinsicRatio() {
72 Maybe
<AspectRatio
> ratio
= InnerImage()->GetIntrinsicRatio();
73 if (ratio
&& mOrientation
.SwapsWidthAndHeight()) {
74 ratio
= Some(ratio
->Inverted());
79 already_AddRefed
<SourceSurface
> OrientedImage::OrientSurface(
80 Orientation aOrientation
, SourceSurface
* aSurface
) {
83 // If the image does not require any re-orientation, return aSurface itself.
84 if (aOrientation
.IsIdentity()) {
85 return do_AddRef(aSurface
);
88 // Determine the size of the new surface.
89 nsIntSize originalSize
= aSurface
->GetSize();
90 nsIntSize targetSize
= originalSize
;
91 if (aOrientation
.SwapsWidthAndHeight()) {
92 swap(targetSize
.width
, targetSize
.height
);
95 // Create our drawable.
96 RefPtr
<gfxDrawable
> drawable
= new gfxSurfaceDrawable(aSurface
, originalSize
);
98 // Determine an appropriate format for the surface.
99 gfx::SurfaceFormat surfaceFormat
= IsOpaque(aSurface
->GetFormat())
100 ? gfx::SurfaceFormat::OS_RGBX
101 : gfx::SurfaceFormat::OS_RGBA
;
103 // Create the new surface to draw into.
104 RefPtr
<DrawTarget
> target
=
105 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
106 targetSize
, surfaceFormat
);
107 if (!target
|| !target
->IsValid()) {
108 NS_ERROR("Could not create a DrawTarget");
113 RefPtr
<gfxContext
> ctx
= gfxContext::CreateOrNull(target
);
114 MOZ_ASSERT(ctx
); // already checked the draw target above
115 ctx
->Multiply(OrientationMatrix(aOrientation
, originalSize
));
116 gfxUtils::DrawPixelSnapped(ctx
, drawable
, SizeDouble(originalSize
),
117 ImageRegion::Create(originalSize
), surfaceFormat
,
118 SamplingFilter::LINEAR
);
120 return target
->Snapshot();
123 NS_IMETHODIMP_(already_AddRefed
<SourceSurface
>)
124 OrientedImage::GetFrame(uint32_t aWhichFrame
, uint32_t aFlags
) {
125 // Get a SourceSurface for the inner image then orient it according to
127 RefPtr
<SourceSurface
> innerSurface
=
128 InnerImage()->GetFrame(aWhichFrame
, aFlags
);
129 NS_ENSURE_TRUE(innerSurface
, nullptr);
131 return OrientSurface(mOrientation
, innerSurface
);
134 NS_IMETHODIMP_(already_AddRefed
<SourceSurface
>)
135 OrientedImage::GetFrameAtSize(const IntSize
& aSize
, uint32_t aWhichFrame
,
137 // Get a SourceSurface for the inner image then orient it according to
139 IntSize innerSize
= aSize
;
140 if (mOrientation
.SwapsWidthAndHeight()) {
141 swap(innerSize
.width
, innerSize
.height
);
143 RefPtr
<SourceSurface
> innerSurface
=
144 InnerImage()->GetFrameAtSize(innerSize
, aWhichFrame
, aFlags
);
145 NS_ENSURE_TRUE(innerSurface
, nullptr);
147 return OrientSurface(mOrientation
, innerSurface
);
151 OrientedImage::IsImageContainerAvailable(LayerManager
* aManager
,
153 if (mOrientation
.IsIdentity()) {
154 return InnerImage()->IsImageContainerAvailable(aManager
, aFlags
);
159 NS_IMETHODIMP_(already_AddRefed
<ImageContainer
>)
160 OrientedImage::GetImageContainer(LayerManager
* aManager
, uint32_t aFlags
) {
161 // XXX(seth): We currently don't have a way of orienting the result of
162 // GetImageContainer. We work around this by always returning null, but if it
163 // ever turns out that OrientedImage is widely used on codepaths that can
164 // actually benefit from GetImageContainer, it would be a good idea to fix
165 // that method for performance reasons.
167 if (mOrientation
.IsIdentity()) {
168 return InnerImage()->GetImageContainer(aManager
, aFlags
);
175 OrientedImage::IsImageContainerAvailableAtSize(LayerManager
* aManager
,
176 const IntSize
& aSize
,
178 if (mOrientation
.IsIdentity()) {
179 return InnerImage()->IsImageContainerAvailableAtSize(aManager
, aSize
,
185 NS_IMETHODIMP_(ImgDrawResult
)
186 OrientedImage::GetImageContainerAtSize(
187 layers::LayerManager
* aManager
, const gfx::IntSize
& aSize
,
188 const Maybe
<SVGImageContext
>& aSVGContext
, uint32_t aFlags
,
189 layers::ImageContainer
** aOutContainer
) {
190 // XXX(seth): We currently don't have a way of orienting the result of
191 // GetImageContainer. We work around this by always returning null, but if it
192 // ever turns out that OrientedImage is widely used on codepaths that can
193 // actually benefit from GetImageContainer, it would be a good idea to fix
194 // that method for performance reasons.
196 if (mOrientation
.IsIdentity()) {
197 return InnerImage()->GetImageContainerAtSize(aManager
, aSize
, aSVGContext
,
198 aFlags
, aOutContainer
);
201 return ImgDrawResult::NOT_SUPPORTED
;
204 struct MatrixBuilder
{
205 explicit MatrixBuilder(bool aInvert
) : mInvert(aInvert
) {}
207 gfxMatrix
Build() { return mMatrix
; }
209 void Scale(gfxFloat aX
, gfxFloat aY
) {
211 mMatrix
*= gfxMatrix::Scaling(1.0 / aX
, 1.0 / aY
);
213 mMatrix
.PreScale(aX
, aY
);
217 void Rotate(gfxFloat aPhi
) {
219 mMatrix
*= gfxMatrix::Rotation(-aPhi
);
221 mMatrix
.PreRotate(aPhi
);
225 void Translate(gfxPoint aDelta
) {
227 mMatrix
*= gfxMatrix::Translation(-aDelta
);
229 mMatrix
.PreTranslate(aDelta
);
238 gfxMatrix
OrientedImage::OrientationMatrix(Orientation aOrientation
,
239 const nsIntSize
& aSize
,
240 bool aInvert
/* = false */) {
241 MatrixBuilder
builder(aInvert
);
243 // Apply reflection, if present. (For a regular, non-flipFirst reflection,
244 // this logically happens second, but we apply it first because these
245 // transformations are all premultiplied.) A translation is necessary to place
246 // the image back in the first quadrant.
247 if (aOrientation
.flip
== Flip::Horizontal
&& !aOrientation
.flipFirst
) {
248 if (aOrientation
.SwapsWidthAndHeight()) {
249 builder
.Translate(gfxPoint(aSize
.height
, 0));
251 builder
.Translate(gfxPoint(aSize
.width
, 0));
253 builder
.Scale(-1.0, 1.0);
256 // Apply rotation, if present. Again, a translation is used to place the
257 // image back in the first quadrant.
258 switch (aOrientation
.rotation
) {
262 builder
.Translate(gfxPoint(aSize
.height
, 0));
263 builder
.Rotate(-1.5 * M_PI
);
266 builder
.Translate(gfxPoint(aSize
.width
, aSize
.height
));
267 builder
.Rotate(-1.0 * M_PI
);
270 builder
.Translate(gfxPoint(0, aSize
.width
));
271 builder
.Rotate(-0.5 * M_PI
);
274 MOZ_ASSERT(false, "Invalid rotation value");
277 // Apply a flipFirst reflection.
278 if (aOrientation
.flip
== Flip::Horizontal
&& aOrientation
.flipFirst
) {
279 builder
.Translate(gfxPoint(aSize
.width
, 0.0));
280 builder
.Scale(-1.0, 1.0);
283 return builder
.Build();
286 NS_IMETHODIMP_(ImgDrawResult
)
287 OrientedImage::Draw(gfxContext
* aContext
, const nsIntSize
& aSize
,
288 const ImageRegion
& aRegion
, uint32_t aWhichFrame
,
289 SamplingFilter aSamplingFilter
,
290 const Maybe
<SVGImageContext
>& aSVGContext
, uint32_t aFlags
,
292 if (mOrientation
.IsIdentity()) {
293 return InnerImage()->Draw(aContext
, aSize
, aRegion
, aWhichFrame
,
294 aSamplingFilter
, aSVGContext
, aFlags
, aOpacity
);
297 // Update the image size to match the image's coordinate system. (This could
298 // be done using TransformBounds but since it's only a size a swap is enough.)
299 nsIntSize
size(aSize
);
300 if (mOrientation
.SwapsWidthAndHeight()) {
301 swap(size
.width
, size
.height
);
304 // Update the matrix so that we transform the image into the orientation
305 // expected by the caller before drawing.
306 gfxMatrix
matrix(OrientationMatrix(size
));
307 gfxContextMatrixAutoSaveRestore
saveMatrix(aContext
);
308 aContext
->Multiply(matrix
);
310 // The region is already in the orientation expected by the caller, but we
311 // need it to be in the image's coordinate system, so we transform it using
312 // the inverse of the orientation matrix.
313 gfxMatrix
inverseMatrix(OrientationMatrix(size
, /* aInvert = */ true));
314 ImageRegion
region(aRegion
);
315 region
.TransformBoundsBy(inverseMatrix
);
317 auto orientViewport
= [&](const SVGImageContext
& aOldContext
) {
318 SVGImageContext
context(aOldContext
);
319 auto oldViewport
= aOldContext
.GetViewportSize();
320 if (oldViewport
&& mOrientation
.SwapsWidthAndHeight()) {
321 // Swap width and height:
322 CSSIntSize
newViewport(oldViewport
->height
, oldViewport
->width
);
323 context
.SetViewportSize(Some(newViewport
));
328 return InnerImage()->Draw(aContext
, size
, region
, aWhichFrame
,
329 aSamplingFilter
, aSVGContext
.map(orientViewport
),
333 nsIntSize
OrientedImage::OptimalImageSizeForDest(const gfxSize
& aDest
,
334 uint32_t aWhichFrame
,
335 SamplingFilter aSamplingFilter
,
337 if (!mOrientation
.SwapsWidthAndHeight()) {
338 return InnerImage()->OptimalImageSizeForDest(aDest
, aWhichFrame
,
339 aSamplingFilter
, aFlags
);
342 // Swap the size for the calculation, then swap it back for the caller.
343 gfxSize
destSize(aDest
.height
, aDest
.width
);
344 nsIntSize
innerImageSize(InnerImage()->OptimalImageSizeForDest(
345 destSize
, aWhichFrame
, aSamplingFilter
, aFlags
));
346 return nsIntSize(innerImageSize
.height
, innerImageSize
.width
);
349 NS_IMETHODIMP_(nsIntRect
)
350 OrientedImage::GetImageSpaceInvalidationRect(const nsIntRect
& aRect
) {
351 nsIntRect
rect(InnerImage()->GetImageSpaceInvalidationRect(aRect
));
353 if (mOrientation
.IsIdentity()) {
358 nsresult rv
= InnerImage()->GetWidth(&innerSize
.width
);
359 rv
= NS_FAILED(rv
) ? rv
: InnerImage()->GetHeight(&innerSize
.height
);
361 // Fall back to identity if the width and height aren't available.
365 // Transform the invalidation rect into the correct orientation.
366 gfxMatrix
matrix(OrientationMatrix(innerSize
));
367 gfxRect
invalidRect(matrix
.TransformBounds(
368 gfxRect(rect
.X(), rect
.Y(), rect
.Width(), rect
.Height())));
370 return IntRect::RoundOut(invalidRect
.X(), invalidRect
.Y(),
371 invalidRect
.Width(), invalidRect
.Height());
375 } // namespace mozilla