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:
Jonathan G Rennison
2021-02-19 15:38:34 +00:00
176 changed files with 4275 additions and 3826 deletions

View File

@@ -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
View 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
View 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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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;

View File

@@ -7,3 +7,8 @@ add_files(
unix.cpp
CONDITION UNIX AND NOT OPTION_OS2
)
add_files(
font_unix.cpp
CONDITION Fontconfig_FOUND
)

171
src/os/unix/font_unix.cpp Normal file
View File

@@ -0,0 +1,171 @@
/*
* 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_unix.cpp Functions related to font handling on Unix/Fontconfig. */
#include "../../stdafx.h"
#include "../../debug.h"
#include "../../fontdetection.h"
#include "../../string_func.h"
#include "../../strings_func.h"
#include <fontconfig/fontconfig.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;
if (!FcInit()) {
ShowInfoF("Unable to load font configuration");
} else {
FcPattern *match;
FcPattern *pat;
FcFontSet *fs;
FcResult result;
char *font_style;
char *font_family;
/* Split & strip the font's style */
font_family = stredup(font_name);
font_style = strchr(font_family, ',');
if (font_style != nullptr) {
font_style[0] = '\0';
font_style++;
while (*font_style == ' ' || *font_style == '\t') font_style++;
}
/* Resolve the name and populate the information structure */
pat = FcNameParse((FcChar8 *)font_family);
if (font_style != nullptr) FcPatternAddString(pat, FC_STYLE, (FcChar8 *)font_style);
FcConfigSubstitute(0, pat, FcMatchPattern);
FcDefaultSubstitute(pat);
fs = FcFontSetCreate();
match = FcFontMatch(0, pat, &result);
if (fs != nullptr && match != nullptr) {
int i;
FcChar8 *family;
FcChar8 *style;
FcChar8 *file;
FcFontSetAdd(fs, match);
for (i = 0; err != FT_Err_Ok && i < fs->nfont; i++) {
/* Try the new filename */
if (FcPatternGetString(fs->fonts[i], FC_FILE, 0, &file) == FcResultMatch &&
FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, &family) == FcResultMatch &&
FcPatternGetString(fs->fonts[i], FC_STYLE, 0, &style) == FcResultMatch) {
/* The correct style? */
if (font_style != nullptr && strcasecmp(font_style, (char *)style) != 0) continue;
/* Font config takes the best shot, which, if the family name is spelled
* wrongly a 'random' font, so check whether the family name is the
* same as the supplied name */
if (strcasecmp(font_family, (char *)family) == 0) {
err = FT_New_Face(_library, (char *)file, 0, face);
}
}
}
}
free(font_family);
FcPatternDestroy(pat);
FcFontSetDestroy(fs);
FcFini();
}
return err;
}
#endif /* WITH_FREETYPE */
bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback)
{
if (!FcInit()) return false;
bool ret = false;
/* Fontconfig doesn't handle full language isocodes, only the part
* before the _ of e.g. en_GB is used, so "remove" everything after
* the _. */
char lang[16];
seprintf(lang, lastof(lang), ":lang=%s", language_isocode);
char *split = strchr(lang, '_');
if (split != nullptr) *split = '\0';
/* First create a pattern to match the wanted language. */
FcPattern *pat = FcNameParse((FcChar8 *)lang);
/* We only want to know the filename. */
FcObjectSet *os = FcObjectSetBuild(FC_FILE, FC_SPACING, FC_SLANT, FC_WEIGHT, nullptr);
/* Get the list of filenames matching the wanted language. */
FcFontSet *fs = FcFontList(nullptr, pat, os);
/* We don't need these anymore. */
FcObjectSetDestroy(os);
FcPatternDestroy(pat);
if (fs != nullptr) {
int best_weight = -1;
const char *best_font = nullptr;
for (int i = 0; i < fs->nfont; i++) {
FcPattern *font = fs->fonts[i];
FcChar8 *file = nullptr;
FcResult res = FcPatternGetString(font, FC_FILE, 0, &file);
if (res != FcResultMatch || file == nullptr) {
continue;
}
/* Get a font with the right spacing .*/
int value = 0;
FcPatternGetInteger(font, FC_SPACING, 0, &value);
if (callback->Monospace() != (value == FC_MONO) && value != FC_DUAL) continue;
/* Do not use those that explicitly say they're slanted. */
FcPatternGetInteger(font, FC_SLANT, 0, &value);
if (value != 0) continue;
/* We want the fatter font as they look better at small sizes. */
FcPatternGetInteger(font, FC_WEIGHT, 0, &value);
if (value <= best_weight) continue;
callback->SetFontNames(settings, (const char *)file);
bool missing = callback->FindMissingGlyphs();
DEBUG(freetype, 1, "Font \"%s\" misses%s glyphs", file, missing ? "" : " no");
if (!missing) {
best_weight = value;
best_font = (const char *)file;
}
}
if (best_font != nullptr) {
ret = true;
callback->SetFontNames(settings, best_font);
InitFreeType(callback->Monospace());
}
/* Clean up the list of filenames. */
FcFontSetDestroy(fs);
}
FcFini();
return ret;
}

View File

@@ -28,6 +28,10 @@
#include <SDL.h>
#endif
#ifdef __EMSCRIPTEN__
# include <emscripten.h>
#endif
#ifdef __APPLE__
# include <sys/mount.h>
#elif (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L) || defined(__GLIBC__)
@@ -288,7 +292,13 @@ bool GetClipboardContents(char *buffer, const char *last)
#endif
#ifndef __APPLE__
#if defined(__EMSCRIPTEN__)
void OSOpenBrowser(const char *url)
{
/* Implementation in pre.js */
EM_ASM({ if(window["openttd_open_url"]) window.openttd_open_url($0, $1) }, url, strlen(url));
}
#elif !defined( __APPLE__)
void OSOpenBrowser(const char *url)
{
pid_t child_pid = fork();

View File

@@ -1,5 +1,7 @@
add_files(
crashlog_win.cpp
font_win32.cpp
font_win32.h
string_uniscribe.cpp
string_uniscribe.h
win32.cpp

View File

@@ -27,6 +27,7 @@
#endif
#include <windows.h>
#include <mmsystem.h>
#include <signal.h>
#include "../../safeguards.h"
@@ -617,6 +618,9 @@ void *_safe_esp = nullptr;
static LONG WINAPI ExceptionHandler(EXCEPTION_POINTERS *ep)
{
/* Restore system timer resolution. */
timeEndPeriod(1);
/* Disable our event loop. */
SetWindowLongPtr(GetActiveWindow(), GWLP_WNDPROC, (LONG_PTR)&DefWindowProc);

View File

@@ -0,0 +1,677 @@
/*
* 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_win32.cpp Functions related to font handling on Win32. */
#include "../../stdafx.h"
#include "../../debug.h"
#include "font_win32.h"
#include "../../blitter/factory.hpp"
#include "../../core/alloc_func.hpp"
#include "../../core/math_func.hpp"
#include "../../fileio_func.h"
#include "../../fontdetection.h"
#include "../../fontcache.h"
#include "../../string_func.h"
#include "../../strings_func.h"
#include "../../zoom_func.h"
#include "../../table/control_codes.h"
#include <windows.h>
#include <shlobj.h> /* SHGetFolderPath */
#include "os/windows/win32.h"
#undef small // Say what, Windows?
#include "safeguards.h"
#ifdef WITH_FREETYPE
#include <ft2build.h>
#include FT_FREETYPE_H
extern FT_Library _library;
/**
* Get the short DOS 8.3 format for paths.
* FreeType doesn't support Unicode filenames and Windows' fopen (as used
* by FreeType) doesn't support UTF-8 filenames. So we have to convert the
* filename into something that isn't UTF-8 but represents the Unicode file
* name. This is the short DOS 8.3 format. This does not contain any
* characters that fopen doesn't support.
* @param long_path the path in system encoding.
* @return the short path in ANSI (ASCII).
*/
static const char *GetShortPath(const TCHAR *long_path)
{
static char short_path[MAX_PATH];
#ifdef UNICODE
WCHAR short_path_w[MAX_PATH];
GetShortPathName(long_path, short_path_w, lengthof(short_path_w));
WideCharToMultiByte(CP_ACP, 0, short_path_w, -1, short_path, lengthof(short_path), nullptr, nullptr);
#else
/* Technically not needed, but do it for consistency. */
GetShortPathName(long_path, short_path, lengthof(short_path));
#endif
return short_path;
}
/* Get the font file to be loaded into Freetype by looping the registry
* location where windows lists all installed fonts. Not very nice, will
* surely break if the registry path changes, but it works. Much better
* solution would be to use CreateFont, and extract the font data from it
* by GetFontData. The problem with this is that the font file needs to be
* kept in memory then until the font is no longer needed. This could mean
* an additional memory usage of 30MB (just for fonts!) when using an eastern
* font for all font sizes */
#define FONT_DIR_NT "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"
#define FONT_DIR_9X "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts"
FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
{
FT_Error err = FT_Err_Cannot_Open_Resource;
HKEY hKey;
LONG ret;
TCHAR vbuffer[MAX_PATH], dbuffer[256];
TCHAR *pathbuf;
const char *font_path;
uint index;
size_t path_len;
/* On windows NT (2000, NT3.5, XP, etc.) the fonts are stored in the
* "Windows NT" key, on Windows 9x in the Windows key. To save us having
* to retrieve the windows version, we'll just query both */
ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(FONT_DIR_NT), 0, KEY_READ, &hKey);
if (ret != ERROR_SUCCESS) ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(FONT_DIR_9X), 0, KEY_READ, &hKey);
if (ret != ERROR_SUCCESS) {
DEBUG(freetype, 0, "Cannot open registry key HKLM\\SOFTWARE\\Microsoft\\Windows (NT)\\CurrentVersion\\Fonts");
return err;
}
/* Convert font name to file system encoding. */
TCHAR *font_namep = _tcsdup(OTTD2FS(font_name));
for (index = 0;; index++) {
TCHAR *s;
DWORD vbuflen = lengthof(vbuffer);
DWORD dbuflen = lengthof(dbuffer);
ret = RegEnumValue(hKey, index, vbuffer, &vbuflen, nullptr, nullptr, (byte *)dbuffer, &dbuflen);
if (ret != ERROR_SUCCESS) goto registry_no_font_found;
/* The font names in the registry are of the following 3 forms:
* - ADMUI3.fon
* - Book Antiqua Bold (TrueType)
* - Batang & BatangChe & Gungsuh & GungsuhChe (TrueType)
* We will strip the font-type '()' if any and work with the font name
* itself, which must match exactly; if...
* TTC files, font files which contain more than one font are separated
* by '&'. Our best bet will be to do substr match for the fontname
* and then let FreeType figure out which index to load */
s = _tcschr(vbuffer, _T('('));
if (s != nullptr) s[-1] = '\0';
if (_tcschr(vbuffer, _T('&')) == nullptr) {
if (_tcsicmp(vbuffer, font_namep) == 0) break;
} else {
if (_tcsstr(vbuffer, font_namep) != nullptr) break;
}
}
if (!SUCCEEDED(OTTDSHGetFolderPath(nullptr, CSIDL_FONTS, nullptr, SHGFP_TYPE_CURRENT, vbuffer))) {
DEBUG(freetype, 0, "SHGetFolderPath cannot return fonts directory");
goto folder_error;
}
/* Some fonts are contained in .ttc files, TrueType Collection fonts. These
* contain multiple fonts inside this single file. GetFontData however
* returns the whole file, so we need to check each font inside to get the
* proper font. */
path_len = _tcslen(vbuffer) + _tcslen(dbuffer) + 2; // '\' and terminating nul.
pathbuf = AllocaM(TCHAR, path_len);
_sntprintf(pathbuf, path_len, _T("%s\\%s"), vbuffer, dbuffer);
/* Convert the path into something that FreeType understands. */
font_path = GetShortPath(pathbuf);
index = 0;
do {
err = FT_New_Face(_library, font_path, index, face);
if (err != FT_Err_Ok) break;
if (strncasecmp(font_name, (*face)->family_name, strlen((*face)->family_name)) == 0) break;
/* Try english name if font name failed */
if (strncasecmp(font_name + strlen(font_name) + 1, (*face)->family_name, strlen((*face)->family_name)) == 0) break;
err = FT_Err_Cannot_Open_Resource;
} while ((FT_Long)++index != (*face)->num_faces);
folder_error:
registry_no_font_found:
free(font_namep);
RegCloseKey(hKey);
return err;
}
/**
* Fonts can have localised names and when the system locale is the same as
* one of those localised names Windows will always return that localised name
* instead of allowing to get the non-localised (English US) name of the font.
* This will later on give problems as freetype uses the non-localised name of
* the font and we need to compare based on that name.
* Windows furthermore DOES NOT have an API to get the non-localised name nor
* can we override the system locale. This means that we have to actually read
* the font itself to gather the font name we want.
* Based on: http://blogs.msdn.com/michkap/archive/2006/02/13/530814.aspx
* @param logfont the font information to get the english name of.
* @return the English name (if it could be found).
*/
static const char *GetEnglishFontName(const ENUMLOGFONTEX *logfont)
{
static char font_name[MAX_PATH];
const char *ret_font_name = nullptr;
uint pos = 0;
HDC dc;
HGDIOBJ oldfont;
byte *buf;
DWORD dw;
uint16 format, count, stringOffset, platformId, encodingId, languageId, nameId, length, offset;
HFONT font = CreateFontIndirect(&logfont->elfLogFont);
if (font == nullptr) goto err1;
dc = GetDC(nullptr);
oldfont = SelectObject(dc, font);
dw = GetFontData(dc, 'eman', 0, nullptr, 0);
if (dw == GDI_ERROR) goto err2;
buf = MallocT<byte>(dw);
dw = GetFontData(dc, 'eman', 0, buf, dw);
if (dw == GDI_ERROR) goto err3;
format = buf[pos++] << 8;
format += buf[pos++];
assert(format == 0);
count = buf[pos++] << 8;
count += buf[pos++];
stringOffset = buf[pos++] << 8;
stringOffset += buf[pos++];
for (uint i = 0; i < count; i++) {
platformId = buf[pos++] << 8;
platformId += buf[pos++];
encodingId = buf[pos++] << 8;
encodingId += buf[pos++];
languageId = buf[pos++] << 8;
languageId += buf[pos++];
nameId = buf[pos++] << 8;
nameId += buf[pos++];
if (nameId != 1) {
pos += 4; // skip length and offset
continue;
}
length = buf[pos++] << 8;
length += buf[pos++];
offset = buf[pos++] << 8;
offset += buf[pos++];
/* Don't buffer overflow */
length = std::min<uint16>(length, MAX_PATH - 1);
for (uint j = 0; j < length; j++) font_name[j] = buf[stringOffset + offset + j];
font_name[length] = '\0';
if ((platformId == 1 && languageId == 0) || // Macintosh English
(platformId == 3 && languageId == 0x0409)) { // Microsoft English (US)
ret_font_name = font_name;
break;
}
}
err3:
free(buf);
err2:
SelectObject(dc, oldfont);
ReleaseDC(nullptr, dc);
DeleteObject(font);
err1:
return ret_font_name == nullptr ? WIDE_TO_MB((const TCHAR *)logfont->elfFullName) : ret_font_name;
}
#endif /* WITH_FREETYPE */
class FontList {
protected:
TCHAR **fonts;
uint items;
uint capacity;
public:
FontList() : fonts(nullptr), items(0), capacity(0) { };
~FontList() {
if (this->fonts == nullptr) return;
for (uint i = 0; i < this->items; i++) {
free(this->fonts[i]);
}
free(this->fonts);
}
bool Add(const TCHAR *font) {
for (uint i = 0; i < this->items; i++) {
if (_tcscmp(this->fonts[i], font) == 0) return false;
}
if (this->items == this->capacity) {
this->capacity += 10;
this->fonts = ReallocT(this->fonts, this->capacity);
}
this->fonts[this->items++] = _tcsdup(font);
return true;
}
};
struct EFCParam {
FreeTypeSettings *settings;
LOCALESIGNATURE locale;
MissingGlyphSearcher *callback;
FontList fonts;
};
static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *logfont, const NEWTEXTMETRICEX *metric, DWORD type, LPARAM lParam)
{
EFCParam *info = (EFCParam *)lParam;
/* Skip duplicates */
if (!info->fonts.Add((const TCHAR *)logfont->elfFullName)) return 1;
/* Only use TrueType fonts */
if (!(type & TRUETYPE_FONTTYPE)) return 1;
/* Don't use SYMBOL fonts */
if (logfont->elfLogFont.lfCharSet == SYMBOL_CHARSET) return 1;
/* Use monospaced fonts when asked for it. */
if (info->callback->Monospace() && (logfont->elfLogFont.lfPitchAndFamily & (FF_MODERN | FIXED_PITCH)) != (FF_MODERN | FIXED_PITCH)) return 1;
/* The font has to have at least one of the supported locales to be usable. */
if ((metric->ntmFontSig.fsCsb[0] & info->locale.lsCsbSupported[0]) == 0 && (metric->ntmFontSig.fsCsb[1] & info->locale.lsCsbSupported[1]) == 0) {
/* On win9x metric->ntmFontSig seems to contain garbage. */
FONTSIGNATURE fs;
memset(&fs, 0, sizeof(fs));
HFONT font = CreateFontIndirect(&logfont->elfLogFont);
if (font != nullptr) {
HDC dc = GetDC(nullptr);
HGDIOBJ oldfont = SelectObject(dc, font);
GetTextCharsetInfo(dc, &fs, 0);
SelectObject(dc, oldfont);
ReleaseDC(nullptr, dc);
DeleteObject(font);
}
if ((fs.fsCsb[0] & info->locale.lsCsbSupported[0]) == 0 && (fs.fsCsb[1] & info->locale.lsCsbSupported[1]) == 0) return 1;
}
char font_name[MAX_PATH];
convert_from_fs((const TCHAR *)logfont->elfFullName, font_name, lengthof(font_name));
#ifdef WITH_FREETYPE
/* Add english name after font name */
const char *english_name = GetEnglishFontName(logfont);
strecpy(font_name + strlen(font_name) + 1, english_name, lastof(font_name));
/* Check whether we can actually load the font. */
bool ft_init = _library != nullptr;
bool found = false;
FT_Face face;
/* Init FreeType if needed. */
if ((ft_init || FT_Init_FreeType(&_library) == FT_Err_Ok) && GetFontByFaceName(font_name, &face) == FT_Err_Ok) {
FT_Done_Face(face);
found = true;
}
if (!ft_init) {
/* Uninit FreeType if we did the init. */
FT_Done_FreeType(_library);
_library = nullptr;
}
if (!found) return 1;
#else
const char *english_name = font_name;
#endif /* WITH_FREETYPE */
info->callback->SetFontNames(info->settings, font_name, &logfont->elfLogFont);
if (info->callback->FindMissingGlyphs()) return 1;
DEBUG(freetype, 1, "Fallback font: %s (%s)", font_name, english_name);
return 0; // stop enumerating
}
bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback)
{
DEBUG(freetype, 1, "Trying fallback fonts");
EFCParam langInfo;
if (GetLocaleInfo(MAKELCID(winlangid, SORT_DEFAULT), LOCALE_FONTSIGNATURE, (LPTSTR)&langInfo.locale, sizeof(langInfo.locale) / sizeof(TCHAR)) == 0) {
/* Invalid langid or some other mysterious error, can't determine fallback font. */
DEBUG(freetype, 1, "Can't get locale info for fallback font (langid=0x%x)", winlangid);
return false;
}
langInfo.settings = settings;
langInfo.callback = callback;
LOGFONT font;
/* Enumerate all fonts. */
font.lfCharSet = DEFAULT_CHARSET;
font.lfFaceName[0] = '\0';
font.lfPitchAndFamily = 0;
HDC dc = GetDC(nullptr);
int ret = EnumFontFamiliesEx(dc, &font, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&langInfo, 0);
ReleaseDC(nullptr, dc);
return ret == 0;
}
#ifndef ANTIALIASED_QUALITY
#define ANTIALIASED_QUALITY 4
#endif
/**
* Create a new Win32FontCache.
* @param fs The font size that is going to be cached.
* @param logfont The font that has to be loaded.
* @param pixels The number of pixels this font should be high.
*/
Win32FontCache::Win32FontCache(FontSize fs, const LOGFONT &logfont, int pixels) : TrueTypeFontCache(fs, pixels), logfont(logfont)
{
this->dc = CreateCompatibleDC(nullptr);
this->SetFontSize(fs, pixels);
}
Win32FontCache::~Win32FontCache()
{
this->ClearFontCache();
DeleteDC(this->dc);
DeleteObject(this->font);
}
void Win32FontCache::SetFontSize(FontSize fs, int pixels)
{
if (pixels == 0) {
/* Try to determine a good height based on the minimal height recommended by the font. */
int scaled_height = ScaleFontTrad(this->GetDefaultFontHeight(this->fs));
pixels = scaled_height;
HFONT temp = CreateFontIndirect(&this->logfont);
if (temp != nullptr) {
HGDIOBJ old = SelectObject(this->dc, temp);
UINT size = GetOutlineTextMetrics(this->dc, 0, nullptr);
LPOUTLINETEXTMETRIC otm = (LPOUTLINETEXTMETRIC)AllocaM(BYTE, size);
GetOutlineTextMetrics(this->dc, size, otm);
/* 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(otm->otmusMinimumPPEM, MAX_FONT_MIN_REC_SIZE) + diff, scaled_height, MAX_FONT_SIZE);
SelectObject(dc, old);
DeleteObject(temp);
}
} else {
pixels = ScaleFontTrad(pixels);
}
this->used_size = pixels;
/* Create GDI font handle. */
this->logfont.lfHeight = -pixels;
this->logfont.lfWidth = 0;
this->logfont.lfOutPrecision = ANTIALIASED_QUALITY;
if (this->font != nullptr) {
SelectObject(dc, this->old_font);
DeleteObject(this->font);
}
this->font = CreateFontIndirect(&this->logfont);
this->old_font = SelectObject(this->dc, this->font);
/* Query the font metrics we needed. */
UINT otmSize = GetOutlineTextMetrics(this->dc, 0, nullptr);
POUTLINETEXTMETRIC otm = (POUTLINETEXTMETRIC)AllocaM(BYTE, otmSize);
GetOutlineTextMetrics(this->dc, otmSize, otm);
this->units_per_em = otm->otmEMSquare;
this->ascender = otm->otmTextMetrics.tmAscent;
this->descender = otm->otmTextMetrics.tmDescent;
this->height = this->ascender + this->descender;
this->glyph_size.cx = otm->otmTextMetrics.tmMaxCharWidth;
this->glyph_size.cy = otm->otmTextMetrics.tmHeight;
font_height_cache[this->fs] = this->GetHeight();
DEBUG(freetype, 2, "Loaded font '%s' with size %d", FS2OTTD((LPTSTR)((BYTE *)otm + (ptrdiff_t)otm->otmpFullName)), pixels);
}
/**
* Reset cached glyphs.
*/
void Win32FontCache::ClearFontCache()
{
/* GUI scaling might have changed, determine font size anew if it was automatically selected. */
if (this->font != nullptr) this->SetFontSize(this->fs, this->req_size);
this->TrueTypeFontCache::ClearFontCache();
}
/* virtual */ const Sprite *Win32FontCache::InternalGetGlyph(GlyphID key, bool aa)
{
GLYPHMETRICS gm;
MAT2 mat = { {0, 1}, {0, 0}, {0, 0}, {0, 1} };
/* Make a guess for the needed memory size. */
DWORD size = this->glyph_size.cy * Align(aa ? this->glyph_size.cx : std::max(this->glyph_size.cx / 8l, 1l), 4); // Bitmap data is DWORD-aligned rows.
byte *bmp = AllocaM(byte, size);
size = GetGlyphOutline(this->dc, key, GGO_GLYPH_INDEX | (aa ? GGO_GRAY8_BITMAP : GGO_BITMAP), &gm, size, bmp, &mat);
if (size == GDI_ERROR) {
/* No dice with the guess. First query size of needed glyph memory, then allocate the
* memory and query again. This dance is necessary as some glyphs will only render with
* the exact matching size; e.g. the space glyph has no pixels and must be requested
* with size == 0, anything else fails. Unfortunately, a failed call doesn't return any
* info about the size and thus the triple GetGlyphOutline()-call. */
size = GetGlyphOutline(this->dc, key, GGO_GLYPH_INDEX | (aa ? GGO_GRAY8_BITMAP : GGO_BITMAP), &gm, 0, nullptr, &mat);
if (size == GDI_ERROR) usererror("Unable to render font glyph");
bmp = AllocaM(byte, size);
GetGlyphOutline(this->dc, key, GGO_GLYPH_INDEX | (aa ? GGO_GRAY8_BITMAP : GGO_BITMAP), &gm, size, bmp, &mat);
}
/* Add 1 pixel for the shadow on the medium font. Our sprite must be at least 1x1 pixel. */
uint width = std::max(1U, (uint)gm.gmBlackBoxX + (this->fs == FS_NORMAL));
uint height = std::max(1U, (uint)gm.gmBlackBoxY + (this->fs == FS_NORMAL));
/* Limit glyph size to prevent overflows later on. */
if (width > MAX_GLYPH_DIM || height > MAX_GLYPH_DIM) usererror("Font glyph is too large");
/* GDI has rendered the glyph, now we allocate a sprite and copy the image into it. */
SpriteLoader::Sprite sprite;
sprite.AllocateData(ZOOM_LVL_NORMAL, width * height);
sprite.type = ST_FONT;
sprite.width = width;
sprite.height = height;
sprite.x_offs = gm.gmptGlyphOrigin.x;
sprite.y_offs = this->ascender - gm.gmptGlyphOrigin.y;
if (size > 0) {
/* All pixel data returned by GDI is in the form of DWORD-aligned rows.
* For a non anti-aliased glyph, the returned bitmap has one bit per pixel.
* For anti-aliased rendering, GDI uses the strange value range of 0 to 64,
* inclusively. To map this to 0 to 255, we shift left by two and then
* subtract one. */
uint pitch = Align(aa ? gm.gmBlackBoxX : std::max(gm.gmBlackBoxX / 8u, 1u), 4);
/* Draw shadow for medium size. */
if (this->fs == FS_NORMAL && !aa) {
for (uint y = 0; y < gm.gmBlackBoxY; y++) {
for (uint x = 0; x < gm.gmBlackBoxX; x++) {
if (aa ? (bmp[x + y * pitch] > 0) : HasBit(bmp[(x / 8) + y * pitch], 7 - (x % 8))) {
sprite.data[1 + x + (1 + y) * sprite.width].m = SHADOW_COLOUR;
sprite.data[1 + x + (1 + y) * sprite.width].a = aa ? (bmp[x + y * pitch] << 2) - 1 : 0xFF;
}
}
}
}
for (uint y = 0; y < gm.gmBlackBoxY; y++) {
for (uint x = 0; x < gm.gmBlackBoxX; x++) {
if (aa ? (bmp[x + y * pitch] > 0) : HasBit(bmp[(x / 8) + y * pitch], 7 - (x % 8))) {
sprite.data[x + y * sprite.width].m = FACE_COLOUR;
sprite.data[x + y * sprite.width].a = aa ? (bmp[x + y * pitch] << 2) - 1 : 0xFF;
}
}
}
}
GlyphEntry new_glyph;
new_glyph.sprite = BlitterFactory::GetCurrentBlitter()->Encode(&sprite, AllocateFont);
new_glyph.width = gm.gmCellIncX;
this->SetGlyphPtr(key, &new_glyph);
return new_glyph.sprite;
}
/* virtual */ GlyphID Win32FontCache::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 BMP into surrogate pairs. */
WCHAR chars[2];
if (key >= 0x010000U) {
chars[0] = (WCHAR)(((key - 0x010000U) >> 10) + 0xD800);
chars[1] = (WCHAR)(((key - 0x010000U) & 0x3FF) + 0xDC00);
} else {
chars[0] = (WCHAR)(key & 0xFFFF);
}
WORD glyphs[2] = { 0, 0 };
GetGlyphIndicesW(this->dc, chars, key >= 0x010000U ? 2 : 1, glyphs, GGI_MARK_NONEXISTING_GLYPHS);
return glyphs[0] != 0xFFFF ? glyphs[0] : 0;
}
/* virtual */ const void *Win32FontCache::InternalGetFontTable(uint32 tag, size_t &length)
{
DWORD len = GetFontData(this->dc, tag, 0, nullptr, 0);
void *result = nullptr;
if (len != GDI_ERROR && len > 0) {
result = MallocT<BYTE>(len);
GetFontData(this->dc, tag, 0, result, len);
}
length = len;
return result;
}
/**
* Loads the GDI font.
* If a GDI 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 LoadWin32Font(FontSize fs)
{
static const char *SIZE_TO_NAME[] = { "medium", "small", "large", "mono" };
FreeTypeSubSetting *settings = nullptr;
switch (fs) {
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;
default: NOT_REACHED();
}
if (StrEmpty(settings->font)) return;
LOGFONT logfont;
MemSetT(&logfont, 0);
logfont.lfPitchAndFamily = fs == FS_MONO ? FIXED_PITCH : VARIABLE_PITCH;
logfont.lfCharSet = DEFAULT_CHARSET;
logfont.lfOutPrecision = OUT_OUTLINE_PRECIS;
logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
if (settings->os_handle != nullptr) {
logfont = *(const LOGFONT *)settings->os_handle;
} else if (strchr(settings->font, '.') != nullptr) {
/* Might be a font file name, try load it. */
TCHAR fontPath[MAX_PATH] = {};
/* See if this is an absolute path. */
if (FileExists(settings->font)) {
convert_to_fs(settings->font, fontPath, lengthof(fontPath), false);
} 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()) {
convert_to_fs(full_font.c_str(), fontPath, lengthof(fontPath), false);
}
}
if (fontPath[0] != 0) {
if (AddFontResourceEx(fontPath, FR_PRIVATE, 0) != 0) {
/* Try a nice little undocumented function first for getting the internal font name.
* Some documentation is found at: http://www.undocprint.org/winspool/getfontresourceinfo */
typedef BOOL(WINAPI *PFNGETFONTRESOURCEINFO)(LPCTSTR, LPDWORD, LPVOID, DWORD);
#ifdef UNICODE
static PFNGETFONTRESOURCEINFO GetFontResourceInfo = (PFNGETFONTRESOURCEINFO)GetProcAddress(GetModuleHandle(_T("Gdi32")), "GetFontResourceInfoW");
#else
static PFNGETFONTRESOURCEINFO GetFontResourceInfo = (PFNGETFONTRESOURCEINFO)GetProcAddress(GetModuleHandle(_T("Gdi32")), "GetFontResourceInfoA");
#endif
if (GetFontResourceInfo != nullptr) {
/* Try to query an array of LOGFONTs that describe the file. */
DWORD len = 0;
if (GetFontResourceInfo(fontPath, &len, nullptr, 2) && len >= sizeof(LOGFONT)) {
LOGFONT *buf = (LOGFONT *)AllocaM(byte, len);
if (GetFontResourceInfo(fontPath, &len, buf, 2)) {
logfont = *buf; // Just use first entry.
}
}
}
/* No dice yet. Use the file name as the font face name, hoping it matches. */
if (logfont.lfFaceName[0] == 0) {
TCHAR fname[_MAX_FNAME];
_tsplitpath(fontPath, nullptr, nullptr, fname, nullptr);
_tcsncpy_s(logfont.lfFaceName, lengthof(logfont.lfFaceName), fname, _TRUNCATE);
logfont.lfWeight = strcasestr(settings->font, " bold") != nullptr || strcasestr(settings->font, "-bold") != nullptr ? FW_BOLD : FW_NORMAL; // Poor man's way to allow selecting bold fonts.
}
} else {
ShowInfoF("Unable to load file '%s' for %s font, using default windows font selection instead", settings->font, SIZE_TO_NAME[fs]);
}
}
}
if (logfont.lfFaceName[0] == 0) {
logfont.lfWeight = strcasestr(settings->font, " bold") != nullptr ? FW_BOLD : FW_NORMAL; // Poor man's way to allow selecting bold fonts.
convert_to_fs(settings->font, logfont.lfFaceName, lengthof(logfont.lfFaceName), false);
}
HFONT font = CreateFontIndirect(&logfont);
if (font == nullptr) {
ShowInfoF("Unable to use '%s' for %s font, Win32 reported error 0x%lX, using sprite font instead", settings->font, SIZE_TO_NAME[fs], GetLastError());
return;
}
DeleteObject(font);
new Win32FontCache(fs, logfont, settings->size);
}

View File

@@ -0,0 +1,42 @@
/*
* 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_win32.h Functions related to font handling on Win32. */
#ifndef FONT_WIN32_H
#define FONT_WIN32_H
#include "../../fontcache_internal.h"
#include "win32.h"
/** Font cache for fonts that are based on a Win32 font. */
class Win32FontCache : public TrueTypeFontCache {
private:
LOGFONT logfont; ///< Logical font information for selecting the font face.
HFONT font = nullptr; ///< The font face associated with this font.
HDC dc = nullptr; ///< Cached GDI device context.
HGDIOBJ old_font; ///< Old font selected into the GDI context.
SIZE glyph_size; ///< Maximum size of regular glyphs.
void SetFontSize(FontSize fs, int pixels);
protected:
const void *InternalGetFontTable(uint32 tag, size_t &length) override;
const Sprite *InternalGetGlyph(GlyphID key, bool aa) override;
public:
Win32FontCache(FontSize fs, const LOGFONT &logfont, int pixels);
~Win32FontCache();
void ClearFontCache() override;
GlyphID MapCharToGlyph(WChar key) override;
const char *GetFontName() override { return WIDE_TO_MB(this->logfont.lfFaceName); }
const void *GetOSHandle() override { return &this->logfont; }
};
void LoadWin32Font(FontSize fs);
#endif /* FONT_WIN32_H */

View File

@@ -144,7 +144,7 @@ void UniscribeResetScriptCache(FontSize size)
/** Load the matching native Windows font. */
static HFONT HFontFromFont(Font *font)
{
if (font->fc->GetOSHandle() != nullptr) return CreateFontIndirect((PLOGFONT)font->fc->GetOSHandle());
if (font->fc->GetOSHandle() != nullptr) return CreateFontIndirect((const PLOGFONT)font->fc->GetOSHandle());
LOGFONT logfont;
ZeroMemory(&logfont, sizeof(LOGFONT));

View File

@@ -14,6 +14,7 @@
#include "../../fileio_func.h"
#include <windows.h>
#include <fcntl.h>
#include <mmsystem.h>
#include <regstr.h>
#define NO_SHOBJIDL_SORTDIRECTION // Avoid multiple definition of SORT_ASCENDING
#include <shlobj.h> /* SHGetFolderPath */
@@ -439,6 +440,9 @@ int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi
int argc;
char *argv[64]; // max 64 command line arguments
/* Set system timer resolution to 1ms. */
timeBeginPeriod(1);
CrashLog::InitialiseCrashLog();
#if defined(UNICODE)
@@ -466,6 +470,10 @@ int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi
for (int i = 0; i < argc; i++) ValidateString(argv[i]);
openttd_main(argc, argv);
/* Restore system timer resolution. */
timeEndPeriod(1);
free(cmdline);
return 0;
}