Bug 1768646 [wpt PR 34018] - [mq-4] Enable CSSMediaQueries4 for stable, a=testonly
[gecko.git] / image / OrientedImage.cpp
blob988c0ad8d4c77969cef7b72ae8d7ae699092792c
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 "OrientedImage.h"
8 #include <algorithm>
10 #include "gfx2DGlue.h"
11 #include "gfxContext.h"
12 #include "gfxDrawable.h"
13 #include "gfxPlatform.h"
14 #include "gfxUtils.h"
15 #include "ImageRegion.h"
16 #include "mozilla/SVGImageContext.h"
18 using std::swap;
20 namespace mozilla {
22 using namespace gfx;
23 using layers::ImageContainer;
25 namespace image {
27 NS_IMETHODIMP
28 OrientedImage::GetWidth(int32_t* aWidth) {
29 if (mOrientation.SwapsWidthAndHeight()) {
30 return InnerImage()->GetHeight(aWidth);
31 } else {
32 return InnerImage()->GetWidth(aWidth);
36 NS_IMETHODIMP
37 OrientedImage::GetHeight(int32_t* aHeight) {
38 if (mOrientation.SwapsWidthAndHeight()) {
39 return InnerImage()->GetWidth(aHeight);
40 } else {
41 return InnerImage()->GetHeight(aHeight);
45 nsresult OrientedImage::GetNativeSizes(nsTArray<IntSize>& aNativeSizes) const {
46 nsresult rv = InnerImage()->GetNativeSizes(aNativeSizes);
48 if (mOrientation.SwapsWidthAndHeight()) {
49 auto i = aNativeSizes.Length();
50 while (i > 0) {
51 --i;
52 swap(aNativeSizes[i].width, aNativeSizes[i].height);
56 return rv;
59 NS_IMETHODIMP
60 OrientedImage::GetIntrinsicSize(nsSize* aSize) {
61 nsresult rv = InnerImage()->GetIntrinsicSize(aSize);
63 if (mOrientation.SwapsWidthAndHeight()) {
64 swap(aSize->width, aSize->height);
67 return rv;
70 Maybe<AspectRatio> OrientedImage::GetIntrinsicRatio() {
71 Maybe<AspectRatio> ratio = InnerImage()->GetIntrinsicRatio();
72 if (ratio && mOrientation.SwapsWidthAndHeight()) {
73 ratio = Some(ratio->Inverted());
75 return ratio;
78 already_AddRefed<SourceSurface> OrientedImage::OrientSurface(
79 Orientation aOrientation, SourceSurface* aSurface) {
80 MOZ_ASSERT(aSurface);
82 // If the image does not require any re-orientation, return aSurface itself.
83 if (aOrientation.IsIdentity()) {
84 return do_AddRef(aSurface);
87 // Determine the size of the new surface.
88 nsIntSize originalSize = aSurface->GetSize();
89 nsIntSize targetSize = originalSize;
90 if (aOrientation.SwapsWidthAndHeight()) {
91 swap(targetSize.width, targetSize.height);
94 // Create our drawable.
95 RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(aSurface, originalSize);
97 // Determine an appropriate format for the surface.
98 gfx::SurfaceFormat surfaceFormat = IsOpaque(aSurface->GetFormat())
99 ? gfx::SurfaceFormat::OS_RGBX
100 : gfx::SurfaceFormat::OS_RGBA;
102 // Create the new surface to draw into.
103 RefPtr<DrawTarget> target =
104 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
105 targetSize, surfaceFormat);
106 if (!target || !target->IsValid()) {
107 NS_ERROR("Could not create a DrawTarget");
108 return nullptr;
111 // Draw.
112 RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(target);
113 MOZ_ASSERT(ctx); // already checked the draw target above
114 ctx->Multiply(OrientationMatrix(aOrientation, originalSize));
115 gfxUtils::DrawPixelSnapped(ctx, drawable, SizeDouble(originalSize),
116 ImageRegion::Create(originalSize), surfaceFormat,
117 SamplingFilter::LINEAR);
119 return target->Snapshot();
122 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
123 OrientedImage::GetFrame(uint32_t aWhichFrame, uint32_t aFlags) {
124 // Get a SourceSurface for the inner image then orient it according to
125 // mOrientation.
126 RefPtr<SourceSurface> innerSurface =
127 InnerImage()->GetFrame(aWhichFrame, aFlags);
128 NS_ENSURE_TRUE(innerSurface, nullptr);
130 return OrientSurface(mOrientation, innerSurface);
133 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
134 OrientedImage::GetFrameAtSize(const IntSize& aSize, uint32_t aWhichFrame,
135 uint32_t aFlags) {
136 // Get a SourceSurface for the inner image then orient it according to
137 // mOrientation.
138 IntSize innerSize = aSize;
139 if (mOrientation.SwapsWidthAndHeight()) {
140 swap(innerSize.width, innerSize.height);
142 RefPtr<SourceSurface> innerSurface =
143 InnerImage()->GetFrameAtSize(innerSize, aWhichFrame, aFlags);
144 NS_ENSURE_TRUE(innerSurface, nullptr);
146 return OrientSurface(mOrientation, innerSurface);
149 NS_IMETHODIMP_(bool)
150 OrientedImage::IsImageContainerAvailable(WindowRenderer* aRenderer,
151 uint32_t aFlags) {
152 if (mOrientation.IsIdentity()) {
153 return InnerImage()->IsImageContainerAvailable(aRenderer, aFlags);
155 return false;
158 NS_IMETHODIMP_(ImgDrawResult)
159 OrientedImage::GetImageProvider(WindowRenderer* aRenderer,
160 const gfx::IntSize& aSize,
161 const Maybe<SVGImageContext>& aSVGContext,
162 const Maybe<ImageIntRegion>& aRegion,
163 uint32_t aFlags,
164 WebRenderImageProvider** aProvider) {
165 // XXX(seth): We currently don't have a way of orienting the result of
166 // GetImageContainer. We work around this by always returning null, but if it
167 // ever turns out that OrientedImage is widely used on codepaths that can
168 // actually benefit from GetImageContainer, it would be a good idea to fix
169 // that method for performance reasons.
171 if (mOrientation.IsIdentity()) {
172 return InnerImage()->GetImageProvider(aRenderer, aSize, aSVGContext,
173 aRegion, aFlags, aProvider);
176 return ImgDrawResult::NOT_SUPPORTED;
179 struct MatrixBuilder {
180 explicit MatrixBuilder(bool aInvert) : mInvert(aInvert) {}
182 gfxMatrix Build() { return mMatrix; }
184 void Scale(gfxFloat aX, gfxFloat aY) {
185 if (mInvert) {
186 mMatrix *= gfxMatrix::Scaling(1.0 / aX, 1.0 / aY);
187 } else {
188 mMatrix.PreScale(aX, aY);
192 void Rotate(gfxFloat aPhi) {
193 if (mInvert) {
194 mMatrix *= gfxMatrix::Rotation(-aPhi);
195 } else {
196 mMatrix.PreRotate(aPhi);
200 void Translate(gfxPoint aDelta) {
201 if (mInvert) {
202 mMatrix *= gfxMatrix::Translation(-aDelta);
203 } else {
204 mMatrix.PreTranslate(aDelta);
208 private:
209 gfxMatrix mMatrix;
210 bool mInvert;
213 gfxMatrix OrientedImage::OrientationMatrix(Orientation aOrientation,
214 const nsIntSize& aSize,
215 bool aInvert /* = false */) {
216 MatrixBuilder builder(aInvert);
218 // Apply reflection, if present. (For a regular, non-flipFirst reflection,
219 // this logically happens second, but we apply it first because these
220 // transformations are all premultiplied.) A translation is necessary to place
221 // the image back in the first quadrant.
222 if (aOrientation.flip == Flip::Horizontal && !aOrientation.flipFirst) {
223 if (aOrientation.SwapsWidthAndHeight()) {
224 builder.Translate(gfxPoint(aSize.height, 0));
225 } else {
226 builder.Translate(gfxPoint(aSize.width, 0));
228 builder.Scale(-1.0, 1.0);
231 // Apply rotation, if present. Again, a translation is used to place the
232 // image back in the first quadrant.
233 switch (aOrientation.rotation) {
234 case Angle::D0:
235 break;
236 case Angle::D90:
237 builder.Translate(gfxPoint(aSize.height, 0));
238 builder.Rotate(-1.5 * M_PI);
239 break;
240 case Angle::D180:
241 builder.Translate(gfxPoint(aSize.width, aSize.height));
242 builder.Rotate(-1.0 * M_PI);
243 break;
244 case Angle::D270:
245 builder.Translate(gfxPoint(0, aSize.width));
246 builder.Rotate(-0.5 * M_PI);
247 break;
248 default:
249 MOZ_ASSERT(false, "Invalid rotation value");
252 // Apply a flipFirst reflection.
253 if (aOrientation.flip == Flip::Horizontal && aOrientation.flipFirst) {
254 builder.Translate(gfxPoint(aSize.width, 0.0));
255 builder.Scale(-1.0, 1.0);
258 return builder.Build();
261 NS_IMETHODIMP_(ImgDrawResult)
262 OrientedImage::Draw(gfxContext* aContext, const nsIntSize& aSize,
263 const ImageRegion& aRegion, uint32_t aWhichFrame,
264 SamplingFilter aSamplingFilter,
265 const Maybe<SVGImageContext>& aSVGContext, uint32_t aFlags,
266 float aOpacity) {
267 if (mOrientation.IsIdentity()) {
268 return InnerImage()->Draw(aContext, aSize, aRegion, aWhichFrame,
269 aSamplingFilter, aSVGContext, aFlags, aOpacity);
272 // Update the image size to match the image's coordinate system. (This could
273 // be done using TransformBounds but since it's only a size a swap is enough.)
274 nsIntSize size(aSize);
275 if (mOrientation.SwapsWidthAndHeight()) {
276 swap(size.width, size.height);
279 // Update the matrix so that we transform the image into the orientation
280 // expected by the caller before drawing.
281 gfxMatrix matrix(OrientationMatrix(size));
282 gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
283 aContext->Multiply(matrix);
285 // The region is already in the orientation expected by the caller, but we
286 // need it to be in the image's coordinate system, so we transform it using
287 // the inverse of the orientation matrix.
288 gfxMatrix inverseMatrix(OrientationMatrix(size, /* aInvert = */ true));
289 ImageRegion region(aRegion);
290 region.TransformBoundsBy(inverseMatrix);
292 auto orientViewport = [&](const SVGImageContext& aOldContext) {
293 SVGImageContext context(aOldContext);
294 auto oldViewport = aOldContext.GetViewportSize();
295 if (oldViewport && mOrientation.SwapsWidthAndHeight()) {
296 // Swap width and height:
297 CSSIntSize newViewport(oldViewport->height, oldViewport->width);
298 context.SetViewportSize(Some(newViewport));
300 return context;
303 return InnerImage()->Draw(aContext, size, region, aWhichFrame,
304 aSamplingFilter, aSVGContext.map(orientViewport),
305 aFlags, aOpacity);
308 nsIntSize OrientedImage::OptimalImageSizeForDest(const gfxSize& aDest,
309 uint32_t aWhichFrame,
310 SamplingFilter aSamplingFilter,
311 uint32_t aFlags) {
312 if (!mOrientation.SwapsWidthAndHeight()) {
313 return InnerImage()->OptimalImageSizeForDest(aDest, aWhichFrame,
314 aSamplingFilter, aFlags);
317 // Swap the size for the calculation, then swap it back for the caller.
318 gfxSize destSize(aDest.height, aDest.width);
319 nsIntSize innerImageSize(InnerImage()->OptimalImageSizeForDest(
320 destSize, aWhichFrame, aSamplingFilter, aFlags));
321 return nsIntSize(innerImageSize.height, innerImageSize.width);
324 NS_IMETHODIMP_(nsIntRect)
325 OrientedImage::GetImageSpaceInvalidationRect(const nsIntRect& aRect) {
326 nsIntRect rect(InnerImage()->GetImageSpaceInvalidationRect(aRect));
328 if (mOrientation.IsIdentity()) {
329 return rect;
332 nsIntSize innerSize;
333 nsresult rv = InnerImage()->GetWidth(&innerSize.width);
334 rv = NS_FAILED(rv) ? rv : InnerImage()->GetHeight(&innerSize.height);
335 if (NS_FAILED(rv)) {
336 // Fall back to identity if the width and height aren't available.
337 return rect;
340 // Transform the invalidation rect into the correct orientation.
341 gfxMatrix matrix(OrientationMatrix(innerSize));
342 gfxRect invalidRect(matrix.TransformBounds(
343 gfxRect(rect.X(), rect.Y(), rect.Width(), rect.Height())));
345 return IntRect::RoundOut(invalidRect.X(), invalidRect.Y(),
346 invalidRect.Width(), invalidRect.Height());
349 } // namespace image
350 } // namespace mozilla