void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, const FloatRect& srcRect, const FloatRect& dstRect, ExceptionCode& ec) { ASSERT(image); ec = 0; FloatRect imageRect = FloatRect(FloatPoint(), size(image)); if (!(imageRect.contains(srcRect) && srcRect.width() >= 0 && srcRect.height() >= 0 && dstRect.width() >= 0 && dstRect.height() >= 0)) { ec = INDEX_SIZE_ERR; return; } if (srcRect.isEmpty() || dstRect.isEmpty()) return; GraphicsContext* c = drawingContext(); if (!c) return; CachedImage* cachedImage = image->cachedImage(); if (!cachedImage) return; FloatRect sourceRect = c->roundToDevicePixels(srcRect); FloatRect destRect = c->roundToDevicePixels(dstRect); willDraw(destRect); #ifdef __OWB__ c->drawImage(cachedImage->image()->nativeImageForCurrentFrame(), destRect, sourceRect, state().m_globalComposite); cachedImage->image()->startAnimation(); #else c->drawImage(cachedImage->image(), destRect, sourceRect, state().m_globalComposite); #endif //__OWB__ }
void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, const FloatRect& srcRect, const FloatRect& dstRect, ExceptionCode& ec) { ASSERT(image); ec = 0; FloatRect imageRect = FloatRect(FloatPoint(), size(image)); if (!(imageRect.contains(srcRect) && srcRect.width() >= 0 && srcRect.height() >= 0 && dstRect.width() >= 0 && dstRect.height() >= 0)) { ec = INDEX_SIZE_ERR; return; } if (srcRect.isEmpty() || dstRect.isEmpty()) return; GraphicsContext* c = drawingContext(); if (!c) return; CachedImage* cachedImage = image->cachedImage(); if (!cachedImage) return; if (m_canvas->originClean()) checkOrigin(KURL(cachedImage->url())); FloatRect sourceRect = c->roundToDevicePixels(srcRect); FloatRect destRect = c->roundToDevicePixels(dstRect); willDraw(destRect); c->drawImage(cachedImage->image(), destRect, sourceRect, state().m_globalComposite); }
void BitmapImageSingleFrameSkia::draw(GraphicsContext* ctxt, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace styleColorSpace, CompositeOperator compositeOp) { FloatRect normDstRect = normalizeRect(dstRect); FloatRect normSrcRect = normalizeRect(srcRect); if (normSrcRect.isEmpty() || normDstRect.isEmpty()) return; // Nothing to draw. if (ctxt->platformContext()->useGPU() && ctxt->platformContext()->canAccelerate()) { drawBitmapGLES2(ctxt, &m_nativeImage, srcRect, dstRect, styleColorSpace, compositeOp); return; } ctxt->platformContext()->prepareForSoftwareDraw(); paintSkBitmap(ctxt->platformContext(), m_nativeImage, enclosingIntRect(normSrcRect), normDstRect, WebCoreCompositeToSkiaComposite(compositeOp)); }
void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace colorSpace, CompositeOperator compositeOp) { if (!m_source.initialized()) return; // Spin the animation to the correct frame before we try to draw it, so we // don't draw an old frame and then immediately need to draw a newer one, // causing flicker and wasting CPU. startAnimation(); NativeImageSkia* bm = nativeImageForCurrentFrame(); if (!bm) return; // It's too early and we don't have an image yet. FloatRect normDstRect = normalizeRect(dstRect); FloatRect normSrcRect = normalizeRect(srcRect); if (normSrcRect.isEmpty() || normDstRect.isEmpty()) return; // Nothing to draw. paintSkBitmap(ctxt->platformContext(), *bm, enclosingIntRect(normSrcRect), normDstRect, WebCoreCompositeToSkiaComposite(compositeOp)); if (ImageObserver* observer = imageObserver()) observer->didDraw(this); }
FloatRect RenderSVGImage::repaintRectInLocalCoordinates() const { // If we already have a cached repaint rect, return that if (!m_cachedLocalRepaintRect.isEmpty()) return m_cachedLocalRepaintRect; m_cachedLocalRepaintRect = m_localBounds; // FIXME: We need to be careful here. We assume that there is no filter, // clipper or masker if the rects are empty. FloatRect rect = filterBoundingBoxForRenderer(this); if (!rect.isEmpty()) m_cachedLocalRepaintRect = rect; rect = clipperBoundingBoxForRenderer(this); if (!rect.isEmpty()) m_cachedLocalRepaintRect.intersect(rect); rect = maskerBoundingBoxForRenderer(this); if (!rect.isEmpty()) m_cachedLocalRepaintRect.intersect(rect); style()->svgStyle()->inflateForShadow(m_cachedLocalRepaintRect); return m_cachedLocalRepaintRect; }
FloatRect RenderPath::repaintRectInLocalCoordinates() const { if (m_path.isEmpty()) return FloatRect(); // If we already have a cached repaint rect, return that if (!m_cachedLocalRepaintRect.isEmpty()) return m_cachedLocalRepaintRect; // FIXME: We need to be careful here. We assume that there is no filter, // clipper, marker or masker if the rects are empty. FloatRect rect = filterBoundingBoxForRenderer(this); if (!rect.isEmpty()) m_cachedLocalRepaintRect = rect; else { m_cachedLocalRepaintRect = strokeBoundingBox(); m_cachedLocalRepaintRect.unite(markerBoundingBox()); } rect = clipperBoundingBoxForRenderer(this); if (!rect.isEmpty()) m_cachedLocalRepaintRect.intersect(rect); rect = maskerBoundingBoxForRenderer(this); if (!rect.isEmpty()) m_cachedLocalRepaintRect.intersect(rect); style()->svgStyle()->inflateForShadow(m_cachedLocalRepaintRect); return m_cachedLocalRepaintRect; }
void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace colorSpace, CompositeOperator compositeOp) { if (!m_source.initialized()) return; // Spin the animation to the correct frame before we try to draw it, so we // don't draw an old frame and then immediately need to draw a newer one, // causing flicker and wasting CPU. startAnimation(); NativeImageSkia* bm = nativeImageForCurrentFrame(); if (!bm) return; // It's too early and we don't have an image yet. FloatRect normDstRect = normalizeRect(dstRect); FloatRect normSrcRect = normalizeRect(srcRect); if (normSrcRect.isEmpty() || normDstRect.isEmpty()) return; // Nothing to draw. #if ENABLE(ACCELERATED_2D_CANVAS) if (ctxt->platformContext()->useGPU() && ctxt->platformContext()->canAccelerate()) { drawBitmapGLES2(ctxt, bm, normSrcRect, normDstRect, colorSpace, compositeOp); return; } #endif ctxt->platformContext()->prepareForSoftwareDraw(); paintSkBitmap(ctxt->platformContext(), *bm, enclosingIntRect(normSrcRect), normDstRect, WebCoreCompositeToSkiaComposite(compositeOp)); }
SVGPaintServer LayoutSVGResourceGradient::preparePaintServer( const LayoutObject& object) { clearInvalidationMask(); // Be sure to synchronize all SVG properties on the gradientElement _before_ // processing any further. Otherwhise the call to collectGradientAttributes() // in createTileImage(), may cause the SVG DOM property synchronization to // kick in, which causes removeAllClientsFromCache() to be called, which in // turn deletes our GradientData object! Leaving out the line below will cause // svg/dynamic-updates/SVG*GradientElement-svgdom* to crash. SVGGradientElement* gradientElement = toSVGGradientElement(element()); if (!gradientElement) return SVGPaintServer::invalid(); if (m_shouldCollectGradientAttributes) { gradientElement->synchronizeAnimatedSVGAttribute(anyQName()); if (!collectGradientAttributes(gradientElement)) return SVGPaintServer::invalid(); m_shouldCollectGradientAttributes = false; } // Spec: When the geometry of the applicable element has no width or height // and objectBoundingBox is specified, then the given effect (e.g. a gradient // or a filter) will be ignored. FloatRect objectBoundingBox = object.objectBoundingBox(); if (gradientUnits() == SVGUnitTypes::kSvgUnitTypeObjectboundingbox && objectBoundingBox.isEmpty()) return SVGPaintServer::invalid(); std::unique_ptr<GradientData>& gradientData = m_gradientMap.add(&object, nullptr).storedValue->value; if (!gradientData) gradientData = WTF::wrapUnique(new GradientData); // Create gradient object if (!gradientData->gradient) { gradientData->gradient = buildGradient(); // We want the text bounding box applied to the gradient space transform // now, so the gradient shader can use it. if (gradientUnits() == SVGUnitTypes::kSvgUnitTypeObjectboundingbox && !objectBoundingBox.isEmpty()) { gradientData->userspaceTransform.translate(objectBoundingBox.x(), objectBoundingBox.y()); gradientData->userspaceTransform.scaleNonUniform( objectBoundingBox.width(), objectBoundingBox.height()); } AffineTransform gradientTransform = calculateGradientTransform(); gradientData->userspaceTransform *= gradientTransform; } if (!gradientData->gradient) return SVGPaintServer::invalid(); return SVGPaintServer(gradientData->gradient, gradientData->userspaceTransform); }
void Image::drawPattern(GraphicsContext* context, const FloatRect& floatSrcRect, const FloatSize& scale, const FloatPoint& phase, SkXfermode::Mode compositeOp, const FloatRect& destRect, const IntSize& repeatSpacing) { TRACE_EVENT0("skia", "Image::drawPattern"); SkBitmap bitmap; if (!bitmapForCurrentFrame(&bitmap)) return; FloatRect normSrcRect = floatSrcRect; normSrcRect.intersect(FloatRect(0, 0, bitmap.width(), bitmap.height())); if (destRect.isEmpty() || normSrcRect.isEmpty()) return; // nothing to draw SkMatrix localMatrix; // We also need to translate it such that the origin of the pattern is the // origin of the destination rect, which is what WebKit expects. Skia uses // the coordinate system origin as the base for the pattern. If WebKit wants // a shifted image, it will shift it from there using the localMatrix. const float adjustedX = phase.x() + normSrcRect.x() * scale.width(); const float adjustedY = phase.y() + normSrcRect.y() * scale.height(); localMatrix.setTranslate(SkFloatToScalar(adjustedX), SkFloatToScalar(adjustedY)); // Because no resizing occurred, the shader transform should be // set to the pattern's transform, which just includes scale. localMatrix.preScale(scale.width(), scale.height()); SkBitmap bitmapToPaint; bitmap.extractSubset(&bitmapToPaint, enclosingIntRect(normSrcRect)); if (!repeatSpacing.isZero()) { SkScalar ctmScaleX = 1.0; SkScalar ctmScaleY = 1.0; if (!RuntimeEnabledFeatures::slimmingPaintEnabled()) { AffineTransform ctm = context->getCTM(); ctmScaleX = ctm.xScale(); ctmScaleY = ctm.yScale(); } bitmapToPaint = createBitmapWithSpace( bitmapToPaint, repeatSpacing.width() * ctmScaleX / scale.width(), repeatSpacing.height() * ctmScaleY / scale.height()); } RefPtr<SkShader> shader = adoptRef(SkShader::CreateBitmapShader(bitmapToPaint, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix)); bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap); { SkPaint paint; int initialSaveCount = context->preparePaintForDrawRectToRect(&paint, floatSrcRect, destRect, compositeOp, !bitmap.isOpaque(), isLazyDecoded, bitmap.isImmutable()); paint.setShader(shader.get()); context->drawRect(destRect, paint); context->canvas()->restoreToCount(initialSaveCount); } if (isLazyDecoded) PlatformInstrumentation::didDrawLazyPixelRef(bitmap.getGenerationID()); }
void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, const FloatRect& srcRect, SkXfermode::Mode compositeOp, RespectImageOrientationEnum shouldRespectImageOrientation) { TRACE_EVENT0("skia", "BitmapImage::draw"); SkBitmap bitmap; if (!bitmapForCurrentFrame(&bitmap)) return; // It's too early and we don't have an image yet. FloatRect normDstRect = adjustForNegativeSize(dstRect); FloatRect normSrcRect = adjustForNegativeSize(srcRect); normSrcRect.intersect(FloatRect(0, 0, bitmap.width(), bitmap.height())); if (normSrcRect.isEmpty() || normDstRect.isEmpty()) return; // Nothing to draw. ImageOrientation orientation = DefaultImageOrientation; if (shouldRespectImageOrientation == RespectImageOrientation) orientation = frameOrientationAtIndex(m_currentFrame); GraphicsContextStateSaver saveContext(*ctxt, false); if (orientation != DefaultImageOrientation) { saveContext.save(); // ImageOrientation expects the origin to be at (0, 0) ctxt->translate(normDstRect.x(), normDstRect.y()); normDstRect.setLocation(FloatPoint()); ctxt->concatCTM(orientation.transformFromDefault(normDstRect.size())); if (orientation.usesWidthAsHeight()) { // The destination rect will have it's width and height already reversed for the orientation of // the image, as it was needed for page layout, so we need to reverse it back here. normDstRect = FloatRect(normDstRect.x(), normDstRect.y(), normDstRect.height(), normDstRect.width()); } } bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap); bool isOpaque = bitmap.isOpaque(); { SkPaint paint; SkRect skSrcRect = normSrcRect; int initialSaveCount = ctxt->preparePaintForDrawRectToRect(&paint, skSrcRect, normDstRect, compositeOp, !isOpaque, isLazyDecoded, bitmap.isImmutable()); // We want to filter it if we decided to do interpolation above, or if // there is something interesting going on with the matrix (like a rotation). // Note: for serialization, we will want to subset the bitmap first so we // don't send extra pixels. ctxt->drawBitmapRect(bitmap, &skSrcRect, normDstRect, &paint); ctxt->canvas()->restoreToCount(initialSaveCount); } if (isLazyDecoded) PlatformInstrumentation::didDrawLazyPixelRef(bitmap.getGenerationID()); if (ImageObserver* observer = imageObserver()) observer->didDraw(this); startAnimation(); }
void Image::drawPattern(GraphicsContext& context, const FloatRect& floatSrcRect, const FloatSize& scale, const FloatPoint& phase, SkBlendMode compositeOp, const FloatRect& destRect, const FloatSize& repeatSpacing) { TRACE_EVENT0("skia", "Image::drawPattern"); sk_sp<SkImage> image = imageForCurrentFrame(); if (!image) return; FloatRect normSrcRect = floatSrcRect; normSrcRect.intersect(FloatRect(0, 0, image->width(), image->height())); if (destRect.isEmpty() || normSrcRect.isEmpty()) return; // nothing to draw SkMatrix localMatrix; // We also need to translate it such that the origin of the pattern is the // origin of the destination rect, which is what WebKit expects. Skia uses // the coordinate system origin as the base for the pattern. If WebKit wants // a shifted image, it will shift it from there using the localMatrix. const float adjustedX = phase.x() + normSrcRect.x() * scale.width(); const float adjustedY = phase.y() + normSrcRect.y() * scale.height(); localMatrix.setTranslate(SkFloatToScalar(adjustedX), SkFloatToScalar(adjustedY)); // Because no resizing occurred, the shader transform should be // set to the pattern's transform, which just includes scale. localMatrix.preScale(scale.width(), scale.height()); // Fetch this now as subsetting may swap the image. auto imageID = image->uniqueID(); image = image->makeSubset(enclosingIntRect(normSrcRect)); if (!image) return; { SkPaint paint = context.fillPaint(); paint.setColor(SK_ColorBLACK); paint.setBlendMode(static_cast<SkBlendMode>(compositeOp)); paint.setFilterQuality( context.computeFilterQuality(this, destRect, normSrcRect)); paint.setAntiAlias(context.shouldAntialias()); paint.setShader(createPatternShader( image.get(), localMatrix, paint, FloatSize(repeatSpacing.width() / scale.width(), repeatSpacing.height() / scale.height()))); context.drawRect(destRect, paint); } if (currentFrameIsLazyDecoded()) PlatformInstrumentation::didDrawLazyPixelRef(imageID); }
void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace colorSpace, CompositeOperator compositeOp, RespectImageOrientationEnum shouldRespectImageOrientation) { if (!m_source.initialized()) return; // Spin the animation to the correct frame before we try to draw it, so we // don't draw an old frame and then immediately need to draw a newer one, // causing flicker and wasting CPU. startAnimation(); NativeImageSkia* bm = nativeImageForCurrentFrame(); if (!bm) return; // It's too early and we don't have an image yet. FloatRect normDstRect = normalizeRect(dstRect); FloatRect normSrcRect = normalizeRect(srcRect); normSrcRect.intersect(FloatRect(0, 0, bm->bitmap().width(), bm->bitmap().height())); if (normSrcRect.isEmpty() || normDstRect.isEmpty()) return; // Nothing to draw. ImageOrientation orientation = DefaultImageOrientation; if (shouldRespectImageOrientation == RespectImageOrientation) orientation = frameOrientationAtIndex(m_currentFrame); GraphicsContextStateSaver saveContext(*ctxt, false); if (orientation != DefaultImageOrientation) { saveContext.save(); // ImageOrientation expects the origin to be at (0, 0) ctxt->translate(normDstRect.x(), normDstRect.y()); normDstRect.setLocation(FloatPoint()); ctxt->concatCTM(orientation.transformFromDefault(normDstRect.size())); if (orientation.usesWidthAsHeight()) { // The destination rect will have it's width and height already reversed for the orientation of // the image, as it was needed for page layout, so we need to reverse it back here. normDstRect = FloatRect(normDstRect.x(), normDstRect.y(), normDstRect.height(), normDstRect.width()); } } paintSkBitmap(ctxt->platformContext(), *bm, normSrcRect, normDstRect, WebCoreCompositeToSkiaComposite(compositeOp)); if (ImageObserver* observer = imageObserver()) observer->didDraw(this); }
void BitmapImage::draw(SkCanvas* canvas, const SkPaint& paint, const FloatRect& dstRect, const FloatRect& srcRect, RespectImageOrientationEnum shouldRespectImageOrientation, ImageClampingMode clampMode) { TRACE_EVENT0("skia", "BitmapImage::draw"); RefPtr<SkImage> image = imageForCurrentFrame(); if (!image) return; // It's too early and we don't have an image yet. FloatRect adjustedSrcRect = srcRect; adjustedSrcRect.intersect(FloatRect(0, 0, image->width(), image->height())); if (adjustedSrcRect.isEmpty() || dstRect.isEmpty()) return; // Nothing to draw. ImageOrientation orientation = DefaultImageOrientation; if (shouldRespectImageOrientation == RespectImageOrientation) orientation = frameOrientationAtIndex(m_currentFrame); int initialSaveCount = canvas->getSaveCount(); FloatRect adjustedDstRect = dstRect; if (orientation != DefaultImageOrientation) { canvas->save(); // ImageOrientation expects the origin to be at (0, 0) canvas->translate(adjustedDstRect.x(), adjustedDstRect.y()); adjustedDstRect.setLocation(FloatPoint()); canvas->concat(affineTransformToSkMatrix(orientation.transformFromDefault(adjustedDstRect.size()))); if (orientation.usesWidthAsHeight()) { // The destination rect will have it's width and height already reversed for the orientation of // the image, as it was needed for page layout, so we need to reverse it back here. adjustedDstRect = FloatRect(adjustedDstRect.x(), adjustedDstRect.y(), adjustedDstRect.height(), adjustedDstRect.width()); } } SkRect skSrcRect = adjustedSrcRect; canvas->drawImageRect(image.get(), skSrcRect, adjustedDstRect, &paint, WebCoreClampingModeToSkiaRectConstraint(clampMode)); canvas->restoreToCount(initialSaveCount); if (currentFrameIsLazyDecoded()) PlatformInstrumentation::didDrawLazyPixelRef(image->uniqueID()); if (ImageObserver* observer = imageObserver()) observer->didDraw(this); startAnimation(); }
bool FloatRect::intersects(const FloatRect& other) const { // Checking emptiness handles negative widths as well as zero. return !isEmpty() && !other.isEmpty() && x() < other.maxX() && other.x() < maxX() && y() < other.maxY() && other.y() < maxY(); }
void KdPath::addRoundedRect(const FloatRect& rect, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius) { if (rect.isEmpty()) return; if (rect.width() < topLeftRadius.width() + topRightRadius.width() || rect.width() < bottomLeftRadius.width() + bottomRightRadius.width() || rect.height() < topLeftRadius.height() + bottomLeftRadius.height() || rect.height() < topRightRadius.height() + bottomRightRadius.height()) { // If all the radii cannot be accommodated, return a rect. addRect(rect); return; } moveTo(FloatPoint(rect.x() + topLeftRadius.width(), rect.y())); addLineTo(FloatPoint(rect.x() + rect.width() - topRightRadius.width(), rect.y())); addBezierCurveTo(FloatPoint(rect.x() + rect.width() - topRightRadius.width() * gCircleControlPoint, rect.y()), FloatPoint(rect.x() + rect.width(), rect.y() + topRightRadius.height() * gCircleControlPoint), FloatPoint(rect.x() + rect.width(), rect.y() + topRightRadius.height())); addLineTo(FloatPoint(rect.x() + rect.width(), rect.y() + rect.height() - bottomRightRadius.height())); addBezierCurveTo(FloatPoint(rect.x() + rect.width(), rect.y() + rect.height() - bottomRightRadius.height() * gCircleControlPoint), FloatPoint(rect.x() + rect.width() - bottomRightRadius.width() * gCircleControlPoint, rect.y() + rect.height()), FloatPoint(rect.x() + rect.width() - bottomRightRadius.width(), rect.y() + rect.height())); addLineTo(FloatPoint(rect.x() + bottomLeftRadius.width(), rect.y() + rect.height())); addBezierCurveTo(FloatPoint(rect.x() + bottomLeftRadius.width() * gCircleControlPoint, rect.y() + rect.height()), FloatPoint(rect.x(), rect.y() + rect.height() - bottomLeftRadius.height() * gCircleControlPoint), FloatPoint(rect.x(), rect.y() + rect.height() - bottomLeftRadius.height())); addLineTo(FloatPoint(rect.x(), rect.y() + topLeftRadius.height())); addBezierCurveTo(FloatPoint(rect.x(), rect.y() + topLeftRadius.height() * gCircleControlPoint), FloatPoint(rect.x() + topLeftRadius.width() * gCircleControlPoint, rect.y()), FloatPoint(rect.x() + topLeftRadius.width(), rect.y())); closeSubpath(); }
void BitmapImage::drawPattern(GraphicsContext& ctxt, const FloatRect& tileRect, const AffineTransform& transform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator op, const FloatRect& destRect, BlendMode blendMode) { if (tileRect.isEmpty()) return; if (!ctxt.drawLuminanceMask()) { Image::drawPattern(ctxt, tileRect, transform, phase, spacing, op, destRect, blendMode); return; } if (!m_cachedImage) { auto buffer = ImageBuffer::createCompatibleBuffer(expandedIntSize(tileRect.size()), ctxt); if (!buffer) return; ImageObserver* observer = imageObserver(); ASSERT(observer); // Temporarily reset image observer, we don't want to receive any changeInRect() calls due to this relayout. setImageObserver(nullptr); draw(buffer->context(), tileRect, tileRect, op, blendMode, ImageOrientationDescription()); setImageObserver(observer); buffer->convertToLuminanceMask(); m_cachedImage = buffer->copyImage(DontCopyBackingStore, Unscaled); if (!m_cachedImage) return; } ctxt.setDrawLuminanceMask(false); m_cachedImage->drawPattern(ctxt, tileRect, transform, phase, spacing, op, destRect, blendMode); }
FloatRect findInPageRectFromAbsoluteRect(const FloatRect& inputRect, const LayoutObject* baseLayoutObject) { if (!baseLayoutObject || inputRect.isEmpty()) return FloatRect(); // Normalize the input rect to its container block. const LayoutBlock* baseContainer = enclosingScrollableAncestor(baseLayoutObject); FloatRect normalizedRect = toNormalizedRect(inputRect, baseLayoutObject, baseContainer); // Go up across frames. for (const LayoutBox* layoutObject = baseContainer; layoutObject; ) { // Go up the layout tree until we reach the root of the current frame (the LayoutView). while (!layoutObject->isLayoutView()) { const LayoutBlock* container = enclosingScrollableAncestor(layoutObject); // Compose the normalized rects. FloatRect normalizedBoxRect = toNormalizedRect(layoutObject->absoluteBoundingBoxRect(), layoutObject, container); normalizedRect.scale(normalizedBoxRect.width(), normalizedBoxRect.height()); normalizedRect.moveBy(normalizedBoxRect.location()); layoutObject = container; } ASSERT(layoutObject->isLayoutView()); // Jump to the layoutObject owning the frame, if any. layoutObject = layoutObject->frame() ? layoutObject->frame()->ownerLayoutObject() : 0; } return normalizedRect; }
static FloatRect toNormalizedRect(const FloatRect& absoluteRect, const LayoutObject* layoutObject, const LayoutBlock* container) { ASSERT(layoutObject); ASSERT(container || layoutObject->isLayoutView()); if (!container) return FloatRect(); // We want to normalize by the max layout overflow size instead of only the visible bounding box. // Quads and their enclosing bounding boxes need to be used in order to keep results transform-friendly. FloatPoint scrolledOrigin; // For overflow:scroll we need to get where the actual origin is independently of the scroll. if (container->hasOverflowClip()) scrolledOrigin = -IntPoint(container->scrolledContentOffset()); FloatRect overflowRect(scrolledOrigin, FloatSize(container->maxLayoutOverflow())); FloatRect containerRect = container->localToAbsoluteQuad(FloatQuad(overflowRect)).enclosingBoundingBox(); if (containerRect.isEmpty()) return FloatRect(); // Make the coordinates relative to the container enclosing bounding box. // Since we work with rects enclosing quad unions this is still transform-friendly. FloatRect normalizedRect = absoluteRect; normalizedRect.moveBy(-containerRect.location()); // Fixed positions do not make sense in this coordinate system, but need to leave consistent tickmarks. // So, use their position when the view is not scrolled, like an absolute position. if (layoutObject->style()->position() == FixedPosition && container->isLayoutView()) normalizedRect.moveBy(-toLayoutView(container)->frameView()->scrollPosition()); normalizedRect.scale(1 / containerRect.width(), 1 / containerRect.height()); return normalizedRect; }
FloatRect RenderSVGResourceClipper::resourceBoundingBox(const FloatRect& objectBoundingBox) const { FloatRect clipRect; for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) { if (!childNode->isSVGElement() || !static_cast<SVGElement*>(childNode)->isStyledTransformable()) continue; SVGStyledTransformableElement* styled = static_cast<SVGStyledTransformableElement*>(childNode); RenderStyle* style = styled->renderer() ? styled->renderer()->style() : 0; if (!style || style->display() == NONE) continue; clipRect.unite(styled->renderer()->objectBoundingBox()); } if (clipRect.isEmpty()) return FloatRect(); if (static_cast<SVGClipPathElement*>(node())->clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { AffineTransform obbTransform; obbTransform.translate(objectBoundingBox.x(), objectBoundingBox.y()); obbTransform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height()); return obbTransform.mapRect(clipRect); } return clipRect; }
void CCDirectRenderer::drawRenderPass(DrawingFrame& frame, const CCRenderPass* renderPass) { if (!useRenderPass(frame, renderPass)) return; frame.scissorRectInRenderPassSpace = frame.currentRenderPass->outputRect(); if (frame.rootDamageRect != frame.rootRenderPass->outputRect()) { WebTransformationMatrix inverseTransformToRoot = frame.currentRenderPass->transformToRootTarget().inverse(); frame.scissorRectInRenderPassSpace.intersect(CCMathUtil::projectClippedRect(inverseTransformToRoot, frame.rootDamageRect)); } enableScissorTestRect(moveScissorToWindowSpace(frame, frame.scissorRectInRenderPassSpace)); clearFramebuffer(frame); const CCQuadList& quadList = renderPass->quadList(); for (CCQuadList::constBackToFrontIterator it = quadList.backToFrontBegin(); it != quadList.backToFrontEnd(); ++it) { FloatRect quadScissorRect = frame.scissorRectInRenderPassSpace; quadScissorRect.intersect(it->get()->clippedRectInTarget()); if (!quadScissorRect.isEmpty()) { enableScissorTestRect(moveScissorToWindowSpace(frame, quadScissorRect)); drawQuad(frame, it->get()); } } CachedTexture* texture = m_renderPassTextures.get(renderPass->id()); if (texture) texture->setIsComplete(!renderPass->hasOcclusionFromOutsideTargetSurface()); }
void Path::addRoundedRect(const FloatRect& rect, const FloatSize& roundingRadii) { if (rect.isEmpty()) return; FloatSize radius(roundingRadii); FloatSize halfSize(rect.width() / 2, rect.height() / 2); // Apply the SVG corner radius constraints, per the rect section of the SVG // shapes spec: if one of rx,ry is negative, then the other corner radius // value is used. If both values are negative then rx = ry = 0. If rx is // greater than half of the width of the rectangle then set rx to half of the // width; ry is handled similarly. if (radius.width() < 0) radius.setWidth((radius.height() < 0) ? 0 : radius.height()); if (radius.height() < 0) radius.setHeight(radius.width()); if (radius.width() > halfSize.width()) radius.setWidth(halfSize.width()); if (radius.height() > halfSize.height()) radius.setHeight(halfSize.height()); addPathForRoundedRect(rect, radius, radius, radius, radius); }
void BitmapImage::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const AffineTransform& transform, const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect, BlendMode blendMode) { if (tileRect.isEmpty()) return; if (!ctxt->drawLuminanceMask()) { Image::drawPattern(ctxt, tileRect, transform, phase, styleColorSpace, op, destRect, blendMode); return; } if (!m_cachedImage) { OwnPtr<ImageBuffer> buffer = ImageBuffer::create(expandedIntSize(tileRect.size())); ASSERT(buffer.get()); ImageObserver* observer = imageObserver(); ASSERT(observer); // Temporarily reset image observer, we don't want to receive any changeInRect() calls due to this relayout. setImageObserver(0); draw(buffer->context(), tileRect, tileRect, styleColorSpace, op, blendMode); setImageObserver(observer); buffer->convertToLuminanceMask(); m_cachedImage = buffer->copyImage(DontCopyBackingStore, Unscaled); m_cachedImage->setSpaceSize(spaceSize()); setImageObserver(observer); } ctxt->setDrawLuminanceMask(false); m_cachedImage->drawPattern(ctxt, tileRect, transform, phase, styleColorSpace, op, destRect, blendMode); }
FloatRect findInPageRectFromAbsoluteRect(const FloatRect& inputRect, const RenderObject* baseRenderer) { if (!baseRenderer || inputRect.isEmpty()) return FloatRect(); // Normalize the input rect to its container block. const RenderBlock* baseContainer = enclosingScrollableAncestor(baseRenderer); FloatRect normalizedRect = toNormalizedRect(inputRect, baseRenderer, baseContainer); // Go up across frames. for (const RenderBox* renderer = baseContainer; renderer; ) { // Go up the render tree until we reach the root of the current frame (the RenderView). while (!renderer->isRenderView()) { const RenderBlock* container = enclosingScrollableAncestor(renderer); // Compose the normalized rects. FloatRect normalizedBoxRect = toNormalizedRect(renderer->absoluteBoundingBoxRect(), renderer, container); normalizedRect.scale(normalizedBoxRect.width(), normalizedBoxRect.height()); normalizedRect.moveBy(normalizedBoxRect.location()); renderer = container; } ASSERT(renderer->isRenderView()); // Jump to the renderer owning the frame, if any. renderer = renderer->frame() ? renderer->frame()->ownerRenderer() : 0; } return normalizedRect; }
FloatRect SVGSVGElement::currentViewBoxRect() const { if (useCurrentView()) { if (SVGViewSpec* view = currentView()) // what if we should use it but it is not set? return view->viewBox(); return FloatRect(); } FloatRect useViewBox = viewBox(); if (!useViewBox.isEmpty()) return useViewBox; if (!renderer() || !renderer()->isSVGRoot()) return FloatRect(); if (!toRenderSVGRoot(renderer())->isEmbeddedThroughSVGImage()) return FloatRect(); Length intrinsicWidth = this->intrinsicWidth(); Length intrinsicHeight = this->intrinsicHeight(); if (!intrinsicWidth.isFixed() || !intrinsicHeight.isFixed()) return FloatRect(); // If no viewBox is specified but non-relative width/height values, then we // should always synthesize a viewBox if we're embedded through a SVGImage. return FloatRect(FloatPoint(), FloatSize(intrinsicWidth.calcFloatValue(0), intrinsicHeight.calcFloatValue(0))); }
void RenderSVGPath::updateCachedBoundaries() { if (m_path.isEmpty()) { m_fillBoundingBox = FloatRect(); m_strokeAndMarkerBoundingBox = FloatRect(); m_repaintBoundingBox = FloatRect(); return; } // Cache _unclipped_ fill bounding box, used for calculations in resources m_fillBoundingBox = m_path.boundingRect(); // Cache _unclipped_ stroke bounding box, used for calculations in resources (includes marker boundaries) m_strokeAndMarkerBoundingBox = m_fillBoundingBox; const SVGRenderStyle* svgStyle = style()->svgStyle(); if (svgStyle->hasStroke()) { BoundingRectStrokeStyleApplier strokeStyle(this, style()); m_strokeAndMarkerBoundingBox.unite(m_path.strokeBoundingRect(&strokeStyle)); } if (svgStyle->hasMarkers()) { FloatRect markerBounds = calculateMarkerBoundsIfNeeded(); if (!markerBounds.isEmpty()) m_strokeAndMarkerBoundingBox.unite(markerBounds); } // Cache smallest possible repaint rectangle m_repaintBoundingBox = m_strokeAndMarkerBoundingBox; SVGRenderSupport::intersectRepaintRectWithResources(this, m_repaintBoundingBox); }
static inline bool createMaskAndSwapContextForTextGradient(GraphicsContext*& context, GraphicsContext*& savedContext, OwnPtr<ImageBuffer>& imageBuffer, RenderObject* object) { RenderObject* textRootBlock = RenderSVGText::locateRenderSVGTextAncestor(object); ASSERT(textRootBlock); AffineTransform absoluteTransform; SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem(textRootBlock, absoluteTransform); FloatRect absoluteTargetRect = absoluteTransform.mapRect(textRootBlock->repaintRectInLocalCoordinates()); FloatRect clampedAbsoluteTargetRect = SVGImageBufferTools::clampedAbsoluteTargetRectForRenderer(textRootBlock, absoluteTargetRect); if (clampedAbsoluteTargetRect.isEmpty()) return false; OwnPtr<ImageBuffer> maskImage; if (!SVGImageBufferTools::createImageBuffer(absoluteTargetRect, clampedAbsoluteTargetRect, maskImage, ColorSpaceDeviceRGB)) return false; GraphicsContext* maskImageContext = maskImage->context(); ASSERT(maskImageContext); maskImageContext->translate(-clampedAbsoluteTargetRect.x(), -clampedAbsoluteTargetRect.y()); maskImageContext->concatCTM(absoluteTransform); ASSERT(maskImage); savedContext = context; context = maskImageContext; imageBuffer = maskImage.release(); return true; }
FloatRect RenderSVGResourcePattern::calculatePatternBoundariesIncludingOverflow(PatternAttributes& attributes, const FloatRect& objectBoundingBox, const AffineTransform& viewBoxCTM, const FloatRect& patternBoundaries) const { // Eventually calculate the pattern content boundaries (only needed with overflow="visible"). FloatRect patternContentBoundaries; const RenderStyle* style = this->style(); if (style->overflowX() == OVISIBLE && style->overflowY() == OVISIBLE) { for (Node* node = attributes.patternContentElement()->firstChild(); node; node = node->nextSibling()) { if (!node->isSVGElement() || !static_cast<SVGElement*>(node)->isStyledTransformable() || !node->renderer()) continue; patternContentBoundaries.unite(node->renderer()->repaintRectInLocalCoordinates()); } } if (patternContentBoundaries.isEmpty()) return patternBoundaries; FloatRect patternBoundariesIncludingOverflow = patternBoundaries; // Respect objectBoundingBoxMode for patternContentUnits, if viewBox is not set. if (!viewBoxCTM.isIdentity()) patternContentBoundaries = viewBoxCTM.mapRect(patternContentBoundaries); else if (attributes.boundingBoxModeContent()) patternContentBoundaries = FloatRect(patternContentBoundaries.x() * objectBoundingBox.width(), patternContentBoundaries.y() * objectBoundingBox.height(), patternContentBoundaries.width() * objectBoundingBox.width(), patternContentBoundaries.height() * objectBoundingBox.height()); patternBoundariesIncludingOverflow.unite(patternContentBoundaries); return patternBoundariesIncludingOverflow; }
void TilingData::intersectDrawQuad(const FloatRect& srcRect, const FloatRect& dstRect, int tile, FloatRect* newSrc, FloatRect* newDst) const { // Intersect with tile FloatRect tileBounds = this->tileBounds(tile); FloatRect srcRectIntersected = srcRect; srcRectIntersected.intersect(tileBounds); if (srcRectIntersected.isEmpty()) { *newSrc = *newDst = FloatRect(0, 0, 0, 0); return; } float srcRectIntersectedNormX = (srcRectIntersected.x() - srcRect.x()) / srcRect.width(); float srcRectIntersectedNormY = (srcRectIntersected.y() - srcRect.y()) / srcRect.height(); float srcRectIntersectedNormW = srcRectIntersected.width() / srcRect.width(); float srcRectIntersectedNormH = srcRectIntersected.height() / srcRect.height(); *newSrc = srcRectIntersected; newSrc->move( -tileBounds.x() + ((tileXIndex(tile) > 0) ? m_borderTexels : 0), -tileBounds.y() + ((tileYIndex(tile) > 0) ? m_borderTexels : 0)); *newDst = FloatRect( srcRectIntersectedNormX * dstRect.width() + dstRect.x(), srcRectIntersectedNormY * dstRect.height() + dstRect.y(), srcRectIntersectedNormW * dstRect.width(), srcRectIntersectedNormH * dstRect.height()); }
void Path::addRoundedRect(const FloatRect& rect, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius) { if (rect.isEmpty()) return; if (rect.width() < topLeftRadius.width() + topRightRadius.width() || rect.width() < bottomLeftRadius.width() + bottomRightRadius.width() || rect.height() < topLeftRadius.height() + bottomLeftRadius.height() || rect.height() < topRightRadius.height() + bottomRightRadius.height()) { // If all the radii cannot be accommodated, return a rect. // FIXME: Is this an error scenario, given that it appears the code in // FloatRoundedRect::constrainRadii() should be always called first? Should // we assert that this code is not reached? This fallback is very bad, since // it means that radii that are just barely too big due to rounding or // snapping will get completely ignored. addRect(rect); return; } addPathForRoundedRect(rect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); }
bool RenderSVGResourceMasker::applyResource(RenderElement& renderer, const RenderStyle&, GraphicsContext*& context, unsigned short resourceMode) { ASSERT(context); ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode); bool missingMaskerData = !m_masker.contains(&renderer); if (missingMaskerData) m_masker.set(&renderer, std::make_unique<MaskerData>()); MaskerData* maskerData = m_masker.get(&renderer); AffineTransform absoluteTransform = SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(renderer); FloatRect repaintRect = renderer.repaintRectInLocalCoordinates(); if (!maskerData->maskImage && !repaintRect.isEmpty()) { const SVGRenderStyle& svgStyle = style().svgStyle(); ColorSpace colorSpace = svgStyle.colorInterpolation() == CI_LINEARRGB ? ColorSpaceLinearRGB : ColorSpaceDeviceRGB; maskerData->maskImage = SVGRenderingContext::createImageBuffer(repaintRect, absoluteTransform, colorSpace, Unaccelerated); if (!maskerData->maskImage) return false; if (!drawContentIntoMaskImage(maskerData, colorSpace, &renderer)) maskerData->maskImage.reset(); } if (!maskerData->maskImage) return false; SVGRenderingContext::clipToImageBuffer(context, absoluteTransform, repaintRect, maskerData->maskImage, missingMaskerData); return true; }