Bumping manifests a=b2g-bump
[gecko.git] / image / src / OrientedImage.cpp
blobbec3a43fb1c4554fd5a29e21fbf488a728454c66
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 nsIntRect
30 OrientedImage::FrameRect(uint32_t aWhichFrame)
32 nsresult rv;
34 // Retrieve the frame rect of the inner image.
35 nsIntRect innerRect = InnerImage()->FrameRect(aWhichFrame);
36 if (mOrientation.IsIdentity()) {
37 return innerRect;
40 // Get the underlying image's dimensions.
41 nsIntSize size;
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);
52 NS_IMETHODIMP
53 OrientedImage::GetWidth(int32_t* aWidth)
55 if (mOrientation.SwapsWidthAndHeight()) {
56 return InnerImage()->GetHeight(aWidth);
57 } else {
58 return InnerImage()->GetWidth(aWidth);
62 NS_IMETHODIMP
63 OrientedImage::GetHeight(int32_t* aHeight)
65 if (mOrientation.SwapsWidthAndHeight()) {
66 return InnerImage()->GetWidth(aHeight);
67 } else {
68 return InnerImage()->GetHeight(aHeight);
72 NS_IMETHODIMP
73 OrientedImage::GetIntrinsicSize(nsSize* aSize)
75 nsresult rv = InnerImage()->GetIntrinsicSize(aSize);
77 if (mOrientation.SwapsWidthAndHeight()) {
78 swap(aSize->width, aSize->height);
81 return rv;
84 NS_IMETHODIMP
85 OrientedImage::GetIntrinsicRatio(nsSize* aRatio)
87 nsresult rv = InnerImage()->GetIntrinsicRatio(aRatio);
89 if (mOrientation.SwapsWidthAndHeight()) {
90 swap(aRatio->width, aRatio->height);
93 return rv;
96 NS_IMETHODIMP_(TemporaryRef<SourceSurface>)
97 OrientedImage::GetFrame(uint32_t aWhichFrame,
98 uint32_t aFlags)
100 nsresult rv;
102 if (mOrientation.IsIdentity()) {
103 return InnerImage()->GetFrame(aWhichFrame, aFlags);
106 // Get the underlying dimensions.
107 gfxIntSize size;
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;
117 } else {
118 surfaceFormat = gfx::SurfaceFormat::B8G8R8A8;
121 // Create a surface to draw into.
122 RefPtr<DrawTarget> target =
123 gfxPlatform::GetPlatform()->
124 CreateOffscreenContentDrawTarget(ToIntSize(size), surfaceFormat);
125 if (!target) {
126 NS_ERROR("Could not create a DrawTarget");
127 return nullptr;
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);
138 // Draw.
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();
148 NS_IMETHODIMP
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);
161 *_retval = nullptr;
162 return NS_OK;
165 struct MatrixBuilder
167 MatrixBuilder(bool aInvert) : mInvert(aInvert) { }
169 gfxMatrix Build() { return mMatrix; }
171 void Scale(gfxFloat aX, gfxFloat aY) {
172 if (mInvert) {
173 mMatrix *= gfxMatrix::Scaling(1.0 / aX, 1.0 / aY);
174 } else {
175 mMatrix.Scale(aX, aY);
179 void Rotate(gfxFloat aPhi) {
180 if (mInvert) {
181 mMatrix *= gfxMatrix::Rotation(-aPhi);
182 } else {
183 mMatrix.Rotate(aPhi);
187 void Translate(gfxPoint aDelta) {
188 if (mInvert) {
189 mMatrix *= gfxMatrix::Translation(-aDelta);
190 } else {
191 mMatrix.Translate(aDelta);
195 private:
196 gfxMatrix mMatrix;
197 bool mInvert;
201 * OrientationMatrix() computes a matrix that applies the rotation and
202 * reflection specified by mOrientation, or that matrix's inverse if aInvert is
203 * true.
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.
213 gfxMatrix
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:
224 break;
225 case Flip::Horizontal:
226 if (mOrientation.SwapsWidthAndHeight()) {
227 builder.Translate(gfxPoint(aSize.height, 0));
228 } else {
229 builder.Translate(gfxPoint(aSize.width, 0));
231 builder.Scale(-1.0, 1.0);
232 break;
233 default:
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) {
240 case Angle::D0:
241 break;
242 case Angle::D90:
243 builder.Translate(gfxPoint(aSize.height, 0));
244 builder.Rotate(-1.5 * M_PI);
245 break;
246 case Angle::D180:
247 builder.Translate(gfxPoint(aSize.width, aSize.height));
248 builder.Rotate(-1.0 * M_PI);
249 break;
250 case Angle::D270:
251 builder.Translate(gfxPoint(0, aSize.width));
252 builder.Rotate(-0.5 * M_PI);
253 break;
254 default:
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());
273 NS_IMETHODIMP
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,
280 uint32_t aFlags)
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),
309 aFlags);
312 nsIntSize
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,
323 aWhichFrame,
324 aFilter,
325 aFlags));
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()) {
335 return rect;
338 nsIntSize innerSize;
339 nsresult rv = InnerImage()->GetWidth(&innerSize.width);
340 rv = NS_FAILED(rv) ? rv : InnerImage()->GetHeight(&innerSize.height);
341 if (NS_FAILED(rv)) {
342 // Fall back to identity if the width and height aren't available.
343 return rect;
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);
356 } // namespace image
357 } // namespace mozilla