Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(307)

Side by Side Diff: gfx/canvas_win.cc

Issue 2862025: Canvas refactoring part 2.... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 10 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « gfx/canvas_skia_win.cc ('k') | gfx/gfx.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "gfx/canvas.h"
6
7 #include <limits>
8
9 #include "base/i18n/rtl.h"
10 #include "gfx/font.h"
11 #include "gfx/rect.h"
12 #include "third_party/skia/include/core/SkShader.h"
13
14 namespace {
15
16 // We make sure that LTR text we draw in an RTL context is modified
17 // appropriately to make sure it maintains it LTR orientation.
18 void DoDrawText(HDC hdc, const std::wstring& text,
19 RECT* text_bounds, int flags) {
20 std::wstring localized_text;
21 const wchar_t* string_ptr = text.c_str();
22 int string_size = static_cast<int>(text.length());
23 // Only adjust string directionality if both of the following are true:
24 // 1. The current locale is RTL.
25 // 2. The string itself has RTL directionality.
26 if (flags & DT_RTLREADING) {
27 if (base::i18n::AdjustStringForLocaleDirection(text, &localized_text)) {
28 string_ptr = localized_text.c_str();
29 string_size = static_cast<int>(localized_text.length());
30 }
31 }
32
33 DrawText(hdc, string_ptr, string_size, text_bounds, flags);
34 }
35
36 // Compute the windows flags necessary to implement the provided text Canvas
37 // flags.
38 int ComputeFormatFlags(int flags, const std::wstring& text) {
39 // Setting the text alignment explicitly in case it hasn't already been set.
40 // This will make sure that we don't align text to the left on RTL locales
41 // just because no alignment flag was passed to DrawStringInt().
42 if (!(flags & (gfx::Canvas::TEXT_ALIGN_CENTER |
43 gfx::Canvas::TEXT_ALIGN_RIGHT |
44 gfx::Canvas::TEXT_ALIGN_LEFT))) {
45 flags |= gfx::Canvas::DefaultCanvasTextAlignment();
46 }
47
48 // horizontal alignment
49 int f = 0;
50 if (flags & gfx::Canvas::TEXT_ALIGN_CENTER)
51 f |= DT_CENTER;
52 else if (flags & gfx::Canvas::TEXT_ALIGN_RIGHT)
53 f |= DT_RIGHT;
54 else
55 f |= DT_LEFT;
56
57 // vertical alignment
58 if (flags & gfx::Canvas::TEXT_VALIGN_TOP)
59 f |= DT_TOP;
60 else if (flags & gfx::Canvas::TEXT_VALIGN_BOTTOM)
61 f |= DT_BOTTOM;
62 else
63 f |= DT_VCENTER;
64
65 if (flags & gfx::Canvas::MULTI_LINE) {
66 f |= DT_WORDBREAK;
67 if (flags & gfx::Canvas::CHARACTER_BREAK)
68 f |= DT_EDITCONTROL; // Turns on character breaking (not documented)
69 else if (!(flags & gfx::Canvas::NO_ELLIPSIS))
70 f |= DT_WORD_ELLIPSIS;
71 } else {
72 f |= DT_SINGLELINE;
73 }
74
75 if (flags & gfx::Canvas::HIDE_PREFIX)
76 f |= DT_HIDEPREFIX;
77 else if ((flags & gfx::Canvas::SHOW_PREFIX) == 0)
78 f |= DT_NOPREFIX;
79
80 if (!(flags & gfx::Canvas::NO_ELLIPSIS))
81 f |= DT_END_ELLIPSIS;
82
83 // In order to make sure RTL/BiDi strings are rendered correctly, we must
84 // pass the flag DT_RTLREADING to DrawText (when the locale's language is
85 // a right-to-left language) so that Windows does the right thing.
86 //
87 // In addition to correctly displaying text containing both RTL and LTR
88 // elements (for example, a string containing a telephone number within a
89 // sentence in Hebrew, or a sentence in Hebrew that contains a word in
90 // English) this flag also makes sure that if there is not enough space to
91 // display the entire string, the ellipsis is displayed on the left hand side
92 // of the truncated string and not on the right hand side.
93 //
94 // We make a distinction between Chrome UI strings and text coming from a web
95 // page.
96 //
97 // For text coming from a web page we determine the alignment based on the
98 // first character with strong directionality. If the directionality of the
99 // first character with strong directionality in the text is LTR, the
100 // alignment is set to DT_LEFT, and the directionality should not be set as
101 // DT_RTLREADING.
102 //
103 // This heuristic doesn't work for Chrome UI strings since even in RTL
104 // locales, some of those might start with English text but we know they're
105 // localized so we always want them to be right aligned, and their
106 // directionality should be set as DT_RTLREADING.
107 //
108 // Caveat: If the string is purely LTR, don't set DTL_RTLREADING since when
109 // the flag is set, LRE-PDF don't have the desired effect of rendering
110 // multiline English-only text as LTR.
111 //
112 // Note that if the caller is explicitly requesting displaying the text
113 // using RTL directionality then we respect that and pass DT_RTLREADING to
114 // ::DrawText even if the locale is LTR.
115 if ((flags & gfx::Canvas::FORCE_RTL_DIRECTIONALITY) ||
116 (base::i18n::IsRTL() &&
117 (f & DT_RIGHT) && base::i18n::StringContainsStrongRTLChars(text))) {
118 f |= DT_RTLREADING;
119 }
120
121 return f;
122 }
123
124 } // anonymous namespace
125
126 namespace gfx {
127
128 Canvas::Canvas(int width, int height, bool is_opaque)
129 : skia::PlatformCanvas(width, height, is_opaque) {
130 }
131
132 Canvas::Canvas() : skia::PlatformCanvas() {
133 }
134
135 Canvas::~Canvas() {
136 }
137
138 // static
139 void Canvas::SizeStringInt(const std::wstring& text,
140 const gfx::Font& font,
141 int* width, int* height, int flags) {
142 // Clamp the max amount of text we'll measure to 2K. When the string is
143 // actually drawn, it will be clipped to whatever size box is provided, and
144 // the time to do that doesn't depend on the length being clipped off.
145 const int kMaxStringLength = 2048 - 1; // So the trailing \0 fits in 2K.
146 std::wstring clamped_string(text.substr(0, kMaxStringLength));
147
148 if (*width == 0) {
149 // If multi-line + character break are on, the computed width will be one
150 // character wide (useless). Furthermore, if in this case the provided text
151 // contains very long "words" (substrings without a word-breaking point),
152 // DrawText() can run extremely slowly (e.g. several seconds). So in this
153 // case, we turn character breaking off to get a more accurate "desired"
154 // width and avoid the slowdown.
155 if (flags & (gfx::Canvas::MULTI_LINE | gfx::Canvas::CHARACTER_BREAK))
156 flags &= ~gfx::Canvas::CHARACTER_BREAK;
157
158 // Weird undocumented behavior: if the width is 0, DoDrawText() won't
159 // calculate a size at all. So set it to 1, which it will then change.
160 if (!text.empty())
161 *width = 1;
162 }
163 RECT r = { 0, 0, *width, *height };
164
165 HDC dc = GetDC(NULL);
166 HFONT old_font = static_cast<HFONT>(SelectObject(dc, font.hfont()));
167 DoDrawText(dc, clamped_string, &r,
168 ComputeFormatFlags(flags, clamped_string) | DT_CALCRECT);
169 SelectObject(dc, old_font);
170 ReleaseDC(NULL, dc);
171
172 *width = r.right;
173 *height = r.bottom;
174 }
175
176 void Canvas::DrawStringInt(const std::wstring& text, HFONT font,
177 const SkColor& color, int x, int y, int w, int h,
178 int flags) {
179 if (!IntersectsClipRectInt(x, y, w, h))
180 return;
181
182 // Clamp the max amount of text we'll draw to 32K. There seem to be bugs in
183 // DrawText() if you e.g. ask it to character-break a no-whitespace string of
184 // length > 43680 (for which it draws nothing), and since we clamped to 2K in
185 // SizeStringInt() we're unlikely to be able to display this much anyway.
186 const int kMaxStringLength = 32768 - 1; // So the trailing \0 fits in 32K.
187 std::wstring clamped_string(text.substr(0, kMaxStringLength));
188
189 RECT text_bounds = { x, y, x + w, y + h };
190 HDC dc = beginPlatformPaint();
191 SetBkMode(dc, TRANSPARENT);
192 HFONT old_font = (HFONT)SelectObject(dc, font);
193 COLORREF brush_color = RGB(SkColorGetR(color), SkColorGetG(color),
194 SkColorGetB(color));
195 SetTextColor(dc, brush_color);
196
197 int f = ComputeFormatFlags(flags, clamped_string);
198 DoDrawText(dc, clamped_string, &text_bounds, f);
199 endPlatformPaint();
200
201 // Restore the old font. This way we don't have to worry if the caller
202 // deletes the font and the DC lives longer.
203 SelectObject(dc, old_font);
204
205 // Windows will have cleared the alpha channel of the text we drew. Assume
206 // we're drawing to an opaque surface, or at least the text rect area is
207 // opaque.
208 getTopPlatformDevice().makeOpaque(x, y, w, h);
209 }
210
211 void Canvas::DrawStringInt(const std::wstring& text,
212 const gfx::Font& font,
213 const SkColor& color,
214 int x, int y, int w, int h, int flags) {
215 DrawStringInt(text, font.hfont(), color, x, y, w, h, flags);
216 }
217
218 // Checks each pixel immediately adjacent to the given pixel in the bitmap. If
219 // any of them are not the halo color, returns true. This defines the halo of
220 // pixels that will appear around the text. Note that we have to check each
221 // pixel against both the halo color and transparent since DrawStringWithHalo
222 // will modify the bitmap as it goes, and clears pixels shouldn't count as
223 // changed.
224 static bool pixelShouldGetHalo(const SkBitmap& bitmap, int x, int y,
225 SkColor halo_color) {
226 if (x > 0 &&
227 *bitmap.getAddr32(x - 1, y) != halo_color &&
228 *bitmap.getAddr32(x - 1, y) != 0)
229 return true; // Touched pixel to the left.
230 if (x < bitmap.width() - 1 &&
231 *bitmap.getAddr32(x + 1, y) != halo_color &&
232 *bitmap.getAddr32(x + 1, y) != 0)
233 return true; // Touched pixel to the right.
234 if (y > 0 &&
235 *bitmap.getAddr32(x, y - 1) != halo_color &&
236 *bitmap.getAddr32(x, y - 1) != 0)
237 return true; // Touched pixel above.
238 if (y < bitmap.height() - 1 &&
239 *bitmap.getAddr32(x, y + 1) != halo_color &&
240 *bitmap.getAddr32(x, y + 1) != 0)
241 return true; // Touched pixel below.
242 return false;
243 }
244
245 void Canvas::DrawStringWithHalo(const std::wstring& text,
246 const gfx::Font& font,
247 const SkColor& text_color,
248 const SkColor& halo_color_in,
249 int x, int y, int w, int h,
250 int flags) {
251 // Some callers will have semitransparent halo colors, which we don't handle
252 // (since the resulting image can have 1-bit transparency only).
253 SkColor halo_color = halo_color_in | 0xFF000000;
254
255 // Create a temporary buffer filled with the halo color. It must leave room
256 // for the 1-pixel border around the text.
257 Canvas text_canvas(w + 2, h + 2, true);
258 SkPaint bkgnd_paint;
259 bkgnd_paint.setColor(halo_color);
260 text_canvas.FillRectInt(0, 0, w + 2, h + 2, bkgnd_paint);
261
262 // Draw the text into the temporary buffer. This will have correct
263 // ClearType since the background color is the same as the halo color.
264 text_canvas.DrawStringInt(text, font, text_color, 1, 1, w, h, flags);
265
266 // Windows will have cleared the alpha channel for the pixels it drew. Make it
267 // opaque. We have to do this first since pixelShouldGetHalo will check for
268 // 0 to see if a pixel has been modified to transparent, and black text that
269 // Windows draw will look transparent to it!
270 text_canvas.getTopPlatformDevice().makeOpaque(0, 0, w + 2, h + 2);
271
272 uint32_t halo_premul = SkPreMultiplyColor(halo_color);
273 SkBitmap& text_bitmap = const_cast<SkBitmap&>(
274 text_canvas.getTopPlatformDevice().accessBitmap(true));
275 for (int cur_y = 0; cur_y < h + 2; cur_y++) {
276 uint32_t* text_row = text_bitmap.getAddr32(0, cur_y);
277 for (int cur_x = 0; cur_x < w + 2; cur_x++) {
278 if (text_row[cur_x] == halo_premul) {
279 // This pixel was not touched by the text routines. See if it borders
280 // a touched pixel in any of the 4 directions (not diagonally).
281 if (!pixelShouldGetHalo(text_bitmap, cur_x, cur_y, halo_premul))
282 text_row[cur_x] = 0; // Make transparent.
283 } else {
284 text_row[cur_x] |= 0xff << SK_A32_SHIFT; // Make opaque.
285 }
286 }
287 }
288
289 // Draw the halo bitmap with blur.
290 drawBitmap(text_bitmap, SkIntToScalar(x - 1), SkIntToScalar(y - 1));
291 }
292
293 } // namespace gfx
OLDNEW
« no previous file with comments | « gfx/canvas_skia_win.cc ('k') | gfx/gfx.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698