diff options
Diffstat (limited to 'Source/WebCore/rendering/TextPainter.cpp')
-rw-r--r-- | Source/WebCore/rendering/TextPainter.cpp | 254 |
1 files changed, 122 insertions, 132 deletions
diff --git a/Source/WebCore/rendering/TextPainter.cpp b/Source/WebCore/rendering/TextPainter.cpp index d5bbdb92d..eeb0027e2 100644 --- a/Source/WebCore/rendering/TextPainter.cpp +++ b/Source/WebCore/rendering/TextPainter.cpp @@ -26,161 +26,151 @@ #include "GraphicsContext.h" #include "InlineTextBox.h" #include "RenderCombineText.h" -#include "TextPaintStyle.h" +#include <wtf/NeverDestroyed.h> namespace WebCore { -TextPainter::TextPainter(GraphicsContext& context, bool paintSelectedTextOnly, bool paintSelectedTextSeparately, const Font& font, - int startPositionInTextRun, int endPositionInTextBoxString, int length, const AtomicString& emphasisMark, RenderCombineText* combinedText, TextRun& textRun, - FloatRect& boxRect, FloatPoint& textOrigin, int emphasisMarkOffset, const ShadowData* textShadow, const ShadowData* selectionShadow, - bool textBoxIsHorizontal, TextPaintStyle& textPaintStyle, TextPaintStyle& selectionPaintStyle) - : m_paintSelectedTextOnly(paintSelectedTextOnly) - , m_paintSelectedTextSeparately(paintSelectedTextSeparately) - , m_font(font) - , m_startPositionInTextRun(startPositionInTextRun) - , m_endPositionInTextRun(endPositionInTextBoxString) - , m_length(length) - , m_emphasisMark(emphasisMark) - , m_combinedText(combinedText) - , m_textRun(textRun) - , m_boxRect(boxRect) - , m_textOrigin(textOrigin) - , m_emphasisMarkOffset(emphasisMarkOffset) - , m_textBoxIsHorizontal(textBoxIsHorizontal) - , m_savedDrawingStateForMask(&context, &textPaintStyle, &selectionPaintStyle, textShadow, selectionShadow) +ShadowApplier::ShadowApplier(GraphicsContext& context, const ShadowData* shadow, const FloatRect& textRect, bool lastShadowIterationShouldDrawText, bool opaque, FontOrientation orientation) + : m_context(context) + , m_shadow(shadow) + , m_onlyDrawsShadow(!isLastShadowIteration() || !lastShadowIterationShouldDrawText) + , m_avoidDrawingShadow(shadowIsCompletelyCoveredByText(opaque)) + , m_nothingToDraw(shadow && m_avoidDrawingShadow && m_onlyDrawsShadow) + , m_didSaveContext(false) { + if (!shadow || m_nothingToDraw) { + m_shadow = nullptr; + return; + } + + int shadowX = orientation == Horizontal ? shadow->x() : shadow->y(); + int shadowY = orientation == Horizontal ? shadow->y() : -shadow->x(); + FloatSize shadowOffset(shadowX, shadowY); + int shadowRadius = shadow->radius(); + const Color& shadowColor = shadow->color(); + + // When drawing shadows, we usually clip the context to the area the shadow will reside, and then + // draw the text itself outside the clipped area (so only the shadow shows up). However, we can + // often draw the *last* shadow and the text itself in a single call. + if (m_onlyDrawsShadow) { + FloatRect shadowRect(textRect); + shadowRect.inflate(shadow->paintingExtent() + 3 * textRect.height()); + shadowRect.move(shadowOffset); + context.save(); + context.clip(shadowRect); + + m_didSaveContext = true; + m_extraOffset = FloatSize(0, 2 * shadowRect.height() + std::max(0.0f, shadowOffset.height()) + shadowRadius); + shadowOffset -= m_extraOffset; + } + + if (!m_avoidDrawingShadow) + context.setShadow(shadowOffset, shadowRadius, shadowColor); } -static void drawTextOrEmphasisMarks(GraphicsContext& context, const Font& font, const TextRun& textRun, const AtomicString& emphasisMark, - int emphasisMarkOffset, const FloatPoint& point, const int from, const int to) +ShadowApplier::~ShadowApplier() { - if (emphasisMark.isEmpty()) - context.drawText(font, textRun, point, from, to); - else - context.drawEmphasisMarks(font, textRun, emphasisMark, point + IntSize(0, emphasisMarkOffset), from, to); + if (!m_shadow) + return; + if (m_onlyDrawsShadow) + m_context.restore(); + else if (!m_avoidDrawingShadow) + m_context.clearShadow(); } -static void paintTextWithShadows(GraphicsContext* context, const Font& font, const TextRun& textRun, const AtomicString& emphasisMark, - int emphasisMarkOffset, int startOffset, int endOffset, int truncationPoint, const FloatPoint& textOrigin, const FloatRect& boxRect, - const ShadowData* shadow, bool stroked, bool horizontal) +TextPainter::TextPainter(GraphicsContext& context) + : m_context(context) { - Color fillColor = context->fillColor(); - ColorSpace fillColorSpace = context->fillColorSpace(); - bool opaque = !fillColor.hasAlpha(); - if (!opaque) - context->setFillColor(Color::black, fillColorSpace); - - do { - IntSize extraOffset; - if (shadow) - extraOffset = roundedIntSize(InlineTextBox::applyShadowToGraphicsContext(context, shadow, boxRect, stroked, opaque, horizontal)); - else if (!opaque) - context->setFillColor(fillColor, fillColorSpace); - - if (startOffset <= endOffset) - drawTextOrEmphasisMarks(*context, font, textRun, emphasisMark, emphasisMarkOffset, textOrigin + extraOffset, startOffset, endOffset); - else { - if (endOffset > 0) - drawTextOrEmphasisMarks(*context, font, textRun, emphasisMark, emphasisMarkOffset, textOrigin + extraOffset, 0, endOffset); - if (startOffset < truncationPoint) - drawTextOrEmphasisMarks(*context, font, textRun, emphasisMark, emphasisMarkOffset, textOrigin + extraOffset, startOffset, truncationPoint); - } - - if (!shadow) - break; - - if (shadow->next() || stroked || !opaque) - context->restore(); - else - context->clearShadow(); - - shadow = shadow->next(); - } while (shadow || stroked || !opaque); } -void TextPainter::paintText() +void TextPainter::drawTextOrEmphasisMarks(const FontCascade& font, const TextRun& textRun, const AtomicString& emphasisMark, + float emphasisMarkOffset, const FloatPoint& textOrigin, unsigned startOffset, unsigned endOffset) { - ASSERT(m_savedDrawingStateForMask.m_textPaintStyle); - ASSERT(m_savedDrawingStateForMask.m_selectionPaintStyle); - - FloatPoint boxOrigin = m_boxRect.location(); + ASSERT(startOffset < endOffset); + if (emphasisMark.isEmpty()) + m_context.drawText(font, textRun, textOrigin, startOffset, endOffset); + else + m_context.drawEmphasisMarks(font, textRun, emphasisMark, textOrigin + FloatSize(0, emphasisMarkOffset), startOffset, endOffset); +} - if (!m_paintSelectedTextOnly) { - // For stroked painting, we have to change the text drawing mode. It's probably dangerous to leave that mutated as a side - // effect, so only when we know we're stroking, do a save/restore. - GraphicsContextStateSaver stateSaver(*m_savedDrawingStateForMask.m_context, m_savedDrawingStateForMask.m_textPaintStyle->strokeWidth > 0); - - updateGraphicsContext(*m_savedDrawingStateForMask.m_context, *m_savedDrawingStateForMask.m_textPaintStyle); - if (!m_paintSelectedTextSeparately || m_endPositionInTextRun <= m_startPositionInTextRun) { - // FIXME: Truncate right-to-left text correctly. - paintTextWithShadows(m_savedDrawingStateForMask.m_context, m_font, m_textRun, nullAtom, 0, 0, m_length, m_length, m_textOrigin, m_boxRect, m_savedDrawingStateForMask.m_textShadow, m_savedDrawingStateForMask.m_textPaintStyle->strokeWidth > 0, m_textBoxIsHorizontal); - } else - paintTextWithShadows(m_savedDrawingStateForMask.m_context, m_font, m_textRun, nullAtom, 0, m_endPositionInTextRun, m_startPositionInTextRun, m_length, m_textOrigin, m_boxRect, m_savedDrawingStateForMask.m_textShadow, m_savedDrawingStateForMask.m_textPaintStyle->strokeWidth > 0, m_textBoxIsHorizontal); - - if (!m_emphasisMark.isEmpty()) { - updateGraphicsContext(*m_savedDrawingStateForMask.m_context, *m_savedDrawingStateForMask.m_textPaintStyle, UseEmphasisMarkColor); - - DEFINE_STATIC_LOCAL(TextRun, objectReplacementCharacterTextRun, (&objectReplacementCharacter, 1)); - TextRun& emphasisMarkTextRun = m_combinedText ? objectReplacementCharacterTextRun : m_textRun; - FloatPoint emphasisMarkTextOrigin = m_combinedText ? FloatPoint(boxOrigin.x() + m_boxRect.width() / 2, boxOrigin.y() + m_font.fontMetrics().ascent()) : m_textOrigin; - if (m_combinedText) - m_savedDrawingStateForMask.m_context->concatCTM(rotation(m_boxRect, Clockwise)); - - if (!m_paintSelectedTextSeparately || m_endPositionInTextRun <= m_startPositionInTextRun) { - // FIXME: Truncate right-to-left text correctly. - paintTextWithShadows(m_savedDrawingStateForMask.m_context, m_combinedText ? m_combinedText->originalFont() : m_font, emphasisMarkTextRun, m_emphasisMark, m_emphasisMarkOffset, 0, m_length, m_length, emphasisMarkTextOrigin, m_boxRect, m_savedDrawingStateForMask.m_textShadow, m_savedDrawingStateForMask.m_textPaintStyle->strokeWidth > 0, m_textBoxIsHorizontal); - } else - paintTextWithShadows(m_savedDrawingStateForMask.m_context, m_combinedText ? m_combinedText->originalFont() : m_font, emphasisMarkTextRun, m_emphasisMark, m_emphasisMarkOffset, m_endPositionInTextRun, m_startPositionInTextRun, m_length, emphasisMarkTextOrigin, m_boxRect, m_savedDrawingStateForMask.m_textShadow, m_savedDrawingStateForMask.m_textPaintStyle->strokeWidth > 0, m_textBoxIsHorizontal); - - if (m_combinedText) - m_savedDrawingStateForMask.m_context->concatCTM(rotation(m_boxRect, Counterclockwise)); - } +void TextPainter::paintTextWithShadows(const ShadowData* shadow, const FontCascade& font, const TextRun& textRun, const FloatRect& boxRect, const FloatPoint& textOrigin, + unsigned startOffset, unsigned endOffset, const AtomicString& emphasisMark, float emphasisMarkOffset, bool stroked) +{ + if (!shadow) { + drawTextOrEmphasisMarks(font, textRun, emphasisMark, emphasisMarkOffset, textOrigin, startOffset, endOffset); + return; } - if ((m_paintSelectedTextOnly || m_paintSelectedTextSeparately) && m_startPositionInTextRun < m_endPositionInTextRun) { - // paint only the text that is selected - GraphicsContextStateSaver stateSaver(*m_savedDrawingStateForMask.m_context, m_savedDrawingStateForMask.m_selectionPaintStyle->strokeWidth > 0); - - updateGraphicsContext(*m_savedDrawingStateForMask.m_context, *m_savedDrawingStateForMask.m_selectionPaintStyle); - paintTextWithShadows(m_savedDrawingStateForMask.m_context, m_font, m_textRun, nullAtom, 0, m_startPositionInTextRun, m_endPositionInTextRun, m_length, m_textOrigin, m_boxRect, m_savedDrawingStateForMask.m_selectionShadow, m_savedDrawingStateForMask.m_selectionPaintStyle->strokeWidth > 0, m_textBoxIsHorizontal); - if (!m_emphasisMark.isEmpty()) { - updateGraphicsContext(*m_savedDrawingStateForMask.m_context, *m_savedDrawingStateForMask.m_selectionPaintStyle, UseEmphasisMarkColor); - - DEFINE_STATIC_LOCAL(TextRun, objectReplacementCharacterTextRun, (&objectReplacementCharacter, 1)); - TextRun& emphasisMarkTextRun = m_combinedText ? objectReplacementCharacterTextRun : m_textRun; - FloatPoint emphasisMarkTextOrigin = m_combinedText ? FloatPoint(boxOrigin.x() + m_boxRect.width() / 2, boxOrigin.y() + m_font.fontMetrics().ascent()) : m_textOrigin; - if (m_combinedText) - m_savedDrawingStateForMask.m_context->concatCTM(rotation(m_boxRect, Clockwise)); - - paintTextWithShadows(m_savedDrawingStateForMask.m_context, m_combinedText ? m_combinedText->originalFont() : m_font, emphasisMarkTextRun, m_emphasisMark, m_emphasisMarkOffset, m_startPositionInTextRun, m_endPositionInTextRun, m_length, emphasisMarkTextOrigin, m_boxRect, m_savedDrawingStateForMask.m_selectionShadow, m_savedDrawingStateForMask.m_selectionPaintStyle->strokeWidth > 0, m_textBoxIsHorizontal); + Color fillColor = m_context.fillColor(); + bool opaque = fillColor.isOpaque(); + bool lastShadowIterationShouldDrawText = !stroked && opaque; + if (!opaque) + m_context.setFillColor(Color::black); + while (shadow) { + ShadowApplier shadowApplier(m_context, shadow, boxRect, lastShadowIterationShouldDrawText, opaque, m_textBoxIsHorizontal ? Horizontal : Vertical); + if (!shadowApplier.nothingToDraw()) + drawTextOrEmphasisMarks(font, textRun, emphasisMark, emphasisMarkOffset, textOrigin + shadowApplier.extraOffset(), startOffset, endOffset); + shadow = shadow->next(); + } - if (m_combinedText) - m_savedDrawingStateForMask.m_context->concatCTM(rotation(m_boxRect, Counterclockwise)); - } + if (!lastShadowIterationShouldDrawText) { + if (!opaque) + m_context.setFillColor(fillColor); + drawTextOrEmphasisMarks(font, textRun, emphasisMark, emphasisMarkOffset, textOrigin, startOffset, endOffset); } } -void TextPainter::paintTextInContext(GraphicsContext& context, float amountToIncreaseStrokeWidthBy) +void TextPainter::paintTextAndEmphasisMarksIfNeeded(const TextRun& textRun, const FloatRect& boxRect, const FloatPoint& textOrigin, unsigned startOffset, unsigned endOffset, + const TextPaintStyle& paintStyle, const ShadowData* shadow) { - SavedDrawingStateForMask savedDrawingStateForMask = m_savedDrawingStateForMask; - - ASSERT(m_savedDrawingStateForMask.m_textPaintStyle); - ASSERT(m_savedDrawingStateForMask.m_selectionPaintStyle); - m_savedDrawingStateForMask.m_context = &context; - m_savedDrawingStateForMask.m_textPaintStyle->strokeWidth += amountToIncreaseStrokeWidthBy; - m_savedDrawingStateForMask.m_selectionPaintStyle->strokeWidth += amountToIncreaseStrokeWidthBy; - m_savedDrawingStateForMask.m_textShadow = nullptr; - m_savedDrawingStateForMask.m_selectionShadow = nullptr; - paintText(); - - m_savedDrawingStateForMask = savedDrawingStateForMask; + // FIXME: Truncate right-to-left text correctly. + paintTextWithShadows(shadow, *m_font, textRun, boxRect, textOrigin, startOffset, endOffset, nullAtom, 0, paintStyle.strokeWidth > 0); + + if (m_emphasisMark.isEmpty()) + return; + + FloatPoint boxOrigin = boxRect.location(); + updateGraphicsContext(m_context, paintStyle, UseEmphasisMarkColor); + static NeverDestroyed<TextRun> objectReplacementCharacterTextRun(StringView(&objectReplacementCharacter, 1)); + const TextRun& emphasisMarkTextRun = m_combinedText ? objectReplacementCharacterTextRun.get() : textRun; + FloatPoint emphasisMarkTextOrigin = m_combinedText ? FloatPoint(boxOrigin.x() + boxRect.width() / 2, boxOrigin.y() + m_font->fontMetrics().ascent()) : textOrigin; + if (m_combinedText) + m_context.concatCTM(rotation(boxRect, Clockwise)); + + // FIXME: Truncate right-to-left text correctly. + paintTextWithShadows(shadow, m_combinedText ? m_combinedText->originalFont() : *m_font, emphasisMarkTextRun, boxRect, emphasisMarkTextOrigin, startOffset, endOffset, + m_emphasisMark, m_emphasisMarkOffset, paintStyle.strokeWidth > 0); + + if (m_combinedText) + m_context.concatCTM(rotation(boxRect, Counterclockwise)); } - -#if ENABLE(CSS3_TEXT_DECORATION_SKIP_INK) -DashArray TextPainter::dashesForIntersectionsWithRect(const FloatRect& lineExtents) + +void TextPainter::paintText(const TextRun& textRun, unsigned length, const FloatRect& boxRect, const FloatPoint& textOrigin, unsigned selectionStart, unsigned selectionEnd, + bool paintSelectedTextOnly, bool paintSelectedTextSeparately) { - return m_font.dashesForIntersectionsWithRect(m_textRun, m_textOrigin, lineExtents); + ASSERT(m_font); + if (!paintSelectedTextOnly) { + // For stroked painting, we have to change the text drawing mode. It's probably dangerous to leave that mutated as a side + // effect, so only when we know we're stroking, do a save/restore. + GraphicsContextStateSaver stateSaver(m_context, m_textPaintStyle.strokeWidth > 0); + updateGraphicsContext(m_context, m_textPaintStyle); + bool fullPaint = !paintSelectedTextSeparately || selectionEnd <= selectionStart; + if (fullPaint) + paintTextAndEmphasisMarksIfNeeded(textRun, boxRect, textOrigin, 0, length, m_textPaintStyle, m_textShadow); + else { + // Paint the before and after selection parts. + if (selectionStart > 0) + paintTextAndEmphasisMarksIfNeeded(textRun, boxRect, textOrigin, 0, selectionStart, m_textPaintStyle, m_textShadow); + if (selectionEnd < length) + paintTextAndEmphasisMarksIfNeeded(textRun, boxRect, textOrigin, selectionEnd, length, m_textPaintStyle, m_textShadow); + } + } + // Paint only the text that is selected. + if ((paintSelectedTextOnly || paintSelectedTextSeparately) && selectionStart < selectionEnd) { + GraphicsContextStateSaver stateSaver(m_context, m_selectionPaintStyle.strokeWidth > 0); + updateGraphicsContext(m_context, m_selectionPaintStyle); + paintTextAndEmphasisMarksIfNeeded(textRun, boxRect, textOrigin, selectionStart, selectionEnd, m_selectionPaintStyle, m_selectionShadow); + } } -#endif } // namespace WebCore |