ecere/gfx: Font outline support
authorJerome St-Louis <jerome@ecere.com>
Sun, 3 Jul 2016 00:18:10 +0000 (20:18 -0400)
committerJerome St-Louis <jerome@ecere.com>
Sun, 3 Jul 2016 08:06:59 +0000 (04:06 -0400)
21 files changed:
ecere/Makefile
ecere/ecere.epj
ecere/src/gfx/Display.ec
ecere/src/gfx/DisplaySystem.ec
ecere/src/gfx/FontResource.ec
ecere/src/gfx/Surface.ec
ecere/src/gfx/drivers/CocoaOpenGLDisplayDriver.ec
ecere/src/gfx/drivers/Direct3D8DisplayDriver.ec
ecere/src/gfx/drivers/Direct3D9DisplayDriver.ec
ecere/src/gfx/drivers/DirectDrawDisplayDriver.ec
ecere/src/gfx/drivers/GDIDisplayDriver.ec
ecere/src/gfx/drivers/LFBDisplayDriver.ec
ecere/src/gfx/drivers/NCursesDisplayDriver.ec
ecere/src/gfx/drivers/OpenGLDisplayDriver.ec
ecere/src/gfx/drivers/Win32BitmapPrinterDisplayDriver.ec
ecere/src/gfx/drivers/Win32ConsoleDisplayDriver.ec
ecere/src/gfx/drivers/Win32PrinterDisplayDriver.ec
ecere/src/gfx/drivers/XDisplayDriver.ec
ecere/src/gfx/fontRendering.ec
ecere/src/gfx/imgDistMap.ec [new file with mode: 0644]
ecere/src/gui/Window.ec

index 1d6c13b..f2141d9 100644 (file)
@@ -119,6 +119,7 @@ _ECSOURCES2 = \
        src/gfx/Surface.ec \
        src/gfx/fontManagement.ec \
        src/gfx/fontRendering.ec \
+       src/gfx/imgDistMap.ec \
        src/gui/controls/Button.ec \
        src/gui/controls/CalendarControl.ec \
        src/gui/controls/DataBox.ec \
@@ -820,6 +821,9 @@ $(OBJ)fontManagement.sym: src/gfx/fontManagement.ec
 $(OBJ)fontRendering.sym: src/gfx/fontRendering.ec
        $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c $(call quote_path,src/gfx/fontRendering.ec) -o $(call quote_path,$@)
 
+$(OBJ)imgDistMap.sym: src/gfx/imgDistMap.ec
+       $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c $(call quote_path,src/gfx/imgDistMap.ec) -o $(call quote_path,$@)
+
 $(OBJ)Button.sym: src/gui/controls/Button.ec
        $(ECP) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c src/gui/controls/Button.ec -o $(OBJ)Button.sym
 
@@ -1238,6 +1242,9 @@ $(OBJ)fontManagement.c: src/gfx/fontManagement.ec $(OBJ)fontManagement.sym | $(S
 $(OBJ)fontRendering.c: src/gfx/fontRendering.ec $(OBJ)fontRendering.sym | $(SYMBOLS)
        $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c $(call quote_path,src/gfx/fontRendering.ec) -o $(call quote_path,$@) -symbols $(OBJ)
 
+$(OBJ)imgDistMap.c: src/gfx/imgDistMap.ec $(OBJ)imgDistMap.sym | $(SYMBOLS)
+       $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c $(call quote_path,src/gfx/imgDistMap.ec) -o $(call quote_path,$@) -symbols $(OBJ)
+
 $(OBJ)Button.c: src/gui/controls/Button.ec $(OBJ)Button.sym | $(SYMBOLS)
        $(ECC) $(CFLAGS) $(CECFLAGS) $(ECFLAGS) $(PRJ_CFLAGS) -c src/gui/controls/Button.ec -o $(OBJ)Button.c -symbols $(OBJ)
 
@@ -1722,6 +1729,9 @@ $(OBJ)fontManagement$(O): $(OBJ)fontManagement.c
 $(OBJ)fontRendering$(O): $(OBJ)fontRendering.c
        $(CC) $(CFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c $(call quote_path,$(OBJ)fontRendering.c) -o $(call quote_path,$@)
 
+$(OBJ)imgDistMap$(O): $(OBJ)imgDistMap.c
+       $(CC) $(CFLAGS) $(PRJ_CFLAGS) $(FVISIBILITY) -c $(call quote_path,$(OBJ)imgDistMap.c) -o $(call quote_path,$@)
+
 $(OBJ)Button.o: $(OBJ)Button.c
        $(CC) $(CFLAGS) $(PRJ_CFLAGS) -c $(OBJ)Button.c -o $(OBJ)Button.o
 
index 1771a3c..19c6df2 100644 (file)
@@ -1647,7 +1647,8 @@ if distributed with the Ecere SDK Windows installer.
                   "Resource.ec",
                   "Surface.ec",
                   "fontManagement.ec",
-                  "fontRendering.ec"
+                  "fontRendering.ec",
+                  "imgDistMap.ec"
                ],
                "Configurations" : [
                   {
index 586fa60..c42a6d2 100644 (file)
@@ -127,7 +127,7 @@ public:
    virtual bool ::MakeDDBitmap(DisplaySystem, Bitmap, bool);
 
    // Font loading
-   virtual Font ::LoadFont(DisplaySystem displaySystem, const char * faceName, float size, FontFlags flags);
+   virtual Font ::LoadFont(DisplaySystem displaySystem, const char * faceName, float size, FontFlags flags, float outlineSize, float outlineFade);
    virtual void ::UnloadFont(DisplaySystem, Font);
 
    // 2D Drawing
index dff6d7a..b49a03d 100644 (file)
@@ -79,7 +79,7 @@ public:
       return result;
    }
 
-   Font LoadFont(const char * faceName, float size, FontFlags flags)
+   Font LoadOutlineFont(const char * faceName, float size, FontFlags flags, float outlineSize, float outlineFade)
    {
       Font result = null;
       subclass(DisplayDriver) driver = this ? this.driver : ((subclass(DisplayDriver))class(LFBDisplayDriver));
@@ -93,7 +93,7 @@ public:
          {
             TrimLSpaces(fonts[c],fonts[c]);
             TrimRSpaces(fonts[c],fonts[c]);
-            if((result = driver.LoadFont(this, fonts[c], size, flags)))
+            if((result = driver.LoadFont(this, fonts[c], size, flags, outlineSize, outlineFade)))
             {
                break;
             }
@@ -103,6 +103,11 @@ public:
       return result;
    }
 
+   Font LoadFont(const char * faceName, float size, FontFlags flags)
+   {
+      return LoadOutlineFont(faceName, size, flags, 0, 0);
+   }
+
    void UnloadFont(Font font)
    {
       subclass(DisplayDriver) driver = this ? this.driver : ((subclass(DisplayDriver))class(LFBDisplayDriver));
index 1d7cb03..befecab 100644 (file)
@@ -11,7 +11,9 @@ public:
    property bool italic { set { flags.italic = value; } get { return this ? flags.italic : false; } };
    property bool underline { set { flags.underline = value; } get { return this ? flags.underline : false; } };
    property Font font { get { return this ? font : null; } };
-   property Window window { set { if(value) value.AddResource(this); } };
+   property Window window { set { if(value) { value.RemoveResource(this); value.AddResource(this); } }  };
+   property float outlineSize { set { outlineSize = value; } get { return this ? outlineSize : 0; } };
+   property float outlineFade { set { outlineFade = value; } get { return this ? outlineFade : 0; } };
 
 private:
    char * faceName;
@@ -19,16 +21,21 @@ private:
    float size;
    FontFlags flags;
    DisplaySystem displaySystem;
+   float outlineSize, outlineFade;
 
    void Load(FontResource copy, DisplaySystem displaySystem)
    {
       delete faceName;
-      faceName = CopyString(copy.faceName);
-      size = copy.size;
-      flags = copy.flags;
-      this.displaySystem = displaySystem;
-      if(faceName)
-         font = displaySystem.LoadFont(faceName, size, flags);
+      faceName = *&CopyString(copy.faceName);
+      *&size = *&copy.size;
+      *&flags = *&copy.flags;
+      *&outlineSize = *&copy.outlineSize;
+      *&outlineFade = *&copy.outlineFade;
+      if(faceName && displaySystem)
+      {
+         this.displaySystem = displaySystem;
+         font = displaySystem.LoadOutlineFont(faceName, size, flags, outlineSize, outlineFade);
+      }
    }
 
    void Reference(FontResource reference)
index 847e92b..ca04ea7 100644 (file)
@@ -131,12 +131,14 @@ private:
    bool blend;
    bool writeColor;
    ColorAlpha blitTint;
+   ColorAlpha outlineColor;
 
    blitTint = white;
 
    blend = true;
    writeColor = true;
    alphaWrite = blend;
+   outlineColor = black;
 
    ~Surface()
    {
@@ -162,6 +164,11 @@ public:
          return ((LFBSurface)driverData).bitmap;
       }
    }
+   property ColorAlpha outlineColor
+   {
+      set { outlineColor = value; }
+      get { return outlineColor; }
+   }
 
    ColorAlpha GetPixel(int x, int y)
    {
index 0e640d0..45b6239 100644 (file)
@@ -51,6 +51,7 @@ class SurfaceData : struct
    bool opaqueText;
    int  xOffset;
    bool writingText;
+   bool writingOutline;
 
    float foreground[4], background[4], bitmapMult[4];
 };
@@ -973,9 +974,9 @@ class CocoaOpenGLDisplayDriver : DisplayDriver
       LFBDisplayDriver::UnloadFont(displaySystem, font);
    }
 
-   Font LoadFont(DisplaySystem displaySystem, const char * faceName, float size, FontFlags flags)
+   Font LoadFont(DisplaySystem displaySystem, const char * faceName, float size, FontFlags flags, float outlineSize, float outlineFade)
    {
-      Font font = LFBDisplayDriver::LoadFont(displaySystem, faceName, size, flags);
+      Font font = LFBDisplayDriver::LoadFont(displaySystem, faceName, size, flags, outlineSize, outlineFade);
 
       printf("CocoaOpenGLDisplayDriver:LoadFont(%s):%p %s:%i\n", faceName, font, __FILE__, __LINE__);
 
index 96e4989..817f9e6 100644 (file)
@@ -76,6 +76,7 @@ static class D3D8Surface : struct
    bool opaqueText;
    int xOffset;
    bool writingText;
+   bool writingOutline;
 
    ColorAlpha background;
 };
@@ -982,9 +983,9 @@ class Direct3D8DisplayDriver : DisplayDriver
       ((subclass(DisplayDriver))class(LFBDisplayDriver)).UnloadFont(displaySystem, font);
    }
 
-   Font LoadFont(DisplaySystem displaySystem, const char * faceName, float size, FontFlags flags)
+   Font LoadFont(DisplaySystem displaySystem, const char * faceName, float size, FontFlags flags, float outlineSize, float outlineFade)
    {
-      return ((subclass(DisplayDriver))class(LFBDisplayDriver)).LoadFont(displaySystem, faceName, size, flags);
+      return ((subclass(DisplayDriver))class(LFBDisplayDriver)).LoadFont(displaySystem, faceName, size, flags, outlineSize, outlineFade);
    }
 
    void TextFont(Display display, Surface surface, Font font)
index 0be479f..13d5ae5 100644 (file)
@@ -82,6 +82,7 @@ static class D3DSurface : struct
    bool opaqueText;
    int xOffset;
    bool writingText;
+   bool writingOutline;
 
    ColorAlpha background;
 };
@@ -998,9 +999,9 @@ class Direct3D9DisplayDriver : DisplayDriver
       ((subclass(DisplayDriver))class(LFBDisplayDriver)).UnloadFont(displaySystem, font);
    }
 
-   Font LoadFont(DisplaySystem displaySystem, const char * faceName, float size, FontFlags flags)
+   Font LoadFont(DisplaySystem displaySystem, const char * faceName, float size, FontFlags flags, float outlineSize, float outlineFade)
    {
-      return ((subclass(DisplayDriver))class(LFBDisplayDriver)).LoadFont(displaySystem, faceName, size, flags);
+      return ((subclass(DisplayDriver))class(LFBDisplayDriver)).LoadFont(displaySystem, faceName, size, flags, outlineSize, outlineFade);
    }
 
    void TextFont(Display display, Surface surface, Font font)
index 4ab7750..9f1cfa0 100644 (file)
@@ -573,7 +573,7 @@ class DirectDrawDisplayDriver : DisplayDriver
    #endif
    }
 
-   Font LoadFont(DisplaySystem displaySystem, const char * faceName, float size, FontFlags flags)
+   Font LoadFont(DisplaySystem displaySystem, const char * faceName, float size, FontFlags flags, float outlineSize, float outlineFade)
    {
    #ifdef USE_GDI_FONT
       void * font;
@@ -586,7 +586,7 @@ class DirectDrawDisplayDriver : DisplayDriver
       ReleaseDC(null, hdc);
       return font;
    #else
-      Font font = ((subclass(DisplayDriver))class(LFBDisplayDriver)).LoadFont(displaySystem, faceName, size, flags);
+      Font font = ((subclass(DisplayDriver))class(LFBDisplayDriver)).LoadFont(displaySystem, faceName, size, flags, outlineSize, outlineFade);
       /*if(font)
       {
          font.bitmap.pixelFormat = C8;
index 5b9998f..19dab0d 100644 (file)
@@ -74,6 +74,8 @@ static class GDIFont : struct
    char faceName[512];
    FontFlags flags;
    float size;
+   float outlineSize;
+   float outlineFade;
    int ascent, descent;
    float scale;
 
@@ -849,10 +851,10 @@ class GDIDisplayDriver : DisplayDriver
       Filter(display, surface, src, dx, dy, sx, sy, w, h, sw, sh);
    }
 
-   Font LoadFont(DisplaySystem displaySystem, const char * faceName, float size, FontFlags flags)
+   Font LoadFont(DisplaySystem displaySystem, const char * faceName, float size, FontFlags flags, float outlineSize, float outlineFade)
    {
       if(false) //display.alphaBlend)
-         return ((subclass(DisplayDriver))class(LFBDisplayDriver)).LoadFont(displaySystem, faceName, size, flags);
+         return ((subclass(DisplayDriver))class(LFBDisplayDriver)).LoadFont(displaySystem, faceName, size, flags, outlineSize, outlineFade);
       else
       {
          GDIFont font { };
@@ -901,7 +903,7 @@ class GDIDisplayDriver : DisplayDriver
          if(!gdiFont.font)
          {
             gdiFont.font = ((subclass(DisplayDriver))class(LFBDisplayDriver)).LoadFont(display.displaySystem,
-               gdiFont.faceName, gdiFont.size, gdiFont.flags);
+               gdiFont.faceName, gdiFont.size, gdiFont.flags, gdiFont.outlineSize, gdiFont.outlineFade);
          }
          ((subclass(DisplayDriver))class(LFBDisplayDriver)).TextFont(display, surface, gdiFont.font);
       }
@@ -930,7 +932,7 @@ class GDIDisplayDriver : DisplayDriver
          if(!gdiFont.font)
          {
             gdiFont.font = ((subclass(DisplayDriver))class(LFBDisplayDriver)).LoadFont(display.displaySystem,
-               gdiFont.faceName, gdiFont.size, gdiFont.flags);
+               gdiFont.faceName, gdiFont.size, gdiFont.flags, gdiFont.outlineSize, gdiFont.outlineFade);
          }
          if(surface.textOpacity)
          {
index 397ee7a..644df65 100644 (file)
@@ -113,6 +113,7 @@ public:
    bool opaqueText;
    int xOffset;
    bool writingText;
+   bool writingOutline;
 
    Bitmap bitmap;
 
@@ -2014,10 +2015,10 @@ public class LFBDisplayDriver : DisplayDriver
       delete font;
    }
 
-   Font LoadFont(DisplaySystem displaySystem, const char * faceName, float size, FontFlags flags)
+   Font LoadFont(DisplaySystem displaySystem, const char * faceName, float size, FontFlags flags, float outlineSize, float outlineFade)
    {
 #ifndef ECERE_NOTRUETYPE
-      return Font::Load(displaySystem, faceName, size, flags);
+      return Font::Load(displaySystem, faceName, size, flags, outlineSize, outlineFade);
 #else
       return { };
 #endif
@@ -2040,7 +2041,7 @@ public class LFBDisplayDriver : DisplayDriver
          if(width)
          {
             int w = 0, advance = 0;
-            font.ProcessString(displaySystem, (const byte *)text, len, null, null, null, &w, 0, prevGlyph, rPrevGlyph, &advance);
+            font.ProcessString(displaySystem, (const byte *)text, len, false, null, null, &w, 0, prevGlyph, rPrevGlyph, &advance);
             if(adv) *adv = advance >> 6;
             //*width = (w + 64 - w % 64) >> 6;
             *width = w >> 6;
@@ -2056,13 +2057,6 @@ public class LFBDisplayDriver : DisplayDriver
       }
    }
 
-#if !defined(ECERE_NOTRUETYPE)
-   void ::OutputGlyph(Surface surface, Display display, int x, int y, Glyph glyph, Bitmap bitmap)
-   {
-      surface.driver.Blit(display, surface, bitmap, x + glyph.left, y + glyph.top, glyph.x, glyph.y, glyph.w, glyph.h);
-   }
-#endif
-
    void WriteText(Display display, Surface surface, int x, int y, const char * text, int len, int prevGlyph, int * rPrevGlyph)
    {
       LFBSurface lfbSurface = surface.driverData;
@@ -2093,7 +2087,7 @@ public class LFBDisplayDriver : DisplayDriver
          lfbSurface.writingText = true;
 #if !defined(ECERE_NOTRUETYPE)
          x <<= 6;
-         lfbSurface.font.ProcessString(surface.displaySystem, (const byte *)text, len, OutputGlyph, surface, display, &x, y, prevGlyph, rPrevGlyph, &adv);
+         lfbSurface.font.ProcessString(surface.displaySystem, (const byte *)text, len, true, surface, display, &x, y, prevGlyph, rPrevGlyph, &adv);
          x += adv;
 #endif
          lfbSurface.writingText = false;
index 99a0302..02e63c7 100644 (file)
@@ -339,7 +339,7 @@ class NCursesDisplayDriver : DisplayDriver
 
    }
 
-   Font LoadFont(DisplaySystem displaySystem, const char * faceName, float size, FontFlags flags)
+   Font LoadFont(DisplaySystem displaySystem, const char * faceName, float size, FontFlags flags, float outlineSize, float outlineFade)
    {
       return (void *) true;
    }
index 14e99db..0229ea3 100644 (file)
@@ -1615,6 +1615,7 @@ class OGLSurface : struct
    bool opaqueText;
    int xOffset;
    bool writingText;
+   bool writingOutline;
 
    float foreground[4], background[4], bitmapMult[4];
 } OGLSurface;
@@ -3443,12 +3444,12 @@ class OpenGLDisplayDriver : DisplayDriver
       ((subclass(DisplayDriver))class(LFBDisplayDriver)).UnloadFont(displaySystem, font);
    }
 
-   Font LoadFont(DisplaySystem displaySystem, const char * faceName, float size, FontFlags flags)
+   Font LoadFont(DisplaySystem displaySystem, const char * faceName, float size, FontFlags flags, float outlineSize, float outlineFade)
    {
       Font font;
       OGLSystem oglSystem = displaySystem.driverData;
       oglSystem.loadingFont = true;
-      font = ((subclass(DisplayDriver))class(LFBDisplayDriver)).LoadFont(displaySystem, faceName, size, flags);
+      font = ((subclass(DisplayDriver))class(LFBDisplayDriver)).LoadFont(displaySystem, faceName, size, flags, outlineSize, outlineFade);
       return font;
    }
 
@@ -3478,6 +3479,14 @@ class OpenGLDisplayDriver : DisplayDriver
       oglSurface.writingText = true;
 
       glEnable(GL_TEXTURE_2D);
+      if(surface.font.outlineSize)
+      {
+         ColorAlpha outlineColor = surface.outlineColor;
+         glColor4ub(outlineColor.color.r, outlineColor.color.g, outlineColor.color.b, outlineColor.a);
+         oglSurface.writingOutline = true;
+         ((subclass(DisplayDriver))class(LFBDisplayDriver)).WriteText(display, surface, x, y, text, len, prevGlyph, rPrevGlyph);
+         oglSurface.writingOutline = false;
+      }
       glColor4fv(oglSurface.foreground);
 
       ((subclass(DisplayDriver))class(LFBDisplayDriver)).WriteText(display, surface, x, y, text, len, prevGlyph, rPrevGlyph);
index 6d3e37a..ca717dd 100644 (file)
@@ -793,7 +793,7 @@ class Win32BitmapPrinterDisplayDriver : DisplayDriver
       Filter(display, surface, src, dx, dy, sx, sy, w, h, sw, sh);
    }
 
-   Font LoadFont(DisplaySystem displaySystem, const char * faceName, float size, FontFlags flags)
+   Font LoadFont(DisplaySystem displaySystem, const char * faceName, float size, FontFlags flags, float outlineSize, float outlineFade)
    {
       Win32BitmapPrinterSystem gdiSystem = displaySystem.driverData;
       HDC hdc = gdiSystem.hdc;
index c3be3a9..fbd3ecc 100644 (file)
@@ -337,7 +337,7 @@ class Win32ConsoleDisplayDriver : DisplayDriver
 
    }
 
-   Font LoadFont(DisplaySystem displaySystem, const char * string, float size, FontFlags flags)
+   Font LoadFont(DisplaySystem displaySystem, const char * string, float size, FontFlags flags, float outlineSize, float outlineFade)
    {
       return (void *) bool::true;
    }
index 8034060..9fbe9ea 100644 (file)
@@ -831,7 +831,7 @@ class Win32PrinterDisplayDriver : DisplayDriver
       Filter(display, surface, src, dx, dy, sx, sy, w, h, sw, sh);
    }
 
-   Font LoadFont(DisplaySystem displaySystem, const char * faceName, float size, FontFlags flags)
+   Font LoadFont(DisplaySystem displaySystem, const char * faceName, float size, FontFlags flags, float outlineSize, float outlineFade)
    {
       Win32PrinterSystem gdiSystem = displaySystem.driverData;
       HDC hdc = gdiSystem.hdc;
index 93bb6cc..d12c422 100644 (file)
@@ -75,6 +75,7 @@ class XSurface : struct
    bool opaqueText;
    int xOffset;
    bool writingText;
+   bool writingOutline;
 
    ColorAlpha foreground, background;
    bool opaque;
@@ -1658,10 +1659,10 @@ class XDisplayDriver : DisplayDriver
       }
    }
 
-   Font LoadFont(DisplaySystem displaySystem, const char * faceName, float size, FontFlags flags)
+   Font LoadFont(DisplaySystem displaySystem, const char * faceName, float size, FontFlags flags, float outlineSize, float outlineFade)
    {
       Font font;
-      font = ((subclass(DisplayDriver))class(LFBDisplayDriver)).LoadFont(displaySystem, faceName, size, flags);
+      font = ((subclass(DisplayDriver))class(LFBDisplayDriver)).LoadFont(displaySystem, faceName, size, flags, outlineSize, outlineFade);
       return font;
    }
 
index 4d1667b..4a242a6 100644 (file)
@@ -28,6 +28,10 @@ static uint16 * utf16 = null;
 
 #if !defined(ECERE_NOTRUETYPE)
 
+#if !defined(ECERE_VANILLA)
+import "imgDistMap"
+#endif
+
 #define MAX_FONT_LINK_ENTRIES   10
 
 static HB_Script theCurrentScript;
@@ -92,7 +96,6 @@ static void hb_getAdvances(HB_Font font, const HB_Glyph * glyphs, uint numGlyphs
          {
             glFont.glyphPacks.Add((pack = GlyphPack { key = (uintptr)packNo }));
             pack.Render(glFont, fontEntryNum, glFont.displaySystem);
-            pack.bitmap.alphaBlend = true;
          }
          lastPack = packNo;
       }
@@ -179,7 +182,6 @@ static void hb_getGlyphMetrics(HB_Font font, HB_Glyph theGlyph, HB_GlyphMetrics
             pack = { key = (uintptr)packNo };
             glFont.glyphPacks.Add(pack);
             pack.Render(glFont, fontEntryNum, glFont.displaySystem);
-            pack.bitmap.alphaBlend = true;
          }
          lastPack = packNo;
       }
@@ -364,6 +366,7 @@ struct Glyph
 {
    int ax, ay;
    int x, y;
+   int ox, oy; // Outline
    int w, h;
    int left, top;
    int bx, by;
@@ -374,56 +377,32 @@ struct Glyph
 class GlyphPack : BTNode
 {
    Glyph glyphs[256];
-   Bitmap bitmap { };
+   Bitmap bitmap { transparent = true, alphaBlend = true };
+   Bitmap outline;
    int cellWidth, cellHeight;
+   int oCellWidth, oCellHeight;
+
+   ~GlyphPack()
+   {
+      delete outline;
+   }
 
    void Render(Font font, int startFontEntry, DisplaySystem displaySystem)
    {
 #if !defined(ECERE_NOTRUETYPE)
       unichar c;
-      int maxWidth, maxHeight;
+      int maxWidth = 0, maxHeight = 0;
       int cellWidth, cellHeight;
-      int width, height;
+      int oCellWidth = 0, oCellHeight = 0;
+      int width, height, oWidth = 0, oHeight = 0;
       FontEntry fontEntry = null;
       FT_Face faces[128];
       float scales[128];
+      float outlineSize = font.outlineSize;
+      int padding = 1 + (int)outlineSize;
       bool isGlyph = ((uint)key & 0x80000000) != 0;
-      //int curScript = ((uint)key & 0x7F000000) >> 24;
       unichar testChar = 0;
-      /*
-      if(isGlyph)
-      {
-         switch(curScript)
-         {
-            case HB_Script_Arabic:
-               testChar = 0x621;
-               // printf("\nRendering arabic in %s (%d)\n", faceName, key & 0xFFFFFF);
-               break;
-            case HB_Script_Devanagari:
-               testChar = 0x905;
-               break;
-            case 60: testChar = 'あ'; break;
-            case 61: testChar = 0x3400; break;
-         }
-      }
-      */
-      /*
-      FT_GlyphSlot slot;
-      FT_Matrix matrix;
-      FT_Vector pen = { 0, 0 };
-      if(fakeItalic)
-      {
-         matrix.xx = (FT_Fixed)( 1.0 * 0x10000L );
-         matrix.xy = (FT_Fixed)( 0.3 * 0x10000L );
-         matrix.yx = (FT_Fixed)( 0.0 * 0x10000L );
-         matrix.yy = (FT_Fixed)( 1.0 * 0x10000L );
-         FT_Set_Transform( fontEntry.face, &matrix, &pen );
-      }
-      FT_Set_Char_Size( fontEntry.face, (int)(size * 64), (int)(size * 64), 96, 96);
-      */
-
-      maxWidth = 0;
-      maxHeight = 0;
+      Bitmap bitmap = this.bitmap;
 
       for(c = 0; c < MAX_FONT_LINK_ENTRIES; c++)
       {
@@ -441,7 +420,6 @@ class GlyphPack : BTNode
                matrix.yy = (FT_Fixed)( 1.0 * 0x10000L );
                FT_Set_Transform(fontEntry.face, &matrix, &pen);
             }
-            //FT_Set_Char_Size(fontEntry.face, (int)(size * 64), (int)(size * 64), 96, 96);
             fontEntry.scale = FaceSetCharSize(fontEntry.face, font.size);
             if(!font.scale)
                font.scale = fontEntry.scale;
@@ -505,34 +483,44 @@ class GlyphPack : BTNode
          {
             maxWidth = Max(maxWidth, ((faces[c]->glyph->metrics.width + 64 + (64 - (faces[c]->glyph->metrics.width & 0x3F))) >> 6));
             maxHeight = Max(maxHeight, ((faces[c]->glyph->metrics.height + 64 + (64 - (faces[c]->glyph->metrics.height & 0x3F))) >> 6));
-            //maxHeight = Max(maxHeight, ((faces[c]->glyph->metrics.height) >> 6));
          }
       }
       this.cellWidth = cellWidth = maxWidth;
       this.cellHeight = cellHeight = maxHeight;
 
-      width = maxWidth * 16;
-      height = maxHeight * 8;
+      width = pow2i(maxWidth * 16);
+      height = pow2i(maxHeight * 8);
 
-      if(true)
+      if(outlineSize)
       {
-         width = pow2i(width);
-         height = pow2i(height);
+         oCellWidth = 2*padding + cellWidth;
+         oCellHeight = 2*padding + cellHeight;
+         oWidth = pow2i(oCellWidth * 16);
+         oHeight = pow2i(oCellHeight * 8);
+         outline = { transparent = true, alphaBlend = true };
       }
 
-      if(bitmap.Allocate(null, width, height, 0, pixelFormatAlpha, false /*true*/))
+      if(bitmap.Allocate(null, width, height, 0, pixelFormatAlpha, false) &&
+         (!outline || outline.Allocate(null, oWidth, oHeight, 0, pixelFormatAlpha, false)))
       {
-         Bitmap bitmap = this.bitmap;
-
-         bitmap.transparent = true;
+         float fade = font.outlineFade;
+         float alphaFactor = 1.0f / (0.2f + fade);
+         // float intensityFactor = 1.0f / (0.2f + outlineSize);
+         float range = outlineSize, rangeInv = 1.0f / range;
+         float * distanceMap = null;
+         byte * padded = null;
+         if(outline)
+         {
+            distanceMap = new float[oCellWidth * oCellHeight];
+            padded = new byte[oCellWidth * oCellHeight];
+         }
 
          for(c = 0; c < 128; c++)
          {
             FT_Int i, j, p, q;
             FT_Int xMax, yMax;
-            int sx = (c % 16) * cellWidth;
-            int sy = (c / 16) * cellHeight;
-            int x, y;
+            int gx = ((uint)c & 0xF), gy = ((uint)c >> 4);
+            int sx = gx * cellWidth, sy = gy * cellHeight;
             byte * picture = (byte *)bitmap.picture;
             Glyph * glyph = &glyphs[c];
             FT_GlyphSlot slot = null;
@@ -540,7 +528,6 @@ class GlyphPack : BTNode
             if(faces[c])
             {
                double em_size = 1.0 * faces[c]->units_per_EM;
-               //double x_scale = faces[c]->size->metrics.x_ppem / em_size;
                double y_scale = em_size ? (faces[c]->size->metrics.y_ppem / em_size) : 1;
                double ascender = faces[c]->ascender * y_scale;
                slot = faces[c]->glyph;
@@ -549,40 +536,20 @@ class GlyphPack : BTNode
 
                FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL);
 
-               x = sx;
-               y = sy;
-               //printf("%d, %d\n", maxHeight, faces[c]->size->metrics.height >> 6);
-
                glyph->left = slot->bitmap_left;
-               // glyph->top = ((64 + (64 - faces[c]->glyph->metrics.height & 0x3F)) >> 6) + (int)(ascender - slot->bitmap_top) + height - (faces[c]->size->metrics.height >> 6);
-               // glyph->top = (int)(ascender - slot->bitmap_top) + 2 * (height - maxHeight);
-               //glyph->top = (int)(ascender - slot->bitmap_top) + 2 * ((((faces[c]->size->metrics.height + 64 + (64 - faces[c]->size->metrics.height & 0x3F)) >> 6)) - height);
-               //glyph->top = (int)(ascender - slot->bitmap_top) + (height - (faces[c]->size->metrics.height >> 6));
-
-               //glyph->top = (int)(ascender + (height *64 - /*faces[0]->size->metrics.height - */faces[c]->size->metrics.height) / 64.0  + 0.5) - slot->bitmap_top;
-               //glyph->top = (int)(ascender + (height *64 - /*faces[0]->size->metrics.height - */faces[c]->size->metrics.height) / 64.0  + 0.5) - slot->bitmap_top;
-
-               //glyph->top = (int)((ascender - slot->bitmap_top) + (height * 64 - maxHeight * 64 + faces[c]->glyph->metrics.height - faces[c]->glyph->metrics.height) / 64);
-
-               //glyph->top = (int)(ascender - slot->bitmap_top); // + ((faces[c]->size->metrics.height >> 6) - (faces[0]->size->metrics.height >> 6)) + (height - (faces[c]->size->metrics.height >> 6));
-               //glyph->top = (int)(ascender - slot->bitmap_top); // + ((faces[c]->size->metrics.height >> 6) - (faces[0]->size->metrics.height >> 6)) + (height - (faces[c]->size->metrics.height >> 6));
-
-               //glyph->top = (int)(ascender - slot->bitmap_top);// + (height - maxHeight);
                glyph->top = (int)(ascender - slot->bitmap_top) + (int)(font.height - (faces[c]->size->metrics.height >> 6)) / 2;
 
-               // printf("[char: %d] mode: %d, width: %d, height: %d, pitch: %d\n", key + c, slot->bitmap.pixel_mode, slot->bitmap.width, slot->bitmap.rows, slot->bitmap.pitch);
-               xMax = x + slot->bitmap.width;
-               yMax = y + slot->bitmap.rows;
+               xMax = sx + slot->bitmap.width;
+               yMax = sy + slot->bitmap.rows;
 
                {
                   int total = 0;
                   int numPixels = 0;
-                  //int max;
                   if(slot->bitmap.pixel_mode != FT_PIXEL_MODE_MONO)
                   {
-                     for(j = y, q = 0; j<yMax; j++)
+                     for(j = sy, q = 0; j<yMax; j++)
                      {
-                        for(p = 0, i = x; i<xMax; i++)
+                        for(p = 0, i = sx; i<xMax; i++)
                         {
                            byte value = ((byte *)slot->bitmap.buffer)[q + p++];
                            if(value > 32)
@@ -594,12 +561,11 @@ class GlyphPack : BTNode
                         q += slot->bitmap.pitch;
                      }
                   }
-                  //max = numPixels ? (total / numPixels) : 1;
 
-                  for(j = y, q = 0; j<yMax; j++)
+                  for(j = sy, q = 0; j<yMax; j++)
                   {
                      int bit = 0x80;
-                     for(p = 0, i = x; i<xMax; i++)
+                     for(p = 0, i = sx; i<xMax; i++)
                      {
                         if(slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO)
                         {
@@ -610,12 +576,60 @@ class GlyphPack : BTNode
                         else
                         {
                            byte value = ((byte *)slot->bitmap.buffer)[q + p++];
-                           picture[j * bitmap.stride + i] = /*(max < 192) ? Min(255, value * 192/max) :*/ value;
+                           picture[j * bitmap.stride + i] = value;
                         }
                      }
                      q += slot->bitmap.pitch;
                   }
                }
+               if(outline)
+               {
+                  // Generate outline from distance map
+                  float * dmap = distanceMap;
+                  byte *dst = outline.picture + gy * oCellHeight * oWidth + gx * oCellWidth;
+                  int x, y;
+                  byte * core = picture + gy * cellHeight * width + gx * cellWidth, * p;
+                  memset(padded, 0, oCellWidth * oCellHeight);
+                  if(slot->bitmap.width)
+                  {
+                     byte * src = padded + padding * oCellWidth + padding;
+                     picture = core;
+                     for(y = 0; y < cellHeight; y++)
+                     {
+                        byte * pic = picture;
+                        p = src;
+                        for(x = 0; x < cellWidth; x++, p++, pic++)
+                           *p = *pic;
+                        picture += width;
+                        src += oCellWidth;
+                     }
+                  }
+                  imgDistMapBuild(distanceMap, padded, oCellWidth, oCellHeight, 1, oCellWidth);
+
+                  //core -= padding * width;
+                  for(y = 0; y < oCellHeight; y++, dst += oWidth)
+                  {
+                     byte * dstRow = dst;
+                     p = core - padding;
+                     for(x = 0; x < oCellWidth; x++, dstRow++, dmap++, p++)
+                     {
+                        float rangeBase = (range - *dmap) * rangeInv, alpha = alphaFactor * rangeBase;
+                        *dstRow = (byte)( Max( 0.0f, Min( 255.0f, alpha * 255.0f ) ) + 0.5f);
+                        /*
+                        if(y >= padding && y < cellWidth + padding && x >= padding && x < cellWidth + padding)
+                        {
+                           float intensity = Max( (float) *p * (1.0f/255.0f), intensityFactor * rangeBase );
+                           byte v = (byte)( Max( 0.0f, Min( 255.0f, intensity * 255.0f ) ) + 0.5f);
+                           *p = v;
+                        }
+                        */
+                     }
+                     //core += width;
+                  }
+
+                  glyph->ox = gx * oCellWidth;
+                  glyph->oy = gy * oCellHeight;
+               }
             }
             glyph->x = sx;
             glyph->y = sy;
@@ -634,6 +648,10 @@ class GlyphPack : BTNode
             }
             glyph->scale = scales[c];
          }
+
+         delete padded;
+         delete distanceMap;
+
          #if 0
          {
             int c;
@@ -643,16 +661,25 @@ class GlyphPack : BTNode
                bitmap.palette[c] = ColorAlpha { 255, { (byte)c,(byte)c,(byte)c } };
             bitmap.pixelFormat = pixelFormat8;
 
-            /*
-            //strcpy(fileName, faceName);
-            if(flags)
-               strcat(fileName, "Bold");
-            */
-            sprintf(fileName, "font%d", fid++);
+            sprintf(fileName, "font%d", fid);
             ChangeExtension(fileName, "pcx", fileName);
 
             bitmap.Save(fileName, null, 0);
             bitmap.pixelFormat = pixelFormatAlpha;
+
+            if(outline)
+            {
+               for(c = 0; c<256; c++)
+                  outline.palette[c] = ColorAlpha { 255, { (byte)c,(byte)c,(byte)c } };
+               outline.pixelFormat = pixelFormat8;
+
+               sprintf(fileName, "outline%d", fid);
+               ChangeExtension(fileName, "pcx", fileName);
+
+               outline.Save(fileName, null, 0);
+               outline.pixelFormat = pixelFormatAlpha;
+            }
+            fid++;
          }
          #endif
 
@@ -667,10 +694,13 @@ class GlyphPack : BTNode
             || displaySystem.driver == class(Direct3D8DisplayDriver)
             || displaySystem.driver == class(Direct3D9DisplayDriver)
 #endif
-
                )
 #endif
+            {
                bitmap.MakeDD(displaySystem);
+               if(outline)
+                  outline.MakeDD(displaySystem);
+            }
             displaySystem.Unlock();
          }
       }
@@ -742,6 +772,8 @@ public class Font : struct
    char faceName[512];
    FontFlags flags;
    float size;
+   float outlineSize;
+   float outlineFade;
    int ascent, descent;
    float scale;
 
@@ -793,12 +825,14 @@ public class Font : struct
       get { return (int)(this ? descent * scale : 0); }
    }
 
-   void Setup(DisplaySystem displaySystem, const String faceName, float size, FontFlags flags)
+   void Setup(DisplaySystem displaySystem, const String faceName, float size, FontFlags flags, float outlineSize, float outlineFade)
    {
       strcpy(this.faceName, faceName);
       this.flags = flags;
       this.displaySystem = displaySystem;
       this.size = size;
+      this.outlineSize = outlineSize;
+      this.outlineFade = outlineFade;
    }
 
    bool LoadEntry(FaceInfo info)
@@ -842,7 +876,7 @@ public class Font : struct
       return result;
    }
 
-   Font ::Load(DisplaySystem displaySystem, const char * faceName, float size, FontFlags flags)
+   Font ::Load(DisplaySystem displaySystem, const char * faceName, float size, FontFlags flags, float outlineSize, float outlineFade)
    {
       Font result = null;
       Array<FaceInfo> infos = ResolveFont(faceName, size, flags);
@@ -850,7 +884,7 @@ public class Font : struct
       {
          Font font { };
          bool success = false;
-         font.Setup(displaySystem, faceName, size, flags);
+         font.Setup(displaySystem, faceName, size, flags, outlineSize, outlineFade);
          for(f : infos)
             success |= font.LoadEntry(f);
          if(success)
@@ -867,17 +901,21 @@ public class Font : struct
    }
 
    void ProcessString(DisplaySystem displaySystem, const byte * text, int len,
-                      void (* callback)(Surface surface, Display display, int x, int y, Glyph glyph, Bitmap bitmap),
+                      bool output,
                       Surface surface, Display display, int * x, int y, int prevGlyph, int * rPrevGlyph, int * advance)
    {
 #if !defined(ECERE_NOTRUETYPE)
       if(this && fontEntries[0])
       {
+         LFBSurface lfbSurface = surface ? surface.driverData : null;
          int previousGlyph = prevGlyph;
          FT_Face previousFace = 0;
          int c, nb, glyphIndex = 0;
-         unichar lastPack = 0;
+         bool writingOutline = lfbSurface && lfbSurface.writingOutline;
+         int padding = writingOutline ? 1 + (int)surface.font.outlineSize : 0;
+         unichar lastPack = writingOutline ? -1 : 0;
          GlyphPack pack = asciiPack;
+         Bitmap bitmap = writingOutline ? pack.outline : pack.bitmap;
          int wc = 0;
          uint * glyphs = null;
          int numGlyphs = 0;
@@ -888,8 +926,6 @@ public class Font : struct
          Glyph * lastGlyph = null;
          int lastAX = 0;
 
-         pack.bitmap.alphaBlend = true;
-
          for(c = 0; c < len || (numGlyphs && (rightToLeft ? (glyphIndex >= 0) : (glyphIndex < numGlyphs)));)
          {
             uint glyphNo = 0;
@@ -1098,7 +1134,7 @@ public class Font : struct
                      pack.Render(this, fontEntryNum, displaySystem);
                   }
                }
-               pack.bitmap.alphaBlend = true;
+               bitmap = writingOutline ? pack.outline : pack.bitmap;
                lastPack = packNo;
             }
             if(pack)
@@ -1126,8 +1162,9 @@ public class Font : struct
                previousGlyph = glyph->glyphNo;
                previousFace = curFontEntry ? curFontEntry.face : null;
 
-               if(callback)
-                  callback(surface, display, ((*x) >> 6), y + (oy >> 6), glyph, pack.bitmap);
+               if(output)
+                  surface.driver.Blit(display, surface, bitmap, ((*x) >> 6) + glyph->left - writingOutline * padding, y + (oy >> 6) + glyph->top - writingOutline * padding,
+                     writingOutline ? glyph->ox : glyph->x, writingOutline ? glyph->oy : glyph->y, glyph->w + writingOutline + 2 * padding, glyph->h + writingOutline + 2 * padding);
                *x += ax;
 
                lastGlyph = glyph;
@@ -1144,11 +1181,8 @@ public class Font : struct
                *advance = w - lastAX;
          }
          if(rPrevGlyph) *rPrevGlyph = previousGlyph;
-      }
-      if(surface)
-      {
-         LFBSurface lfbSurface = surface.driverData;
-         lfbSurface.xOffset = 0;
+         if(lfbSurface)
+            lfbSurface.xOffset = 0;
       }
 #endif
    }
diff --git a/ecere/src/gfx/imgDistMap.ec b/ecere/src/gfx/imgDistMap.ec
new file mode 100644 (file)
index 0000000..e8bb81f
--- /dev/null
@@ -0,0 +1,387 @@
+import "instance"
+
+#include <math.h>
+
+// Based on The ‘‘dead reckoning’’ signed distance transform
+// by George J. Grevera
+
+struct MapDistMapPoint
+{
+  float distance;
+  float x, y;
+  float bias;
+};
+
+#define IMGDISTMAP_SQRT2 (1.41421356237f)
+#define IMGDISTMAP_SQRT5 (2.2360679775f)
+#define IMGDISTMAP_SQRT10 (3.16227766017f)
+#define IMGDISTMAP_SQRT13 (3.60555127546f)
+
+
+#if defined(__GNUC__)
+ #define ALWAYS_INLINE __attribute__((always_inline))
+#else
+ #define ALWAYS_INLINE
+#endif
+
+
+static inline ALWAYS_INLINE void imgDistMapCheck( MapDistMapPoint pt, float fx, float fy, MapDistMapPoint ptref, float delta )
+{
+  float dx, dy;
+  if( ( ptref.distance + delta ) < pt.distance )
+  {
+    pt.x = ptref.x;
+    pt.y = ptref.y;
+    dx = ptref.x - fx;
+    dy = ptref.y - fy;
+    pt.distance = sqrtf( ( dx * dx ) + ( dy * dy ) ) + ptref.bias;
+    pt.bias = ptref.bias;
+  }
+}
+
+#define IMGDIST_OPFLAGS_XP1_SHIFT (0)
+#define IMGDIST_OPFLAGS_XP2_SHIFT (1)
+#define IMGDIST_OPFLAGS_XP3_SHIFT (2)
+#define IMGDIST_OPFLAGS_XM1_SHIFT (3)
+#define IMGDIST_OPFLAGS_XM2_SHIFT (4)
+#define IMGDIST_OPFLAGS_XM3_SHIFT (5)
+#define IMGDIST_OPFLAGS_YP1_SHIFT (6)
+#define IMGDIST_OPFLAGS_YP2_SHIFT (7)
+#define IMGDIST_OPFLAGS_YP3_SHIFT (8)
+#define IMGDIST_OPFLAGS_YM1_SHIFT (9)
+#define IMGDIST_OPFLAGS_YM2_SHIFT (10)
+#define IMGDIST_OPFLAGS_YM3_SHIFT (11)
+
+#define IMGDIST_OPFLAGS_XP1 (1<<IMGDIST_OPFLAGS_XP1_SHIFT)
+#define IMGDIST_OPFLAGS_XP2 (1<<IMGDIST_OPFLAGS_XP2_SHIFT)
+#define IMGDIST_OPFLAGS_XP3 (1<<IMGDIST_OPFLAGS_XP3_SHIFT)
+#define IMGDIST_OPFLAGS_XM1 (1<<IMGDIST_OPFLAGS_XM1_SHIFT)
+#define IMGDIST_OPFLAGS_XM2 (1<<IMGDIST_OPFLAGS_XM2_SHIFT)
+#define IMGDIST_OPFLAGS_XM3 (1<<IMGDIST_OPFLAGS_XM3_SHIFT)
+#define IMGDIST_OPFLAGS_YP1 (1<<IMGDIST_OPFLAGS_YP1_SHIFT)
+#define IMGDIST_OPFLAGS_YP2 (1<<IMGDIST_OPFLAGS_YP2_SHIFT)
+#define IMGDIST_OPFLAGS_YP3 (1<<IMGDIST_OPFLAGS_YP3_SHIFT)
+#define IMGDIST_OPFLAGS_YM1 (1<<IMGDIST_OPFLAGS_YM1_SHIFT)
+#define IMGDIST_OPFLAGS_YM2 (1<<IMGDIST_OPFLAGS_YM2_SHIFT)
+#define IMGDIST_OPFLAGS_YM3 (1<<IMGDIST_OPFLAGS_YM3_SHIFT)
+
+
+static inline ALWAYS_INLINE int imgDistMapForwardOpFlags( int x, int y, int sizex, int sizey )
+{
+  int opflags;
+  opflags = 0;
+  opflags |= ( x >= 1 ) << IMGDIST_OPFLAGS_XM1_SHIFT;
+  opflags |= ( x >= 2 ) << IMGDIST_OPFLAGS_XM2_SHIFT;
+  opflags |= ( x >= 3 ) << IMGDIST_OPFLAGS_XM3_SHIFT;
+  opflags |= ( y >= 1 ) << IMGDIST_OPFLAGS_YM1_SHIFT;
+  opflags |= ( y >= 2 ) << IMGDIST_OPFLAGS_YM2_SHIFT;
+  opflags |= ( y >= 3 ) << IMGDIST_OPFLAGS_YM3_SHIFT;
+  opflags |= ( x < (sizex-1) ) << IMGDIST_OPFLAGS_XP1_SHIFT;
+  opflags |= ( x < (sizex-2) ) << IMGDIST_OPFLAGS_XP2_SHIFT;
+  opflags |= ( x < (sizex-3) ) << IMGDIST_OPFLAGS_XP3_SHIFT;
+  return opflags;
+}
+
+static void imgDistMapForwardClamp( MapDistMapPoint *p, float fx, float fy, int x, int y, int sizex, int opflags )
+{
+  if( opflags & IMGDIST_OPFLAGS_XM1 )
+    imgDistMapCheck( p, fx, fy, p+( 0*sizex)+(-1), 1.0f );
+  if( opflags & IMGDIST_OPFLAGS_YM1 )
+  {
+    imgDistMapCheck( p, fx, fy, p+(-1*sizex)+( 0), 1.0f );
+    if( opflags & IMGDIST_OPFLAGS_XM1 )
+      imgDistMapCheck( p, fx, fy, p+(-1*sizex)+(-1), IMGDISTMAP_SQRT2 );
+    if( opflags & IMGDIST_OPFLAGS_XP1 )
+      imgDistMapCheck( p, fx, fy, p+(-1*sizex)+( 1), IMGDISTMAP_SQRT2 );
+    if( opflags & IMGDIST_OPFLAGS_XM2 )
+      imgDistMapCheck( p, fx, fy, p+(-1*sizex)+(-2), IMGDISTMAP_SQRT5 );
+    if( opflags & IMGDIST_OPFLAGS_XP2 )
+      imgDistMapCheck( p, fx, fy, p+(-1*sizex)+( 2), IMGDISTMAP_SQRT5 );
+    if( opflags & IMGDIST_OPFLAGS_XM3 )
+      imgDistMapCheck( p, fx, fy, p+(-1*sizex)+(-3), IMGDISTMAP_SQRT10 );
+    if( opflags & IMGDIST_OPFLAGS_XP3 )
+      imgDistMapCheck( p, fx, fy, p+(-1*sizex)+( 3), IMGDISTMAP_SQRT10 );
+  }
+  if( opflags & IMGDIST_OPFLAGS_YM2 )
+  {
+    if( opflags & IMGDIST_OPFLAGS_XM1 )
+      imgDistMapCheck( p, fx, fy, p+(-2*sizex)+(-1), IMGDISTMAP_SQRT5 );
+    if( opflags & IMGDIST_OPFLAGS_XP1 )
+      imgDistMapCheck( p, fx, fy, p+(-2*sizex)+( 1), IMGDISTMAP_SQRT5 );
+    if( opflags & IMGDIST_OPFLAGS_XM3 )
+      imgDistMapCheck( p, fx, fy, p+(-2*sizex)+(-3), IMGDISTMAP_SQRT13 );
+    if( opflags & IMGDIST_OPFLAGS_XP3 )
+      imgDistMapCheck( p, fx, fy, p+(-2*sizex)+( 3), IMGDISTMAP_SQRT13 );
+  }
+  if( opflags & IMGDIST_OPFLAGS_YM3 )
+  {
+    if( opflags & IMGDIST_OPFLAGS_XM1 )
+      imgDistMapCheck( p, fx, fy, p+(-3*sizex)+(-1), IMGDISTMAP_SQRT10 );
+    if( opflags & IMGDIST_OPFLAGS_XP1 )
+      imgDistMapCheck( p, fx, fy, p+(-3*sizex)+( 1), IMGDISTMAP_SQRT10 );
+    if( opflags & IMGDIST_OPFLAGS_XM2 )
+      imgDistMapCheck( p, fx, fy, p+(-3*sizex)+(-2), IMGDISTMAP_SQRT13 );
+    if( opflags & IMGDIST_OPFLAGS_XP2 )
+      imgDistMapCheck( p, fx, fy, p+(-3*sizex)+( 2), IMGDISTMAP_SQRT13 );
+  }
+}
+
+static inline ALWAYS_INLINE int imgDistMapBackwardOpFlags( int x, int y, int sizex, int sizey )
+{
+  int opflags;
+  opflags = 0;
+  opflags |= ( x >= 1 ) << IMGDIST_OPFLAGS_XM1_SHIFT;
+  opflags |= ( x >= 2 ) << IMGDIST_OPFLAGS_XM2_SHIFT;
+  opflags |= ( x >= 3 ) << IMGDIST_OPFLAGS_XM3_SHIFT;
+  opflags |= ( x < (sizex-1) ) << IMGDIST_OPFLAGS_XP1_SHIFT;
+  opflags |= ( x < (sizex-2) ) << IMGDIST_OPFLAGS_XP2_SHIFT;
+  opflags |= ( x < (sizex-3) ) << IMGDIST_OPFLAGS_XP3_SHIFT;
+  opflags |= ( y < (sizey-1) ) << IMGDIST_OPFLAGS_YP1_SHIFT;
+  opflags |= ( y < (sizey-2) ) << IMGDIST_OPFLAGS_YP2_SHIFT;
+  opflags |= ( y < (sizey-3) ) << IMGDIST_OPFLAGS_YP3_SHIFT;
+  return opflags;
+}
+
+static void imgDistMapBackwardClamp( MapDistMapPoint *p, float fx, float fy, int x, int y, int sizex, int opflags )
+{
+  if( opflags & IMGDIST_OPFLAGS_XP1 )
+    imgDistMapCheck( p, fx, fy, p+( 0*sizex)+( 1), 1.0f );
+  if( opflags & IMGDIST_OPFLAGS_YP1 )
+  {
+    imgDistMapCheck( p, fx, fy, p+( 1*sizex)+( 0), 1.0f );
+    if( opflags & IMGDIST_OPFLAGS_XM1 )
+      imgDistMapCheck( p, fx, fy, p+( 1*sizex)+(-1), IMGDISTMAP_SQRT2 );
+    if( opflags & IMGDIST_OPFLAGS_XP1 )
+      imgDistMapCheck( p, fx, fy, p+( 1*sizex)+( 1), IMGDISTMAP_SQRT2 );
+    if( opflags & IMGDIST_OPFLAGS_XM2 )
+      imgDistMapCheck( p, fx, fy, p+( 1*sizex)+(-2), IMGDISTMAP_SQRT5 );
+    if( opflags & IMGDIST_OPFLAGS_XP2 )
+      imgDistMapCheck( p, fx, fy, p+( 1*sizex)+( 2), IMGDISTMAP_SQRT5 );
+    if( opflags & IMGDIST_OPFLAGS_XM3 )
+      imgDistMapCheck( p, fx, fy, p+( 1*sizex)+(-3), IMGDISTMAP_SQRT10 );
+    if( opflags & IMGDIST_OPFLAGS_XP3 )
+      imgDistMapCheck( p, fx, fy, p+( 1*sizex)+( 3), IMGDISTMAP_SQRT10 );
+  }
+  if( opflags & IMGDIST_OPFLAGS_YP2 )
+  {
+    if( opflags & IMGDIST_OPFLAGS_XM1 )
+      imgDistMapCheck( p, fx, fy, p+( 2*sizex)+(-1), IMGDISTMAP_SQRT5 );
+    if( opflags & IMGDIST_OPFLAGS_XP1 )
+      imgDistMapCheck( p, fx, fy, p+( 2*sizex)+( 1), IMGDISTMAP_SQRT5 );
+    if( opflags & IMGDIST_OPFLAGS_XM3 )
+      imgDistMapCheck( p, fx, fy, p+( 2*sizex)+(-3), IMGDISTMAP_SQRT13 );
+    if( opflags & IMGDIST_OPFLAGS_XP3 )
+      imgDistMapCheck( p, fx, fy, p+( 2*sizex)+( 3), IMGDISTMAP_SQRT13 );
+  }
+  if( opflags & IMGDIST_OPFLAGS_YP3 )
+  {
+    if( opflags & IMGDIST_OPFLAGS_XM1 )
+      imgDistMapCheck( p, fx, fy, p+( 3*sizex)+(-1), IMGDISTMAP_SQRT10 );
+    if( opflags & IMGDIST_OPFLAGS_XP1 )
+      imgDistMapCheck( p, fx, fy, p+( 3*sizex)+( 1), IMGDISTMAP_SQRT10 );
+    if( opflags & IMGDIST_OPFLAGS_XM2 )
+      imgDistMapCheck( p, fx, fy, p+( 3*sizex)+(-2), IMGDISTMAP_SQRT13 );
+    if( opflags & IMGDIST_OPFLAGS_XP2 )
+      imgDistMapCheck( p, fx, fy, p+( 3*sizex)+( 2), IMGDISTMAP_SQRT13 );
+  }
+}
+
+
+// The outer edge must be all zeroes
+void imgDistMapBuild( float *distancemap, byte *src, int sizex, int sizey, int srcbytesperpixel, int srcbytesperline )
+{
+  int x, y, mapindex, srcindex;
+  byte pixel;
+  float fx, fy, bias, maxdistance;
+  MapDistMapPoint *ptmap;
+  MapDistMapPoint *p;
+
+  if( ( sizex < 4 ) || ( sizey < 4 ) )
+    return;
+
+  ptmap = new MapDistMapPoint[sizex * sizey];
+
+  maxdistance = sizex + sizey;
+  mapindex = 0;
+  for( y = 0 ; y < sizey ; y++ )
+  {
+    srcindex = y * srcbytesperline;
+    for( x = 0 ; x < sizex ; x++ )
+    {
+      pixel = src[ srcindex ];
+      if( pixel )
+      {
+        fx = (float)x;
+        fy = (float)y;
+        bias = 1.0f - ( (float)pixel * (1.0f/255.0f) );
+        ptmap[mapindex].distance = bias;
+        ptmap[mapindex].x = fx;
+        ptmap[mapindex].y = fy;
+        ptmap[mapindex].bias = bias;
+      }
+      else
+      {
+        ptmap[mapindex].distance = maxdistance;
+        ptmap[mapindex].x = -1.0f;
+        ptmap[mapindex].y = -1.0f;
+        ptmap[mapindex].bias = 0.0f;
+      }
+      mapindex++;
+      srcindex += srcbytesperpixel;
+    }
+  }
+
+  // Forward pass
+  for( y = 0 ; y < 3 ; y++ )
+  {
+    mapindex = y * sizex;
+    for( x = 0 ; x < sizex ; x++ )
+    {
+      p = &ptmap[mapindex];
+      fx = (float)x;
+      fy = (float)y;
+      imgDistMapForwardClamp( p, fx, fy, x, y, sizex, imgDistMapForwardOpFlags( x, y, sizex, sizey ) );
+      mapindex++;
+    }
+  }
+  for( ; y < sizey - 3 ; y++ )
+  {
+    mapindex = y * sizex;
+    for( x = 0 ; x < 3 ; x++ )
+    {
+      p = &ptmap[mapindex];
+      fx = (float)x;
+      fy = (float)y;
+      imgDistMapForwardClamp( p, fx, fy, x, y, sizex, imgDistMapForwardOpFlags( x, y, sizex, sizey ) );
+      mapindex++;
+    }
+    for( x = 3 ; x < sizex - 3 ; x++ )
+    {
+      p = &ptmap[mapindex];
+      fx = (float)x;
+      fy = (float)y;
+
+      imgDistMapCheck( p, fx, fy, p+( 0*sizex)+(-1), 1.0f );
+      imgDistMapCheck( p, fx, fy, p+(-1*sizex)+( 0), 1.0f );
+      imgDistMapCheck( p, fx, fy, p+(-1*sizex)+(-1), IMGDISTMAP_SQRT2 );
+      imgDistMapCheck( p, fx, fy, p+(-1*sizex)+( 1), IMGDISTMAP_SQRT2 );
+
+      imgDistMapCheck( p, fx, fy, p+(-2*sizex)+(-1), IMGDISTMAP_SQRT5 );
+      imgDistMapCheck( p, fx, fy, p+(-2*sizex)+( 1), IMGDISTMAP_SQRT5 );
+      imgDistMapCheck( p, fx, fy, p+(-1*sizex)+(-2), IMGDISTMAP_SQRT5 );
+      imgDistMapCheck( p, fx, fy, p+(-1*sizex)+( 2), IMGDISTMAP_SQRT5 );
+
+      imgDistMapCheck( p, fx, fy, p+(-3*sizex)+(-1), IMGDISTMAP_SQRT10 );
+      imgDistMapCheck( p, fx, fy, p+(-3*sizex)+( 1), IMGDISTMAP_SQRT10 );
+      imgDistMapCheck( p, fx, fy, p+(-1*sizex)+(-3), IMGDISTMAP_SQRT10 );
+      imgDistMapCheck( p, fx, fy, p+(-1*sizex)+( 3), IMGDISTMAP_SQRT10 );
+
+      imgDistMapCheck( p, fx, fy, p+(-3*sizex)+(-2), IMGDISTMAP_SQRT13 );
+      imgDistMapCheck( p, fx, fy, p+(-3*sizex)+( 2), IMGDISTMAP_SQRT13 );
+      imgDistMapCheck( p, fx, fy, p+(-2*sizex)+(-3), IMGDISTMAP_SQRT13 );
+      imgDistMapCheck( p, fx, fy, p+(-2*sizex)+( 3), IMGDISTMAP_SQRT13 );
+      mapindex++;
+    }
+    for( ; x < sizex ; x++ )
+    {
+      p = &ptmap[mapindex];
+      fx = (float)x;
+      fy = (float)y;
+      imgDistMapForwardClamp( p, fx, fy, x, y, sizex, imgDistMapForwardOpFlags( x, y, sizex, sizey ) );
+      mapindex++;
+    }
+  }
+  for( ; y < sizey ; y++ )
+  {
+    mapindex = y * sizex;
+    for( x = 0 ; x < sizex ; x++ )
+    {
+      p = &ptmap[mapindex];
+      fx = (float)x;
+      fy = (float)y;
+      imgDistMapForwardClamp( p, fx, fy, x, y, sizex, imgDistMapForwardOpFlags( x, y, sizex, sizey ) );
+      mapindex++;
+    }
+  }
+
+  // Backward pass
+  for( y = sizey - 1 ; y >= sizey - 3 ; y-- )
+  {
+    mapindex = ( y * sizex ) + ( sizex - 1 );
+    for( x = sizex - 1 ; x >= 0 ; x-- )
+    {
+      p = &ptmap[mapindex];
+      fx = (float)x;
+      fy = (float)y;
+      imgDistMapBackwardClamp( p, fx, fy, x, y, sizex, imgDistMapBackwardOpFlags( x, y, sizex, sizey ) );
+      distancemap[ mapindex ] = p->distance;
+      mapindex--;
+    }
+  }
+  for( ; y >= 3 ; y-- )
+  {
+    mapindex = ( y * sizex ) + ( sizex - 1 );
+    for( x = sizex - 1 ; x >= sizex - 3 ; x-- )
+    {
+      p = &ptmap[mapindex];
+      fx = (float)x;
+      fy = (float)y;
+      imgDistMapBackwardClamp( p, fx, fy, x, y, sizex, imgDistMapBackwardOpFlags( x, y, sizex, sizey ) );
+      distancemap[ mapindex ] = p->distance;
+      mapindex--;
+    }
+    for( ; x >= 3 ; x-- )
+    {
+      p = &ptmap[mapindex];
+      fx = (float)x;
+      fy = (float)y;
+
+      imgDistMapCheck( p, fx, fy, p+( 0*sizex)+( 1), 1.0f );
+      imgDistMapCheck( p, fx, fy, p+( 1*sizex)+( 0), 1.0f );
+      imgDistMapCheck( p, fx, fy, p+( 1*sizex)+(-1), IMGDISTMAP_SQRT2 );
+      imgDistMapCheck( p, fx, fy, p+( 1*sizex)+( 1), IMGDISTMAP_SQRT2 );
+
+      imgDistMapCheck( p, fx, fy, p+( 2*sizex)+(-1), IMGDISTMAP_SQRT5 );
+      imgDistMapCheck( p, fx, fy, p+( 2*sizex)+( 1), IMGDISTMAP_SQRT5 );
+      imgDistMapCheck( p, fx, fy, p+( 1*sizex)+(-2), IMGDISTMAP_SQRT5 );
+      imgDistMapCheck( p, fx, fy, p+( 1*sizex)+( 2), IMGDISTMAP_SQRT5 );
+
+      imgDistMapCheck( p, fx, fy, p+( 3*sizex)+(-1), IMGDISTMAP_SQRT10 );
+      imgDistMapCheck( p, fx, fy, p+( 3*sizex)+( 1), IMGDISTMAP_SQRT10 );
+      imgDistMapCheck( p, fx, fy, p+( 1*sizex)+(-3), IMGDISTMAP_SQRT10 );
+      imgDistMapCheck( p, fx, fy, p+( 1*sizex)+( 3), IMGDISTMAP_SQRT10 );
+
+      imgDistMapCheck( p, fx, fy, p+( 3*sizex)+(-2), IMGDISTMAP_SQRT13 );
+      imgDistMapCheck( p, fx, fy, p+( 3*sizex)+( 2), IMGDISTMAP_SQRT13 );
+      imgDistMapCheck( p, fx, fy, p+( 2*sizex)+(-3), IMGDISTMAP_SQRT13 );
+      imgDistMapCheck( p, fx, fy, p+( 2*sizex)+( 3), IMGDISTMAP_SQRT13 );
+
+      distancemap[ mapindex ] = p->distance;
+      mapindex--;
+    }
+    for( ; x >= 0 ; x-- )
+    {
+      p = &ptmap[mapindex];
+      fx = (float)x;
+      fy = (float)y;
+      imgDistMapBackwardClamp( p, fx, fy, x, y, sizex, imgDistMapBackwardOpFlags( x, y, sizex, sizey ) );
+      distancemap[ mapindex ] = p->distance;
+      mapindex--;
+    }
+  }
+  for( ; y >= 0 ; y-- )
+  {
+    mapindex = ( y * sizex ) + ( sizex - 1 );
+    for( x = sizex - 1 ; x >= 0 ; x-- )
+    {
+      p = &ptmap[mapindex];
+      fx = (float)x;
+      fy = (float)y;
+      imgDistMapBackwardClamp( p, fx, fy, x, y, sizex, imgDistMapBackwardOpFlags( x, y, sizex, sizey ) );
+      distancemap[ mapindex ] = p->distance;
+      mapindex--;
+    }
+  }
+
+  delete ptmap;
+}
index 7d5226e..37d3974 100644 (file)
@@ -2711,6 +2711,7 @@ private:
          // Default Settings
          surface.TextFont(usedFont.font);
          surface.TextOpacity(false);
+         surface.outlineColor = black;
 
          OnRedraw(surface);
 
@@ -2739,6 +2740,7 @@ private:
 
          surface.TextFont(usedFont.font);
          surface.TextOpacity(false);
+         surface.outlineColor = black;
 
          OnDrawOverChildren(surface);