tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsImageRenderer.cpp (43697B)


      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 #ifdef MOZ_WIDGET_GTK
     14 #  include "nsIconChannel.h"
     15 #endif
     16 #include "ImageOps.h"
     17 #include "ImageRegion.h"
     18 #include "gfxContext.h"
     19 #include "gfxDrawable.h"
     20 #include "mozilla/ISVGDisplayableFrame.h"
     21 #include "mozilla/SVGIntegrationUtils.h"
     22 #include "mozilla/SVGObserverUtils.h"
     23 #include "mozilla/SVGPaintServerFrame.h"
     24 #include "mozilla/StaticPrefs_image.h"
     25 #include "mozilla/image/WebRenderImageProvider.h"
     26 #include "mozilla/layers/RenderRootStateManager.h"
     27 #include "mozilla/layers/StackingContextHelper.h"
     28 #include "mozilla/layers/WebRenderLayerManager.h"
     29 #include "nsCSSRendering.h"
     30 #include "nsCSSRenderingGradients.h"
     31 #include "nsContentUtils.h"
     32 #include "nsDeviceContext.h"
     33 #include "nsIFrame.h"
     34 #include "nsLayoutUtils.h"
     35 #include "nsStyleStructInlines.h"
     36 
     37 using namespace mozilla;
     38 using namespace mozilla::gfx;
     39 using namespace mozilla::image;
     40 using namespace mozilla::layers;
     41 
     42 nsSize CSSSizeOrRatio::ComputeConcreteSize() const {
     43  NS_ASSERTION(CanComputeConcreteSize(), "Cannot compute");
     44  if (mHasWidth && mHasHeight) {
     45    return nsSize(mWidth, mHeight);
     46  }
     47  if (mHasWidth) {
     48    return nsSize(mWidth, mRatio.Inverted().ApplyTo(mWidth));
     49  }
     50 
     51  MOZ_ASSERT(mHasHeight);
     52  return nsSize(mRatio.ApplyTo(mHeight), mHeight);
     53 }
     54 
     55 nsImageRenderer::nsImageRenderer(nsIFrame* aForFrame, const StyleImage* aImage,
     56                                 uint32_t aFlags)
     57    : mForFrame(aForFrame),
     58      mImage(&aImage->FinalImage()),
     59      mImageResolution(aImage->GetResolution(aForFrame->Style())),
     60      mType(mImage->tag),
     61      mImageContainer(nullptr),
     62      mGradientData(nullptr),
     63      mPaintServerFrame(nullptr),
     64      mPrepareResult(ImgDrawResult::NOT_READY),
     65      mSize(0, 0),
     66      mFlags(aFlags),
     67      mExtendMode(ExtendMode::CLAMP),
     68      mMaskOp(StyleMaskMode::MatchSource) {}
     69 
     70 using SymbolicImageKey = std::tuple<RefPtr<nsAtom>, int, nscolor>;
     71 struct SymbolicImageEntry {
     72  SymbolicImageKey mKey;
     73  nsCOMPtr<imgIContainer> mImage;
     74 };
     75 struct SymbolicImageCache final
     76    : public mozilla::MruCache<SymbolicImageKey, SymbolicImageEntry,
     77                               SymbolicImageCache, 5> {
     78  static HashNumber Hash(const KeyType& aKey) {
     79    return AddToHash(std::get<0>(aKey)->hash(),
     80                     HashGeneric(std::get<1>(aKey), std::get<2>(aKey)));
     81  }
     82  static bool Match(const KeyType& aKey, const ValueType& aVal) {
     83    return aVal.mKey == aKey;
     84  }
     85 };
     86 
     87 NS_DECLARE_FRAME_PROPERTY_DELETABLE(SymbolicImageCacheProp, SymbolicImageCache);
     88 
     89 static already_AddRefed<imgIContainer> GetSymbolicIconImage(nsAtom* aName,
     90                                                            int aScale,
     91                                                            nsIFrame* aFrame) {
     92  if (NS_WARN_IF(!XRE_IsParentProcess())) {
     93    return nullptr;
     94  }
     95  const auto fg = aFrame->StyleText()->mColor.ToColor();
     96  auto key = std::make_tuple(aName, aScale, fg);
     97  auto* cache = aFrame->GetProperty(SymbolicImageCacheProp());
     98  if (!cache) {
     99    cache = new SymbolicImageCache();
    100    aFrame->SetProperty(SymbolicImageCacheProp(), cache);
    101  }
    102  auto lookup = cache->Lookup(key);
    103  if (lookup) {
    104    return do_AddRef(lookup.Data().mImage);
    105  }
    106  RefPtr<gfx::DataSourceSurface> surface;
    107 #ifdef MOZ_WIDGET_GTK
    108  surface =
    109      nsIconChannel::GetSymbolicIcon(nsAtomCString(aName), 16, aScale, fg);
    110 #endif
    111  if (NS_WARN_IF(!surface)) {
    112    return nullptr;
    113  }
    114  RefPtr drawable = new gfxSurfaceDrawable(surface, surface->GetSize());
    115  nsCOMPtr<imgIContainer> container = ImageOps::CreateFromDrawable(drawable);
    116  MOZ_ASSERT(container);
    117  lookup.Set(SymbolicImageEntry{std::move(key), std::move(container)});
    118  return do_AddRef(lookup.Data().mImage);
    119 }
    120 
    121 bool nsImageRenderer::PrepareImage() {
    122  if (mImage->IsNone()) {
    123    mPrepareResult = ImgDrawResult::BAD_IMAGE;
    124    return false;
    125  }
    126 
    127  const bool isImageRequest = mImage->IsImageRequestType();
    128  MOZ_ASSERT_IF(!isImageRequest, !mImage->GetImageRequest());
    129  imgRequestProxy* request = nullptr;
    130  if (isImageRequest) {
    131    request = mImage->GetImageRequest();
    132    if (!request) {
    133      // request could be null here if the StyleImage refused
    134      // to load a same-document URL, or the url was invalid, for example.
    135      mPrepareResult = ImgDrawResult::BAD_IMAGE;
    136      return false;
    137    }
    138  }
    139 
    140  if (!mImage->IsComplete()) {
    141    MOZ_DIAGNOSTIC_ASSERT(isImageRequest);
    142 
    143    // Make sure the image is actually decoding.
    144    bool frameComplete = request->StartDecodingWithResult(
    145        imgIContainer::FLAG_ASYNC_NOTIFY |
    146        imgIContainer::FLAG_AVOID_REDECODE_FOR_SIZE);
    147 
    148    // Boost the loading priority since we know we want to draw the image.
    149    if (mFlags & nsImageRenderer::FLAG_PAINTING_TO_WINDOW) {
    150      request->BoostPriority(imgIRequest::CATEGORY_DISPLAY);
    151    }
    152 
    153    // Check again to see if we finished.
    154    // We cannot prepare the image for rendering if it is not fully loaded.
    155    if (!frameComplete && !mImage->IsComplete()) {
    156      uint32_t imageStatus = 0;
    157      request->GetImageStatus(&imageStatus);
    158      if (imageStatus & imgIRequest::STATUS_ERROR) {
    159        mPrepareResult = ImgDrawResult::BAD_IMAGE;
    160        return false;
    161      }
    162 
    163      // If not errored, and we requested a sync decode, and the image has
    164      // loaded, push on through because the Draw() will do a sync decode then.
    165      const bool syncDecodeWillComplete =
    166          (mFlags & FLAG_SYNC_DECODE_IMAGES) &&
    167          (imageStatus & imgIRequest::STATUS_LOAD_COMPLETE);
    168 
    169      bool canDrawPartial =
    170          (mFlags & nsImageRenderer::FLAG_DRAW_PARTIAL_FRAMES) &&
    171          isImageRequest && mImage->IsSizeAvailable();
    172 
    173      // If we are drawing a partial frame then we want to make sure there are
    174      // some pixels to draw, otherwise we waste effort pushing through to draw
    175      // nothing.
    176      if (!syncDecodeWillComplete && canDrawPartial) {
    177        nsCOMPtr<imgIContainer> image;
    178        canDrawPartial =
    179            canDrawPartial &&
    180            NS_SUCCEEDED(request->GetImage(getter_AddRefs(image))) && image &&
    181            image->GetType() == imgIContainer::TYPE_RASTER &&
    182            image->HasDecodedPixels();
    183      }
    184 
    185      // If we can draw partial then proceed if we at least have the size
    186      // available.
    187      if (!(syncDecodeWillComplete || canDrawPartial)) {
    188        mPrepareResult = ImgDrawResult::NOT_READY;
    189        return false;
    190      }
    191    }
    192  }
    193 
    194  if (isImageRequest) {
    195    nsCOMPtr<imgIContainer> srcImage;
    196    nsresult rv = request->GetImage(getter_AddRefs(srcImage));
    197    MOZ_ASSERT(NS_SUCCEEDED(rv) && srcImage,
    198               "If GetImage() is failing, mImage->IsComplete() "
    199               "should have returned false");
    200    if (!NS_SUCCEEDED(rv)) {
    201      srcImage = nullptr;
    202    }
    203 
    204    if (srcImage) {
    205      StyleImageOrientation orientation =
    206          mForFrame->StyleVisibility()->UsedImageOrientation(request);
    207      srcImage = nsLayoutUtils::OrientImage(srcImage, orientation);
    208    }
    209 
    210    mImageContainer.swap(srcImage);
    211    mPrepareResult = ImgDrawResult::SUCCESS;
    212  } else if (mImage->IsGradient()) {
    213    mGradientData = &*mImage->AsGradient();
    214    mPrepareResult = ImgDrawResult::SUCCESS;
    215  } else if (mImage->IsElement()) {
    216    dom::Element* paintElement =  // may be null
    217        SVGObserverUtils::GetAndObserveBackgroundImage(
    218            mForFrame->FirstContinuation(), mImage->AsElement().AsAtom());
    219    // If the referenced element is an <img>, <canvas>, or <video> element,
    220    // prefer SurfaceFromElement as it's more reliable.
    221    mImageElementSurface = nsLayoutUtils::SurfaceFromElement(paintElement);
    222 
    223    if (!mImageElementSurface.GetSourceSurface()) {
    224      nsIFrame* paintServerFrame =
    225          paintElement ? paintElement->GetPrimaryFrame() : nullptr;
    226      // If there's no referenced frame, or the referenced frame is
    227      // non-displayable SVG, then we have nothing valid to paint.
    228      if (!paintServerFrame || (paintServerFrame->IsSVGFrame() &&
    229                                !static_cast<SVGPaintServerFrame*>(
    230                                    do_QueryFrame(paintServerFrame)) &&
    231                                !static_cast<ISVGDisplayableFrame*>(
    232                                    do_QueryFrame(paintServerFrame)))) {
    233        mPrepareResult = ImgDrawResult::BAD_IMAGE;
    234        return false;
    235      }
    236      mPaintServerFrame = paintServerFrame;
    237    }
    238 
    239    mPrepareResult = ImgDrawResult::SUCCESS;
    240  } else if (mImage->IsMozSymbolicIcon()) {
    241    auto deviceScale =
    242        std::ceil(mForFrame->PresContext()->CSSToDevPixelScale().scale);
    243    mImageResolution.ScaleBy(deviceScale);
    244    mImageContainer = GetSymbolicIconImage(mImage->AsMozSymbolicIcon().AsAtom(),
    245                                           int(deviceScale), mForFrame);
    246    if (!mImageContainer) {
    247      mPrepareResult = ImgDrawResult::BAD_IMAGE;
    248      return false;
    249    }
    250    mPrepareResult = ImgDrawResult::SUCCESS;
    251  } else if (mImage->IsCrossFade()) {
    252    // See bug 546052 - cross-fade implementation still being worked
    253    // on.
    254    mPrepareResult = ImgDrawResult::BAD_IMAGE;
    255    return false;
    256  } else {
    257    MOZ_ASSERT(mImage->IsNone(), "Unknown image type?");
    258  }
    259 
    260  return IsReady();
    261 }
    262 
    263 CSSSizeOrRatio nsImageRenderer::ComputeIntrinsicSize() {
    264  NS_ASSERTION(IsReady(),
    265               "Ensure PrepareImage() has returned true "
    266               "before calling me");
    267 
    268  CSSSizeOrRatio result;
    269  switch (mType) {
    270    case StyleImage::Tag::MozSymbolicIcon:
    271    case StyleImage::Tag::Url: {
    272      bool haveWidth, haveHeight;
    273      CSSIntSize imageIntSize;
    274      nsLayoutUtils::ComputeSizeForDrawing(mImageContainer, mImageResolution,
    275                                           imageIntSize, result.mRatio,
    276                                           haveWidth, haveHeight);
    277      if (haveWidth) {
    278        result.SetWidth(CSSPixel::ToAppUnits(imageIntSize.width));
    279      }
    280      if (haveHeight) {
    281        result.SetHeight(CSSPixel::ToAppUnits(imageIntSize.height));
    282      }
    283 
    284      // If we know the aspect ratio and one of the dimensions,
    285      // we can compute the other missing width or height.
    286      if (!haveHeight && haveWidth && result.mRatio) {
    287        CSSIntCoord intrinsicHeight =
    288            result.mRatio.Inverted().ApplyTo(imageIntSize.width);
    289        result.SetHeight(nsPresContext::CSSPixelsToAppUnits(intrinsicHeight));
    290      } else if (haveHeight && !haveWidth && result.mRatio) {
    291        CSSIntCoord intrinsicWidth = result.mRatio.ApplyTo(imageIntSize.height);
    292        result.SetWidth(nsPresContext::CSSPixelsToAppUnits(intrinsicWidth));
    293      }
    294 
    295      break;
    296    }
    297    case StyleImage::Tag::Element: {
    298      // XXX element() should have the width/height of the referenced element,
    299      //     and that element's ratio, if it matches.  If it doesn't match, it
    300      //     should have no width/height or ratio.  See element() in CSS images:
    301      //     <http://dev.w3.org/csswg/css-images-4/#element-notation>.
    302      //     Make sure to change nsStyleImageLayers::Size::DependsOnFrameSize
    303      //     when fixing this!
    304      if (mPaintServerFrame) {
    305        // SVG images have no intrinsic size
    306        if (!mPaintServerFrame->IsSVGFrame()) {
    307          // The intrinsic image size for a generic nsIFrame paint server is
    308          // the union of the border-box rects of all of its continuations,
    309          // rounded to device pixels.
    310          int32_t appUnitsPerDevPixel =
    311              mForFrame->PresContext()->AppUnitsPerDevPixel();
    312          result.SetSize(IntSizeToAppUnits(
    313              SVGIntegrationUtils::GetContinuationUnionSize(mPaintServerFrame)
    314                  .ToNearestPixels(appUnitsPerDevPixel),
    315              appUnitsPerDevPixel));
    316        }
    317      } else {
    318        NS_ASSERTION(mImageElementSurface.GetSourceSurface(),
    319                     "Surface should be ready.");
    320        IntSize surfaceSize = mImageElementSurface.mSize;
    321        result.SetSize(
    322            nsSize(nsPresContext::CSSPixelsToAppUnits(surfaceSize.width),
    323                   nsPresContext::CSSPixelsToAppUnits(surfaceSize.height)));
    324      }
    325      break;
    326    }
    327    case StyleImage::Tag::ImageSet:
    328      MOZ_FALLTHROUGH_ASSERT("image-set() should be resolved already");
    329    case StyleImage::Tag::LightDark:
    330      MOZ_FALLTHROUGH_ASSERT("light-dark() should be resolved already");
    331    // Bug 546052 cross-fade not yet implemented.
    332    case StyleImage::Tag::CrossFade:
    333    // Per <http://dev.w3.org/csswg/css3-images/#gradients>, gradients have no
    334    // intrinsic dimensions.
    335    case StyleImage::Tag::Gradient:
    336    case StyleImage::Tag::None:
    337      break;
    338  }
    339 
    340  return result;
    341 }
    342 
    343 /* static */
    344 nsSize nsImageRenderer::ComputeConcreteSize(
    345    const CSSSizeOrRatio& aSpecifiedSize, const CSSSizeOrRatio& aIntrinsicSize,
    346    const nsSize& aDefaultSize) {
    347  // The specified size is fully specified, just use that
    348  if (aSpecifiedSize.IsConcrete()) {
    349    return aSpecifiedSize.ComputeConcreteSize();
    350  }
    351 
    352  MOZ_ASSERT(!aSpecifiedSize.mHasWidth || !aSpecifiedSize.mHasHeight);
    353 
    354  if (!aSpecifiedSize.mHasWidth && !aSpecifiedSize.mHasHeight) {
    355    // no specified size, try using the intrinsic size
    356    if (aIntrinsicSize.CanComputeConcreteSize()) {
    357      return aIntrinsicSize.ComputeConcreteSize();
    358    }
    359 
    360    if (aIntrinsicSize.mHasWidth) {
    361      return nsSize(aIntrinsicSize.mWidth, aDefaultSize.height);
    362    }
    363    if (aIntrinsicSize.mHasHeight) {
    364      return nsSize(aDefaultSize.width, aIntrinsicSize.mHeight);
    365    }
    366 
    367    // couldn't use the intrinsic size either, revert to using the default size
    368    return ComputeConstrainedSize(aDefaultSize, aIntrinsicSize.mRatio, CONTAIN);
    369  }
    370 
    371  MOZ_ASSERT(aSpecifiedSize.mHasWidth || aSpecifiedSize.mHasHeight);
    372 
    373  // The specified height is partial, try to compute the missing part.
    374  if (aSpecifiedSize.mHasWidth) {
    375    nscoord height;
    376    if (aIntrinsicSize.HasRatio()) {
    377      height = aIntrinsicSize.mRatio.Inverted().ApplyTo(aSpecifiedSize.mWidth);
    378    } else if (aIntrinsicSize.mHasHeight) {
    379      height = aIntrinsicSize.mHeight;
    380    } else {
    381      height = aDefaultSize.height;
    382    }
    383    return nsSize(aSpecifiedSize.mWidth, height);
    384  }
    385 
    386  MOZ_ASSERT(aSpecifiedSize.mHasHeight);
    387  nscoord width;
    388  if (aIntrinsicSize.HasRatio()) {
    389    width = aIntrinsicSize.mRatio.ApplyTo(aSpecifiedSize.mHeight);
    390  } else if (aIntrinsicSize.mHasWidth) {
    391    width = aIntrinsicSize.mWidth;
    392  } else {
    393    width = aDefaultSize.width;
    394  }
    395  return nsSize(width, aSpecifiedSize.mHeight);
    396 }
    397 
    398 /* static */
    399 nsSize nsImageRenderer::ComputeConstrainedSize(
    400    const nsSize& aConstrainingSize, const AspectRatio& aIntrinsicRatio,
    401    FitType aFitType) {
    402  if (!aIntrinsicRatio) {
    403    return aConstrainingSize;
    404  }
    405 
    406  // Suppose we're doing a "contain" fit. If the image's aspect ratio has a
    407  // "fatter" shape than the constraint area, then we need to use the
    408  // constraint area's full width, and we need to use the aspect ratio to
    409  // produce a height. On the other hand, if the aspect ratio is "skinnier", we
    410  // use the constraint area's full height, and we use the aspect ratio to
    411  // produce a width. (If instead we're doing a "cover" fit, then it can easily
    412  // be seen that we should do precisely the opposite.)
    413  //
    414  // We check if the image's aspect ratio is "fatter" than the constraint area
    415  // by simply applying the aspect ratio to the constraint area's height, to
    416  // produce a "hypothetical width", and we check whether that
    417  // aspect-ratio-provided "hypothetical width" is wider than the constraint
    418  // area's actual width. If it is, then the aspect ratio is fatter than the
    419  // constraint area.
    420  //
    421  // This is equivalent to the more descriptive alternative:
    422  //
    423  //   AspectRatio::FromSize(aConstrainingSize) < aIntrinsicRatio
    424  //
    425  // But gracefully handling the case where one of the two dimensions from
    426  // aConstrainingSize is zero. This is easy to prove since:
    427  //
    428  //   aConstrainingSize.width / aConstrainingSize.height < aIntrinsicRatio
    429  //
    430  // Is trivially equivalent to:
    431  //
    432  //   aIntrinsicRatio.width < aIntrinsicRatio * aConstrainingSize.height
    433  //
    434  // For the cases where height is not zero.
    435  //
    436  // We use float math here to avoid losing precision for very large backgrounds
    437  // since we use saturating nscoord math otherwise.
    438  const float constraintWidth = float(aConstrainingSize.width);
    439  const float hypotheticalWidth =
    440      aIntrinsicRatio.ApplyToFloat(aConstrainingSize.height);
    441 
    442  nsSize size;
    443  if ((aFitType == CONTAIN) == (constraintWidth < hypotheticalWidth)) {
    444    size.width = aConstrainingSize.width;
    445    size.height = aIntrinsicRatio.Inverted().ApplyTo(aConstrainingSize.width);
    446    // If we're reducing the size by less than one css pixel, then just use the
    447    // constraining size.
    448    if (aFitType == CONTAIN &&
    449        aConstrainingSize.height - size.height < AppUnitsPerCSSPixel()) {
    450      size.height = aConstrainingSize.height;
    451    }
    452  } else {
    453    size.height = aConstrainingSize.height;
    454    size.width = aIntrinsicRatio.ApplyTo(aConstrainingSize.height);
    455    if (aFitType == CONTAIN &&
    456        aConstrainingSize.width - size.width < AppUnitsPerCSSPixel()) {
    457      size.width = aConstrainingSize.width;
    458    }
    459  }
    460  return size;
    461 }
    462 
    463 /**
    464 * mSize is the image's "preferred" size for this particular rendering, while
    465 * the drawn (aka concrete) size is the actual rendered size after accounting
    466 * for background-size etc..  The preferred size is most often the image's
    467 * intrinsic dimensions.  But for images with incomplete intrinsic dimensions,
    468 * the preferred size varies, depending on the specified and default sizes, see
    469 * nsImageRenderer::Compute*Size.
    470 *
    471 * This distinction is necessary because the components of a vector image are
    472 * specified with respect to its preferred size for a rendering situation, not
    473 * to its actual rendered size.  For example, consider a 4px wide background
    474 * vector image with no height which contains a left-aligned
    475 * 2px wide black rectangle with height 100%.  If the background-size width is
    476 * auto (or 4px), the vector image will render 4px wide, and the black rectangle
    477 * will be 2px wide.  If the background-size width is 8px, the vector image will
    478 * render 8px wide, and the black rectangle will be 4px wide -- *not* 2px wide.
    479 * In both cases mSize.width will be 4px; but in the first case the returned
    480 * width will be 4px, while in the second case the returned width will be 8px.
    481 */
    482 void nsImageRenderer::SetPreferredSize(const CSSSizeOrRatio& aIntrinsicSize,
    483                                       const nsSize& aDefaultSize) {
    484  mSize.width =
    485      aIntrinsicSize.mHasWidth ? aIntrinsicSize.mWidth : aDefaultSize.width;
    486  mSize.height =
    487      aIntrinsicSize.mHasHeight ? aIntrinsicSize.mHeight : aDefaultSize.height;
    488 }
    489 
    490 // Convert from nsImageRenderer flags to the flags we want to use for drawing in
    491 // the imgIContainer namespace.
    492 static uint32_t ConvertImageRendererToDrawFlags(uint32_t aImageRendererFlags) {
    493  uint32_t drawFlags = imgIContainer::FLAG_ASYNC_NOTIFY;
    494  if (aImageRendererFlags & nsImageRenderer::FLAG_SYNC_DECODE_IMAGES) {
    495    drawFlags |= imgIContainer::FLAG_SYNC_DECODE;
    496  } else {
    497    drawFlags |= imgIContainer::FLAG_SYNC_DECODE_IF_FAST;
    498  }
    499  if (aImageRendererFlags & (nsImageRenderer::FLAG_PAINTING_TO_WINDOW |
    500                             nsImageRenderer::FLAG_HIGH_QUALITY_SCALING)) {
    501    drawFlags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
    502  }
    503  return drawFlags;
    504 }
    505 
    506 ImgDrawResult nsImageRenderer::Draw(nsPresContext* aPresContext,
    507                                    gfxContext& aRenderingContext,
    508                                    const nsRect& aDirtyRect,
    509                                    const nsRect& aDest, const nsRect& aFill,
    510                                    const nsPoint& aAnchor,
    511                                    const nsSize& aRepeatSize,
    512                                    const CSSIntRect& aSrc, float aOpacity) {
    513  if (!IsReady()) {
    514    MOZ_ASSERT_UNREACHABLE(
    515        "Ensure PrepareImage() has returned true before "
    516        "calling me");
    517    return ImgDrawResult::TEMPORARY_ERROR;
    518  }
    519 
    520  if (aDest.IsEmpty() || aFill.IsEmpty() || mSize.width <= 0 ||
    521      mSize.height <= 0) {
    522    return ImgDrawResult::SUCCESS;
    523  }
    524 
    525  SamplingFilter samplingFilter =
    526      nsLayoutUtils::GetSamplingFilterForFrame(mForFrame);
    527  ImgDrawResult result = ImgDrawResult::SUCCESS;
    528  gfxContext* ctx = &aRenderingContext;
    529  Maybe<gfxContext> tempCtx;
    530  CompositionOp savedCompositionOp = CompositionOp::OP_OVER;
    531 
    532  if (mMaskOp == StyleMaskMode::Luminance) {
    533    savedCompositionOp = ctx->CurrentOp();
    534    ctx->SetOp(CompositionOp::OP_OVER);
    535 
    536    RefPtr<DrawTarget> tempDT = ctx->GetDrawTarget()->CreateClippedDrawTarget(
    537        Rect(), SurfaceFormat::B8G8R8A8);
    538    if (!tempDT || !tempDT->IsValid()) {
    539      gfxDevCrash(LogReason::InvalidContext)
    540          << "ImageRenderer::Draw problem " << gfx::hexa(tempDT);
    541      return ImgDrawResult::TEMPORARY_ERROR;
    542    }
    543    tempCtx.emplace(tempDT, /* aPreserveTransform */ true);
    544    ctx = &tempCtx.ref();
    545    if (!ctx) {
    546      gfxDevCrash(LogReason::InvalidContext)
    547          << "ImageRenderer::Draw problem " << gfx::hexa(tempDT);
    548      return ImgDrawResult::TEMPORARY_ERROR;
    549    }
    550  } else if (ctx->CurrentOp() != CompositionOp::OP_OVER) {
    551    savedCompositionOp = ctx->CurrentOp();
    552    ctx->SetOp(CompositionOp::OP_OVER);
    553 
    554    IntRect clipRect =
    555        RoundedOut(ToRect(ctx->GetClipExtents(gfxContext::eDeviceSpace)));
    556    ctx->GetDrawTarget()->PushLayerWithBlend(false, 1.0, nullptr,
    557                                             mozilla::gfx::Matrix(), clipRect,
    558                                             false, savedCompositionOp);
    559  }
    560 
    561  switch (mType) {
    562    case StyleImage::Tag::MozSymbolicIcon:
    563    case StyleImage::Tag::Url: {
    564      result = nsLayoutUtils::DrawBackgroundImage(
    565          *ctx, mForFrame, aPresContext, mImageContainer, samplingFilter, aDest,
    566          aFill, aRepeatSize, aAnchor, aDirtyRect,
    567          ConvertImageRendererToDrawFlags(mFlags), mExtendMode, aOpacity);
    568      break;
    569    }
    570    case StyleImage::Tag::Gradient: {
    571      nsCSSGradientRenderer renderer = nsCSSGradientRenderer::Create(
    572          aPresContext, mForFrame->Style(), *mGradientData, mSize);
    573 
    574      renderer.Paint(*ctx, aDest, aFill, aRepeatSize, aSrc, aDirtyRect,
    575                     aOpacity);
    576      break;
    577    }
    578    case StyleImage::Tag::Element: {
    579      RefPtr<gfxDrawable> drawable = DrawableForElement(aDest, *ctx);
    580      if (!drawable) {
    581        NS_WARNING("Could not create drawable for element");
    582        return ImgDrawResult::TEMPORARY_ERROR;
    583      }
    584 
    585      nsCOMPtr<imgIContainer> image(ImageOps::CreateFromDrawable(drawable));
    586      result = nsLayoutUtils::DrawImage(
    587          *ctx, mForFrame->Style(), aPresContext, image, samplingFilter, aDest,
    588          aFill, aAnchor, aDirtyRect, ConvertImageRendererToDrawFlags(mFlags),
    589          aOpacity);
    590      break;
    591    }
    592    case StyleImage::Tag::ImageSet:
    593      MOZ_FALLTHROUGH_ASSERT("image-set() should be resolved already");
    594    case StyleImage::Tag::LightDark:
    595      MOZ_FALLTHROUGH_ASSERT("light-dark() should be resolved already");
    596    // See bug 546052 - cross-fade implementation still being worked
    597    // on.
    598    case StyleImage::Tag::CrossFade:
    599    case StyleImage::Tag::None:
    600      break;
    601  }
    602 
    603  if (mMaskOp == StyleMaskMode::Luminance) {
    604    RefPtr<SourceSurface> surf = ctx->GetDrawTarget()->IntoLuminanceSource(
    605        LuminanceType::LUMINANCE, 1.0f);
    606    DrawTarget* dt = aRenderingContext.GetDrawTarget();
    607    Matrix oldTransform = dt->GetTransform();
    608    dt->SetTransform(Matrix());
    609    dt->MaskSurface(ColorPattern(DeviceColor(0, 0, 0, 1.0f)), surf, Point(0, 0),
    610                    DrawOptions(1.0f, savedCompositionOp));
    611    dt->SetTransform(oldTransform);
    612    aRenderingContext.SetOp(savedCompositionOp);
    613  } else if (savedCompositionOp != CompositionOp::OP_OVER) {
    614    aRenderingContext.GetDrawTarget()->PopLayer();
    615    aRenderingContext.SetOp(savedCompositionOp);
    616  }
    617 
    618  if (!mImage->IsComplete()) {
    619    result &= ImgDrawResult::SUCCESS_NOT_COMPLETE;
    620  }
    621 
    622  return result;
    623 }
    624 
    625 ImgDrawResult nsImageRenderer::BuildWebRenderDisplayItems(
    626    nsPresContext* aPresContext, mozilla::wr::DisplayListBuilder& aBuilder,
    627    mozilla::wr::IpcResourceUpdateQueue& aResources,
    628    const mozilla::layers::StackingContextHelper& aSc,
    629    mozilla::layers::RenderRootStateManager* aManager, nsDisplayItem* aItem,
    630    const nsRect& aDirtyRect, const nsRect& aDest, const nsRect& aFill,
    631    const nsPoint& aAnchor, const nsSize& aRepeatSize, const CSSIntRect& aSrc,
    632    float aOpacity) {
    633  if (!IsReady()) {
    634    MOZ_ASSERT_UNREACHABLE(
    635        "Ensure PrepareImage() has returned true before "
    636        "calling me");
    637    return ImgDrawResult::NOT_READY;
    638  }
    639 
    640  if (aDest.IsEmpty() || aFill.IsEmpty() || mSize.width <= 0 ||
    641      mSize.height <= 0) {
    642    return ImgDrawResult::SUCCESS;
    643  }
    644 
    645  ImgDrawResult drawResult = ImgDrawResult::SUCCESS;
    646  switch (mType) {
    647    case StyleImage::Tag::Gradient: {
    648      nsCSSGradientRenderer renderer = nsCSSGradientRenderer::Create(
    649          aPresContext, mForFrame->Style(), *mGradientData, mSize);
    650 
    651      renderer.BuildWebRenderDisplayItems(aBuilder, aSc, aDest, aFill,
    652                                          aRepeatSize, aSrc,
    653                                          !aItem->BackfaceIsHidden(), aOpacity);
    654      break;
    655    }
    656    case StyleImage::Tag::MozSymbolicIcon:
    657    case StyleImage::Tag::Url: {
    658      ExtendMode extendMode = mExtendMode;
    659      if (aDest.Contains(aFill)) {
    660        extendMode = ExtendMode::CLAMP;
    661      }
    662 
    663      uint32_t containerFlags = ConvertImageRendererToDrawFlags(mFlags);
    664      if (extendMode == ExtendMode::CLAMP &&
    665          StaticPrefs::image_svg_blob_image() &&
    666          mImageContainer->GetType() == imgIContainer::TYPE_VECTOR) {
    667        containerFlags |= imgIContainer::FLAG_RECORD_BLOB;
    668      }
    669 
    670      CSSIntSize destCSSSize{
    671          nsPresContext::AppUnitsToIntCSSPixels(aDest.width),
    672          nsPresContext::AppUnitsToIntCSSPixels(aDest.height)};
    673 
    674      SVGImageContext svgContext(Some(destCSSSize));
    675      Maybe<ImageIntRegion> region;
    676 
    677      const int32_t appUnitsPerDevPixel =
    678          mForFrame->PresContext()->AppUnitsPerDevPixel();
    679      LayoutDeviceRect destRect =
    680          LayoutDeviceRect::FromAppUnits(aDest, appUnitsPerDevPixel);
    681      LayoutDeviceRect clipRect =
    682          LayoutDeviceRect::FromAppUnits(aFill, appUnitsPerDevPixel);
    683      auto stretchSize = wr::ToLayoutSize(destRect.Size());
    684 
    685      gfx::IntSize decodeSize =
    686          nsLayoutUtils::ComputeImageContainerDrawingParameters(
    687              mImageContainer, mForFrame, destRect, clipRect, aSc,
    688              containerFlags, svgContext, region);
    689 
    690      RefPtr<image::WebRenderImageProvider> provider;
    691      drawResult = mImageContainer->GetImageProvider(
    692          aManager->LayerManager(), decodeSize, svgContext, region,
    693          containerFlags, getter_AddRefs(provider));
    694 
    695      Maybe<wr::ImageKey> key =
    696          aManager->CommandBuilder().CreateImageProviderKey(
    697              aItem, provider, drawResult, aResources);
    698      if (key.isNothing()) {
    699        break;
    700      }
    701 
    702      auto rendering =
    703          wr::ToImageRendering(aItem->Frame()->UsedImageRendering());
    704      wr::LayoutRect clip = wr::ToLayoutRect(clipRect);
    705 
    706      // If we provided a region to the provider, then it already took the
    707      // dest rect into account when it did the recording.
    708      wr::LayoutRect dest = region ? clip : wr::ToLayoutRect(destRect);
    709 
    710      if (extendMode == ExtendMode::CLAMP) {
    711        // The image is not repeating. Just push as a regular image.
    712        aBuilder.PushImage(dest, clip, !aItem->BackfaceIsHidden(), false,
    713                           rendering, key.value(), true,
    714                           wr::ColorF{1.0f, 1.0f, 1.0f, aOpacity});
    715      } else {
    716        nsPoint firstTilePos = nsLayoutUtils::GetBackgroundFirstTilePos(
    717            aDest.TopLeft(), aFill.TopLeft(), aRepeatSize);
    718        LayoutDeviceRect fillRect = LayoutDeviceRect::FromAppUnits(
    719            nsRect(firstTilePos.x, firstTilePos.y,
    720                   aFill.XMost() - firstTilePos.x,
    721                   aFill.YMost() - firstTilePos.y),
    722            appUnitsPerDevPixel);
    723        wr::LayoutRect fill = wr::ToLayoutRect(fillRect);
    724 
    725        switch (extendMode) {
    726          case ExtendMode::REPEAT_Y:
    727            fill.min.x = dest.min.x;
    728            fill.max.x = dest.max.x;
    729            stretchSize.width = dest.width();
    730            break;
    731          case ExtendMode::REPEAT_X:
    732            fill.min.y = dest.min.y;
    733            fill.max.y = dest.max.y;
    734            stretchSize.height = dest.height();
    735            break;
    736          default:
    737            break;
    738        }
    739 
    740        LayoutDeviceSize gapSize = LayoutDeviceSize::FromAppUnits(
    741            aRepeatSize - aDest.Size(), appUnitsPerDevPixel);
    742 
    743        aBuilder.PushRepeatingImage(fill, clip, !aItem->BackfaceIsHidden(),
    744                                    stretchSize, wr::ToLayoutSize(gapSize),
    745                                    rendering, key.value(), true,
    746                                    wr::ColorF{1.0f, 1.0f, 1.0f, aOpacity});
    747      }
    748      break;
    749    }
    750    default:
    751      break;
    752  }
    753 
    754  if (!mImage->IsComplete() && drawResult == ImgDrawResult::SUCCESS) {
    755    return ImgDrawResult::SUCCESS_NOT_COMPLETE;
    756  }
    757  return drawResult;
    758 }
    759 
    760 already_AddRefed<gfxDrawable> nsImageRenderer::DrawableForElement(
    761    const nsRect& aImageRect, gfxContext& aContext) {
    762  NS_ASSERTION(mType == StyleImage::Tag::Element,
    763               "DrawableForElement only makes sense if backed by an element");
    764  if (mPaintServerFrame) {
    765    // XXX(seth): In order to not pass FLAG_SYNC_DECODE_IMAGES here,
    766    // DrawableFromPaintServer would have to return a ImgDrawResult indicating
    767    // whether any images could not be painted because they weren't fully
    768    // decoded. Even always passing FLAG_SYNC_DECODE_IMAGES won't eliminate all
    769    // problems, as it won't help if there are image which haven't finished
    770    // loading, but it's better than nothing.
    771    int32_t appUnitsPerDevPixel =
    772        mForFrame->PresContext()->AppUnitsPerDevPixel();
    773    nsRect destRect = aImageRect - aImageRect.TopLeft();
    774    nsIntSize roundedOut = destRect.ToOutsidePixels(appUnitsPerDevPixel).Size();
    775    IntSize imageSize(roundedOut.width, roundedOut.height);
    776 
    777    RefPtr<gfxDrawable> drawable;
    778 
    779    SurfaceFormat format = aContext.GetDrawTarget()->GetFormat();
    780    // Don't allow creating images that are too big
    781    if (aContext.GetDrawTarget()->CanCreateSimilarDrawTarget(imageSize,
    782                                                             format)) {
    783      drawable = SVGIntegrationUtils::DrawableFromPaintServer(
    784          mPaintServerFrame, mForFrame, mSize, imageSize,
    785          aContext.GetDrawTarget(), aContext.CurrentMatrixDouble(),
    786          SVGIntegrationUtils::DecodeFlag::SyncDecodeImages);
    787    }
    788 
    789    return drawable.forget();
    790  }
    791  NS_ASSERTION(mImageElementSurface.GetSourceSurface(),
    792               "Surface should be ready.");
    793  RefPtr<gfxDrawable> drawable =
    794      new gfxSurfaceDrawable(mImageElementSurface.GetSourceSurface().get(),
    795                             mImageElementSurface.mSize);
    796  return drawable.forget();
    797 }
    798 
    799 ImgDrawResult nsImageRenderer::DrawLayer(
    800    nsPresContext* aPresContext, gfxContext& aRenderingContext,
    801    const nsRect& aDest, const nsRect& aFill, const nsPoint& aAnchor,
    802    const nsRect& aDirty, const nsSize& aRepeatSize, float aOpacity) {
    803  if (!IsReady()) {
    804    MOZ_ASSERT_UNREACHABLE(
    805        "Ensure PrepareImage() has returned true before "
    806        "calling me");
    807    return ImgDrawResult::TEMPORARY_ERROR;
    808  }
    809 
    810  if (aDest.IsEmpty() || aFill.IsEmpty() || mSize.width <= 0 ||
    811      mSize.height <= 0) {
    812    return ImgDrawResult::SUCCESS;
    813  }
    814 
    815  return Draw(
    816      aPresContext, aRenderingContext, aDirty, aDest, aFill, aAnchor,
    817      aRepeatSize,
    818      CSSIntRect(0, 0, nsPresContext::AppUnitsToIntCSSPixels(mSize.width),
    819                 nsPresContext::AppUnitsToIntCSSPixels(mSize.height)),
    820      aOpacity);
    821 }
    822 
    823 ImgDrawResult nsImageRenderer::BuildWebRenderDisplayItemsForLayer(
    824    nsPresContext* aPresContext, mozilla::wr::DisplayListBuilder& aBuilder,
    825    mozilla::wr::IpcResourceUpdateQueue& aResources,
    826    const mozilla::layers::StackingContextHelper& aSc,
    827    mozilla::layers::RenderRootStateManager* aManager, nsDisplayItem* aItem,
    828    const nsRect& aDest, const nsRect& aFill, const nsPoint& aAnchor,
    829    const nsRect& aDirty, const nsSize& aRepeatSize, float aOpacity) {
    830  if (!IsReady()) {
    831    MOZ_ASSERT_UNREACHABLE(
    832        "Ensure PrepareImage() has returned true before "
    833        "calling me");
    834    return mPrepareResult;
    835  }
    836 
    837  CSSIntRect srcRect(0, 0, nsPresContext::AppUnitsToIntCSSPixels(mSize.width),
    838                     nsPresContext::AppUnitsToIntCSSPixels(mSize.height));
    839 
    840  if (aDest.IsEmpty() || aFill.IsEmpty() || srcRect.IsEmpty()) {
    841    return ImgDrawResult::SUCCESS;
    842  }
    843  return BuildWebRenderDisplayItems(aPresContext, aBuilder, aResources, aSc,
    844                                    aManager, aItem, aDirty, aDest, aFill,
    845                                    aAnchor, aRepeatSize, srcRect, aOpacity);
    846 }
    847 
    848 /**
    849 * Compute the size and position of the master copy of the image. I.e., a single
    850 * tile used to fill the dest rect.
    851 * aFill The destination rect to be filled
    852 * aHFill and aVFill are the repeat patterns for the component -
    853 * StyleBorderImageRepeatKeyword - i.e., how a tiling unit is used to fill aFill
    854 * aUnitSize The size of the source rect in dest coords.
    855 */
    856 static nsRect ComputeTile(nsRect& aFill, StyleBorderImageRepeatKeyword aHFill,
    857                          StyleBorderImageRepeatKeyword aVFill,
    858                          const nsSize& aUnitSize, nsSize& aRepeatSize) {
    859  nsRect tile;
    860  switch (aHFill) {
    861    case StyleBorderImageRepeatKeyword::Stretch:
    862      tile.x = aFill.x;
    863      tile.width = aFill.width;
    864      aRepeatSize.width = tile.width;
    865      break;
    866    case StyleBorderImageRepeatKeyword::Repeat:
    867      tile.x = aFill.x + aFill.width / 2 - aUnitSize.width / 2;
    868      tile.width = aUnitSize.width;
    869      aRepeatSize.width = tile.width;
    870      break;
    871    case StyleBorderImageRepeatKeyword::Round:
    872      tile.x = aFill.x;
    873      tile.width =
    874          nsCSSRendering::ComputeRoundedSize(aUnitSize.width, aFill.width);
    875      aRepeatSize.width = tile.width;
    876      break;
    877    case StyleBorderImageRepeatKeyword::Space: {
    878      nscoord space;
    879      aRepeatSize.width = nsCSSRendering::ComputeBorderSpacedRepeatSize(
    880          aUnitSize.width, aFill.width, space);
    881      tile.x = aFill.x + space;
    882      tile.width = aUnitSize.width;
    883      aFill.x = tile.x;
    884      aFill.width = aFill.width - space * 2;
    885    } break;
    886    default:
    887      MOZ_ASSERT_UNREACHABLE("unrecognized border-image fill style");
    888  }
    889 
    890  switch (aVFill) {
    891    case StyleBorderImageRepeatKeyword::Stretch:
    892      tile.y = aFill.y;
    893      tile.height = aFill.height;
    894      aRepeatSize.height = tile.height;
    895      break;
    896    case StyleBorderImageRepeatKeyword::Repeat:
    897      tile.y = aFill.y + aFill.height / 2 - aUnitSize.height / 2;
    898      tile.height = aUnitSize.height;
    899      aRepeatSize.height = tile.height;
    900      break;
    901    case StyleBorderImageRepeatKeyword::Round:
    902      tile.y = aFill.y;
    903      tile.height =
    904          nsCSSRendering::ComputeRoundedSize(aUnitSize.height, aFill.height);
    905      aRepeatSize.height = tile.height;
    906      break;
    907    case StyleBorderImageRepeatKeyword::Space: {
    908      nscoord space;
    909      aRepeatSize.height = nsCSSRendering::ComputeBorderSpacedRepeatSize(
    910          aUnitSize.height, aFill.height, space);
    911      tile.y = aFill.y + space;
    912      tile.height = aUnitSize.height;
    913      aFill.y = tile.y;
    914      aFill.height = aFill.height - space * 2;
    915    } break;
    916    default:
    917      MOZ_ASSERT_UNREACHABLE("unrecognized border-image fill style");
    918  }
    919 
    920  return tile;
    921 }
    922 
    923 /**
    924 * Returns true if the given set of arguments will require the tiles which fill
    925 * the dest rect to be scaled from the source tile. See comment on ComputeTile
    926 * for argument descriptions.
    927 */
    928 static bool RequiresScaling(const nsRect& aFill,
    929                            StyleBorderImageRepeatKeyword aHFill,
    930                            StyleBorderImageRepeatKeyword aVFill,
    931                            const nsSize& aUnitSize) {
    932  // If we have no tiling in either direction, we can skip the intermediate
    933  // scaling step.
    934  return (aHFill != StyleBorderImageRepeatKeyword::Stretch ||
    935          aVFill != StyleBorderImageRepeatKeyword::Stretch) &&
    936         (aUnitSize.width != aFill.width || aUnitSize.height != aFill.height);
    937 }
    938 
    939 ImgDrawResult nsImageRenderer::DrawBorderImageComponent(
    940    nsPresContext* aPresContext, gfxContext& aRenderingContext,
    941    const nsRect& aDirtyRect, const nsRect& aFill, const CSSIntRect& aSrc,
    942    StyleBorderImageRepeatKeyword aHFill, StyleBorderImageRepeatKeyword aVFill,
    943    const nsSize& aUnitSize, uint8_t aIndex,
    944    const Maybe<nsSize>& aSVGViewportSize, const bool aHasIntrinsicRatio) {
    945  if (!IsReady()) {
    946    MOZ_ASSERT_UNREACHABLE(
    947        "Ensure PrepareImage() has returned true before "
    948        "calling me");
    949    return ImgDrawResult::BAD_ARGS;
    950  }
    951 
    952  if (aFill.IsEmpty() || aSrc.IsEmpty()) {
    953    return ImgDrawResult::SUCCESS;
    954  }
    955 
    956  const bool hasImage = !!mImageContainer;
    957  if (hasImage || mType == StyleImage::Tag::Element) {
    958    nsCOMPtr<imgIContainer> subImage;
    959 
    960    // To draw one portion of an image into a border component, we stretch that
    961    // portion to match the size of that border component and then draw onto.
    962    // However, preserveAspectRatio attribute of a SVG image may break this
    963    // rule. To get correct rendering result, we add
    964    // FLAG_FORCE_PRESERVEASPECTRATIO_NONE flag here, to tell mImage to ignore
    965    // preserveAspectRatio attribute, and always do non-uniform stretch.
    966    uint32_t drawFlags = ConvertImageRendererToDrawFlags(mFlags) |
    967                         imgIContainer::FLAG_FORCE_PRESERVEASPECTRATIO_NONE;
    968    // For those SVG image sources which don't have fixed aspect ratio (i.e.
    969    // without viewport size and viewBox), we should scale the source uniformly
    970    // after the viewport size is decided by "Default Sizing Algorithm".
    971    if (!aHasIntrinsicRatio) {
    972      drawFlags = drawFlags | imgIContainer::FLAG_FORCE_UNIFORM_SCALING;
    973    }
    974    // Retrieve or create the subimage we'll draw.
    975    nsIntRect srcRect(aSrc.x, aSrc.y, aSrc.width, aSrc.height);
    976    if (hasImage) {
    977      subImage = ImageOps::Clip(mImageContainer, srcRect, aSVGViewportSize);
    978    } else {
    979      // This path, for Element, is currently slower than it
    980      // needs to be because we don't cache anything. (In particular, if we have
    981      // to draw to a temporary surface inside ClippedImage, we don't cache that
    982      // temporary surface since we immediately throw the ClippedImage we create
    983      // here away.) However, if we did cache, we'd need to know when to
    984      // invalidate that cache, and it's not clear that it's worth the trouble
    985      // since using border-image with -moz-element is rare.
    986 
    987      RefPtr<gfxDrawable> drawable =
    988          DrawableForElement(nsRect(nsPoint(), mSize), aRenderingContext);
    989      if (!drawable) {
    990        NS_WARNING("Could not create drawable for element");
    991        return ImgDrawResult::TEMPORARY_ERROR;
    992      }
    993 
    994      nsCOMPtr<imgIContainer> image(ImageOps::CreateFromDrawable(drawable));
    995      subImage = ImageOps::Clip(image, srcRect, aSVGViewportSize);
    996    }
    997 
    998    MOZ_ASSERT(!aSVGViewportSize ||
    999               subImage->GetType() == imgIContainer::TYPE_VECTOR);
   1000 
   1001    SamplingFilter samplingFilter =
   1002        nsLayoutUtils::GetSamplingFilterForFrame(mForFrame);
   1003 
   1004    if (!RequiresScaling(aFill, aHFill, aVFill, aUnitSize)) {
   1005      ImgDrawResult result = nsLayoutUtils::DrawSingleImage(
   1006          aRenderingContext, aPresContext, subImage, samplingFilter, aFill,
   1007          aDirtyRect, SVGImageContext(), drawFlags);
   1008 
   1009      if (!mImage->IsComplete()) {
   1010        result &= ImgDrawResult::SUCCESS_NOT_COMPLETE;
   1011      }
   1012 
   1013      return result;
   1014    }
   1015 
   1016    nsSize repeatSize;
   1017    nsRect fillRect(aFill);
   1018    nsRect tile = ComputeTile(fillRect, aHFill, aVFill, aUnitSize, repeatSize);
   1019 
   1020    ImgDrawResult result = nsLayoutUtils::DrawBackgroundImage(
   1021        aRenderingContext, mForFrame, aPresContext, subImage, samplingFilter,
   1022        tile, fillRect, repeatSize, tile.TopLeft(), aDirtyRect, drawFlags,
   1023        ExtendMode::CLAMP, 1.0);
   1024 
   1025    if (!mImage->IsComplete()) {
   1026      result &= ImgDrawResult::SUCCESS_NOT_COMPLETE;
   1027    }
   1028 
   1029    return result;
   1030  }
   1031 
   1032  nsSize repeatSize(aFill.Size());
   1033  nsRect fillRect(aFill);
   1034  nsRect destTile =
   1035      RequiresScaling(fillRect, aHFill, aVFill, aUnitSize)
   1036          ? ComputeTile(fillRect, aHFill, aVFill, aUnitSize, repeatSize)
   1037          : fillRect;
   1038 
   1039  return Draw(aPresContext, aRenderingContext, aDirtyRect, destTile, fillRect,
   1040              destTile.TopLeft(), repeatSize, aSrc);
   1041 }
   1042 
   1043 ImgDrawResult nsImageRenderer::DrawShapeImage(nsPresContext* aPresContext,
   1044                                              gfxContext& aRenderingContext) {
   1045  if (!IsReady()) {
   1046    MOZ_ASSERT_UNREACHABLE(
   1047        "Ensure PrepareImage() has returned true before "
   1048        "calling me");
   1049    return ImgDrawResult::NOT_READY;
   1050  }
   1051 
   1052  if (mSize.width <= 0 || mSize.height <= 0) {
   1053    return ImgDrawResult::SUCCESS;
   1054  }
   1055 
   1056  if (mImage->IsImageRequestType()) {
   1057    uint32_t drawFlags =
   1058        ConvertImageRendererToDrawFlags(mFlags) | imgIContainer::FRAME_FIRST;
   1059    nsRect dest(nsPoint(0, 0), mSize);
   1060    // We have a tricky situation in our choice of SamplingFilter. Shape
   1061    // images define a float area based on the alpha values in the rendered
   1062    // pixels. When multiple device pixels are used for one css pixel, the
   1063    // sampling can change crisp edges into aliased edges. For visual pixels,
   1064    // that's usually the right choice. For defining a float area, it can
   1065    // cause problems. If a style is using a shape-image-threshold value that
   1066    // is less than the alpha of the edge pixels, any filtering may smear the
   1067    // alpha into adjacent pixels and expand the float area in a confusing
   1068    // way. Since the alpha threshold can be set precisely in CSS, and since a
   1069    // web author may be counting on that threshold to define a precise float
   1070    // area from an image, it is least confusing to have the rendered pixels
   1071    // have unfiltered alpha. We use SamplingFilter::POINT to ensure that each
   1072    // rendered pixel has an alpha that precisely matches the alpha of the
   1073    // closest pixel in the image.
   1074    return nsLayoutUtils::DrawSingleImage(
   1075        aRenderingContext, aPresContext, mImageContainer, SamplingFilter::POINT,
   1076        dest, dest, SVGImageContext(), drawFlags);
   1077  }
   1078 
   1079  if (mImage->IsGradient()) {
   1080    nsCSSGradientRenderer renderer = nsCSSGradientRenderer::Create(
   1081        aPresContext, mForFrame->Style(), *mGradientData, mSize);
   1082    nsRect dest(nsPoint(0, 0), mSize);
   1083    renderer.Paint(aRenderingContext, dest, dest, mSize,
   1084                   CSSIntRect::FromAppUnitsRounded(dest), dest, 1.0);
   1085    return ImgDrawResult::SUCCESS;
   1086  }
   1087 
   1088  // Unsupported image type.
   1089  return ImgDrawResult::BAD_IMAGE;
   1090 }
   1091 
   1092 bool nsImageRenderer::IsRasterImage() const {
   1093  return mImageContainer &&
   1094         mImageContainer->GetType() == imgIContainer::TYPE_RASTER;
   1095 }
   1096 
   1097 already_AddRefed<imgIContainer> nsImageRenderer::GetImage() {
   1098  return do_AddRef(mImageContainer);
   1099 }