changed copyright years in source files
[fegdk.git] / core / code / video / f_fontft.cpp
blob58b0361aa981a0be16e12f5f2fdc7835d261364f
1 /*
2 fegdk: FE Game Development Kit
3 Copyright (C) 2001-2008 Alexey "waker" Yakovenko
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with this library; if not, write to the Free
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 Alexey Yakovenko
20 waker@users.sourceforge.net
23 #include "pch.h"
24 #include "f_fontft.h"
25 #include "f_error.h"
26 #include "f_filesystem.h"
27 #include "f_engine.h"
28 #include "f_effect.h"
29 #include "f_baseviewport.h"
30 #include "f_texture.h"
31 #include "f_drawutil.h"
32 #include "f_baserenderer.h"
33 #include "f_resourcemgr.h"
34 #include "utf8.h"
35 #include "cvars.h"
37 extern "C" {
38 #include <jpeglib.h>
41 #include <ft2build.h>
42 #include FT_FREETYPE_H
43 #include FT_GLYPH_H
44 //#include FT_INTERNAL_OBJECTS_H
45 //#include FT_INTERNAL_CALC_H
46 #include FT_OUTLINE_H
47 #include FT_TRIGONOMETRY_H
48 #include FT_SYNTHESIS_H
50 namespace fe
52 #if 0
53 #define FT_BOLD_THRESHOLD 0x0100
56 /*************************************************************************/
57 /*************************************************************************/
58 /**** ****/
59 /**** EXPERIMENTAL OBLIQUING SUPPORT ****/
60 /**** ****/
61 /*************************************************************************/
62 /*************************************************************************/
64 FT_EXPORT_DEF (void)
65 FT_GlyphSlot_Oblique (FT_GlyphSlot slot)
67 FT_Matrix transform;
68 FT_Outline* outline = &slot->outline;
71 /* only oblique outline glyphs */
72 if (slot->format != FT_GLYPH_FORMAT_OUTLINE)
73 return;
75 /* we don't touch the advance width */
77 /* For italic, simply apply a shear transform, with an angle */
78 /* of about 12 degrees. */
80 transform.xx = 0x10000L;
81 transform.yx = 0x00000L;
83 transform.xy = 0x06000L;
84 transform.yy = 0x10000L;
86 FT_Outline_Transform (outline, &transform);
90 /*************************************************************************/
91 /*************************************************************************/
92 /**** ****/
93 /**** EXPERIMENTAL EMBOLDENING/OUTLINING SUPPORT ****/
94 /**** ****/
95 /*************************************************************************/
96 /*************************************************************************/
100 static int
101 ft_test_extrema (FT_Outline* outline,
102 int n)
104 FT_Vector *prev, *cur, *next;
105 FT_Pos product;
106 FT_Int c, first, last;
109 /* we need to compute the `previous' and `next' point */
110 /* for these extrema. */
111 cur = outline->points + n;
112 prev = cur - 1;
113 next = cur + 1;
115 first = 0;
116 for (c = 0; c < outline->n_contours; c++)
118 last = outline->contours[c];
120 if (n == first)
121 prev = outline->points + last;
123 if (n == last)
124 next = outline->points + first;
126 first = last + 1;
129 product = FT_MulDiv (cur->x - prev->x, /* in.x */
130 next->y - cur->y, /* out.y */
131 0x40)
133 FT_MulDiv (cur->y - prev->y, /* in.y */
134 next->x - cur->x, /* out.x */
135 0x40);
137 if (product)
138 product = product > 0 ? 1 : -1;
140 return product;
144 /* Compute the orientation of path filling. It differs between TrueType */
145 /* and Type1 formats. We could use the `FT_OUTLINE_REVERSE_FILL' flag, */
146 /* but it is better to re-compute it directly (it seems that this flag */
147 /* isn't correctly set for some weird composite glyphs currently). */
148 /* */
149 /* We do this by computing bounding box points, and computing their */
150 /* curvature. */
151 /* */
152 /* The function returns either 1 or -1. */
153 /* */
154 static int
155 ft_get_orientation (FT_Outline* outline)
157 FT_BBox box;
158 FT_BBox indices;
159 int n, last;
162 indices.xMin = -1;
163 indices.yMin = -1;
164 indices.xMax = -1;
165 indices.yMax = -1;
167 box.xMin = box.yMin = 32767;
168 box.xMax = box.yMax = -32768;
170 /* is it empty ? */
171 if (outline->n_contours < 1)
172 return 1;
174 last = outline->contours[outline->n_contours - 1];
176 for (n = 0; n <= last; n++)
178 FT_Pos x, y;
181 x = outline->points[n].x;
182 if (x < box.xMin)
184 box.xMin = x;
185 indices.xMin = n;
187 if (x > box.xMax)
189 box.xMax = x;
190 indices.xMax = n;
193 y = outline->points[n].y;
194 if (y < box.yMin)
196 box.yMin = y;
197 indices.yMin = n;
199 if (y > box.yMax)
201 box.yMax = y;
202 indices.yMax = n;
206 /* test orientation of the xmin */
207 n = ft_test_extrema (outline, indices.xMin);
208 if (n)
209 goto Exit;
211 n = ft_test_extrema (outline, indices.yMin);
212 if (n)
213 goto Exit;
215 n = ft_test_extrema (outline, indices.xMax);
216 if (n)
217 goto Exit;
219 n = ft_test_extrema (outline, indices.yMax);
220 if (!n)
221 n = 1;
223 Exit:
224 return n;
228 FT_EXPORT_DEF (void)
229 FT_GlyphSlot_Embolden (FT_GlyphSlot slot)
231 FT_Vector* points;
232 FT_Vector v_prev, v_first, v_next, v_cur;
233 FT_Pos distance;
234 FT_Outline* outline = &slot->outline;
235 FT_Face face = FT_SLOT_FACE (slot);
236 FT_Angle rotate, angle_in, angle_out;
237 FT_Int c, n, first, orientation;
240 /* only embolden outline glyph images */
241 if (slot->format != FT_GLYPH_FORMAT_OUTLINE)
242 return;
244 /* compute control distance */
245 distance = FT_MulFix (face->units_per_EM / 60,
246 face->size->metrics.y_scale);
248 orientation = ft_get_orientation (outline);
249 rotate = FT_ANGLE_PI2*orientation;
251 points = outline->points;
253 first = 0;
254 for (c = 0; c < outline->n_contours; c++)
256 int last = outline->contours[c];
259 v_first = points[first];
260 v_prev = points[last];
261 v_cur = v_first;
263 for (n = first; n <= last; n++)
265 FT_Pos d;
266 FT_Vector in, out;
267 FT_Fixed scale;
268 FT_Angle angle_diff;
271 if (n < last) v_next = points[n + 1];
272 else v_next = v_first;
274 /* compute the in and out vectors */
275 in.x = v_cur.x - v_prev.x;
276 in.y = v_cur.y - v_prev.y;
278 out.x = v_next.x - v_cur.x;
279 out.y = v_next.y - v_cur.y;
281 angle_in = FT_Atan2 (in.x, in.y);
282 angle_out = FT_Atan2 (out.x, out.y);
283 angle_diff = FT_Angle_Diff (angle_in, angle_out);
284 scale = FT_Cos (angle_diff/2);
286 if (scale < 0x400L && scale > -0x400L)
288 if (scale >= 0)
289 scale = 0x400L;
290 else
291 scale = -0x400L;
294 d = FT_DivFix (distance, scale);
296 FT_Vector_From_Polar (&in, d, angle_in + angle_diff/2 - rotate);
298 outline->points[n].x = v_cur.x + distance + in.x;
299 outline->points[n].y = v_cur.y + distance + in.y;
301 v_prev = v_cur;
302 v_cur = v_next;
305 first = last + 1;
308 slot->metrics.horiAdvance = (slot->metrics.horiAdvance + distance*4) & -64;
312 /* END */
313 #endif
315 const int FontTextureSize = 256;
317 // horz alignment
318 const int fontFT::hAlignLeft = 0x00000001; // could it be a default
319 const int fontFT::hAlignRight = 0x00000002;
320 const int fontFT::hAlignCenter = 0x00000004;
321 const int fontFT::hAlignStretch = 0x00000008;
323 // vert alignment
324 const int fontFT::vAlignTop = 0x00000010;
325 const int fontFT::vAlignBottom = 0x00000020;
326 const int fontFT::vAlignCenter = 0x00000040;
328 // word wrap
329 const int fontFT::wordWrap = 0x00000100;
331 // removes line gap before first line
332 const int fontFT::remove1stLineGap = 0x00000200;
333 const int fontFT::useClipRect = 0x00000400;
335 //fontFT::fontFT (const char *fname, const char *fontname, int size, bool antialias, bool bold, bool italic)
336 fontFT::fontFT (const char *name)
338 mLibrary = NULL;
339 mFace = NULL;
340 mpFntCache = NULL;
342 // parse fname
343 char *buf = new char[strlen (name)+1];
344 char *tmp = buf;
345 strcpy (tmp, name);
346 char *colon;
348 mbAntialias = 0;
349 mHeight = 0;
350 mbBold = 0;
351 mbItalic = 0;
353 // font file name
354 colon = strchr (tmp, ':');
355 if (colon)
357 *colon = 0;
358 mFontFileName = tmp;
359 *colon = ':';
360 tmp = colon + 1;
362 // size
363 colon = strchr (tmp, ':');
364 if (colon)
366 *colon = 0;
367 mHeight = atoi (tmp);
368 *colon = ':';
369 tmp = colon + 1;
371 // antialias
372 colon = strchr (tmp, ':');
373 if (colon)
375 *colon = 0;
376 mbAntialias = atoi (tmp) ? true : false;
377 *colon = ':';
378 tmp = colon + 1;
380 // bold
381 colon = strchr (tmp, ':');
382 if (colon)
384 *colon = 0;
385 mbBold = atoi (tmp) ? true : false;
386 *colon = ':';
387 tmp = colon + 1;
389 // italic
390 colon = strchr (tmp, ':');
391 if (colon)
393 *colon = 0;
394 mbItalic = atoi (tmp) ? true : false;
395 *colon = ':';
396 tmp = colon + 1;
398 else if (*tmp)
399 mbItalic = atoi (tmp) ? true : false;
401 else if (*tmp)
402 mbBold = atoi (tmp) ? true : false;
404 else if (*tmp)
405 mbAntialias = atoi (tmp) ? true : false;
407 else if (*tmp)
408 mHeight = atoi (tmp);
410 else if (*tmp)
411 mFontFileName = name;
413 delete[] buf;
415 setName (name);
418 NOTE: oooooooold stuff from win32 gdi days
419 96 is the default windows value for LOGPIXELSY
420 72 is the value from formula from msdn article on LOGFONT structure
421 [q] For the MM_TEXT mapping mode, you can use the following formula to specify a height for a font with a specified point size:
422 lfHeight = -MulDiv (PointSize, GetDeviceCaps (hDC, LOGPIXELSY), 72); [/q]
425 // try to load the glyph name as-is
426 char fname[100] = "fonts/";
427 strcat (fname, mFontFileName);
428 file *f;
429 // open using fe filesystem
430 f = g_engine->getFileSystem ()->openFile (fname, FS_MODE_READ);
431 if (!f)
433 if (!strstr (fname, ".ttf"))
434 strcat (fname, ".ttf");
435 f = g_engine->getFileSystem ()->openFile (fname, FS_MODE_READ);
436 if (!f)
437 sys_error ("failed to open font file (%s)", name);
440 mFntCacheSize = f->getSize ();
441 mpFntCache = new FT_Byte[mFntCacheSize];
442 f->read (mpFntCache, mFntCacheSize);
443 f->close ();
445 mbLost = true;
447 restore ();
450 fontFT::fontFT (charParser &parser, const char *name)
454 fontFT::~fontFT (void)
456 if (!mbLost)
457 loose ();
458 if (mpFntCache)
460 delete[] mpFntCache;
461 mpFntCache = NULL;
465 texturePtr fontFT::allocTexture (void) const
467 cStr texture_name;
468 texture_name.printf ("empty:%d:%d:%s:%d", FontTextureSize, FontTextureSize, mName.c_str (), mpTextures.size ());
469 texturePtr t = g_engine->getResourceMgr ()->createTexture (texture_name);
470 mpTextures.push_back (t);
472 // will black
473 lockedRect rc = t->lockRect (0, NULL, false);
474 memset (rc.pBits, 0x00, rc.pitch * t->getHeight ());
475 t->unlockRect (0);
477 return t;
480 void
481 screenshot_write (const char *fname, const char *bits, int pitch)
483 struct jpeg_compress_struct cinfo;
484 struct jpeg_error_mgr jerr;
486 FILE* fp; //Target file
487 int nSampsPerRow; //Physical row width in image buffer
488 int i;
490 cinfo.err = jpeg_std_error(&jerr); //Use default error handling (ugly!)
492 jpeg_create_compress(&cinfo);
494 fp = fopen (fname, "w+b");
496 jpeg_stdio_dest(&cinfo, fp);
498 cinfo.image_width = FontTextureSize; //Image width and height, in pixels
499 cinfo.image_height = FontTextureSize;
500 cinfo.input_components = 1; //Color components per pixel
501 //(RGB_PIXELSIZE - see jmorecfg.h)
502 cinfo.in_color_space = JCS_GRAYSCALE; //Colorspace of input image
504 jpeg_set_defaults(&cinfo);
506 jpeg_set_quality(&cinfo,
507 100, //Quality: 0-100 scale
508 TRUE); //Limit to baseline-JPEG values
510 jpeg_start_compress(&cinfo, TRUE);
512 //JSAMPLEs per row in output buffer
513 nSampsPerRow = cinfo.image_width * cinfo.input_components;
515 //Write the array of scan lines to the JPEG file
516 for (i = 0; i < FontTextureSize; i++)
518 // unsigned char *c = &bits[i*width*3];
519 unsigned char *c = new unsigned char[FontTextureSize];
520 for (int k = 0; k < FontTextureSize; k++)
522 c[k] = bits[i * pitch + k*4+2];
524 jpeg_write_scanlines(&cinfo, &c, 1);
525 delete[] c;
528 jpeg_finish_compress(&cinfo); //Always finish
530 fclose(fp);
532 jpeg_destroy_compress(&cinfo); //Free resources
535 int fontFT::getGlyphIdx (wchar_t c) const
537 int ptsize = mHeight;
538 FT_Error error;
539 FT_Encoding encoding = ft_encoding_unicode;
540 glyphMap::const_iterator it = mGlyphMap.find (c);
541 if (it == mGlyphMap.end ())
543 int sx = mCurrentX, sy = mCurrentY;
544 texturePtr t;
545 if (mpTextures.empty ())
546 t = allocTexture ();
547 else
548 t = mpTextures[mpTextures.size () - 1];
549 lockedRect rc = t->lockRect (0, NULL, 0);
550 // add glyph
551 glyph g;
552 g.texture = mpTextures.size () - 1;
553 g.mins[0] = 0;
554 g.mins[1] = 1;
555 g.maxs[0] = 0;
556 g.maxs[1] = 1;
557 FT_UInt load_flags = FT_LOAD_DEFAULT;
558 error = FT_Select_Charmap (mFace, encoding);
559 if (error)
560 sys_error ("Invalid charmap");
561 g.ft_glyph_index = FT_Get_Char_Index (mFace, (FT_ULong)c);
562 error = FT_Load_Glyph (mFace, g.ft_glyph_index, load_flags);
563 if (error)
564 sys_error ("FT_Load_Glyph failed!");
566 /* if (mbBold)
567 FT_GlyphSlot_Embolden (mFace->glyph);
568 if (mbItalic)
569 FT_GlyphSlot_Oblique (mFace->glyph);*/
571 FT_Glyph gimage;
572 error = FT_Get_Glyph (mFace->glyph, &gimage);
573 if (error)
574 sys_error ("FT_Get_Glyph failed!");
576 FT_BBox bb;
577 FT_Glyph_Get_CBox (gimage, ft_glyph_bbox_truncate, &bb);
578 g.origin[0] = bb.xMin;
579 g.origin[1] = bb.yMin;
581 // convert to bitmap
582 for (;;)
584 FT_Glyph image;
585 error = FT_Glyph_Copy (gimage, &image);
586 if (error)
587 break;
588 error = FT_Glyph_To_Bitmap (&image, mbAntialias ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, 0, 1);
589 if (error)
590 break;
591 // throw genericError ("FT_Glyph_To_Bitmap failed!");
592 FT_BitmapGlyph bitmap = (FT_BitmapGlyph)image;
593 FT_Bitmap* source = &bitmap->bitmap;
595 // blit to texture
596 int rows = source->rows;
597 int width = source->width;
598 int pitch = source->pitch;
599 unsigned char *buffer = source->buffer;
601 int max = source->num_grays - 1;
602 if (max == 0)
603 max = 1;
604 if (!mbAntialias)
605 max = 0xff;
608 int y;
609 unsigned char* read;
610 ulong* write;
612 read = buffer;
614 if (sx + width >= FontTextureSize)
616 sx = 0;
617 sy += ptsize;
620 g.mins[0] = sx;
621 g.mins[1] = sy;
622 g.maxs[0] = sx + width;
623 g.maxs[1] = sy + rows;
625 g.advance[0] = mFace->glyph->advance.x >> 6;
626 g.advance[1] = mFace->glyph->advance.y >> 6;
628 // this hack was used for some crazy font which had tiny space symbol
629 if (c == 32)
631 // space may be to small
632 g.advance[0]*=2;
635 uchar *dst = (unsigned char *)rc.pBits;
636 write = (ulong*) (dst + sx*4 + rc.pitch * sy);
637 y = rows;
640 if (sy + (rows-y) >= FontTextureSize)
642 // out of texture space
643 sx = 0; sy = 0;
644 t->unlockRect (0);
645 t = allocTexture ();
646 lockedRect rc = t->lockRect (0, NULL, 0);
647 y = rows;
648 dst = (unsigned char *)rc.pBits;
649 read = buffer;
650 write = (ulong*) (dst + sx*4 + rc.pitch * sy);
651 g.mins[0] = sx;
652 g.mins[1] = sy;
653 g.maxs[0] = sx + width;
654 g.maxs[1] = sy + rows;
655 g.texture = mpTextures.size () - 1;
656 continue;
658 unsigned char* _read = read;
659 uchar* _write = (uchar *)write;
660 int x = width;
661 int xx = 7;
663 while (x > 0)
665 unsigned char val;
667 val = *_read;
669 if (!mbAntialias)
670 val = ( (val & (1<<xx))>>xx) ? 0xff : 0x00;
672 if (val)
674 if (val == max)
676 _write[0] = 0xff;
677 _write[1] = 0xff;
678 _write[2] = 0xff;
679 _write[3] = 0xff;
681 else
683 /* compose gray value */
684 unsigned char pix;
686 pix = _write[0];
688 int d, half = max >> 1;
689 d = (int)0xff - pix;
690 pix += (unsigned char) ((val*d + half)/max);
691 _write[0] = pix;
692 _write[1] = pix;
693 _write[2] = pix;
694 _write[3] = pix;
697 _write +=4;
698 xx--;
699 if (mbAntialias || xx < 0)
701 xx = 7;
702 _read ++;
704 x--;
707 read += pitch;
708 write = (ulong*) (( (uchar *)write)+rc.pitch);
709 y--;
711 while (y > 0);
713 sx += width+1;
714 FT_Done_Glyph (image);
715 break;
718 FT_Done_Glyph (gimage);
719 int idx = mGlyphs.size ();
720 mGlyphMap[c] = idx;
721 mGlyphs.push_back (g);
722 if (sys_debug_freetype->ivalue)
724 char str[100];
725 sprintf (str, "%s%d.jpg", name (), mpTextures.size ()-1);
726 char *ptr;
727 while (ptr = strstr (str, ":"))
729 *ptr = '-';
731 screenshot_write (str, (char *)rc.pBits, rc.pitch);
733 t->unlockRect (0);
734 mCurrentX = sx;
735 mCurrentY = sy;
736 return idx;
738 return (*it).second;
741 void fontFT::restore (void)
743 FT_Error error;
744 int hinted = 1;
745 int antialias = mbAntialias;
746 int use_sbits = 0;
747 int kerning = 1;
748 int use_gamma = 0;
749 int res = 72;
750 int ptsize = mHeight;
752 error = FT_Init_FreeType (&mLibrary);
753 if (error)
754 sys_error ("Could not initialize FreeType library");
756 error = FT_New_Memory_Face (mLibrary, mpFntCache, mFntCacheSize, 0, &mFace);
757 if (error)
758 sys_error ("FT_New_Memory_Face failed (%s)", mName.c_str ());
760 // FIXME we should ALWAYS select from available font sizes instead of forcing font to scale
761 if (ptsize)
763 (void)FT_Set_Char_Size (mFace,
764 ptsize << 6,
765 ptsize << 6,
766 res,
767 res);
769 else
771 mHeight = (mFace->available_sizes[0].size>>6)+1;
772 fprintf (stderr, "INFO: autodetected %s font size is %d\n", mName.c_str (), mHeight);
775 mCurrentX = mCurrentY = 0;
777 mGlyphs = std::vector <glyph> ();
778 mGlyphs.reserve (128);
780 mbLost = false;
783 void fontFT::loose (void)
785 mpTextures.clear ();
786 mGlyphs.clear ();
787 mGlyphMap.clear ();
788 if (mFace)
790 FT_Done_Face (mFace);
791 mFace = NULL;
793 if (mLibrary)
795 FT_Done_FreeType (mLibrary);
796 mLibrary = NULL;
799 mbLost = true;
802 void fontFT::drawTextString (const char *text, int sx, int sy, unsigned long color) const
804 drawTextStringEx (text, -1, sx, sy, color, 0);
807 void fontFT::drawTextStringEx (const char *text, int size, int sx_, int sy, unsigned long color, float spacing, bool setProj) const
809 int sz;
810 char clr = 0;
811 int clrstackdepth = 0;
812 char clrstack[100] = { 0 };
814 unsigned long colortable[] = {
815 color,
816 0xffff0000,
817 0xff00ff00,
818 0xff0000ff,
819 0xffffff00,
820 0xff00ffff,
821 0xffff00ff,
822 0xffffffff,
823 0xff000000,
824 0xff000000
827 // this will tell us size IN BYTES
828 if (size == -1)
829 sz = strlen (text);
830 else
831 sz = size;
833 if (0 == sz)
834 return;
836 float sx = (float)sx_;
838 // g_engine->getDrawUtil ()->fastDrawBegin ();
840 /* if (setProj)
842 matrix4 mProj;
843 matrix4 mIdent (true);
844 mProj.orthoOffCenterLH (0, g_engine->getViewport ()->getWidth (), 0, g_engine->getViewport ()->getHeight (), .001f, 1.f);
845 g_engine->getEffectParms ()->setMatrix (effectParm_WorldMatrix, mIdent);
846 g_engine->getEffectParms ()->setMatrix (effectParm_ViewMatrix, mIdent);
847 g_engine->getEffectParms ()->setMatrix (effectParm_ProjMatrix, mProj);
850 g_engine->getDrawUtil ()->allowTransforms (true);
852 int fontpage = -1;
854 const glyph *gprev = NULL;
855 const char *c = text;
856 int numchars = 0;
857 while (c - text < sz)
859 if (*c == 1)
861 // change color
862 c++;
863 if (*c == 0)
864 continue;
865 else if (*c == (char)-1)
867 // prev color
868 if (clrstackdepth > 0)
870 clrstackdepth--;
871 clr = clrstack[clrstackdepth];
873 c++;
874 continue;
876 else if (*c >= 1 && *c <= 10)
878 clrstackdepth++;
879 clr = *c - 1;
880 clrstack[clrstackdepth] = clr;
881 c++;
882 continue;
885 else if (*c == 2 || *c == 3)
887 // skip
888 c++;
889 if (0 == *c)
890 break;
891 c++;
892 continue;
894 else if (*c == '\t')
896 // make tab
897 c++;
898 sx += mGlyphs[0].advance[0] * 8;
899 continue;
901 else if (*c < 32 && *c > 0)
903 c++;
904 continue;
907 // get glyph
908 uint32 charcode;
909 int incr = 0;
910 if (*c > 0)
912 charcode = *c;
913 incr = 1;
915 else
917 charcode = u8_nextchar (c, &incr);
919 int gidx = getGlyphIdx (charcode);
920 const glyph *g = &mGlyphs[gidx];
922 int x = (int)floor (sx);
923 int y = sy;
925 if (gprev && 32 != charcode)
927 FT_Vector kern;
928 FT_Get_Kerning (mFace, gprev->ft_glyph_index, g->ft_glyph_index,
929 ft_kerning_default,
930 &kern);
931 x += kern.x >> 6;
932 sx += kern.x >> 6;
935 // lower left
936 x += g->origin[0];
937 y -= g->origin[1];
938 // convert to upper left
939 y -= (g->maxs[1] - g->mins[1]);
940 y += this->getTextHeight ();
942 if (g->texture != fontpage)
944 fontpage = g->texture;
945 mpTextures[fontpage]->bind (0);
947 g_engine->getDrawUtil ()->drawPic (x, y, g->maxs[0] - g->mins[0], g->maxs[1] - g->mins[1], (float)g->mins[0] / (float)FontTextureSize, (float)g->mins[1] / (float)FontTextureSize, (float)g->maxs[0] / (float)FontTextureSize, (float)g->maxs[1] / (float)FontTextureSize, colortable[clr], true);
949 sx = sx + g->advance[0];
950 if (32 == *c)
951 sx += spacing;
952 gprev = g;
953 c += incr;
954 numchars++;
958 int fontFT::getLineGap (void) const
960 return mHeight - ( (mFace->size->metrics.ascender + mFace->size->metrics.descender) >> 6);
963 int fontFT::getTextHeight (void) const
965 return mHeight;
968 char* fontFT::preformat (const char *text
969 , int wx, int wy, int ww, int wh // page window
970 , unsigned long format_flags) const // flags
972 if (NULL == text)
973 return NULL;
974 const char *start;
976 // determine number of '\n's
977 int numindents = 1;
978 for (start = text; *start; start++)
980 if (*start == '\\' && * (start+1) == 'n')
981 numindents++;
984 size_t str_len = strlen (text)+1+numindents * 4;
985 char *formatted = new char[str_len];
986 const char *end = text;
987 char *fptr = formatted;
989 // format whole text
990 char *indent_ptr = fptr;
991 indent_ptr[0] = 2;
992 indent_ptr[1] = 1;
993 indent_ptr[2] = 3;
994 indent_ptr[3] = 1;
995 fptr = indent_ptr + 4;
996 for (;;)
998 if (0 == *end)
1000 *fptr = *end;
1001 break;
1004 if (*end < 32 && *end > 0)
1006 end++;
1007 continue;
1010 if (*end == '\\')
1012 end++;
1013 if (*end == '0')
1015 *fptr = 0;
1016 break;
1018 else if (*end == '\\') // pass '\' char
1020 *fptr = *end;
1021 fptr++;
1022 end++;
1024 else if (*end == 'n')
1026 // newline
1027 *fptr = '\n';
1028 end++;
1029 fptr++;
1030 indent_ptr = fptr;
1031 indent_ptr[0] = 2;
1032 indent_ptr[1] = 1;
1033 fptr += 2;
1035 else if (*end == 't')
1037 *fptr = '\t';
1038 end++;
1039 fptr++;
1041 else if (*end == 'c')
1043 end++;
1044 if (*end == 0)
1046 *fptr = 0;
1047 break;
1049 *fptr = 1; // color tag
1050 fptr++;
1051 if (*end == '-')
1052 *fptr = -1;
1053 else if (*end >= '0' && *end <= '9')
1054 *fptr = (*end) - '0' + 1; // FIXME: is it the right way?
1055 else // typo
1057 * (fptr-1) = '\\';
1058 *fptr = 'c';
1059 fptr++;
1060 *fptr = *end;
1062 fptr++;
1063 end++;
1065 else if (*end == 'i') // newline indentation
1067 end++;
1068 if (*end == 0)
1070 *fptr = 0;
1071 break;
1074 indent_ptr[1] = *end - '0' + 1;
1075 end++;
1077 else if (*end == 'I') // newline indentation
1079 end++;
1080 if (*end == 0)
1082 *fptr = 0;
1083 break;
1086 indent_ptr[3] = *end - '0' + 1;
1087 end++;
1090 /* else if (*end < 0)
1092 *fptr = L'?';
1093 fptr++;
1094 end++;
1096 else
1098 *fptr = *end;
1099 fptr++;
1100 end++;
1104 assert (fptr - formatted + 1 <= (int)str_len);
1105 return formatted;
1108 int fontFT::getLineCnt (const char *text
1109 , int wx, int wy, int ww, int wh // page window
1110 , unsigned long flags) const // flags
1112 if (NULL == text)
1113 return 0;
1114 // do precalc on numlines
1115 const char *start = text;
1116 int numlines = 0;
1117 bool i1 = true;
1118 bool i2 = false;
1119 int ix = 0, ix2 = 0;
1120 for (;;)
1122 // break on terminating zero
1123 if (0 == *start)
1125 break;
1127 int wordcnt = 0;
1128 int w = 0;
1130 while (*start == 32)
1131 start++;
1133 const char *end = start;
1134 const char *lastword = start;
1135 int lastword_w = 0;
1136 int numchars = 0;
1138 // calculate a line
1139 for (;;)
1141 if (0 == *end)
1142 break;
1143 else if ('\n' == *end)
1145 end++;
1146 i1 = true;
1147 i2 = false;
1148 break;
1150 else if (1 == *end)
1152 end++;
1153 if (0 == *end)
1154 break;
1155 end++;
1156 continue;
1158 else if (2 == *end)
1160 end++;
1161 if (0 == *end)
1162 break;
1163 ix = *end-1;
1164 end++;
1165 continue;
1167 else if (3 == *end)
1169 end++;
1170 if (0 == *end)
1171 break;
1172 ix2 = *end-1;
1173 end++;
1174 continue;
1176 else if (32 > *end)
1178 end++;
1179 continue;
1182 // get glyph
1183 int gidx = getGlyphIdx (*end);
1184 const glyph *g = &mGlyphs[gidx];
1185 if (w + g->advance[0] >= ww && (flags & wordWrap))
1187 if (0 == wordcnt)
1189 // window is less than word width
1191 else
1193 end = lastword;
1194 w = lastword_w;
1197 if (*start == 32)
1199 start++;
1201 if (* (end-1) == 32)
1203 end--;
1206 break;
1209 if (*end == 32)
1211 lastword = end;
1212 lastword_w = w;
1213 wordcnt++;
1215 if (0 == numchars)
1217 if (i1)
1218 w += ix * mGlyphs[0].advance[0];
1219 else if (i2)
1220 w += ix2 * mGlyphs[0].advance[0];
1222 w += g->advance[0];
1223 numchars++;
1225 end++;
1227 numlines++;
1228 start = end;
1230 return numlines;
1233 void fontFT::freePreformatted (char *text) const
1235 if (text)
1236 delete[] text;
1239 int fontFT::getNumberOfLines (const char *text
1240 , int wx, int wy, int ww, int wh // page window
1241 , unsigned long flags) const // flags
1243 if (NULL == text)
1244 return 0;
1245 char *formatted = preformat (text, wx, wy, ww, wh, flags);
1246 int lines = getLineCnt (formatted, wx, wy, ww, wh, flags);
1247 freePreformatted (formatted);
1248 return lines;
1251 void fontFT::drawTextStringPreformatted (const char *formatted, int sx, int sy, int sw, int sh, int wx, int wy, int ww, int wh, unsigned long color, unsigned long flags) const
1253 bool vp_changed = false;
1254 int H = g_engine->getViewport ()->getHeight ();
1255 matrix4 mProj;
1256 if (flags&useClipRect)
1258 mProj.orthoOffCenterLH (sx, sx + sw, H - (sy + sh), H - sy, .001f, 1.f);
1261 const char *start = formatted;
1262 int ix = 0;
1263 int ix2 = 0;
1265 bool i1 = true;
1266 bool i2 = false;
1268 int line_gap = 0;
1269 if (flags & remove1stLineGap)
1271 line_gap = getLineGap ();
1274 int numlines = 0;
1275 if ( (flags & vAlignBottom) || (flags&vAlignCenter))
1277 numlines = getLineCnt (formatted, wx, wy, ww, wh, flags);
1278 if (flags & vAlignBottom)
1279 wy = wy + wh - (numlines * getTextHeight () - line_gap) - line_gap;
1280 else if (flags&vAlignCenter)
1281 wy = wy + wh / 2 - (numlines * getTextHeight () - line_gap) / 2 - line_gap;
1283 else
1284 wy -= line_gap;
1286 start = formatted;
1287 ix = 0;
1288 ix2 = 0;
1290 i1 = true;
1291 i2 = false;
1292 int nline = 0;
1293 for (;;)
1295 // break on terminating zero
1296 if (0 == *start)
1297 break;
1298 int wordcnt = 0;
1299 int w = 0;
1301 while (*start == 32)
1302 start++;
1304 const char *end = start;
1305 const char *lastword = start;
1306 int lastword_w = 0;
1307 int numchars = 0;
1309 // calculate a line
1310 int prevchar = -1;
1311 int previncr = 0;
1312 for (;;)
1314 if (0 == *end)
1315 break;
1316 else if ('\n' == *end)
1318 end++;
1319 i1 = true;
1320 i2 = false;
1321 break;
1323 else if (1 == *end)
1325 end++;
1326 if (0 == *end)
1327 break;
1328 end++;
1329 continue;
1331 else if (2 == *end)
1333 end++;
1334 if (0 == *end)
1335 break;
1336 if (i1)
1337 w -= ix * mGlyphs[0].advance[0];
1338 ix = *end-1;
1339 if (i1)
1340 w += ix * mGlyphs[0].advance[0];
1341 end++;
1342 continue;
1344 else if (3 == *end)
1346 end++;
1347 if (0 == *end)
1348 break;
1349 if (i2)
1350 w -= ix2 * mGlyphs[0].advance[0];
1351 ix2 = *end-1;
1352 if (i2)
1353 w += ix2 * mGlyphs[0].advance[0];
1354 end++;
1355 continue;
1357 else if (32 > *end)
1359 end++;
1360 continue;
1363 // get glyph
1364 uint32 charcode = 0;
1365 int incr = 0;
1366 if (*end > 0)
1368 charcode = *end;
1369 incr = 1;
1371 else
1373 charcode = u8_nextchar (end, &incr);
1374 fprintf (stderr, "INFO: printing char %d, incr=%d\n", charcode, incr);
1376 int gidx = getGlyphIdx (charcode);
1377 const glyph *g = &mGlyphs[gidx];
1378 if (w + g->advance[0] >= ww && (flags & wordWrap))
1380 if (0 == wordcnt)
1382 // window is less than word width
1384 else
1386 end = lastword;
1387 w = lastword_w;
1390 if (*start == 32)
1392 start++;
1394 if (prevchar == 32)
1396 end -= previncr;
1399 break;
1402 if (charcode == 32)
1404 lastword = end;
1405 lastword_w = w;
1406 wordcnt++;
1408 if (0 == numchars)
1410 if (i1)
1411 w += ix * mGlyphs[0].advance[0];
1412 else if (i2)
1413 w += ix2 * mGlyphs[0].advance[0];
1415 w += g->advance[0];
1416 numchars++;
1417 prevchar = charcode;
1418 previncr = incr;
1419 end += incr;
1422 // assert (w <= ww);
1424 // ok, we have a line between a 'start' and 'end'
1425 // width is 'w' (in pixels)
1427 // do alignment (relative to page window)
1428 int x = wx;
1429 float spacing;
1431 // check if we've got a EOL
1432 const char *eol = end;
1433 while (*eol && *eol <= 32)
1435 if (*eol < 32)
1436 break;
1437 eol++;
1439 if (*eol > 32)
1440 eol = NULL;
1442 if (flags & hAlignLeft)
1444 // do nothing, it's aligned already
1445 x = wx;
1446 spacing = 0;
1448 else if (flags & hAlignRight)
1450 // make it right
1451 x = wx + ww - w;
1452 spacing = 0;
1454 else if (flags & hAlignCenter)
1456 // make it center
1457 x = wx + ww / 2 - w / 2;
1458 spacing = 0;
1460 else if (flags & hAlignStretch)
1462 // stretch -- calc number of pixels required to fill a line (float value?)
1464 x = wx;
1465 if (eol)
1466 spacing = 0;
1467 else
1468 spacing = wordcnt > 1 ? (float) (ww - w) / (wordcnt - 1) : 0;
1470 else // same as 'AlignLeft'
1472 x = wx;
1473 spacing = 0;
1476 int y = /*g_engine->getViewport ()->getHeight () - getTextHeight () - */wy;
1478 // render a line
1480 if (false == vp_changed && (flags & useClipRect))
1482 g_engine->getRenderer ()->setViewport (sx, sy, sw, sh);
1484 vp_changed = true;
1487 matrix4 mIdent (true);
1488 g_engine->getEffectParms ()->setMatrix (effectParm_WorldMatrix, mIdent);
1489 g_engine->getEffectParms ()->setMatrix (effectParm_ViewMatrix, mIdent);
1490 g_engine->getEffectParms ()->setMatrix (effectParm_ProjMatrix, mProj);
1492 drawTextStringEx (start, end-start
1493 , (i1 ? mGlyphs[0].advance[0] * ix : 0) + (i2 ? mGlyphs[0].advance[0] * ix2 : 0) + x
1495 , color
1496 , spacing
1497 , !vp_changed);
1499 nline++;
1500 i1 = false;
1501 i2 = true;
1503 start = end;
1504 wy += getTextHeight ();
1507 if (vp_changed)
1509 g_engine->getRenderer ()->setViewport (0, 0, g_engine->getViewport ()->getWidth (), g_engine->getViewport ()->getHeight ());
1513 void fontFT::drawTextStringFormatted (const char *text
1514 , int sx, int sy, int sw, int sh // display window
1515 , int wx, int wy, int ww, int wh // page window
1516 , unsigned long color // outer color
1517 , unsigned long flags) const // flags
1519 if (NULL == text)
1520 return;
1521 // NOTE: it is guaranteed that formatted line will be smaller
1522 // NOTE: in size than original, due to the fact that any
1523 // NOTE: formatting sequence will be converted into lesser
1524 // NOTE: or exactly the same number of string characters
1525 // NOTE: e.g.: \c0 = 0x0101, \n = 0x0a, \i5 = 0x0206
1526 // NOTE: though, each indented block is being prepended
1527 // NOTE: with 4 words of indentation parameters
1529 char *formatted = preformat (text, wx, wy, ww, wh, flags);
1530 drawTextStringPreformatted (formatted, sx, sy, sw, sh, wx, wy, ww, wh, color, flags);
1531 freePreformatted (formatted);
1535 void fontFT::getTextExtent (const char *text, size_t sz, int &sx, int &sy) const
1537 const char *c = text;
1538 sx = 0;
1539 while (c - text < (int)sz)
1541 // get glyph
1542 int incr = 0;
1543 wchar_t wc = u8_nextchar (c, &incr);
1544 const glyph *g = &mGlyphs[getGlyphIdx (wc)];
1545 sx = sx + g->advance[0];
1546 c+=incr;
1548 sy = getTextHeight ();