Backed out changeset 177eae915693 (bug 1206581) for bustage
[gecko.git] / image / OrientedImage.cpp
blob91bc0abef7e6fd63740c78545ea4642da53f5091
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_(already_AddRefed<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 IntSize 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(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 RefPtr<gfxDrawable> drawable =
113 new gfxSurfaceDrawable(innerSurface, size);
115 // Draw.
116 RefPtr<gfxContext> ctx = new gfxContext(target);
117 ctx->Multiply(OrientationMatrix(size));
118 gfxUtils::DrawPixelSnapped(ctx, drawable, size, ImageRegion::Create(size),
119 surfaceFormat, Filter::LINEAR);
121 return target->Snapshot();
124 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
125 OrientedImage::GetFrameAtSize(const IntSize& aSize,
126 uint32_t aWhichFrame,
127 uint32_t aFlags)
129 // XXX(seth): It'd be nice to support downscale-during-decode for this case,
130 // but right now we just fall back to the intrinsic size.
131 return GetFrame(aWhichFrame, aFlags);
134 NS_IMETHODIMP_(bool)
135 OrientedImage::IsImageContainerAvailable(LayerManager* aManager, uint32_t aFlags)
137 if (mOrientation.IsIdentity()) {
138 return InnerImage()->IsImageContainerAvailable(aManager, aFlags);
140 return false;
143 NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
144 OrientedImage::GetImageContainer(LayerManager* aManager, uint32_t aFlags)
146 // XXX(seth): We currently don't have a way of orienting the result of
147 // GetImageContainer. We work around this by always returning null, but if it
148 // ever turns out that OrientedImage is widely used on codepaths that can
149 // actually benefit from GetImageContainer, it would be a good idea to fix
150 // that method for performance reasons.
152 if (mOrientation.IsIdentity()) {
153 return InnerImage()->GetImageContainer(aManager, aFlags);
156 return nullptr;
159 struct MatrixBuilder
161 explicit MatrixBuilder(bool aInvert) : mInvert(aInvert) { }
163 gfxMatrix Build() { return mMatrix; }
165 void Scale(gfxFloat aX, gfxFloat aY)
167 if (mInvert) {
168 mMatrix *= gfxMatrix::Scaling(1.0 / aX, 1.0 / aY);
169 } else {
170 mMatrix.Scale(aX, aY);
174 void Rotate(gfxFloat aPhi)
176 if (mInvert) {
177 mMatrix *= gfxMatrix::Rotation(-aPhi);
178 } else {
179 mMatrix.Rotate(aPhi);
183 void Translate(gfxPoint aDelta)
185 if (mInvert) {
186 mMatrix *= gfxMatrix::Translation(-aDelta);
187 } else {
188 mMatrix.Translate(aDelta);
192 private:
193 gfxMatrix mMatrix;
194 bool mInvert;
198 * OrientationMatrix() computes a matrix that applies the rotation and
199 * reflection specified by mOrientation, or that matrix's inverse if aInvert is
200 * true.
202 * @param aSize The scaled size of the inner image. (When outside code specifies
203 * the scaled size, as with imgIContainer::Draw and its aSize
204 * parameter, it's necessary to swap the width and height if
205 * mOrientation.SwapsWidthAndHeight() is true.)
206 * @param aInvert If true, compute the inverse of the orientation matrix. Prefer
207 * this approach to OrientationMatrix(..).Invert(), because it's
208 * more numerically accurate.
210 gfxMatrix
211 OrientedImage::OrientationMatrix(const nsIntSize& aSize,
212 bool aInvert /* = false */)
214 MatrixBuilder builder(aInvert);
216 // Apply reflection, if present. (This logically happens second, but we
217 // apply it first because these transformations are all premultiplied.) A
218 // translation is necessary to place the image back in the first quadrant.
219 switch (mOrientation.flip) {
220 case Flip::Unflipped:
221 break;
222 case Flip::Horizontal:
223 if (mOrientation.SwapsWidthAndHeight()) {
224 builder.Translate(gfxPoint(aSize.height, 0));
225 } else {
226 builder.Translate(gfxPoint(aSize.width, 0));
228 builder.Scale(-1.0, 1.0);
229 break;
230 default:
231 MOZ_ASSERT(false, "Invalid flip value");
234 // Apply rotation, if present. Again, a translation is used to place the
235 // image back in the first quadrant.
236 switch (mOrientation.rotation) {
237 case Angle::D0:
238 break;
239 case Angle::D90:
240 builder.Translate(gfxPoint(aSize.height, 0));
241 builder.Rotate(-1.5 * M_PI);
242 break;
243 case Angle::D180:
244 builder.Translate(gfxPoint(aSize.width, aSize.height));
245 builder.Rotate(-1.0 * M_PI);
246 break;
247 case Angle::D270:
248 builder.Translate(gfxPoint(0, aSize.width));
249 builder.Rotate(-0.5 * M_PI);
250 break;
251 default:
252 MOZ_ASSERT(false, "Invalid rotation value");
255 return builder.Build();
258 static SVGImageContext
259 OrientViewport(const SVGImageContext& aOldContext,
260 const Orientation& aOrientation)
262 CSSIntSize viewportSize(aOldContext.GetViewportSize());
263 if (aOrientation.SwapsWidthAndHeight()) {
264 swap(viewportSize.width, viewportSize.height);
266 return SVGImageContext(viewportSize,
267 aOldContext.GetPreserveAspectRatio());
270 NS_IMETHODIMP_(DrawResult)
271 OrientedImage::Draw(gfxContext* aContext,
272 const nsIntSize& aSize,
273 const ImageRegion& aRegion,
274 uint32_t aWhichFrame,
275 Filter aFilter,
276 const Maybe<SVGImageContext>& aSVGContext,
277 uint32_t aFlags)
279 if (mOrientation.IsIdentity()) {
280 return InnerImage()->Draw(aContext, aSize, aRegion,
281 aWhichFrame, aFilter, aSVGContext, aFlags);
284 // Update the image size to match the image's coordinate system. (This could
285 // be done using TransformBounds but since it's only a size a swap is enough.)
286 nsIntSize size(aSize);
287 if (mOrientation.SwapsWidthAndHeight()) {
288 swap(size.width, size.height);
291 // Update the matrix so that we transform the image into the orientation
292 // expected by the caller before drawing.
293 gfxMatrix matrix(OrientationMatrix(size));
294 gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
295 aContext->Multiply(matrix);
297 // The region is already in the orientation expected by the caller, but we
298 // need it to be in the image's coordinate system, so we transform it using
299 // the inverse of the orientation matrix.
300 gfxMatrix inverseMatrix(OrientationMatrix(size, /* aInvert = */ true));
301 ImageRegion region(aRegion);
302 region.TransformBoundsBy(inverseMatrix);
304 return InnerImage()->Draw(aContext, size, region, aWhichFrame, aFilter,
305 aSVGContext.map(OrientViewport, mOrientation),
306 aFlags);
309 nsIntSize
310 OrientedImage::OptimalImageSizeForDest(const gfxSize& aDest,
311 uint32_t aWhichFrame,
312 Filter aFilter, uint32_t aFlags)
314 if (!mOrientation.SwapsWidthAndHeight()) {
315 return InnerImage()->OptimalImageSizeForDest(aDest, aWhichFrame, aFilter,
316 aFlags);
319 // Swap the size for the calculation, then swap it back for the caller.
320 gfxSize destSize(aDest.height, aDest.width);
321 nsIntSize innerImageSize(InnerImage()->OptimalImageSizeForDest(destSize,
322 aWhichFrame,
323 aFilter,
324 aFlags));
325 return nsIntSize(innerImageSize.height, innerImageSize.width);
328 NS_IMETHODIMP_(nsIntRect)
329 OrientedImage::GetImageSpaceInvalidationRect(const nsIntRect& aRect)
331 nsIntRect rect(InnerImage()->GetImageSpaceInvalidationRect(aRect));
333 if (mOrientation.IsIdentity()) {
334 return rect;
337 nsIntSize innerSize;
338 nsresult rv = InnerImage()->GetWidth(&innerSize.width);
339 rv = NS_FAILED(rv) ? rv : InnerImage()->GetHeight(&innerSize.height);
340 if (NS_FAILED(rv)) {
341 // Fall back to identity if the width and height aren't available.
342 return rect;
345 // Transform the invalidation rect into the correct orientation.
346 gfxMatrix matrix(OrientationMatrix(innerSize));
347 gfxRect invalidRect(matrix.TransformBounds(gfxRect(rect.x, rect.y,
348 rect.width, rect.height)));
349 invalidRect.RoundOut();
351 return nsIntRect(invalidRect.x, invalidRect.y,
352 invalidRect.width, invalidRect.height);
355 } // namespace image
356 } // namespace mozilla