bool RenderSVGResourceMasker::drawContentIntoMaskImage(MaskerData* maskerData, ColorSpace colorSpace, 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;
        if (renderer->needsLayout())
            return false;
        RenderStyle* style = renderer->style();
        if (!style || style->display() == NONE || style->visibility() != VISIBLE)
            continue;
        SVGImageBufferTools::renderSubtreeToImageBuffer(maskerData->maskImage.get(), renderer, maskContentTransformation);
    }

#if !USE(CG)
    maskerData->maskImage->transformColorSpace(ColorSpaceDeviceRGB, colorSpace);
#else
    UNUSED_PARAM(colorSpace);
#endif

    // Create the luminance mask.
    maskerData->maskImage->convertToLuminanceMask();
    return true;
}
예제 #2
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;
}
예제 #3
0
AffineTransform RenderSVGContainer::getAspectRatio(const FloatRect& logical, const FloatRect& physical) const
{
    AffineTransform temp;

    float logicX = logical.x();
    float logicY = logical.y();
    float logicWidth = logical.width();
    float logicHeight = logical.height();
    float physWidth = physical.width();
    float physHeight = physical.height();

    float vpar = logicWidth / logicHeight;
    float svgar = physWidth / physHeight;

    if (align() == ALIGN_NONE) {
        temp.scale(physWidth / logicWidth, physHeight / logicHeight);
        temp.translate(-logicX, -logicY);
    } else if ((vpar < svgar && !slice()) || (vpar >= svgar && slice())) {
        temp.scale(physHeight / logicHeight, physHeight / logicHeight);

        if (align() == ALIGN_XMINYMIN || align() == ALIGN_XMINYMID || align() == ALIGN_XMINYMAX)
            temp.translate(-logicX, -logicY);
        else if (align() == ALIGN_XMIDYMIN || align() == ALIGN_XMIDYMID || align() == ALIGN_XMIDYMAX)
            temp.translate(-logicX - (logicWidth - physWidth * logicHeight / physHeight) / 2, -logicY);
        else
            temp.translate(-logicX - (logicWidth - physWidth * logicHeight / physHeight), -logicY);
    } else {
        temp.scale(physWidth / logicWidth, physWidth / logicWidth);

        if (align() == ALIGN_XMINYMIN || align() == ALIGN_XMIDYMIN || align() == ALIGN_XMAXYMIN)
            temp.translate(-logicX, -logicY);
        else if (align() == ALIGN_XMINYMID || align() == ALIGN_XMIDYMID || align() == ALIGN_XMAXYMID)
            temp.translate(-logicX, -logicY - (logicHeight - physHeight * logicWidth / physWidth) / 2);
        else
            temp.translate(-logicX, -logicY - (logicHeight - physHeight * logicWidth / physWidth));
    }

    return temp;
}
예제 #4
0
bool FilterEffectRendererHelper::prepareFilterEffect(RenderLayer* renderLayer, const LayoutRect& filterBoxRect, const LayoutRect& dirtyRect)
{
    ASSERT(m_haveFilterEffect && renderLayer->filterRenderer());
    m_renderLayer = renderLayer;
    m_paintInvalidationRect = dirtyRect;

    // Prepare a transformation that brings the coordinates into the space
    // filter coordinates are defined in.
    AffineTransform absoluteTransform;
    // FIXME: Should these really be upconverted to doubles and not rounded? crbug.com/350474
    absoluteTransform.translate(filterBoxRect.x().toDouble(), filterBoxRect.y().toDouble());

    FilterEffectRenderer* filter = renderLayer->filterRenderer();
    filter->setAbsoluteTransform(absoluteTransform);

    IntRect filterSourceRect = pixelSnappedIntRect(filter->computeSourceImageRectForDirtyRect(filterBoxRect, dirtyRect));

    if (filterSourceRect.isEmpty()) {
        // The dirty rect is not in view, just bail out.
        m_haveFilterEffect = false;
        return false;
    }

    m_filterBoxRect = filterBoxRect;
    filter->setFilterRegion(filter->mapAbsoluteRectToLocalRect(filterSourceRect));
    filter->lastEffect()->determineFilterPrimitiveSubregion(MapRectForward);

    bool hasUpdatedBackingStore = filter->updateBackingStoreRect(filterSourceRect);
    if (filter->hasFilterThatMovesPixels()) {
        if (hasUpdatedBackingStore)
            m_paintInvalidationRect = filterSourceRect;
        else
            m_paintInvalidationRect.intersect(filterSourceRect);
    }
    return true;
}
예제 #5
0
void ImageBuffer::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const AffineTransform& patternTransform,
                              const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& dstRect)
{
    if (m_data.m_tiledImage && context != m_context.get()) {
        FloatRect src = srcRect;
        if (src.width() == -1)
            src.setWidth(m_data.m_tiledImage->size().width());
        if (src.height() == -1)
            src.setHeight(m_data.m_tiledImage->size().height());

        ASSERT(context->platformContext()->activePainter());

        AffineTransform phasedPatternTransform;
        phasedPatternTransform.translate(phase.x(), phase.y());
        phasedPatternTransform.multLeft(patternTransform);

        PatternOpenVG pattern(m_data.m_tiledImage, src);
        pattern.setTransformation(phasedPatternTransform);

        PainterOpenVG* painter = context->platformContext()->activePainter();

        PaintOpenVG currentPaint = painter->fillPaint();
        CompositeOperator currentOp = painter->compositeOperation();

        painter->setCompositeOperation(op);
        painter->setFillPattern(pattern);
        painter->drawRect(dstRect, VG_FILL_PATH);

        painter->setFillPaint(currentPaint);
        painter->setCompositeOperation(currentOp);
        return;
    }

    RefPtr<Image> imageCopy = copyImage();
    imageCopy->drawPattern(context, srcRect, patternTransform, phase, styleColorSpace, op, dstRect);
}
예제 #6
0
void TransparencyWin::setupTransformForKeepTransform(const IntRect& region)
{
    if (!m_validLayer)
        return;

    if (m_layerMode != NoLayer) {
        // Need to save things since we're modifying the transform.
        m_drawContext->save();
        m_savedOnDrawContext = true;

        // Account for the fact that the layer may be offset from the
        // original. This only happens when we create a layer that has the
        // same coordinate space as the parent.
        AffineTransform xform;
        xform.translate(-m_transformedSourceRect.x(), -m_transformedSourceRect.y());

        // We're making a layer, so apply the old transform to the new one
        // so it's maintained. We know the new layer has the identity
        // transform now, we we can just multiply it.
        xform *= m_orgTransform;
        m_drawContext->concatCTM(xform);
    }
    m_drawRect = m_sourceRect;
}
예제 #7
0
AffineTransform SVGPreserveAspectRatio::getCTM(double logicX, double logicY,
                                               double logicWidth, double logicHeight,
                                               double /*physX*/, double /*physY*/,
                                               double physWidth, double physHeight)
{
    AffineTransform temp;

    if (align() == SVG_PRESERVEASPECTRATIO_UNKNOWN)
        return temp;

    double vpar = logicWidth / logicHeight;
    double svgar = physWidth / physHeight;

    if (align() == SVG_PRESERVEASPECTRATIO_NONE) {
        temp.scale(physWidth / logicWidth, physHeight / logicHeight);
        temp.translate(-logicX, -logicY);
    } else if (vpar < svgar && (meetOrSlice() == SVG_MEETORSLICE_MEET) || vpar >= svgar && (meetOrSlice() == SVG_MEETORSLICE_SLICE)) {
        temp.scale(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.scale(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;
}
void RenderSVGResourceMasker::createMaskImage(MaskerData* maskerData, const SVGMaskElement* maskElement, RenderObject* object)
{
    FloatRect objectBoundingBox = object->objectBoundingBox();

    // Mask rect clipped with clippingBoundingBox and filterBoundingBox as long as they are present.
    maskerData->maskRect = object->repaintRectInLocalCoordinates();
    if (maskerData->maskRect.isEmpty()) {
        maskerData->emptyMask = true;
        return;
    }
    
    if (m_maskBoundaries.isEmpty())
        calculateMaskContentRepaintRect();

    FloatRect repaintRect = m_maskBoundaries;
    AffineTransform contextTransform;
    // We need to scale repaintRect for objectBoundingBox to get the drawing area.
    if (maskElement->maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
        contextTransform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
        FloatPoint contextAdjustment = repaintRect.location();
        repaintRect = contextTransform.mapRect(repaintRect);
        repaintRect.move(objectBoundingBox.x(), objectBoundingBox.y());
        contextTransform.translate(-contextAdjustment.x(), -contextAdjustment.y());
    }
    repaintRect.intersect(maskerData->maskRect);
    maskerData->maskRect = repaintRect;
    IntRect maskImageRect = enclosingIntRect(maskerData->maskRect);

    maskImageRect.setLocation(IntPoint());

    // Don't create ImageBuffers with image size of 0
    if (maskImageRect.isEmpty()) {
        maskerData->emptyMask = true;
        return;
    }

    // FIXME: This changes color space to linearRGB, the default color space
    // for masking operations in SVG. We need a switch for the other color-space
    // attribute values sRGB, inherit and auto.
    maskerData->maskImage = ImageBuffer::create(maskImageRect.size(), LinearRGB);
    if (!maskerData->maskImage)
        return;

    GraphicsContext* maskImageContext = maskerData->maskImage->context();
    ASSERT(maskImageContext);

    maskImageContext->save();

    if (maskElement->maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE)
        maskImageContext->translate(-maskerData->maskRect.x(), -maskerData->maskRect.y());
    maskImageContext->concatCTM(contextTransform);

    // 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;
        renderSubtreeToImage(maskerData->maskImage.get(), renderer);
    }

    maskImageContext->restore();

    // create the luminance mask
    RefPtr<ImageData> imageData(maskerData->maskImage->getUnmultipliedImageData(maskImageRect));
    CanvasPixelArray* srcPixelArray(imageData->data());

    for (unsigned pixelOffset = 0; pixelOffset < srcPixelArray->length(); 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());
}
void SVGTextRunRenderingContext::drawSVGGlyphs(GraphicsContext* context, const TextRun& run, const SimpleFontData* fontData, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const
{
    SVGFontElement* fontElement = 0;
    SVGFontFaceElement* fontFaceElement = 0;

    const SVGFontData* svgFontData = svgFontAndFontFaceElementForFontData(fontData, fontFaceElement, fontElement);
    if (!fontElement || !fontFaceElement)
        return;

    // We can only paint SVGFonts if a context is available.
    RenderSVGResource* activePaintingResource = activePaintingResourceFromRun(run);
    RenderObject* renderObject = renderObjectFromRun(run);
    RenderObject* parentRenderObject = firstParentRendererForNonTextNode(renderObject);
    RenderStyle* parentRenderObjectStyle = 0;

    ASSERT(renderObject);
    if (!activePaintingResource) {
        // TODO: We're only supporting simple filled HTML text so far.
        RenderSVGResourceSolidColor* solidPaintingResource = RenderSVGResource::sharedSolidPaintingResource();
        solidPaintingResource->setColor(context->fillColor());
        activePaintingResource = solidPaintingResource;
    }
 
    bool isVerticalText = false;
    if (parentRenderObject) {
        parentRenderObjectStyle = parentRenderObject->style();
        ASSERT(parentRenderObjectStyle);
        isVerticalText = parentRenderObjectStyle->svgStyle()->isVerticalWritingMode();
    }

    float scale = scaleEmToUnits(fontData->platformData().size(), fontFaceElement->unitsPerEm());
    ASSERT(activePaintingResource);

    FloatPoint glyphOrigin;
    glyphOrigin.setX(svgFontData->horizontalOriginX() * scale);
    glyphOrigin.setY(svgFontData->horizontalOriginY() * scale);

    FloatPoint currentPoint = point;
    RenderSVGResourceMode resourceMode = context->textDrawingMode() == TextModeStroke ? ApplyToStrokeMode : ApplyToFillMode;
    for (int i = 0; i < numGlyphs; ++i) {
        Glyph glyph = glyphBuffer.glyphAt(from + i);
        if (!glyph)
            continue;

        float advance = glyphBuffer.advanceAt(from + i);
        SVGGlyph svgGlyph = fontElement->svgGlyphForGlyph(glyph);
        ASSERT(!svgGlyph.isPartOfLigature);
        ASSERT(svgGlyph.tableEntry == glyph);

        SVGGlyphElement::inheritUnspecifiedAttributes(svgGlyph, svgFontData);

        // FIXME: Support arbitary SVG content as glyph (currently limited to <glyph d="..."> situations).
        if (svgGlyph.pathData.isEmpty()) {
            if (isVerticalText)
                currentPoint.move(0, advance);
            else
                currentPoint.move(advance, 0);
            continue;
         }

        context->save();

        if (isVerticalText) {
            glyphOrigin.setX(svgGlyph.verticalOriginX * scale);
            glyphOrigin.setY(svgGlyph.verticalOriginY * scale);
         }

        AffineTransform glyphPathTransform;
        glyphPathTransform.translate(currentPoint.x() + glyphOrigin.x(), currentPoint.y() + glyphOrigin.y());
        glyphPathTransform.scale(scale, -scale);

        Path glyphPath = svgGlyph.pathData;
        glyphPath.transform(glyphPathTransform);

        if (activePaintingResource->applyResource(parentRenderObject, parentRenderObjectStyle, context, resourceMode)) {
            if (renderObject && renderObject->isSVGInlineText()) {
                const RenderSVGInlineText* textRenderer = toRenderSVGInlineText(renderObject);
                context->setStrokeThickness(context->strokeThickness() * textRenderer->scalingFactor());
            }
            activePaintingResource->postApplyResource(parentRenderObject, context, resourceMode, &glyphPath, 0);
         }
 
        context->restore();

        if (isVerticalText)
            currentPoint.move(0, advance);
        else
            currentPoint.move(advance, 0);
    }
}
예제 #10
0
bool RenderSVGResourceGradient::applyResource(RenderObject* object, RenderStyle* style, GraphicsContext*& context, unsigned short resourceMode)
{
    ASSERT(object);
    ASSERT(style);
    ASSERT(context);
    ASSERT(resourceMode != ApplyToDefaultMode);

    // 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 invalidateClients() 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 = static_cast<SVGGradientElement*>(node());
    if (!gradientElement)
        return false;

    gradientElement->updateAnimatedSVGAttribute(anyQName());

    if (!m_gradient.contains(object))
        m_gradient.set(object, new GradientData);

    GradientData* gradientData = m_gradient.get(object);

    // Create gradient object
    if (!gradientData->gradient)
        buildGradient(gradientData, gradientElement);

    if (!gradientData->gradient)
        return false;

    // Draw gradient
    context->save();

    bool isPaintingText = resourceMode & ApplyToTextMode;
    if (isPaintingText) {
#if PLATFORM(CG)
        if (!createMaskAndSwapContextForTextGradient(context, m_savedContext, m_imageBuffer, object)) {
            context->restore();
            return false;
        }
#endif

        context->setTextDrawingMode(resourceMode & ApplyToFillMode ? cTextFill : cTextStroke);
    }

    AffineTransform transform;

    // CG platforms will handle the gradient space transform for text after applying the
    // resource, so don't apply it here. For non-CG platforms, we want the text bounding
    // box applied to the gradient space transform now, so the gradient shader can use it.
#if PLATFORM(CG)
    if (gradientData->boundingBoxMode && !isPaintingText) {
#else
    if (gradientData->boundingBoxMode) {
#endif
        FloatRect objectBoundingBox = object->objectBoundingBox();
        transform.translate(objectBoundingBox.x(), objectBoundingBox.y());
        transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
    }

    transform.multiply(gradientData->transform);
    gradientData->gradient->setGradientSpaceTransform(transform);

    const SVGRenderStyle* svgStyle = style->svgStyle();
    ASSERT(svgStyle);

    if (resourceMode & ApplyToFillMode) {
        context->setAlpha(svgStyle->fillOpacity());
        context->setFillGradient(gradientData->gradient);
        context->setFillRule(svgStyle->fillRule());
    } else if (resourceMode & ApplyToStrokeMode) {
        context->setAlpha(svgStyle->strokeOpacity());
        context->setStrokeGradient(gradientData->gradient);
        applyStrokeStyleToContext(context, style, object);
    }

    return true;
}

void RenderSVGResourceGradient::postApplyResource(RenderObject* object, GraphicsContext*& context, unsigned short resourceMode)
{
    ASSERT(context);
    ASSERT(resourceMode != ApplyToDefaultMode);

    if (resourceMode & ApplyToTextMode) {
#if PLATFORM(CG)
        // CG requires special handling for gradient on text
        if (m_savedContext && m_gradient.contains(object)) {
            GradientData* gradientData = m_gradient.get(object);

            // Restore on-screen drawing context
            context = m_savedContext;
            m_savedContext = 0;

            gradientData->gradient->setGradientSpaceTransform(clipToTextMask(context, m_imageBuffer, object, gradientData));
            context->setFillGradient(gradientData->gradient);

            const RenderObject* textRootBlock = findTextRootObject(object);
            context->fillRect(textRootBlock->repaintRectInLocalCoordinates());

            m_imageBuffer.clear();
        }
#else
        UNUSED_PARAM(object);
#endif
    } else {
        if (resourceMode & ApplyToFillMode)
            context->fillPath();
        else if (resourceMode & ApplyToStrokeMode)
            context->strokePath();
    }

    context->restore();
}
PassRefPtr<const SkPicture> LayoutSVGResourceClipper::createContentPicture(AffineTransform& contentTransformation, const FloatRect& targetBoundingBox,
    GraphicsContext& context)
{
    ASSERT(frame());

    if (clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
        contentTransformation.translate(targetBoundingBox.x(), targetBoundingBox.y());
        contentTransformation.scaleNonUniform(targetBoundingBox.width(), targetBoundingBox.height());
    }

    if (m_clipContentPicture)
        return m_clipContentPicture;

    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();

    SkPictureBuilder pictureBuilder(bounds, nullptr, &context);

    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;

        bool isUseElement = isSVGUseElement(*childElement);
        if (isUseElement) {
            const SVGGraphicsElement* clippingElement = toSVGUseElement(*childElement).targetGraphicsElementForClipping();
            if (!clippingElement)
                continue;

            layoutObject = clippingElement->layoutObject();
            if (!layoutObject)
                continue;
        }

        // Only shapes, paths and texts are allowed for clipping.
        if (!layoutObject->isSVGShape() && !layoutObject->isSVGText())
            continue;

        if (isUseElement)
            layoutObject = childElement->layoutObject();

        // Switch to a paint behavior where all children of this <clipPath> will be laid out using special constraints:
        // - fill-opacity/stroke-opacity/opacity set to 1
        // - masker/filter not applied when laying out the children
        // - fill is set to the initial fill paint server (solid, black)
        // - stroke is set to the initial stroke paint server (none)
        PaintInfo info(pictureBuilder.context(), LayoutRect::infiniteIntRect(), PaintPhaseForeground, GlobalPaintNormalPhase, PaintLayerPaintingRenderingClipPathAsMask);
        layoutObject->paint(info, IntPoint());
    }

    m_clipContentPicture = pictureBuilder.endRecording();
    return m_clipContentPicture;
}
예제 #12
0
bool RenderSVGResourceClipper::drawContentIntoMaskImage(ClipperData* clipperData, const FloatRect& objectBoundingBox)
{
    ASSERT(clipperData);
    ASSERT(clipperData->clipMaskImage);

    GraphicsContext* maskContext = clipperData->clipMaskImage->context();
    ASSERT(maskContext);

    AffineTransform maskContentTransformation;
    SVGClipPathElement* clipPath = static_cast<SVGClipPathElement*>(node());
    if (clipPath->clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
        maskContentTransformation.translate(objectBoundingBox.x(), objectBoundingBox.y());
        maskContentTransformation.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
        maskContext->concatCTM(maskContentTransformation);
    }

    // Draw all clipPath children into a global mask.
    for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) {
        RenderObject* renderer = childNode->renderer();
        if (!childNode->isSVGElement() || !static_cast<SVGElement*>(childNode)->isStyled() || !renderer)
            continue;
        RenderStyle* style = renderer->style();
        if (!style || style->display() == NONE || style->visibility() != VISIBLE)
            continue;

        WindRule newClipRule = style->svgStyle()->clipRule();
        bool isUseElement = renderer->isSVGShadowTreeRootContainer();
        if (isUseElement) {
            SVGUseElement* useElement = static_cast<SVGUseElement*>(childNode);
            renderer = useElement->rendererClipChild();
            if (!renderer)
                continue;
            if (!useElement->hasAttribute(SVGNames::clip_ruleAttr))
                newClipRule = renderer->style()->svgStyle()->clipRule();
        }

        // Only shapes, paths and texts are allowed for clipping.
        if (!renderer->isSVGPath() && !renderer->isSVGText())
            continue;

        // Save the old RenderStyle of the current object for restoring after drawing
        // it to the MaskImage. The new intermediate RenderStyle needs to inherit from
        // the old one.
        RefPtr<RenderStyle> oldRenderStyle = renderer->style();
        RefPtr<RenderStyle> newRenderStyle = RenderStyle::clone(oldRenderStyle.get());
        SVGRenderStyle* svgStyle = newRenderStyle.get()->accessSVGStyle();
        svgStyle->setFillPaint(SVGPaint::defaultFill());
        svgStyle->setStrokePaint(SVGPaint::defaultStroke());
        svgStyle->setFillRule(newClipRule);
        newRenderStyle.get()->setOpacity(1.0f);
        svgStyle->setFillOpacity(1.0f);
        svgStyle->setStrokeOpacity(1.0f);
        svgStyle->setFilterResource(String());
        svgStyle->setMaskerResource(String());

        // The setStyle() call results in a styleDidChange() call, which in turn invalidations the resources.
        // As we're mutating the resource on purpose, block updates until we've resetted the style again.
        m_invalidationBlocked = true;
        renderer->setStyle(newRenderStyle.release());

        // 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.
        SVGImageBufferTools::renderSubtreeToImageBuffer(clipperData->clipMaskImage.get(), isUseElement ? childNode->renderer() : renderer, maskContentTransformation);

        renderer->setStyle(oldRenderStyle.release());
        m_invalidationBlocked = false;
    }

    return true;
}
void SVGTextRunRenderingContext::drawSVGGlyphs(GraphicsContext* context, const TextRun& run, const SimpleFontData* fontData, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const
{
    SVGFontElement* fontElement = 0;
    SVGFontFaceElement* fontFaceElement = 0;

    const SVGFontData* svgFontData = svgFontAndFontFaceElementForFontData(fontData, fontFaceElement, fontElement);
    if (!fontElement || !fontFaceElement)
        return;

    // We can only paint SVGFonts if a context is available.
    RenderObject* renderObject = renderObjectFromRun(run);
    ASSERT(renderObject);

    bool isVerticalText = false;
    if (RenderObject* parentRenderObject = firstParentRendererForNonTextNode(renderObject)) {
        RenderStyle* parentRenderObjectStyle = parentRenderObject->style();
        ASSERT(parentRenderObjectStyle);
        isVerticalText = parentRenderObjectStyle->svgStyle().isVerticalWritingMode();
    }

    float scale = scaleEmToUnits(fontData->platformData().size(), fontFaceElement->unitsPerEm());

    FloatPoint glyphOrigin;
    glyphOrigin.setX(svgFontData->horizontalOriginX() * scale);
    glyphOrigin.setY(svgFontData->horizontalOriginY() * scale);

    unsigned short resourceMode = context->textDrawingMode() == TextModeStroke ? ApplyToStrokeMode : ApplyToFillMode;

    FloatPoint currentPoint = point;
    for (int i = 0; i < numGlyphs; ++i) {
        Glyph glyph = glyphBuffer.glyphAt(from + i);
        if (!glyph)
            continue;

        float advance = glyphBuffer.advanceAt(from + i);
        SVGGlyph svgGlyph = fontElement->svgGlyphForGlyph(glyph);
        ASSERT(!svgGlyph.isPartOfLigature);
        ASSERT(svgGlyph.tableEntry == glyph);

        SVGGlyphElement::inheritUnspecifiedAttributes(svgGlyph, svgFontData);

        // FIXME: Support arbitary SVG content as glyph (currently limited to <glyph d="..."> situations).
        if (svgGlyph.pathData.isEmpty()) {
            if (isVerticalText)
                currentPoint.move(0, advance);
            else
                currentPoint.move(advance, 0);
            continue;
         }

        if (isVerticalText) {
            glyphOrigin.setX(svgGlyph.verticalOriginX * scale);
            glyphOrigin.setY(svgGlyph.verticalOriginY * scale);
         }

        AffineTransform glyphPathTransform;
        glyphPathTransform.translate(currentPoint.x() + glyphOrigin.x(), currentPoint.y() + glyphOrigin.y());
        glyphPathTransform.scale(scale, -scale);

        Path glyphPath = svgGlyph.pathData;
        glyphPath.transform(glyphPathTransform);

        SVGRenderSupport::fillOrStrokePath(context, resourceMode, glyphPath);

        if (isVerticalText)
            currentPoint.move(0, advance);
        else
            currentPoint.move(advance, 0);
    }
}
void Path::translate(const FloatSize& size)
{
    AffineTransform transformation;
    transformation.translate(size.width(), size.height());
    transform(transformation);
}
예제 #15
0
bool SVGClipPainter::prepareEffect(const LayoutObject& target,
                                   const FloatRect& targetBoundingBox,
                                   const FloatRect& paintInvalidationRect,
                                   const FloatPoint& layerPositionOffset,
                                   GraphicsContext& context,
                                   ClipperState& clipperState) {
  DCHECK_EQ(clipperState, ClipperState::NotApplied);
  SECURITY_DCHECK(!m_clip.needsLayout());

  m_clip.clearInvalidationMask();

  if (paintInvalidationRect.isEmpty() || m_clip.hasCycle())
    return false;

  SVGClipExpansionCycleHelper inClipExpansionChange(m_clip);

  AffineTransform animatedLocalTransform =
      toSVGClipPathElement(m_clip.element())->calculateAnimatedLocalTransform();
  // When drawing a clip for non-SVG elements, the CTM does not include the zoom
  // factor.  In this case, we need to apply the zoom scale explicitly - but
  // only for clips with userSpaceOnUse units (the zoom is accounted for
  // objectBoundingBox-resolved lengths).
  if (!target.isSVG() &&
      m_clip.clipPathUnits() == SVGUnitTypes::kSvgUnitTypeUserspaceonuse) {
    DCHECK(m_clip.style());
    animatedLocalTransform.scale(m_clip.style()->effectiveZoom());
  }

  // First, try to apply the clip as a clipPath.
  Path clipPath;
  if (m_clip.asPath(animatedLocalTransform, targetBoundingBox, clipPath)) {
    AffineTransform positionTransform;
    positionTransform.translate(layerPositionOffset.x(),
                                layerPositionOffset.y());
    clipPath.transform(positionTransform);
    clipperState = ClipperState::AppliedPath;
    context.getPaintController().createAndAppend<BeginClipPathDisplayItem>(
        target, clipPath);
    return true;
  }

  // Fall back to masking.
  clipperState = ClipperState::AppliedMask;

  // Begin compositing the clip mask.
  CompositingRecorder::beginCompositing(
      context, target, SkXfermode::kSrcOver_Mode, 1, &paintInvalidationRect);
  {
    if (!drawClipAsMask(context, target, targetBoundingBox,
                        paintInvalidationRect, animatedLocalTransform,
                        layerPositionOffset)) {
      // End the clip mask's compositor.
      CompositingRecorder::endCompositing(context, target);
      return false;
    }
  }

  // Masked content layer start.
  CompositingRecorder::beginCompositing(
      context, target, SkXfermode::kSrcIn_Mode, 1, &paintInvalidationRect);

  return true;
}
예제 #16
0
bool RenderSVGResourceClipper::tryPathOnlyClipping(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();

    for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element()); childElement; childElement = Traversal<SVGElement>::nextSibling(*childElement)) {
        RenderObject* renderer = childElement->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 (!childElement->isSVGGraphicsElement())
            continue;
        SVGGraphicsElement* styled = toSVGGraphicsElement(childElement);
        RenderStyle* style = renderer->style();
        if (!style || 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;

        if (clipPath.isEmpty()) {
            // First clip shape.
            styled->toClipPath(clipPath);
            clipRule = svgStyle.clipRule();
            clipPath.setWindRule(clipRule);
            continue;
        }

        if (RuntimeEnabledFeatures::pathOpsSVGClippingEnabled()) {
            // Attempt to generate a combined clip path, fall back to masking if not possible.
            Path subPath;
            styled->toClipPath(subPath);
            subPath.setWindRule(svgStyle.clipRule());
            if (!clipPath.unionPath(subPath))
                return false;
        } else {
            return false;
        }
    }
    // Only one visible shape/path was found. Directly continue clipping and transform the content to userspace if necessary.
    if (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;
}
예제 #17
0
void BlockPainter::paintObject(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
    if (RuntimeEnabledFeatures::slimmingPaintOffsetCachingEnabled() && m_layoutBlock.childrenInline() && !paintInfo.context.paintController().skippingCache()) {
        if (m_layoutBlock.paintOffsetChanged(paintOffset)) {
            LineBoxListPainter(m_layoutBlock.lineBoxes()).invalidateLineBoxPaintOffsets(paintInfo);
            paintInfo.context.paintController().invalidatePaintOffset(m_layoutBlock);
        }
        // Set previousPaintOffset here in case that m_layoutBlock paints nothing and no
        // LayoutObjectDrawingRecorder updates its previousPaintOffset.
        // TODO(wangxianzhu): Integrate paint offset checking into new paint invalidation.
        m_layoutBlock.mutableForPainting().setPreviousPaintOffset(paintOffset);
    }

    const PaintPhase paintPhase = paintInfo.phase;

    if ((paintPhase == PaintPhaseSelfBlockBackground || paintPhase == PaintPhaseBlockBackground)
        && m_layoutBlock.style()->visibility() == VISIBLE
        && m_layoutBlock.hasBoxDecorationBackground())
        m_layoutBlock.paintBoxDecorationBackground(paintInfo, paintOffset);

    if (paintPhase == PaintPhaseMask && m_layoutBlock.style()->visibility() == VISIBLE) {
        m_layoutBlock.paintMask(paintInfo, paintOffset);
        return;
    }

    if (paintPhase == PaintPhaseClippingMask && m_layoutBlock.style()->visibility() == VISIBLE) {
        BoxPainter(m_layoutBlock).paintClippingMask(paintInfo, paintOffset);
        return;
    }

    // FIXME: When Skia supports annotation rect covering (https://code.google.com/p/skia/issues/detail?id=3872),
    // this rect may be covered by foreground and descendant drawings. Then we may need a dedicated paint phase.
    if (paintPhase == PaintPhaseForeground && paintInfo.isPrinting())
        ObjectPainter(m_layoutBlock).addPDFURLRectIfNeeded(paintInfo, paintOffset);

    {
        Optional<ScrollRecorder> scrollRecorder;
        Optional<PaintInfo> scrolledPaintInfo;
        if (m_layoutBlock.hasOverflowClip()) {
            IntSize scrollOffset = m_layoutBlock.scrolledContentOffset();
            if (m_layoutBlock.layer()->scrollsOverflow() || !scrollOffset.isZero()) {
                scrollRecorder.emplace(paintInfo.context, m_layoutBlock, paintPhase, scrollOffset);
                scrolledPaintInfo.emplace(paintInfo);
                AffineTransform transform;
                transform.translate(-scrollOffset.width(), -scrollOffset.height());
                scrolledPaintInfo->updateCullRect(transform);
            }
        }

        // We're done. We don't bother painting any children.
        if (paintPhase == PaintPhaseSelfBlockBackground || paintInfo.paintRootBackgroundOnly())
            return;

        const PaintInfo& contentsPaintInfo = scrolledPaintInfo ? *scrolledPaintInfo : paintInfo;

        if (paintPhase != PaintPhaseSelfOutline)
            paintContents(contentsPaintInfo, paintOffset);

        if (paintPhase == PaintPhaseForeground && !paintInfo.isPrinting())
            m_layoutBlock.paintSelection(contentsPaintInfo, paintOffset); // Fill in gaps in selection on lines and between blocks.

        if (paintPhase == PaintPhaseFloat || paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip)
            m_layoutBlock.paintFloats(contentsPaintInfo, paintOffset);
    }

    if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && m_layoutBlock.style()->hasOutline() && m_layoutBlock.style()->visibility() == VISIBLE)
        ObjectPainter(m_layoutBlock).paintOutline(paintInfo, paintOffset);

    // If the caret's node's layout object's containing block is this block, and the paint action is PaintPhaseForeground,
    // then paint the caret.
    if (paintPhase == PaintPhaseForeground && m_layoutBlock.hasCaret() && !LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(paintInfo.context, m_layoutBlock, DisplayItem::Caret, paintOffset)) {
        LayoutRect bounds = m_layoutBlock.visualOverflowRect();
        bounds.moveBy(paintOffset);
        LayoutObjectDrawingRecorder recorder(paintInfo.context, m_layoutBlock, DisplayItem::Caret, bounds, paintOffset);
        paintCarets(paintInfo, paintOffset);
    }
}
예제 #18
0
bool RenderSVGResourceClipper::drawContentIntoMaskImage(ClipperData* clipperData, const FloatRect& objectBoundingBox)
{
    ASSERT(frame());
    ASSERT(clipperData);
    ASSERT(clipperData->clipMaskImage);

    GraphicsContext* maskContext = clipperData->clipMaskImage->context();
    ASSERT(maskContext);

    AffineTransform maskContentTransformation;
    SVGClipPathElement* clipPath = static_cast<SVGClipPathElement*>(node());
    if (clipPath->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 = frame()->view()->paintBehavior();
    frame()->view()->setPaintBehavior(oldBehavior | PaintBehaviorRenderingSVGMask);

    // Draw all clipPath children into a global mask.
    for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) {
        RenderObject* renderer = childNode->renderer();
        if (!childNode->isSVGElement() || !toSVGElement(childNode)->isSVGStyledElement() || !renderer)
            continue;
        if (renderer->needsLayout()) {
            frame()->view()->setPaintBehavior(oldBehavior);
            return false;
        }
        RenderStyle* style = renderer->style();
        if (!style || style->display() == NONE || style->visibility() != VISIBLE)
            continue;

        WindRule newClipRule = style->svgStyle()->clipRule();
        bool isUseElement = childNode->hasTagName(SVGNames::useTag);
        if (isUseElement) {
            SVGUseElement* useElement = static_cast<SVGUseElement*>(childNode);
            renderer = useElement->rendererClipChild();
            if (!renderer)
                continue;
            if (!useElement->hasAttribute(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(clipperData->clipMaskImage.get(), isUseElement ? childNode->renderer() : renderer, maskContentTransformation);
    }

    frame()->view()->setPaintBehavior(oldBehavior);
    return true;
}
예제 #19
0
void BlockPainter::paintObject(const PaintInfo& paintInfo,
                               const LayoutPoint& paintOffset) {
  const PaintPhase paintPhase = paintInfo.phase;

  if (shouldPaintSelfBlockBackground(paintPhase)) {
    if (m_layoutBlock.style()->visibility() == EVisibility::Visible &&
        m_layoutBlock.hasBoxDecorationBackground())
      m_layoutBlock.paintBoxDecorationBackground(paintInfo, paintOffset);
    // We're done. We don't bother painting any children.
    if (paintPhase == PaintPhaseSelfBlockBackgroundOnly)
      return;
  }

  if (paintInfo.paintRootBackgroundOnly())
    return;

  if (paintPhase == PaintPhaseMask &&
      m_layoutBlock.style()->visibility() == EVisibility::Visible) {
    m_layoutBlock.paintMask(paintInfo, paintOffset);
    return;
  }

  if (paintPhase == PaintPhaseClippingMask &&
      m_layoutBlock.style()->visibility() == EVisibility::Visible) {
    BoxPainter(m_layoutBlock).paintClippingMask(paintInfo, paintOffset);
    return;
  }

  if (paintPhase == PaintPhaseForeground && paintInfo.isPrinting())
    ObjectPainter(m_layoutBlock).addPDFURLRectIfNeeded(paintInfo, paintOffset);

  if (paintPhase != PaintPhaseSelfOutlineOnly) {
    Optional<ScopedPaintChunkProperties> m_scopedScrollProperty;
    Optional<ScrollRecorder> scrollRecorder;
    Optional<PaintInfo> scrolledPaintInfo;
    if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) {
      const auto* objectProperties = m_layoutBlock.paintProperties();
      if (auto* scroll =
              objectProperties ? objectProperties->scroll() : nullptr) {
        PaintChunkProperties properties(paintInfo.context.getPaintController()
                                            .currentPaintChunkProperties());
        auto* scrollTranslation = objectProperties->scrollTranslation();
        DCHECK(scrollTranslation);
        properties.transform = scrollTranslation;
        properties.scroll = scroll;
        m_scopedScrollProperty.emplace(
            paintInfo.context.getPaintController(), m_layoutBlock,
            DisplayItem::paintPhaseToDrawingType(paintPhase), properties);
        scrolledPaintInfo.emplace(paintInfo);
        scrolledPaintInfo->updateCullRect(
            scrollTranslation->matrix().toAffineTransform());
      }
    } else if (m_layoutBlock.hasOverflowClip()) {
      IntSize scrollOffset = m_layoutBlock.scrolledContentOffset();
      if (m_layoutBlock.layer()->scrollsOverflow() || !scrollOffset.isZero()) {
        scrollRecorder.emplace(paintInfo.context, m_layoutBlock, paintPhase,
                               scrollOffset);
        scrolledPaintInfo.emplace(paintInfo);
        AffineTransform transform;
        transform.translate(-scrollOffset.width(), -scrollOffset.height());
        scrolledPaintInfo->updateCullRect(transform);
      }
    }

    const PaintInfo& contentsPaintInfo =
        scrolledPaintInfo ? *scrolledPaintInfo : paintInfo;

    if (m_layoutBlock.isLayoutBlockFlow()) {
      BlockFlowPainter blockFlowPainter(toLayoutBlockFlow(m_layoutBlock));
      blockFlowPainter.paintContents(contentsPaintInfo, paintOffset);
      if (paintPhase == PaintPhaseFloat || paintPhase == PaintPhaseSelection ||
          paintPhase == PaintPhaseTextClip)
        blockFlowPainter.paintFloats(contentsPaintInfo, paintOffset);
    } else {
      paintContents(contentsPaintInfo, paintOffset);
    }
  }

  if (shouldPaintSelfOutline(paintPhase))
    ObjectPainter(m_layoutBlock).paintOutline(paintInfo, paintOffset);

  // If the caret's node's layout object's containing block is this block, and
  // the paint action is PaintPhaseForeground, then paint the caret.
  if (paintPhase == PaintPhaseForeground && m_layoutBlock.hasCaret())
    paintCarets(paintInfo, paintOffset);
}
예제 #20
0
void Font::drawComplexText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const
{
    const FontPlatformData& fontPlatformData = primaryFont()->platformData();
    const float scaleFactor = fontPlatformData.scaleFactor();
    Olympia::Platform::Text::Font* font = fontPlatformData.font(); // FIXME: use fallback fonts as well?
    Olympia::Platform::Text::DrawParam drawParam;

    String sanitized = setupTextDrawing(this, run, &drawParam);
    adjustOffsetsForTextDrawing(run, from, to);

    SurfaceOpenVG* surface = context->platformContext();
    surface->makeCurrent();

    // Before passing control to Text API, make sure we're ok ourselves.
    ASSERT_VG_NO_ERROR();

    PainterOpenVG* painter = surface->activePainter();
    painter->save();
    painter->setStateModified(PainterOpenVG::AllStateCategories);

    Olympia::Platform::Text::GraphicsContext* textContext = FontPlatformData::textGraphicsContext();

#if PLATFORM(EGL)
    textContext->setDisplay(static_cast<Olympia::Platform::Text::NativeGraphicsDisplay>(surface->eglDisplay()));
    textContext->setSurface(static_cast<Olympia::Platform::Text::NativeGraphicsSurface>(surface->eglSurface()));
    textContext->setContext(static_cast<Olympia::Platform::Text::NativeGraphicsContext>(surface->eglContext()));
    ASSERT_EGL_NO_ERROR();
#endif

    if (painter->textDrawingMode() & cTextClip)
        return; // unsupported for every port except CG at the time of writing
    if (!(painter->textDrawingMode() & cTextFill))
        painter->setFillColor(Color::transparent);
    if (!(painter->textDrawingMode() & cTextStroke))
        painter->setStrokeStyle(NoStroke);

    if (from > 0 || to < sanitized.length()) {
        // Clip to the from-to region. We need to draw the full text
        // (and clip) because characters might change appearance when
        // connected to other characters.
        double fromX;
        FontPlatformData::engine()->textPosToX(from, fromX, *font,
            sanitized.characters(), sanitized.length(), drawParam);
        ASSERT_VG_NO_ERROR();
        fromX *= scaleFactor;

        double toX;
        FontPlatformData::engine()->textPosToX(to, toX, *font,
            sanitized.characters(), sanitized.length(), drawParam);
        ASSERT_VG_NO_ERROR();
        toX *= scaleFactor;

        // FIXME: Some glyphs can be higher than primary font's height.
        // We multiply ascent and descent by 4 so the clip rect will be high
        // enough for all glyphs we know. This is safe because we only apply
        // the clip rect when drawing partial strings, and we are only interested
        // in horizontal clip range.
        const float y = point.y() - primaryFont()->ascent() * 4;
        const float height = (primaryFont()->ascent() + primaryFont()->descent()) * 4;
        FloatRect clipRect = (toX > fromX)
            ? FloatRect(point.x() + fromX, y, toX - fromX, height)
            : FloatRect(point.x() + toX, y, fromX - toX, height);

        painter->clipRect(clipRect, PainterOpenVG::IntersectClip);
    }

    AffineTransform transformation = painter->transformation();
    transformation.translate(point.x(), point.y());
    FloatPoint currentTranslation(transformation.e(), transformation.f());
    transformation.setE(0);
    transformation.setF(0);
    transformation.scale(scaleFactor);
    transformation.setE(roundf(currentTranslation.x()));
    transformation.setF(roundf(currentTranslation.y()));
    painter->setTransformation(transformation);

    if (painter->shadowEnabled()) {
        painter->beginDrawShadow();
        FontPlatformData::engine()->drawText(textContext, *font,
            sanitized.characters(), sanitized.length(), 0, 0,
            0 /* no wrap */, &drawParam, 0 /* returned metrics */);
        painter->endDrawShadow();
    }

    FontPlatformData::engine()->drawText(textContext, *font,
        sanitized.characters(), sanitized.length(), 0, 0,
        0 /* no wrap */, &drawParam, 0 /* returned metrics */);

#if PLATFORM(EGL)
    SurfaceOpenVG* sharedSurface = surface->sharedSurface();
    textContext->setDisplay(static_cast<Olympia::Platform::Text::NativeGraphicsDisplay>(sharedSurface->eglDisplay()));
    textContext->setSurface(static_cast<Olympia::Platform::Text::NativeGraphicsSurface>(sharedSurface->eglSurface()));
    textContext->setContext(static_cast<Olympia::Platform::Text::NativeGraphicsContext>(sharedSurface->eglContext()));
    ASSERT_EGL_NO_ERROR();
#endif

    // Let's make sure Text API didn't cause any troubles.
    ASSERT_VG_NO_ERROR();
    painter->restore();
}