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