Bumping manifests a=b2g-bump
[gecko.git] / image / src / OrientedImage.cpp
blob7fea526ca6772b358faaecea06d0879759da0a76
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 <algorithm>
8 #include "gfx2DGlue.h"
9 #include "gfxDrawable.h"
10 #include "gfxPlatform.h"
11 #include "gfxUtils.h"
12 #include "ImageRegion.h"
13 #include "SVGImageContext.h"
15 #include "OrientedImage.h"
17 using std::swap;
19 namespace mozilla {
21 using namespace gfx;
22 using layers::LayerManager;
23 using layers::ImageContainer;
25 namespace image {
27 NS_IMPL_ISUPPORTS_INHERITED0(OrientedImage, ImageWrapper)
29 NS_IMETHODIMP
30 OrientedImage::GetWidth(int32_t* aWidth)
32 if (mOrientation.SwapsWidthAndHeight()) {
33 return InnerImage()->GetHeight(aWidth);
34 } else {
35 return InnerImage()->GetWidth(aWidth);
39 NS_IMETHODIMP
40 OrientedImage::GetHeight(int32_t* aHeight)
42 if (mOrientation.SwapsWidthAndHeight()) {
43 return InnerImage()->GetWidth(aHeight);
44 } else {
45 return InnerImage()->GetHeight(aHeight);
49 NS_IMETHODIMP
50 OrientedImage::GetIntrinsicSize(nsSize* aSize)
52 nsresult rv = InnerImage()->GetIntrinsicSize(aSize);
54 if (mOrientation.SwapsWidthAndHeight()) {
55 swap(aSize->width, aSize->height);
58 return rv;
61 NS_IMETHODIMP
62 OrientedImage::GetIntrinsicRatio(nsSize* aRatio)
64 nsresult rv = InnerImage()->GetIntrinsicRatio(aRatio);
66 if (mOrientation.SwapsWidthAndHeight()) {
67 swap(aRatio->width, aRatio->height);
70 return rv;
73 NS_IMETHODIMP_(TemporaryRef<SourceSurface>)
74 OrientedImage::GetFrame(uint32_t aWhichFrame,
75 uint32_t aFlags)
77 nsresult rv;
79 if (mOrientation.IsIdentity()) {
80 return InnerImage()->GetFrame(aWhichFrame, aFlags);
83 // Get the underlying dimensions.
84 gfxIntSize size;
85 rv = InnerImage()->GetWidth(&size.width);
86 NS_ENSURE_SUCCESS(rv, nullptr);
87 rv = InnerImage()->GetHeight(&size.height);
88 NS_ENSURE_SUCCESS(rv, nullptr);
90 // Determine an appropriate format for the surface.
91 gfx::SurfaceFormat surfaceFormat;
92 if (InnerImage()->IsOpaque()) {
93 surfaceFormat = gfx::SurfaceFormat::B8G8R8X8;
94 } else {
95 surfaceFormat = gfx::SurfaceFormat::B8G8R8A8;
98 // Create a surface to draw into.
99 RefPtr<DrawTarget> target =
100 gfxPlatform::GetPlatform()->
101 CreateOffscreenContentDrawTarget(ToIntSize(size), surfaceFormat);
102 if (!target) {
103 NS_ERROR("Could not create a DrawTarget");
104 return nullptr;
108 // Create our drawable.
109 RefPtr<SourceSurface> innerSurface =
110 InnerImage()->GetFrame(aWhichFrame, aFlags);
111 NS_ENSURE_TRUE(innerSurface, nullptr);
112 nsRefPtr<gfxDrawable> drawable =
113 new gfxSurfaceDrawable(innerSurface, size);
115 // Draw.
116 nsRefPtr<gfxContext> ctx = new gfxContext(target);
117 ctx->Multiply(OrientationMatrix(size));
118 gfxUtils::DrawPixelSnapped(ctx, drawable, size,
119 ImageRegion::Create(size),
120 surfaceFormat, GraphicsFilter::FILTER_FAST);
122 return target->Snapshot();
125 NS_IMETHODIMP
126 OrientedImage::GetImageContainer(LayerManager* aManager,
127 ImageContainer** _retval)
129 // XXX(seth): We currently don't have a way of orienting the result of
130 // GetImageContainer. We work around this by always returning null, but if it
131 // ever turns out that OrientedImage is widely used on codepaths that can
132 // actually benefit from GetImageContainer, it would be a good idea to fix
133 // that method for performance reasons.
135 if (mOrientation.IsIdentity()) {
136 return InnerImage()->GetImageContainer(aManager, _retval);
139 *_retval = nullptr;
140 return NS_OK;
143 struct MatrixBuilder
145 explicit MatrixBuilder(bool aInvert) : mInvert(aInvert) { }
147 gfxMatrix Build() { return mMatrix; }
149 void Scale(gfxFloat aX, gfxFloat aY)
151 if (mInvert) {
152 mMatrix *= gfxMatrix::Scaling(1.0 / aX, 1.0 / aY);
153 } else {
154 mMatrix.Scale(aX, aY);
158 void Rotate(gfxFloat aPhi)
160 if (mInvert) {
161 mMatrix *= gfxMatrix::Rotation(-aPhi);
162 } else {
163 mMatrix.Rotate(aPhi);
167 void Translate(gfxPoint aDelta)
169 if (mInvert) {
170 mMatrix *= gfxMatrix::Translation(-aDelta);
171 } else {
172 mMatrix.Translate(aDelta);
176 private:
177 gfxMatrix mMatrix;
178 bool mInvert;
182 * OrientationMatrix() computes a matrix that applies the rotation and
183 * reflection specified by mOrientation, or that matrix's inverse if aInvert is
184 * true.
186 * @param aSize The scaled size of the inner image. (When outside code specifies
187 * the scaled size, as with imgIContainer::Draw and its aSize
188 * parameter, it's necessary to swap the width and height if
189 * mOrientation.SwapsWidthAndHeight() is true.)
190 * @param aInvert If true, compute the inverse of the orientation matrix. Prefer
191 * this approach to OrientationMatrix(..).Invert(), because it's
192 * more numerically accurate.
194 gfxMatrix
195 OrientedImage::OrientationMatrix(const nsIntSize& aSize,
196 bool aInvert /* = false */)
198 MatrixBuilder builder(aInvert);
200 // Apply reflection, if present. (This logically happens second, but we
201 // apply it first because these transformations are all premultiplied.) A
202 // translation is necessary to place the image back in the first quadrant.
203 switch (mOrientation.flip) {
204 case Flip::Unflipped:
205 break;
206 case Flip::Horizontal:
207 if (mOrientation.SwapsWidthAndHeight()) {
208 builder.Translate(gfxPoint(aSize.height, 0));
209 } else {
210 builder.Translate(gfxPoint(aSize.width, 0));
212 builder.Scale(-1.0, 1.0);
213 break;
214 default:
215 MOZ_ASSERT(false, "Invalid flip value");
218 // Apply rotation, if present. Again, a translation is used to place the
219 // image back in the first quadrant.
220 switch (mOrientation.rotation) {
221 case Angle::D0:
222 break;
223 case Angle::D90:
224 builder.Translate(gfxPoint(aSize.height, 0));
225 builder.Rotate(-1.5 * M_PI);
226 break;
227 case Angle::D180:
228 builder.Translate(gfxPoint(aSize.width, aSize.height));
229 builder.Rotate(-1.0 * M_PI);
230 break;
231 case Angle::D270:
232 builder.Translate(gfxPoint(0, aSize.width));
233 builder.Rotate(-0.5 * M_PI);
234 break;
235 default:
236 MOZ_ASSERT(false, "Invalid rotation value");
239 return builder.Build();
242 static SVGImageContext
243 OrientViewport(const SVGImageContext& aOldContext,
244 const Orientation& aOrientation)
246 nsIntSize viewportSize(aOldContext.GetViewportSize());
247 if (aOrientation.SwapsWidthAndHeight()) {
248 swap(viewportSize.width, viewportSize.height);
250 return SVGImageContext(viewportSize,
251 aOldContext.GetPreserveAspectRatio());
254 NS_IMETHODIMP_(DrawResult)
255 OrientedImage::Draw(gfxContext* aContext,
256 const nsIntSize& aSize,
257 const ImageRegion& aRegion,
258 uint32_t aWhichFrame,
259 GraphicsFilter aFilter,
260 const Maybe<SVGImageContext>& aSVGContext,
261 uint32_t aFlags)
263 if (mOrientation.IsIdentity()) {
264 return InnerImage()->Draw(aContext, aSize, aRegion,
265 aWhichFrame, aFilter, aSVGContext, aFlags);
268 // Update the image size to match the image's coordinate system. (This could
269 // be done using TransformBounds but since it's only a size a swap is enough.)
270 nsIntSize size(aSize);
271 if (mOrientation.SwapsWidthAndHeight()) {
272 swap(size.width, size.height);
275 // Update the matrix so that we transform the image into the orientation
276 // expected by the caller before drawing.
277 gfxMatrix matrix(OrientationMatrix(size));
278 gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
279 aContext->Multiply(matrix);
281 // The region is already in the orientation expected by the caller, but we
282 // need it to be in the image's coordinate system, so we transform it using
283 // the inverse of the orientation matrix.
284 gfxMatrix inverseMatrix(OrientationMatrix(size, /* aInvert = */ true));
285 ImageRegion region(aRegion);
286 region.TransformBoundsBy(inverseMatrix);
288 return InnerImage()->Draw(aContext, size, region, aWhichFrame, aFilter,
289 aSVGContext.map(OrientViewport, mOrientation),
290 aFlags);
293 nsIntSize
294 OrientedImage::OptimalImageSizeForDest(const gfxSize& aDest,
295 uint32_t aWhichFrame,
296 GraphicsFilter aFilter, uint32_t aFlags)
298 if (!mOrientation.SwapsWidthAndHeight()) {
299 return InnerImage()->OptimalImageSizeForDest(aDest, aWhichFrame, aFilter,
300 aFlags);
303 // Swap the size for the calculation, then swap it back for the caller.
304 gfxSize destSize(aDest.height, aDest.width);
305 nsIntSize innerImageSize(InnerImage()->OptimalImageSizeForDest(destSize,
306 aWhichFrame,
307 aFilter,
308 aFlags));
309 return nsIntSize(innerImageSize.height, innerImageSize.width);
312 NS_IMETHODIMP_(nsIntRect)
313 OrientedImage::GetImageSpaceInvalidationRect(const nsIntRect& aRect)
315 nsIntRect rect(InnerImage()->GetImageSpaceInvalidationRect(aRect));
317 if (mOrientation.IsIdentity()) {
318 return rect;
321 nsIntSize innerSize;
322 nsresult rv = InnerImage()->GetWidth(&innerSize.width);
323 rv = NS_FAILED(rv) ? rv : InnerImage()->GetHeight(&innerSize.height);
324 if (NS_FAILED(rv)) {
325 // Fall back to identity if the width and height aren't available.
326 return rect;
329 // Transform the invalidation rect into the correct orientation.
330 gfxMatrix matrix(OrientationMatrix(innerSize, /* aInvert = */ true));
331 gfxRect invalidRect(matrix.TransformBounds(gfxRect(rect.x, rect.y,
332 rect.width, rect.height)));
333 invalidRect.RoundOut();
335 return nsIntRect(invalidRect.x, invalidRect.y,
336 invalidRect.width, invalidRect.height);
339 } // namespace image
340 } // namespace mozilla