1 | /* |
2 | * Copyright (C) 2013 Apple Inc. All rights reserved. |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without |
5 | * modification, are permitted provided that the following conditions |
6 | * are met: |
7 | * 1. Redistributions of source code must retain the above copyright |
8 | * notice, this list of conditions and the following disclaimer. |
9 | * 2. Redistributions in binary form must reproduce the above copyright |
10 | * notice, this list of conditions and the following disclaimer in the |
11 | * documentation and/or other materials provided with the distribution. |
12 | * |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
15 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
17 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
23 | * THE POSSIBILITY OF SUCH DAMAGE. |
24 | */ |
25 | |
26 | #include "config.h" |
27 | #include "TextPaintStyle.h" |
28 | |
29 | #include "FocusController.h" |
30 | #include "Frame.h" |
31 | #include "GraphicsContext.h" |
32 | #include "Page.h" |
33 | #include "PaintInfo.h" |
34 | #include "RenderStyle.h" |
35 | #include "RenderText.h" |
36 | #include "RenderTheme.h" |
37 | #include "RenderView.h" |
38 | #include "Settings.h" |
39 | |
40 | namespace WebCore { |
41 | |
42 | TextPaintStyle::TextPaintStyle(const Color& color) |
43 | : fillColor(color) |
44 | , strokeColor(color) |
45 | { |
46 | } |
47 | |
48 | bool TextPaintStyle::operator==(const TextPaintStyle& other) const |
49 | { |
50 | return fillColor == other.fillColor && strokeColor == other.strokeColor && emphasisMarkColor == other.emphasisMarkColor |
51 | && strokeWidth == other.strokeWidth && paintOrder == other.paintOrder && lineJoin == other.lineJoin |
52 | #if ENABLE(LETTERPRESS) |
53 | && useLetterpressEffect == other.useLetterpressEffect |
54 | #endif |
55 | && lineCap == other.lineCap && miterLimit == other.miterLimit; |
56 | } |
57 | |
58 | bool textColorIsLegibleAgainstBackgroundColor(const Color& textColor, const Color& backgroundColor) |
59 | { |
60 | // Semi-arbitrarily chose 65025 (255^2) value here after a few tests. |
61 | return differenceSquared(textColor, backgroundColor) > 65025; |
62 | } |
63 | |
64 | static Color adjustColorForVisibilityOnBackground(const Color& textColor, const Color& backgroundColor) |
65 | { |
66 | if (textColorIsLegibleAgainstBackgroundColor(textColor, backgroundColor)) |
67 | return textColor; |
68 | |
69 | int distanceFromWhite = differenceSquared(textColor, Color::white); |
70 | int distanceFromBlack = differenceSquared(textColor, Color::black); |
71 | if (distanceFromWhite < distanceFromBlack) |
72 | return textColor.dark(); |
73 | |
74 | return textColor.light(); |
75 | } |
76 | |
77 | TextPaintStyle computeTextPaintStyle(const Frame& frame, const RenderStyle& lineStyle, const PaintInfo& paintInfo) |
78 | { |
79 | TextPaintStyle paintStyle; |
80 | |
81 | #if ENABLE(LETTERPRESS) |
82 | paintStyle.useLetterpressEffect = lineStyle.textDecorationsInEffect().contains(TextDecoration::Letterpress); |
83 | #endif |
84 | auto viewportSize = frame.view() ? frame.view()->size() : IntSize(); |
85 | paintStyle.strokeWidth = lineStyle.computedStrokeWidth(viewportSize); |
86 | paintStyle.paintOrder = lineStyle.paintOrder(); |
87 | paintStyle.lineJoin = lineStyle.joinStyle(); |
88 | paintStyle.lineCap = lineStyle.capStyle(); |
89 | paintStyle.miterLimit = lineStyle.strokeMiterLimit(); |
90 | |
91 | if (paintInfo.forceTextColor()) { |
92 | paintStyle.fillColor = paintInfo.forcedTextColor(); |
93 | paintStyle.strokeColor = paintInfo.forcedTextColor(); |
94 | paintStyle.emphasisMarkColor = paintInfo.forcedTextColor(); |
95 | return paintStyle; |
96 | } |
97 | |
98 | if (lineStyle.insideDefaultButton()) { |
99 | Page* page = frame.page(); |
100 | if (page && page->focusController().isActive()) { |
101 | OptionSet<StyleColor::Options> options; |
102 | if (page->useSystemAppearance()) |
103 | options.add(StyleColor::Options::UseSystemAppearance); |
104 | paintStyle.fillColor = RenderTheme::singleton().systemColor(CSSValueActivebuttontext, options); |
105 | return paintStyle; |
106 | } |
107 | } |
108 | |
109 | paintStyle.fillColor = lineStyle.visitedDependentColorWithColorFilter(CSSPropertyWebkitTextFillColor); |
110 | |
111 | bool forceBackgroundToWhite = false; |
112 | if (frame.document() && frame.document()->printing()) { |
113 | if (lineStyle.printColorAdjust() == PrintColorAdjust::Economy) |
114 | forceBackgroundToWhite = true; |
115 | if (frame.settings().shouldPrintBackgrounds()) |
116 | forceBackgroundToWhite = false; |
117 | } |
118 | |
119 | // Make the text fill color legible against a white background |
120 | if (forceBackgroundToWhite) |
121 | paintStyle.fillColor = adjustColorForVisibilityOnBackground(paintStyle.fillColor, Color::white); |
122 | |
123 | paintStyle.strokeColor = lineStyle.colorByApplyingColorFilter(lineStyle.computedStrokeColor()); |
124 | |
125 | // Make the text stroke color legible against a white background |
126 | if (forceBackgroundToWhite) |
127 | paintStyle.strokeColor = adjustColorForVisibilityOnBackground(paintStyle.strokeColor, Color::white); |
128 | |
129 | paintStyle.emphasisMarkColor = lineStyle.visitedDependentColorWithColorFilter(CSSPropertyWebkitTextEmphasisColor); |
130 | |
131 | // Make the text stroke color legible against a white background |
132 | if (forceBackgroundToWhite) |
133 | paintStyle.emphasisMarkColor = adjustColorForVisibilityOnBackground(paintStyle.emphasisMarkColor, Color::white); |
134 | |
135 | return paintStyle; |
136 | } |
137 | |
138 | TextPaintStyle computeTextSelectionPaintStyle(const TextPaintStyle& textPaintStyle, const RenderText& renderer, const RenderStyle& lineStyle, const PaintInfo& paintInfo, Optional<ShadowData>& selectionShadow) |
139 | { |
140 | TextPaintStyle selectionPaintStyle = textPaintStyle; |
141 | |
142 | #if ENABLE(TEXT_SELECTION) |
143 | Color foreground = paintInfo.forceTextColor() ? paintInfo.forcedTextColor() : renderer.selectionForegroundColor(); |
144 | if (foreground.isValid() && foreground != selectionPaintStyle.fillColor) |
145 | selectionPaintStyle.fillColor = foreground; |
146 | |
147 | Color emphasisMarkForeground = paintInfo.forceTextColor() ? paintInfo.forcedTextColor() : renderer.selectionEmphasisMarkColor(); |
148 | if (emphasisMarkForeground.isValid() && emphasisMarkForeground != selectionPaintStyle.emphasisMarkColor) |
149 | selectionPaintStyle.emphasisMarkColor = emphasisMarkForeground; |
150 | |
151 | if (auto pseudoStyle = renderer.selectionPseudoStyle()) { |
152 | selectionShadow = ShadowData::clone(paintInfo.forceTextColor() ? nullptr : pseudoStyle->textShadow()); |
153 | auto viewportSize = renderer.frame().view() ? renderer.frame().view()->size() : IntSize(); |
154 | float strokeWidth = pseudoStyle->computedStrokeWidth(viewportSize); |
155 | if (strokeWidth != selectionPaintStyle.strokeWidth) |
156 | selectionPaintStyle.strokeWidth = strokeWidth; |
157 | |
158 | Color stroke = paintInfo.forceTextColor() ? paintInfo.forcedTextColor() : pseudoStyle->computedStrokeColor(); |
159 | if (stroke != selectionPaintStyle.strokeColor) |
160 | selectionPaintStyle.strokeColor = stroke; |
161 | } else |
162 | selectionShadow = ShadowData::clone(paintInfo.forceTextColor() ? nullptr : lineStyle.textShadow()); |
163 | #else |
164 | UNUSED_PARAM(renderer); |
165 | UNUSED_PARAM(lineStyle); |
166 | UNUSED_PARAM(paintInfo); |
167 | selectionShadow = ShadowData::clone(paintInfo.forceTextColor() ? nullptr : lineStyle.textShadow()); |
168 | #endif |
169 | return selectionPaintStyle; |
170 | } |
171 | |
172 | void updateGraphicsContext(GraphicsContext& context, const TextPaintStyle& paintStyle, FillColorType fillColorType) |
173 | { |
174 | TextDrawingModeFlags mode = context.textDrawingMode(); |
175 | TextDrawingModeFlags newMode = mode; |
176 | #if ENABLE(LETTERPRESS) |
177 | if (paintStyle.useLetterpressEffect) |
178 | newMode |= TextModeLetterpress; |
179 | else |
180 | newMode &= ~TextModeLetterpress; |
181 | #endif |
182 | if (paintStyle.strokeWidth > 0 && paintStyle.strokeColor.isVisible()) |
183 | newMode |= TextModeStroke; |
184 | if (mode != newMode) { |
185 | context.setTextDrawingMode(newMode); |
186 | mode = newMode; |
187 | } |
188 | |
189 | Color fillColor = fillColorType == UseEmphasisMarkColor ? paintStyle.emphasisMarkColor : paintStyle.fillColor; |
190 | if (mode & TextModeFill && (fillColor != context.fillColor())) |
191 | context.setFillColor(fillColor); |
192 | |
193 | if (mode & TextModeStroke) { |
194 | if (paintStyle.strokeColor != context.strokeColor()) |
195 | context.setStrokeColor(paintStyle.strokeColor); |
196 | if (paintStyle.strokeWidth != context.strokeThickness()) |
197 | context.setStrokeThickness(paintStyle.strokeWidth); |
198 | context.setLineJoin(paintStyle.lineJoin); |
199 | context.setLineCap(paintStyle.lineCap); |
200 | if (paintStyle.lineJoin == MiterJoin) |
201 | context.setMiterLimit(paintStyle.miterLimit); |
202 | } |
203 | } |
204 | |
205 | } |
206 | |