]> git.lizzy.rs Git - dragonfireclient.git/blob - src/irrlicht_changes/CGUITTFont.cpp
Irrlicht support code maintenance
[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, color, hcenter, vcenter, clip);
551 }
552
553 void CGUITTFont::draw(const EnrichedString &text, const core::rect<s32>& position, video::SColor color, bool hcenter, bool vcenter, const core::rect<s32>* clip)
554 {
555         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         }
566
567         // Set up some variables.
568         core::dimension2d<s32> textDimension;
569         core::position2d<s32> offset = position.UpperLeftCorner;
570
571         // Determine offset positions.
572         if (hcenter || vcenter)
573         {
574                 textDimension = getDimension(text.c_str());
575
576                 if (hcenter)
577                         offset.X = ((position.getWidth() - textDimension.Width) >> 1) + offset.X;
578
579                 if (vcenter)
580                         offset.Y = ((position.getHeight() - textDimension.Height) >> 1) + offset.Y;
581         }
582
583         // Convert to a unicode string.
584         core::ustring utext = text.getString();
585
586         // Set up our render map.
587         core::map<u32, CGUITTGlyphPage*> Render_Map;
588
589         // Start parsing characters.
590         u32 n;
591         uchar32_t previousChar = 0;
592         core::ustring::const_iterator iter(utext);
593         std::vector<video::SColor> applied_colors;
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                         Render_Map.set(glyph.glyph_page, page);
640                         u32 current_color = iter.getPos();
641                         if (current_color < colors.size())
642                                 applied_colors.push_back(colors[current_color]);
643                 }
644                 if (n > 0)
645                 {
646                         offset.X += getWidthFromCharacter(currentChar);
647                 }
648                 else if (fallback != 0)
649                 {
650                         // Let the fallback font draw it, this isn't super efficient but hopefully that doesn't matter
651                         wchar_t l1[] = { (wchar_t) currentChar, 0 }, l2 = (wchar_t) previousChar;
652
653                         if (visible)
654                         {
655                                 // Apply kerning.
656                                 offset.X += fallback->getKerningWidth(l1, &l2);
657                                 offset.Y += fallback->getKerningHeight();
658
659                                 u32 current_color = iter.getPos();
660                                 fallback->draw(core::stringw(l1),
661                                         core::rect<s32>({offset.X-1, offset.Y-1}, position.LowerRightCorner), // ???
662                                         current_color < colors.size() ? colors[current_color] : video::SColor(255, 255, 255, 255),
663                                         false, false, clip);
664                         }
665
666                         offset.X += fallback->getDimension(l1).Width;
667                 }
668
669                 previousChar = currentChar;
670                 ++iter;
671         }
672
673         // Draw now.
674         update_glyph_pages();
675         core::map<u32, CGUITTGlyphPage*>::Iterator j = Render_Map.getIterator();
676         while (!j.atEnd())
677         {
678                 core::map<u32, CGUITTGlyphPage*>::Node* n = j.getNode();
679                 j++;
680                 if (n == 0) continue;
681
682                 CGUITTGlyphPage* page = n->getValue();
683
684                 if (shadow_offset) {
685                         for (size_t i = 0; i < page->render_positions.size(); ++i)
686                                 page->render_positions[i] += core::vector2di(shadow_offset, shadow_offset);
687                         Driver->draw2DImageBatch(page->texture, page->render_positions, page->render_source_rects, clip, video::SColor(shadow_alpha,0,0,0), true);
688                         for (size_t i = 0; i < page->render_positions.size(); ++i)
689                                 page->render_positions[i] -= core::vector2di(shadow_offset, shadow_offset);
690                 }
691                 for (size_t i = 0; i < page->render_positions.size(); ++i) {
692                         irr::video::SColor col;
693                         if (!applied_colors.empty()) {
694                                 col = applied_colors[i < applied_colors.size() ? i : 0];
695                         } else {
696                                 col = irr::video::SColor(255, 255, 255, 255);
697                         }
698                         if (!use_transparency)
699                                 col.color |= 0xff000000;
700                         Driver->draw2DImage(page->texture, page->render_positions[i], page->render_source_rects[i], clip, col, true);
701                 }
702         }
703 }
704
705 core::dimension2d<u32> CGUITTFont::getCharDimension(const wchar_t ch) const
706 {
707         return core::dimension2d<u32>(getWidthFromCharacter(ch), getHeightFromCharacter(ch));
708 }
709
710 core::dimension2d<u32> CGUITTFont::getDimension(const wchar_t* text) const
711 {
712         return getDimension(core::ustring(text));
713 }
714
715 core::dimension2d<u32> CGUITTFont::getDimension(const core::ustring& text) const
716 {
717         // Get the maximum font height.  Unfortunately, we have to do this hack as
718         // Irrlicht will draw things wrong.  In FreeType, the font size is the
719         // maximum size for a single glyph, but that glyph may hang "under" the
720         // draw line, increasing the total font height to beyond the set size.
721         // Irrlicht does not understand this concept when drawing fonts.  Also, I
722         // add +1 to give it a 1 pixel blank border.  This makes things like
723         // tooltips look nicer.
724         s32 test1 = getHeightFromCharacter((uchar32_t)'g') + 1;
725         s32 test2 = getHeightFromCharacter((uchar32_t)'j') + 1;
726         s32 test3 = getHeightFromCharacter((uchar32_t)'_') + 1;
727         s32 max_font_height = core::max_(test1, core::max_(test2, test3));
728
729         core::dimension2d<u32> text_dimension(0, max_font_height);
730         core::dimension2d<u32> line(0, max_font_height);
731
732         uchar32_t previousChar = 0;
733         core::ustring::const_iterator iter = text.begin();
734         for (; !iter.atEnd(); ++iter)
735         {
736                 uchar32_t p = *iter;
737                 bool lineBreak = false;
738                 if (p == '\r')  // Mac or Windows line breaks.
739                 {
740                         lineBreak = true;
741                         if (*(iter + 1) == '\n')
742                         {
743                                 ++iter;
744                                 p = *iter;
745                         }
746                 }
747                 else if (p == '\n')     // Unix line breaks.
748                 {
749                         lineBreak = true;
750                 }
751
752                 // Kerning.
753                 core::vector2di k = getKerning(p, previousChar);
754                 line.Width += k.X;
755                 previousChar = p;
756
757                 // Check for linebreak.
758                 if (lineBreak)
759                 {
760                         previousChar = 0;
761                         text_dimension.Height += line.Height;
762                         if (text_dimension.Width < line.Width)
763                                 text_dimension.Width = line.Width;
764                         line.Width = 0;
765                         line.Height = max_font_height;
766                         continue;
767                 }
768                 line.Width += getWidthFromCharacter(p);
769         }
770         if (text_dimension.Width < line.Width)
771                 text_dimension.Width = line.Width;
772
773         return text_dimension;
774 }
775
776 inline u32 CGUITTFont::getWidthFromCharacter(wchar_t c) const
777 {
778         return getWidthFromCharacter((uchar32_t)c);
779 }
780
781 inline u32 CGUITTFont::getWidthFromCharacter(uchar32_t c) const
782 {
783         // Set the size of the face.
784         // This is because we cache faces and the face may have been set to a different size.
785         //FT_Set_Pixel_Sizes(tt_face, 0, size);
786
787         u32 n = getGlyphIndexByChar(c);
788         if (n > 0)
789         {
790                 int w = Glyphs[n-1].advance.x / 64;
791                 return w;
792         }
793         if (fallback != 0)
794         {
795                 wchar_t s[] = { (wchar_t) c, 0 };
796                 return fallback->getDimension(s).Width;
797         }
798
799         if (c >= 0x2000)
800                 return (font_metrics.ascender / 64);
801         else return (font_metrics.ascender / 64) / 2;
802 }
803
804 inline u32 CGUITTFont::getHeightFromCharacter(wchar_t c) const
805 {
806         return getHeightFromCharacter((uchar32_t)c);
807 }
808
809 inline u32 CGUITTFont::getHeightFromCharacter(uchar32_t c) const
810 {
811         // Set the size of the face.
812         // This is because we cache faces and the face may have been set to a different size.
813         //FT_Set_Pixel_Sizes(tt_face, 0, size);
814
815         u32 n = getGlyphIndexByChar(c);
816         if (n > 0)
817         {
818                 // Grab the true height of the character, taking into account underhanging glyphs.
819                 s32 height = (font_metrics.ascender / 64) - Glyphs[n-1].offset.Y + Glyphs[n-1].source_rect.getHeight();
820                 return height;
821         }
822         if (fallback != 0)
823         {
824                 wchar_t s[] = { (wchar_t) c, 0 };
825                 return fallback->getDimension(s).Height;
826         }
827
828         if (c >= 0x2000)
829                 return (font_metrics.ascender / 64);
830         else return (font_metrics.ascender / 64) / 2;
831 }
832
833 u32 CGUITTFont::getGlyphIndexByChar(wchar_t c) const
834 {
835         return getGlyphIndexByChar((uchar32_t)c);
836 }
837
838 u32 CGUITTFont::getGlyphIndexByChar(uchar32_t c) const
839 {
840         // Get the glyph.
841         u32 glyph = FT_Get_Char_Index(tt_face, c);
842
843         // Check for a valid glyph.
844         if (glyph == 0)
845                 return 0;
846
847         // If our glyph is already loaded, don't bother doing any batch loading code.
848         if (glyph != 0 && Glyphs[glyph - 1].isLoaded)
849                 return glyph;
850
851         // Determine our batch loading positions.
852         u32 half_size = (batch_load_size / 2);
853         u32 start_pos = 0;
854         if (c > half_size) start_pos = c - half_size;
855         u32 end_pos = start_pos + batch_load_size;
856
857         // Load all our characters.
858         do
859         {
860                 // Get the character we are going to load.
861                 u32 char_index = FT_Get_Char_Index(tt_face, start_pos);
862
863                 // If the glyph hasn't been loaded yet, do it now.
864                 if (char_index)
865                 {
866                         SGUITTGlyph& glyph = Glyphs[char_index - 1];
867                         if (!glyph.isLoaded)
868                         {
869                                 glyph.preload(char_index, tt_face, Driver, size, load_flags);
870                                 Glyph_Pages[glyph.glyph_page]->pushGlyphToBePaged(&glyph);
871                         }
872                 }
873         }
874         while (++start_pos < end_pos);
875
876         // Return our original character.
877         return glyph;
878 }
879
880 s32 CGUITTFont::getCharacterFromPos(const wchar_t* text, s32 pixel_x) const
881 {
882         return getCharacterFromPos(core::ustring(text), pixel_x);
883 }
884
885 s32 CGUITTFont::getCharacterFromPos(const core::ustring& text, s32 pixel_x) const
886 {
887         s32 x = 0;
888         //s32 idx = 0;
889
890         u32 character = 0;
891         uchar32_t previousChar = 0;
892         core::ustring::const_iterator iter = text.begin();
893         while (!iter.atEnd())
894         {
895                 uchar32_t c = *iter;
896                 x += getWidthFromCharacter(c);
897
898                 // Kerning.
899                 core::vector2di k = getKerning(c, previousChar);
900                 x += k.X;
901
902                 if (x >= pixel_x)
903                         return character;
904
905                 previousChar = c;
906                 ++iter;
907                 ++character;
908         }
909
910         return -1;
911 }
912
913 void CGUITTFont::setKerningWidth(s32 kerning)
914 {
915         GlobalKerningWidth = kerning;
916 }
917
918 void CGUITTFont::setKerningHeight(s32 kerning)
919 {
920         GlobalKerningHeight = kerning;
921 }
922
923 s32 CGUITTFont::getKerningWidth(const wchar_t* thisLetter, const wchar_t* previousLetter) const
924 {
925         if (tt_face == 0)
926                 return GlobalKerningWidth;
927         if (thisLetter == 0 || previousLetter == 0)
928                 return 0;
929
930         return getKerningWidth((uchar32_t)*thisLetter, (uchar32_t)*previousLetter);
931 }
932
933 s32 CGUITTFont::getKerningWidth(const uchar32_t thisLetter, const uchar32_t previousLetter) const
934 {
935         // Return only the kerning width.
936         return getKerning(thisLetter, previousLetter).X;
937 }
938
939 s32 CGUITTFont::getKerningHeight() const
940 {
941         // FreeType 2 currently doesn't return any height kerning information.
942         return GlobalKerningHeight;
943 }
944
945 core::vector2di CGUITTFont::getKerning(const wchar_t thisLetter, const wchar_t previousLetter) const
946 {
947         return getKerning((uchar32_t)thisLetter, (uchar32_t)previousLetter);
948 }
949
950 core::vector2di CGUITTFont::getKerning(const uchar32_t thisLetter, const uchar32_t previousLetter) const
951 {
952         if (tt_face == 0 || thisLetter == 0 || previousLetter == 0)
953                 return core::vector2di();
954
955         // Set the size of the face.
956         // This is because we cache faces and the face may have been set to a different size.
957         FT_Set_Pixel_Sizes(tt_face, 0, size);
958
959         core::vector2di ret(GlobalKerningWidth, GlobalKerningHeight);
960
961         u32 n = getGlyphIndexByChar(thisLetter);
962
963         // If we don't have this glyph, ask fallback font
964         if (n == 0)
965         {
966                 if (fallback != 0) {
967                         wchar_t l1 = (wchar_t) thisLetter, l2 = (wchar_t) previousLetter;
968                         ret.X = fallback->getKerningWidth(&l1, &l2);
969                         ret.Y = fallback->getKerningHeight();
970                 }
971                 return ret;
972         }
973
974         // If we don't have kerning, no point in continuing.
975         if (!FT_HAS_KERNING(tt_face))
976                 return ret;
977
978         // Get the kerning information.
979         FT_Vector v;
980         FT_Get_Kerning(tt_face, getGlyphIndexByChar(previousLetter), n, FT_KERNING_DEFAULT, &v);
981
982         // If we have a scalable font, the return value will be in font points.
983         if (FT_IS_SCALABLE(tt_face))
984         {
985                 // Font points, so divide by 64.
986                 ret.X += (v.x / 64);
987                 ret.Y += (v.y / 64);
988         }
989         else
990         {
991                 // Pixel units.
992                 ret.X += v.x;
993                 ret.Y += v.y;
994         }
995         return ret;
996 }
997
998 void CGUITTFont::setInvisibleCharacters(const wchar_t *s)
999 {
1000         core::ustring us(s);
1001         Invisible = us;
1002 }
1003
1004 void CGUITTFont::setInvisibleCharacters(const core::ustring& s)
1005 {
1006         Invisible = s;
1007 }
1008
1009 video::IImage* CGUITTFont::createTextureFromChar(const uchar32_t& ch)
1010 {
1011         u32 n = getGlyphIndexByChar(ch);
1012         if (n == 0)
1013                 n = getGlyphIndexByChar((uchar32_t) core::unicode::UTF_REPLACEMENT_CHARACTER);
1014
1015         const SGUITTGlyph& glyph = Glyphs[n-1];
1016         CGUITTGlyphPage* page = Glyph_Pages[glyph.glyph_page];
1017
1018         if (page->dirty)
1019                 page->updateTexture();
1020
1021         video::ITexture* tex = page->texture;
1022
1023         // Acquire a read-only lock of the corresponding page texture.
1024         void* ptr = tex->lock(video::ETLM_READ_ONLY);
1025
1026         video::ECOLOR_FORMAT format = tex->getColorFormat();
1027         core::dimension2du tex_size = tex->getOriginalSize();
1028         video::IImage* pageholder = Driver->createImageFromData(format, tex_size, ptr, true, false);
1029
1030         // Copy the image data out of the page texture.
1031         core::dimension2du glyph_size(glyph.source_rect.getSize());
1032         video::IImage* image = Driver->createImage(format, glyph_size);
1033         pageholder->copyTo(image, core::position2di(0, 0), glyph.source_rect);
1034
1035         tex->unlock();
1036         return image;
1037 }
1038
1039 video::ITexture* CGUITTFont::getPageTextureByIndex(const u32& page_index) const
1040 {
1041         if (page_index < Glyph_Pages.size())
1042                 return Glyph_Pages[page_index]->texture;
1043         else
1044                 return 0;
1045 }
1046
1047 void CGUITTFont::createSharedPlane()
1048 {
1049         /*
1050                 2___3
1051                 |  /|
1052                 | / |   <-- plane mesh is like this, point 2 is (0,0), point 0 is (0, -1)
1053                 |/  |   <-- the texture coords of point 2 is (0,0, point 0 is (0, 1)
1054                 0---1
1055         */
1056
1057         using namespace core;
1058         using namespace video;
1059         using namespace scene;
1060         S3DVertex vertices[4];
1061         u16 indices[6] = {0,2,3,3,1,0};
1062         vertices[0] = S3DVertex(vector3df(0,-1,0), vector3df(0,0,-1), SColor(255,255,255,255), vector2df(0,1));
1063         vertices[1] = S3DVertex(vector3df(1,-1,0), vector3df(0,0,-1), SColor(255,255,255,255), vector2df(1,1));
1064         vertices[2] = S3DVertex(vector3df(0, 0,0), vector3df(0,0,-1), SColor(255,255,255,255), vector2df(0,0));
1065         vertices[3] = S3DVertex(vector3df(1, 0,0), vector3df(0,0,-1), SColor(255,255,255,255), vector2df(1,0));
1066
1067         SMeshBuffer* buf = new SMeshBuffer();
1068         buf->append(vertices, 4, indices, 6);
1069
1070         shared_plane_.addMeshBuffer( buf );
1071
1072         shared_plane_ptr_ = &shared_plane_;
1073         buf->drop(); //the addMeshBuffer method will grab it, so we can drop this ptr.
1074 }
1075
1076 core::dimension2d<u32> CGUITTFont::getDimensionUntilEndOfLine(const wchar_t* p) const
1077 {
1078         core::stringw s;
1079         for (const wchar_t* temp = p; temp && *temp != '\0' && *temp != L'\r' && *temp != L'\n'; ++temp )
1080                 s.append(*temp);
1081
1082         return getDimension(s.c_str());
1083 }
1084
1085 core::array<scene::ISceneNode*> CGUITTFont::addTextSceneNode(const wchar_t* text, scene::ISceneManager* smgr, scene::ISceneNode* parent, const video::SColor& color, bool center)
1086 {
1087         using namespace core;
1088         using namespace video;
1089         using namespace scene;
1090
1091         array<scene::ISceneNode*> container;
1092
1093         if (!Driver || !smgr) return container;
1094         if (!parent)
1095                 parent = smgr->addEmptySceneNode(smgr->getRootSceneNode(), -1);
1096         // if you don't specify parent, then we add a empty node attached to the root node
1097         // this is generally undesirable.
1098
1099         if (!shared_plane_ptr_) //this points to a static mesh that contains the plane
1100                 createSharedPlane(); //if it's not initialized, we create one.
1101
1102         dimension2d<s32> text_size(getDimension(text)); //convert from unsigned to signed.
1103         vector3df start_point(0, 0, 0), offset;
1104
1105         /** NOTICE:
1106                 Because we are considering adding texts into 3D world, all Y axis vectors are inverted.
1107         **/
1108
1109         // There's currently no "vertical center" concept when you apply text scene node to the 3D world.
1110         if (center)
1111         {
1112                 offset.X = start_point.X = -text_size.Width / 2.f;
1113                 offset.Y = start_point.Y = +text_size.Height/ 2.f;
1114                 offset.X += (text_size.Width - getDimensionUntilEndOfLine(text).Width) >> 1;
1115         }
1116
1117         // the default font material
1118         SMaterial mat;
1119         mat.setFlag(video::EMF_LIGHTING, true);
1120         mat.setFlag(video::EMF_ZWRITE_ENABLE, false);
1121         mat.setFlag(video::EMF_NORMALIZE_NORMALS, true);
1122         mat.ColorMaterial = video::ECM_NONE;
1123         mat.MaterialType = use_transparency ? video::EMT_TRANSPARENT_ALPHA_CHANNEL : video::EMT_SOLID;
1124         mat.MaterialTypeParam = 0.01f;
1125         mat.DiffuseColor = color;
1126
1127         wchar_t current_char = 0, previous_char = 0;
1128         u32 n = 0;
1129
1130         array<u32> glyph_indices;
1131
1132         while (*text)
1133         {
1134                 current_char = *text;
1135                 bool line_break=false;
1136                 if (current_char == L'\r') // Mac or Windows breaks
1137                 {
1138                         line_break = true;
1139                         if (*(text + 1) == L'\n') // Windows line breaks.
1140                                 current_char = *(++text);
1141                 }
1142                 else if (current_char == L'\n') // Unix breaks
1143                 {
1144                         line_break = true;
1145                 }
1146
1147                 if (line_break)
1148                 {
1149                         previous_char = 0;
1150                         offset.Y -= tt_face->size->metrics.ascender / 64;
1151                         offset.X = start_point.X;
1152                         if (center)
1153                                 offset.X += (text_size.Width - getDimensionUntilEndOfLine(text+1).Width) >> 1;
1154                         ++text;
1155                 }
1156                 else
1157                 {
1158                         n = getGlyphIndexByChar(current_char);
1159                         if (n > 0)
1160                         {
1161                                 glyph_indices.push_back( n );
1162
1163                                 // Store glyph size and offset informations.
1164                                 SGUITTGlyph const& glyph = Glyphs[n-1];
1165                                 u32 texw = glyph.source_rect.getWidth();
1166                                 u32 texh = glyph.source_rect.getHeight();
1167                                 s32 offx = glyph.offset.X;
1168                                 s32 offy = (font_metrics.ascender / 64) - glyph.offset.Y;
1169
1170                                 // Apply kerning.
1171                                 vector2di k = getKerning(current_char, previous_char);
1172                                 offset.X += k.X;
1173                                 offset.Y += k.Y;
1174
1175                                 vector3df current_pos(offset.X + offx, offset.Y - offy, 0);
1176                                 dimension2d<u32> letter_size = dimension2d<u32>(texw, texh);
1177
1178                                 // Now we copy planes corresponding to the letter size.
1179                                 IMeshManipulator* mani = smgr->getMeshManipulator();
1180                                 IMesh* meshcopy = mani->createMeshCopy(shared_plane_ptr_);
1181                                 mani->scale(meshcopy, vector3df((f32)letter_size.Width, (f32)letter_size.Height, 1));
1182
1183                                 ISceneNode* current_node = smgr->addMeshSceneNode(meshcopy, parent, -1, current_pos);
1184                                 meshcopy->drop();
1185
1186                                 current_node->getMaterial(0) = mat;
1187                                 current_node->setAutomaticCulling(EAC_OFF);
1188                                 current_node->setIsDebugObject(true);  //so the picking won't have any effect on individual letter
1189                                 //current_node->setDebugDataVisible(EDS_BBOX); //de-comment this when debugging
1190
1191                                 container.push_back(current_node);
1192                         }
1193                         offset.X += getWidthFromCharacter(current_char);
1194                         // Note that fallback font handling is missing here (Minetest never uses this)
1195
1196                         previous_char = current_char;
1197                         ++text;
1198                 }
1199         }
1200
1201         update_glyph_pages();
1202         //only after we update the textures can we use the glyph page textures.
1203
1204         for (u32 i = 0; i < glyph_indices.size(); ++i)
1205         {
1206                 u32 n = glyph_indices[i];
1207                 SGUITTGlyph const& glyph = Glyphs[n-1];
1208                 ITexture* current_tex = Glyph_Pages[glyph.glyph_page]->texture;
1209                 f32 page_texture_size = (f32)current_tex->getSize().Width;
1210                 //Now we calculate the UV position according to the texture size and the source rect.
1211                 //
1212                 //  2___3
1213                 //  |  /|
1214                 //  | / |       <-- plane mesh is like this, point 2 is (0,0), point 0 is (0, -1)
1215                 //  |/  |       <-- the texture coords of point 2 is (0,0, point 0 is (0, 1)
1216                 //  0---1
1217                 //
1218                 f32 u1 = glyph.source_rect.UpperLeftCorner.X / page_texture_size;
1219                 f32 u2 = u1 + (glyph.source_rect.getWidth() / page_texture_size);
1220                 f32 v1 = glyph.source_rect.UpperLeftCorner.Y / page_texture_size;
1221                 f32 v2 = v1 + (glyph.source_rect.getHeight() / page_texture_size);
1222
1223                 //we can be quite sure that this is IMeshSceneNode, because we just added them in the above loop.
1224                 IMeshSceneNode* node = static_cast<IMeshSceneNode*>(container[i]);
1225
1226                 S3DVertex* pv = static_cast<S3DVertex*>(node->getMesh()->getMeshBuffer(0)->getVertices());
1227                 //pv[0].TCoords.Y = pv[1].TCoords.Y = (letter_size.Height - 1) / static_cast<f32>(letter_size.Height);
1228                 //pv[1].TCoords.X = pv[3].TCoords.X = (letter_size.Width - 1)  / static_cast<f32>(letter_size.Width);
1229                 pv[0].TCoords = vector2df(u1, v2);
1230                 pv[1].TCoords = vector2df(u2, v2);
1231                 pv[2].TCoords = vector2df(u1, v1);
1232                 pv[3].TCoords = vector2df(u2, v1);
1233
1234                 container[i]->getMaterial(0).setTexture(0, current_tex);
1235         }
1236
1237         return container;
1238 }
1239
1240 } // end namespace gui
1241 } // end namespace irr