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