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