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