Ejemplo n.º 1
0
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);
}
Ejemplo n.º 2
0
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;
}
Ejemplo n.º 3
0
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;
}
Ejemplo n.º 8
0
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;
}
Ejemplo n.º 15
0
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*)&current);
    current.scaleNonUniform(scaleFactors.width(), scaleFactors.height());
    platformContext()->setTransform((double*)&current);
}
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;
}
Ejemplo n.º 19
0
// 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;
}
Ejemplo n.º 20
0
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());
}
Ejemplo n.º 21
0
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;
}
Ejemplo n.º 24
0
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;
}
Ejemplo n.º 25
0
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());
}
Ejemplo n.º 26
0
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;
}
Ejemplo n.º 27
0
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());
}
Ejemplo n.º 28
0
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());
}
Ejemplo n.º 29
0
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;
}