image/src/OrientedImage.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:8b76b93b3603
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/. */
5
6 #include <algorithm>
7
8 #include "gfxDrawable.h"
9 #include "gfxPlatform.h"
10 #include "gfxUtils.h"
11
12 #include "OrientedImage.h"
13
14 using namespace mozilla::gfx;
15
16 using std::swap;
17 using mozilla::layers::LayerManager;
18 using mozilla::layers::ImageContainer;
19
20 namespace mozilla {
21 namespace image {
22
23 NS_IMPL_ISUPPORTS(OrientedImage, imgIContainer)
24
25 nsIntRect
26 OrientedImage::FrameRect(uint32_t aWhichFrame)
27 {
28 if (mOrientation.SwapsWidthAndHeight()) {
29 nsIntRect innerRect = InnerImage()->FrameRect(aWhichFrame);
30 return nsIntRect(innerRect.x, innerRect.y, innerRect.height, innerRect.width);
31 } else {
32 return InnerImage()->FrameRect(aWhichFrame);
33 }
34 }
35
36 NS_IMETHODIMP
37 OrientedImage::GetWidth(int32_t* aWidth)
38 {
39 if (mOrientation.SwapsWidthAndHeight()) {
40 return InnerImage()->GetHeight(aWidth);
41 } else {
42 return InnerImage()->GetWidth(aWidth);
43 }
44 }
45
46 NS_IMETHODIMP
47 OrientedImage::GetHeight(int32_t* aHeight)
48 {
49 if (mOrientation.SwapsWidthAndHeight()) {
50 return InnerImage()->GetWidth(aHeight);
51 } else {
52 return InnerImage()->GetHeight(aHeight);
53 }
54 }
55
56 NS_IMETHODIMP
57 OrientedImage::GetIntrinsicSize(nsSize* aSize)
58 {
59 nsresult rv = InnerImage()->GetIntrinsicSize(aSize);
60
61 if (mOrientation.SwapsWidthAndHeight()) {
62 swap(aSize->width, aSize->height);
63 }
64
65 return rv;
66 }
67
68 NS_IMETHODIMP
69 OrientedImage::GetIntrinsicRatio(nsSize* aRatio)
70 {
71 nsresult rv = InnerImage()->GetIntrinsicRatio(aRatio);
72
73 if (mOrientation.SwapsWidthAndHeight()) {
74 swap(aRatio->width, aRatio->height);
75 }
76
77 return rv;
78 }
79
80 NS_IMETHODIMP_(TemporaryRef<SourceSurface>)
81 OrientedImage::GetFrame(uint32_t aWhichFrame,
82 uint32_t aFlags)
83 {
84 nsresult rv;
85
86 if (mOrientation.IsIdentity()) {
87 return InnerImage()->GetFrame(aWhichFrame, aFlags);
88 }
89
90 // Get the underlying dimensions.
91 int32_t width, height;
92 rv = InnerImage()->GetWidth(&width);
93 NS_ENSURE_SUCCESS(rv, nullptr);
94 rv = InnerImage()->GetHeight(&height);
95 NS_ENSURE_SUCCESS(rv, nullptr);
96 if (mOrientation.SwapsWidthAndHeight()) {
97 swap(width, height);
98 }
99 NS_ENSURE_SUCCESS(rv, nullptr);
100
101 // Determine an appropriate format for the surface.
102 gfx::SurfaceFormat surfaceFormat;
103 gfxImageFormat imageFormat;
104 if (InnerImage()->FrameIsOpaque(aWhichFrame)) {
105 surfaceFormat = gfx::SurfaceFormat::B8G8R8X8;
106 imageFormat = gfxImageFormat::ARGB32;
107 } else {
108 surfaceFormat = gfx::SurfaceFormat::B8G8R8A8;
109 imageFormat = gfxImageFormat::ARGB32;
110 }
111
112 // Create a surface to draw into.
113 mozilla::RefPtr<DrawTarget> target =
114 gfxPlatform::GetPlatform()->
115 CreateOffscreenContentDrawTarget(IntSize(width, height), surfaceFormat);
116
117 // Create our drawable.
118 RefPtr<SourceSurface> innerSurface =
119 InnerImage()->GetFrame(aWhichFrame, aFlags);
120 NS_ENSURE_TRUE(innerSurface, nullptr);
121 nsRefPtr<gfxDrawable> drawable =
122 new gfxSurfaceDrawable(innerSurface, gfxIntSize(width, height));
123
124 // Draw.
125 nsRefPtr<gfxContext> ctx = new gfxContext(target);
126 gfxRect imageRect(0, 0, width, height);
127 gfxUtils::DrawPixelSnapped(ctx, drawable, OrientationMatrix(nsIntSize(width, height)),
128 imageRect, imageRect, imageRect, imageRect,
129 imageFormat, GraphicsFilter::FILTER_FAST);
130
131 return target->Snapshot();
132 }
133
134 NS_IMETHODIMP
135 OrientedImage::GetImageContainer(LayerManager* aManager, ImageContainer** _retval)
136 {
137 // XXX(seth): We currently don't have a way of orienting the result of
138 // GetImageContainer. We work around this by always returning null, but if it
139 // ever turns out that OrientedImage is widely used on codepaths that can
140 // actually benefit from GetImageContainer, it would be a good idea to fix
141 // that method for performance reasons.
142
143 if (mOrientation.IsIdentity()) {
144 return InnerImage()->GetImageContainer(aManager, _retval);
145 }
146
147 *_retval = nullptr;
148 return NS_OK;
149 }
150
151 gfxMatrix
152 OrientedImage::OrientationMatrix(const nsIntSize& aViewportSize)
153 {
154 gfxMatrix matrix;
155
156 int32_t width, height;
157 if (InnerImage()->GetType() == imgIContainer::TYPE_VECTOR) {
158 width = mOrientation.SwapsWidthAndHeight() ? aViewportSize.height : aViewportSize.width;
159 height = mOrientation.SwapsWidthAndHeight() ? aViewportSize.width : aViewportSize.height;
160 } else {
161 nsresult rv = InnerImage()->GetWidth(&width);
162 rv = NS_FAILED(rv) ? rv : InnerImage()->GetHeight(&height);
163 if (NS_FAILED(rv)) {
164 // Fall back to identity if the width and height aren't available.
165 return matrix;
166 }
167 }
168
169 // Apply reflection, if present. (This logically happens second, but we
170 // apply it first because these transformations are all premultiplied.) A
171 // translation is necessary to place the image back in the first quadrant.
172 switch (mOrientation.flip) {
173 case Flip::Unflipped:
174 break;
175 case Flip::Horizontal:
176 if (mOrientation.SwapsWidthAndHeight()) {
177 // In the coordinate system after the rotation the reflection we want
178 // is across the x-axis rather than the y-axis.
179 matrix.Translate(gfxPoint(0, height));
180 matrix.Scale(1.0, -1.0);
181 } else {
182 matrix.Translate(gfxPoint(width, 0));
183 matrix.Scale(-1.0, 1.0);
184 }
185 break;
186 default:
187 MOZ_ASSERT(false, "Invalid flip value");
188 }
189
190 // Apply rotation, if present. Again, a translation is used to place the
191 // image back in the first quadrant.
192 switch (mOrientation.rotation) {
193 case Angle::D0:
194 break;
195 case Angle::D90:
196 matrix.Translate(gfxPoint(0, height));
197 matrix.Rotate(-0.5 * M_PI);
198 break;
199 case Angle::D180:
200 matrix.Translate(gfxPoint(width, height));
201 matrix.Rotate(-1.0 * M_PI);
202 break;
203 case Angle::D270:
204 matrix.Translate(gfxPoint(width, 0));
205 matrix.Rotate(-1.5 * M_PI);
206 break;
207 default:
208 MOZ_ASSERT(false, "Invalid rotation value");
209 }
210
211 return matrix;
212 }
213
214 NS_IMETHODIMP
215 OrientedImage::Draw(gfxContext* aContext,
216 GraphicsFilter aFilter,
217 const gfxMatrix& aUserSpaceToImageSpace,
218 const gfxRect& aFill,
219 const nsIntRect& aSubimage,
220 const nsIntSize& aViewportSize,
221 const SVGImageContext* aSVGContext,
222 uint32_t aWhichFrame,
223 uint32_t aFlags)
224 {
225 if (mOrientation.IsIdentity()) {
226 return InnerImage()->Draw(aContext, aFilter, aUserSpaceToImageSpace,
227 aFill, aSubimage, aViewportSize, aSVGContext,
228 aWhichFrame, aFlags);
229 }
230
231 // Update the matrix to reorient the image.
232 gfxMatrix matrix(OrientationMatrix(aViewportSize));
233 gfxMatrix userSpaceToImageSpace(aUserSpaceToImageSpace * matrix);
234
235 // Update the subimage.
236 gfxRect gfxSubimage(matrix.TransformBounds(gfxRect(aSubimage.x, aSubimage.y, aSubimage.width, aSubimage.height)));
237 nsIntRect subimage(gfxSubimage.x, gfxSubimage.y, gfxSubimage.width, gfxSubimage.height);
238
239 // Update the viewport size. (This could be done using TransformBounds but
240 // since it's only a size a swap is enough.)
241 nsIntSize viewportSize(aViewportSize);
242 if (mOrientation.SwapsWidthAndHeight()) {
243 swap(viewportSize.width, viewportSize.height);
244 }
245
246 return InnerImage()->Draw(aContext, aFilter, userSpaceToImageSpace,
247 aFill, subimage, viewportSize, aSVGContext,
248 aWhichFrame, aFlags);
249 }
250
251 } // namespace image
252 } // namespace mozilla

mercurial