Merge branch 'master' into jgrpp
# Conflicts: # src/company_cmd.cpp # src/core/geometry_func.cpp # src/date.cpp # src/genworld_gui.cpp # src/gfx.cpp # src/object_gui.cpp # src/openttd.cpp # src/settings_type.h # src/video/allegro_v.cpp # src/video/dedicated_v.cpp # src/video/null_v.cpp # src/video/sdl2_v.cpp # src/video/sdl_v.cpp # src/video/win32_v.cpp
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
add_files(
|
||||
crashlog_osx.cpp
|
||||
font_osx.cpp
|
||||
font_osx.h
|
||||
macos.h
|
||||
macos.mm
|
||||
osx_stdafx.h
|
||||
splash.cpp
|
||||
splash.h
|
||||
string_osx.cpp
|
||||
string_osx.h
|
||||
CONDITION APPLE
|
||||
|
424
src/os/macosx/font_osx.cpp
Normal file
424
src/os/macosx/font_osx.cpp
Normal file
@@ -0,0 +1,424 @@
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD 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 Foundation, version 2.
|
||||
* OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file font_osx.cpp Functions related to font handling on MacOS. */
|
||||
|
||||
#include "../../stdafx.h"
|
||||
#include "../../debug.h"
|
||||
#include "font_osx.h"
|
||||
#include "../../blitter/factory.hpp"
|
||||
#include "../../fileio_func.h"
|
||||
#include "../../fontdetection.h"
|
||||
#include "../../string_func.h"
|
||||
#include "../../strings_func.h"
|
||||
#include "../../zoom_func.h"
|
||||
#include "macos.h"
|
||||
#include <cmath>
|
||||
|
||||
#include "../../table/control_codes.h"
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
|
||||
#ifdef WITH_FREETYPE
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
extern FT_Library _library;
|
||||
|
||||
|
||||
FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
|
||||
{
|
||||
FT_Error err = FT_Err_Cannot_Open_Resource;
|
||||
|
||||
/* Get font reference from name. */
|
||||
UInt8 file_path[PATH_MAX];
|
||||
OSStatus os_err = -1;
|
||||
CFAutoRelease<CFStringRef> name(CFStringCreateWithCString(kCFAllocatorDefault, font_name, kCFStringEncodingUTF8));
|
||||
|
||||
/* Simply creating the font using CTFontCreateWithNameAndSize will *always* return
|
||||
* something, no matter the name. As such, we can't use it to check for existence.
|
||||
* We instead query the list of all font descriptors that match the given name which
|
||||
* does not do this stupid name fallback. */
|
||||
CFAutoRelease<CTFontDescriptorRef> name_desc(CTFontDescriptorCreateWithNameAndSize(name.get(), 0.0));
|
||||
CFAutoRelease<CFSetRef> mandatory_attribs(CFSetCreate(kCFAllocatorDefault, const_cast<const void **>(reinterpret_cast<const void *const *>(&kCTFontNameAttribute)), 1, &kCFTypeSetCallBacks));
|
||||
CFAutoRelease<CFArrayRef> descs(CTFontDescriptorCreateMatchingFontDescriptors(name_desc.get(), mandatory_attribs.get()));
|
||||
|
||||
/* Loop over all matches until we can get a path for one of them. */
|
||||
for (CFIndex i = 0; descs.get() != nullptr && i < CFArrayGetCount(descs.get()) && os_err != noErr; i++) {
|
||||
CFAutoRelease<CTFontRef> font(CTFontCreateWithFontDescriptor((CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), i), 0.0, nullptr));
|
||||
CFAutoRelease<CFURLRef> fontURL((CFURLRef)CTFontCopyAttribute(font.get(), kCTFontURLAttribute));
|
||||
if (CFURLGetFileSystemRepresentation(fontURL.get(), true, file_path, lengthof(file_path))) os_err = noErr;
|
||||
}
|
||||
|
||||
if (os_err == noErr) {
|
||||
DEBUG(freetype, 3, "Font path for %s: %s", font_name, file_path);
|
||||
err = FT_New_Face(_library, (const char *)file_path, 0, face);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#endif /* WITH_FREETYPE */
|
||||
|
||||
|
||||
bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback)
|
||||
{
|
||||
/* Determine fallback font using CoreText. This uses the language isocode
|
||||
* to find a suitable font. CoreText is available from 10.5 onwards. */
|
||||
char lang[16];
|
||||
if (strcmp(language_isocode, "zh_TW") == 0) {
|
||||
/* Traditional Chinese */
|
||||
strecpy(lang, "zh-Hant", lastof(lang));
|
||||
} else if (strcmp(language_isocode, "zh_CN") == 0) {
|
||||
/* Simplified Chinese */
|
||||
strecpy(lang, "zh-Hans", lastof(lang));
|
||||
} else {
|
||||
/* Just copy the first part of the isocode. */
|
||||
strecpy(lang, language_isocode, lastof(lang));
|
||||
char *sep = strchr(lang, '_');
|
||||
if (sep != nullptr) *sep = '\0';
|
||||
}
|
||||
|
||||
/* Create a font descriptor matching the wanted language and latin (english) glyphs.
|
||||
* Can't use CFAutoRelease here for everything due to the way the dictionary has to be created. */
|
||||
CFStringRef lang_codes[2];
|
||||
lang_codes[0] = CFStringCreateWithCString(kCFAllocatorDefault, lang, kCFStringEncodingUTF8);
|
||||
lang_codes[1] = CFSTR("en");
|
||||
CFArrayRef lang_arr = CFArrayCreate(kCFAllocatorDefault, (const void **)lang_codes, lengthof(lang_codes), &kCFTypeArrayCallBacks);
|
||||
CFAutoRelease<CFDictionaryRef> lang_attribs(CFDictionaryCreate(kCFAllocatorDefault, const_cast<const void **>(reinterpret_cast<const void *const *>(&kCTFontLanguagesAttribute)), (const void **)&lang_arr, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
|
||||
CFAutoRelease<CTFontDescriptorRef> lang_desc(CTFontDescriptorCreateWithAttributes(lang_attribs.get()));
|
||||
CFRelease(lang_arr);
|
||||
CFRelease(lang_codes[0]);
|
||||
|
||||
/* Get array of all font descriptors for the wanted language. */
|
||||
CFAutoRelease<CFSetRef> mandatory_attribs(CFSetCreate(kCFAllocatorDefault, const_cast<const void **>(reinterpret_cast<const void *const *>(&kCTFontLanguagesAttribute)), 1, &kCFTypeSetCallBacks));
|
||||
CFAutoRelease<CFArrayRef> descs(CTFontDescriptorCreateMatchingFontDescriptors(lang_desc.get(), mandatory_attribs.get()));
|
||||
|
||||
bool result = false;
|
||||
for (int tries = 0; tries < 2; tries++) {
|
||||
for (CFIndex i = 0; descs.get() != nullptr && i < CFArrayGetCount(descs.get()); i++) {
|
||||
CTFontDescriptorRef font = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), i);
|
||||
|
||||
/* Get font traits. */
|
||||
CFAutoRelease<CFDictionaryRef> traits((CFDictionaryRef)CTFontDescriptorCopyAttribute(font, kCTFontTraitsAttribute));
|
||||
CTFontSymbolicTraits symbolic_traits;
|
||||
CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(traits.get(), kCTFontSymbolicTrait), kCFNumberIntType, &symbolic_traits);
|
||||
|
||||
/* Skip symbol fonts and vertical fonts. */
|
||||
if ((symbolic_traits & kCTFontClassMaskTrait) == (CTFontStylisticClass)kCTFontSymbolicClass || (symbolic_traits & kCTFontVerticalTrait)) continue;
|
||||
/* Skip bold fonts (especially Arial Bold, which looks worse than regular Arial). */
|
||||
if (symbolic_traits & kCTFontBoldTrait) continue;
|
||||
/* Select monospaced fonts if asked for. */
|
||||
if (((symbolic_traits & kCTFontMonoSpaceTrait) == kCTFontMonoSpaceTrait) != callback->Monospace()) continue;
|
||||
|
||||
/* Get font name. */
|
||||
char name[128];
|
||||
CFAutoRelease<CFStringRef> font_name((CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontDisplayNameAttribute));
|
||||
CFStringGetCString(font_name.get(), name, lengthof(name), kCFStringEncodingUTF8);
|
||||
|
||||
/* Serif fonts usually look worse on-screen with only small
|
||||
* font sizes. As such, we try for a sans-serif font first.
|
||||
* If we can't find one in the first try, try all fonts. */
|
||||
if (tries == 0 && (symbolic_traits & kCTFontClassMaskTrait) != (CTFontStylisticClass)kCTFontSansSerifClass) continue;
|
||||
|
||||
/* There are some special fonts starting with an '.' and the last
|
||||
* resort font that aren't usable. Skip them. */
|
||||
if (name[0] == '.' || strncmp(name, "LastResort", 10) == 0) continue;
|
||||
|
||||
/* Save result. */
|
||||
callback->SetFontNames(settings, name);
|
||||
if (!callback->FindMissingGlyphs()) {
|
||||
DEBUG(freetype, 2, "CT-Font for %s: %s", language_isocode, name);
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
/* For some OS versions, the font 'Arial Unicode MS' does not report all languages it
|
||||
* supports. If we didn't find any other font, just try it, maybe we get lucky. */
|
||||
callback->SetFontNames(settings, "Arial Unicode MS");
|
||||
result = !callback->FindMissingGlyphs();
|
||||
}
|
||||
|
||||
callback->FindMissingGlyphs();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
CoreTextFontCache::CoreTextFontCache(FontSize fs, CFAutoRelease<CTFontDescriptorRef> &&font, int pixels) : TrueTypeFontCache(fs, pixels), font_desc(std::move(font))
|
||||
{
|
||||
this->SetFontSize(pixels);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset cached glyphs.
|
||||
*/
|
||||
void CoreTextFontCache::ClearFontCache()
|
||||
{
|
||||
/* GUI scaling might have changed, determine font size anew if it was automatically selected. */
|
||||
if (this->font) this->SetFontSize(this->req_size);
|
||||
|
||||
this->TrueTypeFontCache::ClearFontCache();
|
||||
}
|
||||
|
||||
void CoreTextFontCache::SetFontSize(int pixels)
|
||||
{
|
||||
if (pixels == 0) {
|
||||
/* Try to determine a good height based on the height recommended by the font. */
|
||||
int scaled_height = ScaleFontTrad(this->GetDefaultFontHeight(this->fs));
|
||||
pixels = scaled_height;
|
||||
|
||||
CFAutoRelease<CTFontRef> font(CTFontCreateWithFontDescriptor(this->font_desc.get(), 0.0f, nullptr));
|
||||
if (font) {
|
||||
float min_size = 0.0f;
|
||||
|
||||
/* The 'head' TrueType table contains information about the
|
||||
* 'smallest readable size in pixels'. Try to read it, if
|
||||
* that doesn't work, we use the default OS font size instead.
|
||||
*
|
||||
* Reference: https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6head.html */
|
||||
CFAutoRelease<CFDataRef> data(CTFontCopyTable(font.get(), kCTFontTableHead, kCTFontTableOptionNoOptions));
|
||||
if (data) {
|
||||
uint16_t lowestRecPPEM; // At offset 46 of the 'head' TrueType table.
|
||||
CFDataGetBytes(data.get(), CFRangeMake(46, sizeof(lowestRecPPEM)), (UInt8 *)&lowestRecPPEM);
|
||||
min_size = CFSwapInt16BigToHost(lowestRecPPEM); // TrueType data is always big-endian.
|
||||
} else {
|
||||
CFAutoRelease<CFNumberRef> size((CFNumberRef)CTFontCopyAttribute(font.get(), kCTFontSizeAttribute));
|
||||
CFNumberGetValue(size.get(), kCFNumberFloatType, &min_size);
|
||||
}
|
||||
|
||||
/* Font height is minimum height plus the difference between the default
|
||||
* height for this font size and the small size. */
|
||||
int diff = scaled_height - ScaleFontTrad(this->GetDefaultFontHeight(FS_SMALL));
|
||||
pixels = Clamp(std::min<int>(min_size, MAX_FONT_MIN_REC_SIZE) + diff, scaled_height, MAX_FONT_SIZE);
|
||||
}
|
||||
} else {
|
||||
pixels = ScaleFontTrad(pixels);
|
||||
}
|
||||
this->used_size = pixels;
|
||||
|
||||
this->font.reset(CTFontCreateWithFontDescriptor(this->font_desc.get(), pixels, nullptr));
|
||||
|
||||
/* Query the font metrics we needed. We generally round all values up to
|
||||
* make sure we don't inadvertently cut off a row or column of pixels,
|
||||
* except when determining glyph to glyph advances. */
|
||||
this->units_per_em = CTFontGetUnitsPerEm(this->font.get());
|
||||
this->ascender = (int)std::ceil(CTFontGetAscent(this->font.get()));
|
||||
this->descender = -(int)std::ceil(CTFontGetDescent(this->font.get()));
|
||||
this->height = this->ascender - this->descender;
|
||||
|
||||
/* Get real font name. */
|
||||
char name[128];
|
||||
CFAutoRelease<CFStringRef> font_name((CFStringRef)CTFontCopyAttribute(this->font.get(), kCTFontDisplayNameAttribute));
|
||||
CFStringGetCString(font_name.get(), name, lengthof(name), kCFStringEncodingUTF8);
|
||||
this->font_name = name;
|
||||
|
||||
DEBUG(freetype, 2, "Loaded font '%s' with size %d", this->font_name.c_str(), pixels);
|
||||
}
|
||||
|
||||
GlyphID CoreTextFontCache::MapCharToGlyph(WChar key)
|
||||
{
|
||||
assert(IsPrintable(key));
|
||||
|
||||
if (key >= SCC_SPRITE_START && key <= SCC_SPRITE_END) {
|
||||
return this->parent->MapCharToGlyph(key);
|
||||
}
|
||||
|
||||
/* Convert characters outside of the Basic Multilingual Plane into surrogate pairs. */
|
||||
UniChar chars[2];
|
||||
if (key >= 0x010000U) {
|
||||
chars[0] = (UniChar)(((key - 0x010000U) >> 10) + 0xD800);
|
||||
chars[1] = (UniChar)(((key - 0x010000U) & 0x3FF) + 0xDC00);
|
||||
} else {
|
||||
chars[0] = (UniChar)(key & 0xFFFF);
|
||||
}
|
||||
|
||||
CGGlyph glyph[2] = {0, 0};
|
||||
if (CTFontGetGlyphsForCharacters(this->font.get(), chars, glyph, key >= 0x010000U ? 2 : 1)) {
|
||||
return glyph[0];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const void *CoreTextFontCache::InternalGetFontTable(uint32 tag, size_t &length)
|
||||
{
|
||||
CFAutoRelease<CFDataRef> data(CTFontCopyTable(this->font.get(), (CTFontTableTag)tag, kCTFontTableOptionNoOptions));
|
||||
if (!data) return nullptr;
|
||||
|
||||
length = CFDataGetLength(data.get());
|
||||
auto buf = MallocT<UInt8>(length);
|
||||
|
||||
CFDataGetBytes(data.get(), CFRangeMake(0, (CFIndex)length), buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
const Sprite *CoreTextFontCache::InternalGetGlyph(GlyphID key, bool use_aa)
|
||||
{
|
||||
/* Get glyph size. */
|
||||
CGGlyph glyph = (CGGlyph)key;
|
||||
CGRect bounds = CGRectNull;
|
||||
if (MacOSVersionIsAtLeast(10, 8, 0)) {
|
||||
bounds = CTFontGetOpticalBoundsForGlyphs(this->font.get(), &glyph, nullptr, 1, 0);
|
||||
} else {
|
||||
bounds = CTFontGetBoundingRectsForGlyphs(this->font.get(), kCTFontOrientationDefault, &glyph, nullptr, 1);
|
||||
}
|
||||
if (CGRectIsNull(bounds)) usererror("Unable to render font glyph");
|
||||
|
||||
uint bb_width = (uint)std::ceil(bounds.size.width) + 1; // Sometimes the glyph bounds are too tight and cut of the last pixel after rounding.
|
||||
uint bb_height = (uint)std::ceil(bounds.size.height);
|
||||
|
||||
/* Add 1 pixel for the shadow on the medium font. Our sprite must be at least 1x1 pixel. */
|
||||
uint width = std::max(1U, bb_width + (this->fs == FS_NORMAL ? 1 : 0));
|
||||
uint height = std::max(1U, bb_height + (this->fs == FS_NORMAL ? 1 : 0));
|
||||
|
||||
/* Limit glyph size to prevent overflows later on. */
|
||||
if (width > MAX_GLYPH_DIM || height > MAX_GLYPH_DIM) usererror("Font glyph is too large");
|
||||
|
||||
SpriteLoader::Sprite sprite;
|
||||
sprite.AllocateData(ZOOM_LVL_NORMAL, width * height);
|
||||
sprite.type = ST_FONT;
|
||||
sprite.width = width;
|
||||
sprite.height = height;
|
||||
sprite.x_offs = (int16)std::round(CGRectGetMinX(bounds));
|
||||
sprite.y_offs = this->ascender - (int16)std::ceil(CGRectGetMaxY(bounds));
|
||||
|
||||
if (bounds.size.width > 0) {
|
||||
/* Glyph is not a white-space glyph. Render it to a bitmap context. */
|
||||
|
||||
/* We only need the alpha channel, as we apply our own colour constants to the sprite. */
|
||||
int pitch = Align(bb_width, 16);
|
||||
byte *bmp = CallocT<byte>(bb_height * pitch);
|
||||
CFAutoRelease<CGContextRef> context(CGBitmapContextCreate(bmp, bb_width, bb_height, 8, pitch, nullptr, kCGImageAlphaOnly));
|
||||
/* Set antialias according to requirements. */
|
||||
CGContextSetAllowsAntialiasing(context.get(), use_aa);
|
||||
CGContextSetAllowsFontSubpixelPositioning(context.get(), use_aa);
|
||||
CGContextSetAllowsFontSubpixelQuantization(context.get(), !use_aa);
|
||||
CGContextSetShouldSmoothFonts(context.get(), false);
|
||||
|
||||
float offset = 0.5f; // CoreText uses 0.5 as pixel centers. We want pixel alignment.
|
||||
CGPoint pos{offset - bounds.origin.x, offset - bounds.origin.y};
|
||||
CTFontDrawGlyphs(this->font.get(), &glyph, &pos, 1, context.get());
|
||||
|
||||
/* Draw shadow for medium size. */
|
||||
if (this->fs == FS_NORMAL && !use_aa) {
|
||||
for (uint y = 0; y < bb_height; y++) {
|
||||
for (uint x = 0; x < bb_width; x++) {
|
||||
if (bmp[y * pitch + x] > 0) {
|
||||
sprite.data[1 + x + (1 + y) * sprite.width].m = SHADOW_COLOUR;
|
||||
sprite.data[1 + x + (1 + y) * sprite.width].a = use_aa ? bmp[x + y * pitch] : 0xFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Extract pixel data. */
|
||||
for (uint y = 0; y < bb_height; y++) {
|
||||
for (uint x = 0; x < bb_width; x++) {
|
||||
if (bmp[y * pitch + x] > 0) {
|
||||
sprite.data[x + y * sprite.width].m = FACE_COLOUR;
|
||||
sprite.data[x + y * sprite.width].a = use_aa ? bmp[x + y * pitch] : 0xFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GlyphEntry new_glyph;
|
||||
new_glyph.sprite = BlitterFactory::GetCurrentBlitter()->Encode(&sprite, AllocateFont);
|
||||
new_glyph.width = (byte)std::round(CTFontGetAdvancesForGlyphs(this->font.get(), kCTFontOrientationDefault, &glyph, nullptr, 1));
|
||||
this->SetGlyphPtr(key, &new_glyph);
|
||||
|
||||
return new_glyph.sprite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the TrueType font.
|
||||
* If a CoreText font description is present, e.g. from the automatic font
|
||||
* fallback search, use it. Otherwise, try to resolve it by font name.
|
||||
* @param fs The font size to load.
|
||||
*/
|
||||
void LoadCoreTextFont(FontSize fs)
|
||||
{
|
||||
static const char *SIZE_TO_NAME[] = { "medium", "small", "large", "mono" };
|
||||
|
||||
FreeTypeSubSetting *settings = nullptr;
|
||||
switch (fs) {
|
||||
default: NOT_REACHED();
|
||||
case FS_SMALL: settings = &_freetype.small; break;
|
||||
case FS_NORMAL: settings = &_freetype.medium; break;
|
||||
case FS_LARGE: settings = &_freetype.large; break;
|
||||
case FS_MONO: settings = &_freetype.mono; break;
|
||||
}
|
||||
|
||||
if (StrEmpty(settings->font)) return;
|
||||
|
||||
CFAutoRelease<CTFontDescriptorRef> font_ref;
|
||||
|
||||
if (settings->os_handle != nullptr) {
|
||||
font_ref.reset(static_cast<CTFontDescriptorRef>(const_cast<void *>(settings->os_handle)));
|
||||
CFRetain(font_ref.get()); // Increase ref count to match a later release.
|
||||
}
|
||||
|
||||
if (!font_ref && MacOSVersionIsAtLeast(10, 6, 0)) {
|
||||
/* Might be a font file name, try load it. Direct font loading is
|
||||
* only supported starting on OSX 10.6. */
|
||||
CFAutoRelease<CFStringRef> path;
|
||||
|
||||
/* See if this is an absolute path. */
|
||||
if (FileExists(settings->font)) {
|
||||
path.reset(CFStringCreateWithCString(kCFAllocatorDefault, settings->font, kCFStringEncodingUTF8));
|
||||
} else {
|
||||
/* Scan the search-paths to see if it can be found. */
|
||||
std::string full_font = FioFindFullPath(BASE_DIR, settings->font);
|
||||
if (!full_font.empty()) {
|
||||
path.reset(CFStringCreateWithCString(kCFAllocatorDefault, full_font.c_str(), kCFStringEncodingUTF8));
|
||||
}
|
||||
}
|
||||
|
||||
if (path) {
|
||||
/* Try getting a font descriptor to see if the system can use it. */
|
||||
CFAutoRelease<CFURLRef> url(CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path.get(), kCFURLPOSIXPathStyle, false));
|
||||
CFAutoRelease<CFArrayRef> descs(CTFontManagerCreateFontDescriptorsFromURL(url.get()));
|
||||
|
||||
if (descs && CFArrayGetCount(descs.get()) > 0) {
|
||||
font_ref.reset((CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), 0));
|
||||
CFRetain(font_ref.get());
|
||||
} else {
|
||||
ShowInfoF("Unable to load file '%s' for %s font, using default OS font selection instead", settings->font, SIZE_TO_NAME[fs]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!font_ref) {
|
||||
CFAutoRelease<CFStringRef> name(CFStringCreateWithCString(kCFAllocatorDefault, settings->font, kCFStringEncodingUTF8));
|
||||
|
||||
/* Simply creating the font using CTFontCreateWithNameAndSize will *always* return
|
||||
* something, no matter the name. As such, we can't use it to check for existence.
|
||||
* We instead query the list of all font descriptors that match the given name which
|
||||
* does not do this stupid name fallback. */
|
||||
CFAutoRelease<CTFontDescriptorRef> name_desc(CTFontDescriptorCreateWithNameAndSize(name.get(), 0.0));
|
||||
CFAutoRelease<CFSetRef> mandatory_attribs(CFSetCreate(kCFAllocatorDefault, const_cast<const void **>(reinterpret_cast<const void * const *>(&kCTFontNameAttribute)), 1, &kCFTypeSetCallBacks));
|
||||
CFAutoRelease<CFArrayRef> descs(CTFontDescriptorCreateMatchingFontDescriptors(name_desc.get(), mandatory_attribs.get()));
|
||||
|
||||
/* Assume the first result is the one we want. */
|
||||
if (descs && CFArrayGetCount(descs.get()) > 0) {
|
||||
font_ref.reset((CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), 0));
|
||||
CFRetain(font_ref.get());
|
||||
}
|
||||
}
|
||||
|
||||
if (!font_ref) {
|
||||
ShowInfoF("Unable to use '%s' for %s font, using sprite font instead", settings->font, SIZE_TO_NAME[fs]);
|
||||
return;
|
||||
}
|
||||
|
||||
new CoreTextFontCache(fs, std::move(font_ref), settings->size);
|
||||
}
|
40
src/os/macosx/font_osx.h
Normal file
40
src/os/macosx/font_osx.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD 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 Foundation, version 2.
|
||||
* OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file font_osx.h Functions related to font handling on MacOS. */
|
||||
|
||||
#ifndef FONT_OSX_H
|
||||
#define FONT_OSX_H
|
||||
|
||||
#include "../../fontcache_internal.h"
|
||||
#include "os/macosx/macos.h"
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
class CoreTextFontCache : public TrueTypeFontCache {
|
||||
CFAutoRelease<CTFontDescriptorRef> font_desc; ///< Font descriptor exlcuding font size.
|
||||
CFAutoRelease<CTFontRef> font; ///< CoreText font handle.
|
||||
|
||||
std::string font_name; ///< Cached font name.
|
||||
|
||||
void SetFontSize(int pixels);
|
||||
const Sprite *InternalGetGlyph(GlyphID key, bool use_aa) override;
|
||||
const void *InternalGetFontTable(uint32 tag, size_t &length) override;
|
||||
public:
|
||||
CoreTextFontCache(FontSize fs, CFAutoRelease<CTFontDescriptorRef> &&font, int pixels);
|
||||
~CoreTextFontCache() {}
|
||||
|
||||
void ClearFontCache() override;
|
||||
GlyphID MapCharToGlyph(WChar key) override;
|
||||
const char *GetFontName() override { return font_name.c_str(); }
|
||||
bool IsBuiltInFont() override { return false; }
|
||||
const void *GetOSHandle() override { return font.get(); }
|
||||
};
|
||||
|
||||
void LoadCoreTextFont(FontSize fs);
|
||||
|
||||
#endif /* FONT_OSX_H */
|
@@ -1,185 +0,0 @@
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD 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 Foundation, version 2.
|
||||
* OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file splash.cpp Splash screen support for OSX. */
|
||||
|
||||
#include "../../stdafx.h"
|
||||
#include "../../openttd.h"
|
||||
#include "../../debug.h"
|
||||
#include "../../gfx_func.h"
|
||||
#include "../../fileio_func.h"
|
||||
#include "../../blitter/factory.hpp"
|
||||
#include "../../core/mem_func.hpp"
|
||||
|
||||
#include "splash.h"
|
||||
|
||||
#ifdef WITH_PNG
|
||||
|
||||
#include <png.h>
|
||||
|
||||
#include "../../safeguards.h"
|
||||
|
||||
/**
|
||||
* Handle pnglib error.
|
||||
*
|
||||
* @param png_ptr Pointer to png struct.
|
||||
* @param message Error message text.
|
||||
*/
|
||||
static void PNGAPI png_my_error(png_structp png_ptr, png_const_charp message)
|
||||
{
|
||||
DEBUG(misc, 0, "[libpng] error: %s - %s", message, (char *)png_get_error_ptr(png_ptr));
|
||||
longjmp(png_jmpbuf(png_ptr), 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle warning in pnglib.
|
||||
*
|
||||
* @param png_ptr Pointer to png struct.
|
||||
* @param message Warning message text.
|
||||
*/
|
||||
static void PNGAPI png_my_warning(png_structp png_ptr, png_const_charp message)
|
||||
{
|
||||
DEBUG(misc, 1, "[libpng] warning: %s - %s", message, (char *)png_get_error_ptr(png_ptr));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a splash image shown on startup (WITH_PNG).
|
||||
*/
|
||||
void DisplaySplashImage()
|
||||
{
|
||||
FILE *f = FioFOpenFile(SPLASH_IMAGE_FILE, "r", BASESET_DIR);
|
||||
if (f == nullptr) return;
|
||||
|
||||
png_byte header[8];
|
||||
fread(header, sizeof(png_byte), 8, f);
|
||||
if (png_sig_cmp(header, 0, 8) != 0) {
|
||||
fclose(f);
|
||||
return;
|
||||
}
|
||||
|
||||
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp) nullptr, png_my_error, png_my_warning);
|
||||
|
||||
if (png_ptr == nullptr) {
|
||||
fclose(f);
|
||||
return;
|
||||
}
|
||||
|
||||
png_infop info_ptr = png_create_info_struct(png_ptr);
|
||||
if (info_ptr == nullptr) {
|
||||
png_destroy_read_struct(&png_ptr, (png_infopp)nullptr, (png_infopp)nullptr);
|
||||
fclose(f);
|
||||
return;
|
||||
}
|
||||
|
||||
png_infop end_info = png_create_info_struct(png_ptr);
|
||||
if (end_info == nullptr) {
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)nullptr);
|
||||
fclose(f);
|
||||
return;
|
||||
}
|
||||
|
||||
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
|
||||
fclose(f);
|
||||
return;
|
||||
}
|
||||
|
||||
png_init_io(png_ptr, f);
|
||||
png_set_sig_bytes(png_ptr, 8);
|
||||
|
||||
png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, nullptr);
|
||||
|
||||
uint width = png_get_image_width(png_ptr, info_ptr);
|
||||
uint height = png_get_image_height(png_ptr, info_ptr);
|
||||
uint bit_depth = png_get_bit_depth(png_ptr, info_ptr);
|
||||
uint color_type = png_get_color_type(png_ptr, info_ptr);
|
||||
|
||||
if (color_type != PNG_COLOR_TYPE_PALETTE || bit_depth != 8) {
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
|
||||
fclose(f);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_PLTE)) {
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
|
||||
fclose(f);
|
||||
return;
|
||||
}
|
||||
|
||||
png_colorp palette;
|
||||
int num_palette;
|
||||
png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
|
||||
|
||||
png_bytep *row_pointers = png_get_rows(png_ptr, info_ptr);
|
||||
|
||||
if (width > (uint) _screen.width) width = _screen.width;
|
||||
if (height > (uint) _screen.height) height = _screen.height;
|
||||
|
||||
uint xoff = (_screen.width - width) / 2;
|
||||
uint yoff = (_screen.height - height) / 2;
|
||||
|
||||
switch (BlitterFactory::GetCurrentBlitter()->GetScreenDepth()) {
|
||||
case 8: {
|
||||
uint8 *dst_ptr = (uint8 *)_screen.dst_ptr;
|
||||
/* Initialize buffer */
|
||||
MemSetT(dst_ptr, 0xff, _screen.pitch * _screen.height);
|
||||
|
||||
for (uint y = 0; y < height; y++) {
|
||||
uint8 *src = row_pointers[y];
|
||||
uint8 *dst = dst_ptr + (yoff + y) * _screen.pitch + xoff;
|
||||
|
||||
memcpy(dst, src, width);
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_palette; i++) {
|
||||
_cur_palette.palette[i].a = i == 0 ? 0 : 0xff;
|
||||
_cur_palette.palette[i].r = palette[i].red;
|
||||
_cur_palette.palette[i].g = palette[i].green;
|
||||
_cur_palette.palette[i].b = palette[i].blue;
|
||||
}
|
||||
|
||||
_cur_palette.palette[0xff].a = 0xff;
|
||||
_cur_palette.palette[0xff].r = 0;
|
||||
_cur_palette.palette[0xff].g = 0;
|
||||
_cur_palette.palette[0xff].b = 0;
|
||||
|
||||
_cur_palette.first_dirty = 0;
|
||||
_cur_palette.count_dirty = 256;
|
||||
break;
|
||||
}
|
||||
case 32: {
|
||||
uint32 *dst_ptr = (uint32 *)_screen.dst_ptr;
|
||||
/* Initialize buffer */
|
||||
MemSetT(dst_ptr, 0, _screen.pitch * _screen.height);
|
||||
|
||||
for (uint y = 0; y < height; y++) {
|
||||
uint8 *src = row_pointers[y];
|
||||
uint32 *dst = dst_ptr + (yoff + y) * _screen.pitch + xoff;
|
||||
|
||||
for (uint x = 0; x < width; x++) {
|
||||
dst[x] = palette[src[x]].blue | (palette[src[x]].green << 8) | (palette[src[x]].red << 16) | 0xff000000;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
|
||||
fclose(f);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#else /* WITH_PNG */
|
||||
|
||||
/**
|
||||
* Empty 'Display a splash image' routine (WITHOUT_PNG).
|
||||
*/
|
||||
void DisplaySplashImage() {}
|
||||
|
||||
#endif /* WITH_PNG */
|
@@ -1,17 +0,0 @@
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD 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 Foundation, version 2.
|
||||
* OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file splash.h Functions to support splash screens for OSX. */
|
||||
|
||||
#ifndef SPLASH_H
|
||||
#define SPLASH_H
|
||||
|
||||
#define SPLASH_IMAGE_FILE "splash.png"
|
||||
|
||||
void DisplaySplashImage();
|
||||
|
||||
#endif /* SPLASH_H */
|
@@ -175,12 +175,16 @@ static CTRunDelegateCallbacks _sprite_font_callback = {
|
||||
for (const auto &i : fontMapping) {
|
||||
if (i.first - last == 0) continue;
|
||||
|
||||
if (!_font_cache[i.second->fc->GetSize()]) {
|
||||
/* Cache font information. */
|
||||
CFAutoRelease<CFStringRef> font_name(CFStringCreateWithCString(kCFAllocatorDefault, i.second->fc->GetFontName(), kCFStringEncodingUTF8));
|
||||
_font_cache[i.second->fc->GetSize()].reset(CTFontCreateWithName(font_name.get(), i.second->fc->GetFontSize(), nullptr));
|
||||
CTFontRef font = (CTFontRef)i.second->fc->GetOSHandle();
|
||||
if (font == nullptr) {
|
||||
if (!_font_cache[i.second->fc->GetSize()]) {
|
||||
/* Cache font information. */
|
||||
CFAutoRelease<CFStringRef> font_name(CFStringCreateWithCString(kCFAllocatorDefault, i.second->fc->GetFontName(), kCFStringEncodingUTF8));
|
||||
_font_cache[i.second->fc->GetSize()].reset(CTFontCreateWithName(font_name.get(), i.second->fc->GetFontSize(), nullptr));
|
||||
}
|
||||
font = _font_cache[i.second->fc->GetSize()].get();
|
||||
}
|
||||
CFAttributedStringSetAttribute(str.get(), CFRangeMake(last, i.first - last), kCTFontAttributeName, _font_cache[i.second->fc->GetSize()].get());
|
||||
CFAttributedStringSetAttribute(str.get(), CFRangeMake(last, i.first - last), kCTFontAttributeName, font);
|
||||
|
||||
CGColorRef color = CGColorCreateGenericGray((uint8)i.second->colour / 255.0f, 1.0f); // We don't care about the real colours, just that they are different.
|
||||
CFAttributedStringSetAttribute(str.get(), CFRangeMake(last, i.first - last), kCTForegroundColorAttributeName, color);
|
||||
@@ -301,7 +305,7 @@ void MacOSRegisterExternalFont(const char *file_path)
|
||||
CTFontManagerRegisterFontsForURL(url.get(), kCTFontManagerScopeProcess, nullptr);
|
||||
}
|
||||
|
||||
/** Store current language locale as a CoreFounation locale. */
|
||||
/** Store current language locale as a CoreFoundation locale. */
|
||||
void MacOSSetCurrentLocaleName(const char *iso_code)
|
||||
{
|
||||
if (!MacOSVersionIsAtLeast(10, 5, 0)) return;
|
||||
|
Reference in New Issue
Block a user