|
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 |