void SVGTextChunkBuilder::buildSpacingAndGlyphsTransform(bool isVerticalText, float scale, const SVGTextFragment& fragment, AffineTransform& spacingAndGlyphsTransform) { spacingAndGlyphsTransform.translate(fragment.x, fragment.y); if (isVerticalText) spacingAndGlyphsTransform.scaleNonUniform(1, scale); else spacingAndGlyphsTransform.scaleNonUniform(scale, 1); spacingAndGlyphsTransform.translate(-fragment.x, -fragment.y); }
static inline AffineTransform clipToTextMask(GraphicsContext* context, OwnPtr<ImageBuffer>& imageBuffer, FloatRect& targetRect, const RenderObject* object, GradientData* gradientData) { const RenderObject* textRootBlock = SVGRenderSupport::findTextRootObject(object); ASSERT(textRootBlock); targetRect = textRootBlock->repaintRectInLocalCoordinates(); AffineTransform absoluteTransform; SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem(textRootBlock, absoluteTransform); FloatRect absoluteTargetRect = absoluteTransform.mapRect(targetRect); FloatRect clampedAbsoluteTargetRect = SVGImageBufferTools::clampedAbsoluteTargetRectForRenderer(textRootBlock, absoluteTargetRect); SVGImageBufferTools::clipToImageBuffer(context, absoluteTransform, clampedAbsoluteTargetRect, imageBuffer); AffineTransform matrix; if (gradientData->boundingBoxMode) { FloatRect maskBoundingBox = textRootBlock->objectBoundingBox(); matrix.translate(maskBoundingBox.x(), maskBoundingBox.y()); matrix.scaleNonUniform(maskBoundingBox.width(), maskBoundingBox.height()); } matrix.multLeft(gradientData->transform); return matrix; }
bool RenderSVGResourceClipper::hitTestClipContent(const FloatRect& objectBoundingBox, const FloatPoint& nodeAtPoint) { FloatPoint point = nodeAtPoint; if (!SVGRenderSupport::pointInClippingArea(this, point)) return false; if (static_cast<SVGClipPathElement*>(node())->clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { AffineTransform transform; transform.translate(objectBoundingBox.x(), objectBoundingBox.y()); transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height()); point = transform.inverse().mapPoint(point); } for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) { RenderObject* renderer = childNode->renderer(); if (!childNode->isSVGElement() || !static_cast<SVGElement*>(childNode)->isStyled() || !renderer) continue; if (!renderer->isSVGPath() && !renderer->isSVGText() && !renderer->isSVGShadowTreeRootContainer()) continue; IntPoint hitPoint; HitTestResult result(hitPoint); if (renderer->nodeAtFloatPoint(HitTestRequest(HitTestRequest::SVGClipContent), result, point, HitTestForeground)) return true; } return false; }
FloatRect RenderSVGResourceMasker::resourceBoundingBox(RenderObject* object) { // Save the reference to the calling object for relayouting it on changing resource properties. if (!m_masker.contains(object)) m_masker.set(object, new MaskerData); // Resource was not layouted yet. Give back clipping rect of the mask. SVGMaskElement* maskElement = static_cast<SVGMaskElement*>(node()); FloatRect objectBoundingBox = object->objectBoundingBox(); FloatRect maskBoundaries = maskElement->maskBoundingBox(objectBoundingBox); if (selfNeedsLayout()) return maskBoundaries; if (m_maskBoundaries.isEmpty()) calculateMaskContentRepaintRect(); if (!maskElement) return FloatRect(); FloatRect maskRect = m_maskBoundaries; if (maskElement->maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { AffineTransform transform; transform.translate(objectBoundingBox.x(), objectBoundingBox.y()); transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height()); maskRect = transform.mapRect(maskRect); } maskRect.intersect(maskBoundaries); return maskRect; }
bool RenderSVGResourceClipper::hitTestClipContent(const FloatRect& objectBoundingBox, const FloatPoint& nodeAtPoint) { FloatPoint point = nodeAtPoint; if (!SVGRenderSupport::pointInClippingArea(*this, point)) return false; if (clipPathElement().clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { AffineTransform transform; transform.translate(objectBoundingBox.x(), objectBoundingBox.y()); transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height()); point = transform.inverse().valueOr(AffineTransform()).mapPoint(point); } point = clipPathElement().animatedLocalTransform().inverse().valueOr(AffineTransform()).mapPoint(point); for (Node* childNode = clipPathElement().firstChild(); childNode; childNode = childNode->nextSibling()) { RenderObject* renderer = childNode->renderer(); if (!childNode->isSVGElement() || !renderer) continue; if (!renderer->isSVGShape() && !renderer->isSVGText() && !childNode->hasTagName(SVGNames::useTag)) continue; IntPoint hitPoint; HitTestResult result(hitPoint); if (renderer->nodeAtFloatPoint(HitTestRequest(HitTestRequest::SVGClipContent | HitTestRequest::DisallowUserAgentShadowContent), result, point, HitTestForeground)) return true; } return false; }
FloatRect RenderSVGResourceMasker::resourceBoundingBox(RenderObject* object) { SVGMaskElement* maskElement = static_cast<SVGMaskElement*>(node()); ASSERT(maskElement); FloatRect objectBoundingBox = object->objectBoundingBox(); FloatRect maskBoundaries = maskElement->maskBoundingBox(objectBoundingBox); // Resource was not layouted yet. Give back clipping rect of the mask. if (selfNeedsLayout()) return maskBoundaries; if (m_maskContentBoundaries.isEmpty()) calculateMaskContentRepaintRect(); FloatRect maskRect = m_maskContentBoundaries; if (maskElement->maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { AffineTransform transform; transform.translate(objectBoundingBox.x(), objectBoundingBox.y()); transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height()); maskRect = transform.mapRect(maskRect); } maskRect.intersect(maskBoundaries); return maskRect; }
bool RenderSVGResourceClipper::drawContentIntoMaskImage(const ClipperMaskImage& clipperMaskImage, const FloatRect& objectBoundingBox) { ASSERT(clipperMaskImage); GraphicsContext& maskContext = clipperMaskImage->context(); AffineTransform maskContentTransformation; if (clipPathElement().clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { maskContentTransformation.translate(objectBoundingBox.x(), objectBoundingBox.y()); maskContentTransformation.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height()); maskContext.concatCTM(maskContentTransformation); } // Switch to a paint behavior where all children of this <clipPath> will be rendered using special constraints: // - fill-opacity/stroke-opacity/opacity set to 1 // - masker/filter not applied when rendering the children // - fill is set to the initial fill paint server (solid, black) // - stroke is set to the initial stroke paint server (none) PaintBehavior oldBehavior = view().frameView().paintBehavior(); view().frameView().setPaintBehavior(oldBehavior | PaintBehaviorRenderingSVGMask); // Draw all clipPath children into a global mask. for (auto& child : childrenOfType<SVGElement>(clipPathElement())) { auto renderer = child.renderer(); if (!renderer) continue; if (renderer->needsLayout()) { view().frameView().setPaintBehavior(oldBehavior); return false; } const RenderStyle& style = renderer->style(); if (style.display() == NONE || style.visibility() != VISIBLE) continue; WindRule newClipRule = style.svgStyle().clipRule(); bool isUseElement = child.hasTagName(SVGNames::useTag); if (isUseElement) { SVGUseElement& useElement = downcast<SVGUseElement>(child); renderer = useElement.rendererClipChild(); if (!renderer) continue; if (!useElement.hasAttributeWithoutSynchronization(SVGNames::clip_ruleAttr)) newClipRule = renderer->style().svgStyle().clipRule(); } // Only shapes, paths and texts are allowed for clipping. if (!renderer->isSVGShape() && !renderer->isSVGText()) continue; maskContext.setFillRule(newClipRule); // In the case of a <use> element, we obtained its renderere above, to retrieve its clipRule. // We have to pass the <use> renderer itself to renderSubtreeToImageBuffer() to apply it's x/y/transform/etc. values when rendering. // So if isUseElement is true, refetch the childNode->renderer(), as renderer got overriden above. SVGRenderingContext::renderSubtreeToImageBuffer(clipperMaskImage.get(), isUseElement ? *child.renderer() : *renderer, maskContentTransformation); } view().frameView().setPaintBehavior(oldBehavior); return true; }
bool LayoutSVGResourceClipper::hitTestClipContent( const FloatRect& objectBoundingBox, const FloatPoint& nodeAtPoint) { FloatPoint point = nodeAtPoint; if (!SVGLayoutSupport::pointInClippingArea(*this, point)) return false; if (clipPathUnits() == SVGUnitTypes::kSvgUnitTypeObjectboundingbox) { AffineTransform transform; transform.translate(objectBoundingBox.x(), objectBoundingBox.y()); transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height()); point = transform.inverse().mapPoint(point); } AffineTransform animatedLocalTransform = toSVGClipPathElement(element())->calculateTransform( SVGElement::IncludeMotionTransform); if (!animatedLocalTransform.isInvertible()) return false; point = animatedLocalTransform.inverse().mapPoint(point); for (const SVGElement& childElement : Traversal<SVGElement>::childrenOf(*element())) { if (!contributesToClip(childElement)) continue; IntPoint hitPoint; HitTestResult result(HitTestRequest::SVGClipContent, hitPoint); LayoutObject* layoutObject = childElement.layoutObject(); if (layoutObject->nodeAtFloatPoint(result, point, HitTestForeground)) return true; } return false; }
CGPatternRef Pattern::createPlatformPattern(const AffineTransform& userSpaceTransformation) const { IntRect tileRect = tileImage()->rect(); AffineTransform patternTransform = userSpaceTransformation * m_patternSpaceTransformation; patternTransform.scaleNonUniform(1, -1); patternTransform.translate(0, -tileRect.height()); // If we're repeating in both directions, we can use image-backed patterns // instead of custom patterns, and avoid tiling-edge pixel cracks. if (m_repeatX && m_repeatY) return wkCGPatternCreateWithImageAndTransform(tileImage()->getCGImageRef(), patternTransform, wkPatternTilingConstantSpacing); // If FLT_MAX should also be used for xStep or yStep, nothing is rendered. Using fractions of FLT_MAX also // result in nothing being rendered. // INT_MAX is almost correct, but there seems to be some number wrapping occurring making the fill // pattern is not filled correctly. // To make error of floating point less than 0.5, we use the half of the number of mantissa of float (1 << 22). CGFloat xStep = m_repeatX ? tileRect.width() : (1 << 22); CGFloat yStep = m_repeatY ? tileRect.height() : (1 << 22); // The pattern will release the CGImageRef when it's done rendering in patternReleaseCallback CGImageRef platformImage = CGImageRetain(tileImage()->getCGImageRef()); const CGPatternCallbacks patternCallbacks = { 0, patternCallback, patternReleaseCallback }; return CGPatternCreate(platformImage, tileRect, patternTransform, xStep, yStep, kCGPatternTilingConstantSpacing, TRUE, &patternCallbacks); }
FloatRect LayoutSVGResourceMasker::resourceBoundingBox(const LayoutObject* object) { SVGMaskElement* maskElement = toSVGMaskElement(element()); ASSERT(maskElement); FloatRect objectBoundingBox = object->objectBoundingBox(); FloatRect maskBoundaries = SVGLengthContext::resolveRectangle<SVGMaskElement>(maskElement, maskElement->maskUnits()->currentValue()->enumValue(), objectBoundingBox); // Resource was not layouted yet. Give back clipping rect of the mask. if (selfNeedsLayout()) return maskBoundaries; if (m_maskContentBoundaries.isEmpty()) calculateMaskContentPaintInvalidationRect(); FloatRect maskRect = m_maskContentBoundaries; if (maskElement->maskContentUnits()->currentValue()->value() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { AffineTransform transform; transform.translate(objectBoundingBox.x(), objectBoundingBox.y()); transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height()); maskRect = transform.mapRect(maskRect); } maskRect.intersect(maskBoundaries); return maskRect; }
static inline AffineTransform clipToTextMask(GraphicsContext* context, OwnPtr<ImageBuffer>& imageBuffer, FloatRect& targetRect, RenderObject* object, bool boundingBoxMode, const AffineTransform& gradientTransform) { RenderObject* textRootBlock = RenderSVGText::locateRenderSVGTextAncestor(object); ASSERT(textRootBlock); AffineTransform absoluteTransform; SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(textRootBlock, absoluteTransform); targetRect = textRootBlock->repaintRectInLocalCoordinates(); SVGRenderingContext::clipToImageBuffer(context, absoluteTransform, targetRect, imageBuffer, false); AffineTransform matrix; if (boundingBoxMode) { FloatRect maskBoundingBox = textRootBlock->objectBoundingBox(); matrix.translate(maskBoundingBox.x(), maskBoundingBox.y()); matrix.scaleNonUniform(maskBoundingBox.width(), maskBoundingBox.height()); } matrix *= gradientTransform; return matrix; }
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; }
bool LayoutSVGResourceClipper::hitTestClipContent(const FloatRect& objectBoundingBox, const FloatPoint& nodeAtPoint) { FloatPoint point = nodeAtPoint; if (!SVGLayoutSupport::pointInClippingArea(this, point)) return false; if (clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { AffineTransform transform; transform.translate(objectBoundingBox.x(), objectBoundingBox.y()); transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height()); point = transform.inverse().mapPoint(point); } AffineTransform animatedLocalTransform = toSVGClipPathElement(element())->calculateAnimatedLocalTransform(); if (!animatedLocalTransform.isInvertible()) return false; point = animatedLocalTransform.inverse().mapPoint(point); for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element()); childElement; childElement = Traversal<SVGElement>::nextSibling(*childElement)) { LayoutObject* layoutObject = childElement->layoutObject(); if (!layoutObject) continue; if (!layoutObject->isSVGShape() && !layoutObject->isSVGText() && !isSVGUseElement(*childElement)) continue; IntPoint hitPoint; HitTestResult result(HitTestRequest::SVGClipContent, hitPoint); if (layoutObject->nodeAtFloatPoint(result, point, HitTestForeground)) return true; } return false; }
AffineTransform SVGPreserveAspectRatio::getCTM(float logicalX, float logicalY, float logicalWidth, float logicalHeight, float physicalWidth, float physicalHeight) const { AffineTransform transform; if (m_align == SVG_PRESERVEASPECTRATIO_UNKNOWN) return transform; double extendedLogicalX = logicalX; double extendedLogicalY = logicalY; double extendedLogicalWidth = logicalWidth; double extendedLogicalHeight = logicalHeight; double extendedPhysicalWidth = physicalWidth; double extendedPhysicalHeight = physicalHeight; double logicalRatio = extendedLogicalWidth / extendedLogicalHeight; double physicalRatio = extendedPhysicalWidth / extendedPhysicalHeight; if (m_align == SVG_PRESERVEASPECTRATIO_NONE) { transform.scaleNonUniform(extendedPhysicalWidth / extendedLogicalWidth, extendedPhysicalHeight / extendedLogicalHeight); transform.translate(-extendedLogicalX, -extendedLogicalY); return transform; } if ((logicalRatio < physicalRatio && (m_meetOrSlice == SVG_MEETORSLICE_MEET)) || (logicalRatio >= physicalRatio && (m_meetOrSlice == SVG_MEETORSLICE_SLICE))) { transform.scaleNonUniform(extendedPhysicalHeight / extendedLogicalHeight, extendedPhysicalHeight / extendedLogicalHeight); if (m_align == SVG_PRESERVEASPECTRATIO_XMINYMIN || m_align == SVG_PRESERVEASPECTRATIO_XMINYMID || m_align == SVG_PRESERVEASPECTRATIO_XMINYMAX) transform.translate(-extendedLogicalX, -extendedLogicalY); else if (m_align == SVG_PRESERVEASPECTRATIO_XMIDYMIN || m_align == SVG_PRESERVEASPECTRATIO_XMIDYMID || m_align == SVG_PRESERVEASPECTRATIO_XMIDYMAX) transform.translate(-extendedLogicalX - (extendedLogicalWidth - extendedPhysicalWidth * extendedLogicalHeight / extendedPhysicalHeight) / 2, -extendedLogicalY); else transform.translate(-extendedLogicalX - (extendedLogicalWidth - extendedPhysicalWidth * extendedLogicalHeight / extendedPhysicalHeight), -extendedLogicalY); return transform; } transform.scaleNonUniform(extendedPhysicalWidth / extendedLogicalWidth, extendedPhysicalWidth / extendedLogicalWidth); if (m_align == SVG_PRESERVEASPECTRATIO_XMINYMIN || m_align == SVG_PRESERVEASPECTRATIO_XMIDYMIN || m_align == SVG_PRESERVEASPECTRATIO_XMAXYMIN) transform.translate(-extendedLogicalX, -extendedLogicalY); else if (m_align == SVG_PRESERVEASPECTRATIO_XMINYMID || m_align == SVG_PRESERVEASPECTRATIO_XMIDYMID || m_align == SVG_PRESERVEASPECTRATIO_XMAXYMID) transform.translate(-extendedLogicalX, -extendedLogicalY - (extendedLogicalHeight - extendedPhysicalHeight * extendedLogicalWidth / extendedPhysicalWidth) / 2); else transform.translate(-extendedLogicalX, -extendedLogicalY - (extendedLogicalHeight - extendedPhysicalHeight * extendedLogicalWidth / extendedPhysicalWidth)); return transform; }
AffineTransform HTMLCanvasElement::baseTransform() const { ASSERT(m_hasCreatedImageBuffer); FloatSize unscaledSize(width(), height()); IntSize size = convertLogicalToDevice(unscaledSize); AffineTransform transform; if (size.width() && size.height()) transform.scaleNonUniform(size.width() / unscaledSize.width(), size.height() / unscaledSize.height()); return m_imageBuffer->baseTransform() * transform; }
void GraphicsContext::scale(const FloatSize& scaleFactors) { if (paintingDisabled()) return; AffineTransform current; platformContext()->getTransform((double*)¤t); current.scaleNonUniform(scaleFactors.width(), scaleFactors.height()); platformContext()->setTransform((double*)¤t); }
bool RenderSVGResourceClipper::pathOnlyClipping(GraphicsContext& context, const AffineTransform& animatedLocalTransform, const FloatRect& objectBoundingBox) { // If the current clip-path gets clipped itself, we have to fallback to masking. if (!style().svgStyle().clipperResource().isEmpty()) return false; WindRule clipRule = RULE_NONZERO; Path clipPath = Path(); // If clip-path only contains one visible shape or path, we can use path-based clipping. Invisible // shapes don't affect the clipping and can be ignored. If clip-path contains more than one // visible shape, the additive clipping may not work, caused by the clipRule. EvenOdd // as well as NonZero can cause self-clipping of the elements. // See also http://www.w3.org/TR/SVG/painting.html#FillRuleProperty for (Node* childNode = clipPathElement().firstChild(); childNode; childNode = childNode->nextSibling()) { RenderObject* renderer = childNode->renderer(); if (!renderer) continue; // Only shapes or paths are supported for direct clipping. We need to fallback to masking for texts. if (renderer->isSVGText()) return false; if (!childNode->isSVGElement() || !downcast<SVGElement>(*childNode).isSVGGraphicsElement()) continue; SVGGraphicsElement& styled = downcast<SVGGraphicsElement>(*childNode); const RenderStyle& style = renderer->style(); if (style.display() == NONE || style.visibility() != VISIBLE) continue; const SVGRenderStyle& svgStyle = style.svgStyle(); // Current shape in clip-path gets clipped too. Fallback to masking. if (!svgStyle.clipperResource().isEmpty()) return false; // Fallback to masking, if there is more than one clipping path. if (clipPath.isEmpty()) { styled.toClipPath(clipPath); clipRule = svgStyle.clipRule(); } else return false; } // Only one visible shape/path was found. Directly continue clipping and transform the content to userspace if necessary. if (clipPathElement().clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { AffineTransform transform; transform.translate(objectBoundingBox.x(), objectBoundingBox.y()); transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height()); clipPath.transform(transform); } // Transform path by animatedLocalTransform. clipPath.transform(animatedLocalTransform); // The SVG specification wants us to clip everything, if clip-path doesn't have a child. if (clipPath.isEmpty()) clipPath.addRect(FloatRect()); context.clipPath(clipPath, clipRule); return true; }
AffineTransform RenderSVGResourceMarker::markerContentTransformation(const AffineTransform& contentTransformation, const FloatPoint& origin, float strokeWidth) const { // The 'origin' coordinate maps to SVGs refX/refY, given in coordinates relative to the viewport established by the marker FloatPoint mappedOrigin = viewportTransform().mapPoint(origin); AffineTransform transformation = contentTransformation; if (strokeWidth != -1) transformation.scaleNonUniform(strokeWidth, strokeWidth); transformation.translate(-mappedOrigin.x(), -mappedOrigin.y()); return transformation; }
// FIXME: We should use floats here, like everywhere else! AffineTransform SVGPreserveAspectRatio::getCTM(double logicX, double logicY, double logicWidth, double logicHeight, double physWidth, double physHeight) const { AffineTransform transform; if (m_align == SVG_PRESERVEASPECTRATIO_UNKNOWN) return transform; double logicalRatio = logicWidth / logicHeight; double physRatio = physWidth / physHeight; if (m_align == SVG_PRESERVEASPECTRATIO_NONE) { transform.scaleNonUniform(physWidth / logicWidth, physHeight / logicHeight); transform.translate(-logicX, -logicY); return transform; } if ((logicalRatio < physRatio && (m_meetOrSlice == SVG_MEETORSLICE_MEET)) || (logicalRatio >= physRatio && (m_meetOrSlice == SVG_MEETORSLICE_SLICE))) { transform.scaleNonUniform(physHeight / logicHeight, physHeight / logicHeight); if (m_align == SVG_PRESERVEASPECTRATIO_XMINYMIN || m_align == SVG_PRESERVEASPECTRATIO_XMINYMID || m_align == SVG_PRESERVEASPECTRATIO_XMINYMAX) transform.translate(-logicX, -logicY); else if (m_align == SVG_PRESERVEASPECTRATIO_XMIDYMIN || m_align == SVG_PRESERVEASPECTRATIO_XMIDYMID || m_align == SVG_PRESERVEASPECTRATIO_XMIDYMAX) transform.translate(-logicX - (logicWidth - physWidth * logicHeight / physHeight) / 2, -logicY); else transform.translate(-logicX - (logicWidth - physWidth * logicHeight / physHeight), -logicY); return transform; } transform.scaleNonUniform(physWidth / logicWidth, physWidth / logicWidth); if (m_align == SVG_PRESERVEASPECTRATIO_XMINYMIN || m_align == SVG_PRESERVEASPECTRATIO_XMIDYMIN || m_align == SVG_PRESERVEASPECTRATIO_XMAXYMIN) transform.translate(-logicX, -logicY); else if (m_align == SVG_PRESERVEASPECTRATIO_XMINYMID || m_align == SVG_PRESERVEASPECTRATIO_XMIDYMID || m_align == SVG_PRESERVEASPECTRATIO_XMAXYMID) transform.translate(-logicX, -logicY - (logicHeight - physHeight * logicWidth / physWidth) / 2); else transform.translate(-logicX, -logicY - (logicHeight - physHeight * logicWidth / physWidth)); return transform; }
void RenderSVGResourceMasker::drawContentIntoMaskImage(MaskerData* maskerData, const SVGMaskElement* maskElement, RenderObject* object) { GraphicsContext* maskImageContext = maskerData->maskImage->context(); ASSERT(maskImageContext); // Eventually adjust the mask image context according to the target objectBoundingBox. AffineTransform maskContentTransformation; if (maskElement->maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { FloatRect objectBoundingBox = object->objectBoundingBox(); maskContentTransformation.translate(objectBoundingBox.x(), objectBoundingBox.y()); maskContentTransformation.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height()); maskImageContext->concatCTM(maskContentTransformation); } // Draw the content into the ImageBuffer. for (Node* node = maskElement->firstChild(); node; node = node->nextSibling()) { RenderObject* renderer = node->renderer(); if (!node->isSVGElement() || !static_cast<SVGElement*>(node)->isStyled() || !renderer) continue; RenderStyle* style = renderer->style(); if (!style || style->display() == NONE || style->visibility() != VISIBLE) continue; SVGImageBufferTools::renderSubtreeToImageBuffer(maskerData->maskImage.get(), renderer, maskContentTransformation); } maskImageContext->restore(); #if !PLATFORM(CG) maskerData->maskImage->transformColorSpace(ColorSpaceDeviceRGB, ColorSpaceLinearRGB); #endif // Create the luminance mask. IntRect maskImageRect(IntPoint(), maskerData->maskImage->size()); RefPtr<ImageData> imageData = maskerData->maskImage->getUnmultipliedImageData(maskImageRect); ByteArray* srcPixelArray = imageData->data()->data(); unsigned pixelArrayLength = srcPixelArray->length(); for (unsigned pixelOffset = 0; pixelOffset < pixelArrayLength; pixelOffset += 4) { unsigned char a = srcPixelArray->get(pixelOffset + 3); if (!a) continue; unsigned char r = srcPixelArray->get(pixelOffset); unsigned char g = srcPixelArray->get(pixelOffset + 1); unsigned char b = srcPixelArray->get(pixelOffset + 2); double luma = (r * 0.2125 + g * 0.7154 + b * 0.0721) * ((double)a / 255.0); srcPixelArray->set(pixelOffset + 3, luma); } maskerData->maskImage->putUnmultipliedImageData(imageData.get(), maskImageRect, IntPoint()); }
sk_sp<const SkPicture> LayoutSVGResourceMasker::createContentPicture( AffineTransform& contentTransformation, const FloatRect& targetBoundingBox, GraphicsContext& context) { SVGUnitTypes::SVGUnitType contentUnits = toSVGMaskElement(element()) ->maskContentUnits() ->currentValue() ->enumValue(); if (contentUnits == SVGUnitTypes::kSvgUnitTypeObjectboundingbox) { contentTransformation.translate(targetBoundingBox.x(), targetBoundingBox.y()); contentTransformation.scaleNonUniform(targetBoundingBox.width(), targetBoundingBox.height()); } if (m_maskContentPicture) return m_maskContentPicture; SubtreeContentTransformScope contentTransformScope(contentTransformation); // Using strokeBoundingBox instead of visualRectInLocalCoordinates // to avoid the intersection with local clips/mask, which may yield incorrect // results when mixing objectBoundingBox and userSpaceOnUse units. // http://crbug.com/294900 FloatRect bounds = strokeBoundingBox(); SkPictureBuilder pictureBuilder(bounds, nullptr, &context); ColorFilter maskContentFilter = style()->svgStyle().colorInterpolation() == CI_LINEARRGB ? ColorFilterSRGBToLinearRGB : ColorFilterNone; pictureBuilder.context().setColorFilter(maskContentFilter); for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element()); childElement; childElement = Traversal<SVGElement>::nextSibling(*childElement)) { LayoutObject* layoutObject = childElement->layoutObject(); if (!layoutObject) continue; const ComputedStyle* style = layoutObject->style(); if (!style || style->display() == EDisplay::None || style->visibility() != EVisibility::Visible) continue; SVGPaintContext::paintSubtree(pictureBuilder.context(), layoutObject); } m_maskContentPicture = pictureBuilder.endRecording(); return m_maskContentPicture; }
AffineTransform SVGPreserveAspectRatio::getCTM(double logicX, double logicY, double logicWidth, double logicHeight, double /*physX*/, double /*physY*/, double physWidth, double physHeight) const { AffineTransform temp; if (align() == SVG_PRESERVEASPECTRATIO_UNKNOWN) return temp; double vpar = logicWidth / logicHeight; double svgar = physWidth / physHeight; if (align() == SVG_PRESERVEASPECTRATIO_NONE) { temp.scaleNonUniform(physWidth / logicWidth, physHeight / logicHeight); temp.translate(-logicX, -logicY); } else if ((vpar < svgar && (meetOrSlice() == SVG_MEETORSLICE_MEET)) || (vpar >= svgar && (meetOrSlice() == SVG_MEETORSLICE_SLICE))) { temp.scaleNonUniform(physHeight / logicHeight, physHeight / logicHeight); if (align() == SVG_PRESERVEASPECTRATIO_XMINYMIN || align() == SVG_PRESERVEASPECTRATIO_XMINYMID || align() == SVG_PRESERVEASPECTRATIO_XMINYMAX) temp.translate(-logicX, -logicY); else if (align() == SVG_PRESERVEASPECTRATIO_XMIDYMIN || align() == SVG_PRESERVEASPECTRATIO_XMIDYMID || align() == SVG_PRESERVEASPECTRATIO_XMIDYMAX) temp.translate(-logicX - (logicWidth - physWidth * logicHeight / physHeight) / 2, -logicY); else temp.translate(-logicX - (logicWidth - physWidth * logicHeight / physHeight), -logicY); } else { temp.scaleNonUniform(physWidth / logicWidth, physWidth / logicWidth); if (align() == SVG_PRESERVEASPECTRATIO_XMINYMIN || align() == SVG_PRESERVEASPECTRATIO_XMIDYMIN || align() == SVG_PRESERVEASPECTRATIO_XMAXYMIN) temp.translate(-logicX, -logicY); else if (align() == SVG_PRESERVEASPECTRATIO_XMINYMID || align() == SVG_PRESERVEASPECTRATIO_XMIDYMID || align() == SVG_PRESERVEASPECTRATIO_XMAXYMID) temp.translate(-logicX, -logicY - (logicHeight - physHeight * logicWidth / physWidth) / 2); else temp.translate(-logicX, -logicY - (logicHeight - physHeight * logicWidth / physWidth)); } return temp; }
bool RenderSVGResourceClipper::applyResource(RenderObject* object, GraphicsContext* context) { ASSERT(object); ASSERT(context); m_clipper.add(object); context->beginPath(); AffineTransform obbTransform; FloatRect objectBoundingBox = object->objectBoundingBox(); bool bbox = static_cast<SVGClipPathElement*>(node())->clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX; if (bbox) { obbTransform.translate(objectBoundingBox.x(), objectBoundingBox.y()); obbTransform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height()); } bool hasClipPath = false; WindRule clipRule = RULE_EVENODD; 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; Path pathData = styled->toClipPath(); if (pathData.isEmpty()) continue; if (bbox) pathData.transform(obbTransform); hasClipPath = true; context->addPath(pathData); clipRule = style->svgStyle()->clipRule(); } if (!hasClipPath) { Path clipPath; clipPath.addRect(FloatRect()); context->addPath(clipPath); } // FIXME! // We don't currently allow for heterogenous clip rules. // we would have to detect such, draw to a mask, and then clip // to that mask context->clipPath(clipRule); return true; }
bool SVGClipPainter::drawClipAsMask( GraphicsContext& context, const LayoutObject& layoutObject, const FloatRect& targetBoundingBox, const FloatRect& targetPaintInvalidationRect, const AffineTransform& localTransform, const FloatPoint& layerPositionOffset) { if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible( context, layoutObject, DisplayItem::kSVGClip)) return true; SkPictureBuilder maskPictureBuilder(targetPaintInvalidationRect, nullptr, &context); GraphicsContext& maskContext = maskPictureBuilder.context(); { TransformRecorder recorder(maskContext, layoutObject, localTransform); // Apply any clip-path clipping this clipPath (nested shape/clipPath.) Optional<ClipPathClipper> nestedClipPathClipper; if (ClipPathOperation* clipPathOperation = m_clip.styleRef().clipPath()) nestedClipPathClipper.emplace(maskContext, *clipPathOperation, m_clip, targetBoundingBox, layerPositionOffset); { AffineTransform contentTransform; if (m_clip.clipPathUnits() == SVGUnitTypes::kSvgUnitTypeObjectboundingbox) { contentTransform.translate(targetBoundingBox.x(), targetBoundingBox.y()); contentTransform.scaleNonUniform(targetBoundingBox.width(), targetBoundingBox.height()); } SubtreeContentTransformScope contentTransformScope(contentTransform); TransformRecorder contentTransformRecorder(maskContext, layoutObject, contentTransform); maskContext.getPaintController().createAndAppend<DrawingDisplayItem>( layoutObject, DisplayItem::kSVGClip, m_clip.createContentPicture()); } } LayoutObjectDrawingRecorder drawingRecorder(context, layoutObject, DisplayItem::kSVGClip, targetPaintInvalidationRect); sk_sp<SkPicture> maskPicture = maskPictureBuilder.endRecording(); context.drawPicture(maskPicture.get()); return true; }
static v8::Handle<v8::Value> scaleNonUniformCallback(const v8::Arguments& args) { INC_STATS("DOM.SVGMatrix.scaleNonUniform"); V8SVGPODTypeWrapper<AffineTransform>* impWrapper = V8SVGPODTypeWrapper<AffineTransform>::toNative(args.Holder()); AffineTransform impInstance = *impWrapper; AffineTransform* imp = &impInstance; EXCEPTION_BLOCK(float, scaleFactorX, static_cast<float>(args[0]->NumberValue())); EXCEPTION_BLOCK(float, scaleFactorY, static_cast<float>(args[1]->NumberValue())); AffineTransform result = *imp; result.scaleNonUniform(scaleFactorX, scaleFactorY); RefPtr<V8SVGPODTypeWrapper<AffineTransform> > wrapper = V8SVGStaticPODTypeWrapper<AffineTransform>::create(result); SVGElement* context = V8Proxy::svgContext(impWrapper); V8Proxy::setSVGContext(wrapper.get(), context); impWrapper->commitChange(impInstance, context); return toV8(wrapper.release()); }
static inline AffineTransform clipToTextMask(GraphicsContext* context, OwnPtr<ImageBuffer>& imageBuffer, const RenderObject* object, GradientData* gradientData) { const RenderObject* textRootBlock = findTextRootObject(object); context->clipToImageBuffer(textRootBlock->repaintRectInLocalCoordinates(), imageBuffer.get()); AffineTransform matrix; if (gradientData->boundingBoxMode) { FloatRect maskBoundingBox = textRootBlock->objectBoundingBox(); matrix.translate(maskBoundingBox.x(), maskBoundingBox.y()); matrix.scaleNonUniform(maskBoundingBox.width(), maskBoundingBox.height()); } matrix.multiply(gradientData->transform); return matrix; }
void RenderSVGResourceMasker::drawMaskForRenderer(GraphicsContext* context, const FloatRect& targetBoundingBox) { ASSERT(context); AffineTransform contentTransformation; SVGUnitTypes::SVGUnitType contentUnits = toSVGMaskElement(element())->maskContentUnits()->currentValue()->enumValue(); if (contentUnits == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { contentTransformation.translate(targetBoundingBox.x(), targetBoundingBox.y()); contentTransformation.scaleNonUniform(targetBoundingBox.width(), targetBoundingBox.height()); context->concatCTM(contentTransformation); } if (!m_maskContentDisplayList) createDisplayList(context, contentTransformation); ASSERT(m_maskContentDisplayList); context->drawDisplayList(m_maskContentDisplayList.get()); }
void RenderSVGResourceClipper::drawClipMaskContent(GraphicsContext* context, const FloatRect& targetBoundingBox) { ASSERT(context); AffineTransform contentTransformation; if (clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { contentTransformation.translate(targetBoundingBox.x(), targetBoundingBox.y()); contentTransformation.scaleNonUniform(targetBoundingBox.width(), targetBoundingBox.height()); context->concatCTM(contentTransformation); } if (!m_clipContentDisplayList) createDisplayList(context, contentTransformation); ASSERT(m_clipContentDisplayList); context->drawDisplayList(m_clipContentDisplayList.get()); }
FloatRect LayoutSVGResourceClipper::resourceBoundingBox( const FloatRect& referenceBox) { // The resource has not been layouted yet. Return the reference box. if (selfNeedsLayout()) return referenceBox; if (m_localClipBounds.isEmpty()) calculateLocalClipBounds(); AffineTransform transform = toSVGClipPathElement(element())->calculateTransform( SVGElement::IncludeMotionTransform); if (clipPathUnits() == SVGUnitTypes::kSvgUnitTypeObjectboundingbox) { transform.translate(referenceBox.x(), referenceBox.y()); transform.scaleNonUniform(referenceBox.width(), referenceBox.height()); } return transform.mapRect(m_localClipBounds); }
PassRefPtr<const SkPicture> LayoutSVGResourceMasker::createContentPicture(AffineTransform& contentTransformation, const FloatRect& targetBoundingBox) { SVGUnitTypes::SVGUnitType contentUnits = toSVGMaskElement(element())->maskContentUnits()->currentValue()->enumValue(); if (contentUnits == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { contentTransformation.translate(targetBoundingBox.x(), targetBoundingBox.y()); contentTransformation.scaleNonUniform(targetBoundingBox.width(), targetBoundingBox.height()); } if (m_maskContentPicture) return m_maskContentPicture; SubtreeContentTransformScope contentTransformScope(contentTransformation); // Using strokeBoundingBox (instead of paintInvalidationRectInLocalCoordinates) to avoid the intersection // with local clips/mask, which may yield incorrect results when mixing objectBoundingBox and // userSpaceOnUse units (http://crbug.com/294900). FloatRect bounds = strokeBoundingBox(); OwnPtr<DisplayItemList> displayItemList; if (RuntimeEnabledFeatures::slimmingPaintEnabled()) displayItemList = DisplayItemList::create(); GraphicsContext context(nullptr, displayItemList.get()); context.beginRecording(bounds); ColorFilter maskContentFilter = style()->svgStyle().colorInterpolation() == CI_LINEARRGB ? ColorFilterSRGBToLinearRGB : ColorFilterNone; context.setColorFilter(maskContentFilter); for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element()); childElement; childElement = Traversal<SVGElement>::nextSibling(*childElement)) { LayoutObject* layoutObject = childElement->layoutObject(); if (!layoutObject) continue; const ComputedStyle* style = layoutObject->style(); if (!style || style->display() == NONE || style->visibility() != VISIBLE) continue; SVGPaintContext::paintSubtree(&context, layoutObject); } if (displayItemList) displayItemList->commitNewDisplayItemsAndReplay(context); m_maskContentPicture = context.endRecording(); return m_maskContentPicture; }