diff --git a/apple/OSX/OSX/RetroArch_OSX.xcodeproj/project.pbxproj b/apple/OSX/OSX/RetroArch_OSX.xcodeproj/project.pbxproj index 4f57ec1ae8..da679e55f3 100644 --- a/apple/OSX/OSX/RetroArch_OSX.xcodeproj/project.pbxproj +++ b/apple/OSX/OSX/RetroArch_OSX.xcodeproj/project.pbxproj @@ -12,6 +12,8 @@ 50535530185E0F4000926C26 /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5053552F185E0F4000926C26 /* CoreLocation.framework */; }; 50C374A919F04F7A00984F8D /* CFExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 50C374A819F04F7A00984F8D /* CFExtensions.m */; }; 50D66298199F344700CF54E3 /* Cg.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50D66297199F344700CF54E3 /* Cg.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + 696012EE19F2B7C0006A1088 /* CoreText.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 696012ED19F2B7C0006A1088 /* CoreText.framework */; }; + 696012F019F2B7D8006A1088 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 696012EF19F2B7D8006A1088 /* CoreGraphics.framework */; }; 962EE0E2178B3DF6004224FF /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 962EE0E1178B3DF6004224FF /* IOKit.framework */; }; 96355CE31788E72A0010DBFA /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 96355CE21788E72A0010DBFA /* Cocoa.framework */; }; 964DE7C417D84B57001CBB6C /* Settings.xib in Resources */ = {isa = PBXBuildFile; fileRef = 964DE7C617D84B57001CBB6C /* Settings.xib */; }; @@ -35,6 +37,8 @@ 50C374A819F04F7A00984F8D /* CFExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CFExtensions.m; path = ../../common/CFExtensions.m; sourceTree = SOURCE_ROOT; }; 50D66295199F28AC00CF54E3 /* Cg.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Cg.framework; sourceTree = ""; }; 50D66297199F344700CF54E3 /* Cg.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cg.framework; path = ../../../../../../../Library/Frameworks/Cg.framework; sourceTree = ""; }; + 696012ED19F2B7C0006A1088 /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = System/Library/Frameworks/CoreText.framework; sourceTree = SDKROOT; }; + 696012EF19F2B7D8006A1088 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 962EE0E1178B3DF6004224FF /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = ../../../../../../../System/Library/Frameworks/IOKit.framework; sourceTree = ""; }; 96355CDF1788E72A0010DBFA /* RetroArch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RetroArch.app; sourceTree = BUILT_PRODUCTS_DIR; }; 96355CE21788E72A0010DBFA /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; @@ -61,6 +65,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 696012F019F2B7D8006A1088 /* CoreGraphics.framework in Frameworks */, + 696012EE19F2B7C0006A1088 /* CoreText.framework in Frameworks */, 50D66298199F344700CF54E3 /* Cg.framework in Frameworks */, 50535530185E0F4000926C26 /* CoreLocation.framework in Frameworks */, 962EE0E2178B3DF6004224FF /* IOKit.framework in Frameworks */, @@ -111,6 +117,8 @@ 96355CE11788E72A0010DBFA /* Frameworks */ = { isa = PBXGroup; children = ( + 696012EF19F2B7D8006A1088 /* CoreGraphics.framework */, + 696012ED19F2B7C0006A1088 /* CoreText.framework */, 50D66297199F344700CF54E3 /* Cg.framework */, 50D66295199F28AC00CF54E3 /* Cg.framework */, 5053552F185E0F4000926C26 /* CoreLocation.framework */, diff --git a/apple/iOS/RetroArch_iOS.xcodeproj/project.pbxproj b/apple/iOS/RetroArch_iOS.xcodeproj/project.pbxproj index 7adef723ab..097de1d77f 100644 --- a/apple/iOS/RetroArch_iOS.xcodeproj/project.pbxproj +++ b/apple/iOS/RetroArch_iOS.xcodeproj/project.pbxproj @@ -21,6 +21,7 @@ 50CCC828185E0E7D001F5BC8 /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50CCC827185E0E7D001F5BC8 /* CoreLocation.framework */; }; 50D00E8E19D117C400EBA71E /* cc_resampler_neon.S in Sources */ = {isa = PBXBuildFile; fileRef = 50D00E8D19D117C400EBA71E /* cc_resampler_neon.S */; }; 50E7189F184B88AA001956CE /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50E7189E184B88AA001956CE /* CoreVideo.framework */; }; + 696012F219F3389A006A1088 /* CoreText.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 696012F119F3389A006A1088 /* CoreText.framework */; }; 83D632DF19ECFCC4009E3161 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 83D632D819ECFCC4009E3161 /* Default-568h@2x.png */; }; 83D632E019ECFCC4009E3161 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 83D632D919ECFCC4009E3161 /* Default.png */; }; 83D632E119ECFCC4009E3161 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 83D632DA19ECFCC4009E3161 /* Default@2x.png */; }; @@ -56,6 +57,7 @@ 50CCC827185E0E7D001F5BC8 /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = System/Library/Frameworks/CoreLocation.framework; sourceTree = SDKROOT; }; 50D00E8D19D117C400EBA71E /* cc_resampler_neon.S */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; name = cc_resampler_neon.S; path = ../../audio/resamplers/cc_resampler_neon.S; sourceTree = ""; }; 50E7189E184B88AA001956CE /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; }; + 696012F119F3389A006A1088 /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = System/Library/Frameworks/CoreText.framework; sourceTree = SDKROOT; }; 83D632D819ECFCC4009E3161 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; 83D632D919ECFCC4009E3161 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; }; 83D632DA19ECFCC4009E3161 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = ""; }; @@ -85,6 +87,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 696012F219F3389A006A1088 /* CoreText.framework in Frameworks */, 963C3C34186E3DED00A6EB1E /* GameController.framework in Frameworks */, 50CCC828185E0E7D001F5BC8 /* CoreLocation.framework in Frameworks */, 501881EE184BB54C006F665D /* CoreMedia.framework in Frameworks */, @@ -158,6 +161,7 @@ 96AFAE2816C1D4EA009DE44C /* Frameworks */ = { isa = PBXGroup; children = ( + 696012F119F3389A006A1088 /* CoreText.framework */, 963C3C33186E3DED00A6EB1E /* GameController.framework */, 50CCC827185E0E7D001F5BC8 /* CoreLocation.framework */, 501881ED184BB54C006F665D /* CoreMedia.framework */, @@ -347,7 +351,7 @@ CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; COPY_PHASE_STRIP = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; @@ -404,8 +408,8 @@ CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; COPY_PHASE_STRIP = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_OPTIMIZATION_LEVEL = 2; @@ -452,10 +456,10 @@ 96AFAE5516C1D4EA009DE44C /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = ( - armv7s, - armv7, - ); + ARCHS = ( + armv7s, + armv7, + ); CLANG_CXX_LIBRARY = "libstdc++"; CODE_SIGN_IDENTITY = ""; CODE_SIGN_RESOURCE_RULES_PATH = "$(SDKROOT)/ResourceRules.plist"; @@ -505,13 +509,12 @@ 96AFAE5616C1D4EA009DE44C /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = ( - armv7s, - armv7, - ); + ARCHS = ( + armv7s, + armv7, + ); CLANG_CXX_LIBRARY = "libstdc++"; CODE_SIGN_IDENTITY = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; CODE_SIGN_RESOURCE_RULES_PATH = "$(SDKROOT)/ResourceRules.plist"; GCC_PRECOMPILE_PREFIX_HEADER = NO; GCC_PREFIX_HEADER = ""; @@ -587,7 +590,7 @@ ); PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; - "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; VALID_ARCHS = "armv7 armv7s"; WRAPPER_EXTENSION = app; }; diff --git a/gfx/fonts/coretext.c b/gfx/fonts/coretext.c new file mode 100644 index 0000000000..440fd1826b --- /dev/null +++ b/gfx/fonts/coretext.c @@ -0,0 +1,237 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include "fonts.h" +#include "../../file.h" +#include "../../general.h" +#include +#include +#include + +#include +#include + +#define CT_ATLAS_ROWS 8 +#define CT_ATLAS_COLS 16 +#define CT_ATLAS_SIZE (CT_ATLAS_ROWS * CT_ATLAS_COLS) + +typedef struct coretext_renderer +{ + struct font_atlas atlas; + struct font_glyph glyphs[CT_ATLAS_SIZE]; +} font_renderer_t; + +static const struct font_atlas *font_renderer_ct_get_atlas(void *data) +{ + font_renderer_t *handle = (font_renderer_t*)data; + return &handle->atlas; +} + +static const struct font_glyph *font_renderer_ct_get_glyph(void *data, uint32_t code) +{ + font_renderer_t *handle = (font_renderer_t*)data; + struct font_glyph *result = code < CT_ATLAS_SIZE ? &handle->glyphs[code] : NULL; + return result; +} + +static void font_renderer_ct_free(void *data) +{ + font_renderer_t *handle = (font_renderer_t*)data; + if (!handle) + return; + + free(handle->atlas.buffer); + free(handle); +} + +static bool font_renderer_create_atlas(CTFontRef face, font_renderer_t *handle) +{ + unsigned i; + bool ret = true; + + UniChar characters[CT_ATLAS_SIZE] = {0}; + for (i = 0; i < CT_ATLAS_SIZE; i++) { + characters[i] = (UniChar)i; + } + + CGGlyph glyphs[CT_ATLAS_SIZE]; + CTFontGetGlyphsForCharacters(face, characters, glyphs, CT_ATLAS_SIZE); + + CGRect bounds[CT_ATLAS_SIZE]; + CTFontGetBoundingRectsForGlyphs(face, kCTFontDefaultOrientation, + glyphs, bounds, CT_ATLAS_SIZE); + + CGSize advances[CT_ATLAS_SIZE]; + CTFontGetAdvancesForGlyphs(face, kCTFontDefaultOrientation, + glyphs, advances, CT_ATLAS_SIZE); + + CGFloat ascent = CTFontGetAscent( face ); + CGFloat descent = CTFontGetDescent( face ); + + int max_width = 0; + int max_height = 0; + for (i = 0; i < CT_ATLAS_SIZE; i++) { + struct font_glyph *glyph = &handle->glyphs[i]; + int origin_x = ceil(bounds[i].origin.x); + int origin_y = ceil(bounds[i].origin.y); + + glyph->draw_offset_x = 0; + glyph->draw_offset_y = -1 * (ascent - descent); + glyph->width = ceil(bounds[i].size.width); + glyph->height = ceil(bounds[i].size.height); + glyph->advance_x = ceil(advances[i].width); + glyph->advance_y = ceil(advances[i].height); + + max_width = max(max_width, (origin_x + glyph->width)); + max_height = max(max_height, (origin_y + glyph->height)); + } + max_height = max(max_height, ceil(ascent+descent)); + + handle->atlas.width = max_width * CT_ATLAS_COLS; + handle->atlas.height = max_height * CT_ATLAS_ROWS; + + handle->atlas.buffer = (uint8_t*) + calloc(handle->atlas.width * handle->atlas.height, 1); + + if (!handle->atlas.buffer) { + ret = false; + goto end; + } + + size_t bitsPerComponent = 8; + size_t bytesPerRow = max_width; + void *bitmapData = calloc(max_height, bytesPerRow); + CGContextRef offscreen = + CGBitmapContextCreate(bitmapData, max_width, max_height, + bitsPerComponent, bytesPerRow, NULL, kCGImageAlphaOnly); + CGContextSetTextMatrix(offscreen, CGAffineTransformIdentity); + + CFStringRef keys[] = { kCTFontAttributeName }; + CFTypeRef values[] = { face }; + CFDictionaryRef attr = + CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&values, + sizeof(keys) / sizeof(keys[0]), + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + for (i = 0; i < CT_ATLAS_SIZE; i++) { + struct font_glyph *glyph = &handle->glyphs[i]; + + glyph->width = max_width; + glyph->height = max_height; + + unsigned offset_x = (i % CT_ATLAS_COLS) * max_width; + unsigned offset_y = (i / CT_ATLAS_COLS) * max_height; + + glyph->atlas_offset_x = offset_x; + glyph->atlas_offset_y = offset_y; + + char glyph_cstr[2]; + glyph_cstr[0] = i; + glyph_cstr[1] = 0; + CFStringRef glyph_cfstr = + CFStringCreateWithCString( NULL, glyph_cstr, kCFStringEncodingASCII ); + CFAttributedStringRef attrString = + CFAttributedStringCreate(NULL, glyph_cfstr, attr); + CFRelease(glyph_cfstr), glyph_cfstr = NULL; + CTLineRef line = CTLineCreateWithAttributedString(attrString); + CFRelease(attrString), attrString = NULL; + + memset( bitmapData, 0, max_height * bytesPerRow ); + CGContextSetTextPosition(offscreen, 0, descent); + CTLineDraw(line, offscreen); + CGContextFlush( offscreen ); + + CFRelease( line ), line = NULL; + + uint8_t *dst = (uint8_t*)handle->atlas.buffer; + + const uint8_t *src = (const uint8_t*)bitmapData; + for (unsigned r = 0; r < max_height; r++ ) { + for (unsigned c = 0; c < max_width; c++) { + unsigned src_idx = r * bytesPerRow + c; + unsigned dest_idx = + (r + offset_y) * (CT_ATLAS_COLS * max_width) + (c + offset_x); + uint8_t v = src[src_idx]; + dst[dest_idx] = v; + } + } + } + + CFRelease(attr), attr = NULL; + CGContextRelease(offscreen), offscreen = NULL; + free(bitmapData); + + end: + return ret; +} + +static void *font_renderer_ct_init(const char *font_path, float font_size) +{ + char err = 0; + CFStringRef cf_font_path = NULL; + CTFontRef face = NULL; + + font_renderer_t *handle = (font_renderer_t*) + calloc(1, sizeof(*handle)); + + if (!handle) { + err = 1; goto error; + } + + + cf_font_path = CFStringCreateWithCString( NULL, font_path, kCFStringEncodingASCII ); + if ( ! cf_font_path ) { + err = 1; goto error; + } + face = CTFontCreateWithName( cf_font_path, font_size, NULL ); + if ( ! face ) { + err = 1; goto error; + } + if (! font_renderer_create_atlas(face, handle)) { + err = 1; goto error; + } + + error: + if ( err ) { + font_renderer_ct_free(handle); + handle = NULL; + } + + if ( cf_font_path ) { + CFRelease( cf_font_path ), cf_font_path = NULL ; } + if ( face ) { + CFRelease(face), face = NULL; } + + return handle; +} + +/* We can't tell if a font is going to be there until we actually + initialize CoreText and the best way to get fonts is by name, not + by path. */ +static const char *default_font = "Verdana"; +static const char *font_renderer_ct_get_default_font(void) +{ + return default_font; +} + +font_renderer_driver_t coretext_font_renderer = { + font_renderer_ct_init, + font_renderer_ct_get_atlas, + font_renderer_ct_get_glyph, + font_renderer_ct_free, + font_renderer_ct_get_default_font, + "coretext", +}; diff --git a/gfx/fonts/fonts.c b/gfx/fonts/fonts.c index 1c2b0e587d..9ed7968d85 100644 --- a/gfx/fonts/fonts.c +++ b/gfx/fonts/fonts.c @@ -23,6 +23,9 @@ static const font_renderer_driver_t *font_backends[] = { #ifdef HAVE_FREETYPE &freetype_font_renderer, +#endif +#ifdef __APPLE__ + &coretext_font_renderer, #endif &bitmap_font_renderer, NULL diff --git a/gfx/fonts/fonts.h b/gfx/fonts/fonts.h index 363b9d5914..d8221a6baa 100644 --- a/gfx/fonts/fonts.h +++ b/gfx/fonts/fonts.h @@ -71,6 +71,7 @@ typedef struct font_renderer_driver } font_renderer_driver_t; extern font_renderer_driver_t freetype_font_renderer; +extern font_renderer_driver_t coretext_font_renderer; extern font_renderer_driver_t bitmap_font_renderer; /* font_path can be NULL for default font. */ diff --git a/griffin/griffin.c b/griffin/griffin.c index c926489130..14fb897f86 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -259,6 +259,10 @@ FONTS #include "../gfx/fonts/freetype.c" #endif +#if defined(__APPLE__) +#include "../gfx/fonts/coretext.c" +#endif + #ifdef HAVE_OPENGL #include "../gfx/fonts/gl_font.c" #endif