bool SVGRenderSupport::updateGraphicsContext(const PaintInfo& paintInfo, GraphicsContextStateSaver& stateSaver, RenderStyle* style, RenderObject& renderer, RenderSVGResourceMode resourceMode, const AffineTransform* additionalPaintServerTransform) { ASSERT(style); ASSERT(paintInfo.context == stateSaver.context()); GraphicsContext* context = paintInfo.context; if (paintInfo.isRenderingClipPathAsMaskImage()) { if (resourceMode == ApplyToStrokeMode) return false; context->setAlphaAsFloat(1); context->setFillColor(SVGRenderStyle::initialFillPaintColor()); return true; } SVGPaintServer paintServer = SVGPaintServer::requestForRenderer(renderer, style, resourceMode); if (!paintServer.isValid()) return false; if (additionalPaintServerTransform && paintServer.isTransformDependent()) paintServer.prependTransform(*additionalPaintServerTransform); paintServer.apply(*context, resourceMode, &stateSaver); const SVGRenderStyle& svgStyle = style->svgStyle(); if (resourceMode == ApplyToFillMode) { context->setAlphaAsFloat(svgStyle.fillOpacity()); context->setFillRule(svgStyle.fillRule()); } else { context->setAlphaAsFloat(svgStyle.strokeOpacity()); applyStrokeStyleToContext(context, style, &renderer); } return true; }
bool SVGLayoutSupport::updateGraphicsContext(const PaintInfo& paintInfo, GraphicsContextStateSaver& stateSaver, const ComputedStyle& style, LayoutObject& layoutObject, LayoutSVGResourceMode resourceMode, const AffineTransform* additionalPaintServerTransform) { ASSERT(paintInfo.context == stateSaver.context()); GraphicsContext& context = *paintInfo.context; if (paintInfo.isRenderingClipPathAsMaskImage()) { if (resourceMode == ApplyToStrokeMode) return false; context.setFillColor(SVGComputedStyle::initialFillPaintColor()); return true; } SVGPaintServer paintServer = SVGPaintServer::requestForLayoutObject(layoutObject, style, resourceMode); if (!paintServer.isValid()) return false; if (additionalPaintServerTransform && paintServer.isTransformDependent()) paintServer.prependTransform(*additionalPaintServerTransform); const SVGComputedStyle& svgStyle = style.svgStyle(); float paintAlpha = resourceMode == ApplyToFillMode ? svgStyle.fillOpacity() : svgStyle.strokeOpacity(); paintServer.apply(context, resourceMode, paintAlpha, stateSaver); if (resourceMode == ApplyToFillMode) context.setFillRule(svgStyle.fillRule()); else applyStrokeStyleToContext(context, style, layoutObject); return true; }
SVGPaintServer* KSVGPainterFactory::strokePaintServer(const RenderStyle* style, const RenderObject* item) { if (!style->svgStyle()->hasStroke()) return 0; SVGPaint* stroke = style->svgStyle()->strokePaint(); SVGPaintServer* strokePaintServer = 0; if (stroke->paintType() == SVGPaint::SVG_PAINTTYPE_URI) { AtomicString id(SVGURIReference::getTarget(stroke->uri())); strokePaintServer = getPaintServerById(item->document(), id); SVGElement* svgElement = static_cast<SVGElement*>(item->element()); ASSERT(svgElement && svgElement->document() && svgElement->isStyled()); if (item->isRenderPath() && strokePaintServer) strokePaintServer->addClient(static_cast<SVGStyledElement*>(svgElement)); else if (!strokePaintServer) svgElement->document()->accessSVGExtensions()->addPendingResource(id, static_cast<SVGStyledElement*>(svgElement)); } else { strokePaintServer = sharedSolidPaintServer(); SVGPaintServerSolid* strokePaintServerSolid = static_cast<SVGPaintServerSolid*>(strokePaintServer); if (stroke->paintType() == SVGPaint::SVG_PAINTTYPE_CURRENTCOLOR) strokePaintServerSolid->setColor(style->color()); else strokePaintServerSolid->setColor(stroke->color()); // FIXME: Ideally invalid colors would never get set on the RenderStyle and this could turn into an ASSERT if (!strokePaintServerSolid->color().isValid()) strokePaintServer = 0; } return strokePaintServer; }
SVGPaintServer SVGPaintServer::requestForLayoutObject(const LayoutObject& layoutObject, const ComputedStyle& style, LayoutSVGResourceMode resourceMode) { ASSERT(resourceMode == ApplyToFillMode || resourceMode == ApplyToStrokeMode); SVGPaintDescription paintDescription = requestPaint(layoutObject, style, resourceMode); if (!paintDescription.isValid) return invalid(); if (!paintDescription.resource) return SVGPaintServer(paintDescription.color); SVGPaintServer paintServer = paintDescription.resource->preparePaintServer(layoutObject); if (paintServer.isValid()) return paintServer; if (paintDescription.hasFallback) return SVGPaintServer(paintDescription.color); return invalid(); }
static inline void fillAndStrokePath(const Path& path, GraphicsContext* context, RenderStyle* style, RenderPath* object) { context->beginPath(); SVGPaintServer* fillPaintServer = SVGPaintServer::fillPaintServer(style, object); if (fillPaintServer) { context->addPath(path); fillPaintServer->draw(context, object, ApplyToFillTargetType); } SVGPaintServer* strokePaintServer = SVGPaintServer::strokePaintServer(style, object); if (strokePaintServer) { context->addPath(path); // path is cleared when filled. strokePaintServer->draw(context, object, ApplyToStrokeTargetType); } }
bool SVGPaintContext::paintForLayoutObject( const PaintInfo& paintInfo, const ComputedStyle& style, const LayoutObject& layoutObject, LayoutSVGResourceMode resourceMode, SkPaint& paint, const AffineTransform* additionalPaintServerTransform) { if (paintInfo.isRenderingClipPathAsMaskImage()) { if (resourceMode == ApplyToStrokeMode) return false; paint.setColor(SVGComputedStyle::initialFillPaintColor().rgb()); paint.setShader(nullptr); return true; } SVGPaintServer paintServer = SVGPaintServer::requestForLayoutObject(layoutObject, style, resourceMode); if (!paintServer.isValid()) return false; if (additionalPaintServerTransform && paintServer.isTransformDependent()) paintServer.prependTransform(*additionalPaintServerTransform); const SVGComputedStyle& svgStyle = style.svgStyle(); float paintAlpha = resourceMode == ApplyToFillMode ? svgStyle.fillOpacity() : svgStyle.strokeOpacity(); paintServer.applyToSkPaint(paint, paintAlpha); // We always set filter quality to 'low' here. This value will only have an // effect for patterns, which are SkPictures, so using high-order filter // should have little effect on the overall quality. paint.setFilterQuality(kLow_SkFilterQuality); // TODO(fs): The color filter can set when generating a picture for a mask - // due to color-interpolation. We could also just apply the // color-interpolation property from the the shape itself (which could mean // the paintserver if it has it specified), since that would be more in line // with the spec for color-interpolation. For now, just steal it from the GC // though. // Additionally, it's not really safe/guaranteed to be correct, as // something down the paint pipe may want to farther tweak the color // filter, which could yield incorrect results. (Consider just using // saveLayer() w/ this color filter explicitly instead.) paint.setColorFilter(sk_ref_sp(paintInfo.context.getColorFilter())); return true; }
SVGPaintServer* SVGPaintServer::fillPaintServer(const RenderStyle* style, const RenderObject* item) { if (!style->svgStyle()->hasFill()) return 0; SVGPaint* fill = style->svgStyle()->fillPaint(); SVGPaintServer* fillPaintServer = 0; SVGPaint::SVGPaintType paintType = fill->paintType(); if (paintType == SVGPaint::SVG_PAINTTYPE_URI || paintType == SVGPaint::SVG_PAINTTYPE_URI_RGBCOLOR) { AtomicString id(SVGURIReference::getTarget(fill->uri())); fillPaintServer = getPaintServerById(item->document(), id); SVGElement* svgElement = static_cast<SVGElement*>(item->node()); ASSERT(svgElement && svgElement->document() && svgElement->isStyled()); if (item->isRenderPath() && fillPaintServer) fillPaintServer->addClient(static_cast<SVGStyledElement*>(svgElement)); else if (!fillPaintServer && paintType == SVGPaint::SVG_PAINTTYPE_URI) svgElement->document()->accessSVGExtensions()->addPendingResource(id, static_cast<SVGStyledElement*>(svgElement)); } if (paintType != SVGPaint::SVG_PAINTTYPE_URI && !fillPaintServer) { fillPaintServer = sharedSolidPaintServer(); SVGPaintServerSolid* fillPaintServerSolid = static_cast<SVGPaintServerSolid*>(fillPaintServer); if (paintType == SVGPaint::SVG_PAINTTYPE_CURRENTCOLOR) fillPaintServerSolid->setColor(style->color()); else fillPaintServerSolid->setColor(fill->color()); // FIXME: Ideally invalid colors would never get set on the RenderStyle and this could turn into an ASSERT if (!fillPaintServerSolid->color().isValid()) fillPaintServer = 0; } if (!fillPaintServer) { // default value (black), see bug 11017 fillPaintServer = sharedSolidPaintServer(); static_cast<SVGPaintServerSolid*>(fillPaintServer)->setColor(Color::black); } return fillPaintServer; }
void Font::drawTextUsingSVGFont(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const { SVGFontElement* fontElement = 0; SVGFontFaceElement* fontFaceElement = 0; if (const SVGFontData* fontData = svgFontAndFontFaceElementForFontData(primaryFont(), fontFaceElement, fontElement)) { if (!fontElement) return; SVGTextRunWalkerDrawTextData data; FloatPoint currentPoint = point; float scale = convertEmUnitToPixel(size(), fontFaceElement->unitsPerEm(), 1.0f); SVGPaintServer* activePaintServer = run.activePaintServer(); // If renderObject is not set, we're dealing for HTML text rendered using SVG Fonts. if (!run.referencingRenderObject()) { ASSERT(!activePaintServer); // TODO: We're only supporting simple filled HTML text so far. SVGPaintServerSolid* solidPaintServer = SVGPaintServer::sharedSolidPaintServer(); solidPaintServer->setColor(context->fillColor()); activePaintServer = solidPaintServer; } ASSERT(activePaintServer); int charsConsumed; String glyphName; bool isVerticalText = false; float xStartOffset = floatWidthOfSubStringUsingSVGFont(this, run, 0, run.rtl() ? to : 0, run.rtl() ? run.length() : from, charsConsumed, glyphName); FloatPoint glyphOrigin; String language; // TODO: language matching & svg glyphs should be possible for HTML text, too. if (run.referencingRenderObject()) { isVerticalText = isVerticalWritingMode(run.referencingRenderObject()->style()->svgStyle()); if (SVGElement* element = static_cast<SVGElement*>(run.referencingRenderObject()->element())) language = element->getAttribute(XMLNames::langAttr); } if (!isVerticalText) { glyphOrigin.setX(fontData->horizontalOriginX() * scale); glyphOrigin.setY(fontData->horizontalOriginY() * scale); } data.extraCharsAvailable = 0; data.charsConsumed = 0; SVGTextRunWalker<SVGTextRunWalkerDrawTextData> runWalker(fontData, fontElement, data, drawTextUsingSVGFontCallback, drawTextMissingGlyphCallback); runWalker.walk(run, isVerticalText, language, from, to); SVGPaintTargetType targetType = context->textDrawingMode() == cTextStroke ? ApplyToStrokeTargetType : ApplyToFillTargetType; unsigned numGlyphs = data.glyphIdentifiers.size(); unsigned fallbackCharacterIndex = 0; for (unsigned i = 0; i < numGlyphs; ++i) { const SVGGlyphIdentifier& identifier = data.glyphIdentifiers[run.rtl() ? numGlyphs - i - 1 : i]; if (identifier.isValid) { // FIXME: Support arbitary SVG content as glyph (currently limited to <glyph d="..."> situations). if (!identifier.pathData.isEmpty()) { context->save(); if (isVerticalText) { glyphOrigin.setX(identifier.verticalOriginX * scale); glyphOrigin.setY(identifier.verticalOriginY * scale); } context->translate(xStartOffset + currentPoint.x() + glyphOrigin.x(), currentPoint.y() + glyphOrigin.y()); context->scale(FloatSize(scale, -scale)); context->beginPath(); context->addPath(identifier.pathData); if (activePaintServer->setup(context, run.referencingRenderObject(), targetType)) { // Spec: Any properties specified on a text elements which represents a length, such as the // 'stroke-width' property, might produce surprising results since the length value will be // processed in the coordinate system of the glyph. (TODO: What other lengths? miter-limit? dash-offset?) if (targetType == ApplyToStrokeTargetType && scale != 0.0f) context->setStrokeThickness(context->strokeThickness() / scale); activePaintServer->renderPath(context, run.referencingRenderObject(), targetType); activePaintServer->teardown(context, run.referencingRenderObject(), targetType); } context->restore(); } if (isVerticalText) currentPoint.move(0.0f, identifier.verticalAdvanceY * scale); else currentPoint.move(identifier.horizontalAdvanceX * scale, 0.0f); } else { // Handle system font fallback FontDescription fontDescription(context->font().fontDescription()); fontDescription.setFamily(FontFamily()); Font font(fontDescription, 0, 0); // spacing handled by SVG text code. font.update(context->font().fontSelector()); TextRun fallbackCharacterRun(run); fallbackCharacterRun.setText(&data.fallbackCharacters[run.rtl() ? data.fallbackCharacters.size() - fallbackCharacterIndex - 1 : fallbackCharacterIndex], 1); font.drawText(context, fallbackCharacterRun, currentPoint); if (isVerticalText) currentPoint.move(0.0f, font.floatWidth(fallbackCharacterRun)); else currentPoint.move(font.floatWidth(fallbackCharacterRun), 0.0f); fallbackCharacterIndex++; } } } }
void paintSVGInlineFlow(InlineFlowBox* flow, RenderObject* object, RenderObject::PaintInfo& paintInfo, int tx, int ty) { if (paintInfo.context->paintingDisabled()) return; paintInfo.context->save(); paintInfo.context->concatCTM(object->localTransform()); FloatRect boundingBox(tx + flow->xPos(), ty + flow->yPos(), flow->width(), flow->height()); SVGElement* svgElement = static_cast<SVGElement*>(object->element()); ASSERT(svgElement && svgElement->document() && svgElement->isStyled()); SVGStyledElement* styledElement = static_cast<SVGStyledElement*>(svgElement); const SVGRenderStyle* svgStyle = object->style()->svgStyle(); AtomicString filterId(SVGURIReference::getTarget(svgStyle->filter())); AtomicString clipperId(SVGURIReference::getTarget(svgStyle->clipPath())); AtomicString maskerId(SVGURIReference::getTarget(svgStyle->maskElement())); #if ENABLE(SVG_EXPERIMENTAL_FEATURES) SVGResourceFilter* filter = getFilterById(object->document(), filterId); #endif SVGResourceClipper* clipper = getClipperById(object->document(), clipperId); SVGResourceMasker* masker = getMaskerById(object->document(), maskerId); #if ENABLE(SVG_EXPERIMENTAL_FEATURES) if (filter) filter->prepareFilter(paintInfo.context, boundingBox); else if (!filterId.isEmpty()) svgElement->document()->accessSVGExtensions()->addPendingResource(filterId, styledElement); #endif if (clipper) { clipper->addClient(styledElement); clipper->applyClip(paintInfo.context, boundingBox); } else if (!clipperId.isEmpty()) svgElement->document()->accessSVGExtensions()->addPendingResource(clipperId, styledElement); if (masker) { masker->addClient(styledElement); masker->applyMask(paintInfo.context, boundingBox); } else if (!maskerId.isEmpty()) svgElement->document()->accessSVGExtensions()->addPendingResource(maskerId, styledElement); RenderObject::PaintInfo pi(paintInfo); if (!flow->isRootInlineBox()) pi.rect = (object->localTransform()).inverse().mapRect(pi.rect); float opacity = object->style()->opacity(); if (opacity < 1.0f) { paintInfo.context->clip(enclosingIntRect(boundingBox)); paintInfo.context->beginTransparencyLayer(opacity); } SVGPaintServer* fillPaintServer = KSVGPainterFactory::fillPaintServer(object->style(), object); if (fillPaintServer) { if (fillPaintServer->setup(pi.context, object, ApplyToFillTargetType, true)) { flow->InlineFlowBox::paint(pi, tx, ty); fillPaintServer->teardown(pi.context, object, ApplyToFillTargetType, true); } } SVGPaintServer* strokePaintServer = KSVGPainterFactory::strokePaintServer(object->style(), object); if (strokePaintServer) { if (strokePaintServer->setup(pi.context, object, ApplyToStrokeTargetType, true)) { flow->InlineFlowBox::paint(pi, tx, ty); strokePaintServer->teardown(pi.context, object, ApplyToStrokeTargetType, true); } } #if ENABLE(SVG_EXPERIMENTAL_FEATURES) if (filter) filter->applyFilter(paintInfo.context, boundingBox); #endif if (opacity < 1.0f) paintInfo.context->endTransparencyLayer(); paintInfo.context->restore(); }
void RenderPath::paint(PaintInfo& paintInfo, int, int) { if (paintInfo.context->paintingDisabled() || (paintInfo.phase != PaintPhaseForeground) || style()->visibility() == HIDDEN || m_path.isEmpty()) return; paintInfo.context->save(); paintInfo.context->concatCTM(localTransform()); FloatRect strokeBBox = relativeBBox(true); SVGElement* svgElement = static_cast<SVGElement*>(element()); ASSERT(svgElement && svgElement->document() && svgElement->isStyled()); SVGStyledElement* styledElement = static_cast<SVGStyledElement*>(svgElement); const SVGRenderStyle* svgStyle = style()->svgStyle(); AtomicString filterId(SVGURIReference::getTarget(svgStyle->filter())); AtomicString clipperId(SVGURIReference::getTarget(svgStyle->clipPath())); AtomicString maskerId(SVGURIReference::getTarget(svgStyle->maskElement())); #if ENABLE(SVG_EXPERIMENTAL_FEATURES) SVGResourceFilter* filter = getFilterById(document(), filterId); #endif SVGResourceClipper* clipper = getClipperById(document(), clipperId); SVGResourceMasker* masker = getMaskerById(document(), maskerId); #if ENABLE(SVG_EXPERIMENTAL_FEATURES) if (filter) filter->prepareFilter(paintInfo.context, strokeBBox); else if (!filterId.isEmpty()) svgElement->document()->accessSVGExtensions()->addPendingResource(filterId, styledElement); #endif if (clipper) { clipper->addClient(styledElement); clipper->applyClip(paintInfo.context, strokeBBox); } else if (!clipperId.isEmpty()) svgElement->document()->accessSVGExtensions()->addPendingResource(clipperId, styledElement); if (masker) { masker->addClient(styledElement); masker->applyMask(paintInfo.context, strokeBBox); } else if (!maskerId.isEmpty()) svgElement->document()->accessSVGExtensions()->addPendingResource(maskerId, styledElement); paintInfo.context->beginPath(); SVGPaintServer* fillPaintServer = KSVGPainterFactory::fillPaintServer(style(), this); if (fillPaintServer) { paintInfo.context->addPath(m_path); fillPaintServer->draw(paintInfo.context, this, ApplyToFillTargetType); } SVGPaintServer* strokePaintServer = KSVGPainterFactory::strokePaintServer(style(), this); if (strokePaintServer) { paintInfo.context->addPath(m_path); // path is cleared when filled. strokePaintServer->draw(paintInfo.context, this, ApplyToStrokeTargetType); } if (styledElement->supportsMarkers()) m_markerBounds = drawMarkersIfNeeded(paintInfo.context, paintInfo.rect, m_path); #if ENABLE(SVG_EXPERIMENTAL_FEATURES) // actually apply the filter if (filter) filter->applyFilter(paintInfo.context, strokeBBox); #endif paintInfo.context->restore(); }