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 }