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