Objectpos over limit: Avoid crash caused by sector over limit
[oweals/minetest.git] / src / cguittfont / CGUITTFont.cpp
index fb8199e2104791f7644a4cdddf9b6073015f0eba..c2d37c6c0dda0b3f61666d65810f52326e1bdfab 100644 (file)
@@ -1,6 +1,7 @@
 /*
    CGUITTFont FreeType class for Irrlicht
    Copyright (c) 2009-2010 John Norman
+   Copyright (c) 2016 NathanaĆ«l Courant
 
    This software is provided 'as-is', without any express or implied
    warranty. In no event will the authors be held liable for any
@@ -29,6 +30,7 @@
 */
 
 #include <irrlicht.h>
+#include <iostream>
 #include "CGUITTFont.h"
 
 namespace irr
@@ -64,8 +66,24 @@ scene::SMesh CGUITTFont::shared_plane_;
 
 //
 
+/** Checks that no dimension of the FT_BitMap object is negative.  If either is
+ * negative, abort execution.
+ */
+inline void checkFontBitmapSize(const FT_Bitmap &bits)
+{
+       if ((s32)bits.rows < 0 || (s32)bits.width < 0) {
+               std::cout << "Insane font glyph size. File: "
+                         << __FILE__ << " Line " << __LINE__
+                         << std::endl;
+               abort();
+       }
+}
+
 video::IImage* SGUITTGlyph::createGlyphImage(const FT_Bitmap& bits, video::IVideoDriver* driver) const
 {
+       // Make sure our casts to s32 in the loops below will not cause problems
+       checkFontBitmapSize(bits);
+
        // Determine what our texture size should be.
        // Add 1 because textures are inclusive-exclusive.
        core::dimension2du d(bits.width + 1, bits.rows + 1);
@@ -87,10 +105,11 @@ video::IImage* SGUITTGlyph::createGlyphImage(const FT_Bitmap& bits, video::IVide
                        const u32 image_pitch = image->getPitch() / sizeof(u16);
                        u16* image_data = (u16*)image->lock();
                        u8* glyph_data = bits.buffer;
-                       for (s32 y = 0; y < bits.rows; ++y)
+
+                       for (s32 y = 0; y < (s32)bits.rows; ++y)
                        {
                                u16* row = image_data;
-                               for (s32 x = 0; x < bits.width; ++x)
+                               for (s32 x = 0; x < (s32)bits.width; ++x)
                                {
                                        // Monochrome bitmaps store 8 pixels per byte.  The left-most pixel is the bit 0x80.
                                        // So, we go through the data each bit at a time.
@@ -116,10 +135,10 @@ video::IImage* SGUITTGlyph::createGlyphImage(const FT_Bitmap& bits, video::IVide
                        const u32 image_pitch = image->getPitch() / sizeof(u32);
                        u32* image_data = (u32*)image->lock();
                        u8* glyph_data = bits.buffer;
-                       for (s32 y = 0; y < bits.rows; ++y)
+                       for (s32 y = 0; y < (s32)bits.rows; ++y)
                        {
                                u8* row = glyph_data;
-                               for (s32 x = 0; x < bits.width; ++x)
+                               for (s32 x = 0; x < (s32)bits.width; ++x)
                                {
                                        image_data[y * image_pitch + x] |= static_cast<u32>(255.0f * (static_cast<float>(*row++) / gray_count)) << 24;
                                        //data[y * image_pitch + x] |= ((u32)(*bitsdata++) << 24);
@@ -199,7 +218,7 @@ void SGUITTGlyph::unload()
 
 //////////////////////
 
-CGUITTFont* CGUITTFont::createTTFont(IGUIEnvironment *env, const io::path& filename, const u32 size, const bool antialias, const bool transparency)
+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)
 {
        if (!c_libraryLoaded)
        {
@@ -216,6 +235,9 @@ CGUITTFont* CGUITTFont::createTTFont(IGUIEnvironment *env, const io::path& filen
                return 0;
        }
 
+       font->shadow_offset = shadow;
+       font->shadow_alpha = shadow_alpha;
+
        return font;
 }
 
@@ -524,6 +546,13 @@ void CGUITTFont::setFontHinting(const bool enable, const bool enable_auto_hintin
 
 void CGUITTFont::draw(const core::stringw& text, const core::rect<s32>& position, video::SColor color, bool hcenter, bool vcenter, const core::rect<s32>* clip)
 {
+       draw(EnrichedString(std::wstring(text.c_str()), color), position, color, hcenter, vcenter, clip);
+}
+
+void CGUITTFont::draw(const EnrichedString &text, const core::rect<s32>& position, video::SColor color, bool hcenter, bool vcenter, const core::rect<s32>* clip)
+{
+       std::vector<video::SColor> colors = text.getColors();
+
        if (!Driver)
                return;
 
@@ -551,7 +580,7 @@ void CGUITTFont::draw(const core::stringw& text, const core::rect<s32>& position
        }
 
        // Convert to a unicode string.
-       core::ustring utext(text);
+       core::ustring utext = text.getString();
 
        // Set up our render map.
        core::map<u32, CGUITTGlyphPage*> Render_Map;
@@ -560,37 +589,38 @@ void CGUITTFont::draw(const core::stringw& text, const core::rect<s32>& position
        u32 n;
        uchar32_t previousChar = 0;
        core::ustring::const_iterator iter(utext);
+       std::vector<video::SColor> applied_colors;
        while (!iter.atEnd())
        {
                uchar32_t currentChar = *iter;
                n = getGlyphIndexByChar(currentChar);
                bool visible = (Invisible.findFirst(currentChar) == -1);
-               if (n > 0 && visible)
+               bool lineBreak=false;
+               if (currentChar == L'\r') // Mac or Windows breaks
                {
-                       bool lineBreak=false;
-                       if (currentChar == L'\r') // Mac or Windows breaks
-                       {
-                               lineBreak = true;
-                               if (*(iter + 1) == (uchar32_t)'\n')     // Windows line breaks.
-                                       currentChar = *(++iter);
-                       }
-                       else if (currentChar == (uchar32_t)'\n') // Unix breaks
-                       {
-                               lineBreak = true;
-                       }
+                       lineBreak = true;
+                       if (*(iter + 1) == (uchar32_t)'\n')     // Windows line breaks.
+                               currentChar = *(++iter);
+               }
+               else if (currentChar == (uchar32_t)'\n') // Unix breaks
+               {
+                       lineBreak = true;
+               }
 
-                       if (lineBreak)
-                       {
-                               previousChar = 0;
-                               offset.Y += font_metrics.ascender / 64;
-                               offset.X = position.UpperLeftCorner.X;
+               if (lineBreak)
+               {
+                       previousChar = 0;
+                       offset.Y += font_metrics.height / 64;
+                       offset.X = position.UpperLeftCorner.X;
 
-                               if (hcenter)
-                                       offset.X += (position.getWidth() - textDimension.Width) >> 1;
-                               ++iter;
-                               continue;
-                       }
+                       if (hcenter)
+                               offset.X += (position.getWidth() - textDimension.Width) >> 1;
+                       ++iter;
+                       continue;
+               }
 
+               if (n > 0 && visible)
+               {
                        // Calculate the glyph offset.
                        s32 offx = Glyphs[n-1].offset.X;
                        s32 offy = (font_metrics.ascender / 64) - Glyphs[n-1].offset.Y;
@@ -606,6 +636,9 @@ void CGUITTFont::draw(const core::stringw& text, const core::rect<s32>& position
                        page->render_positions.push_back(core::position2di(offset.X + offx, offset.Y + offy));
                        page->render_source_rects.push_back(glyph.source_rect);
                        Render_Map.set(glyph.glyph_page, page);
+                       u32 current_color = iter.getPos();
+                       if (current_color < colors.size())
+                               applied_colors.push_back(colors[current_color]);
                }
                offset.X += getWidthFromCharacter(currentChar);
 
@@ -624,8 +657,24 @@ void CGUITTFont::draw(const core::stringw& text, const core::rect<s32>& position
 
                CGUITTGlyphPage* page = n->getValue();
 
-               if (!use_transparency) color.color |= 0xff000000;
-               Driver->draw2DImageBatch(page->texture, page->render_positions, page->render_source_rects, clip, color, true);
+               if (shadow_offset) {
+                       for (size_t i = 0; i < page->render_positions.size(); ++i)
+                               page->render_positions[i] += core::vector2di(shadow_offset, shadow_offset);
+                       Driver->draw2DImageBatch(page->texture, page->render_positions, page->render_source_rects, clip, video::SColor(shadow_alpha,0,0,0), true);
+                       for (size_t i = 0; i < page->render_positions.size(); ++i)
+                               page->render_positions[i] -= core::vector2di(shadow_offset, shadow_offset);
+               }
+               for (size_t i = 0; i < page->render_positions.size(); ++i) {
+                       irr::video::SColor col;
+                       if (!applied_colors.empty()) {
+                               col = applied_colors[i < applied_colors.size() ? i : 0];
+                       } else {
+                               col = irr::video::SColor(255, 255, 255, 255);
+                       }
+                       if (!use_transparency)
+                               col.color |= 0xff000000;
+                       Driver->draw2DImage(page->texture, page->render_positions[i], page->render_source_rects[i], clip, col, true);
+               }
        }
 }