]> git.lizzy.rs Git - dragonfireclient.git/blob - src/irrlicht_changes/CGUITTFont.cpp
Fixes needed to use irrArray backed by std::vector (#12263)
[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
297 bool CGUITTFont::load(const io::path& filename, const u32 size, const bool antialias, const bool transparency)
298 {
299         // Some sanity checks.
300         if (Environment == 0 || Driver == 0) return false;
301         if (size == 0) return false;
302         if (filename.size() == 0) return false;
303
304         io::IFileSystem* filesystem = Environment->getFileSystem();
305         irr::ILogger* logger = (Device != 0 ? Device->getLogger() : 0);
306         this->size = size;
307         this->filename = filename;
308
309         // Update the font loading flags when the font is first loaded.
310         this->use_monochrome = !antialias;
311         this->use_transparency = transparency;
312         update_load_flags();
313
314         // Log.
315         if (logger)
316                 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);
317
318         // Grab the face.
319         SGUITTFace* face = 0;
320         auto node = c_faces.find(filename);
321         if (node == c_faces.end())
322         {
323                 face = new SGUITTFace();
324                 c_faces.emplace(filename, face);
325
326                 if (filesystem)
327                 {
328                         // Read in the file data.
329                         io::IReadFile* file = filesystem->createAndOpenFile(filename);
330                         if (file == 0)
331                         {
332                                 if (logger) logger->log(L"CGUITTFont", L"Failed to open the file.", irr::ELL_INFORMATION);
333
334                                 c_faces.erase(filename);
335                                 delete face;
336                                 face = 0;
337                                 return false;
338                         }
339                         face->face_buffer = new FT_Byte[file->getSize()];
340                         file->read(face->face_buffer, file->getSize());
341                         face->face_buffer_size = file->getSize();
342                         file->drop();
343
344                         // Create the face.
345                         if (FT_New_Memory_Face(c_library, face->face_buffer, face->face_buffer_size, 0, &face->face))
346                         {
347                                 if (logger) logger->log(L"CGUITTFont", L"FT_New_Memory_Face failed.", irr::ELL_INFORMATION);
348
349                                 c_faces.erase(filename);
350                                 delete face;
351                                 face = 0;
352                                 return false;
353                         }
354                 }
355                 else
356                 {
357                         core::ustring converter(filename);
358                         if (FT_New_Face(c_library, reinterpret_cast<const char*>(converter.toUTF8_s().c_str()), 0, &face->face))
359                         {
360                                 if (logger) logger->log(L"CGUITTFont", L"FT_New_Face failed.", irr::ELL_INFORMATION);
361
362                                 c_faces.erase(filename);
363                                 delete face;
364                                 face = 0;
365                                 return false;
366                         }
367                 }
368         }
369         else
370         {
371                 // Using another instance of this face.
372                 face = node->second;
373                 face->grab();
374         }
375
376         // Store our face.
377         tt_face = face->face;
378
379         // Store font metrics.
380         FT_Set_Pixel_Sizes(tt_face, size, 0);
381         font_metrics = tt_face->size->metrics;
382
383         // Allocate our glyphs.
384         Glyphs.clear();
385         Glyphs.reallocate(tt_face->num_glyphs);
386         Glyphs.set_used(tt_face->num_glyphs);
387         for (FT_Long i = 0; i < tt_face->num_glyphs; ++i)
388         {
389                 Glyphs[i].isLoaded = false;
390                 Glyphs[i].glyph_page = 0;
391                 Glyphs[i].source_rect = core::recti();
392                 Glyphs[i].offset = core::vector2di();
393                 Glyphs[i].advance = FT_Vector();
394                 Glyphs[i].surface = 0;
395                 Glyphs[i].parent = this;
396         }
397
398         // Cache the first 127 ascii characters.
399         u32 old_size = batch_load_size;
400         batch_load_size = 127;
401         getGlyphIndexByChar((uchar32_t)0);
402         batch_load_size = old_size;
403
404         return true;
405 }
406
407 CGUITTFont::~CGUITTFont()
408 {
409         // Delete the glyphs and glyph pages.
410         reset_images();
411         Glyphs.clear();
412
413         // We aren't using this face anymore.
414         auto n = c_faces.find(filename);
415         if (n != c_faces.end())
416         {
417                 SGUITTFace* f = n->second;
418
419                 // Drop our face.  If this was the last face, the destructor will clean up.
420                 if (f->drop())
421                         c_faces.erase(filename);
422
423                 // If there are no more faces referenced by FreeType, clean up.
424                 if (c_faces.empty())
425                 {
426                         FT_Done_FreeType(c_library);
427                         c_libraryLoaded = false;
428                 }
429         }
430
431         // Drop our driver now.
432         if (Driver)
433                 Driver->drop();
434 }
435
436 void CGUITTFont::reset_images()
437 {
438         // Delete the glyphs.
439         for (u32 i = 0; i != Glyphs.size(); ++i)
440                 Glyphs[i].unload();
441
442         // Unload the glyph pages from video memory.
443         for (u32 i = 0; i != Glyph_Pages.size(); ++i)
444                 delete Glyph_Pages[i];
445         Glyph_Pages.clear();
446
447         // Always update the internal FreeType loading flags after resetting.
448         update_load_flags();
449 }
450
451 void CGUITTFont::update_glyph_pages() const
452 {
453         for (u32 i = 0; i != Glyph_Pages.size(); ++i)
454         {
455                 if (Glyph_Pages[i]->dirty)
456                         Glyph_Pages[i]->updateTexture();
457         }
458 }
459
460 CGUITTGlyphPage* CGUITTFont::getLastGlyphPage() const
461 {
462         CGUITTGlyphPage* page = 0;
463         if (Glyph_Pages.empty())
464                 return 0;
465         else
466         {
467                 page = Glyph_Pages[getLastGlyphPageIndex()];
468                 if (page->available_slots == 0)
469                         page = 0;
470         }
471         return page;
472 }
473
474 CGUITTGlyphPage* CGUITTFont::createGlyphPage(const u8& pixel_mode)
475 {
476         CGUITTGlyphPage* page = 0;
477
478         // Name of our page.
479         io::path name("TTFontGlyphPage_");
480         name += tt_face->family_name;
481         name += ".";
482         name += tt_face->style_name;
483         name += ".";
484         name += size;
485         name += "_";
486         name += Glyph_Pages.size(); // The newly created page will be at the end of the collection.
487
488         // Create the new page.
489         page = new CGUITTGlyphPage(Driver, name);
490
491         // Determine our maximum texture size.
492         // If we keep getting 0, set it to 1024x1024, as that number is pretty safe.
493         core::dimension2du max_texture_size = max_page_texture_size;
494         if (max_texture_size.Width == 0 || max_texture_size.Height == 0)
495                 max_texture_size = Driver->getMaxTextureSize();
496         if (max_texture_size.Width == 0 || max_texture_size.Height == 0)
497                 max_texture_size = core::dimension2du(1024, 1024);
498
499         // We want to try to put at least 144 glyphs on a single texture.
500         core::dimension2du page_texture_size;
501         if (size <= 21) page_texture_size = core::dimension2du(256, 256);
502         else if (size <= 42) page_texture_size = core::dimension2du(512, 512);
503         else if (size <= 84) page_texture_size = core::dimension2du(1024, 1024);
504         else if (size <= 168) page_texture_size = core::dimension2du(2048, 2048);
505         else page_texture_size = core::dimension2du(4096, 4096);
506
507         if (page_texture_size.Width > max_texture_size.Width || page_texture_size.Height > max_texture_size.Height)
508                 page_texture_size = max_texture_size;
509
510         if (!page->createPageTexture(pixel_mode, page_texture_size)) {
511                 // TODO: add error message?
512                 delete page;
513                 return 0;
514         }
515
516         if (page)
517         {
518                 // Determine the number of glyph slots on the page and add it to the list of pages.
519                 page->available_slots = (page_texture_size.Width / size) * (page_texture_size.Height / size);
520                 Glyph_Pages.push_back(page);
521         }
522         return page;
523 }
524
525 void CGUITTFont::setTransparency(const bool flag)
526 {
527         use_transparency = flag;
528         reset_images();
529 }
530
531 void CGUITTFont::setMonochrome(const bool flag)
532 {
533         use_monochrome = flag;
534         reset_images();
535 }
536
537 void CGUITTFont::setFontHinting(const bool enable, const bool enable_auto_hinting)
538 {
539         use_hinting = enable;
540         use_auto_hinting = enable_auto_hinting;
541         reset_images();
542 }
543
544 void CGUITTFont::draw(const core::stringw& text, const core::rect<s32>& position, video::SColor color, bool hcenter, bool vcenter, const core::rect<s32>* clip)
545 {
546         draw(EnrichedString(std::wstring(text.c_str()), color), position, hcenter, vcenter, clip);
547 }
548
549 void CGUITTFont::draw(const EnrichedString &text, const core::rect<s32>& position, bool hcenter, bool vcenter, const core::rect<s32>* clip)
550 {
551         const std::vector<video::SColor> &colors = text.getColors();
552
553         if (!Driver)
554                 return;
555
556         // Clear the glyph pages of their render information.
557         for (u32 i = 0; i < Glyph_Pages.size(); ++i)
558         {
559                 Glyph_Pages[i]->render_positions.clear();
560                 Glyph_Pages[i]->render_source_rects.clear();
561                 Glyph_Pages[i]->render_colors.clear();
562         }
563
564         // Set up some variables.
565         core::dimension2d<s32> textDimension;
566         core::position2d<s32> offset = position.UpperLeftCorner;
567
568         // Determine offset positions.
569         if (hcenter || vcenter)
570         {
571                 textDimension = getDimension(text.c_str());
572
573                 if (hcenter)
574                         offset.X = ((position.getWidth() - textDimension.Width) >> 1) + offset.X;
575
576                 if (vcenter)
577                         offset.Y = ((position.getHeight() - textDimension.Height) >> 1) + offset.Y;
578         }
579
580         // Convert to a unicode string.
581         core::ustring utext = text.getString();
582
583         // Set up our render map.
584         std::map<u32, CGUITTGlyphPage*> Render_Map;
585
586         // Start parsing characters.
587         u32 n;
588         uchar32_t previousChar = 0;
589         core::ustring::const_iterator iter(utext);
590         while (!iter.atEnd())
591         {
592                 uchar32_t currentChar = *iter;
593                 n = getGlyphIndexByChar(currentChar);
594                 bool visible = (Invisible.findFirst(currentChar) == -1);
595                 bool lineBreak=false;
596                 if (currentChar == L'\r') // Mac or Windows breaks
597                 {
598                         lineBreak = true;
599                         if (*(iter + 1) == (uchar32_t)'\n')     // Windows line breaks.
600                                 currentChar = *(++iter);
601                 }
602                 else if (currentChar == (uchar32_t)'\n') // Unix breaks
603                 {
604                         lineBreak = true;
605                 }
606
607                 if (lineBreak)
608                 {
609                         previousChar = 0;
610                         offset.Y += font_metrics.height / 64;
611                         offset.X = position.UpperLeftCorner.X;
612
613                         if (hcenter)
614                                 offset.X += (position.getWidth() - textDimension.Width) >> 1;
615                         ++iter;
616                         continue;
617                 }
618
619                 if (n > 0 && visible)
620                 {
621                         // Calculate the glyph offset.
622                         s32 offx = Glyphs[n-1].offset.X;
623                         s32 offy = (font_metrics.ascender / 64) - Glyphs[n-1].offset.Y;
624
625                         // Apply kerning.
626                         core::vector2di k = getKerning(currentChar, previousChar);
627                         offset.X += k.X;
628                         offset.Y += k.Y;
629
630                         // Determine rendering information.
631                         SGUITTGlyph& glyph = Glyphs[n-1];
632                         CGUITTGlyphPage* const page = Glyph_Pages[glyph.glyph_page];
633                         page->render_positions.push_back(core::position2di(offset.X + offx, offset.Y + offy));
634                         page->render_source_rects.push_back(glyph.source_rect);
635                         if (iter.getPos() < colors.size())
636                                 page->render_colors.push_back(colors[iter.getPos()]);
637                         else
638                                 page->render_colors.push_back(video::SColor(255,255,255,255));
639                         Render_Map[glyph.glyph_page] = page;
640                 }
641                 if (n > 0)
642                 {
643                         offset.X += getWidthFromCharacter(currentChar);
644                 }
645                 else if (fallback != 0)
646                 {
647                         // Let the fallback font draw it, this isn't super efficient but hopefully that doesn't matter
648                         wchar_t l1[] = { (wchar_t) currentChar, 0 }, l2 = (wchar_t) previousChar;
649
650                         if (visible)
651                         {
652                                 // Apply kerning.
653                                 offset.X += fallback->getKerningWidth(l1, &l2);
654                                 offset.Y += fallback->getKerningHeight();
655
656                                 u32 current_color = iter.getPos();
657                                 fallback->draw(core::stringw(l1),
658                                         core::rect<s32>({offset.X-1, offset.Y-1}, position.LowerRightCorner), // ???
659                                         current_color < colors.size() ? colors[current_color] : video::SColor(255, 255, 255, 255),
660                                         false, false, clip);
661                         }
662
663                         offset.X += fallback->getDimension(l1).Width;
664                 }
665
666                 previousChar = currentChar;
667                 ++iter;
668         }
669
670         // Draw now.
671         update_glyph_pages();
672         auto it = Render_Map.begin();
673         auto ie = Render_Map.end();
674         core::array<core::vector2di> tmp_positions;
675         core::array<core::recti> tmp_source_rects;
676         while (it != ie)
677         {
678                 CGUITTGlyphPage* page = it->second;
679                 ++it;
680
681                 if (shadow_offset) {
682                         for (size_t i = 0; i < page->render_positions.size(); ++i)
683                                 page->render_positions[i] += core::vector2di(shadow_offset, shadow_offset);
684                         Driver->draw2DImageBatch(page->texture, page->render_positions, page->render_source_rects, clip, video::SColor(shadow_alpha,0,0,0), true);
685                         for (size_t i = 0; i < page->render_positions.size(); ++i)
686                                 page->render_positions[i] -= core::vector2di(shadow_offset, shadow_offset);
687                 }
688                 // render runs of matching color in batch
689                 size_t ibegin;
690                 video::SColor colprev;
691                 for (size_t i = 0; i < page->render_positions.size(); ++i) {
692                         ibegin = i;
693                         colprev = page->render_colors[i];
694                         do
695                                 ++i;
696                         while (i < page->render_positions.size() && page->render_colors[i] == colprev);
697                         tmp_positions.set_data(&page->render_positions[ibegin], i - ibegin);
698                         tmp_source_rects.set_data(&page->render_source_rects[ibegin], i - ibegin);
699                         --i;
700
701                         if (!use_transparency)
702                                 colprev.color |= 0xff000000;
703                         Driver->draw2DImageBatch(page->texture, tmp_positions, tmp_source_rects, clip, colprev, true);
704                 }
705         }
706 }
707
708 core::dimension2d<u32> CGUITTFont::getCharDimension(const wchar_t ch) const
709 {
710         return core::dimension2d<u32>(getWidthFromCharacter(ch), getHeightFromCharacter(ch));
711 }
712
713 core::dimension2d<u32> CGUITTFont::getDimension(const wchar_t* text) const
714 {
715         return getDimension(core::ustring(text));
716 }
717
718 core::dimension2d<u32> CGUITTFont::getDimension(const core::ustring& text) const
719 {
720         // Get the maximum font height.  Unfortunately, we have to do this hack as
721         // Irrlicht will draw things wrong.  In FreeType, the font size is the
722         // maximum size for a single glyph, but that glyph may hang "under" the
723         // draw line, increasing the total font height to beyond the set size.
724         // Irrlicht does not understand this concept when drawing fonts.  Also, I
725         // add +1 to give it a 1 pixel blank border.  This makes things like
726         // tooltips look nicer.
727         s32 test1 = getHeightFromCharacter((uchar32_t)'g') + 1;
728         s32 test2 = getHeightFromCharacter((uchar32_t)'j') + 1;
729         s32 test3 = getHeightFromCharacter((uchar32_t)'_') + 1;
730         s32 max_font_height = core::max_(test1, core::max_(test2, test3));
731
732         core::dimension2d<u32> text_dimension(0, max_font_height);
733         core::dimension2d<u32> line(0, max_font_height);
734
735         uchar32_t previousChar = 0;
736         core::ustring::const_iterator iter = text.begin();
737         for (; !iter.atEnd(); ++iter)
738         {
739                 uchar32_t p = *iter;
740                 bool lineBreak = false;
741                 if (p == '\r')  // Mac or Windows line breaks.
742                 {
743                         lineBreak = true;
744                         if (*(iter + 1) == '\n')
745                         {
746                                 ++iter;
747                                 p = *iter;
748                         }
749                 }
750                 else if (p == '\n')     // Unix line breaks.
751                 {
752                         lineBreak = true;
753                 }
754
755                 // Kerning.
756                 core::vector2di k = getKerning(p, previousChar);
757                 line.Width += k.X;
758                 previousChar = p;
759
760                 // Check for linebreak.
761                 if (lineBreak)
762                 {
763                         previousChar = 0;
764                         text_dimension.Height += line.Height;
765                         if (text_dimension.Width < line.Width)
766                                 text_dimension.Width = line.Width;
767                         line.Width = 0;
768                         line.Height = max_font_height;
769                         continue;
770                 }
771                 line.Width += getWidthFromCharacter(p);
772         }
773         if (text_dimension.Width < line.Width)
774                 text_dimension.Width = line.Width;
775
776         return text_dimension;
777 }
778
779 inline u32 CGUITTFont::getWidthFromCharacter(wchar_t c) const
780 {
781         return getWidthFromCharacter((uchar32_t)c);
782 }
783
784 inline u32 CGUITTFont::getWidthFromCharacter(uchar32_t c) const
785 {
786         // Set the size of the face.
787         // This is because we cache faces and the face may have been set to a different size.
788         //FT_Set_Pixel_Sizes(tt_face, 0, size);
789
790         u32 n = getGlyphIndexByChar(c);
791         if (n > 0)
792         {
793                 int w = Glyphs[n-1].advance.x / 64;
794                 return w;
795         }
796         if (fallback != 0)
797         {
798                 wchar_t s[] = { (wchar_t) c, 0 };
799                 return fallback->getDimension(s).Width;
800         }
801
802         if (c >= 0x2000)
803                 return (font_metrics.ascender / 64);
804         else return (font_metrics.ascender / 64) / 2;
805 }
806
807 inline u32 CGUITTFont::getHeightFromCharacter(wchar_t c) const
808 {
809         return getHeightFromCharacter((uchar32_t)c);
810 }
811
812 inline u32 CGUITTFont::getHeightFromCharacter(uchar32_t c) const
813 {
814         // Set the size of the face.
815         // This is because we cache faces and the face may have been set to a different size.
816         //FT_Set_Pixel_Sizes(tt_face, 0, size);
817
818         u32 n = getGlyphIndexByChar(c);
819         if (n > 0)
820         {
821                 // Grab the true height of the character, taking into account underhanging glyphs.
822                 s32 height = (font_metrics.ascender / 64) - Glyphs[n-1].offset.Y + Glyphs[n-1].source_rect.getHeight();
823                 return height;
824         }
825         if (fallback != 0)
826         {
827                 wchar_t s[] = { (wchar_t) c, 0 };
828                 return fallback->getDimension(s).Height;
829         }
830
831         if (c >= 0x2000)
832                 return (font_metrics.ascender / 64);
833         else return (font_metrics.ascender / 64) / 2;
834 }
835
836 u32 CGUITTFont::getGlyphIndexByChar(wchar_t c) const
837 {
838         return getGlyphIndexByChar((uchar32_t)c);
839 }
840
841 u32 CGUITTFont::getGlyphIndexByChar(uchar32_t c) const
842 {
843         // Get the glyph.
844         u32 glyph = FT_Get_Char_Index(tt_face, c);
845
846         // Check for a valid glyph.
847         if (glyph == 0)
848                 return 0;
849
850         // If our glyph is already loaded, don't bother doing any batch loading code.
851         if (glyph != 0 && Glyphs[glyph - 1].isLoaded)
852                 return glyph;
853
854         // Determine our batch loading positions.
855         u32 half_size = (batch_load_size / 2);
856         u32 start_pos = 0;
857         if (c > half_size) start_pos = c - half_size;
858         u32 end_pos = start_pos + batch_load_size;
859
860         // Load all our characters.
861         do
862         {
863                 // Get the character we are going to load.
864                 u32 char_index = FT_Get_Char_Index(tt_face, start_pos);
865
866                 // If the glyph hasn't been loaded yet, do it now.
867                 if (char_index)
868                 {
869                         SGUITTGlyph& glyph = Glyphs[char_index - 1];
870                         if (!glyph.isLoaded)
871                         {
872                                 glyph.preload(char_index, tt_face, Driver, size, load_flags);
873                                 Glyph_Pages[glyph.glyph_page]->pushGlyphToBePaged(&glyph);
874                         }
875                 }
876         }
877         while (++start_pos < end_pos);
878
879         // Return our original character.
880         return glyph;
881 }
882
883 s32 CGUITTFont::getCharacterFromPos(const wchar_t* text, s32 pixel_x) const
884 {
885         return getCharacterFromPos(core::ustring(text), pixel_x);
886 }
887
888 s32 CGUITTFont::getCharacterFromPos(const core::ustring& text, s32 pixel_x) const
889 {
890         s32 x = 0;
891         //s32 idx = 0;
892
893         u32 character = 0;
894         uchar32_t previousChar = 0;
895         core::ustring::const_iterator iter = text.begin();
896         while (!iter.atEnd())
897         {
898                 uchar32_t c = *iter;
899                 x += getWidthFromCharacter(c);
900
901                 // Kerning.
902                 core::vector2di k = getKerning(c, previousChar);
903                 x += k.X;
904
905                 if (x >= pixel_x)
906                         return character;
907
908                 previousChar = c;
909                 ++iter;
910                 ++character;
911         }
912
913         return -1;
914 }
915
916 void CGUITTFont::setKerningWidth(s32 kerning)
917 {
918         GlobalKerningWidth = kerning;
919 }
920
921 void CGUITTFont::setKerningHeight(s32 kerning)
922 {
923         GlobalKerningHeight = kerning;
924 }
925
926 s32 CGUITTFont::getKerningWidth(const wchar_t* thisLetter, const wchar_t* previousLetter) const
927 {
928         if (tt_face == 0)
929                 return GlobalKerningWidth;
930         if (thisLetter == 0 || previousLetter == 0)
931                 return 0;
932
933         return getKerningWidth((uchar32_t)*thisLetter, (uchar32_t)*previousLetter);
934 }
935
936 s32 CGUITTFont::getKerningWidth(const uchar32_t thisLetter, const uchar32_t previousLetter) const
937 {
938         // Return only the kerning width.
939         return getKerning(thisLetter, previousLetter).X;
940 }
941
942 s32 CGUITTFont::getKerningHeight() const
943 {
944         // FreeType 2 currently doesn't return any height kerning information.
945         return GlobalKerningHeight;
946 }
947
948 core::vector2di CGUITTFont::getKerning(const wchar_t thisLetter, const wchar_t previousLetter) const
949 {
950         return getKerning((uchar32_t)thisLetter, (uchar32_t)previousLetter);
951 }
952
953 core::vector2di CGUITTFont::getKerning(const uchar32_t thisLetter, const uchar32_t previousLetter) const
954 {
955         if (tt_face == 0 || thisLetter == 0 || previousLetter == 0)
956                 return core::vector2di();
957
958         // Set the size of the face.
959         // This is because we cache faces and the face may have been set to a different size.
960         FT_Set_Pixel_Sizes(tt_face, 0, size);
961
962         core::vector2di ret(GlobalKerningWidth, GlobalKerningHeight);
963
964         u32 n = getGlyphIndexByChar(thisLetter);
965
966         // If we don't have this glyph, ask fallback font
967         if (n == 0)
968         {
969                 if (fallback != 0) {
970                         wchar_t l1 = (wchar_t) thisLetter, l2 = (wchar_t) previousLetter;
971                         ret.X = fallback->getKerningWidth(&l1, &l2);
972                         ret.Y = fallback->getKerningHeight();
973                 }
974                 return ret;
975         }
976
977         // If we don't have kerning, no point in continuing.
978         if (!FT_HAS_KERNING(tt_face))
979                 return ret;
980
981         // Get the kerning information.
982         FT_Vector v;
983         FT_Get_Kerning(tt_face, getGlyphIndexByChar(previousLetter), n, FT_KERNING_DEFAULT, &v);
984
985         // If we have a scalable font, the return value will be in font points.
986         if (FT_IS_SCALABLE(tt_face))
987         {
988                 // Font points, so divide by 64.
989                 ret.X += (v.x / 64);
990                 ret.Y += (v.y / 64);
991         }
992         else
993         {
994                 // Pixel units.
995                 ret.X += v.x;
996                 ret.Y += v.y;
997         }
998         return ret;
999 }
1000
1001 void CGUITTFont::setInvisibleCharacters(const wchar_t *s)
1002 {
1003         core::ustring us(s);
1004         Invisible = us;
1005 }
1006
1007 void CGUITTFont::setInvisibleCharacters(const core::ustring& s)
1008 {
1009         Invisible = s;
1010 }
1011
1012 video::IImage* CGUITTFont::createTextureFromChar(const uchar32_t& ch)
1013 {
1014         u32 n = getGlyphIndexByChar(ch);
1015         if (n == 0)
1016                 n = getGlyphIndexByChar((uchar32_t) core::unicode::UTF_REPLACEMENT_CHARACTER);
1017
1018         const SGUITTGlyph& glyph = Glyphs[n-1];
1019         CGUITTGlyphPage* page = Glyph_Pages[glyph.glyph_page];
1020
1021         if (page->dirty)
1022                 page->updateTexture();
1023
1024         video::ITexture* tex = page->texture;
1025
1026         // Acquire a read-only lock of the corresponding page texture.
1027         void* ptr = tex->lock(video::ETLM_READ_ONLY);
1028
1029         video::ECOLOR_FORMAT format = tex->getColorFormat();
1030         core::dimension2du tex_size = tex->getOriginalSize();
1031         video::IImage* pageholder = Driver->createImageFromData(format, tex_size, ptr, true, false);
1032
1033         // Copy the image data out of the page texture.
1034         core::dimension2du glyph_size(glyph.source_rect.getSize());
1035         video::IImage* image = Driver->createImage(format, glyph_size);
1036         pageholder->copyTo(image, core::position2di(0, 0), glyph.source_rect);
1037
1038         tex->unlock();
1039         return image;
1040 }
1041
1042 video::ITexture* CGUITTFont::getPageTextureByIndex(const u32& page_index) const
1043 {
1044         if (page_index < Glyph_Pages.size())
1045                 return Glyph_Pages[page_index]->texture;
1046         else
1047                 return 0;
1048 }
1049
1050 void CGUITTFont::createSharedPlane()
1051 {
1052         /*
1053                 2___3
1054                 |  /|
1055                 | / |   <-- plane mesh is like this, point 2 is (0,0), point 0 is (0, -1)
1056                 |/  |   <-- the texture coords of point 2 is (0,0, point 0 is (0, 1)
1057                 0---1
1058         */
1059
1060         using namespace core;
1061         using namespace video;
1062         using namespace scene;
1063         S3DVertex vertices[4];
1064         u16 indices[6] = {0,2,3,3,1,0};
1065         vertices[0] = S3DVertex(vector3df(0,-1,0), vector3df(0,0,-1), SColor(255,255,255,255), vector2df(0,1));
1066         vertices[1] = S3DVertex(vector3df(1,-1,0), vector3df(0,0,-1), SColor(255,255,255,255), vector2df(1,1));
1067         vertices[2] = S3DVertex(vector3df(0, 0,0), vector3df(0,0,-1), SColor(255,255,255,255), vector2df(0,0));
1068         vertices[3] = S3DVertex(vector3df(1, 0,0), vector3df(0,0,-1), SColor(255,255,255,255), vector2df(1,0));
1069
1070         SMeshBuffer* buf = new SMeshBuffer();
1071         buf->append(vertices, 4, indices, 6);
1072
1073         shared_plane_.addMeshBuffer( buf );
1074
1075         shared_plane_ptr_ = &shared_plane_;
1076         buf->drop(); //the addMeshBuffer method will grab it, so we can drop this ptr.
1077 }
1078
1079 core::dimension2d<u32> CGUITTFont::getDimensionUntilEndOfLine(const wchar_t* p) const
1080 {
1081         core::stringw s;
1082         for (const wchar_t* temp = p; temp && *temp != '\0' && *temp != L'\r' && *temp != L'\n'; ++temp )
1083                 s.append(*temp);
1084
1085         return getDimension(s.c_str());
1086 }
1087
1088 core::array<scene::ISceneNode*> CGUITTFont::addTextSceneNode(const wchar_t* text, scene::ISceneManager* smgr, scene::ISceneNode* parent, const video::SColor& color, bool center)
1089 {
1090         using namespace core;
1091         using namespace video;
1092         using namespace scene;
1093
1094         array<scene::ISceneNode*> container;
1095
1096         if (!Driver || !smgr) return container;
1097         if (!parent)
1098                 parent = smgr->addEmptySceneNode(smgr->getRootSceneNode(), -1);
1099         // if you don't specify parent, then we add a empty node attached to the root node
1100         // this is generally undesirable.
1101
1102         if (!shared_plane_ptr_) //this points to a static mesh that contains the plane
1103                 createSharedPlane(); //if it's not initialized, we create one.
1104
1105         dimension2d<s32> text_size(getDimension(text)); //convert from unsigned to signed.
1106         vector3df start_point(0, 0, 0), offset;
1107
1108         /** NOTICE:
1109                 Because we are considering adding texts into 3D world, all Y axis vectors are inverted.
1110         **/
1111
1112         // There's currently no "vertical center" concept when you apply text scene node to the 3D world.
1113         if (center)
1114         {
1115                 offset.X = start_point.X = -text_size.Width / 2.f;
1116                 offset.Y = start_point.Y = +text_size.Height/ 2.f;
1117                 offset.X += (text_size.Width - getDimensionUntilEndOfLine(text).Width) >> 1;
1118         }
1119
1120         // the default font material
1121         SMaterial mat;
1122         mat.setFlag(video::EMF_LIGHTING, true);
1123         mat.setFlag(video::EMF_ZWRITE_ENABLE, false);
1124         mat.setFlag(video::EMF_NORMALIZE_NORMALS, true);
1125         mat.ColorMaterial = video::ECM_NONE;
1126         mat.MaterialType = use_transparency ? video::EMT_TRANSPARENT_ALPHA_CHANNEL : video::EMT_SOLID;
1127         mat.MaterialTypeParam = 0.01f;
1128         mat.DiffuseColor = color;
1129
1130         wchar_t current_char = 0, previous_char = 0;
1131         u32 n = 0;
1132
1133         array<u32> glyph_indices;
1134
1135         while (*text)
1136         {
1137                 current_char = *text;
1138                 bool line_break=false;
1139                 if (current_char == L'\r') // Mac or Windows breaks
1140                 {
1141                         line_break = true;
1142                         if (*(text + 1) == L'\n') // Windows line breaks.
1143                                 current_char = *(++text);
1144                 }
1145                 else if (current_char == L'\n') // Unix breaks
1146                 {
1147                         line_break = true;
1148                 }
1149
1150                 if (line_break)
1151                 {
1152                         previous_char = 0;
1153                         offset.Y -= tt_face->size->metrics.ascender / 64;
1154                         offset.X = start_point.X;
1155                         if (center)
1156                                 offset.X += (text_size.Width - getDimensionUntilEndOfLine(text+1).Width) >> 1;
1157                         ++text;
1158                 }
1159                 else
1160                 {
1161                         n = getGlyphIndexByChar(current_char);
1162                         if (n > 0)
1163                         {
1164                                 glyph_indices.push_back( n );
1165
1166                                 // Store glyph size and offset informations.
1167                                 SGUITTGlyph const& glyph = Glyphs[n-1];
1168                                 u32 texw = glyph.source_rect.getWidth();
1169                                 u32 texh = glyph.source_rect.getHeight();
1170                                 s32 offx = glyph.offset.X;
1171                                 s32 offy = (font_metrics.ascender / 64) - glyph.offset.Y;
1172
1173                                 // Apply kerning.
1174                                 vector2di k = getKerning(current_char, previous_char);
1175                                 offset.X += k.X;
1176                                 offset.Y += k.Y;
1177
1178                                 vector3df current_pos(offset.X + offx, offset.Y - offy, 0);
1179                                 dimension2d<u32> letter_size = dimension2d<u32>(texw, texh);
1180
1181                                 // Now we copy planes corresponding to the letter size.
1182                                 IMeshManipulator* mani = smgr->getMeshManipulator();
1183                                 IMesh* meshcopy = mani->createMeshCopy(shared_plane_ptr_);
1184                                 mani->scale(meshcopy, vector3df((f32)letter_size.Width, (f32)letter_size.Height, 1));
1185
1186                                 ISceneNode* current_node = smgr->addMeshSceneNode(meshcopy, parent, -1, current_pos);
1187                                 meshcopy->drop();
1188
1189                                 current_node->getMaterial(0) = mat;
1190                                 current_node->setAutomaticCulling(EAC_OFF);
1191                                 current_node->setIsDebugObject(true);  //so the picking won't have any effect on individual letter
1192                                 //current_node->setDebugDataVisible(EDS_BBOX); //de-comment this when debugging
1193
1194                                 container.push_back(current_node);
1195                         }
1196                         offset.X += getWidthFromCharacter(current_char);
1197                         // Note that fallback font handling is missing here (Minetest never uses this)
1198
1199                         previous_char = current_char;
1200                         ++text;
1201                 }
1202         }
1203
1204         update_glyph_pages();
1205         //only after we update the textures can we use the glyph page textures.
1206
1207         for (u32 i = 0; i < glyph_indices.size(); ++i)
1208         {
1209                 u32 n = glyph_indices[i];
1210                 SGUITTGlyph const& glyph = Glyphs[n-1];
1211                 ITexture* current_tex = Glyph_Pages[glyph.glyph_page]->texture;
1212                 f32 page_texture_size = (f32)current_tex->getSize().Width;
1213                 //Now we calculate the UV position according to the texture size and the source rect.
1214                 //
1215                 //  2___3
1216                 //  |  /|
1217                 //  | / |       <-- plane mesh is like this, point 2 is (0,0), point 0 is (0, -1)
1218                 //  |/  |       <-- the texture coords of point 2 is (0,0, point 0 is (0, 1)
1219                 //  0---1
1220                 //
1221                 f32 u1 = glyph.source_rect.UpperLeftCorner.X / page_texture_size;
1222                 f32 u2 = u1 + (glyph.source_rect.getWidth() / page_texture_size);
1223                 f32 v1 = glyph.source_rect.UpperLeftCorner.Y / page_texture_size;
1224                 f32 v2 = v1 + (glyph.source_rect.getHeight() / page_texture_size);
1225
1226                 //we can be quite sure that this is IMeshSceneNode, because we just added them in the above loop.
1227                 IMeshSceneNode* node = static_cast<IMeshSceneNode*>(container[i]);
1228
1229                 S3DVertex* pv = static_cast<S3DVertex*>(node->getMesh()->getMeshBuffer(0)->getVertices());
1230                 //pv[0].TCoords.Y = pv[1].TCoords.Y = (letter_size.Height - 1) / static_cast<f32>(letter_size.Height);
1231                 //pv[1].TCoords.X = pv[3].TCoords.X = (letter_size.Width - 1)  / static_cast<f32>(letter_size.Width);
1232                 pv[0].TCoords = vector2df(u1, v2);
1233                 pv[1].TCoords = vector2df(u2, v2);
1234                 pv[2].TCoords = vector2df(u1, v1);
1235                 pv[3].TCoords = vector2df(u2, v1);
1236
1237                 container[i]->getMaterial(0).setTexture(0, current_tex);
1238         }
1239
1240         return container;
1241 }
1242
1243 } // end namespace gui
1244 } // end namespace irr