1 // -*- Mode: C++; tab-width:2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 // vi:tw=80:et:ts=2:sts=2
3 //
4 // -----------------------------------------------------------------------
5 //
6 // This file is part of RLVM, a RealLive virtual machine clone.
7 //
8 // -----------------------------------------------------------------------
9 //
10 // Copyright (C) 2006, 2007 Elliot Glaysher
11 //
12 // This program is free software; you can redistribute it and/or modify
13 // it under the terms of the GNU General Public License as published by
14 // the Free Software Foundation; either version 3 of the License, or
15 // (at your option) any later version.
16 //
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 // GNU General Public License for more details.
21 //
22 // You should have received a copy of the GNU General Public License
23 // along with this program; if not, write to the Free Software
24 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
25 //
26 // -----------------------------------------------------------------------
27
28 #include "systems/sdl/sdl_text_system.h"
29
30 #include <SDL/SDL_ttf.h>
31
32 #include <iostream>
33 #include <sstream>
34 #include <stdexcept>
35 #include <string>
36 #include <vector>
37
38 #include "systems/base/graphics_system.h"
39 #include "systems/base/rect.h"
40 #include "systems/base/system_error.h"
41 #include "systems/base/text_key_cursor.h"
42 #include "systems/sdl/sdl_surface.h"
43 #include "systems/sdl/sdl_system.h"
44 #include "systems/sdl/sdl_text_window.h"
45 #include "systems/sdl/sdl_utils.h"
46 #include "utilities/exception.h"
47 #include "utilities/find_font_file.h"
48 #include "libreallive/gameexe.h"
49
SDLTextSystem(SDLSystem & system,Gameexe & gameexe)50 SDLTextSystem::SDLTextSystem(SDLSystem& system, Gameexe& gameexe)
51 : TextSystem(system, gameexe), sdl_system_(system) {
52 if (TTF_Init() == -1) {
53 std::ostringstream oss;
54 oss << "Error initializing SDL_ttf: " << TTF_GetError();
55 throw SystemError(oss.str());
56 }
57 }
58
~SDLTextSystem()59 SDLTextSystem::~SDLTextSystem() {
60 // We should be calling TTF_Quit() here, but somebody is holding on to a font
61 // reference so we'll just leak the FreeType structures.
62 }
63
GetTextWindow(int text_window)64 std::shared_ptr<TextWindow> SDLTextSystem::GetTextWindow(int text_window) {
65 WindowMap::iterator it = text_window_.find(text_window);
66 if (it == text_window_.end()) {
67 it = text_window_.emplace(text_window,
68 std::shared_ptr<TextWindow>(new SDLTextWindow(
69 sdl_system_, text_window))).first;
70 }
71
72 return it->second;
73 }
74
RenderGlyphOnto(const std::string & current,int font_size,bool italic,const RGBColour & font_colour,const RGBColour * shadow_colour,int insertion_point_x,int insertion_point_y,const std::shared_ptr<Surface> & destination)75 Size SDLTextSystem::RenderGlyphOnto(
76 const std::string& current,
77 int font_size,
78 bool italic,
79 const RGBColour& font_colour,
80 const RGBColour* shadow_colour,
81 int insertion_point_x,
82 int insertion_point_y,
83 const std::shared_ptr<Surface>& destination) {
84 SDLSurface* sdl_surface = static_cast<SDLSurface*>(destination.get());
85
86 std::shared_ptr<TTF_Font> font = GetFontOfSize(font_size);
87
88 if (italic) {
89 TTF_SetFontStyle(font.get(), TTF_STYLE_ITALIC);
90 }
91
92 SDL_Color sdl_colour;
93 RGBColourToSDLColor(font_colour, &sdl_colour);
94 std::shared_ptr<SDL_Surface> character(
95 TTF_RenderUTF8_Blended(font.get(), current.c_str(), sdl_colour),
96 SDL_FreeSurface);
97
98 if (character == NULL) {
99 // Bug during Kyou's path. The string is printed "". Regression in parser?
100 std::cerr << "WARNING. TTF_RenderUTF8_Blended didn't render the "
101 << "character \"" << current << "\". Hopefully continuing..."
102 << std::endl;
103 return Size(0, 0);
104 }
105
106 std::shared_ptr<SDL_Surface> shadow;
107 if (shadow_colour && sdl_system_.text().font_shadow()) {
108 SDL_Color sdl_shadow_colour;
109 RGBColourToSDLColor(*shadow_colour, &sdl_shadow_colour);
110
111 shadow.reset(
112 TTF_RenderUTF8_Blended(font.get(), current.c_str(), sdl_shadow_colour),
113 SDL_FreeSurface);
114 }
115
116 if (italic) {
117 TTF_SetFontStyle(font.get(), TTF_STYLE_NORMAL);
118 }
119
120 Point insertion(insertion_point_x, insertion_point_y);
121
122 if (shadow) {
123 Size offset(shadow->w, shadow->h);
124 sdl_surface->blitFROMSurface(shadow.get(),
125 Rect(Point(0, 0), offset),
126 Rect(insertion + Point(2, 2), offset),
127 255);
128 }
129
130 Size size(character->w, character->h);
131 sdl_surface->blitFROMSurface(
132 character.get(), Rect(Point(0, 0), size), Rect(insertion, size), 255);
133 return size;
134 }
135
GetCharWidth(int size,uint16_t codepoint)136 int SDLTextSystem::GetCharWidth(int size, uint16_t codepoint) {
137 std::shared_ptr<TTF_Font> font = GetFontOfSize(size);
138 int minx, maxx, miny, maxy, advance;
139 TTF_GlyphMetrics(font.get(), codepoint, &minx, &maxx, &miny, &maxy, &advance);
140 return advance;
141 }
142
GetFontOfSize(int size)143 std::shared_ptr<TTF_Font> SDLTextSystem::GetFontOfSize(int size) {
144 FontSizeMap::iterator it = map_.find(size);
145 if (it == map_.end()) {
146 std::string filename = FindFontFile(system()).native();
147 TTF_Font* f = TTF_OpenFont(filename.c_str(), size);
148 if (f == NULL) {
149 std::ostringstream oss;
150 oss << "Error loading font: " << TTF_GetError();
151 throw SystemError(oss.str());
152 }
153
154 TTF_SetFontStyle(f, TTF_STYLE_NORMAL);
155
156 // Build a smart_ptr to own this font, and set a deleter function.
157 std::shared_ptr<TTF_Font> font(f, TTF_CloseFont);
158
159 map_[size] = font;
160
161 // Now that we've put this font into the cache, we can safely use
162 // GetCharWidth to ensure whether we're monospaced.
163 if (!is_monospace_.get()) {
164 is_monospace_.reset(new bool);
165 // Why not use TTF_FontFaceIsFixedWidth()? Because that checks if the
166 // font is fixed width, which msgothic.ttc among others is not. However,
167 // it does use monospaced characters.
168 *is_monospace_ = GetCharWidth(size, 'i') == GetCharWidth(size, 'm');
169 }
170 return font;
171 } else {
172 return it->second;
173 }
174 }
175
FontIsMonospaced()176 bool SDLTextSystem::FontIsMonospaced() {
177 return is_monospace_ ? *is_monospace_ : false;
178 }
179