Chromium Code Reviews| 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, |
|
msw
2015/02/25 17:05:20
nit: update line wrapping.
oshima
2015/03/04 08:01:50
Done.
| |
| 180 *width, INT_MAX, | 143 *width, INT_MAX, |
| 181 wrap_behavior, &strings); | 144 wrap_behavior, &strings); |
| 182 Rect rect(ClampToInt(*width), INT_MAX); | 145 Rect rect(ClampToInt(*width), INT_MAX); |
| 183 scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); | 146 scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); |
| 184 UpdateRenderText(rect, base::string16(), font_list, flags, 0, | 147 UpdateRenderText(rect, base::string16(), font_list, flags, 0, |
| 185 render_text.get()); | 148 render_text.get()); |
| 186 | 149 |
| 187 float h = 0; | 150 float h = 0; |
| 188 float w = 0; | 151 float w = 0; |
| 189 for (size_t i = 0; i < strings.size(); ++i) { | 152 for (size_t i = 0; i < strings.size(); ++i) { |
| 190 StripAcceleratorChars(flags, &strings[i]); | 153 StripAcceleratorChars(flags, &strings[i]); |
| 191 render_text->SetText(strings[i]); | 154 render_text->SetText(strings[i]); |
| 192 const SizeF& string_size = render_text->GetStringSizeF(); | 155 const SizeF& string_size = render_text->GetStringSizeF(); |
| 193 w = std::max(w, string_size.width()); | 156 w = std::max(w, string_size.width()); |
| 194 h += (i > 0 && line_height > 0) ? | 157 h += (i > 0 && line_height > 0) ? |
| 195 std::max(static_cast<float>(line_height), string_size.height()) | 158 std::max(static_cast<float>(line_height), string_size.height()) |
| 196 : string_size.height(); | 159 : string_size.height(); |
| 197 } | 160 } |
| 198 *width = w; | 161 *width = w; |
| 199 *height = h; | 162 *height = h; |
| 200 } else { | 163 } else { |
| 201 // If the string is too long, the call by |RenderTextWin| to |ScriptShape()| | 164 scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); |
| 202 // will inexplicably fail with result E_INVALIDARG. Guard against this. | 165 Rect rect(ClampToInt(*width), ClampToInt(*height)); |
| 203 const size_t kMaxRenderTextLength = 5000; | 166 base::string16 adjusted_text = text; |
|
msw
2015/03/04 17:09:36
You should update the comment in text_elider.cc re
| |
| 204 if (adjusted_text.length() >= kMaxRenderTextLength) { | 167 StripAcceleratorChars(flags, &adjusted_text); |
|
msw
2015/02/25 17:05:20
Did you test some very long strings here? (up to 1
oshima
2015/03/04 08:01:50
RenderTextWin no longer exists, so E_INVALIDARG ca
msw
2015/03/04 17:09:36
This was unconditionally clamping at 5k chars, eve
oshima
2015/03/04 20:17:42
In the omibox, which uses TextField that uses RT d
| |
| 205 *width = static_cast<float>( | 168 UpdateRenderText(rect, adjusted_text, font_list, flags, 0, |
| 206 font_list.GetExpectedTextWidth(adjusted_text.length())); | 169 render_text.get()); |
| 207 *height = static_cast<float>(font_list.GetHeight()); | 170 const SizeF& string_size = render_text->GetStringSizeF(); |
| 208 } else { | 171 *width = string_size.width(); |
| 209 scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); | 172 *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 } | 173 } |
| 219 } | 174 } |
| 220 | 175 |
| 221 void Canvas::DrawStringRectWithShadows(const base::string16& text, | 176 void Canvas::DrawStringRectWithShadows(const base::string16& text, |
| 222 const FontList& font_list, | 177 const FontList& font_list, |
| 223 SkColor color, | 178 SkColor color, |
| 224 const Rect& text_bounds, | 179 const Rect& text_bounds, |
| 225 int line_height, | 180 int line_height, |
| 226 int flags, | 181 int flags, |
| 227 const ShadowValues& shadows) { | 182 const ShadowValues& shadows) { |
| 228 if (!IntersectsClipRect(text_bounds)) | 183 if (!IntersectsClipRect(text_bounds)) |
| 229 return; | 184 return; |
| 230 | 185 |
| 231 Rect clip_rect(text_bounds); | 186 Rect clip_rect(text_bounds); |
| 232 clip_rect.Inset(ShadowValue::GetMargin(shadows)); | 187 clip_rect.Inset(ShadowValue::GetMargin(shadows)); |
| 233 | 188 |
| 234 canvas_->save(); | 189 canvas_->save(); |
| 235 ClipRect(clip_rect); | 190 ClipRect(clip_rect); |
| 236 | 191 |
| 237 Rect rect(text_bounds); | 192 Rect rect(text_bounds); |
| 238 base::string16 adjusted_text = text; | |
| 239 | |
| 240 #if defined(OS_WIN) | |
| 241 AdjustStringDirection(flags, &adjusted_text); | |
| 242 #endif | |
| 243 | 193 |
| 244 scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); | 194 scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); |
| 245 render_text->set_shadows(shadows); | 195 render_text->set_shadows(shadows); |
| 246 | 196 |
| 247 if (flags & MULTI_LINE) { | 197 if (flags & MULTI_LINE) { |
| 248 WordWrapBehavior wrap_behavior = IGNORE_LONG_WORDS; | 198 WordWrapBehavior wrap_behavior = IGNORE_LONG_WORDS; |
| 249 if (flags & CHARACTER_BREAK) | 199 if (flags & CHARACTER_BREAK) |
| 250 wrap_behavior = WRAP_LONG_WORDS; | 200 wrap_behavior = WRAP_LONG_WORDS; |
| 251 else if (!(flags & NO_ELLIPSIS)) | 201 else if (!(flags & NO_ELLIPSIS)) |
| 252 wrap_behavior = ELIDE_LONG_WORDS; | 202 wrap_behavior = ELIDE_LONG_WORDS; |
| 253 | 203 |
| 254 std::vector<base::string16> strings; | 204 std::vector<base::string16> strings; |
| 255 ElideRectangleText(adjusted_text, font_list, | 205 ElideRectangleText(text, font_list, |
| 256 static_cast<float>(text_bounds.width()), | 206 static_cast<float>(text_bounds.width()), |
| 257 text_bounds.height(), wrap_behavior, &strings); | 207 text_bounds.height(), wrap_behavior, &strings); |
| 258 | 208 |
| 259 for (size_t i = 0; i < strings.size(); i++) { | 209 for (size_t i = 0; i < strings.size(); i++) { |
| 260 Range range = StripAcceleratorChars(flags, &strings[i]); | 210 Range range = StripAcceleratorChars(flags, &strings[i]); |
| 261 UpdateRenderText(rect, strings[i], font_list, flags, color, | 211 UpdateRenderText(rect, strings[i], font_list, flags, color, |
| 262 render_text.get()); | 212 render_text.get()); |
| 263 int line_padding = 0; | 213 int line_padding = 0; |
| 264 if (line_height > 0) | 214 if (line_height > 0) |
| 265 line_padding = line_height - render_text->GetStringSize().height(); | 215 line_padding = line_height - render_text->GetStringSize().height(); |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 277 | 227 |
| 278 rect.set_height(line_height - line_padding); | 228 rect.set_height(line_height - line_padding); |
| 279 | 229 |
| 280 if (range.IsValid()) | 230 if (range.IsValid()) |
| 281 render_text->ApplyStyle(UNDERLINE, true, range); | 231 render_text->ApplyStyle(UNDERLINE, true, range); |
| 282 render_text->SetDisplayRect(rect); | 232 render_text->SetDisplayRect(rect); |
| 283 render_text->Draw(this); | 233 render_text->Draw(this); |
| 284 rect += Vector2d(0, line_height); | 234 rect += Vector2d(0, line_height); |
| 285 } | 235 } |
| 286 } else { | 236 } else { |
| 237 base::string16 adjusted_text = text; | |
| 287 Range range = StripAcceleratorChars(flags, &adjusted_text); | 238 Range range = StripAcceleratorChars(flags, &adjusted_text); |
| 288 bool elide_text = ((flags & NO_ELLIPSIS) == 0); | 239 bool elide_text = ((flags & NO_ELLIPSIS) == 0); |
| 289 | 240 |
| 290 #if defined(OS_LINUX) | 241 #if defined(OS_LINUX) |
| 291 // On Linux, eliding really means fading the end of the string. But only | 242 // 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 "...". | 243 // for LTR text. RTL text is still elided (on the left) with "...". |
| 293 if (elide_text) { | 244 if (elide_text) { |
| 294 render_text->SetText(adjusted_text); | 245 render_text->SetText(adjusted_text); |
| 295 if (render_text->GetDisplayTextDirection() == base::i18n::LEFT_TO_RIGHT) { | 246 if (render_text->GetDisplayTextDirection() == base::i18n::LEFT_TO_RIGHT) { |
| 296 render_text->SetElideBehavior(FADE_TAIL); | 247 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, | 317 void Canvas::DrawFadedString(const base::string16& text, |
| 367 const FontList& font_list, | 318 const FontList& font_list, |
| 368 SkColor color, | 319 SkColor color, |
| 369 const Rect& display_rect, | 320 const Rect& display_rect, |
| 370 int flags) { | 321 int flags) { |
| 371 // If the whole string fits in the destination then just draw it directly. | 322 // If the whole string fits in the destination then just draw it directly. |
| 372 if (GetStringWidth(text, font_list) <= display_rect.width()) { | 323 if (GetStringWidth(text, font_list) <= display_rect.width()) { |
| 373 DrawStringRectWithFlags(text, font_list, color, display_rect, flags); | 324 DrawStringRectWithFlags(text, font_list, color, display_rect, flags); |
| 374 return; | 325 return; |
| 375 } | 326 } |
| 376 | 327 if (!(flags & |
| 377 // Align with forced content directionality, overriding alignment flags. | 328 (TEXT_ALIGN_LEFT | TEXT_ALIGN_RIGHT | TEXT_ALIGN_TO_HEAD))) { |
| 378 if (flags & FORCE_RTL_DIRECTIONALITY) { | 329 // align with content directionality instead of fading both ends. |
|
msw
2015/02/25 17:05:20
nit: Align
oshima
2015/03/04 08:01:50
Done.
| |
| 379 flags &= ~(TEXT_ALIGN_CENTER | TEXT_ALIGN_LEFT); | |
| 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; | 330 flags &= ~TEXT_ALIGN_CENTER; |
|
msw
2015/02/25 17:05:20
nit: refactor the code to unconditionally prevent
oshima
2015/03/04 08:01:50
Done.
| |
| 387 const bool is_rtl = base::i18n::GetFirstStrongCharacterDirection(text) == | 331 flags |= TEXT_ALIGN_TO_HEAD; |
| 388 base::i18n::RIGHT_TO_LEFT; | |
| 389 flags |= is_rtl ? TEXT_ALIGN_RIGHT : TEXT_ALIGN_LEFT; | |
| 390 } | 332 } |
| 391 flags |= NO_ELLIPSIS; | 333 flags |= NO_ELLIPSIS; |
| 392 | 334 |
| 393 scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); | 335 scoped_ptr<RenderText> render_text(RenderText::CreateInstance()); |
| 394 Rect rect = display_rect; | 336 Rect rect = display_rect; |
| 395 UpdateRenderText(rect, text, font_list, flags, color, render_text.get()); | 337 UpdateRenderText(rect, text, font_list, flags, color, render_text.get()); |
| 396 render_text->SetElideBehavior(FADE_TAIL); | 338 render_text->SetElideBehavior(FADE_TAIL); |
| 397 | 339 |
| 398 canvas_->save(); | 340 canvas_->save(); |
| 399 ClipRect(display_rect); | 341 ClipRect(display_rect); |
| 400 render_text->Draw(this); | 342 render_text->Draw(this); |
| 401 canvas_->restore(); | 343 canvas_->restore(); |
| 402 } | 344 } |
| 403 | 345 |
| 404 } // namespace gfx | 346 } // namespace gfx |
| OLD | NEW |