GlyphOverflow visualOverflowForDecorations(const RenderStyle& lineStyle, const InlineTextBox* inlineTextBox) { ASSERT(!inlineTextBox || inlineTextBox->lineStyle() == lineStyle); TextDecoration decoration = lineStyle.textDecorationsInEffect(); if (decoration == TextDecorationNone) return GlyphOverflow(); float strokeThickness = textDecorationStrokeThickness(lineStyle.fontSize()); float controlPointDistance; float step; float wavyOffset; TextDecorationStyle decorationStyle = lineStyle.textDecorationStyle(); float height = lineStyle.fontCascade().fontMetrics().floatHeight(); GlyphOverflow overflowResult; if (decorationStyle == TextDecorationStyleWavy) { getWavyStrokeParameters(strokeThickness, controlPointDistance, step); wavyOffset = wavyOffsetFromDecoration(); overflowResult.left = strokeThickness; overflowResult.right = strokeThickness; } // These metrics must match where underlines get drawn. if (decoration & TextDecorationUnderline) { // Compensate for the integral ceiling in GraphicsContext::computeLineBoundsAndAntialiasingModeForText() int underlineOffset = 1; underlineOffset += computeUnderlineOffset(lineStyle.textUnderlinePosition(), lineStyle.fontMetrics(), inlineTextBox, strokeThickness); if (decorationStyle == TextDecorationStyleWavy) { extendIntToFloat(overflowResult.bottom, underlineOffset + wavyOffset + controlPointDistance + strokeThickness - height); extendIntToFloat(overflowResult.top, -(underlineOffset + wavyOffset - controlPointDistance - strokeThickness)); } else { extendIntToFloat(overflowResult.bottom, underlineOffset + strokeThickness - height); extendIntToFloat(overflowResult.top, -underlineOffset); } } if (decoration & TextDecorationOverline) { if (decorationStyle == TextDecorationStyleWavy) { extendIntToFloat(overflowResult.bottom, -wavyOffset + controlPointDistance + strokeThickness - height); extendIntToFloat(overflowResult.top, wavyOffset + controlPointDistance + strokeThickness); } else { extendIntToFloat(overflowResult.bottom, strokeThickness - height); // top is untouched } } if (decoration & TextDecorationLineThrough) { float baseline = lineStyle.fontMetrics().floatAscent(); if (decorationStyle == TextDecorationStyleWavy) { extendIntToFloat(overflowResult.bottom, 2 * baseline / 3 + controlPointDistance + strokeThickness - height); extendIntToFloat(overflowResult.top, -(2 * baseline / 3 - controlPointDistance - strokeThickness)); } else { extendIntToFloat(overflowResult.bottom, 2 * baseline / 3 + strokeThickness - height); extendIntToFloat(overflowResult.top, -(2 * baseline / 3)); } } return overflowResult; }
/* * Draw one cubic Bezier curve and repeat the same pattern long the the decoration's axis. * The start point (p1), controlPoint1, controlPoint2 and end point (p2) of the Bezier curve * form a diamond shape: * * step * |-----------| * * controlPoint1 * + * * * . . * . . * . . * (x1, y1) p1 + . + p2 (x2, y2) - <--- Decoration's axis * . . | * . . | * . . | controlPointDistance * | * | * + - * controlPoint2 * * |-----------| * step */ static void strokeWavyTextDecoration(GraphicsContext& context, const FloatPoint& start, const FloatPoint& end, float strokeThickness) { FloatPoint p1 = start; FloatPoint p2 = end; context.adjustLineToPixelBoundaries(p1, p2, strokeThickness, context.strokeStyle()); Path path; path.moveTo(p1); float controlPointDistance; float step; getWavyStrokeParameters(strokeThickness, controlPointDistance, step); bool isVerticalLine = (p1.x() == p2.x()); if (isVerticalLine) { ASSERT(p1.x() == p2.x()); float xAxis = p1.x(); float y1; float y2; if (p1.y() < p2.y()) { y1 = p1.y(); y2 = p2.y(); } else { y1 = p2.y(); y2 = p1.y(); } adjustStepToDecorationLength(step, controlPointDistance, y2 - y1); FloatPoint controlPoint1(xAxis + controlPointDistance, 0); FloatPoint controlPoint2(xAxis - controlPointDistance, 0); for (float y = y1; y + 2 * step <= y2;) { controlPoint1.setY(y + step); controlPoint2.setY(y + step); y += 2 * step; path.addBezierCurveTo(controlPoint1, controlPoint2, FloatPoint(xAxis, y)); } } else { ASSERT(p1.y() == p2.y()); float yAxis = p1.y(); float x1; float x2; if (p1.x() < p2.x()) { x1 = p1.x(); x2 = p2.x(); } else { x1 = p2.x(); x2 = p1.x(); } adjustStepToDecorationLength(step, controlPointDistance, x2 - x1); FloatPoint controlPoint1(0, yAxis + controlPointDistance); FloatPoint controlPoint2(0, yAxis - controlPointDistance); for (float x = x1; x + 2 * step <= x2;) { controlPoint1.setX(x + step); controlPoint2.setX(x + step); x += 2 * step; path.addBezierCurveTo(controlPoint1, controlPoint2, FloatPoint(x, yAxis)); } } context.setShouldAntialias(true); context.strokePath(path); }