factored out the EFFv2 saving into EFFImporter
[gemrb.git] / gemrb / core / Font.cpp
blobd62afecc4f8ea40725c5ba1fed627c45060febdd
1 /* GemRB - Infinity Engine Emulator
2 * Copyright (C) 2003 The GemRB Project
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 //This class represents game fonts. Fonts are special .bam files.
22 //Each cycle stands for a letter.
24 #include "Font.h"
26 #include "win32def.h"
28 #include "GameData.h"
29 #include "Interface.h"
30 #include "Palette.h"
31 #include "Video.h"
33 #include <cassert>
35 unsigned int lastX = 0;
37 #define PARAGRAPH_START_X 5;
39 static const Color black = {0, 0, 0, 0};
41 inline size_t mystrlen(const char* string)
43 if (!string) {
44 return ( size_t ) 0;
46 const char* tmp = string;
47 size_t count = 0;
48 while (*tmp != 0) {
49 if (( ( unsigned char ) * tmp ) >= 0xf0) {
50 tmp += 3;
51 count += 3;
53 count++;
54 tmp++;
56 return count;
59 Font::Font(int w, int h, Palette* pal)
61 lastX = 0;
62 count = 0;
63 FirstChar = 0;
64 sprBuffer = 0;
66 width = w;
67 height = h;
68 tmpPixels = (unsigned char*)malloc(width*height);
70 memset( xPos, 0, sizeof( xPos) );
71 memset( yPos, 0, sizeof( yPos) );
73 pal->IncRef();
74 palette = pal;
75 maxHeight = h;
78 Font::~Font(void)
80 Video *video = core->GetVideoDriver();
81 gamedata->FreePalette( palette );
82 video->FreeSprite( sprBuffer );
85 void Font::FinalizeSprite(bool cK, int index)
87 sprBuffer = core->GetVideoDriver()->CreateSprite8( width, height, 8, tmpPixels, palette ? palette->col : 0, cK, index );
88 tmpPixels = 0;
91 void Font::AddChar(unsigned char* spr, int w, int h, short xPos, short yPos)
93 if (!spr) {
94 size[count].x = 0;
95 size[count].y = 0;
96 size[count].w = 0;
97 size[count].h = 0;
98 this->xPos[count] = 0;
99 this->yPos[count] = 0;
100 count++;
101 return;
103 unsigned char * currPtr = tmpPixels + lastX;
104 unsigned char * srcPtr = ( unsigned char * ) spr;
105 for (int y = 0; y < h; y++) {
106 memcpy( currPtr, srcPtr, w );
107 srcPtr += w;
108 currPtr += width;
110 size[count].x = lastX;
111 size[count].y = 0;
112 size[count].w = w;
113 size[count].h = h;
114 this->xPos[count] = xPos;
115 this->yPos[count] = yPos;
116 count++;
117 lastX += w;
120 void Font::PrintFromLine(int startrow, Region rgn, const unsigned char* string,
121 Palette* hicolor, unsigned char Alignment, Font* initials,
122 Sprite2D* cursor, unsigned int curpos, bool NoColor) const
124 bool enablecap=false;
125 int capital = 0;
126 if (initials)
128 capital=1;
129 enablecap=true;
131 int initials_rows = 0;
132 int initials_x = 0;
134 unsigned int psx = PARAGRAPH_START_X;
135 Palette *pal = hicolor;
136 if (!pal) {
137 pal = palette;
139 if (startrow) enablecap=false;
141 if (initials==this) {
142 enablecap=false;
145 sprBuffer->SetPalette( pal );
146 size_t len = strlen( ( char* ) string );
147 char* tmp = ( char* ) malloc( len + 1 );
148 memcpy( tmp, ( char * ) string, len + 1 );
149 SetupString( tmp, rgn.w, NoColor );
150 int ystep = 0;
151 if (Alignment & IE_FONT_SINGLE_LINE) {
152 for (size_t i = 0; i < len; i++) {
153 int height = yPos[( unsigned char ) tmp[i] - 1];
154 if (ystep < height)
155 ystep = height;
157 } else {
158 ystep = size[1].h;
160 if (!ystep) ystep = maxHeight;
161 int x = psx, y = ystep;
162 int w = CalcStringWidth( tmp, NoColor );
163 if (Alignment & IE_FONT_ALIGN_CENTER) {
164 x = ( rgn.w - w) / 2;
165 } else if (Alignment & IE_FONT_ALIGN_RIGHT) {
166 x = ( rgn.w - w );
168 if (Alignment & IE_FONT_ALIGN_MIDDLE) {
169 int h = 0;
170 for (size_t i = 0; i <= len; i++) {
171 if (( tmp[i] == 0 ) || ( tmp[i] == '\n' ))
172 h++;
174 h = h * ystep;
175 y += ( rgn.h - h ) / 2;
176 } else if (Alignment & IE_FONT_ALIGN_BOTTOM) {
177 int h = 1;
178 for (size_t i = 0; i <= len; i++) {
179 if (( tmp[i] == 0 ) || ( tmp[i] == '\n' ))
180 h++;
182 h = h * ystep;
183 y += ( rgn.h - h );
184 } else if (Alignment & IE_FONT_ALIGN_TOP) {
185 y += 5;
188 Video* video = core->GetVideoDriver();
189 int row = 0;
190 for (size_t i = 0; i < len; i++) {
191 if (( ( unsigned char ) tmp[i] ) == '[' && !NoColor) {
192 i++;
193 char tag[256];
194 tag[0]=0;
196 for (int k = 0; k < 256 && i<len; k++) {
197 if (tmp[i] == ']') {
198 tag[k] = 0;
199 break;
201 tag[k] = tmp[i++];
204 if (strnicmp( tag, "capital=",8)==0) {
205 sscanf( tag, "capital=%d", &capital);
206 if (capital && (row>=startrow) ) {
207 enablecap=true;
209 continue;
213 if (strnicmp( tag, "color=", 6 ) == 0) {
214 unsigned int r,g,b;
215 if (sscanf( tag, "color=%02X%02X%02X", &r, &g, &b ) != 3)
216 continue;
217 const Color c = {(unsigned char) r,(unsigned char)g, (unsigned char)b, 0};
218 Palette* newPal = core->CreatePalette( c, black );
219 sprBuffer->SetPalette( newPal );
220 gamedata->FreePalette( newPal );
221 continue;
223 if (stricmp( tag, "/color" ) == 0) {
224 sprBuffer->SetPalette( pal );
225 continue;
228 if (stricmp( "p", tag ) == 0) {
229 psx = x;
230 continue;
232 if (stricmp( "/p", tag ) == 0) {
233 psx = PARAGRAPH_START_X;
235 continue;
238 if (row < startrow) {
239 if (tmp[i] == 0) {
240 row++;
242 continue;
244 if (( tmp[i] == 0 ) || ( tmp[i] == '\n' )) {
245 y += ystep;
246 x = psx;
247 int w = CalcStringWidth( &tmp[i + 1], NoColor );
248 if (initials_rows > 0) {
249 initials_rows--;
250 x += initials_x;
251 w += initials_x;
253 if (Alignment & IE_FONT_ALIGN_CENTER) {
254 x = ( rgn.w - w ) / 2;
255 } else if (Alignment & IE_FONT_ALIGN_RIGHT) {
256 x = ( rgn.w - w );
258 continue;
260 unsigned char currChar = ( unsigned char ) tmp[i] - 1;
261 if (initials && capital && enablecap) {
262 x = initials->PrintInitial( x, y, rgn, currChar );
263 initials_x = x;
265 //how many more lines to be indented (one was already indented)
266 initials_rows = (initials->maxHeight-1)/maxHeight;
267 enablecap = false;
268 continue;
270 video->BlitSpriteRegion( sprBuffer, size[currChar],
271 x + rgn.x, y + rgn.y - yPos[currChar], true, &rgn );
272 if (cursor && ( i == curpos )) {
273 video->BlitSprite( cursor, x + rgn.x,
274 y + rgn.y, true, &rgn );
276 x += size[currChar].w;
278 if (cursor && ( curpos == len )) {
279 video->BlitSprite( cursor, x + rgn.x,
280 y + rgn.y, true, &rgn );
282 free( tmp );
285 void Font::Print(Region rgn, const unsigned char* string, Palette* hicolor,
286 unsigned char Alignment, bool anchor, Font* initials,
287 Sprite2D* cursor, unsigned int curpos, bool NoColor) const
289 Print(rgn, rgn, string, hicolor, Alignment, anchor, initials, cursor, curpos, NoColor);
292 void Font::Print(Region cliprgn, Region rgn, const unsigned char* string,
293 Palette* hicolor, unsigned char Alignment, bool anchor, Font* initials,
294 Sprite2D* cursor, unsigned int curpos, bool NoColor) const
296 bool enablecap=false;
297 int capital = 0;
298 if (initials)
300 capital=1;
301 enablecap=true;
304 unsigned int psx = PARAGRAPH_START_X;
305 Palette* pal = hicolor;
306 if (!pal) {
307 pal = palette;
309 if (initials==this) {
310 initials = NULL;
313 sprBuffer->SetPalette( pal );
314 size_t len = strlen( ( char* ) string );
315 char* tmp = ( char* ) malloc( len + 1 );
316 memcpy( tmp, ( char * ) string, len + 1 );
317 while (len > 0 && (tmp[len - 1] == '\n' || tmp[len - 1] == '\r')) {
318 // ignore trailing newlines
319 tmp[len - 1] = 0;
320 len--;
323 SetupString( tmp, rgn.w, NoColor );
324 int ystep = 0;
325 if (Alignment & IE_FONT_SINGLE_LINE) {
327 for (size_t i = 0; i < len; i++) {
328 if (tmp[i] == 0) continue;
329 int height = yPos[( unsigned char ) tmp[i] - 1];
330 if (ystep < height)
331 ystep = height;
333 } else {
334 ystep = size[1].h;
336 if (!ystep) ystep = maxHeight;
337 int x = psx, y = ystep;
338 Video* video = core->GetVideoDriver();
340 if (Alignment & IE_FONT_ALIGN_CENTER) {
341 int w = CalcStringWidth( tmp, NoColor );
342 x = ( rgn.w - w ) / 2;
343 } else if (Alignment & IE_FONT_ALIGN_RIGHT) {
344 int w = CalcStringWidth( tmp, NoColor );
345 x = ( rgn.w - w );
348 if (Alignment & IE_FONT_ALIGN_MIDDLE) {
349 int h = 0;
350 for (size_t i = 0; i <= len; i++) {
351 if (tmp[i] == 0)
352 h++;
354 h = h * ystep;
355 y += ( rgn.h - h ) / 2;
356 } else if (Alignment & IE_FONT_ALIGN_BOTTOM) {
357 int h = 1;
358 for (size_t i = 0; i <= len; i++) {
359 if (tmp[i] == 0)
360 h++;
362 h = h * ystep;
363 y += ( rgn.h - h );
364 } else if (Alignment & IE_FONT_ALIGN_TOP) {
365 y += 5;
367 for (size_t i = 0; i < len; i++) {
368 if (( ( unsigned char ) tmp[i] ) == '[' && !NoColor) {
369 i++;
370 char tag[256];
371 tag[0]=0;
372 for (int k = 0; k < 256 && i<len; k++) {
373 if (tmp[i] == ']') {
374 tag[k] = 0;
375 break;
377 tag[k] = tmp[i++];
380 if (strnicmp( tag, "capital=",8)==0) {
381 sscanf( tag, "capital=%d", &capital);
382 if (capital) {
383 enablecap=true;
385 continue;
388 if (strnicmp( tag, "color=", 6 ) == 0) {
389 unsigned int r,g,b;
390 if (sscanf( tag, "color=%02X%02X%02X", &r, &g, &b ) != 3)
391 continue;
392 const Color c = {(unsigned char) r,(unsigned char) g,(unsigned char) b, 0};
393 Palette* newPal = core->CreatePalette( c, black );
394 sprBuffer->SetPalette( newPal );
395 gamedata->FreePalette( newPal );
396 continue;
398 if (stricmp( tag, "/color" ) == 0) {
399 sprBuffer->SetPalette( pal );
400 continue;
402 if (stricmp( "p", tag ) == 0) {
403 psx = x;
404 continue;
406 if (stricmp( "/p", tag ) == 0) {
407 psx = PARAGRAPH_START_X;
408 continue;
410 continue;
413 if (tmp[i] == 0) {
414 y += ystep;
415 x = psx;
416 int w = CalcStringWidth( &tmp[i + 1], NoColor );
417 if (Alignment & IE_FONT_ALIGN_CENTER) {
418 x = ( rgn.w - w ) / 2;
419 } else if (Alignment & IE_FONT_ALIGN_RIGHT) {
420 x = ( rgn.w - w );
422 continue;
424 unsigned char currChar = ( unsigned char ) tmp[i] - 1;
425 if (initials && capital) {
426 x = initials->PrintInitial( x, y, rgn, currChar );
427 enablecap=false;
428 continue;
430 video->BlitSpriteRegion( sprBuffer, size[currChar],
431 x + rgn.x, y + rgn.y - yPos[currChar],
432 anchor, &cliprgn );
433 if (cursor && ( curpos == i ))
434 video->BlitSprite( cursor, x + rgn.x, y + rgn.y, anchor, &cliprgn );
435 x += size[currChar].w;
437 if (cursor && ( curpos == len )) {
438 video->BlitSprite( cursor, x + rgn.x, y + rgn.y, anchor, &cliprgn );
440 free( tmp );
443 int Font::PrintInitial(int x, int y, const Region &rgn, unsigned char currChar) const
445 Video *video = core->GetVideoDriver();
446 video->BlitSpriteRegion( sprBuffer, size[currChar],
447 x + rgn.x, y + rgn.y - yPos[currChar], true, &rgn );
448 x += size[currChar].w;
449 return x;
452 int Font::CalcStringWidth(const char* string, bool NoColor) const
454 size_t ret = 0, len = strlen( string );
455 for (size_t i = 0; i < len; i++) {
456 if (( ( unsigned char ) string[i] ) == '[' && !NoColor) {
457 i++;
458 if (i>=len)
459 break;
460 char tag[256];
461 int k = 0;
462 for (k = 0; k < 256; k++) {
463 if (string[i] == ']') {
464 tag[k] = 0;
465 break;
467 tag[k] = string[i++];
469 continue;
471 ret += size[( unsigned char ) string[i] - 1].w;
473 return ( int ) ret;
476 void Font::SetupString(char* string, unsigned int width, bool NoColor) const
478 size_t len = strlen( string );
479 unsigned int psx = PARAGRAPH_START_X;
480 int lastpos = 0;
481 unsigned int x = psx, wx = 0;
482 bool endword = false;
483 for (size_t pos = 0; pos < len; pos++) {
484 if (x + wx > width) {
485 if (!endword && ( x == psx ))
486 lastpos = ( int ) pos;
487 else
488 string[lastpos] = 0;
489 x = psx;
491 if (string[pos] == 0) {
492 continue;
494 endword = false;
495 if (string[pos] == '\r')
496 string[pos] = ' ';
497 if (string[pos] == '\n') {
498 string[pos] = 0;
499 x = psx;
500 wx = 0;
501 lastpos = ( int ) pos;
502 endword = true;
503 continue;
505 if (( ( unsigned char ) string[pos] ) == '[' && !NoColor) {
506 pos++;
507 if (pos>=len)
508 break;
509 char tag[256];
510 int k = 0;
511 for (k = 0; k < 256; k++) {
512 if (string[pos] == ']') {
513 tag[k] = 0;
514 break;
516 tag[k] = string[pos++];
518 if (stricmp( "p", tag ) == 0) {
519 psx = x;
520 continue;
522 if (stricmp( "/p", tag ) == 0) {
523 psx = PARAGRAPH_START_X;
524 continue;
526 continue;
529 if (string[pos] && string[pos] != ' ') {
530 string[pos] = ( unsigned char ) (string[pos] - FirstChar);
533 wx += size[( unsigned char ) string[pos] - 1].w;
534 if (( string[pos] == ' ' ) || ( string[pos] == '-' )) {
535 x += wx;
536 wx = 0;
537 lastpos = ( int ) pos;
538 endword = true;
543 Palette* Font::GetPalette() const
545 assert(palette);
546 palette->IncRef();
547 return palette;
550 void Font::SetPalette(Palette* pal)
552 if (palette) palette->Release();
553 pal->IncRef();
554 palette = pal;
557 void Font::SetFirstChar( unsigned char first)
559 FirstChar = first;