void SVGLayoutSupport::applyStrokeStyleToStrokeData(StrokeData& strokeData, const ComputedStyle& style, const LayoutObject& object, float dashScaleFactor) { ASSERT(object.node()); ASSERT(object.node()->isSVGElement()); const SVGComputedStyle& svgStyle = style.svgStyle(); SVGLengthContext lengthContext(toSVGElement(object.node())); strokeData.setThickness(lengthContext.valueForLength(svgStyle.strokeWidth())); strokeData.setLineCap(svgStyle.capStyle()); strokeData.setLineJoin(svgStyle.joinStyle()); strokeData.setMiterLimit(svgStyle.strokeMiterLimit()); DashArray dashArray = resolveSVGDashArray(*svgStyle.strokeDashArray(), style, lengthContext); float dashOffset = lengthContext.valueForLength(svgStyle.strokeDashOffset(), style); // Apply scaling from 'pathLength'. if (dashScaleFactor != 1) { ASSERT(dashScaleFactor >= 0); dashOffset *= dashScaleFactor; for (auto& dashItem : dashArray) dashItem *= dashScaleFactor; } strokeData.setLineDash(dashArray, dashOffset); }
void SVGLayoutSupport::applyStrokeStyleToStrokeData(StrokeData& strokeData, const ComputedStyle& style, const LayoutObject& object) { ASSERT(object.node()); ASSERT(object.node()->isSVGElement()); const SVGComputedStyle& svgStyle = style.svgStyle(); SVGLengthContext lengthContext(toSVGElement(object.node())); strokeData.setThickness(lengthContext.valueForLength(svgStyle.strokeWidth())); strokeData.setLineCap(svgStyle.capStyle()); strokeData.setLineJoin(svgStyle.joinStyle()); strokeData.setMiterLimit(svgStyle.strokeMiterLimit()); DashArray dashArray = resolveSVGDashArray(*svgStyle.strokeDashArray(), style, lengthContext); strokeData.setLineDash(dashArray, lengthContext.valueForLength(svgStyle.strokeDashOffset(), style)); }
FloatRect Path::strokeBoundingRect(const StrokeData& strokeData) const { SkPaint paint; strokeData.setupPaint(&paint); SkPath boundingPath; paint.getFillPath(m_path, &boundingPath); return boundingPath.getBounds(); }
bool Path::strokeContains(const FloatPoint& point, const StrokeData& strokeData) const { SkPaint paint; strokeData.setupPaint(&paint); SkPath strokePath; paint.getFillPath(m_path, &strokePath); return SkPathContainsPoint(strokePath, point, SkPath::kWinding_FillType); }
void CanvasRenderingContext2D::drawFocusRing(const Path& path) { m_usageCounters.numDrawFocusCalls++; if (!drawingCanvas()) return; SkColor color = LayoutTheme::theme().focusRingColor().rgb(); const int focusRingWidth = 5; drawPlatformFocusRing(path.getSkPath(), drawingCanvas(), color, focusRingWidth); // We need to add focusRingWidth to dirtyRect. StrokeData strokeData; strokeData.setThickness(focusRingWidth); SkIRect dirtyRect; if (!computeDirtyRect(path.strokeBoundingRect(strokeData), &dirtyRect)) return; didDraw(dirtyRect); }
// FIXME: this method ignores the CTM and may yield inaccurate results for large // scales. SkPath Path::strokePath(const StrokeData& strokeData) const { SkPaint paint; strokeData.setupPaint(&paint); // Skia stroke resolution scale. This is multiplied by 4 internally // (i.e. 1.0 corresponds to 1/4 pixel res). static const SkScalar kResScale = 0.3f; SkPath strokePath; paint.getFillPath(m_path, &strokePath, nullptr, kResScale); return strokePath; }
void SVGShapePainter::paint(const PaintInfo& paintInfo) { if (paintInfo.phase != PaintPhaseForeground || m_layoutSVGShape.style()->visibility() == HIDDEN || m_layoutSVGShape.isShapeEmpty()) return; FloatRect boundingBox = m_layoutSVGShape.paintInvalidationRectInLocalCoordinates(); if (!paintInfo.cullRect().intersectsCullRect(m_layoutSVGShape.localTransform(), boundingBox)) return; PaintInfo paintInfoBeforeFiltering(paintInfo); // Shapes cannot have children so do not call updateCullRect. TransformRecorder transformRecorder(paintInfoBeforeFiltering.context, m_layoutSVGShape, m_layoutSVGShape.localTransform()); { SVGPaintContext paintContext(m_layoutSVGShape, paintInfoBeforeFiltering); if (paintContext.applyClipMaskAndFilterIfNecessary() && !LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(paintContext.paintInfo().context, m_layoutSVGShape, paintContext.paintInfo().phase, LayoutPoint())) { LayoutObjectDrawingRecorder recorder(paintContext.paintInfo().context, m_layoutSVGShape, paintContext.paintInfo().phase, boundingBox, LayoutPoint()); const SVGComputedStyle& svgStyle = m_layoutSVGShape.style()->svgStyle(); bool shouldAntiAlias = svgStyle.shapeRendering() != SR_CRISPEDGES; for (int i = 0; i < 3; i++) { switch (svgStyle.paintOrderType(i)) { case PT_FILL: { SkPaint fillPaint; if (!SVGPaintContext::paintForLayoutObject(paintContext.paintInfo(), m_layoutSVGShape.styleRef(), m_layoutSVGShape, ApplyToFillMode, fillPaint)) break; fillPaint.setAntiAlias(shouldAntiAlias); fillShape(paintContext.paintInfo().context, fillPaint, fillRuleFromStyle(paintContext.paintInfo(), svgStyle)); break; } case PT_STROKE: if (svgStyle.hasVisibleStroke()) { GraphicsContextStateSaver stateSaver(paintContext.paintInfo().context, false); AffineTransform nonScalingTransform; const AffineTransform* additionalPaintServerTransform = 0; if (m_layoutSVGShape.hasNonScalingStroke()) { nonScalingTransform = m_layoutSVGShape.nonScalingStrokeTransform(); if (!setupNonScalingStrokeContext(nonScalingTransform, stateSaver)) return; // Non-scaling stroke needs to reset the transform back to the host transform. additionalPaintServerTransform = &nonScalingTransform; } SkPaint strokePaint; if (!SVGPaintContext::paintForLayoutObject(paintContext.paintInfo(), m_layoutSVGShape.styleRef(), m_layoutSVGShape, ApplyToStrokeMode, strokePaint, additionalPaintServerTransform)) break; strokePaint.setAntiAlias(shouldAntiAlias); StrokeData strokeData; SVGLayoutSupport::applyStrokeStyleToStrokeData(strokeData, m_layoutSVGShape.styleRef(), m_layoutSVGShape, m_layoutSVGShape.dashScaleFactor()); strokeData.setupPaint(&strokePaint); strokeShape(paintContext.paintInfo().context, strokePaint); } break; case PT_MARKERS: paintMarkers(paintContext.paintInfo(), boundingBox); break; default: ASSERT_NOT_REACHED(); break; } } } } if (m_layoutSVGShape.style()->outlineWidth()) { PaintInfo outlinePaintInfo(paintInfoBeforeFiltering); outlinePaintInfo.phase = PaintPhaseSelfOutlineOnly; ObjectPainter(m_layoutSVGShape).paintOutline(outlinePaintInfo, LayoutPoint(boundingBox.location())); } }
void SVGInlineTextBoxPainter::paintTextWithShadows(const PaintInfo& paintInfo, const ComputedStyle& style, TextRun& textRun, const SVGTextFragment& fragment, int startPosition, int endPosition, LayoutSVGResourceMode resourceMode) { LayoutSVGInlineText& textLayoutObject = toLayoutSVGInlineText(m_svgInlineTextBox.layoutObject()); float scalingFactor = textLayoutObject.scalingFactor(); ASSERT(scalingFactor); const Font& scaledFont = textLayoutObject.scaledFont(); const ShadowList* shadowList = style.textShadow(); GraphicsContext* context = paintInfo.context; // Text shadows are disabled when printing. http://crbug.com/258321 bool hasShadow = shadowList && !context->printing(); FloatPoint textOrigin(fragment.x, fragment.y); FloatSize textSize(fragment.width, fragment.height); AffineTransform paintServerTransform; const AffineTransform* additionalPaintServerTransform = 0; GraphicsContextStateSaver stateSaver(*context, false); if (scalingFactor != 1) { textOrigin.scale(scalingFactor, scalingFactor); textSize.scale(scalingFactor); stateSaver.save(); context->scale(1 / scalingFactor, 1 / scalingFactor); // Adjust the paint-server coordinate space. paintServerTransform.scale(scalingFactor); additionalPaintServerTransform = &paintServerTransform; } SkPaint paint; if (!SVGPaintContext::paintForLayoutObject(paintInfo, style, m_svgInlineTextBox.parent()->layoutObject(), resourceMode, paint, additionalPaintServerTransform)) return; paint.setAntiAlias(true); if (hasShadow) { OwnPtr<DrawLooperBuilder> drawLooperBuilder = shadowList->createDrawLooper(DrawLooperBuilder::ShadowRespectsAlpha, style.visitedDependentColor(CSSPropertyColor)); RefPtr<SkDrawLooper> drawLooper = drawLooperBuilder->detachDrawLooper(); paint.setLooper(drawLooper.get()); } if (resourceMode == ApplyToStrokeMode) { StrokeData strokeData; SVGLayoutSupport::applyStrokeStyleToStrokeData(strokeData, style, m_svgInlineTextBox.parent()->layoutObject()); if (style.svgStyle().vectorEffect() != VE_NON_SCALING_STROKE) strokeData.setThickness(strokeData.thickness() * scalingFactor); strokeData.setupPaint(&paint); } TextRunPaintInfo textRunPaintInfo(textRun); textRunPaintInfo.from = startPosition; textRunPaintInfo.to = endPosition; float baseline = scaledFont.fontMetrics().floatAscent(); textRunPaintInfo.bounds = FloatRect(textOrigin.x(), textOrigin.y() - baseline, textSize.width(), textSize.height()); context->drawText(scaledFont, textRunPaintInfo, textOrigin, paint); }
void SVGInlineTextBoxPainter::paintDecoration(const PaintInfo& paintInfo, TextDecoration decoration, const SVGTextFragment& fragment) { if (m_svgInlineTextBox.layoutObject().style()->textDecorationsInEffect() == TextDecorationNone) return; if (fragment.width <= 0) return; // Find out which style defined the text-decoration, as its fill/stroke properties have to be used for drawing instead of ours. LayoutObject* decorationLayoutObject = findLayoutObjectDefininingTextDecoration(m_svgInlineTextBox.parent()); const ComputedStyle& decorationStyle = decorationLayoutObject->styleRef(); if (decorationStyle.visibility() == HIDDEN) return; float scalingFactor = 1; Font scaledFont; LayoutSVGInlineText::computeNewScaledFontForStyle(decorationLayoutObject, &decorationStyle, scalingFactor, scaledFont); ASSERT(scalingFactor); float thickness = thicknessForDecoration(decoration, scaledFont); if (thickness <= 0) return; float decorationOffset = baselineOffsetForDecoration(decoration, scaledFont.fontMetrics(), thickness); FloatPoint decorationOrigin(fragment.x, fragment.y - decorationOffset / scalingFactor); Path path; path.addRect(FloatRect(decorationOrigin, FloatSize(fragment.width, thickness / scalingFactor))); const SVGComputedStyle& svgDecorationStyle = decorationStyle.svgStyle(); for (int i = 0; i < 3; i++) { switch (svgDecorationStyle.paintOrderType(i)) { case PT_FILL: if (svgDecorationStyle.hasFill()) { SkPaint fillPaint; if (!SVGPaintContext::paintForLayoutObject(paintInfo, decorationStyle, *decorationLayoutObject, ApplyToFillMode, fillPaint)) break; fillPaint.setAntiAlias(true); paintInfo.context->drawPath(path.skPath(), fillPaint); } break; case PT_STROKE: if (svgDecorationStyle.hasVisibleStroke()) { SkPaint strokePaint; if (!SVGPaintContext::paintForLayoutObject(paintInfo, decorationStyle, *decorationLayoutObject, ApplyToStrokeMode, strokePaint)) break; strokePaint.setAntiAlias(true); StrokeData strokeData; SVGLayoutSupport::applyStrokeStyleToStrokeData(strokeData, decorationStyle, *decorationLayoutObject); if (svgDecorationStyle.vectorEffect() == VE_NON_SCALING_STROKE) strokeData.setThickness(strokeData.thickness() / scalingFactor); strokeData.setupPaint(&strokePaint); paintInfo.context->drawPath(path.skPath(), strokePaint); } break; case PT_MARKERS: break; default: ASSERT_NOT_REACHED(); } } }