22 #if defined(ION_PLATFORM_MAC)
23 #import <AppKit/AppKit.h>
27 #else // defined(ION_PLATFORM_MAC)
28 #import <CoreText/CoreText.h>
29 #import <UIKit/UIKit.h>
33 #endif // defined(ION_PLATFORM_MAC)
57 static const CGFloat kPathSizePixels = 10000;
68 static Transform CalculateLayoutOptionsTransform(
71 const CGPoint* line_origins,
72 const LayoutOptions& options,
76 for (CFIndex line_index = 0; line_index <
line_count; ++line_index) {
77 CTLineRef line = (CTLineRef)CFArrayGetValueAtIndex(lines, line_index);
78 CGRect line_bounds = CTLineGetBoundsWithOptions(
79 line, kCTLineBoundsUseGlyphPathBounds);
80 line_bounds.origin.x += line_origins[line_index].x;
81 line_bounds.origin.y += line_origins[line_index].y;
82 line_bounds_range.ExtendByPoint(Point2d(CGRectGetMinX(line_bounds),
83 CGRectGetMinY(line_bounds)));
84 line_bounds_range.ExtendByPoint(Point2d(CGRectGetMaxX(line_bounds),
85 CGRectGetMaxY(line_bounds)));
92 const double rect_height =
93 font->GetFontMetrics().line_advance_height * (line_count - 1) + font->GetSizeInPixels();
97 Vector2d scale_for_target_size;
98 if (options.target_size[0] == 0.0f) {
100 const double s = options.target_size[1] / rect_height;
101 scale_for_target_size.Set(s, s);
102 }
else if (options.target_size[1] == 0.0f) {
104 const double s = options.target_size[0] / line_bounds_range.GetSize()[0];
105 scale_for_target_size.Set(s, s);
107 scale_for_target_size.Set(options.target_size[0] / line_bounds_range.GetSize()[0],
108 options.target_size[1] / rect_height);
113 Vector2d translation_for_path;
118 switch (options.horizontal_alignment) {
120 translation_for_path[0] = 0;
123 translation_for_path[0] = -kPathSizePixels * 0.5;
126 translation_for_path[0] = -kPathSizePixels;
131 switch (options.vertical_alignment) {
133 translation_for_path[1] = -line_bounds_range.GetMaxPoint()[1];
136 translation_for_path[1] =
137 -(line_bounds_range.GetMinPoint()[1] + line_bounds_range.GetMaxPoint()[1]) * 0.5;
140 translation_for_path[1] = -line_origins[0].y;
143 translation_for_path[1] = -line_bounds_range.GetMinPoint()[1];
150 result.scale = scale_for_target_size;
151 result.translation[0] =
152 translation_for_path[0] * scale_for_target_size[0] + options.target_point[0];
153 result.translation[1] =
154 translation_for_path[1] * scale_for_target_size[1] + options.target_point[1];
159 static const Layout::Quad BuildXyQuad(
const Range2d& rect) {
160 const Point2f& min(rect.GetMinPoint());
161 const Point2f& max(rect.GetMaxPoint());
162 return Layout::Quad(Point3f(min[0], min[1], 0.0f),
163 Point3f(max[0], min[1], 0.0f),
164 Point3f(max[0], max[1], 0.0f),
165 Point3f(min[0], max[1], 0.0f));
169 static Point2d ApplyTransform(
const Transform& transform,
170 const Point2d& point) {
171 return Point2d(point[0] * transform.scale[0] + transform.translation[0],
172 point[1] * transform.scale[1] + transform.translation[1]);
181 static GlyphIndex CGGlyphFontIndexToGlyphIndex(CGGlyph glyph,
184 "CGGlyph size too large for bit-packing operation.");
186 "CGGlyph and uint16 too large to pack into GlyphIndex.");
187 return static_cast<GlyphIndex>(glyph) | (static_cast<GlyphIndex>(font_index) << 16);
191 static CGGlyph GlyphIndexToCGGlyph(
GlyphIndex glyph_index) {
192 return static_cast<CGGlyph
>(glyph_index);
196 static uint16 GlyphIndexToFontIndex(
GlyphIndex glyph_index) {
197 return static_cast<uint16
>(glyph_index >> 16);
204 static void AddGlyphToLayout(CGGlyph glyph,
206 const Transform& transform,
207 const CGRect& bounds,
209 const CGPoint& line_origin,
213 if (bounds.size.width * bounds.size.height == 0.0)
216 const Point2d glyph_min(CGRectGetMinX(bounds) + position.x + line_origin.x,
217 CGRectGetMinY(bounds) + position.y + line_origin.y);
218 const Point2d glyph_max(CGRectGetMaxX(bounds) + position.x + line_origin.x,
219 CGRectGetMaxY(bounds) + position.y + line_origin.y);
221 Range2d transformed_glyph(ApplyTransform(transform, glyph_min),
222 ApplyTransform(transform, glyph_max));
223 const Range2f tight_bounds(Point2f(transformed_glyph.GetMinPoint()),
224 Point2f(transformed_glyph.GetMaxPoint()));
227 if (sdf_padding != 0) {
228 const double padding = 2.0 * sdf_padding;
229 const Vector2d
scale(
230 (CGRectGetWidth(bounds) + padding) / CGRectGetWidth(bounds),
231 (CGRectGetHeight(bounds) + padding) / CGRectGetHeight(bounds));
235 const Vector2f
offset(
float(CGRectGetMinX(bounds) * transform.scale[0]),
236 float(CGRectGetMinY(bounds) * transform.scale[1]));
238 CHECK(layout->AddGlyph(
239 Layout::Glyph(CGGlyphFontIndexToGlyphIndex(glyph, font_index),
240 BuildXyQuad(transformed_glyph),
256 class CoreTextFont::Helper {
258 Helper(
const CoreTextFont& owning_font,
const void* data,
size_t data_size);
262 CTFrameRef CreateFrame(
const std::string&
text,
264 float line_spacing)
const;
268 const Font::FontMetrics
GetFontMetrics(
size_t size_in_pixels)
const;
271 const Layout
BuildLayout(
const std::string&
text,
const LayoutOptions& options,
const Font* font);
278 uint16 FontToFontIndex(CTFontRef font);
279 CTFontRef FontIndexToFont(uint16 glyph_index);
281 typedef base::AllocMap<CTFontRef, uint16> FontToIndexMap;
282 typedef base::AllocMap<uint16, CTFontRef> IndexToFontMap;
284 FontToIndexMap font_to_index_map_;
285 IndexToFontMap index_to_font_map_;
289 CTFontRef coretext_font_;
293 CoreTextFont::Helper::Helper(
const CoreTextFont& owning_font,
294 const void* data,
size_t data_size)
295 : font_to_index_map_(owning_font.GetAllocator()),
296 index_to_font_map_(owning_font.GetAllocator()),
297 path_(CGPathCreateWithRect(
298 CGRectMake(0, 0, kPathSizePixels, kPathSizePixels),
299 &CGAffineTransformIdentity)) {
300 const size_t size_in_pixels = owning_font.GetSizeInPixels();
301 if (data && data_size) {
302 CGDataProviderRef data_provider =
303 CGDataProviderCreateWithData(NULL, data, data_size, NULL);
304 CGFontRef cg_font = CGFontCreateWithDataProvider(data_provider);
306 CTFontCreateWithGraphicsFont(cg_font, size_in_pixels, NULL, NULL);
308 CFRelease(data_provider);
310 CFStringRef name_ref = CFStringCreateWithCString(
311 NULL, owning_font.GetName().c_str(), kCFStringEncodingUTF8);
312 coretext_font_ = CTFontCreateWithName(name_ref, size_in_pixels, NULL);
317 CoreTextFont::Helper::~Helper() {
318 CFRelease(coretext_font_);
321 for (
auto it = font_to_index_map_.begin(); it != font_to_index_map_.end(); ++it) {
322 CFRelease(it->first);
326 CTFrameRef CoreTextFont::Helper::CreateFrame(
329 CFStringRef cf_string = CFStringCreateWithBytes(
330 NULL, reinterpret_cast<const UInt8 *>(text.data()), text.size(),
331 kCFStringEncodingUTF8,
false);
336 NSMutableParagraphStyle* paragraph_style =
337 [[NSMutableParagraphStyle alloc] init];
338 paragraph_style.lineHeightMultiple = line_spacing;
339 #if defined(ION_PLATFORM_MAC)
340 switch (horizontal_alignment) {
342 paragraph_style.alignment = NSLeftTextAlignment;
345 paragraph_style.alignment = NSCenterTextAlignment;
348 paragraph_style.alignment = NSRightTextAlignment;
351 #else // defined(ION_PLATFORM_MAC)
352 switch (horizontal_alignment) {
354 paragraph_style.alignment = NSTextAlignmentLeft;
357 paragraph_style.alignment = NSTextAlignmentCenter;
360 paragraph_style.alignment = NSTextAlignmentRight;
363 #endif // defined(ION_PLATFORM_MAC)
364 NSDictionary* attributes =
365 @{ NSFontAttributeName: (__bridge
OBJC_FONT *)coretext_font_,
366 NSParagraphStyleAttributeName: paragraph_style };
368 CFAttributedStringRef attrString =
369 CFAttributedStringCreate(NULL, cf_string, (CFDictionaryRef)attributes);
370 CFRelease(cf_string);
373 CTFramesetterRef framesetter =
374 CTFramesetterCreateWithAttributedString(attrString);
375 CFRelease(attrString);
376 CTFrameRef frame = CTFramesetterCreateFrame(
377 framesetter, CFRangeMake(0, 0), path_, NULL);
378 CFRelease(framesetter);
382 const Font::FontMetrics CoreTextFont::Helper::GetFontMetrics(
383 size_t size_in_pixels)
const {
389 CGFloat ascent = CTFontGetAscent(coretext_font_);
390 CGFloat descent = CTFontGetDescent(coretext_font_);
391 CGFloat leading = CTFontGetLeading(coretext_font_);
396 leading =
static_cast<CGFloat
>(floor(leading + 0.5));
397 metrics.line_advance_height =
398 static_cast<float>(floor(ascent + 0.5) + floor(descent + 0.5) + leading);
402 bool CoreTextFont::Helper::LoadGlyphGrid(
GlyphIndex glyph_index,
403 GlyphGrid* glyph_grid) {
404 CGGlyph glyph = GlyphIndexToCGGlyph(glyph_index);
406 CTFontRef font = FontIndexToFont(GlyphIndexToFontIndex(glyph_index));
407 CTFontGetBoundingRectsForGlyphs(
408 font, kCTFontDefaultOrientation, &glyph, &bounds, 1);
409 const size_t pixel_width =
static_cast<size_t>(ceil(CGRectGetWidth(bounds)));
410 const size_t pixel_height =
411 static_cast<size_t>(ceil(CGRectGetHeight(bounds)));
413 if (pixel_width != 0 && pixel_height != 0) {
417 std::unique_ptr<uint8[]> bitmapData(
new uint8[pixel_height * pixel_width]);
418 memset(bitmapData.get(), 0, pixel_height * pixel_width);
419 CGContextRef cg_context = CGBitmapContextCreate(
420 bitmapData.get(), pixel_width, pixel_height,
421 8, pixel_width, NULL, kCGImageAlphaOnly);
422 CGContextSetTextMatrix(cg_context, CGAffineTransformIdentity);
425 CGPoint point = CGPointMake(-CGRectGetMinX(bounds), -CGRectGetMinY(bounds));
426 CTFontDrawGlyphs(font, &glyph, &point, 1, cg_context);
430 static const double kScale = 1.0 / 255.0;
431 glyph_grid->pixels = base::Array2<double>(pixel_width, pixel_height);
432 for (
size_t x = 0; x < pixel_width; x++) {
433 for (
size_t y = 0; y < pixel_height; y++) {
434 glyph_grid->pixels.Set(
435 x, y, (
double)bitmapData[x + y * pixel_width] * kScale);
439 CGContextRelease(cg_context);
450 unichar un = (unichar)char_index;
451 uint16 font_index = FontToFontIndex(coretext_font_);
452 return CTFontGetGlyphsForCharacters(coretext_font_, &un, &glyph, 1) ?
453 CGGlyphFontIndexToGlyphIndex(glyph, font_index) : 0;
456 const Layout CoreTextFont::Helper::BuildLayout(
const std::string& text,
457 const LayoutOptions& options,
459 CTFrameRef frame = CreateFrame(text, options.horizontal_alignment, options.line_spacing);
464 CFArrayRef lines = CTFrameGetLines(frame);
465 const CFIndex line_count = CFArrayGetCount(lines);
466 std::unique_ptr<CGPoint[]> line_origins(
new CGPoint[line_count]);
467 CTFrameGetLineOrigins(frame, CFRangeMake(0, line_count), line_origins.get());
469 Transform layout_options_transform = CalculateLayoutOptionsTransform(
470 lines, line_count, line_origins.get(), options, font);
471 layout.SetLineAdvanceHeight(
472 font->GetFontMetrics().line_advance_height *
473 static_cast<float>(layout_options_transform.scale[1]) *
474 options.line_spacing);
476 for (CFIndex line_index = 0; line_index <
line_count; ++line_index) {
477 CTLineRef line = (CTLineRef)CFArrayGetValueAtIndex(lines, line_index);
478 CFArrayRef runs = CTLineGetGlyphRuns(line);
480 const CFIndex run_count = CFArrayGetCount(runs);
481 for (CFIndex run_index = 0; run_index < run_count; ++run_index) {
482 CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, run_index);
485 NSDictionary* run_attributes = (NSDictionary*)CTRunGetAttributes(run);
487 (__bridge CTFontRef)[run_attributes objectForKey:NSFontAttributeName];
488 uint16 font_index = FontToFontIndex(run_font);
490 const CFIndex glyph_count = CTRunGetGlyphCount(run);
492 std::unique_ptr<CGPoint[]> positions(
new CGPoint[glyph_count]);
493 CTRunGetPositions(run, CFRangeMake(0, glyph_count), positions.get());
495 std::unique_ptr<CGGlyph[]> glyphs(
new CGGlyph[glyph_count]);
496 CTRunGetGlyphs(run, CFRangeMake(0, glyph_count), glyphs.get());
498 std::unique_ptr<CGRect[]> bounds(
new CGRect[glyph_count]);
499 CTFontGetBoundingRectsForGlyphs(run_font, kCTFontDefaultOrientation,
500 glyphs.get(), bounds.get(), glyph_count);
502 for (CFIndex glyph_index = 0; glyph_index < glyph_count; ++glyph_index) {
503 AddGlyphToLayout(glyphs[glyph_index],
505 layout_options_transform,
507 positions[glyph_index],
508 line_origins[line_index],
509 font->GetSdfPadding(),
519 std::string CoreTextFont::Helper::GetCTFontName()
const {
520 return [CFBridgingRelease(CTFontCopyFullName(coretext_font_)) UTF8String];
523 uint16 CoreTextFont::Helper::FontToFontIndex(CTFontRef font) {
525 auto existing = font_to_index_map_.find(font);
526 if (existing == font_to_index_map_.end()) {
529 CHECK_LT(font_to_index_map_.size(), kuint16max);
530 uint16 font_index =
static_cast<uint16
>(font_to_index_map_.size());
531 font_to_index_map_[font] = font_index;
532 index_to_font_map_[font_index] = font;
533 CFRetain((CTFontRef)font);
536 return existing->second;
539 CTFontRef CoreTextFont::Helper::FontIndexToFont(uint16 font_index) {
541 return index_to_font_map_[font_index];
544 void CoreTextFont::Helper::AddFallbackFont(
const FontPtr& fallback) {
545 LOG(
WARNING) <<
"Fallback support for CoreTextFont is not implemented";
554 CoreTextFont::CoreTextFont(
555 const std::string&
name,
size_t size_in_pixels,
size_t sdf_padding,
556 const void* data,
size_t data_size)
557 :
Font(name, size_in_pixels, sdf_padding),
558 helper_(new Helper(*this, data, data_size)) {
566 return helper_->LoadGlyphGrid(glyph_index, glyph_grid);
570 return helper_->GetDefaultGlyphForChar(char_index);
575 return helper_->BuildLayout(text, options,
this);
579 return helper_->GetCTFontName();
583 helper_->AddFallbackFont(fallback);
void SetFontMetrics(const FontMetrics &metrics)
Sets FontMetrics. SetFontMetrics() should only ever be called once.
bool LoadGlyphGrid(GlyphIndex glyph_index, GlyphGrid *glyph_grid) const override
Called by GetGlyphGrid() for missing glyphs.
Matrix< 2, double > Matrix2d
GenericLockGuard< port::Mutex > LockGuard
Convenient typedefs for ion::port::Mutex.
Range< 2, double > Range2d
#define LOG(severity)
Logs the streamed message unconditionally with a severity of severity.
#define DCHECK_GT(val1, val2)
Range< 2, float > Range2f
GlyphIndex GetDefaultGlyphForChar(CharIndex char_index) const override
Font overrides.
uint32 CharIndex
Typedef for a Unicode index of a character.
This struct defines parameters affecting layout of a single text string when passed to BuildLayout()...
#define CHECK_LT(val1, val2)
const Layout BuildLayout(const std::string &text, const LayoutOptions &options) const override
Creates a layout as specified by options for a given single- or multi- line string text...
base::ReferentPtr< Font >::Type FontPtr
const FontMetrics & GetFontMetrics() const
Returns the FontMetrics for the font.
A Layout instance specifies how glyphs are arranged to form text.
UIFont OBJC_FONT
Copyright 2016 Google Inc.
std::string GetCTFontName() const
Returns the name of the backing system font.
HorizontalAlignment
Alignment enums.
#define ION_STATIC_ASSERT(expr, message)
Copyright 2016 Google Inc.
CoreTextFont(const std::string &name, size_t size_in_pixels, size_t sdf_padding, const void *data, size_t data_size)
Constructs an instance using the given name.
port::Mutex mutex_
Protects shared access to the Allocator and FT_Library.
A grid representing a rendered glyph, with each grid pixel representing pixel coverage in the range (...
A SharedPtr is a smart shared pointer to an instance of some class that implements reference counting...
void AddFallbackFont(const FontPtr &fallback) override
Causes this font to use the font fallback as a fallback if a requested glyph is not found...
const Range< Dimension, T > ScaleRangeNonUniformly(const Range< Dimension, T > &r, const Vector< Dimension, T > scale_factors)
Returns a Range that is the input Range scaled nonuniformly about its center by the given per-dimensi...
Font(const std::string &name, size_t size_in_pixels, size_t sdf_padding)
The constructor is protected because this is an abstract base class.
Font is a base class for implementation-specific representations of fonts.