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