Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/image/OrientedImage.cpp
Line
Count
Source (jump to first uncovered line)
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 "OrientedImage.h"
7
8
#include <algorithm>
9
10
#include "gfx2DGlue.h"
11
#include "gfxDrawable.h"
12
#include "gfxPlatform.h"
13
#include "gfxUtils.h"
14
#include "ImageRegion.h"
15
#include "SVGImageContext.h"
16
17
using std::swap;
18
19
namespace mozilla {
20
21
using namespace gfx;
22
using layers::LayerManager;
23
using layers::ImageContainer;
24
25
namespace image {
26
27
NS_IMETHODIMP
28
OrientedImage::GetWidth(int32_t* aWidth)
29
0
{
30
0
  if (mOrientation.SwapsWidthAndHeight()) {
31
0
    return InnerImage()->GetHeight(aWidth);
32
0
  } else {
33
0
    return InnerImage()->GetWidth(aWidth);
34
0
  }
35
0
}
36
37
NS_IMETHODIMP
38
OrientedImage::GetHeight(int32_t* aHeight)
39
0
{
40
0
  if (mOrientation.SwapsWidthAndHeight()) {
41
0
    return InnerImage()->GetWidth(aHeight);
42
0
  } else {
43
0
    return InnerImage()->GetHeight(aHeight);
44
0
  }
45
0
}
46
47
nsresult
48
OrientedImage::GetNativeSizes(nsTArray<IntSize>& aNativeSizes) const
49
0
{
50
0
  nsresult rv = InnerImage()->GetNativeSizes(aNativeSizes);
51
0
52
0
  if (mOrientation.SwapsWidthAndHeight()) {
53
0
    auto i = aNativeSizes.Length();
54
0
    while (i > 0) {
55
0
      --i;
56
0
      swap(aNativeSizes[i].width, aNativeSizes[i].height);
57
0
    }
58
0
  }
59
0
60
0
  return rv;
61
0
}
62
63
NS_IMETHODIMP
64
OrientedImage::GetIntrinsicSize(nsSize* aSize)
65
0
{
66
0
  nsresult rv = InnerImage()->GetIntrinsicSize(aSize);
67
0
68
0
  if (mOrientation.SwapsWidthAndHeight()) {
69
0
    swap(aSize->width, aSize->height);
70
0
  }
71
0
72
0
  return rv;
73
0
}
74
75
NS_IMETHODIMP
76
OrientedImage::GetIntrinsicRatio(nsSize* aRatio)
77
0
{
78
0
  nsresult rv = InnerImage()->GetIntrinsicRatio(aRatio);
79
0
80
0
  if (mOrientation.SwapsWidthAndHeight()) {
81
0
    swap(aRatio->width, aRatio->height);
82
0
  }
83
0
84
0
  return rv;
85
0
}
86
87
NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
88
OrientedImage::GetFrame(uint32_t aWhichFrame,
89
                        uint32_t aFlags)
90
0
{
91
0
  nsresult rv;
92
0
93
0
  if (mOrientation.IsIdentity()) {
94
0
    return InnerImage()->GetFrame(aWhichFrame, aFlags);
95
0
  }
96
0
97
0
  // Get the underlying dimensions.
98
0
  IntSize size;
99
0
  rv = InnerImage()->GetWidth(&size.width);
100
0
  NS_ENSURE_SUCCESS(rv, nullptr);
101
0
  rv = InnerImage()->GetHeight(&size.height);
102
0
  NS_ENSURE_SUCCESS(rv, nullptr);
103
0
104
0
  // Determine an appropriate format for the surface.
105
0
  gfx::SurfaceFormat surfaceFormat;
106
0
  if (InnerImage()->WillDrawOpaqueNow()) {
107
0
    surfaceFormat = gfx::SurfaceFormat::B8G8R8X8;
108
0
  } else {
109
0
    surfaceFormat = gfx::SurfaceFormat::B8G8R8A8;
110
0
  }
111
0
112
0
  // Create a surface to draw into.
113
0
  RefPtr<DrawTarget> target =
114
0
    gfxPlatform::GetPlatform()->
115
0
      CreateOffscreenContentDrawTarget(size, surfaceFormat);
116
0
  if (!target || !target->IsValid()) {
117
0
    NS_ERROR("Could not create a DrawTarget");
118
0
    return nullptr;
119
0
  }
120
0
121
0
122
0
  // Create our drawable.
123
0
  RefPtr<SourceSurface> innerSurface =
124
0
    InnerImage()->GetFrame(aWhichFrame, aFlags);
125
0
  NS_ENSURE_TRUE(innerSurface, nullptr);
126
0
  RefPtr<gfxDrawable> drawable =
127
0
    new gfxSurfaceDrawable(innerSurface, size);
128
0
129
0
  // Draw.
130
0
  RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(target);
131
0
  MOZ_ASSERT(ctx); // already checked the draw target above
132
0
  ctx->Multiply(OrientationMatrix(size));
133
0
  gfxUtils::DrawPixelSnapped(ctx, drawable, SizeDouble(size), ImageRegion::Create(size),
134
0
                             surfaceFormat, SamplingFilter::LINEAR);
135
0
136
0
  return target->Snapshot();
137
0
}
138
139
NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
140
OrientedImage::GetFrameAtSize(const IntSize& aSize,
141
                              uint32_t aWhichFrame,
142
                              uint32_t aFlags)
143
0
{
144
0
  // XXX(seth): It'd be nice to support downscale-during-decode for this case,
145
0
  // but right now we just fall back to the intrinsic size.
146
0
  return GetFrame(aWhichFrame, aFlags);
147
0
}
148
149
NS_IMETHODIMP_(bool)
150
OrientedImage::IsImageContainerAvailable(LayerManager* aManager, uint32_t aFlags)
151
0
{
152
0
  if (mOrientation.IsIdentity()) {
153
0
    return InnerImage()->IsImageContainerAvailable(aManager, aFlags);
154
0
  }
155
0
  return false;
156
0
}
157
158
NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
159
OrientedImage::GetImageContainer(LayerManager* aManager, uint32_t aFlags)
160
0
{
161
0
  // XXX(seth): We currently don't have a way of orienting the result of
162
0
  // GetImageContainer. We work around this by always returning null, but if it
163
0
  // ever turns out that OrientedImage is widely used on codepaths that can
164
0
  // actually benefit from GetImageContainer, it would be a good idea to fix
165
0
  // that method for performance reasons.
166
0
167
0
  if (mOrientation.IsIdentity()) {
168
0
    return InnerImage()->GetImageContainer(aManager, aFlags);
169
0
  }
170
0
171
0
  return nullptr;
172
0
}
173
174
NS_IMETHODIMP_(bool)
175
OrientedImage::IsImageContainerAvailableAtSize(LayerManager* aManager,
176
                                               const IntSize& aSize,
177
                                               uint32_t aFlags)
178
0
{
179
0
  if (mOrientation.IsIdentity()) {
180
0
    return InnerImage()->IsImageContainerAvailableAtSize(aManager, aSize, aFlags);
181
0
  }
182
0
  return false;
183
0
}
184
185
NS_IMETHODIMP_(ImgDrawResult)
186
OrientedImage::GetImageContainerAtSize(layers::LayerManager* aManager,
187
                                       const gfx::IntSize& aSize,
188
                                       const Maybe<SVGImageContext>& aSVGContext,
189
                                       uint32_t aFlags,
190
                                       layers::ImageContainer** aOutContainer)
191
0
{
192
0
  // XXX(seth): We currently don't have a way of orienting the result of
193
0
  // GetImageContainer. We work around this by always returning null, but if it
194
0
  // ever turns out that OrientedImage is widely used on codepaths that can
195
0
  // actually benefit from GetImageContainer, it would be a good idea to fix
196
0
  // that method for performance reasons.
197
0
198
0
  if (mOrientation.IsIdentity()) {
199
0
    return InnerImage()->GetImageContainerAtSize(aManager, aSize, aSVGContext,
200
0
                                                 aFlags, aOutContainer);
201
0
  }
202
0
203
0
  return ImgDrawResult::NOT_SUPPORTED;
204
0
}
205
206
struct MatrixBuilder
207
{
208
0
  explicit MatrixBuilder(bool aInvert) : mInvert(aInvert) { }
209
210
0
  gfxMatrix Build() { return mMatrix; }
211
212
  void Scale(gfxFloat aX, gfxFloat aY)
213
0
  {
214
0
    if (mInvert) {
215
0
      mMatrix *= gfxMatrix::Scaling(1.0 / aX, 1.0 / aY);
216
0
    } else {
217
0
      mMatrix.PreScale(aX, aY);
218
0
    }
219
0
  }
220
221
  void Rotate(gfxFloat aPhi)
222
0
  {
223
0
    if (mInvert) {
224
0
      mMatrix *= gfxMatrix::Rotation(-aPhi);
225
0
    } else {
226
0
      mMatrix.PreRotate(aPhi);
227
0
    }
228
0
  }
229
230
  void Translate(gfxPoint aDelta)
231
0
  {
232
0
    if (mInvert) {
233
0
      mMatrix *= gfxMatrix::Translation(-aDelta);
234
0
    } else {
235
0
      mMatrix.PreTranslate(aDelta);
236
0
    }
237
0
  }
238
239
private:
240
  gfxMatrix mMatrix;
241
  bool      mInvert;
242
};
243
244
/*
245
 * OrientationMatrix() computes a matrix that applies the rotation and
246
 * reflection specified by mOrientation, or that matrix's inverse if aInvert is
247
 * true.
248
 *
249
 * @param aSize The scaled size of the inner image. (When outside code specifies
250
 *              the scaled size, as with imgIContainer::Draw and its aSize
251
 *              parameter, it's necessary to swap the width and height if
252
 *              mOrientation.SwapsWidthAndHeight() is true.)
253
 * @param aInvert If true, compute the inverse of the orientation matrix. Prefer
254
 *                this approach to OrientationMatrix(..).Invert(), because it's
255
 *                more numerically accurate.
256
 */
257
gfxMatrix
258
OrientedImage::OrientationMatrix(const nsIntSize& aSize,
259
                                 bool aInvert /* = false */)
260
0
{
261
0
  MatrixBuilder builder(aInvert);
262
0
263
0
  // Apply reflection, if present. (This logically happens second, but we
264
0
  // apply it first because these transformations are all premultiplied.) A
265
0
  // translation is necessary to place the image back in the first quadrant.
266
0
  switch (mOrientation.flip) {
267
0
    case Flip::Unflipped:
268
0
      break;
269
0
    case Flip::Horizontal:
270
0
      if (mOrientation.SwapsWidthAndHeight()) {
271
0
        builder.Translate(gfxPoint(aSize.height, 0));
272
0
      } else {
273
0
        builder.Translate(gfxPoint(aSize.width, 0));
274
0
      }
275
0
      builder.Scale(-1.0, 1.0);
276
0
      break;
277
0
    default:
278
0
      MOZ_ASSERT(false, "Invalid flip value");
279
0
  }
280
0
281
0
  // Apply rotation, if present. Again, a translation is used to place the
282
0
  // image back in the first quadrant.
283
0
  switch (mOrientation.rotation) {
284
0
    case Angle::D0:
285
0
      break;
286
0
    case Angle::D90:
287
0
      builder.Translate(gfxPoint(aSize.height, 0));
288
0
      builder.Rotate(-1.5 * M_PI);
289
0
      break;
290
0
    case Angle::D180:
291
0
      builder.Translate(gfxPoint(aSize.width, aSize.height));
292
0
      builder.Rotate(-1.0 * M_PI);
293
0
      break;
294
0
    case Angle::D270:
295
0
      builder.Translate(gfxPoint(0, aSize.width));
296
0
      builder.Rotate(-0.5 * M_PI);
297
0
      break;
298
0
    default:
299
0
      MOZ_ASSERT(false, "Invalid rotation value");
300
0
  }
301
0
302
0
  return builder.Build();
303
0
}
304
305
NS_IMETHODIMP_(ImgDrawResult)
306
OrientedImage::Draw(gfxContext* aContext,
307
                    const nsIntSize& aSize,
308
                    const ImageRegion& aRegion,
309
                    uint32_t aWhichFrame,
310
                    SamplingFilter aSamplingFilter,
311
                    const Maybe<SVGImageContext>& aSVGContext,
312
                    uint32_t aFlags,
313
                    float aOpacity)
314
0
{
315
0
  if (mOrientation.IsIdentity()) {
316
0
    return InnerImage()->Draw(aContext, aSize, aRegion,
317
0
                              aWhichFrame, aSamplingFilter,
318
0
                              aSVGContext, aFlags, aOpacity);
319
0
  }
320
0
321
0
  // Update the image size to match the image's coordinate system. (This could
322
0
  // be done using TransformBounds but since it's only a size a swap is enough.)
323
0
  nsIntSize size(aSize);
324
0
  if (mOrientation.SwapsWidthAndHeight()) {
325
0
    swap(size.width, size.height);
326
0
  }
327
0
328
0
  // Update the matrix so that we transform the image into the orientation
329
0
  // expected by the caller before drawing.
330
0
  gfxMatrix matrix(OrientationMatrix(size));
331
0
  gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
332
0
  aContext->Multiply(matrix);
333
0
334
0
  // The region is already in the orientation expected by the caller, but we
335
0
  // need it to be in the image's coordinate system, so we transform it using
336
0
  // the inverse of the orientation matrix.
337
0
  gfxMatrix inverseMatrix(OrientationMatrix(size, /* aInvert = */ true));
338
0
  ImageRegion region(aRegion);
339
0
  region.TransformBoundsBy(inverseMatrix);
340
0
341
0
  auto orientViewport = [&](const SVGImageContext& aOldContext) {
342
0
    SVGImageContext context(aOldContext);
343
0
    auto oldViewport = aOldContext.GetViewportSize();
344
0
    if (oldViewport && mOrientation.SwapsWidthAndHeight()) {
345
0
      // Swap width and height:
346
0
      CSSIntSize newViewport(oldViewport->height, oldViewport->width);
347
0
      context.SetViewportSize(Some(newViewport));
348
0
    }
349
0
    return context;
350
0
  };
351
0
352
0
  return InnerImage()->Draw(aContext, size, region, aWhichFrame,
353
0
                            aSamplingFilter,
354
0
                            aSVGContext.map(orientViewport), aFlags,
355
0
                            aOpacity);
356
0
}
357
358
nsIntSize
359
OrientedImage::OptimalImageSizeForDest(const gfxSize& aDest,
360
                                       uint32_t aWhichFrame,
361
                                       SamplingFilter aSamplingFilter,
362
                                       uint32_t aFlags)
363
0
{
364
0
  if (!mOrientation.SwapsWidthAndHeight()) {
365
0
    return InnerImage()->OptimalImageSizeForDest(aDest, aWhichFrame,
366
0
                                                 aSamplingFilter, aFlags);
367
0
  }
368
0
369
0
  // Swap the size for the calculation, then swap it back for the caller.
370
0
  gfxSize destSize(aDest.height, aDest.width);
371
0
  nsIntSize innerImageSize(InnerImage()->OptimalImageSizeForDest(destSize,
372
0
                                                                 aWhichFrame,
373
0
                                                                 aSamplingFilter,
374
0
                                                                 aFlags));
375
0
  return nsIntSize(innerImageSize.height, innerImageSize.width);
376
0
}
377
378
NS_IMETHODIMP_(nsIntRect)
379
OrientedImage::GetImageSpaceInvalidationRect(const nsIntRect& aRect)
380
0
{
381
0
  nsIntRect rect(InnerImage()->GetImageSpaceInvalidationRect(aRect));
382
0
383
0
  if (mOrientation.IsIdentity()) {
384
0
    return rect;
385
0
  }
386
0
387
0
  nsIntSize innerSize;
388
0
  nsresult rv = InnerImage()->GetWidth(&innerSize.width);
389
0
  rv = NS_FAILED(rv) ? rv : InnerImage()->GetHeight(&innerSize.height);
390
0
  if (NS_FAILED(rv)) {
391
0
    // Fall back to identity if the width and height aren't available.
392
0
    return rect;
393
0
  }
394
0
395
0
  // Transform the invalidation rect into the correct orientation.
396
0
  gfxMatrix matrix(OrientationMatrix(innerSize));
397
0
  gfxRect invalidRect(matrix.TransformBounds(gfxRect(rect.X(), rect.Y(),
398
0
                                                     rect.Width(), rect.Height())));
399
0
400
0
  return IntRect::RoundOut(invalidRect.X(), invalidRect.Y(),
401
0
                           invalidRect.Width(), invalidRect.Height());
402
0
}
403
404
} // namespace image
405
} // namespace mozilla