Fix warnings in CGUITTFont.cpp
[oweals/minetest.git] / src / cguittfont / CGUITTFont.cpp
1 /*
2    CGUITTFont FreeType class for Irrlicht
3    Copyright (c) 2009-2010 John Norman
4
5    This software is provided 'as-is', without any express or implied
6    warranty. In no event will the authors be held liable for any
7    damages arising from the use of this software.
8
9    Permission is granted to anyone to use this software for any
10    purpose, including commercial applications, and to alter it and
11    redistribute it freely, subject to the following restrictions:
12
13    1. The origin of this software must not be misrepresented; you
14       must not claim that you wrote the original software. If you use
15       this software in a product, an acknowledgment in the product
16       documentation would be appreciated but is not required.
17
18    2. Altered source versions must be plainly marked as such, and
19       must not be misrepresented as being the original software.
20
21    3. This notice may not be removed or altered from any source
22       distribution.
23
24    The original version of this class can be located at:
25    http://irrlicht.suckerfreegames.com/
26
27    John Norman
28    john@suckerfreegames.com
29 */
30
31 #include <irrlicht.h>
32 #include "CGUITTFont.h"
33
34 namespace irr
35 {
36 namespace gui
37 {
38
39 // Manages the FT_Face cache.
40 struct SGUITTFace : public virtual irr::IReferenceCounted
41 {
42         SGUITTFace() : face_buffer(0), face_buffer_size(0)
43         {
44                 memset((void*)&face, 0, sizeof(FT_Face));
45         }
46
47         ~SGUITTFace()
48         {
49                 FT_Done_Face(face);
50                 delete[] face_buffer;
51         }
52
53         FT_Face face;
54         FT_Byte* face_buffer;
55         FT_Long face_buffer_size;
56 };
57
58 // Static variables.
59 FT_Library CGUITTFont::c_library;
60 core::map<io::path, SGUITTFace*> CGUITTFont::c_faces;
61 bool CGUITTFont::c_libraryLoaded = false;
62 scene::IMesh* CGUITTFont::shared_plane_ptr_ = 0;
63 scene::SMesh CGUITTFont::shared_plane_;
64
65 //
66
67 video::IImage* SGUITTGlyph::createGlyphImage(const FT_Bitmap& bits, video::IVideoDriver* driver) const
68 {
69         // Determine what our texture size should be.
70         // Add 1 because textures are inclusive-exclusive.
71         core::dimension2du d(bits.width + 1, bits.rows + 1);
72         core::dimension2du texture_size;
73         //core::dimension2du texture_size(bits.width + 1, bits.rows + 1);
74
75         // Create and load our image now.
76         video::IImage* image = 0;
77         switch (bits.pixel_mode)
78         {
79                 case FT_PIXEL_MODE_MONO:
80                 {
81                         // Create a blank image and fill it with transparent pixels.
82                         texture_size = d.getOptimalSize(true, true);
83                         image = driver->createImage(video::ECF_A1R5G5B5, texture_size);
84                         image->fill(video::SColor(0, 255, 255, 255));
85
86                         // Load the monochrome data in.
87                         const u32 image_pitch = image->getPitch() / sizeof(u16);
88                         u16* image_data = (u16*)image->lock();
89                         u8* glyph_data = bits.buffer;
90                         for (int y = 0; y < bits.rows; ++y)
91                         {
92                                 u16* row = image_data;
93                                 for (int x = 0; x < bits.width; ++x)
94                                 {
95                                         // Monochrome bitmaps store 8 pixels per byte.  The left-most pixel is the bit 0x80.
96                                         // So, we go through the data each bit at a time.
97                                         if ((glyph_data[y * bits.pitch + (x / 8)] & (0x80 >> (x % 8))) != 0)
98                                                 *row = 0xFFFF;
99                                         ++row;
100                                 }
101                                 image_data += image_pitch;
102                         }
103                         image->unlock();
104                         break;
105                 }
106
107                 case FT_PIXEL_MODE_GRAY:
108                 {
109                         // Create our blank image.
110                         texture_size = d.getOptimalSize(!driver->queryFeature(video::EVDF_TEXTURE_NPOT), !driver->queryFeature(video::EVDF_TEXTURE_NSQUARE), true, 0);
111                         image = driver->createImage(video::ECF_A8R8G8B8, texture_size);
112                         image->fill(video::SColor(0, 255, 255, 255));
113
114                         // Load the grayscale data in.
115                         const float gray_count = static_cast<float>(bits.num_grays);
116                         const u32 image_pitch = image->getPitch() / sizeof(u32);
117                         u32* image_data = (u32*)image->lock();
118                         u8* glyph_data = bits.buffer;
119                         for (int y = 0; y < bits.rows; ++y)
120                         {
121                                 u8* row = glyph_data;
122                                 for (int x = 0; x < bits.width; ++x)
123                                 {
124                                         image_data[y * image_pitch + x] |= static_cast<u32>(255.0f * (static_cast<float>(*row++) / gray_count)) << 24;
125                                         //data[y * image_pitch + x] |= ((u32)(*bitsdata++) << 24);
126                                 }
127                                 glyph_data += bits.pitch;
128                         }
129                         image->unlock();
130                         break;
131                 }
132                 default:
133                         // TODO: error message?
134                         return 0;
135         }
136         return image;
137 }
138
139 void SGUITTGlyph::preload(u32 char_index, FT_Face face, video::IVideoDriver* driver, u32 font_size, const FT_Int32 loadFlags)
140 {
141         if (isLoaded) return;
142
143         // Set the size of the glyph.
144         FT_Set_Pixel_Sizes(face, 0, font_size);
145
146         // Attempt to load the glyph.
147         if (FT_Load_Glyph(face, char_index, loadFlags) != FT_Err_Ok)
148                 // TODO: error message?
149                 return;
150
151         FT_GlyphSlot glyph = face->glyph;
152         FT_Bitmap bits = glyph->bitmap;
153
154         // Setup the glyph information here:
155         advance = glyph->advance;
156         offset = core::vector2di(glyph->bitmap_left, glyph->bitmap_top);
157
158         // Try to get the last page with available slots.
159         CGUITTGlyphPage* page = parent->getLastGlyphPage();
160
161         // If we need to make a new page, do that now.
162         if (!page)
163         {
164                 page = parent->createGlyphPage(bits.pixel_mode);
165                 if (!page)
166                         // TODO: add error message?
167                         return;
168         }
169
170         glyph_page = parent->getLastGlyphPageIndex();
171         u32 texture_side_length = page->texture->getOriginalSize().Width;
172         core::vector2di page_position(
173                 (page->used_slots % (texture_side_length / font_size)) * font_size,
174                 (page->used_slots / (texture_side_length / font_size)) * font_size
175                 );
176         source_rect.UpperLeftCorner = page_position;
177         source_rect.LowerRightCorner = core::vector2di(page_position.X + bits.width, page_position.Y + bits.rows);
178
179         page->dirty = true;
180         ++page->used_slots;
181         --page->available_slots;
182
183         // We grab the glyph bitmap here so the data won't be removed when the next glyph is loaded.
184         surface = createGlyphImage(bits, driver);
185
186         // Set our glyph as loaded.
187         isLoaded = true;
188 }
189
190 void SGUITTGlyph::unload()
191 {
192         if (surface)
193         {
194                 surface->drop();
195                 surface = 0;
196         }
197         isLoaded = false;
198 }
199
200 //////////////////////
201
202 CGUITTFont* CGUITTFont::createTTFont(IGUIEnvironment *env, const io::path& filename, const u32 size, const bool antialias, const bool transparency, const u32 shadow, const u32 shadow_alpha)
203 {
204         if (!c_libraryLoaded)
205         {
206                 if (FT_Init_FreeType(&c_library))
207                         return 0;
208                 c_libraryLoaded = true;
209         }
210
211         CGUITTFont* font = new CGUITTFont(env);
212         bool ret = font->load(filename, size, antialias, transparency);
213         if (!ret)
214         {
215                 font->drop();
216                 return 0;
217         }
218
219         font->shadow_offset = shadow;
220         font->shadow_alpha = shadow_alpha;
221
222         return font;
223 }
224
225 CGUITTFont* CGUITTFont::createTTFont(IrrlichtDevice *device, const io::path& filename, const u32 size, const bool antialias, const bool transparency)
226 {
227         if (!c_libraryLoaded)
228         {
229                 if (FT_Init_FreeType(&c_library))
230                         return 0;
231                 c_libraryLoaded = true;
232         }
233
234         CGUITTFont* font = new CGUITTFont(device->getGUIEnvironment());
235         font->Device = device;
236         bool ret = font->load(filename, size, antialias, transparency);
237         if (!ret)
238         {
239                 font->drop();
240                 return 0;
241         }
242
243         return font;
244 }
245
246 CGUITTFont* CGUITTFont::create(IGUIEnvironment *env, const io::path& filename, const u32 size, const bool antialias, const bool transparency)
247 {
248         return CGUITTFont::createTTFont(env, filename, size, antialias, transparency);
249 }
250
251 CGUITTFont* CGUITTFont::create(IrrlichtDevice *device, const io::path& filename, const u32 size, const bool antialias, const bool transparency)
252 {
253         return CGUITTFont::createTTFont(device, filename, size, antialias, transparency);
254 }
255
256 //////////////////////
257
258 //! Constructor.
259 CGUITTFont::CGUITTFont(IGUIEnvironment *env)
260 : use_monochrome(false), use_transparency(true), use_hinting(true), use_auto_hinting(true),
261 batch_load_size(1), Device(0), Environment(env), Driver(0), GlobalKerningWidth(0), GlobalKerningHeight(0)
262 {
263         #ifdef _DEBUG
264         setDebugName("CGUITTFont");
265         #endif
266
267         if (Environment)
268         {
269                 // don't grab environment, to avoid circular references
270                 Driver = Environment->getVideoDriver();
271         }
272
273         if (Driver)
274                 Driver->grab();
275
276         setInvisibleCharacters(L" ");
277
278         // Glyphs aren't reference counted, so don't try to delete them when we free the array.
279         Glyphs.set_free_when_destroyed(false);
280 }
281
282 bool CGUITTFont::load(const io::path& filename, const u32 size, const bool antialias, const bool transparency)
283 {
284         // Some sanity checks.
285         if (Environment == 0 || Driver == 0) return false;
286         if (size == 0) return false;
287         if (filename.size() == 0) return false;
288
289         io::IFileSystem* filesystem = Environment->getFileSystem();
290         irr::ILogger* logger = (Device != 0 ? Device->getLogger() : 0);
291         this->size = size;
292         this->filename = filename;
293
294         // Update the font loading flags when the font is first loaded.
295         this->use_monochrome = !antialias;
296         this->use_transparency = transparency;
297         update_load_flags();
298
299         // Log.
300         if (logger)
301                 logger->log(L"CGUITTFont", core::stringw(core::stringw(L"Creating new font: ") + core::ustring(filename).toWCHAR_s() + L" " + core::stringc(size) + L"pt " + (antialias ? L"+antialias " : L"-antialias ") + (transparency ? L"+transparency" : L"-transparency")).c_str(), irr::ELL_INFORMATION);
302
303         // Grab the face.
304         SGUITTFace* face = 0;
305         core::map<io::path, SGUITTFace*>::Node* node = c_faces.find(filename);
306         if (node == 0)
307         {
308                 face = new SGUITTFace();
309                 c_faces.set(filename, face);
310
311                 if (filesystem)
312                 {
313                         // Read in the file data.
314                         io::IReadFile* file = filesystem->createAndOpenFile(filename);
315                         if (file == 0)
316                         {
317                                 if (logger) logger->log(L"CGUITTFont", L"Failed to open the file.", irr::ELL_INFORMATION);
318
319                                 c_faces.remove(filename);
320                                 delete face;
321                                 face = 0;
322                                 return false;
323                         }
324                         face->face_buffer = new FT_Byte[file->getSize()];
325                         file->read(face->face_buffer, file->getSize());
326                         face->face_buffer_size = file->getSize();
327                         file->drop();
328
329                         // Create the face.
330                         if (FT_New_Memory_Face(c_library, face->face_buffer, face->face_buffer_size, 0, &face->face))
331                         {
332                                 if (logger) logger->log(L"CGUITTFont", L"FT_New_Memory_Face failed.", irr::ELL_INFORMATION);
333
334                                 c_faces.remove(filename);
335                                 delete face;
336                                 face = 0;
337                                 return false;
338                         }
339                 }
340                 else
341                 {
342                         core::ustring converter(filename);
343                         if (FT_New_Face(c_library, reinterpret_cast<const char*>(converter.toUTF8_s().c_str()), 0, &face->face))
344                         {
345                                 if (logger) logger->log(L"CGUITTFont", L"FT_New_Face failed.", irr::ELL_INFORMATION);
346
347                                 c_faces.remove(filename);
348                                 delete face;
349                                 face = 0;
350                                 return false;
351                         }
352                 }
353         }
354         else
355         {
356                 // Using another instance of this face.
357                 face = node->getValue();
358                 face->grab();
359         }
360
361         // Store our face.
362         tt_face = face->face;
363
364         // Store font metrics.
365         FT_Set_Pixel_Sizes(tt_face, size, 0);
366         font_metrics = tt_face->size->metrics;
367
368         // Allocate our glyphs.
369         Glyphs.clear();
370         Glyphs.reallocate(tt_face->num_glyphs);
371         Glyphs.set_used(tt_face->num_glyphs);
372         for (FT_Long i = 0; i < tt_face->num_glyphs; ++i)
373         {
374                 Glyphs[i].isLoaded = false;
375                 Glyphs[i].glyph_page = 0;
376                 Glyphs[i].source_rect = core::recti();
377                 Glyphs[i].offset = core::vector2di();
378                 Glyphs[i].advance = FT_Vector();
379                 Glyphs[i].surface = 0;
380                 Glyphs[i].parent = this;
381         }
382
383         // Cache the first 127 ascii characters.
384         u32 old_size = batch_load_size;
385         batch_load_size = 127;
386         getGlyphIndexByChar((uchar32_t)0);
387         batch_load_size = old_size;
388
389         return true;
390 }
391
392 CGUITTFont::~CGUITTFont()
393 {
394         // Delete the glyphs and glyph pages.
395         reset_images();
396         CGUITTAssistDelete::Delete(Glyphs);
397         //Glyphs.clear();
398
399         // We aren't using this face anymore.
400         core::map<io::path, SGUITTFace*>::Node* n = c_faces.find(filename);
401         if (n)
402         {
403                 SGUITTFace* f = n->getValue();
404
405                 // Drop our face.  If this was the last face, the destructor will clean up.
406                 if (f->drop())
407                         c_faces.remove(filename);
408
409                 // If there are no more faces referenced by FreeType, clean up.
410                 if (c_faces.size() == 0)
411                 {
412                         FT_Done_FreeType(c_library);
413                         c_libraryLoaded = false;
414                 }
415         }
416
417         // Drop our driver now.
418         if (Driver)
419                 Driver->drop();
420 }
421
422 void CGUITTFont::reset_images()
423 {
424         // Delete the glyphs.
425         for (u32 i = 0; i != Glyphs.size(); ++i)
426                 Glyphs[i].unload();
427
428         // Unload the glyph pages from video memory.
429         for (u32 i = 0; i != Glyph_Pages.size(); ++i)
430                 delete Glyph_Pages[i];
431         Glyph_Pages.clear();
432
433         // Always update the internal FreeType loading flags after resetting.
434         update_load_flags();
435 }
436
437 void CGUITTFont::update_glyph_pages() const
438 {
439         for (u32 i = 0; i != Glyph_Pages.size(); ++i)
440         {
441                 if (Glyph_Pages[i]->dirty)
442                         Glyph_Pages[i]->updateTexture();
443         }
444 }
445
446 CGUITTGlyphPage* CGUITTFont::getLastGlyphPage() const
447 {
448         CGUITTGlyphPage* page = 0;
449         if (Glyph_Pages.empty())
450                 return 0;
451         else
452         {
453                 page = Glyph_Pages[getLastGlyphPageIndex()];
454                 if (page->available_slots == 0)
455                         page = 0;
456         }
457         return page;
458 }
459
460 CGUITTGlyphPage* CGUITTFont::createGlyphPage(const u8& pixel_mode)
461 {
462         CGUITTGlyphPage* page = 0;
463         
464         // Name of our page.
465         io::path name("TTFontGlyphPage_");
466         name += tt_face->family_name;
467         name += ".";
468         name += tt_face->style_name;
469         name += ".";
470         name += size;
471         name += "_";
472         name += Glyph_Pages.size(); // The newly created page will be at the end of the collection.
473
474         // Create the new page.
475         page = new CGUITTGlyphPage(Driver, name);
476
477         // Determine our maximum texture size.
478         // If we keep getting 0, set it to 1024x1024, as that number is pretty safe.
479         core::dimension2du max_texture_size = max_page_texture_size;
480         if (max_texture_size.Width == 0 || max_texture_size.Height == 0)
481                 max_texture_size = Driver->getMaxTextureSize();
482         if (max_texture_size.Width == 0 || max_texture_size.Height == 0)
483                 max_texture_size = core::dimension2du(1024, 1024);
484
485         // We want to try to put at least 144 glyphs on a single texture.
486         core::dimension2du page_texture_size;
487         if (size <= 21) page_texture_size = core::dimension2du(256, 256);
488         else if (size <= 42) page_texture_size = core::dimension2du(512, 512);
489         else if (size <= 84) page_texture_size = core::dimension2du(1024, 1024);
490         else if (size <= 168) page_texture_size = core::dimension2du(2048, 2048);
491         else page_texture_size = core::dimension2du(4096, 4096);
492
493         if (page_texture_size.Width > max_texture_size.Width || page_texture_size.Height > max_texture_size.Height)
494                 page_texture_size = max_texture_size;
495
496         if (!page->createPageTexture(pixel_mode, page_texture_size))
497                 // TODO: add error message?
498                 return 0;
499
500         if (page)
501         {
502                 // Determine the number of glyph slots on the page and add it to the list of pages.
503                 page->available_slots = (page_texture_size.Width / size) * (page_texture_size.Height / size);
504                 Glyph_Pages.push_back(page);
505         }
506         return page;
507 }
508
509 void CGUITTFont::setTransparency(const bool flag)
510 {
511         use_transparency = flag;
512         reset_images();
513 }
514
515 void CGUITTFont::setMonochrome(const bool flag)
516 {
517         use_monochrome = flag;
518         reset_images();
519 }
520
521 void CGUITTFont::setFontHinting(const bool enable, const bool enable_auto_hinting)
522 {
523         use_hinting = enable;
524         use_auto_hinting = enable_auto_hinting;
525         reset_images();
526 }
527
528 void CGUITTFont::draw(const core::stringw& text, const core::rect<s32>& position, video::SColor color, bool hcenter, bool vcenter, const core::rect<s32>* clip)
529 {
530         if (!Driver)
531                 return;
532
533         // Clear the glyph pages of their render information.
534         for (u32 i = 0; i < Glyph_Pages.size(); ++i)
535         {
536                 Glyph_Pages[i]->render_positions.clear();
537                 Glyph_Pages[i]->render_source_rects.clear();
538         }
539
540         // Set up some variables.
541         core::dimension2d<s32> textDimension;
542         core::position2d<s32> offset = position.UpperLeftCorner;
543
544         // Determine offset positions.
545         if (hcenter || vcenter)
546         {
547                 textDimension = getDimension(text.c_str());
548
549                 if (hcenter)
550                         offset.X = ((position.getWidth() - textDimension.Width) >> 1) + offset.X;
551
552                 if (vcenter)
553                         offset.Y = ((position.getHeight() - textDimension.Height) >> 1) + offset.Y;
554         }
555
556         // Convert to a unicode string.
557         core::ustring utext(text);
558
559         // Set up our render map.
560         core::map<u32, CGUITTGlyphPage*> Render_Map;
561
562         // Start parsing characters.
563         u32 n;
564         uchar32_t previousChar = 0;
565         core::ustring::const_iterator iter(utext);
566         while (!iter.atEnd())
567         {
568                 uchar32_t currentChar = *iter;
569                 n = getGlyphIndexByChar(currentChar);
570                 bool visible = (Invisible.findFirst(currentChar) == -1);
571                 bool lineBreak=false;
572                 if (currentChar == L'\r') // Mac or Windows breaks
573                 {
574                         lineBreak = true;
575                         if (*(iter + 1) == (uchar32_t)'\n')     // Windows line breaks.
576                                 currentChar = *(++iter);
577                 }
578                 else if (currentChar == (uchar32_t)'\n') // Unix breaks
579                 {
580                         lineBreak = true;
581                 }
582
583                 if (lineBreak)
584                 {
585                         previousChar = 0;
586                         offset.Y += font_metrics.height / 64;
587                         offset.X = position.UpperLeftCorner.X;
588
589                         if (hcenter)
590                                 offset.X += (position.getWidth() - textDimension.Width) >> 1;
591                         ++iter;
592                         continue;
593                 }
594
595                 if (n > 0 && visible)
596                 {
597                         // Calculate the glyph offset.
598                         s32 offx = Glyphs[n-1].offset.X;
599                         s32 offy = (font_metrics.ascender / 64) - Glyphs[n-1].offset.Y;
600
601                         // Apply kerning.
602                         core::vector2di k = getKerning(currentChar, previousChar);
603                         offset.X += k.X;
604                         offset.Y += k.Y;
605
606                         // Determine rendering information.
607                         SGUITTGlyph& glyph = Glyphs[n-1];
608                         CGUITTGlyphPage* const page = Glyph_Pages[glyph.glyph_page];
609                         page->render_positions.push_back(core::position2di(offset.X + offx, offset.Y + offy));
610                         page->render_source_rects.push_back(glyph.source_rect);
611                         Render_Map.set(glyph.glyph_page, page);
612                 }
613                 offset.X += getWidthFromCharacter(currentChar);
614
615                 previousChar = currentChar;
616                 ++iter;
617         }
618
619         // Draw now.
620         update_glyph_pages();
621         core::map<u32, CGUITTGlyphPage*>::Iterator j = Render_Map.getIterator();
622         while (!j.atEnd())
623         {
624                 core::map<u32, CGUITTGlyphPage*>::Node* n = j.getNode();
625                 j++;
626                 if (n == 0) continue;
627
628                 CGUITTGlyphPage* page = n->getValue();
629
630                 if (!use_transparency) color.color |= 0xff000000;
631
632                 if (shadow_offset) {
633                         for (size_t i = 0; i < page->render_positions.size(); ++i)
634                                 page->render_positions[i] += core::vector2di(shadow_offset, shadow_offset);
635                         Driver->draw2DImageBatch(page->texture, page->render_positions, page->render_source_rects, clip, video::SColor(shadow_alpha,0,0,0), true);
636                         for (size_t i = 0; i < page->render_positions.size(); ++i)
637                                 page->render_positions[i] -= core::vector2di(shadow_offset, shadow_offset);
638                 }
639                 Driver->draw2DImageBatch(page->texture, page->render_positions, page->render_source_rects, clip, color, true);
640         }
641 }
642
643 core::dimension2d<u32> CGUITTFont::getCharDimension(const wchar_t ch) const
644 {
645         return core::dimension2d<u32>(getWidthFromCharacter(ch), getHeightFromCharacter(ch));
646 }
647
648 core::dimension2d<u32> CGUITTFont::getDimension(const wchar_t* text) const
649 {
650         return getDimension(core::ustring(text));
651 }
652
653 core::dimension2d<u32> CGUITTFont::getDimension(const core::ustring& text) const
654 {
655         // Get the maximum font height.  Unfortunately, we have to do this hack as
656         // Irrlicht will draw things wrong.  In FreeType, the font size is the
657         // maximum size for a single glyph, but that glyph may hang "under" the
658         // draw line, increasing the total font height to beyond the set size.
659         // Irrlicht does not understand this concept when drawing fonts.  Also, I
660         // add +1 to give it a 1 pixel blank border.  This makes things like
661         // tooltips look nicer.
662         s32 test1 = getHeightFromCharacter((uchar32_t)'g') + 1;
663         s32 test2 = getHeightFromCharacter((uchar32_t)'j') + 1;
664         s32 test3 = getHeightFromCharacter((uchar32_t)'_') + 1;
665         s32 max_font_height = core::max_(test1, core::max_(test2, test3));
666
667         core::dimension2d<u32> text_dimension(0, max_font_height);
668         core::dimension2d<u32> line(0, max_font_height);
669
670         uchar32_t previousChar = 0;
671         core::ustring::const_iterator iter = text.begin();
672         for (; !iter.atEnd(); ++iter)
673         {
674                 uchar32_t p = *iter;
675                 bool lineBreak = false;
676                 if (p == '\r')  // Mac or Windows line breaks.
677                 {
678                         lineBreak = true;
679                         if (*(iter + 1) == '\n')
680                         {
681                                 ++iter;
682                                 p = *iter;
683                         }
684                 }
685                 else if (p == '\n')     // Unix line breaks.
686                 {
687                         lineBreak = true;
688                 }
689
690                 // Kerning.
691                 core::vector2di k = getKerning(p, previousChar);
692                 line.Width += k.X;
693                 previousChar = p;
694
695                 // Check for linebreak.
696                 if (lineBreak)
697                 {
698                         previousChar = 0;
699                         text_dimension.Height += line.Height;
700                         if (text_dimension.Width < line.Width)
701                                 text_dimension.Width = line.Width;
702                         line.Width = 0;
703                         line.Height = max_font_height;
704                         continue;
705                 }
706                 line.Width += getWidthFromCharacter(p);
707         }
708         if (text_dimension.Width < line.Width)
709                 text_dimension.Width = line.Width;
710
711         return text_dimension;
712 }
713
714 inline u32 CGUITTFont::getWidthFromCharacter(wchar_t c) const
715 {
716         return getWidthFromCharacter((uchar32_t)c);
717 }
718
719 inline u32 CGUITTFont::getWidthFromCharacter(uchar32_t c) const
720 {
721         // Set the size of the face.
722         // This is because we cache faces and the face may have been set to a different size.
723         //FT_Set_Pixel_Sizes(tt_face, 0, size);
724
725         u32 n = getGlyphIndexByChar(c);
726         if (n > 0)
727         {
728                 int w = Glyphs[n-1].advance.x / 64;
729                 return w;
730         }
731         if (c >= 0x2000)
732                 return (font_metrics.ascender / 64);
733         else return (font_metrics.ascender / 64) / 2;
734 }
735
736 inline u32 CGUITTFont::getHeightFromCharacter(wchar_t c) const
737 {
738         return getHeightFromCharacter((uchar32_t)c);
739 }
740
741 inline u32 CGUITTFont::getHeightFromCharacter(uchar32_t c) const
742 {
743         // Set the size of the face.
744         // This is because we cache faces and the face may have been set to a different size.
745         //FT_Set_Pixel_Sizes(tt_face, 0, size);
746
747         u32 n = getGlyphIndexByChar(c);
748         if (n > 0)
749         {
750                 // Grab the true height of the character, taking into account underhanging glyphs.
751                 s32 height = (font_metrics.ascender / 64) - Glyphs[n-1].offset.Y + Glyphs[n-1].source_rect.getHeight();
752                 return height;
753         }
754         if (c >= 0x2000)
755                 return (font_metrics.ascender / 64);
756         else return (font_metrics.ascender / 64) / 2;
757 }
758
759 u32 CGUITTFont::getGlyphIndexByChar(wchar_t c) const
760 {
761         return getGlyphIndexByChar((uchar32_t)c);
762 }
763
764 u32 CGUITTFont::getGlyphIndexByChar(uchar32_t c) const
765 {
766         // Get the glyph.
767         u32 glyph = FT_Get_Char_Index(tt_face, c);
768
769         // Check for a valid glyph.  If it is invalid, attempt to use the replacement character.
770         if (glyph == 0)
771                 glyph = FT_Get_Char_Index(tt_face, core::unicode::UTF_REPLACEMENT_CHARACTER);
772
773         // If our glyph is already loaded, don't bother doing any batch loading code.
774         if (glyph != 0 && Glyphs[glyph - 1].isLoaded)
775                 return glyph;
776
777         // Determine our batch loading positions.
778         u32 half_size = (batch_load_size / 2);
779         u32 start_pos = 0;
780         if (c > half_size) start_pos = c - half_size;
781         u32 end_pos = start_pos + batch_load_size;
782
783         // Load all our characters.
784         do
785         {
786                 // Get the character we are going to load.
787                 u32 char_index = FT_Get_Char_Index(tt_face, start_pos);
788
789                 // If the glyph hasn't been loaded yet, do it now.
790                 if (char_index)
791                 {
792                         SGUITTGlyph& glyph = Glyphs[char_index - 1];
793                         if (!glyph.isLoaded)
794                         {
795                                 glyph.preload(char_index, tt_face, Driver, size, load_flags);
796                                 Glyph_Pages[glyph.glyph_page]->pushGlyphToBePaged(&glyph);
797                         }
798                 }
799         }
800         while (++start_pos < end_pos);
801
802         // Return our original character.
803         return glyph;
804 }
805
806 s32 CGUITTFont::getCharacterFromPos(const wchar_t* text, s32 pixel_x) const
807 {
808         return getCharacterFromPos(core::ustring(text), pixel_x);
809 }
810
811 s32 CGUITTFont::getCharacterFromPos(const core::ustring& text, s32 pixel_x) const
812 {
813         s32 x = 0;
814         //s32 idx = 0;
815
816         u32 character = 0;
817         uchar32_t previousChar = 0;
818         core::ustring::const_iterator iter = text.begin();
819         while (!iter.atEnd())
820         {
821                 uchar32_t c = *iter;
822                 x += getWidthFromCharacter(c);
823
824                 // Kerning.
825                 core::vector2di k = getKerning(c, previousChar);
826                 x += k.X;
827
828                 if (x >= pixel_x)
829                         return character;
830
831                 previousChar = c;
832                 ++iter;
833                 ++character;
834         }
835
836         return -1;
837 }
838
839 void CGUITTFont::setKerningWidth(s32 kerning)
840 {
841         GlobalKerningWidth = kerning;
842 }
843
844 void CGUITTFont::setKerningHeight(s32 kerning)
845 {
846         GlobalKerningHeight = kerning;
847 }
848
849 s32 CGUITTFont::getKerningWidth(const wchar_t* thisLetter, const wchar_t* previousLetter) const
850 {
851         if (tt_face == 0)
852                 return GlobalKerningWidth;
853         if (thisLetter == 0 || previousLetter == 0)
854                 return 0;
855
856         return getKerningWidth((uchar32_t)*thisLetter, (uchar32_t)*previousLetter);
857 }
858
859 s32 CGUITTFont::getKerningWidth(const uchar32_t thisLetter, const uchar32_t previousLetter) const
860 {
861         // Return only the kerning width.
862         return getKerning(thisLetter, previousLetter).X;
863 }
864
865 s32 CGUITTFont::getKerningHeight() const
866 {
867         // FreeType 2 currently doesn't return any height kerning information.
868         return GlobalKerningHeight;
869 }
870
871 core::vector2di CGUITTFont::getKerning(const wchar_t thisLetter, const wchar_t previousLetter) const
872 {
873         return getKerning((uchar32_t)thisLetter, (uchar32_t)previousLetter);
874 }
875
876 core::vector2di CGUITTFont::getKerning(const uchar32_t thisLetter, const uchar32_t previousLetter) const
877 {
878         if (tt_face == 0 || thisLetter == 0 || previousLetter == 0)
879                 return core::vector2di();
880
881         // Set the size of the face.
882         // This is because we cache faces and the face may have been set to a different size.
883         FT_Set_Pixel_Sizes(tt_face, 0, size);
884
885         core::vector2di ret(GlobalKerningWidth, GlobalKerningHeight);
886
887         // If we don't have kerning, no point in continuing.
888         if (!FT_HAS_KERNING(tt_face))
889                 return ret;
890
891         // Get the kerning information.
892         FT_Vector v;
893         FT_Get_Kerning(tt_face, getGlyphIndexByChar(previousLetter), getGlyphIndexByChar(thisLetter), FT_KERNING_DEFAULT, &v);
894
895         // If we have a scalable font, the return value will be in font points.
896         if (FT_IS_SCALABLE(tt_face))
897         {
898                 // Font points, so divide by 64.
899                 ret.X += (v.x / 64);
900                 ret.Y += (v.y / 64);
901         }
902         else
903         {
904                 // Pixel units.
905                 ret.X += v.x;
906                 ret.Y += v.y;
907         }
908         return ret;
909 }
910
911 void CGUITTFont::setInvisibleCharacters(const wchar_t *s)
912 {
913         core::ustring us(s);
914         Invisible = us;
915 }
916
917 void CGUITTFont::setInvisibleCharacters(const core::ustring& s)
918 {
919         Invisible = s;
920 }
921
922 video::IImage* CGUITTFont::createTextureFromChar(const uchar32_t& ch)
923 {
924         u32 n = getGlyphIndexByChar(ch);
925         const SGUITTGlyph& glyph = Glyphs[n-1];
926         CGUITTGlyphPage* page = Glyph_Pages[glyph.glyph_page];
927
928         if (page->dirty)
929                 page->updateTexture();
930
931         video::ITexture* tex = page->texture;
932
933         // Acquire a read-only lock of the corresponding page texture.
934         #if IRRLICHT_VERSION_MAJOR==1 && IRRLICHT_VERSION_MINOR>=8
935         void* ptr = tex->lock(video::ETLM_READ_ONLY);
936         #else
937         void* ptr = tex->lock(true);
938         #endif
939
940         video::ECOLOR_FORMAT format = tex->getColorFormat();
941         core::dimension2du tex_size = tex->getOriginalSize();
942         video::IImage* pageholder = Driver->createImageFromData(format, tex_size, ptr, true, false);
943
944         // Copy the image data out of the page texture.
945         core::dimension2du glyph_size(glyph.source_rect.getSize());
946         video::IImage* image = Driver->createImage(format, glyph_size);
947         pageholder->copyTo(image, core::position2di(0, 0), glyph.source_rect);
948
949         tex->unlock();
950         return image;
951 }
952
953 video::ITexture* CGUITTFont::getPageTextureByIndex(const u32& page_index) const
954 {
955         if (page_index < Glyph_Pages.size())
956                 return Glyph_Pages[page_index]->texture;
957         else
958                 return 0;
959 }
960
961 void CGUITTFont::createSharedPlane()
962 {
963         /*
964                 2___3
965                 |  /|
966                 | / |   <-- plane mesh is like this, point 2 is (0,0), point 0 is (0, -1)
967                 |/  |   <-- the texture coords of point 2 is (0,0, point 0 is (0, 1)
968                 0---1
969         */
970
971         using namespace core;
972         using namespace video;
973         using namespace scene;
974         S3DVertex vertices[4];
975         u16 indices[6] = {0,2,3,3,1,0};
976         vertices[0] = S3DVertex(vector3df(0,-1,0), vector3df(0,0,-1), SColor(255,255,255,255), vector2df(0,1));
977         vertices[1] = S3DVertex(vector3df(1,-1,0), vector3df(0,0,-1), SColor(255,255,255,255), vector2df(1,1));
978         vertices[2] = S3DVertex(vector3df(0, 0,0), vector3df(0,0,-1), SColor(255,255,255,255), vector2df(0,0));
979         vertices[3] = S3DVertex(vector3df(1, 0,0), vector3df(0,0,-1), SColor(255,255,255,255), vector2df(1,0));
980
981         SMeshBuffer* buf = new SMeshBuffer();
982         buf->append(vertices, 4, indices, 6);
983
984         shared_plane_.addMeshBuffer( buf );
985
986         shared_plane_ptr_ = &shared_plane_;
987         buf->drop(); //the addMeshBuffer method will grab it, so we can drop this ptr.
988 }
989
990 core::dimension2d<u32> CGUITTFont::getDimensionUntilEndOfLine(const wchar_t* p) const
991 {
992         core::stringw s;
993         for (const wchar_t* temp = p; temp && *temp != '\0' && *temp != L'\r' && *temp != L'\n'; ++temp )
994                 s.append(*temp);
995
996         return getDimension(s.c_str());
997 }
998
999 core::array<scene::ISceneNode*> CGUITTFont::addTextSceneNode(const wchar_t* text, scene::ISceneManager* smgr, scene::ISceneNode* parent, const video::SColor& color, bool center)
1000 {
1001         using namespace core;
1002         using namespace video;
1003         using namespace scene;
1004
1005         array<scene::ISceneNode*> container;
1006
1007         if (!Driver || !smgr) return container;
1008         if (!parent)
1009                 parent = smgr->addEmptySceneNode(smgr->getRootSceneNode(), -1);
1010         // if you don't specify parent, then we add a empty node attached to the root node
1011         // this is generally undesirable.
1012
1013         if (!shared_plane_ptr_) //this points to a static mesh that contains the plane
1014                 createSharedPlane(); //if it's not initialized, we create one.
1015
1016         dimension2d<s32> text_size(getDimension(text)); //convert from unsigned to signed.
1017         vector3df start_point(0, 0, 0), offset;
1018
1019         /** NOTICE:
1020                 Because we are considering adding texts into 3D world, all Y axis vectors are inverted.
1021         **/
1022
1023         // There's currently no "vertical center" concept when you apply text scene node to the 3D world.
1024         if (center)
1025         {
1026                 offset.X = start_point.X = -text_size.Width / 2.f;
1027                 offset.Y = start_point.Y = +text_size.Height/ 2.f;
1028                 offset.X += (text_size.Width - getDimensionUntilEndOfLine(text).Width) >> 1;
1029         }
1030
1031         // the default font material
1032         SMaterial mat;
1033         mat.setFlag(video::EMF_LIGHTING, true);
1034         mat.setFlag(video::EMF_ZWRITE_ENABLE, false);
1035         mat.setFlag(video::EMF_NORMALIZE_NORMALS, true);
1036         mat.ColorMaterial = video::ECM_NONE;
1037         mat.MaterialType = use_transparency ? video::EMT_TRANSPARENT_ALPHA_CHANNEL : video::EMT_SOLID;
1038         mat.MaterialTypeParam = 0.01f;
1039         mat.DiffuseColor = color;
1040
1041         wchar_t current_char = 0, previous_char = 0;
1042         u32 n = 0;
1043
1044         array<u32> glyph_indices;
1045
1046         while (*text)
1047         {
1048                 current_char = *text;
1049                 bool line_break=false;
1050                 if (current_char == L'\r') // Mac or Windows breaks
1051                 {
1052                         line_break = true;
1053                         if (*(text + 1) == L'\n') // Windows line breaks.
1054                                 current_char = *(++text);
1055                 }
1056                 else if (current_char == L'\n') // Unix breaks
1057                 {
1058                         line_break = true;
1059                 }
1060
1061                 if (line_break)
1062                 {
1063                         previous_char = 0;
1064                         offset.Y -= tt_face->size->metrics.ascender / 64;
1065                         offset.X = start_point.X;
1066                         if (center)
1067                                 offset.X += (text_size.Width - getDimensionUntilEndOfLine(text+1).Width) >> 1;
1068                         ++text;
1069                 }
1070                 else
1071                 {
1072                         n = getGlyphIndexByChar(current_char);
1073                         if (n > 0)
1074                         {
1075                                 glyph_indices.push_back( n );
1076
1077                                 // Store glyph size and offset informations.
1078                                 SGUITTGlyph const& glyph = Glyphs[n-1];
1079                                 u32 texw = glyph.source_rect.getWidth();
1080                                 u32 texh = glyph.source_rect.getHeight();
1081                                 s32 offx = glyph.offset.X;
1082                                 s32 offy = (font_metrics.ascender / 64) - glyph.offset.Y;
1083
1084                                 // Apply kerning.
1085                                 vector2di k = getKerning(current_char, previous_char);
1086                                 offset.X += k.X;
1087                                 offset.Y += k.Y;
1088
1089                                 vector3df current_pos(offset.X + offx, offset.Y - offy, 0);
1090                                 dimension2d<u32> letter_size = dimension2d<u32>(texw, texh);
1091
1092                                 // Now we copy planes corresponding to the letter size.
1093                                 IMeshManipulator* mani = smgr->getMeshManipulator();
1094                                 IMesh* meshcopy = mani->createMeshCopy(shared_plane_ptr_);
1095                                 #if IRRLICHT_VERSION_MAJOR==1 && IRRLICHT_VERSION_MINOR>=8
1096                                 mani->scale(meshcopy, vector3df((f32)letter_size.Width, (f32)letter_size.Height, 1));
1097                                 #else
1098                                 mani->scaleMesh(meshcopy, vector3df((f32)letter_size.Width, (f32)letter_size.Height, 1));
1099                                 #endif
1100
1101                                 ISceneNode* current_node = smgr->addMeshSceneNode(meshcopy, parent, -1, current_pos);
1102                                 meshcopy->drop();
1103
1104                                 current_node->getMaterial(0) = mat;
1105                                 current_node->setAutomaticCulling(EAC_OFF);
1106                                 current_node->setIsDebugObject(true);  //so the picking won't have any effect on individual letter
1107                                 //current_node->setDebugDataVisible(EDS_BBOX); //de-comment this when debugging
1108
1109                                 container.push_back(current_node);
1110                         }
1111                         offset.X += getWidthFromCharacter(current_char);
1112                         previous_char = current_char;
1113                         ++text;
1114                 }
1115         }
1116
1117         update_glyph_pages();
1118         //only after we update the textures can we use the glyph page textures.
1119
1120         for (u32 i = 0; i < glyph_indices.size(); ++i)
1121         {
1122                 u32 n = glyph_indices[i];
1123                 SGUITTGlyph const& glyph = Glyphs[n-1];
1124                 ITexture* current_tex = Glyph_Pages[glyph.glyph_page]->texture;
1125                 f32 page_texture_size = (f32)current_tex->getSize().Width;
1126                 //Now we calculate the UV position according to the texture size and the source rect.
1127                 //
1128                 //  2___3
1129                 //  |  /|
1130                 //  | / |       <-- plane mesh is like this, point 2 is (0,0), point 0 is (0, -1)
1131                 //  |/  |       <-- the texture coords of point 2 is (0,0, point 0 is (0, 1)
1132                 //  0---1
1133                 //
1134                 f32 u1 = glyph.source_rect.UpperLeftCorner.X / page_texture_size;
1135                 f32 u2 = u1 + (glyph.source_rect.getWidth() / page_texture_size);
1136                 f32 v1 = glyph.source_rect.UpperLeftCorner.Y / page_texture_size;
1137                 f32 v2 = v1 + (glyph.source_rect.getHeight() / page_texture_size);
1138
1139                 //we can be quite sure that this is IMeshSceneNode, because we just added them in the above loop.
1140                 IMeshSceneNode* node = static_cast<IMeshSceneNode*>(container[i]);
1141
1142                 S3DVertex* pv = static_cast<S3DVertex*>(node->getMesh()->getMeshBuffer(0)->getVertices());
1143                 //pv[0].TCoords.Y = pv[1].TCoords.Y = (letter_size.Height - 1) / static_cast<f32>(letter_size.Height);
1144                 //pv[1].TCoords.X = pv[3].TCoords.X = (letter_size.Width - 1)  / static_cast<f32>(letter_size.Width);
1145                 pv[0].TCoords = vector2df(u1, v2);
1146                 pv[1].TCoords = vector2df(u2, v2);
1147                 pv[2].TCoords = vector2df(u1, v1);
1148                 pv[3].TCoords = vector2df(u2, v1);
1149
1150                 container[i]->getMaterial(0).setTexture(0, current_tex);
1151         }
1152
1153         return container;
1154 }
1155
1156 } // end namespace gui
1157 } // end namespace irr