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

Side by Side Diff: ui/gfx/render_text_mac.cc

Issue 10543057: Initial RenderTextMac implementation using CoreText. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 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
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "ui/gfx/render_text_mac.h"
6
7 #include <ApplicationServices/ApplicationServices.h>
8
9 #include <cmath>
10 #include <utility>
11
12 #include "base/mac/foundation_util.h"
13 #include "base/mac/scoped_cftyperef.h"
14 #include "base/sys_string_conversions.h"
15 #include "skia/ext/skia_utils_mac.h"
16
17 namespace {
18
19 // Returns the pixel height of |ct_font|.
Nico 2012/06/18 15:48:01 View units height, not pixel height, I think.
20 CGFloat GetCTFontPixelSize(CTFontRef ct_font) {
21 return CTFontGetAscent(ct_font) + CTFontGetDescent(ct_font);
22 }
23
24 // Creates a CTFont with the given font name and pixel size. Ownership is
Nico 2012/06/18 15:48:01 Again, not pixel size I think. (You don't want to
25 // transferred to the caller.
26 CTFontRef CreateCTFontWithPixelSize(const std::string& font_name,
27 const int target_pixel_size) {
28 // Epsilon value used for comparing font sizes.
29 const CGFloat kEpsilon = 0.001;
30 // The observed pixel to points ratio for Lucida Grande on 10.6. Other fonts
31 // have other ratios and the documentation doesn't provide a guarantee that
32 // the relation is linear. So this ratio is used as a first try before
33 // falling back to the bisection method.
34 const CGFloat kPixelsToPointsRatio = 0.849088;
35
36 base::mac::ScopedCFTypeRef<CFStringRef> font_name_cf_string(
37 base::SysUTF8ToCFStringRef(font_name));
38
39 // First, try using |kPixelsToPointsRatio|.
40 CGFloat point_size = target_pixel_size * kPixelsToPointsRatio;
41 base::mac::ScopedCFTypeRef<CTFontRef> ct_font(
42 CTFontCreateWithName(font_name_cf_string, point_size, NULL));
43 CGFloat actual_pixel_size = GetCTFontPixelSize(ct_font);
44 if (std::fabs(actual_pixel_size - target_pixel_size) < kEpsilon)
45 return ct_font.release();
46
47 // |kPixelsToPointsRatio| wasn't correct. Use the bisection method to find the
48 // right size.
49
50 // First, find the initial bisection range, so that the point size that
51 // corresponds to |target_pixel_size| is between |lo| and |hi|.
52 CGFloat lo = 0;
53 CGFloat hi = point_size;
54 while (actual_pixel_size < target_pixel_size) {
55 lo = hi;
56 hi *= 2;
57 ct_font.reset(CTFontCreateWithName(font_name_cf_string, hi, NULL));
58 actual_pixel_size = GetCTFontPixelSize(ct_font);
59 }
60
61 // Now, bisect to find the right size.
62 while (lo < hi) {
63 point_size = (hi - lo) * 0.5 + lo;
64 ct_font.reset(CTFontCreateWithName(font_name_cf_string, point_size, NULL));
65 actual_pixel_size = GetCTFontPixelSize(ct_font);
66 if (std::fabs(actual_pixel_size - target_pixel_size) < kEpsilon)
67 break;
68 if (target_pixel_size > actual_pixel_size)
69 lo = point_size;
70 else
71 hi = point_size;
72 }
73
74 return ct_font.release();
75 }
76
77 } // namespace
78
79 namespace gfx {
80
81 RenderTextMac::RenderTextMac() : common_baseline_(0), runs_valid_(false) {
82 }
83
84 RenderTextMac::~RenderTextMac() {
85 }
86
87 base::i18n::TextDirection RenderTextMac::GetTextDirection() {
88 return base::i18n::LEFT_TO_RIGHT;
89 }
90
91 Size RenderTextMac::GetStringSize() {
92 EnsureLayout();
93 return string_size_;
94 }
95
96 SelectionModel RenderTextMac::FindCursorPosition(const Point& point) {
97 // TODO(asvitkine): Implement this. http://crbug.com/131618
98 return SelectionModel();
99 }
100
101 std::vector<RenderText::FontSpan> RenderTextMac::GetFontSpansForTesting() {
102 EnsureLayout();
103 if (!runs_valid_)
104 ComputeRuns();
105
106 std::vector<RenderText::FontSpan> spans;
107 for (size_t i = 0; i < runs_.size(); ++i) {
108 gfx::Font font(runs_[i].font_name, runs_[i].text_size);
109 const CFRange cf_range = CTRunGetStringRange(runs_[i].ct_run);
110 const ui::Range range(cf_range.location,
111 cf_range.location + cf_range.length);
112 spans.push_back(RenderText::FontSpan(font, range));
113 }
114
115 return spans;
116 }
117
118 SelectionModel RenderTextMac::AdjacentCharSelectionModel(
119 const SelectionModel& selection,
120 VisualCursorDirection direction) {
121 // TODO(asvitkine): Implement this. http://crbug.com/131618
122 return SelectionModel();
123 }
124
125 SelectionModel RenderTextMac::AdjacentWordSelectionModel(
126 const SelectionModel& selection,
127 VisualCursorDirection direction) {
128 // TODO(asvitkine): Implement this. http://crbug.com/131618
129 return SelectionModel();
130 }
131
132 void RenderTextMac::GetGlyphBounds(size_t index,
133 ui::Range* xspan,
134 int* height) {
135 // TODO(asvitkine): Implement this. http://crbug.com/131618
136 }
137
138 std::vector<Rect> RenderTextMac::GetSubstringBounds(ui::Range range) {
139 // TODO(asvitkine): Implement this. http://crbug.com/131618
140 return std::vector<Rect>();
141 }
142
143 bool RenderTextMac::IsCursorablePosition(size_t position) {
144 // TODO(asvitkine): Implement this. http://crbug.com/131618
145 return false;
146 }
147
148 void RenderTextMac::ResetLayout() {
149 line_.reset();
150 runs_.clear();
151 runs_valid_ = false;
152 }
153
154 void RenderTextMac::EnsureLayout() {
155 if (line_.get())
156 return;
157 runs_.clear();
158 runs_valid_ = false;
159
160 const Font& font = GetFont();
161 CTFontRef ct_font =
162 CreateCTFontWithPixelSize(font.GetFontName(), font.GetFontSize());
163
164 const void* keys[] = { kCTFontAttributeName };
165 const void* values[] = { ct_font };
166 base::mac::ScopedCFTypeRef<CFDictionaryRef> attributes(
167 CFDictionaryCreate(NULL, keys, values, arraysize(keys), NULL, NULL));
168
169 base::mac::ScopedCFTypeRef<CFStringRef> cf_text(
170 base::SysUTF16ToCFStringRef(text()));
171 base::mac::ScopedCFTypeRef<CFAttributedStringRef> attr_text(
172 CFAttributedStringCreate(NULL, cf_text, attributes));
173 base::mac::ScopedCFTypeRef<CFMutableAttributedStringRef> attr_text_mutable(
174 CFAttributedStringCreateMutableCopy(NULL, 0, attr_text));
175
176 ApplyStyles(attr_text_mutable, ct_font);
177 line_.reset(CTLineCreateWithAttributedString(attr_text_mutable));
178
179 CGFloat ascent = 0;
180 CGFloat descent = 0;
181 CGFloat leading = 0;
182 double width = CTLineGetTypographicBounds(line_, &ascent, &descent, &leading);
183 string_size_ = Size(width, ascent + descent + leading);
184 common_baseline_ = ascent;
185 }
186
187 void RenderTextMac::DrawVisualText(Canvas* canvas) {
188 DCHECK(line_);
189 if (!runs_valid_)
190 ComputeRuns();
191
192 internal::SkiaTextRenderer renderer(canvas);
193 ApplyFadeEffects(&renderer);
194 ApplyTextShadows(&renderer);
195
196 for (size_t i = 0; i < runs_.size(); ++i) {
197 const TextRun& run = runs_[i];
198 renderer.SetForegroundColor(run.foreground);
199 renderer.SetTextSize(run.text_size);
200 renderer.SetFontFamilyWithStyle(run.font_name, run.font_style);
201 renderer.DrawPosText(&run.glyph_positions[0], &run.glyphs[0],
202 run.glyphs.size());
203 renderer.DrawDecorations(run.origin.x(), run.origin.y(), run.width,
204 run.style);
205 }
206 }
207
208 RenderTextMac::TextRun::TextRun()
209 : ct_run(NULL),
210 origin(SkPoint::Make(0, 0)),
211 width(0),
212 font_style(Font::NORMAL),
213 text_size(0),
214 foreground(SK_ColorBLACK) {
215 }
216
217 RenderTextMac::TextRun::~TextRun() {
218 }
219
220 void RenderTextMac::ApplyStyles(CFMutableAttributedStringRef attr_string,
221 CTFontRef font) {
222 // https://developer.apple.com/library/mac/#documentation/Carbon/Reference/Cor eText_StringAttributes_Ref/Reference/reference.html
223 for (size_t i = 0; i < style_ranges().size(); ++i) {
224 const StyleRange& style = style_ranges()[i];
225 const CFRange range = CFRangeMake(style.range.start(),
226 style.range.length());
227
228 CGColorRef foreground = gfx::SkColorToCGColorRef(style.foreground);
229 CFAttributedStringSetAttribute(attr_string, range,
230 kCTForegroundColorAttributeName,
231 foreground);
232
233 if (style.font_style & Font::UNDERLINED) {
234 CTUnderlineStyle value = kCTUnderlineStyleSingle;
235 CFNumberRef underline = CFNumberCreate(NULL, kCFNumberSInt32Type, &value);
236 CFAttributedStringSetAttribute(attr_string, range,
237 kCTUnderlineStyleAttributeName,
238 underline);
239 }
240
241 if (style.font_style & (Font::BOLD | Font::ITALIC)) {
242 int traits = 0;
243 if (style.font_style & Font::BOLD)
244 traits |= kCTFontBoldTrait;
245 if (style.font_style & Font::ITALIC)
246 traits |= kCTFontItalicTrait;
247 CTFontRef styled_font =
248 CTFontCreateCopyWithSymbolicTraits(font, 0.0, NULL, traits, traits);
249 CFAttributedStringSetAttribute(attr_string, range, kCTFontAttributeName,
250 styled_font);
251 }
252 }
253 }
254
255 void RenderTextMac::ComputeRuns() {
256 DCHECK(line_);
257
258 CFArrayRef ct_runs = CTLineGetGlyphRuns(line_);
259 const CFIndex ct_runs_count = CFArrayGetCount(ct_runs);
260
261 Point offset(GetTextOrigin());
262 // Skia will draw glyphs with respect to the baseline.
263 offset.Offset(0, common_baseline_);
264
265 const SkScalar x = SkIntToScalar(offset.x());
266 const SkScalar y = SkIntToScalar(offset.y());
267 SkPoint run_origin = SkPoint::Make(offset.x(), offset.y());
268
269 const CFRange empty_cf_range = CFRangeMake(0, 0);
270 for (CFIndex i = 0; i < ct_runs_count; ++i) {
271 CTRunRef ct_run =
272 base::mac::CFCast<CTRunRef>(CFArrayGetValueAtIndex(ct_runs, i));
273 const size_t glyph_count = CTRunGetGlyphCount(ct_run);
274 const double run_width =
275 CTRunGetTypographicBounds(ct_run, empty_cf_range, NULL, NULL, NULL);
276 if (glyph_count == 0) {
277 run_origin.offset(run_width, 0);
278 continue;
279 }
280
281 runs_.push_back(TextRun());
282 TextRun* run = &runs_.back();
283 run->ct_run = ct_run;
284 run->origin = run_origin;
285 run->width = run_width;
286 run->glyphs.resize(glyph_count);
287 CTRunGetGlyphs(ct_run, empty_cf_range, &run->glyphs[0]);
288
289 run->glyph_positions.resize(glyph_count);
290 const CGPoint* positions_ptr = CTRunGetPositionsPtr(ct_run);
291 std::vector<CGPoint> positions;
292 if (positions_ptr == NULL) {
293 positions.resize(glyph_count);
294 CTRunGetPositions(ct_run, empty_cf_range, &positions[0]);
295 positions_ptr = &positions[0];
296 }
297 for (size_t glyph = 0; glyph < glyph_count; glyph++) {
298 SkPoint* point = &run->glyph_positions[glyph];
299 point->set(x + SkDoubleToScalar(positions_ptr[glyph].x),
300 y + SkDoubleToScalar(positions_ptr[glyph].y));
301 }
302
303 CFDictionaryRef attributes = CTRunGetAttributes(ct_run);
304 CTFontRef ct_font =
305 base::mac::GetValueFromDictionary<CTFontRef>(attributes,
306 kCTFontAttributeName);
307 base::mac::ScopedCFTypeRef<CFStringRef> font_name_ref(
308 CTFontCopyFamilyName(ct_font));
309 run->font_name = base::SysCFStringRefToUTF8(font_name_ref);
310 run->text_size = GetCTFontPixelSize(ct_font);
311
312 CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(ct_font);
313 if (traits & kCTFontBoldTrait)
314 run->font_style |= Font::BOLD;
315 if (traits & kCTFontItalicTrait)
316 run->font_style |= Font::ITALIC;
317
318 const CGColorRef foreground =
319 base::mac::GetValueFromDictionary<CGColorRef>(
320 attributes, kCTForegroundColorAttributeName);
321 run->foreground = gfx::CGColorRefToSkColor(foreground);
322
323 const CFNumberRef underline =
324 base::mac::GetValueFromDictionary<CFNumberRef>(
325 attributes, kCTUnderlineStyleAttributeName);
326 CTUnderlineStyle value = kCTUnderlineStyleNone;
327 if (underline && CFNumberGetValue(underline, kCFNumberSInt32Type, &value))
328 run->style.underline = (value == kCTUnderlineStyleSingle);
329
330 run_origin.offset(run_width, 0);
331 }
332 runs_valid_ = true;
333 }
334
335 RenderText* RenderText::CreateRenderText() {
336 return new RenderTextMac;
337 }
338
339 } // namespace gfx
OLDNEW
« no previous file with comments | « ui/gfx/render_text_mac.h ('k') | ui/gfx/render_text_unittest.cc » ('j') | ui/ui.gyp » ('J')

Powered by Google App Engine
This is Rietveld 408576698