Correctly track texture cleared state for sharing
[chromium-blink-merge.git] / ui / gfx / render_text_mac.cc
blob912382f56dcc8bd16cfe7105963e932e17ef4e41
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.
5 #include "ui/gfx/render_text_mac.h"
7 #include <ApplicationServices/ApplicationServices.h>
9 #include <algorithm>
10 #include <cmath>
11 #include <utility>
13 #include "base/mac/foundation_util.h"
14 #include "base/mac/scoped_cftyperef.h"
15 #include "base/strings/sys_string_conversions.h"
16 #include "skia/ext/skia_utils_mac.h"
18 namespace gfx {
20 RenderTextMac::RenderTextMac() : common_baseline_(0), runs_valid_(false) {
23 RenderTextMac::~RenderTextMac() {
26 scoped_ptr<RenderText> RenderTextMac::CreateInstanceOfSameType() const {
27 return scoped_ptr<RenderTextMac>(new RenderTextMac);
30 Size RenderTextMac::GetStringSize() {
31 EnsureLayout();
32 return Size(std::ceil(string_size_.width()), string_size_.height());
35 SizeF RenderTextMac::GetStringSizeF() {
36 EnsureLayout();
37 return string_size_;
40 SelectionModel RenderTextMac::FindCursorPosition(const Point& point) {
41 // TODO(asvitkine): Implement this. http://crbug.com/131618
42 return SelectionModel();
45 std::vector<RenderText::FontSpan> RenderTextMac::GetFontSpansForTesting() {
46 EnsureLayout();
47 if (!runs_valid_)
48 ComputeRuns();
50 std::vector<RenderText::FontSpan> spans;
51 for (size_t i = 0; i < runs_.size(); ++i) {
52 Font font(runs_[i].font_name, runs_[i].text_size);
53 const CFRange cf_range = CTRunGetStringRange(runs_[i].ct_run);
54 const Range range(cf_range.location, cf_range.location + cf_range.length);
55 spans.push_back(RenderText::FontSpan(font, range));
58 return spans;
61 int RenderTextMac::GetLayoutTextBaseline() {
62 EnsureLayout();
63 return common_baseline_;
66 SelectionModel RenderTextMac::AdjacentCharSelectionModel(
67 const SelectionModel& selection,
68 VisualCursorDirection direction) {
69 // TODO(asvitkine): Implement this. http://crbug.com/131618
70 return SelectionModel();
73 SelectionModel RenderTextMac::AdjacentWordSelectionModel(
74 const SelectionModel& selection,
75 VisualCursorDirection direction) {
76 // TODO(asvitkine): Implement this. http://crbug.com/131618
77 return SelectionModel();
80 Range RenderTextMac::GetGlyphBounds(size_t index) {
81 // TODO(asvitkine): Implement this. http://crbug.com/131618
82 return Range();
85 std::vector<Rect> RenderTextMac::GetSubstringBounds(const Range& range) {
86 // TODO(asvitkine): Implement this. http://crbug.com/131618
87 return std::vector<Rect>();
90 size_t RenderTextMac::TextIndexToLayoutIndex(size_t index) const {
91 // TODO(asvitkine): Implement this. http://crbug.com/131618
92 return index;
95 size_t RenderTextMac::LayoutIndexToTextIndex(size_t index) const {
96 // TODO(asvitkine): Implement this. http://crbug.com/131618
97 return index;
100 bool RenderTextMac::IsValidCursorIndex(size_t index) {
101 // TODO(asvitkine): Implement this. http://crbug.com/131618
102 return IsValidLogicalIndex(index);
105 void RenderTextMac::ResetLayout() {
106 line_.reset();
107 attributes_.reset();
108 runs_.clear();
109 runs_valid_ = false;
112 void RenderTextMac::EnsureLayout() {
113 if (line_.get())
114 return;
115 runs_.clear();
116 runs_valid_ = false;
118 CTFontRef ct_font = base::mac::NSToCFCast(
119 font_list().GetPrimaryFont().GetNativeFont());
121 const void* keys[] = { kCTFontAttributeName };
122 const void* values[] = { ct_font };
123 base::ScopedCFTypeRef<CFDictionaryRef> attributes(
124 CFDictionaryCreate(NULL,
125 keys,
126 values,
127 arraysize(keys),
128 NULL,
129 &kCFTypeDictionaryValueCallBacks));
131 base::ScopedCFTypeRef<CFStringRef> cf_text(
132 base::SysUTF16ToCFStringRef(text()));
133 base::ScopedCFTypeRef<CFAttributedStringRef> attr_text(
134 CFAttributedStringCreate(NULL, cf_text, attributes));
135 base::ScopedCFTypeRef<CFMutableAttributedStringRef> attr_text_mutable(
136 CFAttributedStringCreateMutableCopy(NULL, 0, attr_text));
138 // TODO(asvitkine|msw): Respect GetTextDirection(), which may not match the
139 // natural text direction. See kCTTypesetterOptionForcedEmbeddingLevel, etc.
141 ApplyStyles(attr_text_mutable, ct_font);
142 line_.reset(CTLineCreateWithAttributedString(attr_text_mutable));
144 CGFloat ascent = 0;
145 CGFloat descent = 0;
146 CGFloat leading = 0;
147 // TODO(asvitkine): Consider using CTLineGetBoundsWithOptions() on 10.8+.
148 double width = CTLineGetTypographicBounds(line_, &ascent, &descent, &leading);
149 // Ensure ascent and descent are not smaller than ones of the font list.
150 // Keep them tall enough to draw often-used characters.
151 // For example, if a text field contains a Japanese character, which is
152 // smaller than Latin ones, and then later a Latin one is inserted, this
153 // ensures that the text baseline does not shift.
154 CGFloat font_list_height = font_list().GetHeight();
155 CGFloat font_list_baseline = font_list().GetBaseline();
156 ascent = std::max(ascent, font_list_baseline);
157 descent = std::max(descent, font_list_height - font_list_baseline);
158 string_size_ = SizeF(width, ascent + descent + leading);
159 common_baseline_ = ascent;
162 void RenderTextMac::DrawVisualText(Canvas* canvas) {
163 DCHECK(line_);
164 if (!runs_valid_)
165 ComputeRuns();
167 internal::SkiaTextRenderer renderer(canvas);
168 ApplyFadeEffects(&renderer);
169 ApplyTextShadows(&renderer);
171 for (size_t i = 0; i < runs_.size(); ++i) {
172 const TextRun& run = runs_[i];
173 renderer.SetForegroundColor(run.foreground);
174 renderer.SetTextSize(run.text_size);
175 renderer.SetFontFamilyWithStyle(run.font_name, run.font_style);
176 renderer.DrawPosText(&run.glyph_positions[0], &run.glyphs[0],
177 run.glyphs.size());
178 renderer.DrawDecorations(run.origin.x(), run.origin.y(), run.width,
179 run.underline, run.strike, run.diagonal_strike);
182 renderer.EndDiagonalStrike();
185 RenderTextMac::TextRun::TextRun()
186 : ct_run(NULL),
187 origin(SkPoint::Make(0, 0)),
188 width(0),
189 font_style(Font::NORMAL),
190 text_size(0),
191 foreground(SK_ColorBLACK),
192 underline(false),
193 strike(false),
194 diagonal_strike(false) {
197 RenderTextMac::TextRun::~TextRun() {
200 void RenderTextMac::ApplyStyles(CFMutableAttributedStringRef attr_string,
201 CTFontRef font) {
202 // Temporarily apply composition underlines and selection colors.
203 ApplyCompositionAndSelectionStyles();
205 // Note: CFAttributedStringSetAttribute() does not appear to retain the values
206 // passed in, as can be verified via CFGetRetainCount(). To ensure the
207 // attribute objects do not leak, they are saved to |attributes_|.
208 // Clear the attributes storage.
209 attributes_.reset(CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks));
211 // https://developer.apple.com/library/mac/#documentation/Carbon/Reference/CoreText_StringAttributes_Ref/Reference/reference.html
212 internal::StyleIterator style(colors(), styles());
213 const size_t layout_text_length = GetLayoutText().length();
214 for (size_t i = 0, end = 0; i < layout_text_length; i = end) {
215 end = TextIndexToLayoutIndex(style.GetRange().end());
216 const CFRange range = CFRangeMake(i, end - i);
217 base::ScopedCFTypeRef<CGColorRef> foreground(
218 CGColorCreateFromSkColor(style.color()));
219 CFAttributedStringSetAttribute(attr_string, range,
220 kCTForegroundColorAttributeName, foreground);
221 CFArrayAppendValue(attributes_, foreground);
223 if (style.style(UNDERLINE)) {
224 CTUnderlineStyle value = kCTUnderlineStyleSingle;
225 base::ScopedCFTypeRef<CFNumberRef> underline_value(
226 CFNumberCreate(NULL, kCFNumberSInt32Type, &value));
227 CFAttributedStringSetAttribute(attr_string, range,
228 kCTUnderlineStyleAttributeName,
229 underline_value);
230 CFArrayAppendValue(attributes_, underline_value);
233 const int traits = (style.style(BOLD) ? kCTFontBoldTrait : 0) |
234 (style.style(ITALIC) ? kCTFontItalicTrait : 0);
235 if (traits != 0) {
236 base::ScopedCFTypeRef<CTFontRef> styled_font(
237 CTFontCreateCopyWithSymbolicTraits(font, 0.0, NULL, traits, traits));
238 // TODO(asvitkine): Handle |styled_font| == NULL case better.
239 if (styled_font) {
240 CFAttributedStringSetAttribute(attr_string, range, kCTFontAttributeName,
241 styled_font);
242 CFArrayAppendValue(attributes_, styled_font);
246 style.UpdatePosition(LayoutIndexToTextIndex(end));
249 // Undo the temporarily applied composition underlines and selection colors.
250 UndoCompositionAndSelectionStyles();
253 void RenderTextMac::ComputeRuns() {
254 DCHECK(line_);
256 CFArrayRef ct_runs = CTLineGetGlyphRuns(line_);
257 const CFIndex ct_runs_count = CFArrayGetCount(ct_runs);
259 // TODO(asvitkine): Don't use GetLineOffset() until draw time, since it may be
260 // updated based on alignment changes without resetting the layout.
261 Vector2d text_offset = GetLineOffset(0);
262 // Skia will draw glyphs with respect to the baseline.
263 text_offset += Vector2d(0, common_baseline_);
265 const SkScalar x = SkIntToScalar(text_offset.x());
266 const SkScalar y = SkIntToScalar(text_offset.y());
267 SkPoint run_origin = SkPoint::Make(x, y);
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;
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 // CTRunGetGlyphs() sometimes returns glyphs with value 65535 and zero
289 // width (this has been observed at the beginning of a string containing
290 // Arabic content). Passing these to Skia will trigger an assertion;
291 // instead set their values to 0.
292 for (size_t glyph = 0; glyph < glyph_count; glyph++) {
293 if (run->glyphs[glyph] == 65535)
294 run->glyphs[glyph] = 0;
297 run->glyph_positions.resize(glyph_count);
298 const CGPoint* positions_ptr = CTRunGetPositionsPtr(ct_run);
299 std::vector<CGPoint> positions;
300 if (positions_ptr == NULL) {
301 positions.resize(glyph_count);
302 CTRunGetPositions(ct_run, empty_cf_range, &positions[0]);
303 positions_ptr = &positions[0];
305 for (size_t glyph = 0; glyph < glyph_count; glyph++) {
306 SkPoint* point = &run->glyph_positions[glyph];
307 point->set(x + SkDoubleToScalar(positions_ptr[glyph].x),
308 y + SkDoubleToScalar(positions_ptr[glyph].y));
311 // TODO(asvitkine): Style boundaries are not necessarily per-run. Handle
312 // this better. Also, support strike and diagonal_strike.
313 CFDictionaryRef attributes = CTRunGetAttributes(ct_run);
314 CTFontRef ct_font =
315 base::mac::GetValueFromDictionary<CTFontRef>(attributes,
316 kCTFontAttributeName);
317 base::ScopedCFTypeRef<CFStringRef> font_name_ref(
318 CTFontCopyFamilyName(ct_font));
319 run->font_name = base::SysCFStringRefToUTF8(font_name_ref);
320 run->text_size = CTFontGetSize(ct_font);
322 CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(ct_font);
323 if (traits & kCTFontBoldTrait)
324 run->font_style |= Font::BOLD;
325 if (traits & kCTFontItalicTrait)
326 run->font_style |= Font::ITALIC;
328 const CGColorRef foreground =
329 base::mac::GetValueFromDictionary<CGColorRef>(
330 attributes, kCTForegroundColorAttributeName);
331 if (foreground)
332 run->foreground = CGColorRefToSkColor(foreground);
334 const CFNumberRef underline =
335 base::mac::GetValueFromDictionary<CFNumberRef>(
336 attributes, kCTUnderlineStyleAttributeName);
337 CTUnderlineStyle value = kCTUnderlineStyleNone;
338 if (underline && CFNumberGetValue(underline, kCFNumberSInt32Type, &value))
339 run->underline = (value == kCTUnderlineStyleSingle);
341 run_origin.offset(run_width, 0);
343 runs_valid_ = true;
346 RenderText* RenderText::CreateNativeInstance() {
347 return new RenderTextMac;
350 } // namespace gfx