Remove unnecessary function from TextPainter
[WebKit-https.git] / Source / WebCore / rendering / TextPainter.cpp
1 /*
2  * (C) 1999 Lars Knoll (knoll@kde.org)
3  * (C) 2000 Dirk Mueller (mueller@kde.org)
4  * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  *
21  */
22
23 #include "config.h"
24 #include "TextPainter.h"
25
26 #include "GraphicsContext.h"
27 #include "InlineTextBox.h"
28 #include "RenderCombineText.h"
29 #include "TextPaintStyle.h"
30
31 namespace WebCore {
32
33 TextPainter::TextPainter(GraphicsContext& context, bool paintSelectedTextOnly, bool paintSelectedTextSeparately, const Font& font,
34     int startPositionInTextRun, int endPositionInTextBoxString, int length, const AtomicString& emphasisMark, RenderCombineText* combinedText, TextRun& textRun,
35     FloatRect& boxRect, FloatPoint& textOrigin, int emphasisMarkOffset, const ShadowData* textShadow, const ShadowData* selectionShadow,
36     bool textBoxIsHorizontal, TextPaintStyle& textPaintStyle, TextPaintStyle& selectionPaintStyle)
37     : m_paintSelectedTextOnly(paintSelectedTextOnly)
38     , m_paintSelectedTextSeparately(paintSelectedTextSeparately)
39     , m_font(font)
40     , m_startPositionInTextRun(startPositionInTextRun)
41     , m_endPositionInTextRun(endPositionInTextBoxString)
42     , m_length(length)
43     , m_emphasisMark(emphasisMark)
44     , m_combinedText(combinedText)
45     , m_textRun(textRun)
46     , m_boxRect(boxRect)
47     , m_textOrigin(textOrigin)
48     , m_emphasisMarkOffset(emphasisMarkOffset)
49     , m_textBoxIsHorizontal(textBoxIsHorizontal)
50     , m_savedDrawingStateForMask(&context, &textPaintStyle, &selectionPaintStyle, textShadow, selectionShadow)
51 {
52 }
53
54 static void drawTextOrEmphasisMarks(GraphicsContext& context, const Font& font, const TextRun& textRun, const AtomicString& emphasisMark,
55     int emphasisMarkOffset, const FloatPoint& point, const int from, const int to)
56 {
57     if (emphasisMark.isEmpty())
58         context.drawText(font, textRun, point, from, to);
59     else
60         context.drawEmphasisMarks(font, textRun, emphasisMark, point + IntSize(0, emphasisMarkOffset), from, to);
61 }
62
63 static bool isEmptyShadow(const ShadowData* shadow)
64 {
65     if (!shadow)
66         return true;
67     return shadow->location() == IntPoint() && !shadow->radius();
68 }
69
70 static void paintTextWithShadows(GraphicsContext* context, const Font& font, const TextRun& textRun, const AtomicString& emphasisMark,
71     int emphasisMarkOffset, int startOffset, int endOffset, int truncationPoint, const FloatPoint& textOrigin, const FloatRect& boxRect,
72     const ShadowData* shadow, bool stroked, bool horizontal)
73 {
74     Color fillColor = context->fillColor();
75     ColorSpace fillColorSpace = context->fillColorSpace();
76     bool opaque = !fillColor.hasAlpha();
77     if (!opaque)
78         context->setFillColor(Color::black, fillColorSpace);
79
80     do {
81         IntSize extraOffset;
82         bool shadowIsEmpty = isEmptyShadow(shadow);
83         if (!shadowIsEmpty)
84             extraOffset = roundedIntSize(InlineTextBox::applyShadowToGraphicsContext(context, shadow, boxRect, stroked, opaque, horizontal));
85         else if (!opaque)
86             context->setFillColor(fillColor, fillColorSpace);
87
88         if (startOffset <= endOffset)
89             drawTextOrEmphasisMarks(*context, font, textRun, emphasisMark, emphasisMarkOffset, textOrigin + extraOffset, startOffset, endOffset);
90         else {
91             if (endOffset > 0)
92                 drawTextOrEmphasisMarks(*context, font, textRun, emphasisMark, emphasisMarkOffset, textOrigin + extraOffset, 0, endOffset);
93             if (startOffset < truncationPoint)
94                 drawTextOrEmphasisMarks(*context, font, textRun, emphasisMark, emphasisMarkOffset, textOrigin + extraOffset, startOffset, truncationPoint);
95         }
96
97         if (!shadow)
98             break;
99
100         if (shadow->next() || stroked || !opaque)
101             context->restore();
102         else if (!shadowIsEmpty)
103             context->clearShadow();
104
105         shadow = shadow->next();
106     } while (shadow || stroked || !opaque);
107 }
108
109 void TextPainter::paintText()
110 {
111     ASSERT(m_savedDrawingStateForMask.m_textPaintStyle);
112     ASSERT(m_savedDrawingStateForMask.m_selectionPaintStyle);
113     
114     FloatPoint boxOrigin = m_boxRect.location();
115
116     if (!m_paintSelectedTextOnly) {
117         // For stroked painting, we have to change the text drawing mode. It's probably dangerous to leave that mutated as a side
118         // effect, so only when we know we're stroking, do a save/restore.
119         GraphicsContextStateSaver stateSaver(*m_savedDrawingStateForMask.m_context, m_savedDrawingStateForMask.m_textPaintStyle->strokeWidth > 0);
120
121         updateGraphicsContext(*m_savedDrawingStateForMask.m_context, *m_savedDrawingStateForMask.m_textPaintStyle);
122         if (!m_paintSelectedTextSeparately || m_endPositionInTextRun <= m_startPositionInTextRun) {
123             // FIXME: Truncate right-to-left text correctly.
124             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);
125         } else
126             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);
127
128         if (!m_emphasisMark.isEmpty()) {
129             updateGraphicsContext(*m_savedDrawingStateForMask.m_context, *m_savedDrawingStateForMask.m_textPaintStyle, UseEmphasisMarkColor);
130
131             DEPRECATED_DEFINE_STATIC_LOCAL(TextRun, objectReplacementCharacterTextRun, (&objectReplacementCharacter, 1));
132             TextRun& emphasisMarkTextRun = m_combinedText ? objectReplacementCharacterTextRun : m_textRun;
133             FloatPoint emphasisMarkTextOrigin = m_combinedText ? FloatPoint(boxOrigin.x() + m_boxRect.width() / 2, boxOrigin.y() + m_font.fontMetrics().ascent()) : m_textOrigin;
134             if (m_combinedText)
135                 m_savedDrawingStateForMask.m_context->concatCTM(rotation(m_boxRect, Clockwise));
136
137             if (!m_paintSelectedTextSeparately || m_endPositionInTextRun <= m_startPositionInTextRun) {
138                 // FIXME: Truncate right-to-left text correctly.
139                 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);
140             } else
141                 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);
142
143             if (m_combinedText)
144                 m_savedDrawingStateForMask.m_context->concatCTM(rotation(m_boxRect, Counterclockwise));
145         }
146     }
147
148     if ((m_paintSelectedTextOnly || m_paintSelectedTextSeparately) && m_startPositionInTextRun < m_endPositionInTextRun) {
149         // paint only the text that is selected
150         GraphicsContextStateSaver stateSaver(*m_savedDrawingStateForMask.m_context, m_savedDrawingStateForMask.m_selectionPaintStyle->strokeWidth > 0);
151
152         updateGraphicsContext(*m_savedDrawingStateForMask.m_context, *m_savedDrawingStateForMask.m_selectionPaintStyle);
153         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);
154         if (!m_emphasisMark.isEmpty()) {
155             updateGraphicsContext(*m_savedDrawingStateForMask.m_context, *m_savedDrawingStateForMask.m_selectionPaintStyle, UseEmphasisMarkColor);
156
157             DEPRECATED_DEFINE_STATIC_LOCAL(TextRun, objectReplacementCharacterTextRun, (&objectReplacementCharacter, 1));
158             TextRun& emphasisMarkTextRun = m_combinedText ? objectReplacementCharacterTextRun : m_textRun;
159             FloatPoint emphasisMarkTextOrigin = m_combinedText ? FloatPoint(boxOrigin.x() + m_boxRect.width() / 2, boxOrigin.y() + m_font.fontMetrics().ascent()) : m_textOrigin;
160             if (m_combinedText)
161                 m_savedDrawingStateForMask.m_context->concatCTM(rotation(m_boxRect, Clockwise));
162
163             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);
164
165             if (m_combinedText)
166                 m_savedDrawingStateForMask.m_context->concatCTM(rotation(m_boxRect, Counterclockwise));
167         }
168     }
169 }
170
171 #if ENABLE(CSS3_TEXT_DECORATION_SKIP_INK)
172 DashArray TextPainter::dashesForIntersectionsWithRect(const FloatRect& lineExtents)
173 {
174     return m_font.dashesForIntersectionsWithRect(m_textRun, m_textOrigin, lineExtents);
175 }
176 #endif
177
178 } // namespace WebCore