OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ui/gfx/canvas.h" | 5 #include "ui/gfx/canvas.h" |
6 | 6 |
7 #include "base/i18n/rtl.h" | 7 #include "base/i18n/rtl.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
9 #include "base/memory/scoped_ptr.h" | 9 #include "base/memory/scoped_ptr.h" |
10 #include "ui/gfx/font_list.h" | 10 #include "ui/gfx/font_list.h" |
11 #include "ui/gfx/geometry/insets.h" | 11 #include "ui/gfx/geometry/insets.h" |
12 #include "ui/gfx/geometry/rect.h" | 12 #include "ui/gfx/geometry/rect.h" |
13 #include "ui/gfx/geometry/safe_integer_conversions.h" | 13 #include "ui/gfx/geometry/safe_integer_conversions.h" |
14 #include "ui/gfx/range/range.h" | 14 #include "ui/gfx/range/range.h" |
15 #include "ui/gfx/render_text.h" | 15 #include "ui/gfx/render_text.h" |
16 #include "ui/gfx/shadow_value.h" | 16 #include "ui/gfx/shadow_value.h" |
17 #include "ui/gfx/text_elider.h" | 17 #include "ui/gfx/text_elider.h" |
18 #include "ui/gfx/text_utils.h" | 18 #include "ui/gfx/text_utils.h" |
19 | 19 |
20 namespace gfx { | 20 namespace gfx { |
21 | 21 |
22 namespace { | 22 namespace { |
23 | 23 |
24 #if defined(OS_WIN) | |
25 // If necessary, wraps |text| with RTL/LTR directionality characters based on | |
26 // |flags| and |text| content. | |
27 // Returns true if the text will be rendered right-to-left. | |
28 // TODO(msw): Nix this, now that RenderTextWin supports directionality directly. | |
29 bool AdjustStringDirection(int flags, base::string16* text) { | |
30 // TODO(msw): FORCE_LTR_DIRECTIONALITY does not work for RTL text now. | |
31 | |
32 // If the string is empty or LTR was forced, simply return false since the | |
33 // default RenderText directionality is already LTR. | |
34 if (text->empty() || (flags & Canvas::FORCE_LTR_DIRECTIONALITY)) | |
35 return false; | |
36 | |
37 // If RTL is forced, apply it to the string. | |
38 if (flags & Canvas::FORCE_RTL_DIRECTIONALITY) { | |
39 base::i18n::WrapStringWithRTLFormatting(text); | |
40 return true; | |
41 } | |
42 | |
43 // If a direction wasn't forced but the UI language is RTL and there were | |
44 // strong RTL characters, ensure RTL is applied. | |
45 if (base::i18n::IsRTL() && base::i18n::StringContainsStrongRTLChars(*text)) { | |
46 base::i18n::WrapStringWithRTLFormatting(text); | |
47 return true; | |
48 } | |
49 | |
50 // In the default case, the string should be rendered as LTR. RenderText's | |
51 // default directionality is LTR, so the text doesn't need to be wrapped. | |
52 // Note that individual runs within the string may still be rendered RTL | |
53 // (which will be the case for RTL text under non-RTL locales, since under RTL | |
54 // locales it will be handled by the if statement above). | |
55 return false; | |
56 } | |
57 #endif // defined(OS_WIN) | |
58 | |
59 // Checks each pixel immediately adjacent to the given pixel in the bitmap. If | 24 // Checks each pixel immediately adjacent to the given pixel in the bitmap. If |
60 // any of them are not the halo color, returns true. This defines the halo of | 25 // any of them are not the halo color, returns true. This defines the halo of |
61 // pixels that will appear around the text. Note that we have to check each | 26 // pixels that will appear around the text. Note that we have to check each |
62 // pixel against both the halo color and transparent since | 27 // pixel against both the halo color and transparent since |
63 // |DrawStringRectWithHalo| will modify the bitmap as it goes, and cleared | 28 // |DrawStringRectWithHalo| will modify the bitmap as it goes, and cleared |
64 // pixels shouldn't count as changed. | 29 // pixels shouldn't count as changed. |
65 bool PixelShouldGetHalo(const SkBitmap& bitmap, | 30 bool PixelShouldGetHalo(const SkBitmap& bitmap, |
66 int x, int y, | 31 int x, int y, |
67 SkColor halo_color) { | 32 SkColor halo_color) { |
68 if (x > 0 && | 33 if (x > 0 && |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
124 RenderText* render_text) { | 89 RenderText* render_text) { |
125 render_text->SetFontList(font_list); | 90 render_text->SetFontList(font_list); |
126 render_text->SetText(text); | 91 render_text->SetText(text); |
127 render_text->SetCursorEnabled(false); | 92 render_text->SetCursorEnabled(false); |
128 render_text->SetDisplayRect(rect); | 93 render_text->SetDisplayRect(rect); |
129 | 94 |
130 // Set the text alignment explicitly based on the directionality of the UI, | 95 // Set the text alignment explicitly based on the directionality of the UI, |
131 // if not specified. | 96 // if not specified. |
132 if (!(flags & (Canvas::TEXT_ALIGN_CENTER | | 97 if (!(flags & (Canvas::TEXT_ALIGN_CENTER | |
133 Canvas::TEXT_ALIGN_RIGHT | | 98 Canvas::TEXT_ALIGN_RIGHT | |
134 Canvas::TEXT_ALIGN_LEFT))) { | 99 Canvas::TEXT_ALIGN_LEFT | |
| 100 Canvas::TEXT_ALIGN_TO_HEAD))) { |
135 flags |= Canvas::DefaultCanvasTextAlignment(); | 101 flags |= Canvas::DefaultCanvasTextAlignment(); |
136 } | 102 } |
137 | 103 |
138 if (flags & Canvas::TEXT_ALIGN_RIGHT) | 104 if (flags & Canvas::TEXT_ALIGN_TO_HEAD) |
| 105 render_text->SetHorizontalAlignment(ALIGN_TO_HEAD); |
| 106 else if (flags & Canvas::TEXT_ALIGN_RIGHT) |
139 render_text->SetHorizontalAlignment(ALIGN_RIGHT); | 107 render_text->SetHorizontalAlignment(ALIGN_RIGHT); |
140 else if (flags & Canvas::TEXT_ALIGN_CENTER) | 108 else if (flags & Canvas::TEXT_ALIGN_CENTER) |
141 render_text->SetHorizontalAlignment(ALIGN_CENTER); | 109 render_text->SetHorizontalAlignment(ALIGN_CENTER); |
142 else | 110 else |
143 render_text->SetHorizontalAlignment(ALIGN_LEFT); | 111 render_text->SetHorizontalAlignment(ALIGN_LEFT); |
144 | 112 |
145 render_text->set_subpixel_rendering_suppressed( | 113 render_text->set_subpixel_rendering_suppressed( |
146 (flags & Canvas::NO_SUBPIXEL_RENDERING) != 0); | 114 (flags & Canvas::NO_SUBPIXEL_RENDERING) != 0); |
147 | 115 |
148 render_text->SetColor(color); | 116 render_text->SetColor(color); |
149 const int font_style = font_list.GetFontStyle(); | 117 const int font_style = font_list.GetFontStyle(); |
150 render_text->SetStyle(BOLD, (font_style & Font::BOLD) != 0); | 118 render_text->SetStyle(BOLD, (font_style & Font::BOLD) != 0); |
151 render_text->SetStyle(ITALIC, (font_style & Font::ITALIC) != 0); | 119 render_text->SetStyle(ITALIC, (font_style & Font::ITALIC) != 0); |
152 render_text->SetStyle(UNDERLINE, (font_style & Font::UNDERLINE) != 0); | 120 render_text->SetStyle(UNDERLINE, (font_style & Font::UNDERLINE) != 0); |
153 } | 121 } |
154 | 122 |
155 } // namespace | 123 } // namespace |
156 | 124 |
157 // static | 125 // static |
158 void Canvas::SizeStringFloat(const base::string16& text, | 126 void Canvas::SizeStringFloat(const base::string16& text, |
159 const FontList& font_list, | 127 const FontList& font_list, |
160 float* width, float* height, | 128 float* width, float* height, |
161 int line_height, | 129 int line_height, |
162 int flags) { | 130 int flags) { |
163 DCHECK_GE(*width, 0); | 131 DCHECK_GE(*width, 0); |
164 DCHECK_GE(*height, 0); | 132 DCHECK_GE(*height, 0); |
165 | 133 |
166 base::string16 adjusted_text = text; | |
167 #if defined(OS_WIN) | |
168 AdjustStringDirection(flags, &adjusted_text); | |
169 #endif | |
170 | |
171 if ((flags & MULTI_LINE) && *width != 0) { | 134 if ((flags & MULTI_LINE) && *width != 0) { |
172 WordWrapBehavior wrap_behavior = TRUNCATE_LONG_WORDS; | 135 WordWrapBehavior wrap_behavior = TRUNCATE_LONG_WORDS; |
173 if (flags & CHARACTER_BREAK) | 136 if (flags & CHARACTER_BREAK) |
174 wrap_behavior = WRAP_LONG_WORDS; | 137 wrap_behavior = WRAP_LONG_WORDS; |
175 else if (!(flags & NO_ELLIPSIS)) | 138 else if (!(flags & NO_ELLIPSIS)) |
176 wrap_behavior = ELIDE_LONG_WORDS; | 139 wrap_behavior = ELIDE_LONG_WORDS; |
177 | 140 |
178 std::vector<base::string16> strings; | 141 std::vector<base::string16> strings; |
179 ElideRectangleText(adjusted_text, font_list, | 142 ElideRectangleText(text, font_list, *width, INT_MAX, wrap_behavior, |
180 *width, INT_MAX, | 143 &strings); |
181 wrap_behavior, &strings); | |
182 Rect rect(ClampToInt(*width), INT_MAX); | 144 Rect rect(ClampToInt(*width), INT_MAX); |
183 scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); | 145 scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); |
184 UpdateRenderText(rect, base::string16(), font_list, flags, 0, | 146 UpdateRenderText(rect, base::string16(), font_list, flags, 0, |
185 render_text.get()); | 147 render_text.get()); |
186 | 148 |
187 float h = 0; | 149 float h = 0; |
188 float w = 0; | 150 float w = 0; |
189 for (size_t i = 0; i < strings.size(); ++i) { | 151 for (size_t i = 0; i < strings.size(); ++i) { |
190 StripAcceleratorChars(flags, &strings[i]); | 152 StripAcceleratorChars(flags, &strings[i]); |
191 render_text->SetText(strings[i]); | 153 render_text->SetText(strings[i]); |
192 const SizeF& string_size = render_text->GetStringSizeF(); | 154 const SizeF& string_size = render_text->GetStringSizeF(); |
193 w = std::max(w, string_size.width()); | 155 w = std::max(w, string_size.width()); |
194 h += (i > 0 && line_height > 0) ? | 156 h += (i > 0 && line_height > 0) ? |
195 std::max(static_cast<float>(line_height), string_size.height()) | 157 std::max(static_cast<float>(line_height), string_size.height()) |
196 : string_size.height(); | 158 : string_size.height(); |
197 } | 159 } |
198 *width = w; | 160 *width = w; |
199 *height = h; | 161 *height = h; |
200 } else { | 162 } else { |
201 // If the string is too long, the call by |RenderTextWin| to |ScriptShape()| | 163 scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); |
202 // will inexplicably fail with result E_INVALIDARG. Guard against this. | 164 Rect rect(ClampToInt(*width), ClampToInt(*height)); |
203 const size_t kMaxRenderTextLength = 5000; | 165 base::string16 adjusted_text = text; |
204 if (adjusted_text.length() >= kMaxRenderTextLength) { | 166 StripAcceleratorChars(flags, &adjusted_text); |
205 *width = static_cast<float>( | 167 UpdateRenderText(rect, adjusted_text, font_list, flags, 0, |
206 font_list.GetExpectedTextWidth(adjusted_text.length())); | 168 render_text.get()); |
207 *height = static_cast<float>(font_list.GetHeight()); | 169 const SizeF& string_size = render_text->GetStringSizeF(); |
208 } else { | 170 *width = string_size.width(); |
209 scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); | 171 *height = string_size.height(); |
210 Rect rect(ClampToInt(*width), ClampToInt(*height)); | |
211 StripAcceleratorChars(flags, &adjusted_text); | |
212 UpdateRenderText(rect, adjusted_text, font_list, flags, 0, | |
213 render_text.get()); | |
214 const SizeF& string_size = render_text->GetStringSizeF(); | |
215 *width = string_size.width(); | |
216 *height = string_size.height(); | |
217 } | |
218 } | 172 } |
219 } | 173 } |
220 | 174 |
221 void Canvas::DrawStringRectWithShadows(const base::string16& text, | 175 void Canvas::DrawStringRectWithShadows(const base::string16& text, |
222 const FontList& font_list, | 176 const FontList& font_list, |
223 SkColor color, | 177 SkColor color, |
224 const Rect& text_bounds, | 178 const Rect& text_bounds, |
225 int line_height, | 179 int line_height, |
226 int flags, | 180 int flags, |
227 const ShadowValues& shadows) { | 181 const ShadowValues& shadows) { |
228 if (!IntersectsClipRect(text_bounds)) | 182 if (!IntersectsClipRect(text_bounds)) |
229 return; | 183 return; |
230 | 184 |
231 Rect clip_rect(text_bounds); | 185 Rect clip_rect(text_bounds); |
232 clip_rect.Inset(ShadowValue::GetMargin(shadows)); | 186 clip_rect.Inset(ShadowValue::GetMargin(shadows)); |
233 | 187 |
234 canvas_->save(); | 188 canvas_->save(); |
235 ClipRect(clip_rect); | 189 ClipRect(clip_rect); |
236 | 190 |
237 Rect rect(text_bounds); | 191 Rect rect(text_bounds); |
238 base::string16 adjusted_text = text; | |
239 | |
240 #if defined(OS_WIN) | |
241 AdjustStringDirection(flags, &adjusted_text); | |
242 #endif | |
243 | 192 |
244 scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); | 193 scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); |
245 render_text->set_shadows(shadows); | 194 render_text->set_shadows(shadows); |
246 | 195 |
247 if (flags & MULTI_LINE) { | 196 if (flags & MULTI_LINE) { |
248 WordWrapBehavior wrap_behavior = IGNORE_LONG_WORDS; | 197 WordWrapBehavior wrap_behavior = IGNORE_LONG_WORDS; |
249 if (flags & CHARACTER_BREAK) | 198 if (flags & CHARACTER_BREAK) |
250 wrap_behavior = WRAP_LONG_WORDS; | 199 wrap_behavior = WRAP_LONG_WORDS; |
251 else if (!(flags & NO_ELLIPSIS)) | 200 else if (!(flags & NO_ELLIPSIS)) |
252 wrap_behavior = ELIDE_LONG_WORDS; | 201 wrap_behavior = ELIDE_LONG_WORDS; |
253 | 202 |
254 std::vector<base::string16> strings; | 203 std::vector<base::string16> strings; |
255 ElideRectangleText(adjusted_text, font_list, | 204 ElideRectangleText(text, font_list, |
256 static_cast<float>(text_bounds.width()), | 205 static_cast<float>(text_bounds.width()), |
257 text_bounds.height(), wrap_behavior, &strings); | 206 text_bounds.height(), wrap_behavior, &strings); |
258 | 207 |
259 for (size_t i = 0; i < strings.size(); i++) { | 208 for (size_t i = 0; i < strings.size(); i++) { |
260 Range range = StripAcceleratorChars(flags, &strings[i]); | 209 Range range = StripAcceleratorChars(flags, &strings[i]); |
261 UpdateRenderText(rect, strings[i], font_list, flags, color, | 210 UpdateRenderText(rect, strings[i], font_list, flags, color, |
262 render_text.get()); | 211 render_text.get()); |
263 int line_padding = 0; | 212 int line_padding = 0; |
264 if (line_height > 0) | 213 if (line_height > 0) |
265 line_padding = line_height - render_text->GetStringSize().height(); | 214 line_padding = line_height - render_text->GetStringSize().height(); |
(...skipping 11 matching lines...) Expand all Loading... |
277 | 226 |
278 rect.set_height(line_height - line_padding); | 227 rect.set_height(line_height - line_padding); |
279 | 228 |
280 if (range.IsValid()) | 229 if (range.IsValid()) |
281 render_text->ApplyStyle(UNDERLINE, true, range); | 230 render_text->ApplyStyle(UNDERLINE, true, range); |
282 render_text->SetDisplayRect(rect); | 231 render_text->SetDisplayRect(rect); |
283 render_text->Draw(this); | 232 render_text->Draw(this); |
284 rect += Vector2d(0, line_height); | 233 rect += Vector2d(0, line_height); |
285 } | 234 } |
286 } else { | 235 } else { |
| 236 base::string16 adjusted_text = text; |
287 Range range = StripAcceleratorChars(flags, &adjusted_text); | 237 Range range = StripAcceleratorChars(flags, &adjusted_text); |
288 bool elide_text = ((flags & NO_ELLIPSIS) == 0); | 238 bool elide_text = ((flags & NO_ELLIPSIS) == 0); |
289 | 239 |
290 #if defined(OS_LINUX) | 240 #if defined(OS_LINUX) |
291 // On Linux, eliding really means fading the end of the string. But only | 241 // On Linux, eliding really means fading the end of the string. But only |
292 // for LTR text. RTL text is still elided (on the left) with "...". | 242 // for LTR text. RTL text is still elided (on the left) with "...". |
293 if (elide_text) { | 243 if (elide_text) { |
294 render_text->SetText(adjusted_text); | 244 render_text->SetText(adjusted_text); |
295 if (render_text->GetDisplayTextDirection() == base::i18n::LEFT_TO_RIGHT) { | 245 if (render_text->GetDisplayTextDirection() == base::i18n::LEFT_TO_RIGHT) { |
296 render_text->SetElideBehavior(FADE_TAIL); | 246 render_text->SetElideBehavior(FADE_TAIL); |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
366 void Canvas::DrawFadedString(const base::string16& text, | 316 void Canvas::DrawFadedString(const base::string16& text, |
367 const FontList& font_list, | 317 const FontList& font_list, |
368 SkColor color, | 318 SkColor color, |
369 const Rect& display_rect, | 319 const Rect& display_rect, |
370 int flags) { | 320 int flags) { |
371 // If the whole string fits in the destination then just draw it directly. | 321 // If the whole string fits in the destination then just draw it directly. |
372 if (GetStringWidth(text, font_list) <= display_rect.width()) { | 322 if (GetStringWidth(text, font_list) <= display_rect.width()) { |
373 DrawStringRectWithFlags(text, font_list, color, display_rect, flags); | 323 DrawStringRectWithFlags(text, font_list, color, display_rect, flags); |
374 return; | 324 return; |
375 } | 325 } |
376 | 326 // Align with content directionality instead of fading both ends. |
377 // Align with forced content directionality, overriding alignment flags. | 327 flags &= ~TEXT_ALIGN_CENTER; |
378 if (flags & FORCE_RTL_DIRECTIONALITY) { | 328 if (!(flags & (TEXT_ALIGN_LEFT | TEXT_ALIGN_RIGHT))) |
379 flags &= ~(TEXT_ALIGN_CENTER | TEXT_ALIGN_LEFT); | 329 flags |= TEXT_ALIGN_TO_HEAD; |
380 flags |= TEXT_ALIGN_RIGHT; | |
381 } else if (flags & FORCE_LTR_DIRECTIONALITY) { | |
382 flags &= ~(TEXT_ALIGN_CENTER | TEXT_ALIGN_RIGHT); | |
383 flags |= TEXT_ALIGN_LEFT; | |
384 } else if (!(flags & TEXT_ALIGN_LEFT) && !(flags & TEXT_ALIGN_RIGHT)) { | |
385 // Also align with content directionality instead of fading both ends. | |
386 flags &= ~TEXT_ALIGN_CENTER; | |
387 const bool is_rtl = base::i18n::GetFirstStrongCharacterDirection(text) == | |
388 base::i18n::RIGHT_TO_LEFT; | |
389 flags |= is_rtl ? TEXT_ALIGN_RIGHT : TEXT_ALIGN_LEFT; | |
390 } | |
391 flags |= NO_ELLIPSIS; | 330 flags |= NO_ELLIPSIS; |
392 | 331 |
393 scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); | 332 scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); |
394 Rect rect = display_rect; | 333 Rect rect = display_rect; |
395 UpdateRenderText(rect, text, font_list, flags, color, render_text.get()); | 334 UpdateRenderText(rect, text, font_list, flags, color, render_text.get()); |
396 render_text->SetElideBehavior(FADE_TAIL); | 335 render_text->SetElideBehavior(FADE_TAIL); |
397 | 336 |
398 canvas_->save(); | 337 canvas_->save(); |
399 ClipRect(display_rect); | 338 ClipRect(display_rect); |
400 render_text->Draw(this); | 339 render_text->Draw(this); |
401 canvas_->restore(); | 340 canvas_->restore(); |
402 } | 341 } |
403 | 342 |
404 } // namespace gfx | 343 } // namespace gfx |
OLD | NEW |