1 /*
2 * Copyright 2011-2013 Arx Libertatis Team (see the AUTHORS file)
3 *
4 * This file is part of Arx Libertatis.
5 *
6 * Arx Libertatis is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Arx Libertatis is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with Arx Libertatis. If not, see <http://www.gnu.org/licenses/>.
18 */
19 /* Based on:
20 ===========================================================================
21 ARX FATALIS GPL Source Code
22 Copyright (C) 1999-2010 Arkane Studios SA, a ZeniMax Media company.
23
24 This file is part of the Arx Fatalis GPL Source Code ('Arx Fatalis Source Code').
25
26 Arx Fatalis Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
27 License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
28
29 Arx Fatalis Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
30 warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
31
32 You should have received a copy of the GNU General Public License along with Arx Fatalis Source Code. If not, see
33 <http://www.gnu.org/licenses/>.
34
35 In addition, the Arx Fatalis Source Code is also subject to certain additional terms. You should have received a copy of these
36 additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Arx
37 Fatalis Source Code. If not, please request a copy in writing from Arkane Studios at the address below.
38
39 If you have questions concerning this license or the applicable additional terms, you may contact in writing Arkane Studios, c/o
40 ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
41 ===========================================================================
42 */
43 // Copyright (c) 1999-2000 ARKANE Studios SA. All rights reserved
44
45 #include "gui/Text.h"
46
47 #include <sstream>
48
49 #include "core/Localisation.h"
50 #include "core/Config.h"
51 #include "core/Core.h"
52
53 #include "gui/Interface.h"
54 #include "gui/TextManager.h"
55
56 #include "graphics/Renderer.h"
57 #include "graphics/Math.h"
58 #include "graphics/font/FontCache.h"
59
60 #include "io/resource/PakReader.h"
61 #include "io/resource/ResourcePath.h"
62 #include "io/log/Logger.h"
63
64 using std::string;
65
66 TextManager * pTextManage = NULL;
67 TextManager * pTextManageFlyingOver = NULL;
68
69 Font * hFontInBook = NULL;
70 Font * hFontMainMenu = NULL;
71 Font * hFontMenu = NULL;
72 Font * hFontControls = NULL;
73 Font * hFontCredits = NULL;
74 Font * hFontInGame = NULL;
75 Font * hFontInGameNote = NULL;
76
ARX_UNICODE_FormattingInRect(Font * font,const std::string & text,const Rect & rect,Color col,long * textHeight=0,long * numChars=0,bool computeOnly=false)77 void ARX_UNICODE_FormattingInRect(Font * font, const std::string & text,
78 const Rect & rect, Color col, long * textHeight = 0,
79 long * numChars = 0, bool computeOnly = false) {
80
81 std::string::const_iterator itLastLineBreak = text.begin();
82 std::string::const_iterator itLastWordBreak = text.begin();
83 std::string::const_iterator it = text.begin();
84
85 int maxLineWidth;
86 if(rect.right == Rect::Limits::max()) {
87 maxLineWidth = std::numeric_limits<int>::max();
88 } else {
89 maxLineWidth = rect.width();
90 }
91 arx_assert(maxLineWidth > 0);
92 int penY = rect.top;
93
94 if(textHeight) {
95 *textHeight = 0;
96 }
97
98 if(numChars) {
99 *numChars = 0;
100 }
101
102 // Ensure we can at least draw one line...
103 if(penY + font->getLineHeight() > rect.bottom) {
104 return;
105 }
106
107 for(it = text.begin(); it != text.end(); ++it) {
108
109 // Line break ?
110 bool isLineBreak = false;
111 if(*it == '\n' || *it == '*') {
112 isLineBreak = true;
113 } else {
114
115 // Word break ?
116 if(*it == ' ' || *it == '\t') {
117 itLastWordBreak = it;
118 }
119
120 // Check length of string up to this point
121 Vec2i size = font->getTextSize(itLastLineBreak, it + 1);
122 if(size.x > maxLineWidth) { // Too long ?
123 isLineBreak = true;
124 if(itLastWordBreak > itLastLineBreak) {
125 // Draw a line from the last line break up to the last word break
126 it = itLastWordBreak;
127 } else if(it == itLastLineBreak) {
128 // Not enough space to render even one character!
129 break;
130 } else {
131 // The current word is too long to fit on a line, force a line break
132 }
133 }
134 }
135
136 // If we have to draw a line
137 // OR
138 // This is the last character of the string
139 if(isLineBreak || it + 1 == text.end()) {
140
141 std::string::const_iterator itTextStart = itLastLineBreak;
142 std::string::const_iterator itTextEnd;
143
144 itTextEnd = (isLineBreak) ? it : it + 1;
145
146 // Draw the line
147 if(!computeOnly) {
148 font->draw(rect.left, penY, itTextStart, itTextEnd, col);
149 }
150
151 if(it != text.end()) {
152 itLastLineBreak = it + 1;
153 }
154
155 penY += font->getLineHeight();
156
157 // Validate that the new line will fit inside the rect...
158 if(penY + font->getLineHeight() > rect.bottom) {
159 break;
160 }
161 }
162 }
163
164 // Return text height
165 if(textHeight) {
166 *textHeight = penY - rect.top;
167 }
168
169 // Return num characters displayed
170 if(numChars) {
171 *numChars = it - text.begin();
172 }
173 }
174
ARX_UNICODE_ForceFormattingInRect(Font * font,const string & text,const Rect & rect)175 long ARX_UNICODE_ForceFormattingInRect(Font * font, const string & text,
176 const Rect & rect) {
177 long numChars;
178 ARX_UNICODE_FormattingInRect(font, text, rect, Color::none, 0, &numChars, true);
179 return numChars;
180 }
181
182 //-----------------------------------------------------------------------------
ARX_UNICODE_DrawTextInRect(Font * font,float x,float y,float maxx,const std::string & _text,Color col,const Rect * pClipRect)183 long ARX_UNICODE_DrawTextInRect(Font* font,
184 float x, float y,
185 float maxx,
186 const std::string& _text,
187 Color col,
188 const Rect * pClipRect
189 ) {
190
191 Rect previousViewport;
192 if(pClipRect) {
193 previousViewport = GRenderer->GetViewport();
194 GRenderer->SetViewport(*pClipRect);
195 }
196
197 Rect rect((Rect::Num)x, (Rect::Num)y, (Rect::Num)maxx, Rect::Limits::max());
198 if(maxx == std::numeric_limits<float>::infinity()) {
199 rect.right = Rect::Limits::max();
200 }
201
202 long height;
203 ARX_UNICODE_FormattingInRect(font, _text, rect, col, &height);
204
205 if(pClipRect) {
206 GRenderer->SetViewport(previousViewport);
207 }
208
209 return height;
210 }
211
ARX_TEXT_Draw(Font * ef,float x,float y,const std::string & car,Color col)212 void ARX_TEXT_Draw(Font* ef,
213 float x, float y,
214 const std::string& car,
215 Color col) {
216
217 if (car.empty() || car[0] == 0)
218 return;
219
220 ARX_UNICODE_DrawTextInRect(ef, x, y, std::numeric_limits<float>::infinity(), car, col);
221 }
222
ARX_TEXT_DrawRect(Font * ef,float x,float y,float maxx,const string & car,Color col,const Rect * pClipRect)223 long ARX_TEXT_DrawRect(Font* ef,
224 float x, float y,
225 float maxx,
226 const string & car,
227 Color col,
228 const Rect * pClipRect) {
229 return ARX_UNICODE_DrawTextInRect(ef, x, y, maxx, car, col, pClipRect);
230 }
231
DrawBookTextInRect(Font * font,float x,float y,float maxx,const std::string & text,Color col)232 float DrawBookTextInRect(Font* font, float x, float y, float maxx, const std::string& text, Color col) {
233 return (float)ARX_TEXT_DrawRect(font, (BOOKDECX + x) * Xratio, (BOOKDECY + y) * Yratio, (BOOKDECX + maxx) * Xratio, text, col);
234 }
235
236 //-----------------------------------------------------------------------------
DrawBookTextCenter(Font * font,float x,float y,const std::string & text,Color col)237 void DrawBookTextCenter( Font* font, float x, float y, const std::string& text, Color col )
238 {
239 UNICODE_ARXDrawTextCenter(font, (BOOKDECX + x)*Xratio, (BOOKDECY + y)*Yratio, text, col);
240 }
241
242 //-----------------------------------------------------------------------------
243
UNICODE_ARXDrawTextCenter(Font * font,float x,float y,const std::string & str,Color col)244 long UNICODE_ARXDrawTextCenter( Font* font, float x, float y, const std::string& str, Color col )
245 {
246 Vec2i size = font->getTextSize(str);
247 int drawX = ((int)x) - (size.x / 2);
248 int drawY = (int)y;
249
250 font->draw(drawX, drawY, str, col);
251
252 return size.x;
253 }
254
255
256
UNICODE_ARXDrawTextCenteredScroll(Font * font,float x,float y,float x2,const std::string & str,Color col,int iTimeScroll,float fSpeed,int iNbLigne,int iTimeOut)257 long UNICODE_ARXDrawTextCenteredScroll( Font* font, float x, float y, float x2, const std::string& str, Color col, int iTimeScroll, float fSpeed, int iNbLigne, int iTimeOut) {
258
259 Rect::Num _x = checked_range_cast<Rect::Num>(x - x2);
260 Rect::Num _y = checked_range_cast<Rect::Num>(y);
261 Rect::Num w = checked_range_cast<Rect::Num>(x + x2);
262
263 Rect rRect(_x, _y, w, Rect::Limits::max());
264
265 if (pTextManage)
266 {
267 pTextManage->AddText(font,
268 str,
269 rRect,
270 col,
271 iTimeOut,
272 iTimeScroll,
273 fSpeed,
274 iNbLigne
275 );
276
277 return static_cast<long>(x2);
278 }
279
280 return 0;
281 }
282
createFont(const res::path & fontFace,const string & fontProfileName,unsigned int fontSize,float scaleFactor)283 static Font * createFont(const res::path & fontFace,
284 const string & fontProfileName, unsigned int fontSize,
285 float scaleFactor) {
286
287 std::stringstream ss;
288
289 std::string szFontSize;
290 ss << fontSize;
291 ss >> szFontSize;
292 ss.clear();
293
294 std::string szUT;
295 szUT = getLocalised(fontProfileName, szFontSize);
296 ss << szUT;
297 ss >> fontSize;
298 ss.clear();
299
300 fontSize *= scaleFactor;
301
302 Font * newFont = FontCache::getFont(fontFace, fontSize);
303 if(!newFont) {
304 LogError << "Error loading font: " << fontFace << " of size " << fontSize;
305 }
306
307 return newFont;
308 }
309
310 static float created_font_scale = 0.f;
311
getFontFile(res::path & result)312 static bool getFontFile(res::path & result) {
313
314 if(!config.language.empty()) {
315 result = "misc/arx_" + config.language + ".ttf";
316 if(resources->hasFile(result)) {
317 return true;
318 }
319 }
320
321 result = "misc/arx_default.ttf";
322 if(resources->hasFile(result)) {
323 return true;
324 }
325
326 result = "misc/arx.ttf";
327 if(resources->hasFile(result)) {
328 return true;
329 }
330
331 if(!config.language.empty()) {
332 LogCritical << "Missing font file: need either misc/arx_" << config.language
333 << ".ttf, misc/arx_default.ttf or misc/arx.ttf.";
334 } else {
335 LogCritical << "Missing font file: need either misc/arx_default.ttf or misc/arx.ttf.";
336 }
337 return false;
338 }
339
ARX_Text_Init()340 bool ARX_Text_Init() {
341
342 res::path file;
343 if(!getFontFile(file)) {
344 return false;
345 }
346
347 float scale = std::max(std::min(Yratio, Xratio), .001f);
348 if(scale == created_font_scale) {
349 return true;
350 }
351 created_font_scale = scale;
352
353 // Keep small font small when increasing resolution
354 // TODO font size jumps around scale = 1
355 float small_scale = scale > 1.0f ? scale * 0.8f : scale;
356
357 delete pTextManage;
358 pTextManage = new TextManager();
359 delete pTextManageFlyingOver;
360 pTextManageFlyingOver = new TextManager();
361
362 FontCache::initialize();
363
364 Font * nFontMainMenu = createFont(file, "system_font_mainmenu_size", 58, scale);
365 Font * nFontMenu = createFont(file, "system_font_menu_size", 32, scale);
366 Font * nFontControls = createFont(file, "system_font_menucontrols_size", 22, scale);
367 Font * nFontCredits = createFont(file, "system_font_menucredits_size", 36, scale);
368 Font * nFontInGame = createFont(file, "system_font_book_size", 18, small_scale);
369 Font * nFontInGameNote = createFont(file, "system_font_note_size", 18, small_scale);
370 Font * nFontInBook = createFont(file, "system_font_book_size", 18, small_scale);
371
372 // Only release old fonts after creating new ones to allow same fonts to be cached.
373 FontCache::releaseFont(hFontMainMenu);
374 FontCache::releaseFont(hFontMenu);
375 FontCache::releaseFont(hFontControls);
376 FontCache::releaseFont(hFontCredits);
377 FontCache::releaseFont(hFontInGame);
378 FontCache::releaseFont(hFontInGameNote);
379 FontCache::releaseFont(hFontInBook);
380
381 hFontMainMenu = nFontMainMenu;
382 hFontMenu = nFontMenu;
383 hFontControls = nFontControls;
384 hFontCredits = nFontCredits;
385 hFontInGame = nFontInGame;
386 hFontInGameNote = nFontInGameNote;
387 hFontInBook = nFontInBook;
388
389 if(!hFontMainMenu || !hFontMenu || !hFontControls || !hFontCredits || !hFontInGame
390 || !hFontInGameNote || !hFontInBook) {
391 LogCritical << "Could not load font " << file << " for scale " << scale
392 << " / small scale " << small_scale;
393 return false;
394 }
395
396 LogInfo << "Loaded font " << file << " with sizes " << hFontMainMenu->getSize() << ", "
397 << hFontMenu->getSize() << ", " << hFontControls->getSize()
398 << ", " << hFontCredits->getSize() << ", " << hFontInGame->getSize() << ", "
399 << hFontInGameNote->getSize() << ", " << hFontInBook->getSize();
400
401 return true;
402 }
403
404 //-----------------------------------------------------------------------------
ARX_Text_Close()405 void ARX_Text_Close() {
406
407 created_font_scale = 0.f;
408
409 delete pTextManage;
410 pTextManage = NULL;
411
412 delete pTextManageFlyingOver;
413 pTextManageFlyingOver = NULL;
414
415 FontCache::releaseFont(hFontInBook);
416 hFontInBook = NULL;
417
418 FontCache::releaseFont(hFontMainMenu);
419 hFontMainMenu = NULL;
420
421 FontCache::releaseFont(hFontMenu);
422 hFontMenu = NULL;
423
424 FontCache::releaseFont(hFontControls);
425 hFontControls = NULL;
426
427 FontCache::releaseFont(hFontCredits);
428 hFontCredits = NULL;
429
430 FontCache::releaseFont(hFontInGame);
431 hFontInGame = NULL;
432
433 FontCache::releaseFont(hFontInGameNote);
434 hFontInGameNote = NULL;
435
436 FontCache::shutdown();
437 }
438