Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/painting/nsImageRenderer.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
/* utility code for drawing images as CSS borders, backgrounds, and shapes. */
8
9
#include "nsImageRenderer.h"
10
11
#include "mozilla/webrender/WebRenderAPI.h"
12
13
#include "gfxContext.h"
14
#include "gfxDrawable.h"
15
#include "ImageOps.h"
16
#include "ImageRegion.h"
17
#include "mozilla/layers/StackingContextHelper.h"
18
#include "mozilla/layers/WebRenderLayerManager.h"
19
#include "nsContentUtils.h"
20
#include "nsCSSRendering.h"
21
#include "nsCSSRenderingGradients.h"
22
#include "nsDeviceContext.h"
23
#include "nsIFrame.h"
24
#include "nsStyleStructInlines.h"
25
#include "nsSVGDisplayableFrame.h"
26
#include "SVGObserverUtils.h"
27
#include "nsSVGIntegrationUtils.h"
28
#include "mozilla/layers/WebRenderLayerManager.h"
29
30
using namespace mozilla;
31
using namespace mozilla::gfx;
32
using namespace mozilla::image;
33
using namespace mozilla::layers;
34
35
nsSize
36
CSSSizeOrRatio::ComputeConcreteSize() const
37
0
{
38
0
  NS_ASSERTION(CanComputeConcreteSize(), "Cannot compute");
39
0
  if (mHasWidth && mHasHeight) {
40
0
    return nsSize(mWidth, mHeight);
41
0
  }
42
0
  if (mHasWidth) {
43
0
    nscoord height = NSCoordSaturatingNonnegativeMultiply(
44
0
      mWidth, double(mRatio.height) / mRatio.width);
45
0
    return nsSize(mWidth, height);
46
0
  }
47
0
48
0
  MOZ_ASSERT(mHasHeight);
49
0
  nscoord width = NSCoordSaturatingNonnegativeMultiply(
50
0
    mHeight, double(mRatio.width) / mRatio.height);
51
0
  return nsSize(width, mHeight);
52
0
}
53
54
nsImageRenderer::nsImageRenderer(nsIFrame* aForFrame,
55
                                 const nsStyleImage* aImage,
56
                                 uint32_t aFlags)
57
  : mForFrame(aForFrame)
58
  , mImage(aImage)
59
  , mType(aImage->GetType())
60
  , mImageContainer(nullptr)
61
  , mGradientData(nullptr)
62
  , mPaintServerFrame(nullptr)
63
  , mPrepareResult(ImgDrawResult::NOT_READY)
64
  , mSize(0, 0)
65
  , mFlags(aFlags)
66
  , mExtendMode(ExtendMode::CLAMP)
67
  , mMaskOp(NS_STYLE_MASK_MODE_MATCH_SOURCE)
68
0
{
69
0
}
70
71
static bool
72
ShouldTreatAsCompleteDueToSyncDecode(const nsStyleImage* aImage,
73
                                     uint32_t aFlags)
74
0
{
75
0
  if (!(aFlags & nsImageRenderer::FLAG_SYNC_DECODE_IMAGES)) {
76
0
    return false;
77
0
  }
78
0
79
0
  if (aImage->GetType() != eStyleImageType_Image) {
80
0
    return false;
81
0
  }
82
0
83
0
  imgRequestProxy* req = aImage->GetImageData();
84
0
  if (!req) {
85
0
    return false;
86
0
  }
87
0
88
0
  uint32_t status = 0;
89
0
  if (NS_FAILED(req->GetImageStatus(&status))) {
90
0
    return false;
91
0
  }
92
0
93
0
  if (status & imgIRequest::STATUS_ERROR) {
94
0
    // The image is "complete" since it's a corrupt image. If we created an
95
0
    // imgIContainer at all, return true.
96
0
    nsCOMPtr<imgIContainer> image;
97
0
    req->GetImage(getter_AddRefs(image));
98
0
    return bool(image);
99
0
  }
100
0
101
0
  if (!(status & imgIRequest::STATUS_LOAD_COMPLETE)) {
102
0
    // We must have loaded all of the image's data and the size must be
103
0
    // available, or else sync decoding won't be able to decode the image.
104
0
    return false;
105
0
  }
106
0
107
0
  return true;
108
0
}
109
110
bool
111
nsImageRenderer::PrepareImage()
112
0
{
113
0
  if (mImage->IsEmpty()) {
114
0
    mPrepareResult = ImgDrawResult::BAD_IMAGE;
115
0
    return false;
116
0
  }
117
0
118
0
  if (!mImage->IsComplete()) {
119
0
    // Make sure the image is actually decoding.
120
0
    bool frameComplete = mImage->StartDecoding();
121
0
122
0
    // Check again to see if we finished.
123
0
    // We cannot prepare the image for rendering if it is not fully loaded.
124
0
    // Special case: If we requested a sync decode and the image has loaded,
125
0
    // push on through because the Draw() will do a sync decode then.
126
0
    if (!(frameComplete || mImage->IsComplete()) &&
127
0
        !ShouldTreatAsCompleteDueToSyncDecode(mImage, mFlags)) {
128
0
      mPrepareResult = ImgDrawResult::NOT_READY;
129
0
      return false;
130
0
    }
131
0
  }
132
0
133
0
  switch (mType) {
134
0
    case eStyleImageType_Image: {
135
0
      MOZ_ASSERT(mImage->GetImageData(),
136
0
                 "must have image data, since we checked IsEmpty above");
137
0
      nsCOMPtr<imgIContainer> srcImage;
138
0
      DebugOnly<nsresult> rv =
139
0
        mImage->GetImageData()->GetImage(getter_AddRefs(srcImage));
140
0
      MOZ_ASSERT(NS_SUCCEEDED(rv) && srcImage,
141
0
                 "If GetImage() is failing, mImage->IsComplete() "
142
0
                 "should have returned false");
143
0
144
0
      if (!mImage->GetCropRect()) {
145
0
        mImageContainer.swap(srcImage);
146
0
      } else {
147
0
        nsIntRect actualCropRect;
148
0
        bool isEntireImage;
149
0
        bool success =
150
0
          mImage->ComputeActualCropRect(actualCropRect, &isEntireImage);
151
0
        if (!success || actualCropRect.IsEmpty()) {
152
0
          // The cropped image has zero size
153
0
          mPrepareResult = ImgDrawResult::BAD_IMAGE;
154
0
          return false;
155
0
        }
156
0
        if (isEntireImage) {
157
0
          // The cropped image is identical to the source image
158
0
          mImageContainer.swap(srcImage);
159
0
        } else {
160
0
          nsCOMPtr<imgIContainer> subImage =
161
0
            ImageOps::Clip(srcImage, actualCropRect, Nothing());
162
0
          mImageContainer.swap(subImage);
163
0
        }
164
0
      }
165
0
      mPrepareResult = ImgDrawResult::SUCCESS;
166
0
      break;
167
0
    }
168
0
    case eStyleImageType_Gradient:
169
0
      mGradientData = mImage->GetGradientData();
170
0
      mPrepareResult = ImgDrawResult::SUCCESS;
171
0
      break;
172
0
    case eStyleImageType_Element: {
173
0
      nsAutoString elementId =
174
0
        NS_LITERAL_STRING("#") + nsDependentAtomString(mImage->GetElementId());
175
0
      nsCOMPtr<nsIURI> targetURI;
176
0
      nsCOMPtr<nsIURI> base = mForFrame->GetContent()->GetBaseURI();
177
0
      nsContentUtils::NewURIWithDocumentCharset(
178
0
        getter_AddRefs(targetURI),
179
0
        elementId,
180
0
        mForFrame->GetContent()->GetUncomposedDoc(),
181
0
        base);
182
0
      RefPtr<URLAndReferrerInfo> url = new URLAndReferrerInfo(
183
0
        targetURI,
184
0
        mForFrame->GetContent()->OwnerDoc()->GetDocumentURI(),
185
0
        mForFrame->GetContent()->OwnerDoc()->GetReferrerPolicy());
186
0
187
0
      nsSVGPaintingProperty* property = SVGObserverUtils::GetPaintingPropertyForURI(
188
0
          url, mForFrame->FirstContinuation(),
189
0
          SVGObserverUtils::BackgroundImageProperty());
190
0
      if (!property) {
191
0
        mPrepareResult = ImgDrawResult::BAD_IMAGE;
192
0
        return false;
193
0
      }
194
0
195
0
      // If the referenced element is an <img>, <canvas>, or <video> element,
196
0
      // prefer SurfaceFromElement as it's more reliable.
197
0
      mImageElementSurface =
198
0
        nsLayoutUtils::SurfaceFromElement(property->GetReferencedElement());
199
0
      if (!mImageElementSurface.GetSourceSurface()) {
200
0
        nsIFrame* paintServerFrame = property->GetReferencedFrame();
201
0
        // If there's no referenced frame, or the referenced frame is
202
0
        // non-displayable SVG, then we have nothing valid to paint.
203
0
        if (!paintServerFrame ||
204
0
            (paintServerFrame->IsFrameOfType(nsIFrame::eSVG) &&
205
0
             !paintServerFrame->IsFrameOfType(nsIFrame::eSVGPaintServer) &&
206
0
             !static_cast<nsSVGDisplayableFrame*>(
207
0
               do_QueryFrame(paintServerFrame)))) {
208
0
          mPrepareResult = ImgDrawResult::BAD_IMAGE;
209
0
          return false;
210
0
        }
211
0
        mPaintServerFrame = paintServerFrame;
212
0
      }
213
0
214
0
      mPrepareResult = ImgDrawResult::SUCCESS;
215
0
      break;
216
0
    }
217
0
    case eStyleImageType_Null:
218
0
    default:
219
0
      break;
220
0
  }
221
0
222
0
  return IsReady();
223
0
}
224
225
CSSSizeOrRatio
226
nsImageRenderer::ComputeIntrinsicSize()
227
0
{
228
0
  NS_ASSERTION(IsReady(),
229
0
               "Ensure PrepareImage() has returned true "
230
0
               "before calling me");
231
0
232
0
  CSSSizeOrRatio result;
233
0
  switch (mType) {
234
0
    case eStyleImageType_Image: {
235
0
      bool haveWidth, haveHeight;
236
0
      CSSIntSize imageIntSize;
237
0
      nsLayoutUtils::ComputeSizeForDrawing(
238
0
        mImageContainer, imageIntSize, result.mRatio, haveWidth, haveHeight);
239
0
      if (haveWidth) {
240
0
        result.SetWidth(nsPresContext::CSSPixelsToAppUnits(imageIntSize.width));
241
0
      }
242
0
      if (haveHeight) {
243
0
        result.SetHeight(
244
0
          nsPresContext::CSSPixelsToAppUnits(imageIntSize.height));
245
0
      }
246
0
247
0
      // If we know the aspect ratio and one of the dimensions,
248
0
      // we can compute the other missing width or height.
249
0
      if (!haveHeight && haveWidth && result.mRatio.width != 0) {
250
0
        nscoord intrinsicHeight = NSCoordSaturatingNonnegativeMultiply(
251
0
          imageIntSize.width,
252
0
          float(result.mRatio.height) / float(result.mRatio.width));
253
0
        result.SetHeight(nsPresContext::CSSPixelsToAppUnits(intrinsicHeight));
254
0
      } else if (haveHeight && !haveWidth && result.mRatio.height != 0) {
255
0
        nscoord intrinsicWidth = NSCoordSaturatingNonnegativeMultiply(
256
0
          imageIntSize.height,
257
0
          float(result.mRatio.width) / float(result.mRatio.height));
258
0
        result.SetWidth(nsPresContext::CSSPixelsToAppUnits(intrinsicWidth));
259
0
      }
260
0
261
0
      break;
262
0
    }
263
0
    case eStyleImageType_Element: {
264
0
      // XXX element() should have the width/height of the referenced element,
265
0
      //     and that element's ratio, if it matches.  If it doesn't match, it
266
0
      //     should have no width/height or ratio.  See element() in CSS images:
267
0
      //     <http://dev.w3.org/csswg/css-images-4/#element-notation>.
268
0
      //     Make sure to change nsStyleImageLayers::Size::DependsOnFrameSize
269
0
      //     when fixing this!
270
0
      if (mPaintServerFrame) {
271
0
        // SVG images have no intrinsic size
272
0
        if (!mPaintServerFrame->IsFrameOfType(nsIFrame::eSVG)) {
273
0
          // The intrinsic image size for a generic nsIFrame paint server is
274
0
          // the union of the border-box rects of all of its continuations,
275
0
          // rounded to device pixels.
276
0
          int32_t appUnitsPerDevPixel =
277
0
            mForFrame->PresContext()->AppUnitsPerDevPixel();
278
0
          result.SetSize(IntSizeToAppUnits(
279
0
            nsSVGIntegrationUtils::GetContinuationUnionSize(mPaintServerFrame)
280
0
              .ToNearestPixels(appUnitsPerDevPixel),
281
0
            appUnitsPerDevPixel));
282
0
        }
283
0
      } else {
284
0
        NS_ASSERTION(mImageElementSurface.GetSourceSurface(),
285
0
                     "Surface should be ready.");
286
0
        IntSize surfaceSize = mImageElementSurface.mSize;
287
0
        result.SetSize(
288
0
          nsSize(nsPresContext::CSSPixelsToAppUnits(surfaceSize.width),
289
0
                 nsPresContext::CSSPixelsToAppUnits(surfaceSize.height)));
290
0
      }
291
0
      break;
292
0
    }
293
0
    case eStyleImageType_Gradient:
294
0
      // Per <http://dev.w3.org/csswg/css3-images/#gradients>, gradients have no
295
0
      // intrinsic dimensions.
296
0
    case eStyleImageType_Null:
297
0
    default:
298
0
      break;
299
0
  }
300
0
301
0
  return result;
302
0
}
303
304
/* static */ nsSize
305
nsImageRenderer::ComputeConcreteSize(const CSSSizeOrRatio& aSpecifiedSize,
306
                                     const CSSSizeOrRatio& aIntrinsicSize,
307
                                     const nsSize& aDefaultSize)
308
0
{
309
0
  // The specified size is fully specified, just use that
310
0
  if (aSpecifiedSize.IsConcrete()) {
311
0
    return aSpecifiedSize.ComputeConcreteSize();
312
0
  }
313
0
314
0
  MOZ_ASSERT(!aSpecifiedSize.mHasWidth || !aSpecifiedSize.mHasHeight);
315
0
316
0
  if (!aSpecifiedSize.mHasWidth && !aSpecifiedSize.mHasHeight) {
317
0
    // no specified size, try using the intrinsic size
318
0
    if (aIntrinsicSize.CanComputeConcreteSize()) {
319
0
      return aIntrinsicSize.ComputeConcreteSize();
320
0
    }
321
0
322
0
    if (aIntrinsicSize.mHasWidth) {
323
0
      return nsSize(aIntrinsicSize.mWidth, aDefaultSize.height);
324
0
    }
325
0
    if (aIntrinsicSize.mHasHeight) {
326
0
      return nsSize(aDefaultSize.width, aIntrinsicSize.mHeight);
327
0
    }
328
0
329
0
    // couldn't use the intrinsic size either, revert to using the default size
330
0
    return ComputeConstrainedSize(aDefaultSize, aIntrinsicSize.mRatio, CONTAIN);
331
0
  }
332
0
333
0
  MOZ_ASSERT(aSpecifiedSize.mHasWidth || aSpecifiedSize.mHasHeight);
334
0
335
0
  // The specified height is partial, try to compute the missing part.
336
0
  if (aSpecifiedSize.mHasWidth) {
337
0
    nscoord height;
338
0
    if (aIntrinsicSize.HasRatio()) {
339
0
      height = NSCoordSaturatingNonnegativeMultiply(
340
0
        aSpecifiedSize.mWidth,
341
0
        double(aIntrinsicSize.mRatio.height) / aIntrinsicSize.mRatio.width);
342
0
    } else if (aIntrinsicSize.mHasHeight) {
343
0
      height = aIntrinsicSize.mHeight;
344
0
    } else {
345
0
      height = aDefaultSize.height;
346
0
    }
347
0
    return nsSize(aSpecifiedSize.mWidth, height);
348
0
  }
349
0
350
0
  MOZ_ASSERT(aSpecifiedSize.mHasHeight);
351
0
  nscoord width;
352
0
  if (aIntrinsicSize.HasRatio()) {
353
0
    width = NSCoordSaturatingNonnegativeMultiply(
354
0
      aSpecifiedSize.mHeight,
355
0
      double(aIntrinsicSize.mRatio.width) / aIntrinsicSize.mRatio.height);
356
0
  } else if (aIntrinsicSize.mHasWidth) {
357
0
    width = aIntrinsicSize.mWidth;
358
0
  } else {
359
0
    width = aDefaultSize.width;
360
0
  }
361
0
  return nsSize(width, aSpecifiedSize.mHeight);
362
0
}
363
364
/* static */ nsSize
365
nsImageRenderer::ComputeConstrainedSize(const nsSize& aConstrainingSize,
366
                                        const nsSize& aIntrinsicRatio,
367
                                        FitType aFitType)
368
0
{
369
0
  if (aIntrinsicRatio.width <= 0 && aIntrinsicRatio.height <= 0) {
370
0
    return aConstrainingSize;
371
0
  }
372
0
373
0
  float scaleX = double(aConstrainingSize.width) / aIntrinsicRatio.width;
374
0
  float scaleY = double(aConstrainingSize.height) / aIntrinsicRatio.height;
375
0
  nsSize size;
376
0
  if ((aFitType == CONTAIN) == (scaleX < scaleY)) {
377
0
    size.width = aConstrainingSize.width;
378
0
    size.height =
379
0
      NSCoordSaturatingNonnegativeMultiply(aIntrinsicRatio.height, scaleX);
380
0
    // If we're reducing the size by less than one css pixel, then just use the
381
0
    // constraining size.
382
0
    if (aFitType == CONTAIN &&
383
0
        aConstrainingSize.height - size.height < AppUnitsPerCSSPixel()) {
384
0
      size.height = aConstrainingSize.height;
385
0
    }
386
0
  } else {
387
0
    size.width =
388
0
      NSCoordSaturatingNonnegativeMultiply(aIntrinsicRatio.width, scaleY);
389
0
    if (aFitType == CONTAIN &&
390
0
        aConstrainingSize.width - size.width < AppUnitsPerCSSPixel()) {
391
0
      size.width = aConstrainingSize.width;
392
0
    }
393
0
    size.height = aConstrainingSize.height;
394
0
  }
395
0
  return size;
396
0
}
397
398
/**
399
 * mSize is the image's "preferred" size for this particular rendering, while
400
 * the drawn (aka concrete) size is the actual rendered size after accounting
401
 * for background-size etc..  The preferred size is most often the image's
402
 * intrinsic dimensions.  But for images with incomplete intrinsic dimensions,
403
 * the preferred size varies, depending on the specified and default sizes, see
404
 * nsImageRenderer::Compute*Size.
405
 *
406
 * This distinction is necessary because the components of a vector image are
407
 * specified with respect to its preferred size for a rendering situation, not
408
 * to its actual rendered size.  For example, consider a 4px wide background
409
 * vector image with no height which contains a left-aligned
410
 * 2px wide black rectangle with height 100%.  If the background-size width is
411
 * auto (or 4px), the vector image will render 4px wide, and the black rectangle
412
 * will be 2px wide.  If the background-size width is 8px, the vector image will
413
 * render 8px wide, and the black rectangle will be 4px wide -- *not* 2px wide.
414
 * In both cases mSize.width will be 4px; but in the first case the returned
415
 * width will be 4px, while in the second case the returned width will be 8px.
416
 */
417
void
418
nsImageRenderer::SetPreferredSize(const CSSSizeOrRatio& aIntrinsicSize,
419
                                  const nsSize& aDefaultSize)
420
0
{
421
0
  mSize.width =
422
0
    aIntrinsicSize.mHasWidth ? aIntrinsicSize.mWidth : aDefaultSize.width;
423
0
  mSize.height =
424
0
    aIntrinsicSize.mHasHeight ? aIntrinsicSize.mHeight : aDefaultSize.height;
425
0
}
426
427
// Convert from nsImageRenderer flags to the flags we want to use for drawing in
428
// the imgIContainer namespace.
429
static uint32_t
430
ConvertImageRendererToDrawFlags(uint32_t aImageRendererFlags)
431
0
{
432
0
  uint32_t drawFlags = imgIContainer::FLAG_NONE;
433
0
  if (aImageRendererFlags & nsImageRenderer::FLAG_SYNC_DECODE_IMAGES) {
434
0
    drawFlags |= imgIContainer::FLAG_SYNC_DECODE;
435
0
  }
436
0
  if (aImageRendererFlags & nsImageRenderer::FLAG_PAINTING_TO_WINDOW) {
437
0
    drawFlags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
438
0
  }
439
0
  return drawFlags;
440
0
}
441
442
ImgDrawResult
443
nsImageRenderer::Draw(nsPresContext* aPresContext,
444
                      gfxContext& aRenderingContext,
445
                      const nsRect& aDirtyRect,
446
                      const nsRect& aDest,
447
                      const nsRect& aFill,
448
                      const nsPoint& aAnchor,
449
                      const nsSize& aRepeatSize,
450
                      const CSSIntRect& aSrc,
451
                      float aOpacity)
452
0
{
453
0
  if (!IsReady()) {
454
0
    MOZ_ASSERT_UNREACHABLE("Ensure PrepareImage() has returned true before "
455
0
                           "calling me");
456
0
    return ImgDrawResult::TEMPORARY_ERROR;
457
0
  }
458
0
459
0
  if (aDest.IsEmpty() || aFill.IsEmpty() || mSize.width <= 0 ||
460
0
      mSize.height <= 0) {
461
0
    return ImgDrawResult::SUCCESS;
462
0
  }
463
0
464
0
  SamplingFilter samplingFilter =
465
0
    nsLayoutUtils::GetSamplingFilterForFrame(mForFrame);
466
0
  ImgDrawResult result = ImgDrawResult::SUCCESS;
467
0
  RefPtr<gfxContext> ctx = &aRenderingContext;
468
0
  IntRect tmpDTRect;
469
0
470
0
  if (ctx->CurrentOp() != CompositionOp::OP_OVER ||
471
0
      mMaskOp == NS_STYLE_MASK_MODE_LUMINANCE) {
472
0
    gfxRect clipRect = ctx->GetClipExtents(gfxContext::eDeviceSpace);
473
0
    tmpDTRect = RoundedOut(ToRect(clipRect));
474
0
    if (tmpDTRect.IsEmpty()) {
475
0
      return ImgDrawResult::SUCCESS;
476
0
    }
477
0
    RefPtr<DrawTarget> tempDT =
478
0
      gfxPlatform::GetPlatform()->CreateSimilarSoftwareDrawTarget(
479
0
        ctx->GetDrawTarget(), tmpDTRect.Size(), SurfaceFormat::B8G8R8A8);
480
0
    if (!tempDT || !tempDT->IsValid()) {
481
0
      gfxDevCrash(LogReason::InvalidContext)
482
0
        << "ImageRenderer::Draw problem " << gfx::hexa(tempDT);
483
0
      return ImgDrawResult::TEMPORARY_ERROR;
484
0
    }
485
0
    tempDT->SetTransform(ctx->GetDrawTarget()->GetTransform() *
486
0
                         Matrix::Translation(-tmpDTRect.TopLeft()));
487
0
    ctx = gfxContext::CreatePreservingTransformOrNull(tempDT);
488
0
    if (!ctx) {
489
0
      gfxDevCrash(LogReason::InvalidContext)
490
0
        << "ImageRenderer::Draw problem " << gfx::hexa(tempDT);
491
0
      return ImgDrawResult::TEMPORARY_ERROR;
492
0
    }
493
0
  }
494
0
495
0
  switch (mType) {
496
0
    case eStyleImageType_Image: {
497
0
      CSSIntSize imageSize(nsPresContext::AppUnitsToIntCSSPixels(mSize.width),
498
0
                           nsPresContext::AppUnitsToIntCSSPixels(mSize.height));
499
0
      result = nsLayoutUtils::DrawBackgroundImage(
500
0
        *ctx,
501
0
        mForFrame,
502
0
        aPresContext,
503
0
        mImageContainer,
504
0
        imageSize,
505
0
        samplingFilter,
506
0
        aDest,
507
0
        aFill,
508
0
        aRepeatSize,
509
0
        aAnchor,
510
0
        aDirtyRect,
511
0
        ConvertImageRendererToDrawFlags(mFlags),
512
0
        mExtendMode,
513
0
        aOpacity);
514
0
      break;
515
0
    }
516
0
    case eStyleImageType_Gradient: {
517
0
      nsCSSGradientRenderer renderer = nsCSSGradientRenderer::Create(
518
0
        aPresContext, mForFrame->Style(), mGradientData, mSize);
519
0
520
0
      renderer.Paint(
521
0
        *ctx, aDest, aFill, aRepeatSize, aSrc, aDirtyRect, aOpacity);
522
0
      break;
523
0
    }
524
0
    case eStyleImageType_Element: {
525
0
      RefPtr<gfxDrawable> drawable = DrawableForElement(aDest, *ctx);
526
0
      if (!drawable) {
527
0
        NS_WARNING("Could not create drawable for element");
528
0
        return ImgDrawResult::TEMPORARY_ERROR;
529
0
      }
530
0
531
0
      nsCOMPtr<imgIContainer> image(ImageOps::CreateFromDrawable(drawable));
532
0
      result = nsLayoutUtils::DrawImage(*ctx,
533
0
                                        mForFrame->Style(),
534
0
                                        aPresContext,
535
0
                                        image,
536
0
                                        samplingFilter,
537
0
                                        aDest,
538
0
                                        aFill,
539
0
                                        aAnchor,
540
0
                                        aDirtyRect,
541
0
                                        ConvertImageRendererToDrawFlags(mFlags),
542
0
                                        aOpacity);
543
0
      break;
544
0
    }
545
0
    case eStyleImageType_Null:
546
0
    default:
547
0
      break;
548
0
  }
549
0
550
0
  if (!tmpDTRect.IsEmpty()) {
551
0
    DrawTarget* dt = aRenderingContext.GetDrawTarget();
552
0
    Matrix oldTransform = dt->GetTransform();
553
0
    dt->SetTransform(Matrix());
554
0
    if (mMaskOp == NS_STYLE_MASK_MODE_LUMINANCE) {
555
0
      RefPtr<SourceSurface> surf = ctx->GetDrawTarget()->IntoLuminanceSource(
556
0
        LuminanceType::LUMINANCE, 1.0f);
557
0
      dt->MaskSurface(ColorPattern(Color(0, 0, 0, 1.0f)),
558
0
                      surf,
559
0
                      tmpDTRect.TopLeft(),
560
0
                      DrawOptions(1.0f, aRenderingContext.CurrentOp()));
561
0
    } else {
562
0
      RefPtr<SourceSurface> surf = ctx->GetDrawTarget()->Snapshot();
563
0
      dt->DrawSurface(
564
0
        surf,
565
0
        Rect(tmpDTRect.x, tmpDTRect.y, tmpDTRect.width, tmpDTRect.height),
566
0
        Rect(0, 0, tmpDTRect.width, tmpDTRect.height),
567
0
        DrawSurfaceOptions(SamplingFilter::POINT),
568
0
        DrawOptions(1.0f, aRenderingContext.CurrentOp()));
569
0
    }
570
0
571
0
    dt->SetTransform(oldTransform);
572
0
  }
573
0
574
0
  if (!mImage->IsComplete()) {
575
0
    result &= ImgDrawResult::SUCCESS_NOT_COMPLETE;
576
0
  }
577
0
578
0
  return result;
579
0
}
580
581
ImgDrawResult
582
nsImageRenderer::BuildWebRenderDisplayItems(
583
  nsPresContext* aPresContext,
584
  mozilla::wr::DisplayListBuilder& aBuilder,
585
  mozilla::wr::IpcResourceUpdateQueue& aResources,
586
  const mozilla::layers::StackingContextHelper& aSc,
587
  mozilla::layers::WebRenderLayerManager* aManager,
588
  nsDisplayItem* aItem,
589
  const nsRect& aDirtyRect,
590
  const nsRect& aDest,
591
  const nsRect& aFill,
592
  const nsPoint& aAnchor,
593
  const nsSize& aRepeatSize,
594
  const CSSIntRect& aSrc,
595
  float aOpacity)
596
0
{
597
0
  if (!IsReady()) {
598
0
    MOZ_ASSERT_UNREACHABLE("Ensure PrepareImage() has returned true before "
599
0
                           "calling me");
600
0
    return ImgDrawResult::NOT_READY;
601
0
  }
602
0
603
0
  if (aDest.IsEmpty() || aFill.IsEmpty() || mSize.width <= 0 ||
604
0
      mSize.height <= 0) {
605
0
    return ImgDrawResult::SUCCESS;
606
0
  }
607
0
608
0
  ImgDrawResult drawResult = ImgDrawResult::SUCCESS;
609
0
  switch (mType) {
610
0
    case eStyleImageType_Gradient: {
611
0
      nsCSSGradientRenderer renderer = nsCSSGradientRenderer::Create(
612
0
        aPresContext, mForFrame->Style(), mGradientData, mSize);
613
0
614
0
      renderer.BuildWebRenderDisplayItems(aBuilder,
615
0
                                          aSc,
616
0
                                          aDest,
617
0
                                          aFill,
618
0
                                          aRepeatSize,
619
0
                                          aSrc,
620
0
                                          !aItem->BackfaceIsHidden(),
621
0
                                          aOpacity);
622
0
      break;
623
0
    }
624
0
    case eStyleImageType_Image: {
625
0
      uint32_t containerFlags = imgIContainer::FLAG_ASYNC_NOTIFY;
626
0
      if (mFlags & nsImageRenderer::FLAG_PAINTING_TO_WINDOW) {
627
0
        containerFlags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
628
0
      }
629
0
      if (mFlags & nsImageRenderer::FLAG_SYNC_DECODE_IMAGES) {
630
0
        containerFlags |= imgIContainer::FLAG_SYNC_DECODE;
631
0
      }
632
0
633
0
      CSSIntSize imageSize(nsPresContext::AppUnitsToIntCSSPixels(mSize.width),
634
0
                           nsPresContext::AppUnitsToIntCSSPixels(mSize.height));
635
0
      Maybe<SVGImageContext> svgContext(Some(SVGImageContext(Some(imageSize))));
636
0
637
0
      const int32_t appUnitsPerDevPixel =
638
0
        mForFrame->PresContext()->AppUnitsPerDevPixel();
639
0
      LayoutDeviceRect destRect =
640
0
        LayoutDeviceRect::FromAppUnits(aDest, appUnitsPerDevPixel);
641
0
      gfx::IntSize decodeSize =
642
0
        nsLayoutUtils::ComputeImageContainerDrawingParameters(mImageContainer,
643
0
                                                              mForFrame,
644
0
                                                              destRect,
645
0
                                                              aSc,
646
0
                                                              containerFlags,
647
0
                                                              svgContext);
648
0
649
0
      RefPtr<layers::ImageContainer> container;
650
0
      drawResult = mImageContainer->GetImageContainerAtSize(aManager, decodeSize, svgContext,
651
0
                                                            containerFlags, getter_AddRefs(container));
652
0
      if (!container) {
653
0
        NS_WARNING("Failed to get image container");
654
0
        break;
655
0
      }
656
0
657
0
      mozilla::wr::ImageRendering rendering = wr::ToImageRendering(
658
0
        nsLayoutUtils::GetSamplingFilterForFrame(aItem->Frame()));
659
0
      gfx::IntSize size;
660
0
      Maybe<wr::ImageKey> key =
661
0
        aManager->CommandBuilder().CreateImageKey(aItem,
662
0
                                                  container,
663
0
                                                  aBuilder,
664
0
                                                  aResources,
665
0
                                                  rendering,
666
0
                                                  aSc,
667
0
                                                  size,
668
0
                                                  Nothing());
669
0
670
0
      if (key.isNothing()) {
671
0
        break;
672
0
      }
673
0
674
0
      nsPoint firstTilePos = nsLayoutUtils::GetBackgroundFirstTilePos(
675
0
        aDest.TopLeft(), aFill.TopLeft(), aRepeatSize);
676
0
      LayoutDeviceRect fillRect =
677
0
        LayoutDeviceRect::FromAppUnits(nsRect(firstTilePos.x,
678
0
                                              firstTilePos.y,
679
0
                                              aFill.XMost() - firstTilePos.x,
680
0
                                              aFill.YMost() - firstTilePos.y),
681
0
                                       appUnitsPerDevPixel);
682
0
      wr::LayoutRect fill = wr::ToRoundedLayoutRect(fillRect);
683
0
684
0
      wr::LayoutRect roundedDest = wr::ToRoundedLayoutRect(destRect);
685
0
      auto stretchSize = wr::ToLayoutSize(destRect.Size());
686
0
687
0
      // WebRender special cases situations where stretchSize == fillSize to
688
0
      // infer that it shouldn't use repeat sampling. This makes sure
689
0
      // we hit those special cases when not repeating.
690
0
691
0
      switch (mExtendMode) {
692
0
        case ExtendMode::CLAMP:
693
0
          fill = roundedDest;
694
0
          stretchSize = roundedDest.size;
695
0
          break;
696
0
        case ExtendMode::REPEAT_Y:
697
0
          fill.origin.x = roundedDest.origin.x;
698
0
          fill.size.width = roundedDest.size.width;
699
0
          stretchSize.width = roundedDest.size.width;
700
0
          break;
701
0
        case ExtendMode::REPEAT_X:
702
0
          fill.origin.y = roundedDest.origin.y;
703
0
          fill.size.height = roundedDest.size.height;
704
0
          stretchSize.height = roundedDest.size.height;
705
0
          break;
706
0
        default:
707
0
          break;
708
0
      }
709
0
710
0
      wr::LayoutRect clip = wr::ToRoundedLayoutRect(
711
0
        LayoutDeviceRect::FromAppUnits(aFill, appUnitsPerDevPixel));
712
0
713
0
      LayoutDeviceSize gapSize = LayoutDeviceSize::FromAppUnits(
714
0
        aRepeatSize - aDest.Size(), appUnitsPerDevPixel);
715
0
716
0
      aBuilder.PushImage(fill,
717
0
                         clip,
718
0
                         !aItem->BackfaceIsHidden(),
719
0
                         stretchSize,
720
0
                         wr::ToLayoutSize(gapSize),
721
0
                         rendering,
722
0
                         key.value());
723
0
      break;
724
0
    }
725
0
    default:
726
0
      break;
727
0
  }
728
0
729
0
  if (!mImage->IsComplete() && drawResult == ImgDrawResult::SUCCESS) {
730
0
    return ImgDrawResult::SUCCESS_NOT_COMPLETE;
731
0
  }
732
0
  return drawResult;
733
0
}
734
735
already_AddRefed<gfxDrawable>
736
nsImageRenderer::DrawableForElement(const nsRect& aImageRect,
737
                                    gfxContext& aContext)
738
0
{
739
0
  NS_ASSERTION(mType == eStyleImageType_Element,
740
0
               "DrawableForElement only makes sense if backed by an element");
741
0
  if (mPaintServerFrame) {
742
0
    // XXX(seth): In order to not pass FLAG_SYNC_DECODE_IMAGES here,
743
0
    // DrawableFromPaintServer would have to return a ImgDrawResult indicating
744
0
    // whether any images could not be painted because they weren't fully
745
0
    // decoded. Even always passing FLAG_SYNC_DECODE_IMAGES won't eliminate all
746
0
    // problems, as it won't help if there are image which haven't finished
747
0
    // loading, but it's better than nothing.
748
0
    int32_t appUnitsPerDevPixel =
749
0
      mForFrame->PresContext()->AppUnitsPerDevPixel();
750
0
    nsRect destRect = aImageRect - aImageRect.TopLeft();
751
0
    nsIntSize roundedOut = destRect.ToOutsidePixels(appUnitsPerDevPixel).Size();
752
0
    IntSize imageSize(roundedOut.width, roundedOut.height);
753
0
    RefPtr<gfxDrawable> drawable =
754
0
      nsSVGIntegrationUtils::DrawableFromPaintServer(
755
0
        mPaintServerFrame,
756
0
        mForFrame,
757
0
        mSize,
758
0
        imageSize,
759
0
        aContext.GetDrawTarget(),
760
0
        aContext.CurrentMatrixDouble(),
761
0
        nsSVGIntegrationUtils::FLAG_SYNC_DECODE_IMAGES);
762
0
763
0
    return drawable.forget();
764
0
  }
765
0
  NS_ASSERTION(mImageElementSurface.GetSourceSurface(),
766
0
               "Surface should be ready.");
767
0
  RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(
768
0
    mImageElementSurface.GetSourceSurface().get(), mImageElementSurface.mSize);
769
0
  return drawable.forget();
770
0
}
771
772
ImgDrawResult
773
nsImageRenderer::DrawLayer(nsPresContext* aPresContext,
774
                           gfxContext& aRenderingContext,
775
                           const nsRect& aDest,
776
                           const nsRect& aFill,
777
                           const nsPoint& aAnchor,
778
                           const nsRect& aDirty,
779
                           const nsSize& aRepeatSize,
780
                           float aOpacity)
781
0
{
782
0
  if (!IsReady()) {
783
0
    MOZ_ASSERT_UNREACHABLE("Ensure PrepareImage() has returned true before "
784
0
                           "calling me");
785
0
    return ImgDrawResult::TEMPORARY_ERROR;
786
0
  }
787
0
788
0
  if (aDest.IsEmpty() || aFill.IsEmpty() || mSize.width <= 0 ||
789
0
      mSize.height <= 0) {
790
0
    return ImgDrawResult::SUCCESS;
791
0
  }
792
0
793
0
  return Draw(aPresContext,
794
0
              aRenderingContext,
795
0
              aDirty,
796
0
              aDest,
797
0
              aFill,
798
0
              aAnchor,
799
0
              aRepeatSize,
800
0
              CSSIntRect(0,
801
0
                         0,
802
0
                         nsPresContext::AppUnitsToIntCSSPixels(mSize.width),
803
0
                         nsPresContext::AppUnitsToIntCSSPixels(mSize.height)),
804
0
              aOpacity);
805
0
}
806
807
ImgDrawResult
808
nsImageRenderer::BuildWebRenderDisplayItemsForLayer(
809
  nsPresContext* aPresContext,
810
  mozilla::wr::DisplayListBuilder& aBuilder,
811
  mozilla::wr::IpcResourceUpdateQueue& aResources,
812
  const mozilla::layers::StackingContextHelper& aSc,
813
  mozilla::layers::WebRenderLayerManager* aManager,
814
  nsDisplayItem* aItem,
815
  const nsRect& aDest,
816
  const nsRect& aFill,
817
  const nsPoint& aAnchor,
818
  const nsRect& aDirty,
819
  const nsSize& aRepeatSize,
820
  float aOpacity)
821
0
{
822
0
  if (!IsReady()) {
823
0
    MOZ_ASSERT_UNREACHABLE("Ensure PrepareImage() has returned true before "
824
0
                           "calling me");
825
0
    return mPrepareResult;
826
0
  }
827
0
828
0
  if (aDest.IsEmpty() || aFill.IsEmpty() || mSize.width <= 0 ||
829
0
      mSize.height <= 0) {
830
0
    return ImgDrawResult::SUCCESS;
831
0
  }
832
0
  return BuildWebRenderDisplayItems(
833
0
    aPresContext,
834
0
    aBuilder,
835
0
    aResources,
836
0
    aSc,
837
0
    aManager,
838
0
    aItem,
839
0
    aDirty,
840
0
    aDest,
841
0
    aFill,
842
0
    aAnchor,
843
0
    aRepeatSize,
844
0
    CSSIntRect(0,
845
0
               0,
846
0
               nsPresContext::AppUnitsToIntCSSPixels(mSize.width),
847
0
               nsPresContext::AppUnitsToIntCSSPixels(mSize.height)),
848
0
    aOpacity);
849
0
}
850
851
/**
852
 * Compute the size and position of the master copy of the image. I.e., a single
853
 * tile used to fill the dest rect.
854
 * aFill The destination rect to be filled
855
 * aHFill and aVFill are the repeat patterns for the component -
856
 * StyleBorderImageRepeat - i.e., how a tiling unit is used to fill aFill
857
 * aUnitSize The size of the source rect in dest coords.
858
 */
859
static nsRect
860
ComputeTile(nsRect& aFill,
861
            StyleBorderImageRepeat aHFill,
862
            StyleBorderImageRepeat aVFill,
863
            const nsSize& aUnitSize,
864
            nsSize& aRepeatSize)
865
0
{
866
0
  nsRect tile;
867
0
  switch (aHFill) {
868
0
    case StyleBorderImageRepeat::Stretch:
869
0
      tile.x = aFill.x;
870
0
      tile.width = aFill.width;
871
0
      aRepeatSize.width = tile.width;
872
0
      break;
873
0
    case StyleBorderImageRepeat::Repeat:
874
0
      tile.x = aFill.x + aFill.width / 2 - aUnitSize.width / 2;
875
0
      tile.width = aUnitSize.width;
876
0
      aRepeatSize.width = tile.width;
877
0
      break;
878
0
    case StyleBorderImageRepeat::Round:
879
0
      tile.x = aFill.x;
880
0
      tile.width =
881
0
        nsCSSRendering::ComputeRoundedSize(aUnitSize.width, aFill.width);
882
0
      aRepeatSize.width = tile.width;
883
0
      break;
884
0
    case StyleBorderImageRepeat::Space: {
885
0
      nscoord space;
886
0
      aRepeatSize.width = nsCSSRendering::ComputeBorderSpacedRepeatSize(
887
0
        aUnitSize.width, aFill.width, space);
888
0
      tile.x = aFill.x + space;
889
0
      tile.width = aUnitSize.width;
890
0
      aFill.x = tile.x;
891
0
      aFill.width = aFill.width - space * 2;
892
0
    } break;
893
0
    default:
894
0
      MOZ_ASSERT_UNREACHABLE("unrecognized border-image fill style");
895
0
  }
896
0
897
0
  switch (aVFill) {
898
0
    case StyleBorderImageRepeat::Stretch:
899
0
      tile.y = aFill.y;
900
0
      tile.height = aFill.height;
901
0
      aRepeatSize.height = tile.height;
902
0
      break;
903
0
    case StyleBorderImageRepeat::Repeat:
904
0
      tile.y = aFill.y + aFill.height / 2 - aUnitSize.height / 2;
905
0
      tile.height = aUnitSize.height;
906
0
      aRepeatSize.height = tile.height;
907
0
      break;
908
0
    case StyleBorderImageRepeat::Round:
909
0
      tile.y = aFill.y;
910
0
      tile.height =
911
0
        nsCSSRendering::ComputeRoundedSize(aUnitSize.height, aFill.height);
912
0
      aRepeatSize.height = tile.height;
913
0
      break;
914
0
    case StyleBorderImageRepeat::Space: {
915
0
      nscoord space;
916
0
      aRepeatSize.height = nsCSSRendering::ComputeBorderSpacedRepeatSize(
917
0
        aUnitSize.height, aFill.height, space);
918
0
      tile.y = aFill.y + space;
919
0
      tile.height = aUnitSize.height;
920
0
      aFill.y = tile.y;
921
0
      aFill.height = aFill.height - space * 2;
922
0
    } break;
923
0
    default:
924
0
      MOZ_ASSERT_UNREACHABLE("unrecognized border-image fill style");
925
0
  }
926
0
927
0
  return tile;
928
0
}
929
930
/**
931
 * Returns true if the given set of arguments will require the tiles which fill
932
 * the dest rect to be scaled from the source tile. See comment on ComputeTile
933
 * for argument descriptions.
934
 */
935
static bool
936
RequiresScaling(const nsRect& aFill,
937
                StyleBorderImageRepeat aHFill,
938
                StyleBorderImageRepeat aVFill,
939
                const nsSize& aUnitSize)
940
0
{
941
0
  // If we have no tiling in either direction, we can skip the intermediate
942
0
  // scaling step.
943
0
  return (aHFill != StyleBorderImageRepeat::Stretch ||
944
0
          aVFill != StyleBorderImageRepeat::Stretch) &&
945
0
         (aUnitSize.width != aFill.width || aUnitSize.height != aFill.height);
946
0
}
947
948
ImgDrawResult
949
nsImageRenderer::DrawBorderImageComponent(nsPresContext* aPresContext,
950
                                          gfxContext& aRenderingContext,
951
                                          const nsRect& aDirtyRect,
952
                                          const nsRect& aFill,
953
                                          const CSSIntRect& aSrc,
954
                                          StyleBorderImageRepeat aHFill,
955
                                          StyleBorderImageRepeat aVFill,
956
                                          const nsSize& aUnitSize,
957
                                          uint8_t aIndex,
958
                                          const Maybe<nsSize>& aSVGViewportSize,
959
                                          const bool aHasIntrinsicRatio)
960
0
{
961
0
  if (!IsReady()) {
962
0
    MOZ_ASSERT_UNREACHABLE("Ensure PrepareImage() has returned true before "
963
0
                           "calling me");
964
0
    return ImgDrawResult::BAD_ARGS;
965
0
  }
966
0
967
0
  if (aFill.IsEmpty() || aSrc.IsEmpty()) {
968
0
    return ImgDrawResult::SUCCESS;
969
0
  }
970
0
971
0
  if (mType == eStyleImageType_Image || mType == eStyleImageType_Element) {
972
0
    nsCOMPtr<imgIContainer> subImage;
973
0
974
0
    // To draw one portion of an image into a border component, we stretch that
975
0
    // portion to match the size of that border component and then draw onto.
976
0
    // However, preserveAspectRatio attribute of a SVG image may break this
977
0
    // rule. To get correct rendering result, we add
978
0
    // FLAG_FORCE_PRESERVEASPECTRATIO_NONE flag here, to tell mImage to ignore
979
0
    // preserveAspectRatio attribute, and always do non-uniform stretch.
980
0
    uint32_t drawFlags = ConvertImageRendererToDrawFlags(mFlags) |
981
0
                         imgIContainer::FLAG_FORCE_PRESERVEASPECTRATIO_NONE;
982
0
    // For those SVG image sources which don't have fixed aspect ratio (i.e.
983
0
    // without viewport size and viewBox), we should scale the source uniformly
984
0
    // after the viewport size is decided by "Default Sizing Algorithm".
985
0
    if (!aHasIntrinsicRatio) {
986
0
      drawFlags = drawFlags | imgIContainer::FLAG_FORCE_UNIFORM_SCALING;
987
0
    }
988
0
    // Retrieve or create the subimage we'll draw.
989
0
    nsIntRect srcRect(aSrc.x, aSrc.y, aSrc.width, aSrc.height);
990
0
    if (mType == eStyleImageType_Image) {
991
0
      if ((subImage = mImage->GetSubImage(aIndex)) == nullptr) {
992
0
        subImage = ImageOps::Clip(mImageContainer, srcRect, aSVGViewportSize);
993
0
        mImage->SetSubImage(aIndex, subImage);
994
0
      }
995
0
    } else {
996
0
      // This path, for eStyleImageType_Element, is currently slower than it
997
0
      // needs to be because we don't cache anything. (In particular, if we have
998
0
      // to draw to a temporary surface inside ClippedImage, we don't cache that
999
0
      // temporary surface since we immediately throw the ClippedImage we create
1000
0
      // here away.) However, if we did cache, we'd need to know when to
1001
0
      // invalidate that cache, and it's not clear that it's worth the trouble
1002
0
      // since using border-image with -moz-element is rare.
1003
0
1004
0
      RefPtr<gfxDrawable> drawable =
1005
0
        DrawableForElement(nsRect(nsPoint(), mSize), aRenderingContext);
1006
0
      if (!drawable) {
1007
0
        NS_WARNING("Could not create drawable for element");
1008
0
        return ImgDrawResult::TEMPORARY_ERROR;
1009
0
      }
1010
0
1011
0
      nsCOMPtr<imgIContainer> image(ImageOps::CreateFromDrawable(drawable));
1012
0
      subImage = ImageOps::Clip(image, srcRect, aSVGViewportSize);
1013
0
    }
1014
0
1015
0
    MOZ_ASSERT(!aSVGViewportSize ||
1016
0
               subImage->GetType() == imgIContainer::TYPE_VECTOR);
1017
0
1018
0
    SamplingFilter samplingFilter =
1019
0
      nsLayoutUtils::GetSamplingFilterForFrame(mForFrame);
1020
0
1021
0
    if (!RequiresScaling(aFill, aHFill, aVFill, aUnitSize)) {
1022
0
      ImgDrawResult result =
1023
0
        nsLayoutUtils::DrawSingleImage(aRenderingContext,
1024
0
                                       aPresContext,
1025
0
                                       subImage,
1026
0
                                       samplingFilter,
1027
0
                                       aFill,
1028
0
                                       aDirtyRect,
1029
0
                                       /* no SVGImageContext */ Nothing(),
1030
0
                                       drawFlags);
1031
0
1032
0
      if (!mImage->IsComplete()) {
1033
0
        result &= ImgDrawResult::SUCCESS_NOT_COMPLETE;
1034
0
      }
1035
0
1036
0
      return result;
1037
0
    }
1038
0
1039
0
    nsSize repeatSize;
1040
0
    nsRect fillRect(aFill);
1041
0
    nsRect tile = ComputeTile(fillRect, aHFill, aVFill, aUnitSize, repeatSize);
1042
0
    CSSIntSize imageSize(srcRect.width, srcRect.height);
1043
0
1044
0
    ImgDrawResult result = nsLayoutUtils::DrawBackgroundImage(aRenderingContext,
1045
0
                                                              mForFrame,
1046
0
                                                              aPresContext,
1047
0
                                                              subImage,
1048
0
                                                              imageSize,
1049
0
                                                              samplingFilter,
1050
0
                                                              tile,
1051
0
                                                              fillRect,
1052
0
                                                              repeatSize,
1053
0
                                                              tile.TopLeft(),
1054
0
                                                              aDirtyRect,
1055
0
                                                              drawFlags,
1056
0
                                                              ExtendMode::CLAMP,
1057
0
                                                              1.0);
1058
0
1059
0
    if (!mImage->IsComplete()) {
1060
0
      result &= ImgDrawResult::SUCCESS_NOT_COMPLETE;
1061
0
    }
1062
0
1063
0
    return result;
1064
0
  }
1065
0
1066
0
  nsSize repeatSize(aFill.Size());
1067
0
  nsRect fillRect(aFill);
1068
0
  nsRect destTile =
1069
0
    RequiresScaling(fillRect, aHFill, aVFill, aUnitSize)
1070
0
      ? ComputeTile(fillRect, aHFill, aVFill, aUnitSize, repeatSize)
1071
0
      : fillRect;
1072
0
1073
0
  return Draw(aPresContext,
1074
0
              aRenderingContext,
1075
0
              aDirtyRect,
1076
0
              destTile,
1077
0
              fillRect,
1078
0
              destTile.TopLeft(),
1079
0
              repeatSize,
1080
0
              aSrc);
1081
0
}
1082
1083
ImgDrawResult
1084
nsImageRenderer::DrawShapeImage(nsPresContext* aPresContext,
1085
                                gfxContext& aRenderingContext)
1086
0
{
1087
0
  if (!IsReady()) {
1088
0
    MOZ_ASSERT_UNREACHABLE("Ensure PrepareImage() has returned true before "
1089
0
                           "calling me");
1090
0
    return ImgDrawResult::NOT_READY;
1091
0
  }
1092
0
1093
0
  if (mSize.width <= 0 || mSize.height <= 0) {
1094
0
    return ImgDrawResult::SUCCESS;
1095
0
  }
1096
0
1097
0
  ImgDrawResult result = ImgDrawResult::SUCCESS;
1098
0
1099
0
  switch (mType) {
1100
0
    case eStyleImageType_Image: {
1101
0
      uint32_t drawFlags =
1102
0
        ConvertImageRendererToDrawFlags(mFlags) | imgIContainer::FRAME_FIRST;
1103
0
      nsRect dest(nsPoint(0, 0), mSize);
1104
0
      // We have a tricky situation in our choice of SamplingFilter. Shape
1105
0
      // images define a float area based on the alpha values in the rendered
1106
0
      // pixels. When multiple device pixels are used for one css pixel, the
1107
0
      // sampling can change crisp edges into aliased edges. For visual pixels,
1108
0
      // that's usually the right choice. For defining a float area, it can
1109
0
      // cause problems. If a style is using a shape-image-threshold value that
1110
0
      // is less than the alpha of the edge pixels, any filtering may smear the
1111
0
      // alpha into adjacent pixels and expand the float area in a confusing
1112
0
      // way. Since the alpha threshold can be set precisely in CSS, and since a
1113
0
      // web author may be counting on that threshold to define a precise float
1114
0
      // area from an image, it is least confusing to have the rendered pixels
1115
0
      // have unfiltered alpha. We use SamplingFilter::POINT to ensure that each
1116
0
      // rendered pixel has an alpha that precisely matches the alpha of the
1117
0
      // closest pixel in the image.
1118
0
      result = nsLayoutUtils::DrawSingleImage(aRenderingContext,
1119
0
                                              aPresContext,
1120
0
                                              mImageContainer,
1121
0
                                              SamplingFilter::POINT,
1122
0
                                              dest,
1123
0
                                              dest,
1124
0
                                              Nothing(),
1125
0
                                              drawFlags,
1126
0
                                              nullptr,
1127
0
                                              nullptr);
1128
0
      break;
1129
0
    }
1130
0
1131
0
    case eStyleImageType_Gradient: {
1132
0
      nsCSSGradientRenderer renderer = nsCSSGradientRenderer::Create(
1133
0
        aPresContext, mForFrame->Style(), mGradientData, mSize);
1134
0
      nsRect dest(nsPoint(0, 0), mSize);
1135
0
1136
0
      renderer.Paint(aRenderingContext,
1137
0
                     dest,
1138
0
                     dest,
1139
0
                     mSize,
1140
0
                     CSSIntRect::FromAppUnitsRounded(dest),
1141
0
                     dest,
1142
0
                     1.0);
1143
0
      break;
1144
0
    }
1145
0
1146
0
    default:
1147
0
      // Unsupported image type.
1148
0
      result = ImgDrawResult::BAD_IMAGE;
1149
0
      break;
1150
0
  }
1151
0
1152
0
  return result;
1153
0
}
1154
1155
bool
1156
nsImageRenderer::IsRasterImage()
1157
0
{
1158
0
  if (mType != eStyleImageType_Image || !mImageContainer)
1159
0
    return false;
1160
0
  return mImageContainer->GetType() == imgIContainer::TYPE_RASTER;
1161
0
}
1162
1163
bool
1164
nsImageRenderer::IsAnimatedImage()
1165
0
{
1166
0
  if (mType != eStyleImageType_Image || !mImageContainer)
1167
0
    return false;
1168
0
  bool animated = false;
1169
0
  if (NS_SUCCEEDED(mImageContainer->GetAnimated(&animated)) && animated)
1170
0
    return true;
1171
0
1172
0
  return false;
1173
0
}
1174
1175
already_AddRefed<imgIContainer>
1176
nsImageRenderer::GetImage()
1177
0
{
1178
0
  if (mType != eStyleImageType_Image || !mImageContainer) {
1179
0
    return nullptr;
1180
0
  }
1181
0
1182
0
  nsCOMPtr<imgIContainer> image = mImageContainer;
1183
0
  return image.forget();
1184
0
}
1185
1186
bool
1187
nsImageRenderer::IsImageContainerAvailable(layers::LayerManager* aManager,
1188
                                           uint32_t aFlags)
1189
0
{
1190
0
  if (!mImageContainer) {
1191
0
    return false;
1192
0
  }
1193
0
  return mImageContainer->IsImageContainerAvailable(aManager, aFlags);
1194
0
}
1195
1196
void
1197
nsImageRenderer::PurgeCacheForViewportChange(
1198
  const Maybe<nsSize>& aSVGViewportSize,
1199
  const bool aHasIntrinsicRatio)
1200
0
{
1201
0
  // Check if we should flush the cached data - only vector images need to do
1202
0
  // the check since they might not have fixed ratio.
1203
0
  if (mImageContainer &&
1204
0
      mImageContainer->GetType() == imgIContainer::TYPE_VECTOR) {
1205
0
    mImage->PurgeCacheForViewportChange(aSVGViewportSize, aHasIntrinsicRatio);
1206
0
  }
1207
0
}
1208
1209
already_AddRefed<nsStyleGradient>
1210
nsImageRenderer::GetGradientData()
1211
0
{
1212
0
  RefPtr<nsStyleGradient> res = mGradientData;
1213
0
  return res.forget();
1214
0
}