Rename DEFINE_STATIC_LOCAL to DEPRECATED_DEFINE_STATIC_LOCAL
[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 void paintTextWithShadows(GraphicsContext* context, const Font& font, const TextRun& textRun, const AtomicString& emphasisMark,
64     int emphasisMarkOffset, int startOffset, int endOffset, int truncationPoint, const FloatPoint& textOrigin, const FloatRect& boxRect,
65     const ShadowData* shadow, bool stroked, bool horizontal)
66 {
67     Color fillColor = context->fillColor();
68     ColorSpace fillColorSpace = context->fillColorSpace();
69     bool opaque = !fillColor.hasAlpha();
70     if (!opaque)
71         context->setFillColor(Color::black, fillColorSpace);
72
73     do {
74         IntSize extraOffset;
75         if (shadow)
76             extraOffset = roundedIntSize(InlineTextBox::applyShadowToGraphicsContext(context, shadow, boxRect, stroked, opaque, horizontal));
77         else if (!opaque)
78             context->setFillColor(fillColor, fillColorSpace);
79
80         if (startOffset <= endOffset)
81             drawTextOrEmphasisMarks(*context, font, textRun, emphasisMark, emphasisMarkOffset, textOrigin + extraOffset, startOffset, endOffset);
82         else {
83             if (endOffset > 0)
84                 drawTextOrEmphasisMarks(*context, font, textRun, emphasisMark, emphasisMarkOffset, textOrigin + extraOffset, 0, endOffset);
85             if (startOffset < truncationPoint)
86                 drawTextOrEmphasisMarks(*context, font, textRun, emphasisMark, emphasisMarkOffset, textOrigin + extraOffset, startOffset, truncationPoint);
87         }
88
89         if (!shadow)
90             break;
91
92         if (shadow->next() || stroked || !opaque)
93             context->restore();
94         else
95             context->clearShadow();
96
97         shadow = shadow->next();
98     } while (shadow || stroked || !opaque);
99 }
100
101 void TextPainter::paintText()
102 {
103     ASSERT(m_savedDrawingStateForMask.m_textPaintStyle);
104     ASSERT(m_savedDrawingStateForMask.m_selectionPaintStyle);
105     
106     FloatPoint boxOrigin = m_boxRect.location();
107
108     if (!m_paintSelectedTextOnly) {
109         // For stroked painting, we have to change the text drawing mode. It's probably dangerous to leave that mutated as a side
110         // effect, so only when we know we're stroking, do a save/restore.
111         GraphicsContextStateSaver stateSaver(*m_savedDrawingStateForMask.m_context, m_savedDrawingStateForMask.m_textPaintStyle->strokeWidth > 0);
112
113         updateGraphicsContext(*m_savedDrawingStateForMask.m_context, *m_savedDrawingStateForMask.m_textPaintStyle);
114         if (!m_paintSelectedTextSeparately || m_endPositionInTextRun <= m_startPositionInTextRun) {
115             // FIXME: Truncate right-to-left text correctly.
116             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);
117         } else
118             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);
119
120         if (!m_emphasisMark.isEmpty()) {
121             updateGraphicsContext(*m_savedDrawingStateForMask.m_context, *m_savedDrawingStateForMask.m_textPaintStyle, UseEmphasisMarkColor);
122
123             DEPRECATED_DEFINE_STATIC_LOCAL(TextRun, objectReplacementCharacterTextRun, (&objectReplacementCharacter, 1));
124             TextRun& emphasisMarkTextRun = m_combinedText ? objectReplacementCharacterTextRun : m_textRun;
125             FloatPoint emphasisMarkTextOrigin = m_combinedText ? FloatPoint(boxOrigin.x() + m_boxRect.width() / 2, boxOrigin.y() + m_font.fontMetrics().ascent()) : m_textOrigin;
126             if (m_combinedText)
127                 m_savedDrawingStateForMask.m_context->concatCTM(rotation(m_boxRect, Clockwise));
128
129             if (!m_paintSelectedTextSeparately || m_endPositionInTextRun <= m_startPositionInTextRun) {
130                 // FIXME: Truncate right-to-left text correctly.
131                 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);
132             } else
133                 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);
134
135             if (m_combinedText)
136                 m_savedDrawingStateForMask.m_context->concatCTM(rotation(m_boxRect, Counterclockwise));
137         }
138     }
139
140     if ((m_paintSelectedTextOnly || m_paintSelectedTextSeparately) && m_startPositionInTextRun < m_endPositionInTextRun) {
141         // paint only the text that is selected
142         GraphicsContextStateSaver stateSaver(*m_savedDrawingStateForMask.m_context, m_savedDrawingStateForMask.m_selectionPaintStyle->strokeWidth > 0);
143
144         updateGraphicsContext(*m_savedDrawingStateForMask.m_context, *m_savedDrawingStateForMask.m_selectionPaintStyle);
145         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);
146         if (!m_emphasisMark.isEmpty()) {
147             updateGraphicsContext(*m_savedDrawingStateForMask.m_context, *m_savedDrawingStateForMask.m_selectionPaintStyle, UseEmphasisMarkColor);
148
149             DEPRECATED_DEFINE_STATIC_LOCAL(TextRun, objectReplacementCharacterTextRun, (&objectReplacementCharacter, 1));
150             TextRun& emphasisMarkTextRun = m_combinedText ? objectReplacementCharacterTextRun : m_textRun;
151             FloatPoint emphasisMarkTextOrigin = m_combinedText ? FloatPoint(boxOrigin.x() + m_boxRect.width() / 2, boxOrigin.y() + m_font.fontMetrics().ascent()) : m_textOrigin;
152             if (m_combinedText)
153                 m_savedDrawingStateForMask.m_context->concatCTM(rotation(m_boxRect, Clockwise));
154
155             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);
156
157             if (m_combinedText)
158                 m_savedDrawingStateForMask.m_context->concatCTM(rotation(m_boxRect, Counterclockwise));
159         }
160     }
161 }
162
163 void TextPainter::paintTextInContext(GraphicsContext& context, float amountToIncreaseStrokeWidthBy)
164 {
165     SavedDrawingStateForMask savedDrawingStateForMask = m_savedDrawingStateForMask;
166     
167     ASSERT(m_savedDrawingStateForMask.m_textPaintStyle);
168     ASSERT(m_savedDrawingStateForMask.m_selectionPaintStyle);
169     m_savedDrawingStateForMask.m_context = &context;
170     m_savedDrawingStateForMask.m_textPaintStyle->strokeWidth += amountToIncreaseStrokeWidthBy;
171     m_savedDrawingStateForMask.m_selectionPaintStyle->strokeWidth += amountToIncreaseStrokeWidthBy;
172     m_savedDrawingStateForMask.m_textShadow = nullptr;
173     m_savedDrawingStateForMask.m_selectionShadow = nullptr;
174     paintText();
175
176     m_savedDrawingStateForMask = savedDrawingStateForMask;
177 }
178
179 #if ENABLE(CSS3_TEXT_DECORATION_SKIP_INK)
180 DashArray TextPainter::dashesForIntersectionsWithRect(const FloatRect& lineExtents)
181 {
182     return m_font.dashesForIntersectionsWithRect(m_textRun, m_textOrigin, lineExtents);
183 }
184 #endif
185
186 } // namespace WebCore