pick up CXXFLAGS passed on commandline
[rofl0r-df-libgraphics.git] / g_src / ttf_manager.cpp
blob091f281a9063d20ed1e32b0206d87d3bcbf5e5d0
1 #include "ttf_manager.hpp"
2 #include "init.h"
3 #include <iostream>
4 #include <cmath>
6 using namespace std;
8 ttf_managerst ttf_manager;
10 bool ttf_managerst::init(int ceiling, int tile_width) {
11 // Reset to a known state, clear everything
12 if ((!TTF_WasInit()) && (TTF_Init() == -1)) {
13 MessageBox(NULL, TTF_GetError(), "TTF error", MB_OK);
14 return false;
16 if (font) TTF_CloseFont(font);
17 handles.clear();
18 for (auto it = textures.cbegin(); it != textures.cend(); ++it)
19 SDL_FreeSurface(it->second);
20 textures.clear();
21 this->tile_width = tile_width;
22 this->ceiling = ceiling;
23 // Try progressively smaller point sizes until we find one that fits
24 for (int sz=20; sz > 0; sz--) {
25 font = TTF_OpenFont("data/art/font.ttf", sz);
26 if (!font) continue;
27 if (TTF_FontHeight(font) <= ceiling) {
28 #ifdef DEBUG
29 cout << "Picked font at " << sz << " points for ceiling " << ceiling << endl;
30 // get the glyph metric for the letter 'M' in a loaded font
31 cout << "TTF_FontHeight " << TTF_FontHeight(font) << endl;
32 cout << "TTF_FontAscent " << TTF_FontAscent(font) << endl;
33 cout << "TTF_FontDescent " << TTF_FontDescent(font) << endl;
34 cout << "TTF_FontLineSkip " << TTF_FontLineSkip(font) << endl;
35 #endif
36 int minx,maxx,miny,maxy,advance;
37 if (TTF_GlyphMetrics(font, 'M', &minx, &maxx, &miny, &maxy, &advance) == -1)
38 puts(TTF_GetError());
39 else {
40 em_width = maxx;
41 #ifdef DEBUG
42 printf("minx : %d\n",minx);
43 printf("maxx : %d\n",maxx);
44 printf("miny : %d\n",miny);
45 printf("maxy : %d\n",maxy);
46 printf("advance : %d\n",advance);
47 #endif
49 return true;
51 TTF_CloseFont(font);
53 // ..fine.
54 cout << "No font found!" << endl;
55 font = NULL;
56 return false;
59 static void cp437_to_unicode(const string &text, vector<Uint16> &unicode) {
60 unicode.resize(text.length() + 1);
61 int i;
62 for (i=0; i < text.size(); i++) {
63 const int cp437 = (unsigned char)text[i];
64 unicode[i] = charmap[cp437];
66 unicode[i] = 0;
70 int ttf_managerst::size_text(const string &text) {
71 vector<Uint16> text_unicode;
72 cp437_to_unicode(text, text_unicode);
73 int width, height;
74 TTF_SizeUNICODE(font, &text_unicode[0], &width, &height);
75 return (width + tile_width - 1) / tile_width;
79 ttf_details ttf_managerst::get_handle(const list<ttf_id> &text, justification just) {
80 // Check for an existing handle
81 handleid id = {text, just};
82 auto it = handles.find(id);
83 if (it != handles.end()) return it->second;
84 // Right. Make a new one.
85 int handle = ++max_handle;
86 // Split out any tabs
87 list<ttf_id> split_text;
88 for (auto it = text.cbegin(); it != text.cend(); ++it) {
89 int pos = 0;
90 int tabpos;
91 while ((tabpos = it->text.find("\t", pos)) != string::npos) {
92 ttf_id left;
93 left.fg = it->fg; left.bg = it->bg; left.bold = it->bold;
94 left.text = it->text.substr(pos, tabpos - pos);
95 split_text.push_back(left);
96 ttf_id tabber;
97 tabber.fg = tabber.bg = tabber.bold = 255;
98 split_text.push_back(tabber);
99 pos = tabpos + 1;
101 ttf_id right;
102 right.fg = it->fg; right.bg = it->bg; right.bold = it->bold;
103 right.text = it->text.substr(pos);
104 split_text.push_back(right);
106 // Find the total width of the text
107 vector<Uint16> text_unicode;
108 int ttf_width = 0, ttf_height = 0, text_width = 0;
109 for (auto it = split_text.cbegin(); it != split_text.cend(); ++it) {
110 if (it->fg == 255 && it->bg == 255 && it->bold == 255) {
111 // Tab stop
112 int tabstop = tab_width * em_width;
113 int tab_width = tabstop - ((ttf_width - 1) % tabstop) + 1;
114 ttf_width += tab_width;
115 text_width += 1;
116 } else {
117 cp437_to_unicode(it->text, text_unicode);
118 int slice_width, slice_height;
119 TTF_SizeUNICODE(font, &text_unicode[0], &slice_width, &slice_height);
120 ttf_width += slice_width;
121 text_width += it->text.size();
124 ttf_height = ceiling;
125 // Compute geometry
126 double grid_width = double(ttf_width) / tile_width;
127 double offset = just == justify_right ? text_width - grid_width :
128 just == justify_center ? (text_width - grid_width) / 2 :
130 if (just == justify_center && text_width % 2)
131 offset += 0.5; // Arbitrary fixup for approximate grid centering
132 double fraction, integral;
133 fraction = modf(offset, &integral);
134 // Outputs:
135 const int grid_offset = int(integral + 0.001); // Tiles to move to the right in addst
136 const int pixel_offset = int(fraction * tile_width); // Black columns to add to the left of the image
137 // const int full_grid_width = int(ceil(double(ttf_width) / double(tile_width) + fraction) + 0.1); // Total width of the image in grid units
138 const int full_grid_width = text_width;
139 const int pixel_width = full_grid_width * tile_width; // And pixels
140 assert(pixel_width >= ttf_width);
141 // Store for later
142 ttf_details ret; ret.handle = handle; ret.offset = grid_offset; ret.width = full_grid_width;
143 handles[id] = ret;
144 // We do the actual rendering in the render thread, later on.
145 todo.push_back(todum(handle, split_text, ttf_height, pixel_offset, pixel_width));
146 return ret;
149 SDL_Surface *ttf_managerst::get_texture(int handle) {
150 // Run any outstanding renders
151 if (!todo.empty()) {
152 vector<Uint16> text_unicode;
153 for (auto it = todo.cbegin(); it != todo.cend(); ++it) {
154 const int height = it->height;
155 SDL_Surface *textimg = SDL_CreateRGBSurface(SDL_SWSURFACE, it->pixel_width, height, 32, 0, 0, 0, 0);
156 // #ifdef DEBUG
157 // SDL_FillRect(textimg, NULL, SDL_MapRGBA(textimg->format, 255, 0, 0, 255));
158 // #endif
159 // Render each of the text segments
160 int idx = 0;
161 int xpos = it->pixel_offset;
162 for (auto seg = it->text.cbegin(); seg != it->text.cend();) {
163 const ttf_id &text = *seg;
164 ++seg;
165 ++idx;
166 if (text.fg == 255 && text.bg == 255 && text.bold == 255) {
167 // Skip to tab stop
168 int tabstop = tab_width * em_width;
169 int tab_width = tabstop - ((xpos - 1) % tabstop) + 1;
170 xpos += tab_width;
171 continue;
173 if (text.text.size() <= 0)
174 continue;
175 cp437_to_unicode(text.text, text_unicode);
176 const int fg = (text.fg + text.bold * 8) % 16;
177 SDL_Color fgc = {Uint8(enabler.ccolor[fg][0]*255),
178 Uint8(enabler.ccolor[fg][1]*255),
179 Uint8(enabler.ccolor[fg][2]*255)};
180 const int bg = text.bg % 16;
181 Uint32 bgc = SDL_MapRGB(textimg->format,
182 Uint8(enabler.ccolor[bg][0]*255),
183 Uint8(enabler.ccolor[bg][1]*255),
184 Uint8(enabler.ccolor[bg][2]*255));
185 #ifdef DEBUG
186 // SDL_Color white = {255,255,255};
187 // Uint32 red = SDL_MapRGB(textimg->format, 255,0,0);
188 // fgc = white;
189 // bgc = red;
190 #endif
191 if (idx == 0) {
192 // Fill in the left side
193 SDL_Rect left = {0, 0, Sint16(xpos), Sint16(height)};
194 SDL_FillRect(textimg, &left, bgc);
195 } else if (seg == it->text.cend()) {
196 // Fill in the right side
197 SDL_Rect right = {Sint16(xpos), 0, Sint16(it->pixel_width), Sint16(height)};
198 SDL_FillRect(textimg, &right, bgc);
200 // Render the TTF segment
201 SDL_Surface *textimg_seg = TTF_RenderUNICODE_Blended(font, &text_unicode[0], fgc);
202 // Fill the background color of this part of the textimg
203 SDL_Rect dest = {Sint16(xpos), 0, Sint16(textimg_seg->w), Sint16(height)};
204 SDL_FillRect(textimg, &dest,
205 SDL_MapRGB(textimg->format,
206 // Uint8(255),
207 // Uint8(255),
208 // Uint8(255)));
209 Uint8(enabler.ccolor[bg][0]*255),
210 Uint8(enabler.ccolor[bg][1]*255),
211 Uint8(enabler.ccolor[bg][2]*255)));
212 // And copy the TTF segment over.
213 SDL_Rect dest2 = {Sint16(xpos), 0, 0, 0};
214 SDL_BlitSurface(textimg_seg, NULL, textimg, &dest2);
215 // Ready for next segment.
216 xpos += textimg_seg->w;
217 SDL_FreeSurface(textimg_seg);
219 // ..and make the whole thing display format. Phew!
220 SDL_Surface *textimg_2 = SDL_DisplayFormat(textimg);
221 #ifdef DEBUG
222 // cout << "Rendering \"" << text.text << "\" at height " << box2->h << endl;
223 // cout << " width " << textimg->w << " in box of " << box->w << endl;
224 #endif
225 SDL_FreeSurface(textimg);
226 // Store it for later.
227 textures[it->handle] = textimg_2;
229 todo.clear();
231 // Find the li'l texture
232 SDL_Surface *tex = textures[handle];
233 if (!tex) {
234 cout << "Missing/broken TTF handle: " << handle << endl;
236 return tex;
239 void ttf_managerst::gc() {
240 // Just delete everything, for now.
241 for (auto it = textures.begin(); it != textures.end(); ++it)
242 SDL_FreeSurface(it->second);
243 textures.clear();
244 handles.clear();
245 todo.clear();