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