Page Menu
Home
Phabricator (Chris)
Search
Configure Global Search
Log In
Files
F118945
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
154 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/Functions.cpp b/src/Functions.cpp
index 8b2f86e..bc6871b 100644
--- a/src/Functions.cpp
+++ b/src/Functions.cpp
@@ -1,1568 +1,1574 @@
/*
* Copyright (C) 2011-2013 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow 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, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow 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 Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <math.h>
#include <locale.h>
#include <algorithm>
#include <SDL.h>
#include <SDL_mixer.h>
#include <SDL_syswm.h>
#include <SDL_ttf.h>
#include <string>
#include "Globals.h"
#include "Functions.h"
#include "FileManager.h"
#include "GameObjects.h"
#include "LevelPack.h"
#include "TitleMenu.h"
#include "OptionsMenu.h"
#include "CreditsMenu.h"
#include "LevelEditSelect.h"
#include "LevelEditor.h"
#include "Game.h"
#include "LevelPlaySelect.h"
#include "Addons.h"
#include "InputManager.h"
#include "ImageManager.h"
#include "MusicManager.h"
#include "SoundManager.h"
#include "ScriptExecutor.h"
#include "LevelPackManager.h"
#include "ThemeManager.h"
#include "GUIListBox.h"
#include "GUIOverlay.h"
#include "StatisticsManager.h"
#include "StatisticsScreen.h"
#include "Cursors.h"
#include "ScriptAPI.h"
#include "libs/tinyformat/tinyformat.h"
#include "libs/tinygettext/tinygettext.hpp"
#include "libs/tinygettext/log.hpp"
#include "libs/findlocale/findlocale.h"
using namespace std;
#ifdef WIN32
#include <windows.h>
#include <shellapi.h>
#include <shlobj.h>
#define TO_UTF8(SRC, DEST) WideCharToMultiByte(CP_UTF8, 0, SRC, -1, DEST, sizeof(DEST), NULL, NULL)
#define TO_UTF16(SRC, DEST) MultiByteToWideChar(CP_UTF8, 0, SRC, -1, DEST, sizeof(DEST)/sizeof(DEST[0]))
#else
#include <strings.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <dirent.h>
#endif
//Initialise the musicManager.
//The MusicManager is used to prevent loading music files multiple times and for playing/fading music.
MusicManager musicManager;
//Initialise the soundManager.
//The SoundManager is used to keep track of the sfx in the game.
SoundManager soundManager;
//Initialise the levelPackManager.
//The LevelPackManager is used to prevent loading levelpacks multiple times and for the game to know which levelpacks there are.
LevelPackManager levelPackManager;
//Map containing changed settings using command line arguments.
map<string,string> tmpSettings;
//Pointer to the settings object.
//It is used to load and save the settings file and change the settings.
Settings* settings=nullptr;
SDL_Renderer* sdlRenderer=nullptr;
std::string ngettext(const std::string& message,const std::string& messageplural,int num) {
if (dictionaryManager) {
return dictionaryManager->get_dictionary().translate_plural(message, messageplural, num);
} else {
//Assume it's of English plural rule
return (num != 1) ? messageplural : message;
}
}
void applySurface(int x,int y,SDL_Surface* source,SDL_Surface* dest,SDL_Rect* clip){
//The offset is needed to draw at the right location.
SDL_Rect offset;
offset.x=x;
offset.y=y;
//Let SDL do the drawing of the surface.
SDL_BlitSurface(source,clip,dest,&offset);
}
void drawRect(int x,int y,int w,int h,SDL_Renderer& renderer,Uint32 color){
//NOTE: We let SDL_gfx render it.
SDL_SetRenderDrawColor(&renderer,color >> 24,color >> 16,color >> 8,255);
//rectangleRGBA(&renderer,x,y,x+w,y+h,color >> 24,color >> 16,color >> 8,255);
const SDL_Rect r{x,y,w,h};
SDL_RenderDrawRect(&renderer,&r);
}
//Draw a box with anti-aliased borders using SDL_gfx.
void drawGUIBox(int x,int y,int w,int h,SDL_Renderer& renderer,Uint32 color){
SDL_Renderer* rd = &renderer;
//FIXME, this may get the wrong color on system with different endianness.
//Fill content's background color from function parameter
SDL_SetRenderDrawColor(rd,color >> 24,color >> 16,color >> 8,color >> 0);
{
const SDL_Rect r{x+1,y+1,w-2,h-2};
SDL_RenderFillRect(rd, &r);
}
SDL_SetRenderDrawColor(rd,0,0,0,255);
//Draw first black borders around content and leave 1 pixel in every corner
SDL_RenderDrawLine(rd,x+1,y,x+w-2,y);
SDL_RenderDrawLine(rd,x+1,y+h-1,x+w-2,y+h-1);
SDL_RenderDrawLine(rd,x,y+1,x,y+h-2);
SDL_RenderDrawLine(rd,x+w-1,y+1,x+w-1,y+h-2);
//Fill the corners with transperent color to create anti-aliased borders
SDL_SetRenderDrawColor(rd,0,0,0,160);
SDL_RenderDrawPoint(rd,x,y);
SDL_RenderDrawPoint(rd,x,y+h-1);
SDL_RenderDrawPoint(rd,x+w-1,y);
SDL_RenderDrawPoint(rd,x+w-1,y+h-1);
//Draw second lighter border around content
SDL_SetRenderDrawColor(rd,0,0,0,64);
{
const SDL_Rect r{x+1,y+1,w-2,h-2};
SDL_RenderDrawRect(rd,&r);
}
SDL_SetRenderDrawColor(rd,0,0,0,50);
//Create anti-aliasing in corners of second border
SDL_RenderDrawPoint(rd,x+1,y+1);
SDL_RenderDrawPoint(rd,x+1,y+h-2);
SDL_RenderDrawPoint(rd,x+w-2,y+1);
SDL_RenderDrawPoint(rd,x+w-2,y+h-2);
}
void drawLine(int x1,int y1,int x2,int y2,SDL_Renderer& renderer,Uint32 color){
SDL_SetRenderDrawColor(&renderer,color >> 24,color >> 16,color >> 8,255);
//NOTE: We let SDL_gfx render it.
//lineRGBA(&renderer,x1,y1,x2,y2,color >> 24,color >> 16,color >> 8,255);
SDL_RenderDrawLine(&renderer,x1,y1,x2,y2);
}
void drawLineWithArrow(int x1,int y1,int x2,int y2,SDL_Renderer& renderer,Uint32 color,int spacing,int offset,int xsize,int ysize){
//Draw line first
drawLine(x1,y1,x2,y2,renderer,color);
//calc delta and length
double dx=x2-x1;
double dy=y2-y1;
double length=sqrt(dx*dx+dy*dy);
if(length<0.001) return;
//calc the unit vector
dx/=length; dy/=length;
//Now draw arrows on it
for(double p=offset;p<length;p+=spacing){
drawLine(int(x1+p*dx+0.5),int(y1+p*dy+0.5),
int(x1+(p-xsize)*dx-ysize*dy+0.5),int(y1+(p-xsize)*dy+ysize*dx+0.5),renderer,color);
drawLine(int(x1+p*dx+0.5),int(y1+p*dy+0.5),
int(x1+(p-xsize)*dx+ysize*dy+0.5),int(y1+(p-xsize)*dy-ysize*dx+0.5),renderer,color);
}
}
ScreenData creationFailed() {
return ScreenData{ nullptr };
}
ScreenData createScreen(){
//Check if we are going fullscreen.
if(settings->getBoolValue("fullscreen"))
pickFullscreenResolution();
//Set the screen_width and height.
SCREEN_WIDTH=atoi(settings->getValue("width").c_str());
SCREEN_HEIGHT=atoi(settings->getValue("height").c_str());
//Update the camera.
camera.w=SCREEN_WIDTH;
camera.h=SCREEN_HEIGHT;
//Set the flags.
Uint32 flags = 0;
Uint32 currentFlags = SDL_GetWindowFlags(sdlWindow);
//#if !defined(ANDROID)
// flags |= SDL_DOUBLEBUF;
//#endif
if(settings->getBoolValue("fullscreen")) {
flags|=SDL_WINDOW_FULLSCREEN; //TODO with SDL2 we can also do SDL_WINDOW_FULLSCREEN_DESKTOP
}
else if(settings->getBoolValue("resizable"))
flags|=SDL_WINDOW_RESIZABLE;
//Create the window and renderer if they don't exist and check if there weren't any errors.
if (!sdlWindow && !sdlRenderer) {
SDL_CreateWindowAndRenderer(SCREEN_WIDTH, SCREEN_HEIGHT, flags, &sdlWindow, &sdlRenderer);
if(!sdlWindow || !sdlRenderer){
std::cerr << "FATAL ERROR: SDL_CreateWindowAndRenderer failed.\nError: " << SDL_GetError() << std::endl;
return creationFailed();
}
SDL_SetRenderDrawBlendMode(sdlRenderer, SDL_BlendMode::SDL_BLENDMODE_BLEND);
// White background so we see the menu on failure.
SDL_SetRenderDrawColor(sdlRenderer, 255, 255, 255, 255);
} else if (sdlWindow) {
// Try changing to/from fullscreen
if(SDL_SetWindowFullscreen(sdlWindow, flags & SDL_WINDOW_FULLSCREEN) != 0) {
std::cerr << "WARNING: Failed to switch to fullscreen: " << SDL_GetError() << std::endl;
};
currentFlags = SDL_GetWindowFlags(sdlWindow);
// Change fullscreen resolution
if((currentFlags & SDL_WINDOW_FULLSCREEN ) || (currentFlags & SDL_WINDOW_FULLSCREEN_DESKTOP)) {
SDL_DisplayMode m{0,0,0,0,nullptr};
SDL_GetWindowDisplayMode(sdlWindow,&m);
m.w = SCREEN_WIDTH;
m.h = SCREEN_HEIGHT;
if(SDL_SetWindowDisplayMode(sdlWindow, &m) != 0) {
std::cerr << "WARNING: Failed to set display mode: " << SDL_GetError() << std::endl;
}
} else {
SDL_SetWindowSize(sdlWindow, SCREEN_WIDTH, SCREEN_HEIGHT);
}
}
//Now configure the newly created window (if windowed).
if(settings->getBoolValue("fullscreen")==false)
configureWindow();
//Set the the window caption.
SDL_SetWindowTitle(sdlWindow, ("Me and My Shadow "+version).c_str());
//FIXME Seems to be obsolete
// SDL_EnableUNICODE(1);
//Nothing went wrong so return true.
return ScreenData{sdlRenderer};
}
vector<_res> getResolutionList(){
//Vector that will hold the resolutions to choose from.
vector<_res> resolutionList;
//Enumerate available resolutions using SDL_ListModes()
//NOTE: we enumerate fullscreen resolutions because
// windowed resolutions always can be arbitrary
if(resolutionList.empty()){
// SDL_Rect **modes=SDL_ListModes(NULL,SDL_FULLSCREEN|SCREEN_FLAGS|SDL_ANYFORMAT);
//NOTe - currently only using the first display (0)
int numDisplayModes = SDL_GetNumDisplayModes(0);
if(numDisplayModes < 1){
cerr<<"ERROR: Can't enumerate available screen resolutions."
" Use predefined screen resolutions list instead."<<endl;
static const _res predefinedResolutionList[] = {
{800,600},
{1024,600},
{1024,768},
{1152,864},
{1280,720},
{1280,768},
{1280,800},
{1280,960},
{1280,1024},
{1360,768},
{1366,768},
{1440,900},
{1600,900},
{1600,1200},
{1680,1080},
{1920,1080},
{1920,1200},
{2560,1440},
{3840,2160}
};
//Fill the resolutionList.
for(unsigned int i=0;i<sizeof(predefinedResolutionList)/sizeof(_res);i++){
resolutionList.push_back(predefinedResolutionList[i]);
}
}else{
//Fill the resolutionList.
for(int i=0;i < numDisplayModes; ++i){
SDL_DisplayMode mode;
int error = SDL_GetDisplayMode(0, i, &mode);
if(error < 0) {
//We failed to get a display mode. Should we crash here?
std::cerr << "ERROR: Failed to get display mode " << i << " " << std::endl;
}
//Check if the resolution is higher than the minimum (800x600).
if(mode.w >= 800 && mode.h >= 600){
_res res={mode.w, mode.h};
resolutionList.push_back(res);
}
}
//Reverse it so that we begin with the lowest resolution.
reverse(resolutionList.begin(),resolutionList.end());
}
}
//Return the resolution list.
return resolutionList;
}
void pickFullscreenResolution(){
//Get the resolution list.
vector<_res> resolutionList=getResolutionList();
//The resolution that will hold the final result, we start with the minimum (800x600).
_res closestMatch={800,600};
int width=atoi(getSettings()->getValue("width").c_str());
//int height=atoi(getSettings()->getValue("height").c_str());
//Now loop through the resolutionList.
for(int i=0;i<(int)resolutionList.size();i++){
//The delta between the closestMatch and the resolution from the list.
int dM=(closestMatch.w-resolutionList[i].w);
//The delta between the target width and the resolution from the list.
int dT=(width-resolutionList[i].w);
//Since the resolutions are getting higher the lower (more negative) the further away it is.
//That's why we check if the deltaMatch is lower than the the deltaTarget.
if((dM)<(dT)){
closestMatch.w=resolutionList[i].w;
closestMatch.h=resolutionList[i].h;
}
}
//Now set the resolution to the closest match.
char s[64];
sprintf(s,"%d",closestMatch.w);
getSettings()->setValue("width",s);
sprintf(s,"%d",closestMatch.h);
getSettings()->setValue("height",s);
}
void configureWindow(){
//We only need to configure the window if it's resizable.
if(!getSettings()->getBoolValue("resizable"))
return;
//We use a new function in SDL2 to restrict minimum window size
SDL_SetWindowMinimumSize(sdlWindow, 800, 600);
}
void onVideoResize(ImageManager& imageManager, SDL_Renderer &renderer){
//Check if the resize event isn't malformed.
if(event.window.data1<=0 || event.window.data2<=0)
return;
//Check the size limit.
//TODO: SDL2 porting note: This may break on systems non-X11 or Windows systems as the window size won't be limited
//there.
if(event.window.data1<800)
event.window.data1=800;
if(event.window.data2<600)
event.window.data2=600;
//Check if it really resizes.
if(SCREEN_WIDTH==event.window.data1 && SCREEN_HEIGHT==event.window.data2)
return;
char s[32];
//Set the new width and height.
SDL_snprintf(s,32,"%d",event.window.data1);
getSettings()->setValue("width",s);
SDL_snprintf(s,32,"%d",event.window.data2);
getSettings()->setValue("height",s);
//FIXME: THIS doesn't work properly.
//Do resizing.
SCREEN_WIDTH = event.window.data1;
SCREEN_HEIGHT = event.window.data2;
//Update the camera.
camera.w=SCREEN_WIDTH;
camera.h=SCREEN_HEIGHT;
//Tell the theme to resize.
if(!loadTheme(imageManager,renderer,""))
return;
//And let the currentState update it's GUI to the new resolution.
currentState->resize(imageManager, renderer);
}
ScreenData init(){
//Initialze SDL.
if(SDL_Init(SDL_INIT_EVERYTHING)==-1) {
std::cerr << "FATAL ERROR: SDL_Init failed\nError: " << SDL_GetError() << std::endl;
return creationFailed();
}
//Initialze SDL_mixer (audio).
//Note for SDL2 port: Changed frequency from 22050 to 44100.
//22050 caused some sound artifacts on my system, and I'm not sure
//why one would use it in this day and age anyhow.
//unless it's for compatability with some legacy system.
if(Mix_OpenAudio(44100,MIX_DEFAULT_FORMAT,2,1024)==-1){
std::cerr << "FATAL ERROR: Mix_OpenAudio failed\nError: " << Mix_GetError() << std::endl;
return creationFailed();
}
//Set the volume.
Mix_Volume(-1,atoi(settings->getValue("sound").c_str()));
//Increase the number of channels.
soundManager.setNumberOfChannels(48);
//Initialze SDL_ttf (fonts).
if(TTF_Init()==-1){
std::cerr << "FATAL ERROR: TTF_Init failed\nError: " << TTF_GetError() << std::endl;
return creationFailed();
}
//Create the screen.
ScreenData screenData(createScreen());
if(!screenData) {
return creationFailed();
}
//Load key config. Then initialize joystick support.
inputMgr.loadConfig();
inputMgr.openAllJoysitcks();
//Init tinygettext for translations for the right language
dictionaryManager = new tinygettext::DictionaryManager();
dictionaryManager->add_directory(getDataPath()+"locale");
dictionaryManager->set_charset("UTF-8");
//Check if user have defined own language. If not, find it out for the player using findlocale
string lang=getSettings()->getValue("lang");
if(lang.length()>0){
printf("Locale set by user to %s\n",lang.c_str());
language=lang;
}else{
FL_Locale *locale;
FL_FindLocale(&locale,FL_MESSAGES);
printf("Locale isn't set by user: %s\n",locale->lang);
language=locale->lang;
if(locale->country!=NULL){
language+=string("_")+string(locale->country);
}
if(locale->variant!=NULL){
language+=string("@")+string(locale->variant);
}
FL_FreeLocale(&locale);
}
//Now set the language in the dictionaryManager.
dictionaryManager->set_language(tinygettext::Language::from_name(language));
//Disable annoying 'Couldn't translate: blah blah blah'
tinygettext::Log::set_log_info_callback(NULL);
//Set time format to the user-preference of the system.
setlocale(LC_TIME,"");
//Create the types of blocks.
for(int i=0;i<TYPE_MAX;i++){
Game::blockNameMap[Game::blockName[i]]=i;
}
//Structure that holds the event type/name pair.
struct EventTypeName{
int type;
const char* name;
};
//Create the types of game object event types.
{
const EventTypeName types[]={
{GameObjectEvent_PlayerWalkOn,"playerWalkOn"},
{GameObjectEvent_PlayerIsOn,"playerIsOn"},
{GameObjectEvent_PlayerLeave,"playerLeave"},
{GameObjectEvent_OnCreate,"onCreate"},
{GameObjectEvent_OnEnterFrame,"onEnterFrame"},
{ GameObjectEvent_OnPlayerInteraction, "onPlayerInteraction" },
{GameObjectEvent_OnToggle,"onToggle"},
{GameObjectEvent_OnSwitchOn,"onSwitchOn"},
{GameObjectEvent_OnSwitchOff,"onSwitchOff"},
{0,NULL}
};
for(int i=0;types[i].name;i++){
Game::gameObjectEventNameMap[types[i].name]=types[i].type;
Game::gameObjectEventTypeMap[types[i].type]=types[i].name;
}
}
//Create the types of level event types.
{
const EventTypeName types[]={
{LevelEvent_OnCreate,"onCreate"},
{LevelEvent_OnSave,"onSave"},
{LevelEvent_OnLoad,"onLoad"},
{0,NULL}
};
for(int i=0;types[i].name;i++){
Game::levelEventNameMap[types[i].name]=types[i].type;
Game::levelEventTypeMap[types[i].type]=types[i].name;
}
}
//Nothing went wrong so we return true.
return screenData;
}
static TTF_Font* loadFont(const char* name,int size){
TTF_Font* tmpFont=TTF_OpenFont((getDataPath()+"font/"+name+".ttf").c_str(),size);
if(tmpFont){
return tmpFont;
}else{
#if defined(ANDROID)
//Android has built-in DroidSansFallback.ttf. (?)
return TTF_OpenFont("/system/fonts/DroidSansFallback.ttf",size);
#else
return TTF_OpenFont((getDataPath()+"font/DroidSansFallback.ttf").c_str(),size);
#endif
}
}
bool loadFonts(){
//Load the fonts.
//NOTE: This is a separate method because it will be called separately when re-initing in case of language change.
+ //NOTE2: Since the font fallback is implemented, the font will not be loaded again if call loadFonts() twice.
- //First close the fonts if needed.
- if(fontTitle)
- TTF_CloseFont(fontTitle);
- if(fontGUI)
- TTF_CloseFont(fontGUI);
- if(fontGUISmall)
- TTF_CloseFont(fontGUISmall);
- if(fontText)
- TTF_CloseFont(fontText);
- if(fontMono)
- TTF_CloseFont(fontMono);
-
- /// TRANSLATORS: Font used in GUI:
- /// - Use "knewave" for languages using Latin and Latin-derived alphabets
- /// - "DroidSansFallback" can be used for non-Latin writing systems
- fontTitle=loadFont(_("knewave"),55);
- fontGUI=loadFont(_("knewave"),32);
- fontGUISmall=loadFont(_("knewave"),24);
- /// TRANSLATORS: Font used for normal text:
- /// - Use "Blokletters-Viltstift" for languages using Latin and Latin-derived alphabets
- /// - "DroidSansFallback" can be used for non-Latin writing systems
- fontText=loadFont(_("Blokletters-Viltstift"),16);
- fontMono=loadFont("VeraMono",12);
- if(fontTitle==NULL || fontGUI==NULL || fontGUISmall==NULL || fontText==NULL || fontMono==NULL){
+ if (fontTitle || fontGUI || fontGUISmall || fontText || fontMono) {
+ return true;
+ }
+
+ fontTitle = loadFont("knewave", 55);
+ fontGUI = loadFont("knewave", 32);
+ fontGUISmall = loadFont("knewave", 24);
+ fontText = loadFont("Blokletters-Viltstift", 16);
+ fontMono = loadFont("VeraMono", 12);
+
+ if (fontTitle == NULL || fontGUI == NULL || fontGUISmall == NULL || fontText == NULL || fontMono == NULL){
printf("ERROR: Unable to load fonts! \n");
return false;
}
-
+
+ fontFallbackTitle = loadFont("DroidSansFallback", 55);
+ fontFallbackGUI = loadFont("DroidSansFallback", 32);
+ fontFallbackGUISmall = loadFont("DroidSansFallback", 24);
+ fontFallbackText = loadFont("DroidSansFallback", 16);
+ fontFallbackMono = loadFont("DroidSansFallback", 12);
+
+ TTF_SetFontFallback(fontTitle, 1, &fontFallbackTitle);
+ TTF_SetFontFallback(fontGUI, 1, &fontFallbackGUI);
+ TTF_SetFontFallback(fontGUISmall, 1, &fontFallbackGUISmall);
+ TTF_SetFontFallback(fontText, 1, &fontFallbackText);
+ TTF_SetFontFallback(fontMono, 1, &fontFallbackMono);
+
//Nothing went wrong so return true.
return true;
}
//Generate small arrows used for some GUI widgets.
static void generateArrows(SDL_Renderer& renderer){
- TTF_Font* fontArrow=loadFont(_("knewave"),18);
+ // No need to fallback since knewave already has '<' and '>'
+ TTF_Font* fontArrow=loadFont("knewave",18);
arrowLeft1=textureFromText(renderer,*fontArrow,"<",objThemes.getTextColor(false));
arrowRight1=textureFromText(renderer,*fontArrow,">",objThemes.getTextColor(false));
arrowLeft2=textureFromText(renderer,*fontArrow,"<",objThemes.getTextColor(true));
arrowRight2=textureFromText(renderer,*fontArrow,">",objThemes.getTextColor(true));
TTF_CloseFont(fontArrow);
}
bool loadTheme(ImageManager& imageManager,SDL_Renderer& renderer,std::string name){
//Load default fallback theme if it isn't loaded yet
if(objThemes.themeCount()==0){
if(objThemes.appendThemeFromFile(getDataPath()+"themes/Cloudscape/theme.mnmstheme", imageManager, renderer)==NULL){
printf("ERROR: Can't load default theme file\n");
return false;
}
}
//Resize background or load specific theme
bool success=true;
if(name==""||name.empty()){
objThemes.scaleToScreen();
}else{
string theme=processFileName(name);
if(objThemes.appendThemeFromFile(theme+"/theme.mnmstheme", imageManager, renderer)==NULL){
printf("ERROR: Can't load theme %s\n",theme.c_str());
success=false;
}
}
generateArrows(renderer);
//Everything went fine so return true.
return success;
}
static SDL_Cursor* loadCursor(const char* image[]){
int i,row,col;
//The array that holds the data (0=white 1=black)
Uint8 data[4*32];
//The array that holds the alpha mask (0=transparent 1=visible)
Uint8 mask[4*32];
//The coordinates of the hotspot of the cursor.
int hotspotX, hotspotY;
i=-1;
//Loop through the rows and columns.
//NOTE: We assume a cursor size of 32x32.
for(row=0;row<32;++row){
for(col=0; col<32;++col){
if(col % 8) {
data[i]<<=1;
mask[i]<<=1;
}else{
++i;
data[i]=mask[i]=0;
}
switch(image[4+row][col]){
case '+':
data[i] |= 0x01;
mask[i] |= 0x01;
break;
case '.':
mask[i] |= 0x01;
break;
default:
break;
}
}
}
//Get the hotspot x and y locations from the last line of the cursor.
sscanf(image[4+row],"%d,%d",&hotspotX,&hotspotY);
return SDL_CreateCursor(data,mask,32,32,hotspotX,hotspotY);
}
bool loadFiles(ImageManager& imageManager, SDL_Renderer& renderer){
//Load the fonts.
if(!loadFonts())
return false;
//Show a loading screen
{
int w = 0,h = 0;
SDL_GetRendererOutputSize(&renderer, &w, &h);
SDL_Color fg={255,255,255,0};
TexturePtr loadingTexture = titleTextureFromText(renderer, _("Loading..."), fg, w);
SDL_Rect loadingRect = rectFromTexture(*loadingTexture);
loadingRect.x = (w-loadingRect.w)/2;
loadingRect.y = (h-loadingRect.h)/2;
SDL_RenderCopy(sdlRenderer, loadingTexture.get(), NULL, &loadingRect);
SDL_RenderPresent(sdlRenderer);
SDL_RenderClear(sdlRenderer);
}
musicManager.destroy();
//Load the music and play it.
if(musicManager.loadMusic((getDataPath()+"music/menu.music")).empty()){
printf("WARNING: Unable to load background music! \n");
}
musicManager.playMusic("menu",false);
//Load all the music lists from the data and user data path.
{
vector<string> musicLists=enumAllFiles((getDataPath()+"music/"),"list",true);
for(unsigned int i=0;i<musicLists.size();i++)
getMusicManager()->loadMusicList(musicLists[i]);
musicLists=enumAllFiles((getUserPath(USER_DATA)+"music/"),"list",true);
for(unsigned int i=0;i<musicLists.size();i++)
getMusicManager()->loadMusicList(musicLists[i]);
}
//Set the list to the configured one.
getMusicManager()->setMusicList(getSettings()->getValue("musiclist"));
//Check if music is enabled.
if(getSettings()->getBoolValue("music"))
getMusicManager()->setEnabled();
//Load the sound effects
soundManager.loadSound((getDataPath()+"sfx/jump.wav").c_str(),"jump");
soundManager.loadSound((getDataPath()+"sfx/hit.wav").c_str(),"hit");
soundManager.loadSound((getDataPath()+"sfx/checkpoint.wav").c_str(),"checkpoint");
soundManager.loadSound((getDataPath()+"sfx/swap.wav").c_str(),"swap");
soundManager.loadSound((getDataPath()+"sfx/toggle.ogg").c_str(),"toggle");
soundManager.loadSound((getDataPath()+"sfx/error.wav").c_str(),"error");
soundManager.loadSound((getDataPath()+"sfx/collect.wav").c_str(),"collect");
soundManager.loadSound((getDataPath()+"sfx/achievement.ogg").c_str(),"achievement");
//Load the cursor images from the Cursor.h file.
cursors[CURSOR_POINTER]=loadCursor(pointer);
cursors[CURSOR_CARROT]=loadCursor(ibeam);
cursors[CURSOR_DRAG]=loadCursor(closedhand);
cursors[CURSOR_SIZE_HOR]=loadCursor(size_hor);
cursors[CURSOR_SIZE_VER]=loadCursor(size_ver);
cursors[CURSOR_SIZE_FDIAG]=loadCursor(size_fdiag);
cursors[CURSOR_SIZE_BDIAG]=loadCursor(size_bdiag);
cursors[CURSOR_REMOVE]=loadCursor(remove_cursor);
cursors[CURSOR_POINTING_HAND] = loadCursor(pointing_hand);
//Set the default cursor right now.
SDL_SetCursor(cursors[CURSOR_POINTER]);
levelPackManager.destroy();
//Now sum up all the levelpacks.
vector<string> v=enumAllDirs(getDataPath()+"levelpacks/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
levelPackManager.loadLevelPack(getDataPath()+"levelpacks/"+*i);
}
v=enumAllDirs(getUserPath(USER_DATA)+"levelpacks/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
levelPackManager.loadLevelPack(getUserPath(USER_DATA)+"levelpacks/"+*i);
}
v=enumAllDirs(getUserPath(USER_DATA)+"custom/levelpacks/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
levelPackManager.loadLevelPack(getUserPath(USER_DATA)+"custom/levelpacks/"+*i);
}
//Now we add a special levelpack that will contain the levels not in a levelpack.
LevelPack* levelsPack=new LevelPack;
levelsPack->levelpackName="Levels";
levelsPack->levelpackPath="Levels/";
//NOTE: Set the type of 'levels' to main so it won't be added to the custom packs, even though it contains non-main levels.
levelsPack->type=COLLECTION;
LevelPack* customLevelsPack=new LevelPack;
customLevelsPack->levelpackName="Custom Levels";
customLevelsPack->levelpackPath="Custom Levels/";
customLevelsPack->type=COLLECTION;
//List the main levels and add them one for one.
v = enumAllFiles(getDataPath() + "levels/");
for (vector<string>::iterator i = v.begin(); i != v.end(); ++i){
levelsPack->addLevel(getDataPath() + "levels/" + *i);
levelsPack->setLocked(levelsPack->getLevelCount() - 1);
}
//List the addon levels and add them one for one.
v=enumAllFiles(getUserPath(USER_DATA)+"levels/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
levelsPack->addLevel(getUserPath(USER_DATA)+"levels/"+*i);
levelsPack->setLocked(levelsPack->getLevelCount()-1);
}
//List the custom levels and add them one for one.
v=enumAllFiles(getUserPath(USER_DATA)+"custom/levels/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
levelsPack->addLevel(getUserPath(USER_DATA)+"custom/levels/"+*i);
levelsPack->setLocked(levelsPack->getLevelCount()-1);
customLevelsPack->addLevel(getUserPath(USER_DATA)+"custom/levels/"+*i);
customLevelsPack->setLocked(customLevelsPack->getLevelCount()-1);
}
//Add them to the manager.
levelPackManager.addLevelPack(levelsPack);
levelPackManager.addLevelPack(customLevelsPack);
//Load statistics
statsMgr.loadPicture(renderer, imageManager);
statsMgr.registerAchievements(imageManager);
statsMgr.loadFile(getUserPath(USER_CONFIG)+"statistics");
//Do something ugly and slow
statsMgr.reloadCompletedLevelsAndAchievements();
statsMgr.reloadOtherAchievements();
//Load the theme, both menu and default.
//NOTE: Loading theme may fail and returning false would stop everything, default theme will be used instead.
if (!loadTheme(imageManager,renderer,getSettings()->getValue("theme"))){
getSettings()->setValue("theme","%DATA%/themes/Cloudscape");
saveSettings();
}
//Nothing failed so return true.
return true;
}
bool loadSettings(){
//Check the version of config file.
int version = 0;
std::string cfgV05 = getUserPath(USER_CONFIG) + "meandmyshadow_V0.5.cfg";
std::string cfgV04 = getUserPath(USER_CONFIG) + "meandmyshadow.cfg";
if (fileExists(cfgV05.c_str())) {
//We find a config file of current version.
version = 0x000500;
} else if (fileExists(cfgV04.c_str())) {
//We find a config file of V0.4 version or earlier.
copyFile(cfgV04.c_str(), cfgV05.c_str());
version = 0x000400;
} else {
//No config file found, just create a new one.
version = 0x000500;
}
settings=new Settings(cfgV05);
settings->parseFile(version);
//Now apply settings changed through command line arguments, if any.
map<string,string>::iterator it;
for(it=tmpSettings.begin();it!=tmpSettings.end();++it){
settings->setValue(it->first,it->second);
}
tmpSettings.clear();
//Always return true?
return true;
}
bool saveSettings(){
return settings->save();
}
Settings* getSettings(){
return settings;
}
MusicManager* getMusicManager(){
return &musicManager;
}
SoundManager* getSoundManager(){
return &soundManager;
}
LevelPackManager* getLevelPackManager(){
return &levelPackManager;
}
void flipScreen(SDL_Renderer& renderer){
// Render the data from the back buffer.
SDL_RenderPresent(&renderer);
}
void clean(){
//Save statistics
statsMgr.saveFile(getUserPath(USER_CONFIG)+"statistics");
//We delete the settings.
if(settings){
delete settings;
settings=NULL;
}
//Delete dictionaryManager.
delete dictionaryManager;
//Get rid of the currentstate.
//NOTE: The state is probably already deleted by the changeState function.
if(currentState)
delete currentState;
//Destroy the GUI if present.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
//These calls to destroy makes sure stuff is
//deleted before SDL is uninitialised (as these managers are stack allocated
//globals.)
//Destroy the musicManager.
musicManager.destroy();
//Destroy all sounds
soundManager.destroy();
//Destroy the cursors.
for(int i=0;i<CURSOR_MAX;i++){
SDL_FreeCursor(cursors[i]);
cursors[i]=NULL;
}
//Destroy the levelPackManager.
levelPackManager.destroy();
levels=NULL;
//Close all joysticks.
inputMgr.closeAllJoysticks();
//Close the fonts and quit SDL_ttf.
TTF_CloseFont(fontTitle);
TTF_CloseFont(fontGUI);
TTF_CloseFont(fontGUISmall);
TTF_CloseFont(fontText);
TTF_CloseFont(fontMono);
+ TTF_CloseFont(fontFallbackTitle);
+ TTF_CloseFont(fontFallbackGUI);
+ TTF_CloseFont(fontFallbackGUISmall);
+ TTF_CloseFont(fontFallbackText);
+ TTF_CloseFont(fontFallbackMono);
TTF_Quit();
//Remove the temp surface.
SDL_DestroyRenderer(sdlRenderer);
SDL_DestroyWindow(sdlWindow);
arrowLeft1.reset(nullptr);
arrowLeft2.reset(nullptr);
arrowRight1.reset(nullptr);
arrowRight2.reset(nullptr);
//Stop audio.and quit
Mix_CloseAudio();
//SDL2 porting note. Not sure why this was only done on apple.
//#ifndef __APPLE__
Mix_Quit();
//#endif
//And finally quit SDL.
SDL_Quit();
}
void setNextState(int newstate){
//Only change the state when we aren't already exiting.
if(nextState!=STATE_EXIT){
nextState=newstate;
}
}
void changeState(ImageManager& imageManager, SDL_Renderer& renderer, int fade){
//Check if there's a nextState.
if(nextState!=STATE_NULL){
//Fade out, if fading is enabled.
if (currentState && settings->getBoolValue("fading")) {
for (; fade >= 0; fade -= 17) {
currentState->render(imageManager, renderer);
//TODO: Shouldn't the gamestate take care of rendering the GUI?
if (GUIObjectRoot) GUIObjectRoot->render(renderer);
dimScreen(renderer, static_cast<Uint8>(255 - fade));
//draw new achievements (if any) as overlay
statsMgr.render(imageManager, renderer);
flipScreen(renderer);
SDL_Delay(1000/FPS);
}
}
//Delete the currentState.
delete currentState;
currentState=NULL;
//Set the currentState to the nextState.
stateID=nextState;
nextState=STATE_NULL;
//Init the state.
switch(stateID){
case STATE_GAME:
{
currentState=NULL;
Game* game=new Game(renderer, imageManager);
currentState=game;
//Check if we should load record file or a level.
if(!Game::recordFile.empty()){
game->loadRecord(imageManager,renderer,Game::recordFile.c_str());
Game::recordFile.clear();
}else{
game->loadLevel(imageManager,renderer,levels->getLevelFile());
levels->saveLevelProgress();
}
}
break;
case STATE_MENU:
currentState=new Menu(imageManager, renderer);
break;
case STATE_LEVEL_SELECT:
currentState=new LevelPlaySelect(imageManager, renderer);
break;
case STATE_LEVEL_EDIT_SELECT:
currentState=new LevelEditSelect(imageManager, renderer);
break;
case STATE_LEVEL_EDITOR:
{
currentState=NULL;
LevelEditor* levelEditor=new LevelEditor(renderer, imageManager);
currentState=levelEditor;
//Load the selected level.
levelEditor->loadLevel(imageManager,renderer,levels->getLevelFile());
}
break;
case STATE_OPTIONS:
currentState=new Options(imageManager, renderer);
break;
case STATE_ADDONS:
currentState=new Addons(renderer, imageManager);
break;
case STATE_CREDITS:
currentState=new Credits(imageManager,renderer);
break;
case STATE_STATISTICS:
currentState=new StatisticsScreen(imageManager,renderer);
break;
}
//NOTE: STATE_EXIT isn't mentioned, meaning that currentState is null.
//This way the game loop will break and the program will exit.
}
}
void musicStoppedHook(){
//We just call the musicStopped method of the MusicManager.
musicManager.musicStopped();
}
void channelFinishedHook(int channel){
soundManager.channelFinished(channel);
}
bool checkCollision(const SDL_Rect& a,const SDL_Rect& b){
//Check if the left side of box a isn't past the right side of b.
if(a.x>=b.x+b.w){
return false;
}
//Check if the right side of box a isn't left of the left side of b.
if(a.x+a.w<=b.x){
return false;
}
//Check if the top side of box a isn't under the bottom side of b.
if(a.y>=b.y+b.h){
return false;
}
//Check if the bottom side of box a isn't above the top side of b.
if(a.y+a.h<=b.y){
return false;
}
//We have collision.
return true;
}
bool pointOnRect(const SDL_Rect& point, const SDL_Rect& rect) {
if(point.x >= rect.x && point.x < rect.x + rect.w
&& point.y >= rect.y && point.y < rect.y + rect.h) {
return true;
}
return false;
}
int parseArguments(int argc, char** argv){
//Loop through all arguments.
//We start at one since 0 is the command itself.
for(int i=1;i<argc;i++){
string argument=argv[i];
//Check if the argument is the data-dir.
if(argument=="--data-dir"){
//We need a second argument so we increase i.
i++;
if(i>=argc){
printf("ERROR: Missing argument for command '%s'\n\n",argument.c_str());
return -1;
}
//Configure the dataPath with the given path.
dataPath=argv[i];
if(!getDataPath().empty()){
char c=dataPath[dataPath.size()-1];
if(c!='/'&&c!='\\') dataPath+="/";
}
}else if(argument=="--user-dir"){
//We need a second argument so we increase i.
i++;
if(i>=argc){
printf("ERROR: Missing argument for command '%s'\n\n",argument.c_str());
return -1;
}
//Configure the userPath with the given path.
userPath=argv[i];
if(!userPath.empty()){
char c=userPath[userPath.size()-1];
if(c!='/'&&c!='\\') userPath+="/";
}
}else if(argument=="-f" || argument=="-fullscreen" || argument=="--fullscreen"){
tmpSettings["fullscreen"]="1";
}else if(argument=="-w" || argument=="-windowed" || argument=="--windowed"){
tmpSettings["fullscreen"]="0";
}else if(argument=="-mv" || argument=="-music" || argument=="--music"){
//We need a second argument so we increase i.
i++;
if(i>=argc){
printf("ERROR: Missing argument for command '%s'\n\n",argument.c_str());
return -1;
}
//Now set the music volume.
tmpSettings["music"]=argv[i];
}else if(argument=="-sv" || argument=="-sound" || argument=="--sound"){
//We need a second argument so we increase i.
i++;
if(i>=argc){
printf("ERROR: Missing argument for command '%s'\n\n",argument.c_str());
return -1;
}
//Now set sound volume.
tmpSettings["sound"]=argv[i];
}else if(argument=="-set" || argument=="--set"){
//We need a second and a third argument so we increase i.
i+=2;
if(i>=argc){
printf("ERROR: Missing argument for command '%s'\n\n",argument.c_str());
return -1;
}
//And set the setting.
tmpSettings[argv[i-1]]=argv[i];
}else if(argument=="-v" || argument=="-version" || argument=="--version"){
//Print the version.
printf("%s\n",version.c_str());
return 0;
}else if(argument=="-h" || argument=="-help" || argument=="--help"){
//If the help is requested we'll return false without printing an error.
//This way the usage/help text will be printed.
return -1;
}else{
//Any other argument is unknow so we return false.
printf("ERROR: Unknown argument %s\n\n",argument.c_str());
return -1;
}
}
//If everything went well we can return true.
return 1;
}
//Special structure that will recieve the GUIEventCallbacks of the messagebox.
struct msgBoxHandler:public GUIEventCallback{
public:
//Integer containing the ret(urn) value of the messageBox.
int ret;
public:
//Constructor.
msgBoxHandler():ret(0){}
void GUIEventCallback_OnEvent(ImageManager& imageManager, SDL_Renderer& renderer, std::string name,GUIObject* obj,int eventType){
//Make sure it's a click event.
if(eventType==GUIEventClick){
//Set the return value.
ret=obj->value;
//After a click event we can delete the GUI.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
}
};
msgBoxResult msgBox(ImageManager& imageManager,SDL_Renderer& renderer, const string& prompt,msgBoxButtons buttons,const string& title){
//Create the event handler.
msgBoxHandler objHandler;
//The GUI objects.
GUIObject* obj;
//Create the GUIObjectRoot, the height and y location is temp.
//It depends on the content what it will be.
GUIObject* root=new GUIFrame(imageManager,renderer,(SCREEN_WIDTH-600)/2,200,600,200,title.c_str());
//Integer containing the current y location used to grow dynamic depending on the content.
int y=50;
//Now process the prompt.
{
//NOTE: We shouldn't modify the cotents in the c_str() of a string,
//since it's said that at least in g++ the std::string is copy-on-write
//hence if we modify the content it may break
//The copy of the prompt.
std::vector<char> copyOfPrompt(prompt.begin(), prompt.end());
//Append another '\0' to it.
copyOfPrompt.push_back(0);
//Pointer to the string.
char* lps = &(copyOfPrompt[0]);
//Pointer to a character.
char* lp=NULL;
//We keep looping forever.
//The only way out is with the break statement.
for(;;){
//As long as it's still the same sentence we continue.
//It will stop when there's a newline or end of line.
for(lp=lps;*lp!='\n'&&*lp!='\r'&&*lp!=0;lp++);
//Store the character we stopped on. (End or newline)
char c=*lp;
//Set the character in the string to 0, making lps a string containing one sentence.
*lp=0;
//Add a GUIObjectLabel with the sentence.
root->addChild(new GUILabel(imageManager,renderer,0,y,root->width,25,lps,0,true,true,GUIGravityCenter));
//Increase y with 25, about the height of the text.
y+=25;
//Check the stored character if it was a stop.
if(c==0){
//It was so break out of the for loop.
lps=lp;
break;
}
//It wasn't meaning more will follow.
//We set lps to point after the "newline" forming a new string.
lps=lp+1;
}
}
//Add 70 to y to leave some space between the content and the buttons.
y+=70;
//Recalc the size of the message box.
root->top=(SCREEN_HEIGHT-y)/2;
root->height=y;
//Now we need to add the buttons.
//Integer containing the number of buttons to add.
int count=0;
//Array with the return codes for the buttons.
int value[3]={0};
//Array containing the captation for the buttons.
string button[3]={"","",""};
switch(buttons){
case MsgBoxOKCancel:
count=2;
button[0]=_("OK");value[0]=MsgBoxOK;
button[1]=_("Cancel");value[1]=MsgBoxCancel;
break;
case MsgBoxAbortRetryIgnore:
count=3;
button[0]=_("Abort");value[0]=MsgBoxAbort;
button[1]=_("Retry");value[1]=MsgBoxRetry;
button[2]=_("Ignore");value[2]=MsgBoxIgnore;
break;
case MsgBoxYesNoCancel:
count=3;
button[0]=_("Yes");value[0]=MsgBoxYes;
button[1]=_("No");value[1]=MsgBoxNo;
button[2]=_("Cancel");value[2]=MsgBoxCancel;
break;
case MsgBoxYesNo:
count=2;
button[0]=_("Yes");value[0]=MsgBoxYes;
button[1]=_("No");value[1]=MsgBoxNo;
break;
case MsgBoxRetryCancel:
count=2;
button[0]=_("Retry");value[0]=MsgBoxRetry;
button[1]=_("Cancel");value[1]=MsgBoxCancel;
break;
default:
count=1;
button[0]=_("OK");value[0]=MsgBoxOK;
break;
}
//Now we start making the buttons.
{
//Reduce y so that the buttons fit inside the frame.
y-=40;
double places[3]={0.0};
if(count==1){
places[0]=0.5;
}else if(count==2){
places[0]=0.4;
places[1]=0.6;
}else if(count==3){
places[0]=0.3;
places[1]=0.5;
places[2]=0.7;
}
//Loop to add the buttons.
for(int i=0;i<count;i++){
obj=new GUIButton(imageManager,renderer,root->width*places[i],y,-1,36,button[i].c_str(),value[i],true,true,GUIGravityCenter);
obj->eventCallback=&objHandler;
root->addChild(obj);
}
}
//Now we dim the screen and keep the GUI rendering/updating.
GUIOverlay* overlay=new GUIOverlay(renderer,root);
overlay->keyboardNavigationMode = LeftRightFocus | UpDownFocus | TabFocus | ((count == 1) ? 0 : ReturnControls);
overlay->enterLoop(imageManager, renderer, true, count == 1);
//And return the result.
return (msgBoxResult)objHandler.ret;
}
// A helper function to read a character from utf8 string
// s: the string
// p [in,out]: the position
// return value: the character readed, in utf32 format, 0 means end of string, -1 means error
int utf8ReadForward(const char* s, int& p) {
int ch = (unsigned char)s[p];
if (ch < 0x80){
if (ch) p++;
return ch;
} else if (ch < 0xC0){
// skip invalid characters
while (((unsigned char)s[p] & 0xC0) == 0x80) p++;
return -1;
} else if (ch < 0xE0){
int c2 = (unsigned char)s[++p];
if ((c2 & 0xC0) != 0x80) return -1;
ch = ((ch & 0x1F) << 6) | (c2 & 0x3F);
p++;
return ch;
} else if (ch < 0xF0){
int c2 = (unsigned char)s[++p];
if ((c2 & 0xC0) != 0x80) return -1;
int c3 = (unsigned char)s[++p];
if ((c3 & 0xC0) != 0x80) return -1;
ch = ((ch & 0xF) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F);
p++;
return ch;
} else if (ch < 0xF8){
int c2 = (unsigned char)s[++p];
if ((c2 & 0xC0) != 0x80) return -1;
int c3 = (unsigned char)s[++p];
if ((c3 & 0xC0) != 0x80) return -1;
int c4 = (unsigned char)s[++p];
if ((c4 & 0xC0) != 0x80) return -1;
ch = ((ch & 0x7) << 18) | ((c2 & 0x3F) << 12) | ((c3 & 0x3F) << 6) | (c4 & 0x3F);
if (ch >= 0x110000) ch = -1;
p++;
return ch;
} else {
p++;
return -1;
}
}
// A helper function to read a character backward from utf8 string (experimental)
// s: the string
// p [in,out]: the position
// return value: the character readed, in utf32 format, 0 means end of string, -1 means error
int utf8ReadBackward(const char* s, int& p) {
if (p <= 0) return 0;
do {
p--;
} while (p > 0 && ((unsigned char)s[p] & 0xC0) == 0x80);
int tmp = p;
return utf8ReadForward(s, tmp);
}
#ifndef WIN32
// ad-hoc function to check if a program is installed
static bool programExists(const std::string& program) {
std::string p = tfm::format("which \"%s\" 2>&1", program);
const int BUFSIZE = 128;
char buf[BUFSIZE];
FILE *fp;
if ((fp = popen(p.c_str(), "r")) == NULL) {
return false;
}
while (fgets(buf, BUFSIZE, fp) != NULL) {
// Drop all outputs since 'which' returns -1 when the program is not found
}
if (pclose(fp)) {
return false;
}
return true;
}
#endif
void openWebsite(const std::string& url) {
#ifdef WIN32
wchar_t ws[4096];
TO_UTF16(url.c_str(), ws);
SDL_SysWMinfo info = {};
SDL_VERSION(&info.version);
SDL_GetWindowWMInfo(sdlWindow, &info);
ShellExecuteW(info.info.win.window, L"open", ws, NULL, NULL, SW_SHOW);
#else
static int method = -1;
// Some of these methods are copied from https://stackoverflow.com/questions/5116473/
const char* methods[] = {
"xdg-open", "xdg-open \"%s\"",
"gnome-open", "gnome-open \"%s\"",
"kde-open", "kde-open \"%s\"",
"open", "open \"%s\"",
"python", "python -m webbrowser \"%s\"",
"sensible-browser", "sensible-browser \"%s\"",
"x-www-browser", "x-www-browser \"%s\"",
NULL,
};
if (method < 0) {
for (method = 0; methods[method]; method += 2) {
if (programExists(methods[method])) break;
}
}
if (methods[method]) {
std::string p = tfm::format(methods[method + 1], url);
system(p.c_str());
} else {
fprintf(stderr, "TODO: openWebsite is not implemented on your system\n");
}
#endif
}
std::string appendURLToLicense(const std::string& license) {
// if the license doesn't include url, try to detect it from a predefined list
if (license.find("://") == std::string::npos) {
std::string normalized;
for (char c : license) {
if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z')) {
normalized.push_back(c);
} else if (c >= 'a' && c <= 'z') {
normalized.push_back(c + ('A' - 'a'));
}
}
const char* licenses[] = {
// AGPL
"AGPL1", "AGPLV1", NULL, "http://www.affero.org/oagpl.html",
"AGPL2", "AGPLV2", NULL, "http://www.affero.org/agpl2.html",
"AGPL", NULL, "https://gnu.org/licenses/agpl.html",
// LGPL
"LGPL21", "LGPLV21", NULL, "https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html",
"LGPL2", "LGPLV2", NULL, "https://www.gnu.org/licenses/old-licenses/lgpl-2.0.html",
"LGPL", NULL, "https://www.gnu.org/copyleft/lesser.html",
// GPL
"GPL1", "GPLV1", NULL, "https://www.gnu.org/licenses/old-licenses/gpl-1.0.html",
"GPL2", "GPLV2", NULL, "https://www.gnu.org/licenses/old-licenses/gpl-2.0.html",
"GPL", NULL, "https://gnu.org/licenses/gpl.html",
// CC BY-NC-ND
"CCBYNCND1", "CCBYNDNC1", NULL, "https://creativecommons.org/licenses/by-nd-nc/1.0",
"CCBYNCND25", "CCBYNDNC25", NULL, "https://creativecommons.org/licenses/by-nc-nd/2.5",
"CCBYNCND2", "CCBYNDNC2", NULL, "https://creativecommons.org/licenses/by-nc-nd/2.0",
"CCBYNCND3", "CCBYNDNC3", NULL, "https://creativecommons.org/licenses/by-nc-nd/3.0",
"CCBYNCND", "CCBYNDNC", NULL, "https://creativecommons.org/licenses/by-nc-nd/4.0",
// CC BY-NC-SA
"CCBYNCSA1", NULL, "https://creativecommons.org/licenses/by-nc-sa/1.0",
"CCBYNCSA25", NULL, "https://creativecommons.org/licenses/by-nc-sa/2.5",
"CCBYNCSA2", NULL, "https://creativecommons.org/licenses/by-nc-sa/2.0",
"CCBYNCSA3", NULL, "https://creativecommons.org/licenses/by-nc-sa/3.0",
"CCBYNCSA", NULL, "https://creativecommons.org/licenses/by-nc-sa/4.0",
// CC BY-ND
"CCBYND1", NULL, "https://creativecommons.org/licenses/by-nd/1.0",
"CCBYND25", NULL, "https://creativecommons.org/licenses/by-nd/2.5",
"CCBYND2", NULL, "https://creativecommons.org/licenses/by-nd/2.0",
"CCBYND3", NULL, "https://creativecommons.org/licenses/by-nd/3.0",
"CCBYND", NULL, "https://creativecommons.org/licenses/by-nd/4.0",
// CC BY-NC
"CCBYNC1", NULL, "https://creativecommons.org/licenses/by-nc/1.0",
"CCBYNC25", NULL, "https://creativecommons.org/licenses/by-nc/2.5",
"CCBYNC2", NULL, "https://creativecommons.org/licenses/by-nc/2.0",
"CCBYNC3", NULL, "https://creativecommons.org/licenses/by-nc/3.0",
"CCBYNC", NULL, "https://creativecommons.org/licenses/by-nc/4.0",
// CC BY-SA
"CCBYSA1", NULL, "https://creativecommons.org/licenses/by-sa/1.0",
"CCBYSA25", NULL, "https://creativecommons.org/licenses/by-sa/2.5",
"CCBYSA2", NULL, "https://creativecommons.org/licenses/by-sa/2.0",
"CCBYSA3", NULL, "https://creativecommons.org/licenses/by-sa/3.0",
"CCBYSA", NULL, "https://creativecommons.org/licenses/by-sa/4.0",
// CC BY
"CCBY1", NULL, "https://creativecommons.org/licenses/by/1.0",
"CCBY25", NULL, "https://creativecommons.org/licenses/by/2.5",
"CCBY2", NULL, "https://creativecommons.org/licenses/by/2.0",
"CCBY3", NULL, "https://creativecommons.org/licenses/by/3.0",
"CCBY", NULL, "https://creativecommons.org/licenses/by/4.0",
// CC0
"CC0", NULL, "https://creativecommons.org/publicdomain/zero/1.0",
// WTFPL
"WTFPL", NULL, "http://www.wtfpl.net/",
// end
NULL,
};
for (int i = 0; licenses[i]; i++) {
bool found = false;
for (; licenses[i]; i++) {
if (normalized.find(licenses[i]) != std::string::npos) found = true;
}
i++;
if (found) {
return license + tfm::format(" <%s>", licenses[i]);
}
}
}
return license;
}
int getKeyboardRepeatDelay() {
static int ret = -1;
if (ret < 0) {
#ifdef WIN32
int i = 0;
SystemParametersInfoW(SPI_GETKEYBOARDDELAY, 0, &i, 0);
// NOTE: these weird numbers are derived from Microsoft's documentation explaining the return value of SystemParametersInfo.
i = clamp(i, 0, 3);
ret = (i + 1) * 10;
#else
// TODO: platform-dependent code
// Assume it's 250ms, i.e. 10 frames
ret = 10;
#endif
// Debug
#ifdef _DEBUG
printf("getKeyboardRepeatDelay() = %d\n", ret);
#endif
}
return ret;
}
int getKeyboardRepeatInterval() {
static int ret = -1;
if (ret < 0) {
#ifdef WIN32
int i = 0;
SystemParametersInfoW(SPI_GETKEYBOARDSPEED, 0, &i, 0);
// NOTE: these weird numbers are derived from Microsoft's documentation explaining the return value of SystemParametersInfo.
i = clamp(i, 0, 31);
ret = (int)floor(40.0f / (2.5f + 0.887097f * (float)i) + 0.5f);
#else
// TODO: platform-dependent code
// Assume it's 25ms, i.e. 1 frame
ret = 1;
#endif
// Debug
#ifdef _DEBUG
printf("getKeyboardRepeatInterval() = %d\n", ret);
#endif
}
return ret;
}
diff --git a/src/Globals.cpp b/src/Globals.cpp
index 0e57ef4..bcc28d2 100644
--- a/src/Globals.cpp
+++ b/src/Globals.cpp
@@ -1,84 +1,87 @@
/*
* Copyright (C) 2011-2013 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow 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, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow 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 Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Globals.h"
#include "libs/tinygettext/tinygettext.hpp"
//Set the defautl value for the screen width and height.
int SCREEN_WIDTH=800;
int SCREEN_HEIGHT=600;
//Set the default value for the level width and height.
int LEVEL_HEIGHT=0;
int LEVEL_WIDTH=0;
//The language that in which the game should be translated.
std::string language;
//The DictionaryManager that is used to translate the game itself.
tinygettext::DictionaryManager* dictionaryManager=NULL;
//SDL Window and renderer
SDL_Window* sdlWindow=NULL;
//Font that is used for titles.
//Knewave large.
TTF_Font* fontTitle=NULL;
//Font that is used for captions of buttons and other GUI elements.
//Knewave small.
TTF_Font* fontGUI=NULL;
//Font that is used for long captions of buttons and other GUI elements.
//Knewave small.
TTF_Font* fontGUISmall=NULL;
//Font that is used for (long) text.
//Blokletter-Viltstift small.
TTF_Font* fontText=NULL;
//Font used for scripting editor.
//Monospace.
TTF_Font* fontMono=NULL;
+//Fallback fonts. No need to use them directly.
+TTF_Font *fontFallbackTitle = NULL, *fontFallbackGUI = NULL, *fontFallbackGUISmall = NULL, *fontFallbackText = NULL, *fontFallbackMono = NULL;
+
//Small arrows used for GUI widgets.
//2 directions and 2 different/same colors depending on theme.
TexturePtr arrowLeft1=nullptr;
TexturePtr arrowRight1=nullptr;
TexturePtr arrowLeft2=nullptr;
TexturePtr arrowRight2=nullptr;
//Set the current stateID and the nextState.
int stateID=STATE_NULL;
int nextState=STATE_NULL;
GameState* currentState=NULL;
LevelPack* levels=NULL;
//The name of the current level.
std::string levelName;
//Initialise the camera.
//Start location is 0, size is the same as the screen size.
SDL_Rect camera={0,0,SCREEN_WIDTH,SCREEN_HEIGHT};
//The SDL_Event object.
SDL_Event event;
//The current cursor type.
CursorType currentCursor=CURSOR_POINTER;
//Array containing the SDL_Cursors.
SDL_Cursor* cursors[CURSOR_MAX];
//Keyboard only mode. This is set to true if the last menu navigation is performed by keyboard.
bool isKeyboardOnly = false;
diff --git a/src/Globals.h b/src/Globals.h
index 9289da3..a18a5c2 100644
--- a/src/Globals.h
+++ b/src/Globals.h
@@ -1,263 +1,266 @@
/*
* Copyright (C) 2011-2013 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow 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, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow 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 Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GLOBALS_H
#define GLOBALS_H
#include <SDL.h>
#include <string>
#include "libs/tinygettext/tinygettext.hpp"
#include "LevelPack.h"
#include "Render.h"
#if defined (WIN32) || defined (__APPLE__)
//#define DATA_PATH
#else
#include "config.h"
#endif
#define TITLE_FONT_RAISE 19
#define GUI_FONT_RAISE 5
class GameState;
//Global constants
//The width of the screen.
extern int SCREEN_WIDTH;
//The height of the screen.
extern int SCREEN_HEIGHT;
//The depth of the screen.
#if defined(ANDROID)
//TODO: change other surface creating code to make the game runs faster
const int SCREEN_BPP=16; //??? 24?? 32??
//const int SCREEN_FLAGS=SDL_HWSURFACE;
#else
const int SCREEN_BPP=32;
//const int SCREEN_FLAGS=SDL_HWSURFACE;
#endif
const int SCREEN_FLAGS = 0;
//SDL interprets each pixel as a 32-bit number,
// so our masks must depend on the endianness (byte order) of the machine.
//NOTE: We define them here so we only have to do it once.
/*#if SDL_BYTEORDER == SDL_BIG_ENDIAN
const Uint32 BMASK=0xFF000000;
const Uint32 GMASK=0x00FF0000;
const Uint32 RMASK=0x0000FF00;
const Uint32 AMASK=0x000000FF;
#else*/
// NOTE: Changed to ARGB for SDL2.
const Uint32 BMASK=0x000000FF;
const Uint32 GMASK=0x0000FF00;
const Uint32 RMASK=0x00FF0000;
const Uint32 AMASK=0xFF000000;
/*#endif*/
//String containing the version, used in the titelbar.
//NOTE: for development version please write something like "V0.5 Development version"
//which can trigger the corresponding achievement.
const std::string version = "V0.5 Beta";
//The height of the current level.
extern int LEVEL_HEIGHT;
//The width of the current level.
extern int LEVEL_WIDTH;
//The target frames per seconds.
const int FPS=40;
//The language that in which the game should be translated.
extern std::string language;
//The DictionaryManager that is used to translate the game itself.
extern tinygettext::DictionaryManager* dictionaryManager;
//SDL Window and renderer
extern SDL_Window* sdlWindow;
//Font that is used for titles.
//Knewave large.
extern TTF_Font* fontTitle;
//Font that is used for captions of buttons and other GUI elements.
//Knewave small.
extern TTF_Font* fontGUI;
//Font that is used for long captions of buttons and other GUI elements.
//Knewave smaller.
extern TTF_Font* fontGUISmall;
//Font that is used for (long) text.
//Blokletter-Viltstift small.
extern TTF_Font* fontText;
//Font used for scripting editor.
//Monospace.
extern TTF_Font* fontMono;
+//Fallback fonts. No need to use them directly.
+extern TTF_Font *fontFallbackTitle, *fontFallbackGUI, *fontFallbackGUISmall, *fontFallbackText, *fontFallbackMono;
+
//Small arrows used for GUI widgets.
//2 directions and 2 different/same colors depending on theme.
extern TexturePtr arrowLeft1;
extern TexturePtr arrowRight1;
extern TexturePtr arrowLeft2;
extern TexturePtr arrowRight2;
//Event, used for event handling.
extern SDL_Event event;
//GUI
class GUIObject;
extern GUIObject *GUIObjectRoot;
//The state id of the current state.
extern int stateID;
//Integer containing what the next state will be.
extern int nextState;
//The currentState.
extern GameState* currentState;
//Pointer to the current levelpack.
extern LevelPack* levels;
//String containing the name of the current level.
extern std::string levelName;
//SDL rectangle used to store the camera.
//x is the x location of the camera.
//y is the y location of the camera.
//w is the width of the camera. (equal to SCREEN_WIDTH)
//h is the height of the camera. (equal to SCREEN_HEIGHT)
extern SDL_Rect camera;
//Themable colors
const SDL_Color BLACK = SDL_Color{0,0,0,255};
//Enumeration containing the different cursor types there are.
enum CursorType{
//The default pointer.
CURSOR_POINTER,
//The vertical ibeam, used to indicate text input.
CURSOR_CARROT,
//A closed hand, used for indicating a drag action.
CURSOR_DRAG,
//The different (window) size cursor icons.
CURSOR_SIZE_HOR,
CURSOR_SIZE_VER,
CURSOR_SIZE_FDIAG,
CURSOR_SIZE_BDIAG,
//Remove cursor used in level editor
CURSOR_REMOVE,
//Pointing hand cursor, for hyperlinks.
CURSOR_POINTING_HAND,
//The number of cursor types there are.
CURSOR_MAX
};
//Currently used cursor type.
extern CursorType currentCursor;
//Array containing the SDL_Cursors.
extern SDL_Cursor* cursors[CURSOR_MAX];
//Enumeration containing the ids of the game states.
enum GameStates{
//State null is a special state used to indicate no state.
//This is used when no next state is defined.
STATE_NULL,
//This state is before the actual leveleditor used to make levelpacks.
STATE_LEVEL_EDIT_SELECT,
//This state is for the level editor.
STATE_LEVEL_EDITOR,
//This state is for the main menu.
STATE_MENU,
//This state is for the actual game.
STATE_GAME,
//Special state used when exiting meandmyshadow.
STATE_EXIT,
//This state is for the help screen.
STATE_LEVEL_SELECT,
//This state is for the options screen.
STATE_OPTIONS,
//This state is for the addon screen.
STATE_ADDONS,
//This state is for credits screen
STATE_CREDITS,
//This state is for statistics screen
STATE_STATISTICS,
};
//Enumeration containing the ids of the different block types.
enum GameTileType{
//The normal solid block.
TYPE_BLOCK=0,
//Block representing the start location of the player.
TYPE_START_PLAYER,
//Block representing the start location of the shadow.
TYPE_START_SHADOW,
//The exit of the level.
TYPE_EXIT,
//The shadow block which is only solid for the shadow.
TYPE_SHADOW_BLOCK,
//Block that can kill both the player and the shadow.
TYPE_SPIKES,
//Special point where the player can save.
TYPE_CHECKPOINT,
//Block that will switch the location of the player and the shadow when invoked.
TYPE_SWAP,
//Block that will crumble to dust when stepped on it for the third time.
TYPE_FRAGILE,
//Normal block that moves along a path.
TYPE_MOVING_BLOCK,
//Shadow block that moves along a path.
TYPE_MOVING_SHADOW_BLOCK,
//A spike block that moves along a path.
TYPE_MOVING_SPIKES,
//Special block which, once entered, moves the player/shadow to a different portal.
TYPE_PORTAL,
//A block with a button which can activate or stop moving blocks, converyor belts
TYPE_BUTTON,
//A switch which can activate or stop moving blocks, converyor belts
TYPE_SWITCH,
//Solid block which works like
TYPE_CONVEYOR_BELT,
TYPE_SHADOW_CONVEYOR_BELT,
//Block that contains a message that can be read.
TYPE_NOTIFICATION_BLOCK,
//A collectable that is able to open locked doors
TYPE_COLLECTABLE,
//Block that can be pushed by the player and the shadow.
//Pushable blocks can push other pushable blocks.
TYPE_PUSHABLE,
//The (max) number of tiles.
TYPE_MAX
};
//Keyboard only mode. This is set to true if the last menu navigation is performed by keyboard.
extern bool isKeyboardOnly;
#endif
diff --git a/src/libs/SDL2_ttf/SDL_ttf.c b/src/libs/SDL2_ttf/SDL_ttf.c
index 0d25b9c..529721d 100644
--- a/src/libs/SDL2_ttf/SDL_ttf.c
+++ b/src/libs/SDL2_ttf/SDL_ttf.c
@@ -1,2398 +1,2482 @@
/*
SDL_ttf: A companion library to SDL for working with TrueType (tm) fonts
Copyright (C) 2001-2018 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_OUTLINE_H
#include FT_STROKER_H
#include FT_GLYPH_H
#include FT_TRUETYPE_IDS_H
#include "SDL.h"
#include "SDL_endian.h"
#include "SDL_ttf.h"
/* FIXME: Right now we assume the gray-scale renderer Freetype is using
supports 256 shades of gray, but we should instead key off of num_grays
in the result FT_Bitmap after the FT_Render_Glyph() call. */
#define NUM_GRAYS 256
/* Handy routines for converting from fixed point */
#define FT_FLOOR(X) ((X & -64) / 64)
#define FT_CEIL(X) (((X + 63) & -64) / 64)
#define CACHED_METRICS 0x10
#define CACHED_BITMAP 0x01
#define CACHED_PIXMAP 0x02
/* Cached glyph information */
typedef struct cached_glyph {
int stored;
+ FT_Face face; /* for font fallback support */
FT_UInt index;
FT_Bitmap bitmap;
FT_Bitmap pixmap;
int minx;
int maxx;
int miny;
int maxy;
int yoffset;
int advance;
Uint32 cached;
} c_glyph;
/* The structure used to hold internal font information */
struct _TTF_Font {
/* Freetype2 maintains all sorts of useful info itself */
FT_Face face;
+ int num_of_fallbacks;
+ TTF_Font **fallbacks;
+
/* We'll cache these ourselves */
int height;
int ascent;
int descent;
int lineskip;
/* The font style */
int face_style;
int style;
int outline;
/* Whether kerning is desired */
int kerning;
/* Extra width in glyph bounds for text styles */
int glyph_overhang;
float glyph_italics;
/* Information in the font for underlining */
int underline_offset;
int underline_height;
/* Cache for style-transformed glyphs */
c_glyph *current;
c_glyph cache[257]; /* 257 is a prime */
/* We are responsible for closing the font stream */
SDL_RWops *src;
int freesrc;
FT_Open_Args args;
/* For non-scalable formats, we must remember which font index size */
int font_size_family;
/* really just flags passed into FT_Load_Glyph */
int hinting;
};
/* Handle a style only if the font does not already handle it */
#define TTF_HANDLE_STYLE_BOLD(font) (((font)->style & TTF_STYLE_BOLD) && \
!((font)->face_style & TTF_STYLE_BOLD))
#define TTF_HANDLE_STYLE_ITALIC(font) (((font)->style & TTF_STYLE_ITALIC) && \
!((font)->face_style & TTF_STYLE_ITALIC))
#define TTF_HANDLE_STYLE_UNDERLINE(font) ((font)->style & TTF_STYLE_UNDERLINE)
#define TTF_HANDLE_STYLE_STRIKETHROUGH(font) ((font)->style & TTF_STYLE_STRIKETHROUGH)
/* Font styles that does not impact glyph drawing */
#define TTF_STYLE_NO_GLYPH_CHANGE (TTF_STYLE_UNDERLINE | TTF_STYLE_STRIKETHROUGH)
/* The FreeType font engine/library */
static FT_Library library;
static int TTF_initialized = 0;
static int TTF_byteswapped = 0;
#define TTF_CHECKPOINTER(p, errval) \
if (!TTF_initialized) { \
TTF_SetError("Library not initialized"); \
return errval; \
} \
if (!p) { \
TTF_SetError("Passed a NULL pointer"); \
return errval; \
}
/* Gets the top row of the underline. The outline
is taken into account.
*/
static int TTF_underline_top_row(TTF_Font *font)
{
/* With outline, the underline_offset is underline_offset+outline. */
/* So, we don't have to remove the top part of the outline height. */
return font->ascent - font->underline_offset - 1;
}
/* Gets the top row of the underline. for a given glyph. The outline
is taken into account.
Need to update row according to height difference between font and glyph:
font_value - font->ascent + glyph->maxy
*/
static int TTF_Glyph_underline_top_row(TTF_Font *font, c_glyph *glyph)
{
return glyph->maxy - font->underline_offset - 1;
}
/* Gets the bottom row of the underline. The outline
is taken into account.
*/
static int TTF_underline_bottom_row(TTF_Font *font)
{
int row = TTF_underline_top_row(font) + font->underline_height;
if (font->outline > 0) {
/* Add underline_offset outline offset and */
/* the bottom part of the outline. */
row += font->outline * 2;
}
return row;
}
/* Gets the bottom row of the underline. for a given glyph. The outline
is taken into account.
Need to update row according to height difference between font and glyph:
font_value - font->ascent + glyph->maxy
*/
static int TTF_Glyph_underline_bottom_row(TTF_Font *font, c_glyph *glyph)
{
return TTF_underline_bottom_row(font) - font->ascent + glyph->maxy;
}
/* Gets the top row of the strikethrough. The outline
is taken into account.
*/
static int TTF_strikethrough_top_row(TTF_Font *font)
{
/* With outline, the first text row is 'outline'. */
/* So, we don't have to remove the top part of the outline height. */
return font->height / 2;
}
/* Gets the top row of the strikethrough for a given glyph. The outline
is taken into account.
Need to update row according to height difference between font and glyph:
font_value - font->ascent + glyph->maxy
*/
static int TTF_Glyph_strikethrough_top_row(TTF_Font *font, c_glyph *glyph)
{
return TTF_strikethrough_top_row(font) - font->ascent + glyph->maxy;
}
static void TTF_initLineMectrics(const TTF_Font *font, const SDL_Surface *textbuf, const int row, Uint8 **pdst, int *pheight)
{
Uint8 *dst;
int height;
dst = (Uint8 *)textbuf->pixels;
if (row > 0) {
dst += row * textbuf->pitch;
}
height = font->underline_height;
/* Take outline into account */
if (font->outline > 0) {
height += font->outline * 2;
}
*pdst = dst;
*pheight = height;
}
/* Draw a solid line of underline_height (+ optional outline)
at the given row. The row value must take the
outline into account.
*/
static void TTF_drawLine_Solid(const TTF_Font *font, const SDL_Surface *textbuf, const int row)
{
int line;
Uint8 *dst_check = (Uint8*)textbuf->pixels + textbuf->pitch * textbuf->h;
Uint8 *dst;
int height;
TTF_initLineMectrics(font, textbuf, row, &dst, &height);
/* Draw line */
for (line=height; line>0 && dst < dst_check; --line) {
/* 1 because 0 is the bg color */
SDL_memset(dst, 1, textbuf->w);
dst += textbuf->pitch;
}
}
/* Draw a shaded line of underline_height (+ optional outline)
at the given row. The row value must take the
outline into account.
*/
static void TTF_drawLine_Shaded(const TTF_Font *font, const SDL_Surface *textbuf, const int row)
{
int line;
Uint8 *dst_check = (Uint8*)textbuf->pixels + textbuf->pitch * textbuf->h;
Uint8 *dst;
int height;
TTF_initLineMectrics(font, textbuf, row, &dst, &height);
/* Draw line */
for (line=height; line>0 && dst < dst_check; --line) {
SDL_memset(dst, NUM_GRAYS - 1, textbuf->w);
dst += textbuf->pitch;
}
}
/* Draw a blended line of underline_height (+ optional outline)
at the given row. The row value must take the
outline into account.
*/
static void TTF_drawLine_Blended(const TTF_Font *font, const SDL_Surface *textbuf, const int row, const Uint32 color)
{
int line;
Uint32 *dst_check = (Uint32*)textbuf->pixels + textbuf->pitch/4 * textbuf->h;
Uint8 *dst8; /* destination, byte version */
Uint32 *dst;
int height;
int col;
Uint32 pixel = color | 0xFF000000; /* Amask */
TTF_initLineMectrics(font, textbuf, row, &dst8, &height);
dst = (Uint32 *) dst8;
/* Draw line */
for (line=height; line>0 && dst < dst_check; --line) {
for (col=0; col < textbuf->w; ++col) {
dst[col] = pixel;
}
dst += textbuf->pitch/4;
}
}
/* rcg06192001 get linked library's version. */
const SDL_version *TTF_Linked_Version(void)
{
static SDL_version linked_version;
SDL_TTF_VERSION(&linked_version);
return(&linked_version);
}
/* This function tells the library whether UNICODE text is generally
byteswapped. A UNICODE BOM character at the beginning of a string
will override this setting for that string.
*/
void TTF_ByteSwappedUNICODE(int swapped)
{
TTF_byteswapped = swapped;
}
static void TTF_SetFTError(const char *msg, FT_Error error)
{
#ifdef USE_FREETYPE_ERRORS
#undef FTERRORS_H
#define FT_ERRORDEF(e, v, s) { e, s },
static const struct
{
int err_code;
const char* err_msg;
} ft_errors[] = {
#include <freetype/fterrors.h>
};
int i;
const char *err_msg;
char buffer[1024];
err_msg = NULL;
for (i=0; i<((sizeof ft_errors)/(sizeof ft_errors[0])); ++i) {
if (error == ft_errors[i].err_code) {
err_msg = ft_errors[i].err_msg;
break;
}
}
if (!err_msg) {
err_msg = "unknown FreeType error";
}
TTF_SetError("%s: %s", msg, err_msg);
#else
TTF_SetError("%s", msg);
#endif /* USE_FREETYPE_ERRORS */
}
int TTF_Init(void)
{
int status = 0;
if (!TTF_initialized) {
FT_Error error = FT_Init_FreeType(&library);
if (error) {
TTF_SetFTError("Couldn't init FreeType engine", error);
status = -1;
}
}
if (status == 0) {
++TTF_initialized;
}
return status;
}
static unsigned long RWread(
FT_Stream stream,
unsigned long offset,
unsigned char* buffer,
unsigned long count
)
{
SDL_RWops *src;
src = (SDL_RWops *)stream->descriptor.pointer;
SDL_RWseek(src, (int)offset, RW_SEEK_SET);
if (count == 0) {
return 0;
}
return (unsigned long)SDL_RWread(src, buffer, 1, (int)count);
}
TTF_Font* TTF_OpenFontIndexRW(SDL_RWops *src, int freesrc, int ptsize, long index)
{
TTF_Font* font;
FT_Error error;
FT_Face face;
FT_Fixed scale;
FT_Stream stream;
FT_CharMap found;
Sint64 position;
int i;
if (!TTF_initialized) {
TTF_SetError("Library not initialized");
if (src && freesrc) {
SDL_RWclose(src);
}
return NULL;
}
if (!src) {
TTF_SetError("Passed a NULL font source");
return NULL;
}
/* Check to make sure we can seek in this stream */
position = SDL_RWtell(src);
if (position < 0) {
TTF_SetError("Can't seek in stream");
if (freesrc) {
SDL_RWclose(src);
}
return NULL;
}
font = (TTF_Font*)SDL_malloc(sizeof *font);
if (font == NULL) {
TTF_SetError("Out of memory");
if (freesrc) {
SDL_RWclose(src);
}
return NULL;
}
SDL_memset(font, 0, sizeof(*font));
font->src = src;
font->freesrc = freesrc;
stream = (FT_Stream)SDL_malloc(sizeof(*stream));
if (stream == NULL) {
TTF_SetError("Out of memory");
TTF_CloseFont(font);
return NULL;
}
SDL_memset(stream, 0, sizeof(*stream));
stream->read = RWread;
stream->descriptor.pointer = src;
stream->pos = (unsigned long)position;
stream->size = (unsigned long)(SDL_RWsize(src) - position);
font->args.flags = FT_OPEN_STREAM;
font->args.stream = stream;
error = FT_Open_Face(library, &font->args, index, &font->face);
if (error) {
TTF_SetFTError("Couldn't load font file", error);
TTF_CloseFont(font);
return NULL;
}
face = font->face;
/* Set charmap for loaded font */
found = 0;
#if 0 /* Font debug code */
for (i = 0; i < face->num_charmaps; i++) {
FT_CharMap charmap = face->charmaps[i];
printf("Found charmap: platform id %d, encoding id %d\n", charmap->platform_id, charmap->encoding_id);
}
#endif
if (!found) {
for (i = 0; i < face->num_charmaps; i++) {
FT_CharMap charmap = face->charmaps[i];
if (charmap->platform_id == 3 && charmap->encoding_id == 10) { /* UCS-4 Unicode */
found = charmap;
break;
}
}
}
if (!found) {
for (i = 0; i < face->num_charmaps; i++) {
FT_CharMap charmap = face->charmaps[i];
if ((charmap->platform_id == 3 && charmap->encoding_id == 1) /* Windows Unicode */
|| (charmap->platform_id == 3 && charmap->encoding_id == 0) /* Windows Symbol */
|| (charmap->platform_id == 2 && charmap->encoding_id == 1) /* ISO Unicode */
|| (charmap->platform_id == 0)) { /* Apple Unicode */
found = charmap;
break;
}
}
}
if (found) {
/* If this fails, continue using the default charmap */
FT_Set_Charmap(face, found);
}
/* Make sure that our font face is scalable (global metrics) */
if (FT_IS_SCALABLE(face)) {
/* Set the character size and use default DPI (72) */
error = FT_Set_Char_Size(font->face, 0, ptsize * 64, 0, 0);
if (error) {
TTF_SetFTError("Couldn't set font size", error);
TTF_CloseFont(font);
return NULL;
}
/* Get the scalable font metrics for this font */
scale = face->size->metrics.y_scale;
font->ascent = FT_CEIL(FT_MulFix(face->ascender, scale));
font->descent = FT_CEIL(FT_MulFix(face->descender, scale));
font->height = font->ascent - font->descent + /* baseline */ 1;
font->lineskip = FT_CEIL(FT_MulFix(face->height, scale));
font->underline_offset = FT_FLOOR(FT_MulFix(face->underline_position, scale));
font->underline_height = FT_FLOOR(FT_MulFix(face->underline_thickness, scale));
} else {
/* Non-scalable font case. ptsize determines which family
* or series of fonts to grab from the non-scalable format.
* It is not the point size of the font.
* */
if (ptsize >= font->face->num_fixed_sizes)
ptsize = font->face->num_fixed_sizes - 1;
font->font_size_family = ptsize;
error = FT_Set_Pixel_Sizes(face,
face->available_sizes[ptsize].width,
face->available_sizes[ptsize].height);
/* With non-scalale fonts, Freetype2 likes to fill many of the
* font metrics with the value of 0. The size of the
* non-scalable fonts must be determined differently
* or sometimes cannot be determined.
* */
font->ascent = face->available_sizes[ptsize].height;
font->descent = 0;
font->height = face->available_sizes[ptsize].height;
font->lineskip = FT_CEIL(font->ascent);
font->underline_offset = FT_FLOOR(face->underline_position);
font->underline_height = FT_FLOOR(face->underline_thickness);
}
if (font->underline_height < 1) {
font->underline_height = 1;
}
#ifdef DEBUG_FONTS
printf("Font metrics:\n");
printf("\tascent = %d, descent = %d\n",
font->ascent, font->descent);
printf("\theight = %d, lineskip = %d\n",
font->height, font->lineskip);
printf("\tunderline_offset = %d, underline_height = %d\n",
font->underline_offset, font->underline_height);
printf("\tunderline_top_row = %d, strikethrough_top_row = %d\n",
TTF_underline_top_row(font), TTF_strikethrough_top_row(font));
#endif
/* Initialize the font face style */
font->face_style = TTF_STYLE_NORMAL;
if (font->face->style_flags & FT_STYLE_FLAG_BOLD) {
font->face_style |= TTF_STYLE_BOLD;
}
if (font->face->style_flags & FT_STYLE_FLAG_ITALIC) {
font->face_style |= TTF_STYLE_ITALIC;
}
/* Set the default font style */
font->style = font->face_style;
font->outline = 0;
font->kerning = 1;
font->glyph_overhang = face->size->metrics.y_ppem / 10;
/* x offset = cos(((90.0-12)/360)*2*M_PI), or 12 degree angle */
font->glyph_italics = 0.207f;
font->glyph_italics *= font->height;
return font;
}
TTF_Font* TTF_OpenFontRW(SDL_RWops *src, int freesrc, int ptsize)
{
return TTF_OpenFontIndexRW(src, freesrc, ptsize, 0);
}
TTF_Font* TTF_OpenFontIndex(const char *file, int ptsize, long index)
{
SDL_RWops *rw = SDL_RWFromFile(file, "rb");
if (rw == NULL) {
return NULL;
}
return TTF_OpenFontIndexRW(rw, 1, ptsize, index);
}
TTF_Font* TTF_OpenFont(const char *file, int ptsize)
{
return TTF_OpenFontIndex(file, ptsize, 0);
}
static void Flush_Glyph(c_glyph* glyph)
{
+ glyph->face = 0;
glyph->stored = 0;
glyph->index = 0;
if (glyph->bitmap.buffer) {
SDL_free(glyph->bitmap.buffer);
glyph->bitmap.buffer = 0;
}
if (glyph->pixmap.buffer) {
SDL_free(glyph->pixmap.buffer);
glyph->pixmap.buffer = 0;
}
glyph->cached = 0;
}
static void Flush_Cache(TTF_Font* font)
{
int i;
int size = sizeof(font->cache) / sizeof(font->cache[0]);
for (i = 0; i < size; ++i) {
if (font->cache[i].cached) {
Flush_Glyph(&font->cache[i]);
}
}
}
+static int Get_Char_Index_Using_Font_Fallback(TTF_Font* font, Uint32 ch, c_glyph* cached)
+{
+ int i;
+
+ /* Check if the glyph can be found in current face */
+ cached->index = FT_Get_Char_Index(font->face, ch);
+ if (cached->index > 0) {
+ /* Found it */
+ cached->face = font->face;
+ return 1;
+ }
+
+ /* Find the glyph in font fallbacks */
+ for (i = 0; i < font->num_of_fallbacks; i++) {
+ if (font->fallbacks[i] && font->fallbacks[i]->face) {
+ if (Get_Char_Index_Using_Font_Fallback(font->fallbacks[i], ch, cached)) {
+ return 1;
+ }
+ }
+ }
+
+ /* Not found */
+ return 0;
+}
+
static FT_Error Load_Glyph(TTF_Font* font, Uint32 ch, c_glyph* cached, int want)
{
FT_Face face;
FT_Error error;
FT_GlyphSlot glyph;
FT_Glyph_Metrics* metrics;
FT_Outline* outline;
if (!font || !font->face) {
return FT_Err_Invalid_Handle;
}
- face = font->face;
-
/* Load the glyph */
if (!cached->index) {
- cached->index = FT_Get_Char_Index(face, ch);
+ if (!Get_Char_Index_Using_Font_Fallback(font, ch, cached)) {
+ /* Glyph is not found in all of fallbacks */
+ cached->index = 0;
+ cached->face = font->face;
+ }
}
+
+ face = cached->face;
+
error = FT_Load_Glyph(face, cached->index, FT_LOAD_DEFAULT | font->hinting);
if (error) {
return error;
}
/* Get our glyph shortcuts */
glyph = face->glyph;
metrics = &glyph->metrics;
outline = &glyph->outline;
/* Get the glyph metrics if desired */
if ((want & CACHED_METRICS) && !(cached->stored & CACHED_METRICS)) {
if (FT_IS_SCALABLE(face)) {
/* Get the bounding box */
cached->minx = FT_FLOOR(metrics->horiBearingX);
cached->maxx = FT_CEIL(metrics->horiBearingX + metrics->width);
cached->maxy = FT_FLOOR(metrics->horiBearingY);
cached->miny = cached->maxy - FT_CEIL(metrics->height);
cached->yoffset = font->ascent - cached->maxy;
cached->advance = FT_CEIL(metrics->horiAdvance);
} else {
/* Get the bounding box for non-scalable format.
* Again, freetype2 fills in many of the font metrics
* with the value of 0, so some of the values we
* need must be calculated differently with certain
* assumptions about non-scalable formats.
* */
cached->minx = FT_FLOOR(metrics->horiBearingX);
cached->maxx = FT_CEIL(metrics->horiBearingX + metrics->width);
cached->maxy = FT_FLOOR(metrics->horiBearingY);
cached->miny = cached->maxy - FT_CEIL(face->available_sizes[font->font_size_family].height);
cached->yoffset = 0;
cached->advance = FT_CEIL(metrics->horiAdvance);
}
/* Adjust for bold and italic text */
if (TTF_HANDLE_STYLE_BOLD(font)) {
cached->maxx += font->glyph_overhang;
}
if (TTF_HANDLE_STYLE_ITALIC(font)) {
cached->maxx += (int)SDL_ceil(font->glyph_italics);
}
cached->stored |= CACHED_METRICS;
}
if (((want & CACHED_BITMAP) && !(cached->stored & CACHED_BITMAP)) ||
((want & CACHED_PIXMAP) && !(cached->stored & CACHED_PIXMAP))) {
int mono = (want & CACHED_BITMAP);
int i;
FT_Bitmap* src;
FT_Bitmap* dst;
FT_Glyph bitmap_glyph = NULL;
/* Handle the italic style */
if (TTF_HANDLE_STYLE_ITALIC(font)) {
FT_Matrix shear;
shear.xx = 1 << 16;
shear.xy = (int) (font->glyph_italics * (1 << 16)) / font->height;
shear.yx = 0;
shear.yy = 1 << 16;
FT_Outline_Transform(outline, &shear);
}
/* Render as outline */
if ((font->outline > 0) && glyph->format != FT_GLYPH_FORMAT_BITMAP) {
FT_Stroker stroker;
FT_Get_Glyph(glyph, &bitmap_glyph);
error = FT_Stroker_New(library, &stroker);
if (error) {
return error;
}
FT_Stroker_Set(stroker, font->outline * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
FT_Glyph_Stroke(&bitmap_glyph, stroker, 1 /* delete the original glyph */);
FT_Stroker_Done(stroker);
/* Render the glyph */
error = FT_Glyph_To_Bitmap(&bitmap_glyph, mono ? ft_render_mode_mono : ft_render_mode_normal, 0, 1);
if (error) {
FT_Done_Glyph(bitmap_glyph);
return error;
}
src = &((FT_BitmapGlyph)bitmap_glyph)->bitmap;
} else {
/* Render the glyph */
error = FT_Render_Glyph(glyph, mono ? ft_render_mode_mono : ft_render_mode_normal);
if (error) {
return error;
}
src = &glyph->bitmap;
}
/* Copy over information to cache */
if (mono) {
dst = &cached->bitmap;
} else {
dst = &cached->pixmap;
}
SDL_memcpy(dst, src, sizeof(*dst));
/* FT_Render_Glyph() and .fon fonts always generate a
* two-color (black and white) glyphslot surface, even
* when rendered in ft_render_mode_normal. */
/* FT_IS_SCALABLE() means that the font is in outline format,
* but does not imply that outline is rendered as 8-bit
* grayscale, because embedded bitmap/graymap is preferred
* (see FT_LOAD_DEFAULT section of FreeType2 API Reference).
* FT_Render_Glyph() canreturn two-color bitmap or 4/16/256-
* color graymap according to the format of embedded bitmap/
* graymap. */
if (src->pixel_mode == FT_PIXEL_MODE_MONO) {
dst->pitch *= 8;
} else if (src->pixel_mode == FT_PIXEL_MODE_GRAY2) {
dst->pitch *= 4;
} else if (src->pixel_mode == FT_PIXEL_MODE_GRAY4) {
dst->pitch *= 2;
}
/* Adjust for bold and italic text */
if (TTF_HANDLE_STYLE_BOLD(font)) {
int bump = font->glyph_overhang;
dst->pitch += bump;
dst->width += bump;
}
if (TTF_HANDLE_STYLE_ITALIC(font)) {
int bump = (int)SDL_ceil(font->glyph_italics);
dst->pitch += bump;
dst->width += bump;
}
if (dst->rows != 0) {
dst->buffer = (unsigned char *)SDL_malloc(dst->pitch * dst->rows);
if (!dst->buffer) {
return FT_Err_Out_Of_Memory;
}
SDL_memset(dst->buffer, 0, dst->pitch * dst->rows);
for (i = 0; i < src->rows; i++) {
int soffset = i * src->pitch;
int doffset = i * dst->pitch;
if (mono) {
unsigned char *srcp = src->buffer + soffset;
unsigned char *dstp = dst->buffer + doffset;
int j;
if (src->pixel_mode == FT_PIXEL_MODE_MONO) {
for (j = 0; j < src->width; j += 8) {
unsigned char c = *srcp++;
*dstp++ = (c&0x80) >> 7;
c <<= 1;
*dstp++ = (c&0x80) >> 7;
c <<= 1;
*dstp++ = (c&0x80) >> 7;
c <<= 1;
*dstp++ = (c&0x80) >> 7;
c <<= 1;
*dstp++ = (c&0x80) >> 7;
c <<= 1;
*dstp++ = (c&0x80) >> 7;
c <<= 1;
*dstp++ = (c&0x80) >> 7;
c <<= 1;
*dstp++ = (c&0x80) >> 7;
}
} else if (src->pixel_mode == FT_PIXEL_MODE_GRAY2) {
for (j = 0; j < src->width; j += 4) {
unsigned char c = *srcp++;
*dstp++ = (((c&0xA0) >> 6) >= 0x2) ? 1 : 0;
c <<= 2;
*dstp++ = (((c&0xA0) >> 6) >= 0x2) ? 1 : 0;
c <<= 2;
*dstp++ = (((c&0xA0) >> 6) >= 0x2) ? 1 : 0;
c <<= 2;
*dstp++ = (((c&0xA0) >> 6) >= 0x2) ? 1 : 0;
}
} else if (src->pixel_mode == FT_PIXEL_MODE_GRAY4) {
for (j = 0; j < src->width; j += 2) {
unsigned char c = *srcp++;
*dstp++ = (((c&0xF0) >> 4) >= 0x8) ? 1 : 0;
c <<= 4;
*dstp++ = (((c&0xF0) >> 4) >= 0x8) ? 1 : 0;
}
} else {
for (j = 0; j < src->width; j++) {
unsigned char c = *srcp++;
*dstp++ = (c >= 0x80) ? 1 : 0;
}
}
} else if (src->pixel_mode == FT_PIXEL_MODE_MONO) {
/* This special case wouldn't
* be here if the FT_Render_Glyph()
* function wasn't buggy when it tried
* to render a .fon font with 256
* shades of gray. Instead, it
* returns a black and white surface
* and we have to translate it back
* to a 256 gray shaded surface.
* */
unsigned char *srcp = src->buffer + soffset;
unsigned char *dstp = dst->buffer + doffset;
unsigned char c;
int j, k;
for (j = 0; j < src->width; j += 8) {
c = *srcp++;
for (k = 0; k < 8; ++k) {
if ((c&0x80) >> 7) {
*dstp++ = NUM_GRAYS - 1;
} else {
*dstp++ = 0x00;
}
c <<= 1;
}
}
} else if (src->pixel_mode == FT_PIXEL_MODE_GRAY2) {
unsigned char *srcp = src->buffer + soffset;
unsigned char *dstp = dst->buffer + doffset;
unsigned char c;
int j, k;
for (j = 0; j < src->width; j += 4) {
c = *srcp++;
for (k = 0; k < 4; ++k) {
if ((c&0xA0) >> 6) {
*dstp++ = NUM_GRAYS * ((c&0xA0) >> 6) / 3 - 1;
} else {
*dstp++ = 0x00;
}
c <<= 2;
}
}
} else if (src->pixel_mode == FT_PIXEL_MODE_GRAY4) {
unsigned char *srcp = src->buffer + soffset;
unsigned char *dstp = dst->buffer + doffset;
unsigned char c;
int j, k;
for (j = 0; j < src->width; j += 2) {
c = *srcp++;
for (k = 0; k < 2; ++k) {
if ((c&0xF0) >> 4) {
*dstp++ = NUM_GRAYS * ((c&0xF0) >> 4) / 15 - 1;
} else {
*dstp++ = 0x00;
}
c <<= 4;
}
}
} else {
SDL_memcpy(dst->buffer+doffset,
src->buffer+soffset, src->pitch);
}
}
}
/* Handle the bold style */
if (TTF_HANDLE_STYLE_BOLD(font)) {
int row;
int col;
int offset;
int pixel;
Uint8* pixmap;
/* The pixmap is a little hard, we have to add and clamp */
for (row = dst->rows - 1; row >= 0; --row) {
pixmap = (Uint8*) dst->buffer + row * dst->pitch;
for (offset=1; offset <= font->glyph_overhang; ++offset) {
for (col = dst->width - 1; col > 0; --col) {
if (mono) {
pixmap[col] |= pixmap[col-1];
} else {
pixel = (pixmap[col] + pixmap[col-1]);
if (pixel > NUM_GRAYS - 1) {
pixel = NUM_GRAYS - 1;
}
pixmap[col] = (Uint8) pixel;
}
}
}
}
}
/* Mark that we rendered this format */
if (mono) {
cached->stored |= CACHED_BITMAP;
} else {
cached->stored |= CACHED_PIXMAP;
}
/* Free outlined glyph */
if (bitmap_glyph) {
FT_Done_Glyph(bitmap_glyph);
}
}
/* We're done, mark this glyph cached */
cached->cached = ch;
return 0;
}
static FT_Error Find_Glyph(TTF_Font* font, Uint32 ch, int want)
{
int retval = 0;
int hsize = sizeof(font->cache) / sizeof(font->cache[0]);
int h = ch % hsize;
font->current = &font->cache[h];
if (font->current->cached != ch)
Flush_Glyph(font->current);
if ((font->current->stored & want) != want) {
retval = Load_Glyph(font, ch, font->current, want);
}
return retval;
}
void TTF_CloseFont(TTF_Font* font)
{
if (font) {
Flush_Cache(font);
if (font->face) {
FT_Done_Face(font->face);
}
if (font->args.stream) {
SDL_free(font->args.stream);
}
if (font->freesrc) {
SDL_RWclose(font->src);
}
+ if (font->fallbacks) {
+ SDL_free(font->fallbacks);
+ }
SDL_free(font);
}
}
+int TTF_SetFontFallback(TTF_Font *font, int number_of_fallbacks, TTF_Font **fallbacks)
+{
+ /* Flush the cache */
+ Flush_Cache(font);
+
+ /* Clear old font fallbacks */
+ if (font->fallbacks) {
+ SDL_free(font->fallbacks);
+ }
+ font->num_of_fallbacks = 0;
+ font->fallbacks = 0;
+
+ /* Set new font fallbacks */
+ if (number_of_fallbacks > 0 && fallbacks) {
+ TTF_Font **new_fallbacks = (TTF_Font **)SDL_malloc(sizeof(TTF_Font*) * number_of_fallbacks);
+ if (new_fallbacks == NULL) {
+ TTF_SetError("Out of memory");
+ return 0;
+ }
+ memcpy(new_fallbacks, fallbacks, sizeof(TTF_Font*) * number_of_fallbacks);
+ font->num_of_fallbacks = number_of_fallbacks;
+ font->fallbacks = new_fallbacks;
+ }
+
+ return 1;
+}
+
/* Gets the number of bytes needed to convert a Latin-1 string to UTF-8 */
static size_t LATIN1_to_UTF8_len(const char *text)
{
size_t bytes = 1;
while (*text) {
Uint8 ch = *(Uint8*)text++;
if (ch <= 0x7F) {
bytes += 1;
} else {
bytes += 2;
}
}
return bytes;
}
/* Gets the number of bytes needed to convert a UTF-16 string to UTF-8 */
static size_t UTF16_to_UTF8_len(const Uint16 *text)
{
size_t bytes = 1;
int swapped = TTF_byteswapped;
while (*text) {
Uint16 ch = *text++;
if (ch == UNICODE_BOM_NATIVE) {
swapped = 0;
continue;
}
if (ch == UNICODE_BOM_SWAPPED) {
swapped = 1;
continue;
}
if (swapped) {
ch = SDL_Swap16(ch);
}
if (ch <= 0x7F) {
bytes += 1;
} else if (ch <= 0x7FF) {
bytes += 2;
} else if (ch >= 0xD800 && ch <= 0xDBFF) { /* high surrogate */
bytes += 4;
if (*text) text++; /* skip next character unless it is '\0' */
} else {
bytes += 3;
}
}
return bytes;
}
/* Gets the number of bytes needed to convert a UTF-32 string to UTF-8 */
static size_t UTF32_to_UTF8_len(const Uint32 *text)
{
size_t bytes = 1;
while (*text) {
Uint32 ch = *text++;
if (ch <= 0x7F) {
bytes += 1;
} else if (ch <= 0x7FF) {
bytes += 2;
} else if (ch <= 0xFFFF) {
bytes += 3;
} else {
bytes += 4;
}
}
return bytes;
}
/* Convert a Latin-1 string to a UTF-8 string */
static void LATIN1_to_UTF8(const char *src, Uint8 *dst)
{
while (*src) {
Uint8 ch = *(Uint8*)src++;
if (ch <= 0x7F) {
*dst++ = ch;
} else {
*dst++ = 0xC0 | ((ch >> 6) & 0x1F);
*dst++ = 0x80 | (ch & 0x3F);
}
}
*dst = '\0';
}
/* Convert a UTF-16 string to a UTF-8 string */
static void UTF16_to_UTF8(const Uint16 *src, Uint8 *dst)
{
int swapped = TTF_byteswapped;
while (*src) {
Uint16 ch = *src++;
if (ch == UNICODE_BOM_NATIVE) {
swapped = 0;
continue;
}
if (ch == UNICODE_BOM_SWAPPED) {
swapped = 1;
continue;
}
if (swapped) {
ch = SDL_Swap16(ch);
}
if (ch <= 0x7F) {
*dst++ = (Uint8) ch;
} else if (ch <= 0x7FF) {
*dst++ = 0xC0 | (Uint8) ((ch >> 6) & 0x1F);
*dst++ = 0x80 | (Uint8) (ch & 0x3F);
} else if (ch >= 0xD800 && ch <= 0xDBFF) { /* high surrogate */
Uint32 ch2 = 0x10000 + ((ch - 0xD800) << 10);
ch = *src; /* get next character */
if (ch) src++; /* and advance to next character unless current character is '\0' */
if (ch >= 0xDC00 && ch <= 0xDFFF) { /* low surrogate */
ch2 += ch - 0xDC00;
*dst++ = 0xF0 | (Uint8) ((ch2 >> 18) & 0x07);
*dst++ = 0x80 | (Uint8) ((ch2 >> 12) & 0x3F);
*dst++ = 0x80 | (Uint8) ((ch2 >> 6) & 0x3F);
*dst++ = 0x80 | (Uint8) (ch2 & 0x3F);
} else { /* invalid */
*dst++ = 0xEF; *dst++ = 0xBF; *dst++ = 0xBD; /* UTF-8 EF BF BD == 0xFFFD == replacement character */
}
} else {
if (ch >= 0xDC00 && ch <= 0xDFFF) { /* low surrogate, which is invalid alone */
*dst++ = 0xEF; *dst++ = 0xBF; *dst++ = 0xBD; /* UTF-8 EF BF BD == 0xFFFD == replacement character */
} else {
*dst++ = 0xE0 | (Uint8) ((ch >> 12) & 0x0F);
*dst++ = 0x80 | (Uint8) ((ch >> 6) & 0x3F);
*dst++ = 0x80 | (Uint8) (ch & 0x3F);
}
}
}
*dst = '\0';
}
/* Convert a UTF-32 string to a UTF-8 string */
static void UTF32_to_UTF8(const Uint32 *src, Uint8 *dst)
{
while (*src) {
Uint32 ch = *src++;
if (ch <= 0x7F) {
*dst++ = (Uint8) ch;
} else if (ch <= 0x7FF) {
*dst++ = 0xC0 | (Uint8) ((ch >> 6) & 0x1F);
*dst++ = 0x80 | (Uint8) (ch & 0x3F);
} else if ((ch >= 0xD800 && ch <= 0xDFFF) || ch >= 0x10FFFF) { /* surrogate or out of Unicode range */
*dst++ = 0xEF; *dst++ = 0xBF; *dst++ = 0xBD; /* UTF-8 EF BF BD == 0xFFFD == replacement character */
} else if (ch <= 0xFFFF) {
*dst++ = 0xE0 | (Uint8) ((ch >> 12) & 0x0F);
*dst++ = 0x80 | (Uint8) ((ch >> 6) & 0x3F);
*dst++ = 0x80 | (Uint8) (ch & 0x3F);
} else {
*dst++ = 0xF0 | (Uint8) ((ch >> 18) & 0x07);
*dst++ = 0x80 | (Uint8) ((ch >> 12) & 0x3F);
*dst++ = 0x80 | (Uint8) ((ch >> 6) & 0x3F);
*dst++ = 0x80 | (Uint8) (ch & 0x3F);
}
}
*dst = '\0';
}
/* Gets a unicode value from a UTF-8 encoded string and advance the string */
#define UNKNOWN_UNICODE 0xFFFD
static Uint32 UTF8_getch(const char **src, size_t *srclen)
{
const Uint8 *p = *(const Uint8**)src;
size_t left = 0;
SDL_bool overlong = SDL_FALSE;
SDL_bool underflow = SDL_FALSE;
Uint32 ch = UNKNOWN_UNICODE;
if (*srclen == 0) {
return UNKNOWN_UNICODE;
}
if (p[0] >= 0xFC) {
if ((p[0] & 0xFE) == 0xFC) {
if (p[0] == 0xFC && (p[1] & 0xFC) == 0x80) {
overlong = SDL_TRUE;
}
ch = (Uint32) (p[0] & 0x01);
left = 5;
}
} else if (p[0] >= 0xF8) {
if ((p[0] & 0xFC) == 0xF8) {
if (p[0] == 0xF8 && (p[1] & 0xF8) == 0x80) {
overlong = SDL_TRUE;
}
ch = (Uint32) (p[0] & 0x03);
left = 4;
}
} else if (p[0] >= 0xF0) {
if ((p[0] & 0xF8) == 0xF0) {
if (p[0] == 0xF0 && (p[1] & 0xF0) == 0x80) {
overlong = SDL_TRUE;
}
ch = (Uint32) (p[0] & 0x07);
left = 3;
}
} else if (p[0] >= 0xE0) {
if ((p[0] & 0xF0) == 0xE0) {
if (p[0] == 0xE0 && (p[1] & 0xE0) == 0x80) {
overlong = SDL_TRUE;
}
ch = (Uint32) (p[0] & 0x0F);
left = 2;
}
} else if (p[0] >= 0xC0) {
if ((p[0] & 0xE0) == 0xC0) {
if ((p[0] & 0xDE) == 0xC0) {
overlong = SDL_TRUE;
}
ch = (Uint32) (p[0] & 0x1F);
left = 1;
}
} else {
if ((p[0] & 0x80) == 0x00) {
ch = (Uint32) p[0];
}
}
++*src;
--*srclen;
while (left > 0 && *srclen > 0) {
++p;
if ((p[0] & 0xC0) != 0x80) {
ch = UNKNOWN_UNICODE;
break;
}
ch <<= 6;
ch |= (p[0] & 0x3F);
++*src;
--*srclen;
--left;
}
if (left > 0) {
underflow = SDL_TRUE;
}
/* Technically overlong sequences are invalid and should not be interpreted.
However, it doesn't cause a security risk here and I don't see any harm in
displaying them. The application is responsible for any other side effects
of allowing overlong sequences (e.g. string compares failing, etc.)
See bug 1931 for sample input that triggers this.
*/
/*if (overlong) return UNKNOWN_UNICODE;*/
if (underflow ||
(ch >= 0xD800 && ch <= 0xDFFF) ||
(ch == 0xFFFE || ch == 0xFFFF) || ch > 0x10FFFF) {
ch = UNKNOWN_UNICODE;
}
return ch;
}
int TTF_FontHeight(const TTF_Font *font)
{
return(font->height);
}
int TTF_FontAscent(const TTF_Font *font)
{
return(font->ascent);
}
int TTF_FontDescent(const TTF_Font *font)
{
return(font->descent);
}
int TTF_FontLineSkip(const TTF_Font *font)
{
return(font->lineskip);
}
int TTF_GetFontKerning(const TTF_Font *font)
{
return(font->kerning);
}
void TTF_SetFontKerning(TTF_Font *font, int allowed)
{
font->kerning = allowed;
}
long TTF_FontFaces(const TTF_Font *font)
{
return(font->face->num_faces);
}
int TTF_FontFaceIsFixedWidth(const TTF_Font *font)
{
return(FT_IS_FIXED_WIDTH(font->face));
}
char *TTF_FontFaceFamilyName(const TTF_Font *font)
{
return(font->face->family_name);
}
char *TTF_FontFaceStyleName(const TTF_Font *font)
{
return(font->face->style_name);
}
int TTF_GlyphIsProvided(const TTF_Font *font, Uint32 ch)
{
return(FT_Get_Char_Index(font->face, ch));
}
int TTF_GlyphMetrics(TTF_Font *font, Uint32 ch,
int* minx, int* maxx, int* miny, int* maxy, int* advance)
{
FT_Error error;
error = Find_Glyph(font, ch, CACHED_METRICS);
if (error) {
TTF_SetFTError("Couldn't find glyph", error);
return -1;
}
if (minx) {
*minx = font->current->minx;
}
if (maxx) {
*maxx = font->current->maxx;
if (TTF_HANDLE_STYLE_BOLD(font)) {
*maxx += font->glyph_overhang;
}
}
if (miny) {
*miny = font->current->miny;
}
if (maxy) {
*maxy = font->current->maxy;
}
if (advance) {
*advance = font->current->advance;
if (TTF_HANDLE_STYLE_BOLD(font)) {
*advance += font->glyph_overhang;
}
}
return 0;
}
int TTF_SizeText(TTF_Font *font, const char *text, int *w, int *h)
{
int status = -1;
Uint8 *utf8;
TTF_CHECKPOINTER(text, -1);
utf8 = SDL_stack_alloc(Uint8, LATIN1_to_UTF8_len(text));
if (utf8) {
LATIN1_to_UTF8(text, utf8);
status = TTF_SizeUTF8(font, (char *)utf8, w, h);
SDL_stack_free(utf8);
} else {
SDL_OutOfMemory();
}
return status;
}
int TTF_SizeUTF8(TTF_Font *font, const char *text, int *w, int *h)
{
int status;
int x, z;
int minx, maxx;
int miny, maxy;
c_glyph *glyph;
FT_Error error;
FT_Long use_kerning;
+ FT_Face prev_face = 0;
FT_UInt prev_index = 0;
int outline_delta = 0;
size_t textlen;
TTF_CHECKPOINTER(text, -1);
/* Initialize everything to 0 */
status = 0;
minx = maxx = 0;
miny = maxy = 0;
/* check kerning */
use_kerning = FT_HAS_KERNING(font->face) && font->kerning;
/* Init outline handling */
if (font->outline > 0) {
outline_delta = font->outline * 2;
}
/* Load each character and sum it's bounding box */
textlen = SDL_strlen(text);
x= 0;
while (textlen > 0) {
Uint32 c = UTF8_getch(&text, &textlen);
if (c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED) {
continue;
}
error = Find_Glyph(font, c, CACHED_METRICS);
if (error) {
TTF_SetFTError("Couldn't find glyph", error);
return -1;
}
glyph = font->current;
/* handle kerning */
- if (use_kerning && prev_index && glyph->index) {
+ if (use_kerning && prev_index && glyph->index && prev_face == glyph->face) {
FT_Vector delta;
- FT_Get_Kerning(font->face, prev_index, glyph->index, ft_kerning_default, &delta);
+ FT_Get_Kerning(glyph->face, prev_index, glyph->index, ft_kerning_default, &delta);
x += delta.x >> 6;
}
#if 0
if ((ch == text) && (glyph->minx < 0)) {
/* Fixes the texture wrapping bug when the first letter
* has a negative minx value or horibearing value. The entire
* bounding box must be adjusted to be bigger so the entire
* letter can fit without any texture corruption or wrapping.
*
* Effects: First enlarges bounding box.
* Second, xstart has to start ahead of its normal spot in the
* negative direction of the negative minx value.
* (pushes everything to the right).
*
* This will make the memory copy of the glyph bitmap data
* work out correctly.
* */
z -= glyph->minx;
}
#endif
z = x + glyph->minx;
if (minx > z) {
minx = z;
}
if (TTF_HANDLE_STYLE_BOLD(font)) {
x += font->glyph_overhang;
}
if (glyph->advance > glyph->maxx) {
z = x + glyph->advance;
} else {
z = x + glyph->maxx;
}
if (maxx < z) {
maxx = z;
}
x += glyph->advance;
if (glyph->miny < miny) {
miny = glyph->miny;
}
if (glyph->maxy > maxy) {
maxy = glyph->maxy;
}
+ prev_face = glyph->face;
prev_index = glyph->index;
}
/* Fill the bounds rectangle */
if (w) {
/* Add outline extra width */
*w = (maxx - minx) + outline_delta;
}
if (h) {
/* Some fonts descend below font height (FletcherGothicFLF) */
/* Add outline extra height */
*h = (font->ascent - miny) + outline_delta;
if (*h < font->height) {
*h = font->height;
}
/* Update height according to the needs of the underline style */
if (TTF_HANDLE_STYLE_UNDERLINE(font)) {
int bottom_row = TTF_underline_bottom_row(font);
if (*h < bottom_row) {
*h = bottom_row;
}
}
}
return status;
}
int TTF_SizeUNICODE(TTF_Font *font, const Uint16 *text, int *w, int *h)
{
int status = -1;
Uint8 *utf8;
TTF_CHECKPOINTER(text, -1);
utf8 = SDL_stack_alloc(Uint8, UTF16_to_UTF8_len(text));
if (utf8) {
UTF16_to_UTF8(text, utf8);
status = TTF_SizeUTF8(font, (char *)utf8, w, h);
SDL_stack_free(utf8);
} else {
SDL_OutOfMemory();
}
return status;
}
SDL_Surface *TTF_RenderText_Solid(TTF_Font *font,
const char *text, SDL_Color fg)
{
SDL_Surface *surface = NULL;
Uint8 *utf8;
TTF_CHECKPOINTER(text, NULL);
utf8 = SDL_stack_alloc(Uint8, LATIN1_to_UTF8_len(text));
if (utf8) {
LATIN1_to_UTF8(text, utf8);
surface = TTF_RenderUTF8_Solid(font, (char *)utf8, fg);
SDL_stack_free(utf8);
} else {
SDL_OutOfMemory();
}
return surface;
}
SDL_Surface *TTF_RenderUTF8_Solid(TTF_Font *font,
const char *text, SDL_Color fg)
{
int xstart;
int width;
int height;
SDL_Surface* textbuf;
SDL_Palette* palette;
Uint8* src;
Uint8* dst;
Uint8 *dst_check;
int row, col;
c_glyph *glyph;
FT_Bitmap *current;
FT_Error error;
FT_Long use_kerning;
+ FT_Face prev_face = 0;
FT_UInt prev_index = 0;
size_t textlen;
TTF_CHECKPOINTER(text, NULL);
/* Get the dimensions of the text surface */
if ((TTF_SizeUTF8(font, text, &width, &height) < 0) || !width) {
TTF_SetError("Text has zero width");
return NULL;
}
/* Create the target surface */
textbuf = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 8, 0, 0, 0, 0);
if (textbuf == NULL) {
return NULL;
}
/* Adding bound checking to avoid all kinds of memory corruption errors
that may occur. */
dst_check = (Uint8*)textbuf->pixels + textbuf->pitch * textbuf->h;
/* Fill the palette with the foreground color */
palette = textbuf->format->palette;
palette->colors[0].r = 255 - fg.r;
palette->colors[0].g = 255 - fg.g;
palette->colors[0].b = 255 - fg.b;
palette->colors[1].r = fg.r;
palette->colors[1].g = fg.g;
palette->colors[1].b = fg.b;
palette->colors[1].a = fg.a ? fg.a : SDL_ALPHA_OPAQUE;
SDL_SetColorKey(textbuf, SDL_TRUE, 0);
/* check kerning */
use_kerning = FT_HAS_KERNING(font->face) && font->kerning;
/* Load and render each character */
textlen = SDL_strlen(text);
xstart = 0;
while (textlen > 0) {
Uint32 c = UTF8_getch(&text, &textlen);
if (c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED) {
continue;
}
error = Find_Glyph(font, c, CACHED_METRICS|CACHED_BITMAP);
if (error) {
TTF_SetFTError("Couldn't find glyph", error);
SDL_FreeSurface(textbuf);
return NULL;
}
glyph = font->current;
current = &glyph->bitmap;
/* Ensure the width of the pixmap is correct. On some cases,
* freetype may report a larger pixmap than possible.*/
width = current->width;
if (font->outline <= 0 && width > glyph->maxx - glyph->minx) {
width = glyph->maxx - glyph->minx;
}
/* do kerning, if possible AC-Patch */
- if (use_kerning && prev_index && glyph->index) {
+ if (use_kerning && prev_index && glyph->index && prev_face == glyph->face) {
FT_Vector delta;
- FT_Get_Kerning(font->face, prev_index, glyph->index, ft_kerning_default, &delta);
+ FT_Get_Kerning(glyph->face, prev_index, glyph->index, ft_kerning_default, &delta);
xstart += delta.x >> 6;
}
for (row = 0; row < current->rows; ++row) {
/* Make sure we don't go either over, or under the limit */
if ((xstart + glyph->minx) < 0) {
xstart -= (xstart + glyph->minx);
}
if ((row + glyph->yoffset) < 0) {
continue;
}
if ((row + glyph->yoffset) >= textbuf->h) {
continue;
}
dst = (Uint8 *)textbuf->pixels +
(row+glyph->yoffset) * textbuf->pitch +
xstart + glyph->minx;
src = current->buffer + row * current->pitch;
for (col = width; col > 0 && dst < dst_check; --col) {
*dst++ |= *src++;
}
}
xstart += glyph->advance;
if (TTF_HANDLE_STYLE_BOLD(font)) {
xstart += font->glyph_overhang;
}
+ prev_face = glyph->face;
prev_index = glyph->index;
}
/* Handle the underline style */
if (TTF_HANDLE_STYLE_UNDERLINE(font)) {
row = TTF_underline_top_row(font);
TTF_drawLine_Solid(font, textbuf, row);
}
/* Handle the strikethrough style */
if (TTF_HANDLE_STYLE_STRIKETHROUGH(font)) {
row = TTF_strikethrough_top_row(font);
TTF_drawLine_Solid(font, textbuf, row);
}
return textbuf;
}
SDL_Surface *TTF_RenderUNICODE_Solid(TTF_Font *font,
const Uint16 *text, SDL_Color fg)
{
SDL_Surface *surface = NULL;
Uint8 *utf8;
TTF_CHECKPOINTER(text, NULL);
utf8 = SDL_stack_alloc(Uint8, UTF16_to_UTF8_len(text));
if (utf8) {
UTF16_to_UTF8(text, utf8);
surface = TTF_RenderUTF8_Solid(font, (char *)utf8, fg);
SDL_stack_free(utf8);
} else {
SDL_OutOfMemory();
}
return surface;
}
SDL_Surface *TTF_RenderGlyph_Solid(TTF_Font *font, Uint32 ch, SDL_Color fg)
{
Uint32 utf32[2] = { ch, 0 };
Uint8 utf8[8];
UTF32_to_UTF8(utf32, utf8);
return TTF_RenderUTF8_Solid(font, (char *)utf8, fg);
}
SDL_Surface *TTF_RenderText_Shaded(TTF_Font *font,
const char *text, SDL_Color fg, SDL_Color bg)
{
SDL_Surface *surface = NULL;
Uint8 *utf8;
TTF_CHECKPOINTER(text, NULL);
utf8 = SDL_stack_alloc(Uint8, LATIN1_to_UTF8_len(text));
if (utf8) {
LATIN1_to_UTF8(text, utf8);
surface = TTF_RenderUTF8_Shaded(font, (char *)utf8, fg, bg);
SDL_stack_free(utf8);
} else {
SDL_OutOfMemory();
}
return surface;
}
/* Convert the UTF-8 text to UNICODE and render it
*/
SDL_Surface *TTF_RenderUTF8_Shaded(TTF_Font *font,
const char *text, SDL_Color fg, SDL_Color bg)
{
int xstart;
int width;
int height;
SDL_Surface* textbuf;
SDL_Palette* palette;
int index;
int rdiff;
int gdiff;
int bdiff;
int adiff;
Uint8* src;
Uint8* dst;
Uint8* dst_check;
int row, col;
FT_Bitmap* current;
c_glyph *glyph;
FT_Error error;
FT_Long use_kerning;
+ FT_Face prev_face = 0;
FT_UInt prev_index = 0;
size_t textlen;
TTF_CHECKPOINTER(text, NULL);
/* Get the dimensions of the text surface */
if ((TTF_SizeUTF8(font, text, &width, &height) < 0) || !width) {
TTF_SetError("Text has zero width");
return NULL;
}
/* Create the target surface */
textbuf = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 8, 0, 0, 0, 0);
if (textbuf == NULL) {
return NULL;
}
/* Adding bound checking to avoid all kinds of memory corruption errors
that may occur. */
dst_check = (Uint8*)textbuf->pixels + textbuf->pitch * textbuf->h;
/* Support alpha blending */
if (!fg.a) {
fg.a = SDL_ALPHA_OPAQUE;
}
if (!bg.a) {
bg.a = SDL_ALPHA_OPAQUE;
}
if (fg.a != SDL_ALPHA_OPAQUE || bg.a != SDL_ALPHA_OPAQUE) {
SDL_SetSurfaceBlendMode(textbuf, SDL_BLENDMODE_BLEND);
}
/* Fill the palette with NUM_GRAYS levels of shading from bg to fg */
palette = textbuf->format->palette;
rdiff = fg.r - bg.r;
gdiff = fg.g - bg.g;
bdiff = fg.b - bg.b;
adiff = fg.a - bg.a;
for (index = 0; index < NUM_GRAYS; ++index) {
palette->colors[index].r = bg.r + (index*rdiff) / (NUM_GRAYS-1);
palette->colors[index].g = bg.g + (index*gdiff) / (NUM_GRAYS-1);
palette->colors[index].b = bg.b + (index*bdiff) / (NUM_GRAYS-1);
palette->colors[index].a = bg.a + (index*adiff) / (NUM_GRAYS-1);
}
/* check kerning */
use_kerning = FT_HAS_KERNING(font->face) && font->kerning;
/* Load and render each character */
textlen = SDL_strlen(text);
xstart = 0;
while (textlen > 0) {
Uint32 c = UTF8_getch(&text, &textlen);
if (c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED) {
continue;
}
error = Find_Glyph(font, c, CACHED_METRICS|CACHED_PIXMAP);
if (error) {
TTF_SetFTError("Couldn't find glyph", error);
SDL_FreeSurface(textbuf);
return NULL;
}
glyph = font->current;
/* Ensure the width of the pixmap is correct. On some cases,
* freetype may report a larger pixmap than possible.*/
width = glyph->pixmap.width;
if (font->outline <= 0 && width > glyph->maxx - glyph->minx) {
width = glyph->maxx - glyph->minx;
}
/* do kerning, if possible AC-Patch */
- if (use_kerning && prev_index && glyph->index) {
+ if (use_kerning && prev_index && glyph->index && prev_face == glyph->face) {
FT_Vector delta;
- FT_Get_Kerning(font->face, prev_index, glyph->index, ft_kerning_default, &delta);
+ FT_Get_Kerning(glyph->face, prev_index, glyph->index, ft_kerning_default, &delta);
xstart += delta.x >> 6;
}
current = &glyph->pixmap;
for (row = 0; row < current->rows; ++row) {
/* Make sure we don't go either over, or under the limit */
if ((xstart + glyph->minx) < 0) {
xstart -= (xstart + glyph->minx);
}
if ((row + glyph->yoffset) < 0) {
continue;
}
if ((row + glyph->yoffset) >= textbuf->h) {
continue;
}
dst = (Uint8 *)textbuf->pixels +
(row+glyph->yoffset) * textbuf->pitch +
xstart + glyph->minx;
src = current->buffer + row * current->pitch;
for (col = width; col > 0 && dst < dst_check; --col) {
*dst++ |= *src++;
}
}
xstart += glyph->advance;
if (TTF_HANDLE_STYLE_BOLD(font)) {
xstart += font->glyph_overhang;
}
+ prev_face = glyph->face;
prev_index = glyph->index;
}
/* Handle the underline style */
if (TTF_HANDLE_STYLE_UNDERLINE(font)) {
row = TTF_underline_top_row(font);
TTF_drawLine_Shaded(font, textbuf, row);
}
/* Handle the strikethrough style */
if (TTF_HANDLE_STYLE_STRIKETHROUGH(font)) {
row = TTF_strikethrough_top_row(font);
TTF_drawLine_Shaded(font, textbuf, row);
}
return textbuf;
}
SDL_Surface* TTF_RenderUNICODE_Shaded(TTF_Font* font,
const Uint16* text,
SDL_Color fg,
SDL_Color bg)
{
SDL_Surface *surface = NULL;
Uint8 *utf8;
TTF_CHECKPOINTER(text, NULL);
utf8 = SDL_stack_alloc(Uint8, UTF16_to_UTF8_len(text));
if (utf8) {
UTF16_to_UTF8(text, utf8);
surface = TTF_RenderUTF8_Shaded(font, (char *)utf8, fg, bg);
SDL_stack_free(utf8);
} else {
SDL_OutOfMemory();
}
return surface;
}
SDL_Surface* TTF_RenderGlyph_Shaded(TTF_Font* font,
Uint32 ch,
SDL_Color fg,
SDL_Color bg)
{
Uint32 utf32[2] = { ch, 0 };
Uint8 utf8[8];
UTF32_to_UTF8(utf32, utf8);
return TTF_RenderUTF8_Shaded(font, (char *)utf8, fg, bg);
}
SDL_Surface *TTF_RenderText_Blended(TTF_Font *font,
const char *text, SDL_Color fg)
{
SDL_Surface *surface = NULL;
Uint8 *utf8;
TTF_CHECKPOINTER(text, NULL);
utf8 = SDL_stack_alloc(Uint8, LATIN1_to_UTF8_len(text));
if (utf8) {
LATIN1_to_UTF8(text, utf8);
surface = TTF_RenderUTF8_Blended(font, (char *)utf8, fg);
SDL_stack_free(utf8);
} else {
SDL_OutOfMemory();
}
return surface;
}
SDL_Surface *TTF_RenderUTF8_Blended(TTF_Font *font,
const char *text, SDL_Color fg)
{
int i;
int xstart;
int width, height;
SDL_Surface *textbuf;
Uint8 alpha;
Uint8 alpha_table[256];
Uint32 pixel;
Uint8 *src;
Uint32 *dst;
Uint32 *dst_check;
int row, col;
c_glyph *glyph;
FT_Error error;
FT_Long use_kerning;
+ FT_Face prev_face = 0;
FT_UInt prev_index = 0;
size_t textlen;
TTF_CHECKPOINTER(text, NULL);
/* Get the dimensions of the text surface */
if ((TTF_SizeUTF8(font, text, &width, &height) < 0) || !width) {
TTF_SetError("Text has zero width");
return(NULL);
}
/* Create the target surface */
textbuf = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32,
0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
if (textbuf == NULL) {
return(NULL);
}
/* Adding bound checking to avoid all kinds of memory corruption errors
that may occur. */
dst_check = (Uint32*)textbuf->pixels + textbuf->pitch/4 * textbuf->h;
/* check kerning */
use_kerning = FT_HAS_KERNING(font->face) && font->kerning;
/* Support alpha blending */
if (!fg.a) {
fg.a = SDL_ALPHA_OPAQUE;
}
if (fg.a == SDL_ALPHA_OPAQUE) {
for (i = 0; i < SDL_arraysize(alpha_table); ++i) {
alpha_table[i] = (Uint8)i;
}
} else {
for (i = 0; i < SDL_arraysize(alpha_table); ++i) {
alpha_table[i] = (Uint8)(i * fg.a / 255);
}
SDL_SetSurfaceBlendMode(textbuf, SDL_BLENDMODE_BLEND);
}
/* Load and render each character */
textlen = SDL_strlen(text);
xstart = 0;
pixel = (fg.r<<16)|(fg.g<<8)|fg.b;
SDL_FillRect(textbuf, NULL, pixel); /* Initialize with fg and 0 alpha */
while (textlen > 0) {
Uint32 c = UTF8_getch(&text, &textlen);
if (c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED) {
continue;
}
error = Find_Glyph(font, c, CACHED_METRICS|CACHED_PIXMAP);
if (error) {
TTF_SetFTError("Couldn't find glyph", error);
SDL_FreeSurface(textbuf);
return NULL;
}
glyph = font->current;
/* Ensure the width of the pixmap is correct. On some cases,
* freetype may report a larger pixmap than possible.*/
width = glyph->pixmap.width;
if (font->outline <= 0 && width > glyph->maxx - glyph->minx) {
width = glyph->maxx - glyph->minx;
}
/* do kerning, if possible AC-Patch */
- if (use_kerning && prev_index && glyph->index) {
+ if (use_kerning && prev_index && glyph->index && prev_face == glyph->face) {
FT_Vector delta;
- FT_Get_Kerning(font->face, prev_index, glyph->index, ft_kerning_default, &delta);
+ FT_Get_Kerning(glyph->face, prev_index, glyph->index, ft_kerning_default, &delta);
xstart += delta.x >> 6;
}
for (row = 0; row < glyph->pixmap.rows; ++row) {
/* Make sure we don't go either over, or under the limit */
if ((xstart + glyph->minx) < 0) {
xstart -= (xstart + glyph->minx);
}
if ((row + glyph->yoffset) < 0) {
continue;
}
if ((row + glyph->yoffset) >= textbuf->h) {
continue;
}
dst = (Uint32 *)textbuf->pixels +
(row+glyph->yoffset) * textbuf->pitch/4 +
xstart + glyph->minx;
src = (Uint8*)glyph->pixmap.buffer + row * glyph->pixmap.pitch;
for (col = width; col > 0 && dst < dst_check; --col) {
alpha = *src++;
*dst++ |= pixel | ((Uint32)alpha_table[alpha] << 24);
}
}
xstart += glyph->advance;
if (TTF_HANDLE_STYLE_BOLD(font)) {
xstart += font->glyph_overhang;
}
+ prev_face = glyph->face;
prev_index = glyph->index;
}
/* Handle the underline style */
if (TTF_HANDLE_STYLE_UNDERLINE(font)) {
row = TTF_underline_top_row(font);
TTF_drawLine_Blended(font, textbuf, row, pixel | (Uint32)fg.a << 24);
}
/* Handle the strikethrough style */
if (TTF_HANDLE_STYLE_STRIKETHROUGH(font)) {
row = TTF_strikethrough_top_row(font);
TTF_drawLine_Blended(font, textbuf, row, pixel | (Uint32)fg.a << 24);
}
return(textbuf);
}
SDL_Surface *TTF_RenderUNICODE_Blended(TTF_Font *font,
const Uint16 *text, SDL_Color fg)
{
SDL_Surface *surface = NULL;
Uint8 *utf8;
TTF_CHECKPOINTER(text, NULL);
utf8 = SDL_stack_alloc(Uint8, UTF16_to_UTF8_len(text));
if (utf8) {
UTF16_to_UTF8(text, utf8);
surface = TTF_RenderUTF8_Blended(font, (char *)utf8, fg);
SDL_stack_free(utf8);
} else {
SDL_OutOfMemory();
}
return surface;
}
SDL_Surface *TTF_RenderText_Blended_Wrapped(TTF_Font *font, const char *text, SDL_Color fg, Uint32 wrapLength)
{
SDL_Surface *surface = NULL;
Uint8 *utf8;
TTF_CHECKPOINTER(text, NULL);
utf8 = SDL_stack_alloc(Uint8, LATIN1_to_UTF8_len(text));
if (utf8) {
LATIN1_to_UTF8(text, utf8);
surface = TTF_RenderUTF8_Blended_Wrapped(font, (char *)utf8, fg, wrapLength);
SDL_stack_free(utf8);
} else {
SDL_OutOfMemory();
}
return surface;
}
static SDL_bool CharacterIsDelimiter(char c, const char *delimiters)
{
while (*delimiters) {
if (c == *delimiters) {
return SDL_TRUE;
}
++delimiters;
}
return SDL_FALSE;
}
/* Don't define this until we have a release where we can change font rendering
#define TTF_USE_LINESKIP
*/
SDL_Surface *TTF_RenderUTF8_Blended_Wrapped(TTF_Font *font,
const char *text, SDL_Color fg, Uint32 wrapLength)
{
int i;
int xstart;
int width, height;
SDL_Surface *textbuf;
Uint8 alpha;
Uint8 alpha_table[256];
Uint32 pixel;
Uint8 *src;
Uint32 *dst;
Uint32 *dst_check;
int row, col;
c_glyph *glyph;
FT_Error error;
FT_Long use_kerning;
+ FT_Face prev_face = 0;
FT_UInt prev_index = 0;
#ifndef TTF_USE_LINESKIP
const int lineSpace = 2;
#endif
int line, numLines, rowSize;
char *str, **strLines, **newLines;
size_t textlen;
TTF_CHECKPOINTER(text, NULL);
/* Get the dimensions of the text surface */
if ((TTF_SizeUTF8(font, text, &width, &height) < 0) || !width) {
TTF_SetError("Text has zero width");
return(NULL);
}
numLines = 1;
str = NULL;
strLines = NULL;
if (wrapLength > 0 && *text) {
const char *wrapDelims = " \t\r\n";
int w, h;
char *spot, *tok, *next_tok, *end;
char delim;
size_t str_len = SDL_strlen(text);
numLines = 0;
str = SDL_stack_alloc(char, str_len+1);
if (str == NULL) {
TTF_SetError("Out of memory");
return(NULL);
}
SDL_strlcpy(str, text, str_len+1);
tok = str;
end = str + str_len;
do {
newLines = (char **)SDL_realloc(strLines, (numLines+1)*sizeof(*strLines));
if (!newLines) {
TTF_SetError("Out of memory");
SDL_free(strLines);
SDL_stack_free(str);
return(NULL);
}
strLines = newLines;
strLines[numLines++] = tok;
/* Look for the end of the line */
if ((spot = SDL_strchr(tok, '\r')) != NULL ||
(spot = SDL_strchr(tok, '\n')) != NULL) {
if (*spot == '\r') {
++spot;
}
if (*spot == '\n') {
++spot;
}
} else {
spot = end;
}
next_tok = spot;
/* Get the longest string that will fit in the desired space */
for (; ;) {
/* Strip trailing whitespace */
while (spot > tok &&
CharacterIsDelimiter(spot[-1], wrapDelims)) {
--spot;
}
if (spot == tok) {
if (CharacterIsDelimiter(*spot, wrapDelims)) {
*spot = '\0';
}
break;
}
delim = *spot;
*spot = '\0';
TTF_SizeUTF8(font, tok, &w, &h);
if ((Uint32)w <= wrapLength) {
break;
} else {
/* Back up and try again... */
*spot = delim;
}
while (spot > tok &&
!CharacterIsDelimiter(spot[-1], wrapDelims)) {
--spot;
}
if (spot > tok) {
next_tok = spot;
}
}
tok = next_tok;
} while (tok < end);
}
/* Create the target surface */
textbuf = SDL_CreateRGBSurface(SDL_SWSURFACE,
(numLines > 1) ? wrapLength : width,
#ifdef TTF_USE_LINESKIP
numLines * TTF_FontLineSkip(font),
#else
height * numLines + (lineSpace * (numLines - 1)),
#endif
32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
if (textbuf == NULL) {
if (strLines) {
SDL_free(strLines);
SDL_stack_free(str);
}
return(NULL);
}
#ifdef TTF_USE_LINESKIP
rowSize = textbuf->pitch/4 * TTF_FontLineSkip(font);
#else
rowSize = textbuf->pitch/4 * height;
#endif
/* Adding bound checking to avoid all kinds of memory corruption errors
that may occur. */
dst_check = (Uint32*)textbuf->pixels + textbuf->pitch/4 * textbuf->h;
/* check kerning */
use_kerning = FT_HAS_KERNING(font->face) && font->kerning;
/* Support alpha blending */
if (!fg.a) {
fg.a = SDL_ALPHA_OPAQUE;
}
if (fg.a == SDL_ALPHA_OPAQUE) {
for (i = 0; i < SDL_arraysize(alpha_table); ++i) {
alpha_table[i] = (Uint8)i;
}
} else {
for (i = 0; i < SDL_arraysize(alpha_table); ++i) {
alpha_table[i] = (Uint8)(i * fg.a / 255);
}
SDL_SetSurfaceBlendMode(textbuf, SDL_BLENDMODE_BLEND);
}
/* Load and render each character */
pixel = (fg.r<<16)|(fg.g<<8)|fg.b;
SDL_FillRect(textbuf, NULL, pixel); /* Initialize with fg and 0 alpha */
for (line = 0; line < numLines; line++) {
if (strLines) {
text = strLines[line];
}
textlen = SDL_strlen(text);
+ prev_face = 0;
+ prev_index = 0;
xstart = 0;
while (textlen > 0) {
Uint32 c = UTF8_getch(&text, &textlen);
if (c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED) {
continue;
}
error = Find_Glyph(font, c, CACHED_METRICS|CACHED_PIXMAP);
if (error) {
TTF_SetFTError("Couldn't find glyph", error);
SDL_FreeSurface(textbuf);
if (strLines) {
SDL_free(strLines);
SDL_stack_free(str);
}
return NULL;
}
glyph = font->current;
/* Ensure the width of the pixmap is correct. On some cases,
* freetype may report a larger pixmap than possible.*/
width = glyph->pixmap.width;
if (font->outline <= 0 && width > glyph->maxx - glyph->minx) {
width = glyph->maxx - glyph->minx;
}
/* do kerning, if possible AC-Patch */
- if (use_kerning && prev_index && glyph->index) {
+ if (use_kerning && prev_index && glyph->index && prev_face == glyph->face) {
FT_Vector delta;
- FT_Get_Kerning(font->face, prev_index, glyph->index, ft_kerning_default, &delta);
+ FT_Get_Kerning(glyph->face, prev_index, glyph->index, ft_kerning_default, &delta);
xstart += delta.x >> 6;
}
for (row = 0; row < glyph->pixmap.rows; ++row) {
/* Make sure we don't go either over, or under the limit */
if ((xstart + glyph->minx) < 0) {
xstart -= (xstart + glyph->minx);
}
if ((row + glyph->yoffset) < 0) {
continue;
}
if ((row + glyph->yoffset) >= textbuf->h) {
continue;
}
dst = ((Uint32*)textbuf->pixels + rowSize * line) +
(row+glyph->yoffset) * textbuf->pitch/4 +
xstart + glyph->minx;
src = (Uint8*)glyph->pixmap.buffer + row * glyph->pixmap.pitch;
for (col = width; col > 0 && dst < dst_check; --col) {
alpha = *src++;
*dst++ |= pixel | ((Uint32)alpha_table[alpha] << 24);
}
}
xstart += glyph->advance;
if (TTF_HANDLE_STYLE_BOLD(font)) {
xstart += font->glyph_overhang;
}
+ prev_face = glyph->face;
prev_index = glyph->index;
}
/* Handle the underline style *
if (TTF_HANDLE_STYLE_UNDERLINE(font)) {
row = TTF_underline_top_row(font);
TTF_drawLine_Blended(font, textbuf, row, pixel | (Uint32)fg.a << 24);
}
*/
/* Handle the strikethrough style *
if (TTF_HANDLE_STYLE_STRIKETHROUGH(font)) {
row = TTF_strikethrough_top_row(font);
TTF_drawLine_Blended(font, textbuf, row, pixel | (Uint32)fg.a << 24);
}
*/
}
if (strLines) {
SDL_free(strLines);
SDL_stack_free(str);
}
return(textbuf);
}
SDL_Surface *TTF_RenderUNICODE_Blended_Wrapped(TTF_Font *font, const Uint16* text,
SDL_Color fg, Uint32 wrapLength)
{
SDL_Surface *surface = NULL;
Uint8 *utf8;
TTF_CHECKPOINTER(text, NULL);
utf8 = SDL_stack_alloc(Uint8, UTF16_to_UTF8_len(text));
if (utf8) {
UTF16_to_UTF8(text, utf8);
surface = TTF_RenderUTF8_Blended_Wrapped(font, (char *)utf8, fg, wrapLength);
SDL_stack_free(utf8);
} else {
SDL_OutOfMemory();
}
return surface;
}
SDL_Surface *TTF_RenderGlyph_Blended(TTF_Font *font, Uint32 ch, SDL_Color fg)
{
Uint32 utf32[2] = { ch, 0 };
Uint8 utf8[8];
UTF32_to_UTF8(utf32, utf8);
return TTF_RenderUTF8_Blended(font, (char *)utf8, fg);
}
void TTF_SetFontStyle(TTF_Font* font, int style)
{
int prev_style = font->style;
font->style = style | font->face_style;
/* Flush the cache if the style has changed.
* Ignore UNDERLINE which does not impact glyph drawning.
* */
if ((font->style | TTF_STYLE_NO_GLYPH_CHANGE) != (prev_style | TTF_STYLE_NO_GLYPH_CHANGE)) {
Flush_Cache(font);
}
}
int TTF_GetFontStyle(const TTF_Font* font)
{
return font->style;
}
void TTF_SetFontOutline(TTF_Font* font, int outline)
{
font->outline = outline;
Flush_Cache(font);
}
int TTF_GetFontOutline(const TTF_Font* font)
{
return font->outline;
}
void TTF_SetFontHinting(TTF_Font* font, int hinting)
{
if (hinting == TTF_HINTING_LIGHT)
font->hinting = FT_LOAD_TARGET_LIGHT;
else if (hinting == TTF_HINTING_MONO)
font->hinting = FT_LOAD_TARGET_MONO;
else if (hinting == TTF_HINTING_NONE)
font->hinting = FT_LOAD_NO_HINTING;
else
font->hinting = 0;
Flush_Cache(font);
}
int TTF_GetFontHinting(const TTF_Font* font)
{
if (font->hinting == FT_LOAD_TARGET_LIGHT)
return TTF_HINTING_LIGHT;
else if (font->hinting == FT_LOAD_TARGET_MONO)
return TTF_HINTING_MONO;
else if (font->hinting == FT_LOAD_NO_HINTING)
return TTF_HINTING_NONE;
return 0;
}
void TTF_Quit(void)
{
if (TTF_initialized) {
if (--TTF_initialized == 0) {
FT_Done_FreeType(library);
}
}
}
int TTF_WasInit(void)
{
return TTF_initialized;
}
/* don't use this function. It's just here for binary compatibility. */
int TTF_GetFontKerningSize(TTF_Font* font, int prev_index, int index)
{
FT_Vector delta;
FT_Get_Kerning(font->face, prev_index, index, ft_kerning_default, &delta);
return (delta.x >> 6);
}
int TTF_GetFontKerningSizeGlyphs(TTF_Font *font, Uint32 previous_ch, Uint32 ch)
{
int error;
+ FT_Face glyph_face, prev_face;
int glyph_index, prev_index;
FT_Vector delta;
if (ch == UNICODE_BOM_NATIVE || ch == UNICODE_BOM_SWAPPED) {
return 0;
}
if (previous_ch == UNICODE_BOM_NATIVE || previous_ch == UNICODE_BOM_SWAPPED) {
return 0;
}
error = Find_Glyph(font, ch, CACHED_METRICS);
if (error) {
TTF_SetFTError("Couldn't find glyph", error);
return -1;
}
+ glyph_face = font->current->face;
glyph_index = font->current->index;
error = Find_Glyph(font, previous_ch, CACHED_METRICS);
if (error) {
TTF_SetFTError("Couldn't find glyph", error);
return -1;
}
+ prev_face = font->current->face;
prev_index = font->current->index;
- error = FT_Get_Kerning(font->face, prev_index, glyph_index, ft_kerning_default, &delta);
+ if (glyph_face != prev_face) {
+ return 0;
+ }
+
+ error = FT_Get_Kerning(glyph_face, prev_index, glyph_index, ft_kerning_default, &delta);
if (error) {
TTF_SetFTError("Couldn't get glyph kerning", error);
return -1;
}
return (delta.x >> 6);
}
/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/libs/SDL2_ttf/SDL_ttf.h b/src/libs/SDL2_ttf/SDL_ttf.h
index 7815628..3229f7a 100644
--- a/src/libs/SDL2_ttf/SDL_ttf.h
+++ b/src/libs/SDL2_ttf/SDL_ttf.h
@@ -1,294 +1,302 @@
/*
SDL_ttf: A companion library to SDL for working with TrueType (tm) fonts
Copyright (C) 2001-2018 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/* This library is a wrapper around the excellent FreeType 2.0 library,
available at:
http://www.freetype.org/
*/
/* Note: In many places, SDL_ttf will say "glyph" when it means "code point."
Unicode is hard, we learn as we go, and we apologize for adding to the
confusion. */
#ifndef SDL_TTF_H_
#define SDL_TTF_H_
#include "SDL.h"
#include "begin_code.h"
/* Set up for C function definitions, even when using C++ */
#ifdef __cplusplus
extern "C" {
#endif
/* Printable format: "%d.%d.%d", MAJOR, MINOR, PATCHLEVEL
*/
#define SDL_TTF_MAJOR_VERSION 2
#define SDL_TTF_MINOR_VERSION 0
#define SDL_TTF_PATCHLEVEL 14
/* This macro can be used to fill a version structure with the compile-time
* version of the SDL_ttf library.
*/
#define SDL_TTF_VERSION(X) \
{ \
(X)->major = SDL_TTF_MAJOR_VERSION; \
(X)->minor = SDL_TTF_MINOR_VERSION; \
(X)->patch = SDL_TTF_PATCHLEVEL; \
}
/* Backwards compatibility */
#define TTF_MAJOR_VERSION SDL_TTF_MAJOR_VERSION
#define TTF_MINOR_VERSION SDL_TTF_MINOR_VERSION
#define TTF_PATCHLEVEL SDL_TTF_PATCHLEVEL
#define TTF_VERSION(X) SDL_TTF_VERSION(X)
/**
* This is the version number macro for the current SDL_net version.
*/
#define SDL_TTF_COMPILEDVERSION \
SDL_VERSIONNUM(SDL_TTF_MAJOR_VERSION, SDL_TTF_MINOR_VERSION, SDL_TTF_PATCHLEVEL)
/**
* This macro will evaluate to true if compiled with SDL_net at least X.Y.Z.
*/
#define SDL_TTF_VERSION_ATLEAST(X, Y, Z) \
(SDL_TTF_COMPILEDVERSION >= SDL_VERSIONNUM(X, Y, Z))
/* Make sure this is defined (only available in newer SDL versions) */
#ifndef SDL_DEPRECATED
#define SDL_DEPRECATED
#endif
/* This function gets the version of the dynamically linked SDL_ttf library.
it should NOT be used to fill a version structure, instead you should
use the SDL_TTF_VERSION() macro.
*/
extern DECLSPEC const SDL_version * SDLCALL TTF_Linked_Version(void);
/* ZERO WIDTH NO-BREAKSPACE (Unicode byte order mark) */
#define UNICODE_BOM_NATIVE 0xFEFF
#define UNICODE_BOM_SWAPPED 0xFFFE
/* This function tells the library whether UNICODE text is generally
byteswapped. A UNICODE BOM character in a string will override
this setting for the remainder of that string.
*/
extern DECLSPEC void SDLCALL TTF_ByteSwappedUNICODE(int swapped);
/* The internal structure containing font information */
typedef struct _TTF_Font TTF_Font;
/* Initialize the TTF engine - returns 0 if successful, -1 on error */
extern DECLSPEC int SDLCALL TTF_Init(void);
/* Open a font file and create a font of the specified point size.
* Some .fon fonts will have several sizes embedded in the file, so the
* point size becomes the index of choosing which size. If the value
* is too high, the last indexed size will be the default. */
extern DECLSPEC TTF_Font * SDLCALL TTF_OpenFont(const char *file, int ptsize);
extern DECLSPEC TTF_Font * SDLCALL TTF_OpenFontIndex(const char *file, int ptsize, long index);
extern DECLSPEC TTF_Font * SDLCALL TTF_OpenFontRW(SDL_RWops *src, int freesrc, int ptsize);
extern DECLSPEC TTF_Font * SDLCALL TTF_OpenFontIndexRW(SDL_RWops *src, int freesrc, int ptsize, long index);
/* Set and retrieve the font style */
#define TTF_STYLE_NORMAL 0x00
#define TTF_STYLE_BOLD 0x01
#define TTF_STYLE_ITALIC 0x02
#define TTF_STYLE_UNDERLINE 0x04
#define TTF_STYLE_STRIKETHROUGH 0x08
extern DECLSPEC int SDLCALL TTF_GetFontStyle(const TTF_Font *font);
extern DECLSPEC void SDLCALL TTF_SetFontStyle(TTF_Font *font, int style);
extern DECLSPEC int SDLCALL TTF_GetFontOutline(const TTF_Font *font);
extern DECLSPEC void SDLCALL TTF_SetFontOutline(TTF_Font *font, int outline);
/* Set and retrieve FreeType hinter settings */
#define TTF_HINTING_NORMAL 0
#define TTF_HINTING_LIGHT 1
#define TTF_HINTING_MONO 2
#define TTF_HINTING_NONE 3
extern DECLSPEC int SDLCALL TTF_GetFontHinting(const TTF_Font *font);
extern DECLSPEC void SDLCALL TTF_SetFontHinting(TTF_Font *font, int hinting);
/* Get the total height of the font - usually equal to point size */
extern DECLSPEC int SDLCALL TTF_FontHeight(const TTF_Font *font);
/* Get the offset from the baseline to the top of the font
This is a positive value, relative to the baseline.
*/
extern DECLSPEC int SDLCALL TTF_FontAscent(const TTF_Font *font);
/* Get the offset from the baseline to the bottom of the font
This is a negative value, relative to the baseline.
*/
extern DECLSPEC int SDLCALL TTF_FontDescent(const TTF_Font *font);
/* Get the recommended spacing between lines of text for this font */
extern DECLSPEC int SDLCALL TTF_FontLineSkip(const TTF_Font *font);
/* Get/Set whether or not kerning is allowed for this font */
extern DECLSPEC int SDLCALL TTF_GetFontKerning(const TTF_Font *font);
extern DECLSPEC void SDLCALL TTF_SetFontKerning(TTF_Font *font, int allowed);
/* Get the number of faces of the font */
extern DECLSPEC long SDLCALL TTF_FontFaces(const TTF_Font *font);
/* Get the font face attributes, if any */
extern DECLSPEC int SDLCALL TTF_FontFaceIsFixedWidth(const TTF_Font *font);
extern DECLSPEC char * SDLCALL TTF_FontFaceFamilyName(const TTF_Font *font);
extern DECLSPEC char * SDLCALL TTF_FontFaceStyleName(const TTF_Font *font);
/* Check wether a glyph is provided by the font or not */
extern DECLSPEC int SDLCALL TTF_GlyphIsProvided(const TTF_Font *font, Uint32 ch);
/* Get the metrics (dimensions) of a glyph
To understand what these metrics mean, here is a useful link:
http://freetype.sourceforge.net/freetype2/docs/tutorial/step2.html
*/
extern DECLSPEC int SDLCALL TTF_GlyphMetrics(TTF_Font *font, Uint32 ch,
int *minx, int *maxx,
int *miny, int *maxy, int *advance);
/* Get the dimensions of a rendered string of text */
extern DECLSPEC int SDLCALL TTF_SizeText(TTF_Font *font, const char *text, int *w, int *h);
extern DECLSPEC int SDLCALL TTF_SizeUTF8(TTF_Font *font, const char *text, int *w, int *h);
extern DECLSPEC int SDLCALL TTF_SizeUNICODE(TTF_Font *font, const Uint16 *text, int *w, int *h);
/* Create an 8-bit palettized surface and render the given text at
fast quality with the given font and color. The 0 pixel is the
colorkey, giving a transparent background, and the 1 pixel is set
to the text color.
This function returns the new surface, or NULL if there was an error.
*/
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Solid(TTF_Font *font,
const char *text, SDL_Color fg);
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUTF8_Solid(TTF_Font *font,
const char *text, SDL_Color fg);
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUNICODE_Solid(TTF_Font *font,
const Uint16 *text, SDL_Color fg);
/* Create an 8-bit palettized surface and render the given glyph at
fast quality with the given font and color. The 0 pixel is the
colorkey, giving a transparent background, and the 1 pixel is set
to the text color. The glyph is rendered without any padding or
centering in the X direction, and aligned normally in the Y direction.
This function returns the new surface, or NULL if there was an error.
*/
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderGlyph_Solid(TTF_Font *font,
Uint32 ch, SDL_Color fg);
/* Create an 8-bit palettized surface and render the given text at
high quality with the given font and colors. The 0 pixel is background,
while other pixels have varying degrees of the foreground color.
This function returns the new surface, or NULL if there was an error.
*/
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Shaded(TTF_Font *font,
const char *text, SDL_Color fg, SDL_Color bg);
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUTF8_Shaded(TTF_Font *font,
const char *text, SDL_Color fg, SDL_Color bg);
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUNICODE_Shaded(TTF_Font *font,
const Uint16 *text, SDL_Color fg, SDL_Color bg);
/* Create an 8-bit palettized surface and render the given glyph at
high quality with the given font and colors. The 0 pixel is background,
while other pixels have varying degrees of the foreground color.
The glyph is rendered without any padding or centering in the X
direction, and aligned normally in the Y direction.
This function returns the new surface, or NULL if there was an error.
*/
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderGlyph_Shaded(TTF_Font *font,
Uint32 ch, SDL_Color fg, SDL_Color bg);
/* Create a 32-bit ARGB surface and render the given text at high quality,
using alpha blending to dither the font with the given color.
This function returns the new surface, or NULL if there was an error.
*/
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Blended(TTF_Font *font,
const char *text, SDL_Color fg);
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUTF8_Blended(TTF_Font *font,
const char *text, SDL_Color fg);
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUNICODE_Blended(TTF_Font *font,
const Uint16 *text, SDL_Color fg);
/* Create a 32-bit ARGB surface and render the given text at high quality,
using alpha blending to dither the font with the given color.
Text is wrapped to multiple lines on line endings and on word boundaries
if it extends beyond wrapLength in pixels.
This function returns the new surface, or NULL if there was an error.
*/
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Blended_Wrapped(TTF_Font *font,
const char *text, SDL_Color fg, Uint32 wrapLength);
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUTF8_Blended_Wrapped(TTF_Font *font,
const char *text, SDL_Color fg, Uint32 wrapLength);
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUNICODE_Blended_Wrapped(TTF_Font *font,
const Uint16 *text, SDL_Color fg, Uint32 wrapLength);
/* Create a 32-bit ARGB surface and render the given glyph at high quality,
using alpha blending to dither the font with the given color.
The glyph is rendered without any padding or centering in the X
direction, and aligned normally in the Y direction.
This function returns the new surface, or NULL if there was an error.
*/
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderGlyph_Blended(TTF_Font *font,
Uint32 ch, SDL_Color fg);
/* For compatibility with previous versions, here are the old functions */
#define TTF_RenderText(font, text, fg, bg) \
TTF_RenderText_Shaded(font, text, fg, bg)
#define TTF_RenderUTF8(font, text, fg, bg) \
TTF_RenderUTF8_Shaded(font, text, fg, bg)
#define TTF_RenderUNICODE(font, text, fg, bg) \
TTF_RenderUNICODE_Shaded(font, text, fg, bg)
/* Close an opened font file */
extern DECLSPEC void SDLCALL TTF_CloseFont(TTF_Font *font);
/* De-initialize the TTF engine */
extern DECLSPEC void SDLCALL TTF_Quit(void);
/* Check if the TTF engine is initialized */
extern DECLSPEC int SDLCALL TTF_WasInit(void);
/* Get the kerning size of two glyphs indices */
/* DEPRECATED: this function requires FreeType font indexes, not glyphs,
by accident, which we don't expose through this API, so it could give
wildly incorrect results, especially with non-ASCII values.
Going forward, please use TTF_GetFontKerningSizeGlyphs() instead, which
does what you probably expected this function to do. */
extern DECLSPEC int TTF_GetFontKerningSize(TTF_Font *font, int prev_index, int index) SDL_DEPRECATED;
/* Get the kerning size of two glyphs */
extern DECLSPEC int TTF_GetFontKerningSizeGlyphs(TTF_Font *font, Uint32 previous_ch, Uint32 ch);
/* We'll use SDL for reporting errors */
#define TTF_SetError SDL_SetError
#define TTF_GetError SDL_GetError
+/* Set the font fallback.
+ NOTE: The fallback fonts are still owned by caller, i.e. will not be
+ freed automatically. */
+extern DECLSPEC int TTF_SetFontFallback(TTF_Font *font, int number_of_fallbacks, TTF_Font **fallbacks);
+
+#define TTF_ClearFontFallback(font) \
+ TTF_SetFontFallback(font, 0, 0)
+
/* Ends C function definitions when using C++ */
#ifdef __cplusplus
}
#endif
#include "close_code.h"
#endif /* SDL_TTF_H_ */
/* vi: set ts=4 sw=4 expandtab: */
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, May 16, 8:24 PM (1 d, 18 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
63526
Default Alt Text
(154 KB)
Attached To
Mode
R79 meandmyshadow
Attached
Detach File
Event Timeline