DashArray SVGLayoutSupport::resolveSVGDashArray(const SVGDashArray& svgDashArray, const ComputedStyle& style, const SVGLengthContext& lengthContext) { DashArray dashArray; for (const Length& dashLength : svgDashArray.vector()) dashArray.append(lengthContext.valueForLength(dashLength, style)); return dashArray; }
void SVGRenderSupport::applyStrokeStyleToStrokeData(StrokeData* strokeData, const RenderStyle* style, const RenderObject* object) { ASSERT(strokeData); ASSERT(style); ASSERT(object); ASSERT(object->node()); ASSERT(object->node()->isSVGElement()); const SVGRenderStyle* svgStyle = style->svgStyle(); ASSERT(svgStyle); SVGLengthContext lengthContext(toSVGElement(object->node())); strokeData->setThickness(svgStyle->strokeWidth()->value(lengthContext)); strokeData->setLineCap(svgStyle->capStyle()); strokeData->setLineJoin(svgStyle->joinStyle()); strokeData->setMiterLimit(svgStyle->strokeMiterLimit()); RefPtr<SVGLengthList> dashes = svgStyle->strokeDashArray(); if (dashes->isEmpty()) return; DashArray dashArray; size_t length = dashes->length(); for (size_t i = 0; i < length; ++i) dashArray.append(dashes->at(i)->value(lengthContext)); strokeData->setLineDash(dashArray, svgStyle->strokeDashOffset()->value(lengthContext)); }
void GraphicsContext::drawLinesForText(const FloatPoint& point, const DashArray& widths, bool printing, bool doubleUnderlines) { if (paintingDisabled()) return; if (widths.size() <= 0) return; Color localStrokeColor(strokeColor()); bool shouldAntialiasLine; FloatRect bounds = computeLineBoundsAndAntialiasingModeForText(point, widths.last(), printing, shouldAntialiasLine, localStrokeColor); Vector<FloatRect, 4> dashBounds; ASSERT(!(widths.size() % 2)); dashBounds.reserveInitialCapacity(dashBounds.size() / 2); for (size_t i = 0; i < widths.size(); i += 2) dashBounds.append(FloatRect(FloatPoint(bounds.x() + widths[i], bounds.y()), FloatSize(widths[i+1] - widths[i], bounds.height()))); if (doubleUnderlines) { // The space between double underlines is equal to the height of the underline for (size_t i = 0; i < widths.size(); i += 2) dashBounds.append(FloatRect(FloatPoint(bounds.x() + widths[i], bounds.y() + 2 * bounds.height()), FloatSize(widths[i+1] - widths[i], bounds.height()))); } cairo_t* cr = platformContext()->cr(); cairo_save(cr); for (auto& dash : dashBounds) fillRectWithColor(cr, dash, localStrokeColor); cairo_restore(cr); }
void SVGRenderSupport::applyStrokeStyleToContext(GraphicsContext* context, const RenderStyle* style, const RenderObject* object) { ASSERT(context); ASSERT(style); ASSERT(object); ASSERT(object->node()); ASSERT(object->node()->isSVGElement()); const SVGRenderStyle* svgStyle = style->svgStyle(); ASSERT(svgStyle); SVGLengthContext lengthContext(static_cast<SVGElement*>(object->node())); context->setStrokeThickness(svgStyle->strokeWidth().value(lengthContext)); context->setLineCap(svgStyle->capStyle()); context->setLineJoin(svgStyle->joinStyle()); if (svgStyle->joinStyle() == MiterJoin) context->setMiterLimit(svgStyle->strokeMiterLimit()); const Vector<SVGLength>& dashes = svgStyle->strokeDashArray(); if (dashes.isEmpty()) context->setStrokeStyle(SolidStroke); else { DashArray dashArray; const Vector<SVGLength>::const_iterator end = dashes.end(); for (Vector<SVGLength>::const_iterator it = dashes.begin(); it != end; ++it) dashArray.append((*it).value(lengthContext)); context->setLineDash(dashArray, svgStyle->strokeDashOffset().value(lengthContext)); } }
void SVGRenderSupport::applyStrokeStyleToContext(GraphicsContext* context, const RenderStyle& style, const RenderElement& renderer) { ASSERT(context); ASSERT(renderer.element()); ASSERT(renderer.element()->isSVGElement()); const SVGRenderStyle& svgStyle = style.svgStyle(); SVGLengthContext lengthContext(toSVGElement(renderer.element())); context->setStrokeThickness(svgStyle.strokeWidth().value(lengthContext)); context->setLineCap(svgStyle.capStyle()); context->setLineJoin(svgStyle.joinStyle()); if (svgStyle.joinStyle() == MiterJoin) context->setMiterLimit(svgStyle.strokeMiterLimit()); const Vector<SVGLength>& dashes = svgStyle.strokeDashArray(); if (dashes.isEmpty()) context->setStrokeStyle(SolidStroke); else { DashArray dashArray; dashArray.reserveInitialCapacity(dashes.size()); for (unsigned i = 0, size = dashes.size(); i < size; ++i) dashArray.uncheckedAppend(dashes[i].value(lengthContext)); context->setLineDash(dashArray, svgStyle.strokeDashOffset().value(lengthContext)); } }
void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset) { if (paintingDisabled()) return; platformContext()->setLineDash(dashes.data(), dashes.size(), dashOffset); }
void SVGRenderSupport::applyStrokeStyleToContext(GraphicsContext* context, const RenderStyle* style, const RenderObject* object) { ASSERT(context); ASSERT(style); ASSERT(object); ASSERT(object->node()); ASSERT(object->node()->isSVGElement()); const SVGRenderStyle* svgStyle = style->svgStyle(); ASSERT(svgStyle); SVGLengthContext lengthContext(toSVGElement(object->node())); context->setStrokeThickness(svgStyle->strokeWidth()->value(lengthContext)); context->setLineCap(svgStyle->capStyle()); context->setLineJoin(svgStyle->joinStyle()); context->setMiterLimit(svgStyle->strokeMiterLimit()); RefPtr<SVGLengthList> dashes = svgStyle->strokeDashArray(); if (dashes->isEmpty()) return; DashArray dashArray; SVGLengthList::ConstIterator it = dashes->begin(); SVGLengthList::ConstIterator itEnd = dashes->end(); for (; it != itEnd; ++it) dashArray.append(it->value(lengthContext)); context->setLineDash(dashArray, svgStyle->strokeDashOffset()->value(lengthContext)); }
void applyStrokeStyle() { if (strokeStyle == DottedStroke) { VGfloat vgFloatArray[2] = { 1.0, 1.0 }; vgSetfv(VG_STROKE_DASH_PATTERN, 2, vgFloatArray); vgSetf(VG_STROKE_DASH_PHASE, 0.0); } else if (strokeStyle == DashedStroke) { if (!strokeDashArray.size()) { VGfloat vgFloatArray[2] = { 4.0, 3.0 }; vgSetfv(VG_STROKE_DASH_PATTERN, 2, vgFloatArray); } else { Vector<VGfloat> vgFloatArray(strokeDashArray.size()); for (int i = 0; i < strokeDashArray.size(); ++i) vgFloatArray[i] = strokeDashArray[i]; vgSetfv(VG_STROKE_DASH_PATTERN, vgFloatArray.size(), vgFloatArray.data()); } vgSetf(VG_STROKE_DASH_PHASE, strokeDashOffset); } else { vgSetfv(VG_STROKE_DASH_PATTERN, 0, 0); vgSetf(VG_STROKE_DASH_PHASE, 0.0); } ASSERT_VG_NO_ERROR(); }
void SVGRenderSupport::applyStrokeStyleToStrokeData(StrokeData* strokeData, const RenderStyle* style, const RenderObject* object) { ASSERT(strokeData); ASSERT(style); ASSERT(object); ASSERT(object->node()); ASSERT(object->node()->isSVGElement()); const SVGRenderStyle* svgStyle = style->svgStyle(); ASSERT(svgStyle); SVGLengthContext lengthContext(toSVGElement(object->node())); strokeData->setThickness(svgStyle->strokeWidth().value(lengthContext)); strokeData->setLineCap(svgStyle->capStyle()); strokeData->setLineJoin(svgStyle->joinStyle()); strokeData->setMiterLimit(svgStyle->strokeMiterLimit()); const Vector<SVGLength>& dashes = svgStyle->strokeDashArray(); if (dashes.isEmpty()) return; DashArray dashArray; const Vector<SVGLength>::const_iterator end = dashes.end(); for (Vector<SVGLength>::const_iterator it = dashes.begin(); it != end; ++it) dashArray.append((*it).value(lengthContext)); strokeData->setLineDash(dashArray, svgStyle->strokeDashOffset().value(lengthContext)); }
void GraphicsContext::drawLineForText(const FloatPoint& origin, float width, bool printing, bool doubleUnderlines) { DashArray widths; widths.append(width); widths.append(0); drawLinesForText(origin, widths, printing, doubleUnderlines); }
static void writeStyle(TextStream& ts, const RenderObject& object) { const RenderStyle* style = object.style(); const SVGRenderStyle* svgStyle = style->svgStyle(); if (!object.localTransform().isIdentity()) writeNameValuePair(ts, "transform", object.localTransform()); writeIfNotDefault(ts, "image rendering", style->imageRendering(), RenderStyle::initialImageRendering()); writeIfNotDefault(ts, "opacity", style->opacity(), RenderStyle::initialOpacity()); if (object.isSVGShape()) { const RenderSVGShape& shape = static_cast<const RenderSVGShape&>(object); ASSERT(shape.node()); ASSERT(shape.node()->isSVGElement()); Color fallbackColor; if (RenderSVGResource* strokePaintingResource = RenderSVGResource::strokePaintingResource(const_cast<RenderSVGShape*>(&shape), shape.style(), fallbackColor)) { TextStreamSeparator s(" "); ts << " [stroke={" << s; writeSVGPaintingResource(ts, strokePaintingResource); SVGLengthContext lengthContext(toSVGElement(shape.node())); double dashOffset = svgStyle->strokeDashOffset().value(lengthContext); double strokeWidth = svgStyle->strokeWidth().value(lengthContext); const Vector<SVGLength>& dashes = svgStyle->strokeDashArray(); DashArray dashArray; const Vector<SVGLength>::const_iterator end = dashes.end(); for (Vector<SVGLength>::const_iterator it = dashes.begin(); it != end; ++it) dashArray.append((*it).value(lengthContext)); writeIfNotDefault(ts, "opacity", svgStyle->strokeOpacity(), 1.0f); writeIfNotDefault(ts, "stroke width", strokeWidth, 1.0); writeIfNotDefault(ts, "miter limit", svgStyle->strokeMiterLimit(), 4.0f); writeIfNotDefault(ts, "line cap", svgStyle->capStyle(), ButtCap); writeIfNotDefault(ts, "line join", svgStyle->joinStyle(), MiterJoin); writeIfNotDefault(ts, "dash offset", dashOffset, 0.0); if (!dashArray.isEmpty()) writeNameValuePair(ts, "dash array", dashArray); ts << "}]"; } if (RenderSVGResource* fillPaintingResource = RenderSVGResource::fillPaintingResource(const_cast<RenderSVGShape*>(&shape), shape.style(), fallbackColor)) { TextStreamSeparator s(" "); ts << " [fill={" << s; writeSVGPaintingResource(ts, fillPaintingResource); writeIfNotDefault(ts, "opacity", svgStyle->fillOpacity(), 1.0f); writeIfNotDefault(ts, "fill rule", svgStyle->fillRule(), RULE_NONZERO); ts << "}]"; } writeIfNotDefault(ts, "clip rule", svgStyle->clipRule(), RULE_NONZERO); } writeIfNotEmpty(ts, "start marker", svgStyle->markerStartResource()); writeIfNotEmpty(ts, "middle marker", svgStyle->markerMidResource()); writeIfNotEmpty(ts, "end marker", svgStyle->markerEndResource()); }
static void drawSkipInkUnderline(GraphicsContext& context, const FontCascade& font, const TextRun& textRun, const FloatPoint& textOrigin, const FloatPoint& localOrigin, float underlineOffset, float width, bool isPrinting, bool doubleLines, StrokeStyle strokeStyle) { FloatPoint adjustedLocalOrigin = localOrigin; adjustedLocalOrigin.move(0, underlineOffset); FloatRect underlineBoundingBox = context.computeUnderlineBoundsForText(adjustedLocalOrigin, width, isPrinting); DashArray intersections = font.dashesForIntersectionsWithRect(textRun, textOrigin, underlineBoundingBox); DashArray a = translateIntersectionPointsToSkipInkBoundaries(intersections, underlineBoundingBox.height(), width); ASSERT(!(a.size() % 2)); context.drawLinesForText(adjustedLocalOrigin, a, isPrinting, doubleLines, strokeStyle); }
DashArray FontCascade::dashesForIntersectionsWithRect(const TextRun& run, const FloatPoint& textOrigin, const FloatRect& lineExtents) const { if (isLoadingCustomFonts()) return DashArray(); GlyphBuffer glyphBuffer; glyphBuffer.saveOffsetsInString(); float deltaX; if (codePath(run) != FontCascade::Complex) deltaX = getGlyphsAndAdvancesForSimpleText(run, 0, run.length(), glyphBuffer); else deltaX = getGlyphsAndAdvancesForComplexText(run, 0, run.length(), glyphBuffer); if (!glyphBuffer.size()) return DashArray(); // FIXME: Handle SVG + non-SVG interleaved runs. https://bugs.webkit.org/show_bug.cgi?id=133778 FloatPoint origin = FloatPoint(textOrigin.x() + deltaX, textOrigin.y()); std::unique_ptr<GlyphToPathTranslator> translator = std::make_unique<CairoGlyphToPathTranslator>(run, glyphBuffer, origin); DashArray result; for (int index = 0; translator->containsMorePaths(); ++index, translator->advance()) { float centerOfLine = lineExtents.y() + (lineExtents.height() / 2); GlyphIterationState info = GlyphIterationState(FloatPoint(), FloatPoint(), centerOfLine, lineExtents.x() + lineExtents.width(), lineExtents.x()); const Font* localFontData = glyphBuffer.fontAt(index); if (!localFontData) { // The advances will get all messed up if we do anything other than bail here. result.clear(); break; } switch (translator->underlineType()) { case GlyphToPathTranslator::GlyphUnderlineType::SkipDescenders: { Path path = translator->path(); path.apply([&info](const PathElement& pathElement) { findPathIntersections(info, pathElement); }); if (info.minX < info.maxX) { result.append(info.minX - lineExtents.x()); result.append(info.maxX - lineExtents.x()); } break; } case GlyphToPathTranslator::GlyphUnderlineType::SkipGlyph: { std::pair<float, float> extents = translator->extents(); result.append(extents.first - lineExtents.x()); result.append(extents.second - lineExtents.x()); break; } case GlyphToPathTranslator::GlyphUnderlineType::DrawOverGlyph: // Nothing to do break; } } return result; }
static void writeStyle(TextStream& ts, const LayoutObject& object) { const ComputedStyle& style = object.styleRef(); const SVGComputedStyle& svgStyle = style.svgStyle(); if (!object.localTransform().isIdentity()) writeNameValuePair(ts, "transform", object.localTransform()); writeIfNotDefault(ts, "image rendering", style.imageRendering(), ComputedStyle::initialImageRendering()); writeIfNotDefault(ts, "opacity", style.opacity(), ComputedStyle::initialOpacity()); if (object.isSVGShape()) { const LayoutSVGShape& shape = static_cast<const LayoutSVGShape&>(object); ASSERT(shape.element()); SVGPaintDescription strokePaintDescription = LayoutSVGResourcePaintServer::requestPaintDescription(shape, shape.styleRef(), ApplyToStrokeMode); if (strokePaintDescription.isValid) { TextStreamSeparator s(" "); ts << " [stroke={" << s; writeSVGPaintingResource(ts, strokePaintDescription); SVGLengthContext lengthContext(shape.element()); double dashOffset = lengthContext.valueForLength(svgStyle.strokeDashOffset(), style); double strokeWidth = lengthContext.valueForLength(svgStyle.strokeWidth()); DashArray dashArray = SVGLayoutSupport::resolveSVGDashArray(*svgStyle.strokeDashArray(), style, lengthContext); writeIfNotDefault(ts, "opacity", svgStyle.strokeOpacity(), 1.0f); writeIfNotDefault(ts, "stroke width", strokeWidth, 1.0); writeIfNotDefault(ts, "miter limit", svgStyle.strokeMiterLimit(), 4.0f); writeIfNotDefault(ts, "line cap", svgStyle.capStyle(), ButtCap); writeIfNotDefault(ts, "line join", svgStyle.joinStyle(), MiterJoin); writeIfNotDefault(ts, "dash offset", dashOffset, 0.0); if (!dashArray.isEmpty()) writeNameValuePair(ts, "dash array", dashArray); ts << "}]"; } SVGPaintDescription fillPaintDescription = LayoutSVGResourcePaintServer::requestPaintDescription(shape, shape.styleRef(), ApplyToFillMode); if (fillPaintDescription.isValid) { TextStreamSeparator s(" "); ts << " [fill={" << s; writeSVGPaintingResource(ts, fillPaintDescription); writeIfNotDefault(ts, "opacity", svgStyle.fillOpacity(), 1.0f); writeIfNotDefault(ts, "fill rule", svgStyle.fillRule(), RULE_NONZERO); ts << "}]"; } writeIfNotDefault(ts, "clip rule", svgStyle.clipRule(), RULE_NONZERO); } writeIfNotEmpty(ts, "start marker", svgStyle.markerStartResource()); writeIfNotEmpty(ts, "middle marker", svgStyle.markerMidResource()); writeIfNotEmpty(ts, "end marker", svgStyle.markerEndResource()); }
void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset) { if (paintingDisabled()) return; // FIXME: This is lifted directly off SkiaSupport, lines 49-74 // so it is not guaranteed to work correctly. size_t dashLength = dashes.size(); if (!dashLength) { // If no dash is set, revert to solid stroke // FIXME: do we need to set NoStroke in some cases? platformContext()->setStrokeStyle(SolidStroke); platformContext()->setDashPathEffect(0); return; } size_t count = !(dashLength % 2) ? dashLength : dashLength * 2; SkScalar* intervals = new SkScalar[count]; for (unsigned int i = 0; i < count; i++) intervals[i] = dashes[i % dashLength]; platformContext()->setDashPathEffect(new SkDashPathEffect(intervals, count, dashOffset)); delete[] intervals; }
DashArray dashArrayFromRenderingStyle(const RenderStyle* style, RenderStyle* rootStyle) { DashArray array; CSSValueList* dashes = style->svgStyle()->strokeDashArray(); if (dashes) { CSSPrimitiveValue* dash = 0; unsigned long len = dashes->length(); for (unsigned long i = 0; i < len; i++) { dash = static_cast<CSSPrimitiveValue*>(dashes->itemWithoutBoundsCheck(i)); if (!dash) continue; array.append((float) dash->computeLengthFloat(const_cast<RenderStyle*>(style), rootStyle)); } } return array; }
static DashArray translateIntersectionPointsToSkipInkBoundaries(const DashArray& intersections, float dilationAmount, float totalWidth) { ASSERT(!(intersections.size() % 2)); // Step 1: Make pairs so we can sort based on range starting-point. We dilate the ranges in this step as well. Vector<std::pair<float, float>> tuples; for (auto i = intersections.begin(); i != intersections.end(); i++, i++) tuples.append(std::make_pair(*i - dilationAmount, *(i + 1) + dilationAmount)); std::sort(tuples.begin(), tuples.end(), &compareTuples); // Step 2: Deal with intersecting ranges. Vector<std::pair<float, float>> intermediateTuples; if (tuples.size() >= 2) { intermediateTuples.append(*tuples.begin()); for (auto i = tuples.begin() + 1; i != tuples.end(); i++) { float& firstEnd = intermediateTuples.last().second; float secondStart = i->first; float secondEnd = i->second; if (secondStart <= firstEnd && secondEnd <= firstEnd) { // Ignore this range completely } else if (secondStart <= firstEnd) firstEnd = secondEnd; else intermediateTuples.append(*i); } } else intermediateTuples = tuples; // Step 3: Output the space between the ranges, but only if the space warrants an underline. float previous = 0; DashArray result; for (const auto& tuple : intermediateTuples) { if (tuple.first - previous > dilationAmount) { result.append(previous); result.append(tuple.first); } previous = tuple.second; } if (totalWidth - previous > dilationAmount) { result.append(previous); result.append(totalWidth); } return result; }
void PlatformGraphicsContext::setLineDash(const DashArray& dashes, float dashOffset) { size_t dashLength = dashes.size(); if (!dashLength) return; size_t count = !(dashLength % 2) ? dashLength : dashLength * 2; SkScalar* intervals = new SkScalar[count]; for (unsigned int i = 0; i < count; i++) intervals[i] = SkFloatToScalar(dashes[i % dashLength]); SkPathEffect **effectPtr = &m_state->pathEffect; SkSafeUnref(*effectPtr); *effectPtr = new SkDashPathEffect(intervals, count, SkFloatToScalar(dashOffset)); delete[] intervals; }
void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset) { QPainter* p = m_data->p(); QPen pen = p->pen(); unsigned dashLength = dashes.size(); if (dashLength) { QVector<qreal> pattern; unsigned count = dashLength; if (dashLength % 2) count *= 2; float penWidth = narrowPrecisionToFloat(double(pen.widthF())); for (unsigned i = 0; i < count; i++) pattern.append(dashes[i % dashLength] / penWidth); pen.setDashPattern(pattern); pen.setDashOffset(dashOffset); } p->setPen(pen); }
void StrokeData::setLineDash(const DashArray& dashes, float dashOffset) { // FIXME: This is lifted directly off SkiaSupport, lines 49-74 // so it is not guaranteed to work correctly. size_t dashLength = dashes.size(); if (!dashLength) { // If no dash is set, revert to solid stroke // FIXME: do we need to set NoStroke in some cases? m_style = SolidStroke; m_dash.clear(); return; } size_t count = !(dashLength % 2) ? dashLength : dashLength * 2; OwnArrayPtr<SkScalar> intervals = adoptArrayPtr(new SkScalar[count]); for (unsigned i = 0; i < count; i++) intervals[i] = dashes[i % dashLength]; m_dash = adoptRef(new SkDashPathEffect(intervals.get(), count, dashOffset)); }
void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset) { CGContextSetLineDash(platformContext(), dashOffset, dashes.data(), dashes.size()); }
void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset) { cairo_set_dash(platformContext()->cr(), dashes.data(), dashes.size(), dashOffset); }