/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ // vim:cindent:ts=2:et:sw=2: /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* utility functions for drawing borders and backgrounds */ #include "nsImageRenderer.h" #include "mozilla/webrender/WebRenderAPI.h" #include "gfxDrawable.h" #include "ImageOps.h" #include "nsContentUtils.h" #include "nsCSSRendering.h" #include "nsCSSRenderingGradients.h" #include "nsIFrame.h" #include "nsRenderingContext.h" #include "nsStyleStructInlines.h" #include "nsSVGEffects.h" #include "nsSVGIntegrationUtils.h" using namespace mozilla; using namespace mozilla::gfx; using namespace mozilla::image; using namespace mozilla::layers; nsSize CSSSizeOrRatio::ComputeConcreteSize() const { NS_ASSERTION(CanComputeConcreteSize(), "Cannot compute"); if (mHasWidth && mHasHeight) { return nsSize(mWidth, mHeight); } if (mHasWidth) { nscoord height = NSCoordSaturatingNonnegativeMultiply( mWidth, double(mRatio.height) / mRatio.width); return nsSize(mWidth, height); } MOZ_ASSERT(mHasHeight); nscoord width = NSCoordSaturatingNonnegativeMultiply( mHeight, double(mRatio.width) / mRatio.height); return nsSize(width, mHeight); } nsImageRenderer::nsImageRenderer(nsIFrame* aForFrame, const nsStyleImage* aImage, uint32_t aFlags) : mForFrame(aForFrame) , mImage(aImage) , mType(aImage->GetType()) , mImageContainer(nullptr) , mGradientData(nullptr) , mPaintServerFrame(nullptr) , mPrepareResult(DrawResult::NOT_READY) , mSize(0, 0) , mFlags(aFlags) , mExtendMode(ExtendMode::CLAMP) , mMaskOp(NS_STYLE_MASK_MODE_MATCH_SOURCE) { } nsImageRenderer::~nsImageRenderer() { } static bool ShouldTreatAsCompleteDueToSyncDecode(const nsStyleImage* aImage, uint32_t aFlags) { if (!(aFlags & nsImageRenderer::FLAG_SYNC_DECODE_IMAGES)) { return false; } if (aImage->GetType() != eStyleImageType_Image) { return false; } imgRequestProxy* req = aImage->GetImageData(); if (!req) { return false; } uint32_t status = 0; if (NS_FAILED(req->GetImageStatus(&status))) { return false; } if (status & imgIRequest::STATUS_ERROR) { // The image is "complete" since it's a corrupt image. If we created an // imgIContainer at all, return true. nsCOMPtr image; req->GetImage(getter_AddRefs(image)); return bool(image); } if (!(status & imgIRequest::STATUS_LOAD_COMPLETE)) { // We must have loaded all of the image's data and the size must be // available, or else sync decoding won't be able to decode the image. return false; } return true; } bool nsImageRenderer::PrepareImage() { if (mImage->IsEmpty()) { mPrepareResult = DrawResult::BAD_IMAGE; return false; } if (!mImage->IsComplete()) { // Make sure the image is actually decoding. bool frameComplete = mImage->StartDecoding(); // Check again to see if we finished. // We cannot prepare the image for rendering if it is not fully loaded. // Special case: If we requested a sync decode and the image has loaded, push // on through because the Draw() will do a sync decode then. if (!(frameComplete || mImage->IsComplete()) && !ShouldTreatAsCompleteDueToSyncDecode(mImage, mFlags)) { mPrepareResult = DrawResult::NOT_READY; return false; } } switch (mType) { case eStyleImageType_Image: { MOZ_ASSERT(mImage->GetImageData(), "must have image data, since we checked IsEmpty above"); nsCOMPtr srcImage; DebugOnly rv = mImage->GetImageData()->GetImage(getter_AddRefs(srcImage)); MOZ_ASSERT(NS_SUCCEEDED(rv) && srcImage, "If GetImage() is failing, mImage->IsComplete() " "should have returned false"); if (!mImage->GetCropRect()) { mImageContainer.swap(srcImage); } else { nsIntRect actualCropRect; bool isEntireImage; bool success = mImage->ComputeActualCropRect(actualCropRect, &isEntireImage); if (!success || actualCropRect.IsEmpty()) { // The cropped image has zero size mPrepareResult = DrawResult::BAD_IMAGE; return false; } if (isEntireImage) { // The cropped image is identical to the source image mImageContainer.swap(srcImage); } else { nsCOMPtr subImage = ImageOps::Clip(srcImage, actualCropRect, Nothing()); mImageContainer.swap(subImage); } } mPrepareResult = DrawResult::SUCCESS; break; } case eStyleImageType_Gradient: mGradientData = mImage->GetGradientData(); mPrepareResult = DrawResult::SUCCESS; break; case eStyleImageType_Element: { nsAutoString elementId = NS_LITERAL_STRING("#") + nsDependentAtomString(mImage->GetElementId()); nsCOMPtr targetURI; nsCOMPtr base = mForFrame->GetContent()->GetBaseURI(); nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), elementId, mForFrame->GetContent()->GetUncomposedDoc(), base); nsSVGPaintingProperty* property = nsSVGEffects::GetPaintingPropertyForURI( targetURI, mForFrame->FirstContinuation(), nsSVGEffects::BackgroundImageProperty()); if (!property) { mPrepareResult = DrawResult::BAD_IMAGE; return false; } // If the referenced element is an , , or