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