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; }
void TextDecorationPainter::paintTextDecoration(const TextRun& textRun, const FloatPoint& textOrigin, const FloatPoint& boxOrigin) { #if !ENABLE(CSS3_TEXT_DECORATION_SKIP_INK) UNUSED_PARAM(textRun); UNUSED_PARAM(textOrigin); #endif ASSERT(m_font); float textDecorationThickness = textDecorationStrokeThickness(m_lineStyle.fontSize()); m_context.setStrokeThickness(textDecorationThickness); FloatPoint localOrigin = boxOrigin; auto paintDecoration = [&](TextDecoration decoration, TextDecorationStyle style, const Color& color, const FloatPoint& start, const FloatPoint& end, int offset) { m_context.setStrokeColor(color); auto strokeStyle = textDecorationStyleToStrokeStyle(style); if (style == TextDecorationStyleWavy) strokeWavyTextDecoration(m_context, start, end, textDecorationThickness); else if (decoration == TextDecorationUnderline || decoration == TextDecorationOverline) { #if ENABLE(CSS3_TEXT_DECORATION_SKIP_INK) if ((m_lineStyle.textDecorationSkip() == TextDecorationSkipInk || m_lineStyle.textDecorationSkip() == TextDecorationSkipAuto) && m_isHorizontal) { if (!m_context.paintingDisabled()) drawSkipInkUnderline(m_context, *m_font, textRun, textOrigin, localOrigin, offset, m_width, m_isPrinting, style == TextDecorationStyleDouble, strokeStyle); } else // FIXME: Need to support text-decoration-skip: none. #endif m_context.drawLineForText(start, m_width, m_isPrinting, style == TextDecorationStyleDouble, strokeStyle); } else { ASSERT(decoration == TextDecorationLineThrough); m_context.drawLineForText(start, m_width, m_isPrinting, style == TextDecorationStyleDouble, strokeStyle); } }; bool linesAreOpaque = !m_isPrinting && (!(m_decoration & TextDecorationUnderline) || m_styles.underlineColor.isOpaque()) && (!(m_decoration & TextDecorationOverline) || m_styles.overlineColor.isOpaque()) && (!(m_decoration & TextDecorationLineThrough) || m_styles.linethroughColor.isOpaque()); int extraOffset = 0; bool clipping = !linesAreOpaque && m_shadow && m_shadow->next(); if (clipping) { FloatRect clipRect(localOrigin, FloatSize(m_width, m_baseline + 2)); for (const ShadowData* shadow = m_shadow; shadow; shadow = shadow->next()) { int shadowExtent = shadow->paintingExtent(); FloatRect shadowRect(localOrigin, FloatSize(m_width, m_baseline + 2)); shadowRect.inflate(shadowExtent); int shadowX = m_isHorizontal ? shadow->x() : shadow->y(); int shadowY = m_isHorizontal ? shadow->y() : -shadow->x(); shadowRect.move(shadowX, shadowY); clipRect.unite(shadowRect); extraOffset = std::max(extraOffset, std::max(0, shadowY) + shadowExtent); } m_context.save(); m_context.clip(clipRect); extraOffset += m_baseline + 2; localOrigin.move(0, extraOffset); } const ShadowData* shadow = m_shadow; do { if (shadow) { if (!shadow->next()) { // The last set of lines paints normally inside the clip. localOrigin.move(0, -extraOffset); extraOffset = 0; } int shadowX = m_isHorizontal ? shadow->x() : shadow->y(); int shadowY = m_isHorizontal ? shadow->y() : -shadow->x(); m_context.setShadow(FloatSize(shadowX, shadowY - extraOffset), shadow->radius(), shadow->color()); shadow = shadow->next(); } // These decorations should match the visual overflows computed in visualOverflowForDecorations() if (m_decoration & TextDecorationUnderline) { const int offset = computeUnderlineOffset(m_lineStyle.textUnderlinePosition(), m_lineStyle.fontMetrics(), m_inlineTextBox, textDecorationThickness); int wavyOffset = m_styles.underlineStyle == TextDecorationStyleWavy ? m_wavyOffset : 0; FloatPoint start = localOrigin + FloatSize(0, offset + wavyOffset); FloatPoint end = localOrigin + FloatSize(m_width, offset + wavyOffset); paintDecoration(TextDecorationUnderline, m_styles.underlineStyle, m_styles.underlineColor, start, end, offset); } if (m_decoration & TextDecorationOverline) { int wavyOffset = m_styles.overlineStyle == TextDecorationStyleWavy ? m_wavyOffset : 0; FloatPoint start = localOrigin - FloatSize(0, wavyOffset); FloatPoint end = localOrigin + FloatSize(m_width, -wavyOffset); paintDecoration(TextDecorationOverline, m_styles.overlineStyle, m_styles.overlineColor, start, end, 0); } if (m_decoration & TextDecorationLineThrough) { FloatPoint start = localOrigin + FloatSize(0, 2 * m_baseline / 3); FloatPoint end = localOrigin + FloatSize(m_width, 2 * m_baseline / 3); paintDecoration(TextDecorationLineThrough, m_styles.linethroughStyle, m_styles.linethroughColor, start, end, 0); } } while (shadow); if (clipping) m_context.restore(); else if (m_shadow) m_context.clearShadow(); }