Skip to content

Instantly share code, notes, and snippets.

@mdavis199
Forked from Flix01/edtaa3func.h
Created June 30, 2022 05:43
Show Gist options
  • Save mdavis199/13028f61d1e6deda44a70b387c3872d5 to your computer and use it in GitHub Desktop.
Save mdavis199/13028f61d1e6deda44a70b387c3872d5 to your computer and use it in GitHub Desktop.

Revisions

  1. @Flix01 Flix01 revised this gist Oct 29, 2016. 3 changed files with 218 additions and 299 deletions.
    219 changes: 218 additions & 1 deletion edtaa3func.c.inl → edtaa3func.h
    Original file line number Diff line number Diff line change
    @@ -54,6 +54,83 @@
    THE SOFTWARE.
    */

    // Flix01: Added the EDTAA3FUNC_HAS_IMGUI_SUPPORT definition that can be used
    // to provide Dear Imgui (at https://github.com/ocornut/imgui) the method:
    // ImGui::PostBuildForSignedDistanceFontEffect(ImFontAtlas* atlas).
    // USAGE:
    /*
    // In imgui_impl_glfw.cpp (or similiar):
    #define IMIMPL_BUILD_SDF // Twickable
    #ifdef IMIMPL_BUILD_SDF
    #define EDTAA3FUNC_HAS_IMGUI_SUPPORT
    #include "edtaa3func.h"
    #endif //IMIMPL_BUILD_SDF
    bool ImGui_ImplGlfw_CreateFontsTexture()
    {
    // Build texture atlas
    ImGuiIO& io = ImGui::GetIO();
    unsigned char* pixels;
    int width, height;
    // New code -------------------------------------------------
    if (io.Fonts->ConfigData.empty()) io.Fonts->AddFontDefault();
    io.Fonts->Build(); // (change this line if you use [imgui_freetype](https://github.com/Vuhdo/imgui) appropriately)
    # ifdef IMIMPL_BUILD_SDF
    ImGui::PostBuildForSignedDistanceFontEffect(io.Fonts);
    # endif //IMIMPL_BUILD_SDF
    // End new code ----------------------------------------------
    ...
    }
    */

    // The methods ImGuiEdtaaHelper::make_distance_mapd(...) and ImGuiEdtaaHelper::make_distance_mapb(...)
    // are based on https://github.com/rougier/freetype-gl/blob/master/distance-field.c [Copyright 2011,2012 Nicolas P. Rougier. All rights reserved.]
    // freetype-gl license:
    /*
    /* =========================================================================
    * Freetype GL - A C OpenGL Freetype engine
    * Platform: Any
    * WWW: https://github.com/rougier/freetype-gl
    * -------------------------------------------------------------------------
    * Copyright 2011,2012 Nicolas P. Rougier. All rights reserved.
    *
    * Redistribution and use in source and binary forms, with or without
    * modification, are permitted provided that the following conditions are met:
    *
    * 1. Redistributions of source code must retain the above copyright notice,
    * this list of conditions and the following disclaimer.
    *
    * 2. Redistributions in binary form must reproduce the above copyright
    * notice, this list of conditions and the following disclaimer in the
    * documentation and/or other materials provided with the distribution.
    *
    * THIS SOFTWARE IS PROVIDED BY NICOLAS P. ROUGIER ''AS IS'' AND ANY EXPRESS OR
    * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
    * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
    * EVENT SHALL NICOLAS P. ROUGIER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
    * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
    * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    *
    * The views and conclusions contained in the software and documentation are
    * those of the authors and should not be interpreted as representing official
    * policies, either expressed or implied, of Nicolas P. Rougier.
    * =========================================================================
    */

    #ifndef EDTAA3FUNC_H_
    #define EDTAA3FUNC_H_
    #pragma once

    #include <math.h>

    #ifdef __cplusplus
    extern "C" {
    #endif //__cplusplus

    /*
    * Compute the local gradient at edge pixels using convolution filters.
    * The gradient is computed only at edge pixels. At other places in the
    @@ -570,4 +647,144 @@ void edtaa3(double *img, double *gx, double *gy, int w, int h, short *distx, sho

    /* The transformation is completed. */

    }
    }
    #ifdef __cplusplus
    } // extern "C"
    #endif //__cplusplus

    #ifdef EDTAA3FUNC_HAS_IMGUI_SUPPORT

    #include <imgui.h>

    namespace ImGuiEdtaaHelper {
    // The purpose of TmpData is to allow releasing the memory all togther
    // at the end of a multiple call to make_distance_mapb(...)/make_distance_mapd(...)
    // [However we don't use this feature here]
    struct TmpData {
    ImVector<double> v_data;

    ImVector<short> v_xdist;
    ImVector<short> v_ydist;
    ImVector<double> v_gx;
    ImVector<double> v_gy;
    ImVector<double> v_outside;
    ImVector<double> v_inside;

    void freeMemory() {
    TmpData tmp;
    v_data.swap(tmp.v_data);
    v_xdist.swap(tmp.v_xdist);
    v_ydist.swap(tmp.v_ydist);
    v_gx.swap(tmp.v_gx);
    v_outside.swap(tmp.v_outside);
    v_inside.swap(tmp.v_inside);
    }
    };
    static TmpData tmpData;
    static void FreeTmpMemory() {tmpData.freeMemory();}

    // These two methods are adapted from: https://github.com/rougier/freetype-gl (Copyright 2011,2012 Nicolas P. Rougier),
    // so that the memory they use can optionally be freed once, after all the glyphs are processed.
    static double * make_distance_mapd( double *data, unsigned int width, unsigned int height, TmpData* pOptionalTmpData=NULL)
    {
    if (!pOptionalTmpData) pOptionalTmpData=&tmpData;

    const unsigned int area = width * height;
    IM_ASSERT(area>0);
    pOptionalTmpData->v_xdist.resize(area); short* xdist = &pOptionalTmpData->v_xdist[0];
    pOptionalTmpData->v_ydist.resize(area); short* ydist = &pOptionalTmpData->v_ydist[0];
    pOptionalTmpData->v_gx.resize(area); double* gx = &pOptionalTmpData->v_gx[0];
    pOptionalTmpData->v_gy.resize(area); double* gy = &pOptionalTmpData->v_gy[0];
    pOptionalTmpData->v_outside.resize(area); double* outside=&pOptionalTmpData->v_outside[0];
    pOptionalTmpData->v_inside.resize(area); double* inside= &pOptionalTmpData->v_inside[0];

    double vmin = DBL_MAX;
    unsigned int i;

    // Compute outside = edtaa3(bitmap); % Transform background (0's)
    computegradient( data, width, height, gx, gy);
    edtaa3(data, gx, gy, width, height, xdist, ydist, outside);
    for( i=0; i<area; ++i) {
    if( outside[i] < 0.0 ) outside[i] = 0.0;
    }

    // Compute inside = edtaa3(1-bitmap); % Transform foreground (1's)
    memset( gx, 0, sizeof(double)*area );
    memset( gy, 0, sizeof(double)*area );
    for( i=0; i<area; ++i) data[i] = 1 - data[i];
    computegradient( data, width, height, gx, gy );
    edtaa3( data, gx, gy, width, height, xdist, ydist, inside );
    for( i=0; i<area; ++i ) {
    if( inside[i] < 0 ) inside[i] = 0.0;
    }

    // distmap = outside - inside; % Bipolar distance field
    for( i=0; i<area; ++i) {
    outside[i] -= inside[i];
    if( outside[i] < vmin ) vmin = outside[i];
    }

    vmin = fabs(vmin);

    for( i=0; i<area; ++i) {
    double v = outside[i];
    if ( v < -vmin) outside[i] = -vmin;
    else if( v > +vmin) outside[i] = +vmin;
    data[i] = (outside[i]+vmin)/(2*vmin);
    }

    return data;
    }
    static unsigned char * make_distance_mapb(const unsigned char *img,unsigned int width, unsigned int height, unsigned char* imgOutSameSize, TmpData* pOptionalTmpData=NULL)
    {
    IM_ASSERT(imgOutSameSize);

    if (!pOptionalTmpData) pOptionalTmpData=&tmpData;

    const unsigned int area = width * height;
    IM_ASSERT(area>0);
    pOptionalTmpData->v_data.resize(area);
    double* data = &pOptionalTmpData->v_data[0];

    unsigned int i;

    // find minimimum and maximum values
    double img_min = DBL_MAX,img_max = DBL_MIN,v;

    for( i=0; i<area; ++i) {
    data[i] = v = img[i];
    if (v > img_max) img_max = v;
    if (v < img_min) img_min = v;
    }

    // Map values from 0 - 255 to 0.0 - 1.0
    for( i=0; i<area; ++i) data[i] = (img[i]-img_min)/img_max;

    data = make_distance_mapd(data, width, height,pOptionalTmpData);

    // map values from 0.0 - 1.0 to 0 - 255
    for( i=0; i<area; ++i) imgOutSameSize[i] = (unsigned char)(255*(1-data[i]));

    return imgOutSameSize;
    }
    } //ImGuiEdtaaHelper


    namespace ImGui {
    void ProcessAlpha8ImageForSignedDistanceFontEffect(unsigned char* pixels,int w,int h) {
    IM_ASSERT(pixels && w>0 && h>0);
    ImVector<unsigned char> tmpData;tmpData.resize(w*h);
    ImGuiEdtaaHelper::make_distance_mapb(pixels,w,h,&tmpData[0]);
    ImGuiEdtaaHelper::FreeTmpMemory();
    memcpy(pixels,&tmpData[0],w*h);
    }

    void PostBuildForSignedDistanceFontEffect(ImFontAtlas* atlas) {
    if (!atlas->TexPixelsAlpha8) atlas->GetTexDataAsAlpha8(&atlas->TexPixelsAlpha8,NULL,NULL);
    ProcessAlpha8ImageForSignedDistanceFontEffect(atlas->TexPixelsAlpha8,atlas->TexWidth,atlas->TexHeight);
    }
    }

    #endif //EDTAA3FUNC_HAS_IMGUI_SUPPORT

    #endif //EDTAA3FUNC_H_
    182 changes: 0 additions & 182 deletions imguiSdfGenerator.cpp
    Original file line number Diff line number Diff line change
    @@ -1,182 +0,0 @@

    // The methods ImGuiEdtaaHelper::make_distance_mapd(...) and ImGuiEdtaaHelper::make_distance_mapb(...)
    // are based on https://github.com/rougier/freetype-gl/blob/master/distance-field.c [Copyright 2011,2012 Nicolas P. Rougier. All rights reserved.]
    // freetype-gl license:
    /*
    /* =========================================================================
    * Freetype GL - A C OpenGL Freetype engine
    * Platform: Any
    * WWW: https://github.com/rougier/freetype-gl
    * -------------------------------------------------------------------------
    * Copyright 2011,2012 Nicolas P. Rougier. All rights reserved.
    *
    * Redistribution and use in source and binary forms, with or without
    * modification, are permitted provided that the following conditions are met:
    *
    * 1. Redistributions of source code must retain the above copyright notice,
    * this list of conditions and the following disclaimer.
    *
    * 2. Redistributions in binary form must reproduce the above copyright
    * notice, this list of conditions and the following disclaimer in the
    * documentation and/or other materials provided with the distribution.
    *
    * THIS SOFTWARE IS PROVIDED BY NICOLAS P. ROUGIER ''AS IS'' AND ANY EXPRESS OR
    * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
    * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
    * EVENT SHALL NICOLAS P. ROUGIER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
    * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
    * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    *
    * The views and conclusions contained in the software and documentation are
    * those of the authors and should not be interpreted as representing official
    * policies, either expressed or implied, of Nicolas P. Rougier.
    * =========================================================================
    */

    // "edtaa3func.c.inl" by Stefan Gustavson has its own (MIT) license at the top of it

    #include "imguiSdfGenerator.h"
    #include <math.h>

    #include "imgui.h"

    #ifdef __cplusplus
    extern "C" {
    #include "edtaa3func.c.inl"
    }
    #else //__cplusplus
    #include "edtaa3func.c.inl"
    #endif //__cplusplus

    namespace ImGuiEdtaaHelper {
    // The purpose of TmpData is to allow releasing the memory all togther
    // at the end of a multiple call to make_distance_mapb(...)/make_distance_mapd(...)
    // [However we don't use this feature here]
    struct TmpData {
    ImVector<double> v_data;

    ImVector<short> v_xdist;
    ImVector<short> v_ydist;
    ImVector<double> v_gx;
    ImVector<double> v_gy;
    ImVector<double> v_outside;
    ImVector<double> v_inside;

    void freeMemory() {
    TmpData tmp;
    v_data.swap(tmp.v_data);
    v_xdist.swap(tmp.v_xdist);
    v_ydist.swap(tmp.v_ydist);
    v_gx.swap(tmp.v_gx);
    v_outside.swap(tmp.v_outside);
    v_inside.swap(tmp.v_inside);
    }
    };
    static TmpData tmpData;
    static void FreeTmpMemory() {tmpData.freeMemory();}

    // These two methods are adapted from: https://github.com/rougier/freetype-gl (Copyright 2011,2012 Nicolas P. Rougier),
    // so that the memory they use can optionally be freed once, after all the glyphs are processed.
    static double * make_distance_mapd( double *data, unsigned int width, unsigned int height, TmpData* pOptionalTmpData=NULL)
    {
    if (!pOptionalTmpData) pOptionalTmpData=&tmpData;

    const unsigned int area = width * height;
    IM_ASSERT(area>0);
    pOptionalTmpData->v_xdist.resize(area); short* xdist = &pOptionalTmpData->v_xdist[0];
    pOptionalTmpData->v_ydist.resize(area); short* ydist = &pOptionalTmpData->v_ydist[0];
    pOptionalTmpData->v_gx.resize(area); double* gx = &pOptionalTmpData->v_gx[0];
    pOptionalTmpData->v_gy.resize(area); double* gy = &pOptionalTmpData->v_gy[0];
    pOptionalTmpData->v_outside.resize(area); double* outside=&pOptionalTmpData->v_outside[0];
    pOptionalTmpData->v_inside.resize(area); double* inside= &pOptionalTmpData->v_inside[0];

    double vmin = DBL_MAX;
    unsigned int i;

    // Compute outside = edtaa3(bitmap); % Transform background (0's)
    computegradient( data, width, height, gx, gy);
    edtaa3(data, gx, gy, width, height, xdist, ydist, outside);
    for( i=0; i<area; ++i) {
    if( outside[i] < 0.0 ) outside[i] = 0.0;
    }

    // Compute inside = edtaa3(1-bitmap); % Transform foreground (1's)
    memset( gx, 0, sizeof(double)*area );
    memset( gy, 0, sizeof(double)*area );
    for( i=0; i<area; ++i) data[i] = 1 - data[i];
    computegradient( data, width, height, gx, gy );
    edtaa3( data, gx, gy, width, height, xdist, ydist, inside );
    for( i=0; i<area; ++i ) {
    if( inside[i] < 0 ) inside[i] = 0.0;
    }

    // distmap = outside - inside; % Bipolar distance field
    for( i=0; i<area; ++i) {
    outside[i] -= inside[i];
    if( outside[i] < vmin ) vmin = outside[i];
    }

    vmin = fabs(vmin);

    for( i=0; i<area; ++i) {
    double v = outside[i];
    if ( v < -vmin) outside[i] = -vmin;
    else if( v > +vmin) outside[i] = +vmin;
    data[i] = (outside[i]+vmin)/(2*vmin);
    }

    return data;
    }
    static unsigned char * make_distance_mapb(const unsigned char *img,unsigned int width, unsigned int height, unsigned char* imgOutSameSize, TmpData* pOptionalTmpData=NULL)
    {
    IM_ASSERT(imgOutSameSize);

    if (!pOptionalTmpData) pOptionalTmpData=&tmpData;

    const unsigned int area = width * height;
    IM_ASSERT(area>0);
    pOptionalTmpData->v_data.resize(area);
    double* data = &pOptionalTmpData->v_data[0];

    unsigned int i;

    // find minimimum and maximum values
    double img_min = DBL_MAX,img_max = DBL_MIN,v;

    for( i=0; i<area; ++i) {
    data[i] = v = img[i];
    if (v > img_max) img_max = v;
    if (v < img_min) img_min = v;
    }

    // Map values from 0 - 255 to 0.0 - 1.0
    for( i=0; i<area; ++i) data[i] = (img[i]-img_min)/img_max;

    data = make_distance_mapd(data, width, height);

    // map values from 0.0 - 1.0 to 0 - 255
    for( i=0; i<area; ++i) imgOutSameSize[i] = (unsigned char)(255*(1-data[i]));

    return imgOutSameSize;
    }
    } //ImGuiEdtaaHelper


    namespace ImGui {
    void ProcessAlpha8ImageForSignedDistanceFontEffect(unsigned char* pixels,int w,int h) {
    IM_ASSERT(pixels && w>0 && h>0);
    ImVector<unsigned char> tmpData;tmpData.resize(w*h);
    ImGuiEdtaaHelper::make_distance_mapb(pixels,w,h,&tmpData[0]);
    ImGuiEdtaaHelper::FreeTmpMemory();
    memcpy(pixels,&tmpData[0],w*h);
    }

    void PostBuildForSignedDistanceFontEffect(ImFontAtlas* atlas) {
    if (!atlas->TexPixelsAlpha8) atlas->GetTexDataAsAlpha8(&atlas->TexPixelsAlpha8,NULL,NULL);
    ProcessAlpha8ImageForSignedDistanceFontEffect(atlas->TexPixelsAlpha8,atlas->TexWidth,atlas->TexHeight);
    }
    }
    116 changes: 0 additions & 116 deletions imguiSdfGenerator.h
    Original file line number Diff line number Diff line change
    @@ -1,116 +0,0 @@
    #ifndef IMGUISDFGENERATOR_H_
    #define IMGUISDFGENERATOR_H_
    #pragma once

    // VERSION 1.0

    // BASIC USAGE:
    /*
    // We'll use examples/opengl3_example as a reference.
    // In imgui_impl_glfw.cpp (or similiar):
    #define IMIMPL_BUILD_SDF // Twickable
    #ifdef IMIMPL_BUILD_SDF
    #include <imguiSdfGenerator.h>
    #endif //IMIMPL_BUILD_SDF
    bool ImGui_ImplGlfw_CreateFontsTexture()
    {
    // Build texture atlas
    ImGuiIO& io = ImGui::GetIO();
    unsigned char* pixels;
    int width, height;
    // New code -------------------------------------------------
    if (io.Fonts->ConfigData.empty()) io.Fonts->AddFontDefault();
    io.Fonts->Build(); // (change this line if you use [imgui_freetype](https://github.com/Vuhdo/imgui) appropriately)
    # ifdef IMIMPL_BUILD_SDF
    ImGui::PostBuildForSignedDistanceFontEffect(io.Fonts);
    # endif //IMIMPL_BUILD_SDF
    // End new code ----------------------------------------------
    io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits for OpenGL3 demo because it is more likely to be compatible with user's existing shader.
    ...
    }
    // We need to replace the fragment shader code with one of these 2:
    #ifndef IMIMPL_BUILD_SDF
    const GLchar* fragment_shader[] = {
    "#version 100\n"
    "precision mediump float;\n"
    "uniform lowp sampler2D Texture;\n"
    "varying vec2 Frag_UV;\n"
    "varying vec4 Frag_Color;\n"
    "void main()\n"
    "{\n"
    " gl_FragColor = Frag_Color * texture2D( Texture, Frag_UV.st);\n"
    "}\n"
    };
    #else // IMIMPL_BUILD_SDF
    //#define USE_OUTLINE_SHADER // Tweakable
    # ifndef USE_OUTLINE_SHADER
    const char* fragment_shader[] = {
    "#version 100\n"
    "#extension GL_OES_standard_derivatives : enable\n" // fwidth
    "precision mediump float;\n"
    "uniform sampler2D Texture;\n"
    "varying vec2 Frag_UV;\n"
    "varying vec4 Frag_Color;\n"
    "void main(void) {\n"
    "vec4 texColor = texture2D(Texture, Frag_UV.st);\n"
    "float stepThreshold = 0.5;\n" // 0.5 -> threshold for glyph border
    "float width = fwidth(texColor.a);\n"
    "float alpha = smoothstep(stepThreshold - width, stepThreshold + width, texColor.a);\n"
    "gl_FragColor = vec4(Frag_Color.rgb*texColor.rgb,Frag_Color.a*alpha);\n"
    "}\n"
    };
    #else //USE_OUTLINE_SHADER
    const char* fragment_shader[] = {
    "#version 100\n"
    "#extension GL_OES_standard_derivatives : enable\n" // fwidth
    "precision mediump float;\n"
    "uniform sampler2D Texture;\n"
    "varying vec2 Frag_UV;\n"
    "varying vec4 Frag_Color;\n"
    "void main(void) {\n"
    "vec4 texColor = texture2D(Texture, Frag_UV.st);\n"
    "float width = fwidth(texColor.a);\n"
    "\n"
    "float outlineThreshold = 0.5;\n" // 0.5 -> threshold for glyph border
    "float alphaThreshold = 0.425;\n" // 0.2 -> threshold for glyph outline
    "float outlineDarkeningFactor = 0.3;\n" // 0.3
    "\n"
    "float inside = smoothstep(outlineThreshold - width, outlineThreshold + width, texColor.a) ;\n"
    "float glow = smoothstep (0.0 , 20.0 , texColor.a ) ;\n" // This can just be set to 1.0...
    "vec3 insidecolor = Frag_Color.rgb*texColor.rgb;\n"
    "vec3 outlinecolor = insidecolor.rgb*outlineDarkeningFactor;\n"
    "vec3 fragcolor = mix ( glow * outlinecolor , insidecolor , inside ) ;\n"
    "float alpha = smoothstep(alphaThreshold - width, alphaThreshold + width, texColor.a);\n"
    "\n"
    "gl_FragColor = vec4(fragcolor,Frag_Color.a*alpha);\n"
    "}\n"
    };
    #endif //USE_OUTLINE_SHADER
    #endif // IMIMPL_BUILD_SDF
    // Now it should work
    */

    // BEST SETTINGS I'VE FOUND SO FAR:
    /*
    ImGuiIO& io = ImGui::GetIO();
    ImFontConfig cfg;cfg.OversampleH=4;cfg.OversampleV=4;//cfg.PixelSnapH=true;//cfg.GlyphExtraSpacing=ImVec2(2,2);
    io.Fonts->AddFontFromFileTTF("/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-B.ttf", 13.0f,&cfg);
    */



    namespace ImGui {
    void PostBuildForSignedDistanceFontEffect(struct ImFontAtlas* atlas);

    // Optional generic method: Hp) stride == w
    void ProcessAlpha8ImageForSignedDistanceFontEffect(unsigned char* pixels,int w,int h);
    } // namespace ImGui

    #endif //IMGUISDFGENERATOR_H_
  2. @Flix01 Flix01 revised this gist Oct 29, 2016. 3 changed files with 735 additions and 747 deletions.
    573 changes: 573 additions & 0 deletions edtaa3func.c.inl
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,573 @@
    /*
    * edtaa3()
    *
    * Sweep-and-update Euclidean distance transform of an
    * image. Positive pixels are treated as object pixels,
    * zero or negative pixels are treated as background.
    * An attempt is made to treat antialiased edges correctly.
    * The input image must have pixels in the range [0,1],
    * and the antialiased image should be a box-filter
    * sampling of the ideal, crisp edge.
    * If the antialias region is more than 1 pixel wide,
    * the result from this transform will be inaccurate.
    *
    * By Stefan Gustavson ([email protected]).
    *
    * Originally written in 1994, based on a verbal
    * description of the SSED8 algorithm published in the
    * PhD dissertation of Ingemar Ragnemalm. This is his
    * algorithm, I only implemented it in C.
    *
    * Updated in 2004 to treat border pixels correctly,
    * and cleaned up the code to improve readability.
    *
    * Updated in 2009 to handle anti-aliased edges.
    *
    * Updated in 2011 to avoid a corner case infinite loop.
    *
    * Updated 2012 to change license from LGPL to MIT.
    *
    * Updated 2014 to fix a bug in the gradient computations. Ahem.
    *
    */

    /*
    Copyright (C) 2009-2012 Stefan Gustavson ([email protected])
    The code in this file is distributed under the MIT license:
    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:
    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    THE SOFTWARE.
    */

    /*
    * Compute the local gradient at edge pixels using convolution filters.
    * The gradient is computed only at edge pixels. At other places in the
    * image, it is never used, and it's mostly zero anyway.
    */
    void computegradient(double *img, int w, int h, double *gx, double *gy)
    {
    int i,j,k,p,q;
    double glength, phi, phiscaled, ascaled, errsign, pfrac, qfrac, err0, err1, err;
    #define SQRT2 1.4142136
    for(i = 1; i < h-1; i++) { // Avoid edges where the kernels would spill over
    for(j = 1; j < w-1; j++) {
    k = i*w + j;
    if((img[k]>0.0) && (img[k]<1.0)) { // Compute gradient for edge pixels only
    gx[k] = -img[k-w-1] - SQRT2*img[k-1] - img[k+w-1] + img[k-w+1] + SQRT2*img[k+1] + img[k+w+1];
    gy[k] = -img[k-w-1] - SQRT2*img[k-w] - img[k-w+1] + img[k+w-1] + SQRT2*img[k+w] + img[k+w+1];
    glength = gx[k]*gx[k] + gy[k]*gy[k];
    if(glength > 0.0) { // Avoid division by zero
    glength = sqrt(glength);
    gx[k]=gx[k]/glength;
    gy[k]=gy[k]/glength;
    }
    }
    }
    }
    // TODO: Compute reasonable values for gx, gy also around the image edges.
    // (These are zero now, which reduces the accuracy for a 1-pixel wide region
    // around the image edge.) 2x2 kernels would be suitable for this.
    }

    /*
    * A somewhat tricky function to approximate the distance to an edge in a
    * certain pixel, with consideration to either the local gradient (gx,gy)
    * or the direction to the pixel (dx,dy) and the pixel greyscale value a.
    * The latter alternative, using (dx,dy), is the metric used by edtaa2().
    * Using a local estimate of the edge gradient (gx,gy) yields much better
    * accuracy at and near edges, and reduces the error even at distant pixels
    * provided that the gradient direction is accurately estimated.
    */
    double edgedf(double gx, double gy, double a)
    {
    double df, glength, temp, a1;

    if ((gx == 0) || (gy == 0)) { // Either A) gu or gv are zero, or B) both
    df = 0.5-a; // Linear approximation is A) correct or B) a fair guess
    } else {
    glength = sqrt(gx*gx + gy*gy);
    if(glength>0) {
    gx = gx/glength;
    gy = gy/glength;
    }
    /* Everything is symmetric wrt sign and transposition,
    * so move to first octant (gx>=0, gy>=0, gx>=gy) to
    * avoid handling all possible edge directions.
    */
    gx = fabs(gx);
    gy = fabs(gy);
    if(gx<gy) {
    temp = gx;
    gx = gy;
    gy = temp;
    }
    a1 = 0.5*gy/gx;
    if (a < a1) { // 0 <= a < a1
    df = 0.5*(gx + gy) - sqrt(2.0*gx*gy*a);
    } else if (a < (1.0-a1)) { // a1 <= a <= 1-a1
    df = (0.5-a)*gx;
    } else { // 1-a1 < a <= 1
    df = -0.5*(gx + gy) + sqrt(2.0*gx*gy*(1.0-a));
    }
    }
    return df;
    }

    double distaa3(double *img, double *gximg, double *gyimg, int w, int c, int xc, int yc, int xi, int yi)
    {
    double di, df, dx, dy, gx, gy, a;
    int closest;

    closest = c-xc-yc*w; // Index to the edge pixel pointed to from c
    a = img[closest]; // Grayscale value at the edge pixel
    gx = gximg[closest]; // X gradient component at the edge pixel
    gy = gyimg[closest]; // Y gradient component at the edge pixel

    if(a > 1.0) a = 1.0;
    if(a < 0.0) a = 0.0; // Clip grayscale values outside the range [0,1]
    if(a == 0.0) return 1000000.0; // Not an object pixel, return "very far" ("don't know yet")

    dx = (double)xi;
    dy = (double)yi;
    di = sqrt(dx*dx + dy*dy); // Length of integer vector, like a traditional EDT
    if(di==0) { // Use local gradient only at edges
    // Estimate based on local gradient only
    df = edgedf(gx, gy, a);
    } else {
    // Estimate gradient based on direction to edge (accurate for large di)
    df = edgedf(dx, dy, a);
    }
    return di + df; // Same metric as edtaa2, except at edges (where di=0)
    }

    // Shorthand macro: add ubiquitous parameters dist, gx, gy, img and w and call distaa3()
    #define DISTAA(c,xc,yc,xi,yi) (distaa3(img, gx, gy, w, c, xc, yc, xi, yi))

    void edtaa3(double *img, double *gx, double *gy, int w, int h, short *distx, short *disty, double *dist)
    {
    int x, y, i, c;
    int offset_u, offset_ur, offset_r, offset_rd,
    offset_d, offset_dl, offset_l, offset_lu;
    double olddist, newdist;
    int cdistx, cdisty, newdistx, newdisty;
    int changed;
    double epsilon = 1e-3;

    /* Initialize index offsets for the current image width */
    offset_u = -w;
    offset_ur = -w+1;
    offset_r = 1;
    offset_rd = w+1;
    offset_d = w;
    offset_dl = w-1;
    offset_l = -1;
    offset_lu = -w-1;

    /* Initialize the distance images */
    for(i=0; i<w*h; i++) {
    distx[i] = 0; // At first, all pixels point to
    disty[i] = 0; // themselves as the closest known.
    if(img[i] <= 0.0)
    {
    dist[i]= 1000000.0; // Big value, means "not set yet"
    }
    else if (img[i]<1.0) {
    dist[i] = edgedf(gx[i], gy[i], img[i]); // Gradient-assisted estimate
    }
    else {
    dist[i]= 0.0; // Inside the object
    }
    }

    /* Perform the transformation */
    do
    {
    changed = 0;

    /* Scan rows, except first row */
    for(y=1; y<h; y++)
    {

    /* move index to leftmost pixel of current row */
    i = y*w;

    /* scan right, propagate distances from above & left */

    /* Leftmost pixel is special, has no left neighbors */
    olddist = dist[i];
    if(olddist > 0) // If non-zero distance or not set yet
    {
    c = i + offset_u; // Index of candidate for testing
    cdistx = distx[c];
    cdisty = disty[c];
    newdistx = cdistx;
    newdisty = cdisty+1;
    newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
    if(newdist < olddist-epsilon)
    {
    distx[i]=newdistx;
    disty[i]=newdisty;
    dist[i]=newdist;
    olddist=newdist;
    changed = 1;
    }

    c = i+offset_ur;
    cdistx = distx[c];
    cdisty = disty[c];
    newdistx = cdistx-1;
    newdisty = cdisty+1;
    newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
    if(newdist < olddist-epsilon)
    {
    distx[i]=newdistx;
    disty[i]=newdisty;
    dist[i]=newdist;
    changed = 1;
    }
    }
    i++;

    /* Middle pixels have all neighbors */
    for(x=1; x<w-1; x++, i++)
    {
    olddist = dist[i];
    if(olddist <= 0) continue; // No need to update further

    c = i+offset_l;
    cdistx = distx[c];
    cdisty = disty[c];
    newdistx = cdistx+1;
    newdisty = cdisty;
    newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
    if(newdist < olddist-epsilon)
    {
    distx[i]=newdistx;
    disty[i]=newdisty;
    dist[i]=newdist;
    olddist=newdist;
    changed = 1;
    }

    c = i+offset_lu;
    cdistx = distx[c];
    cdisty = disty[c];
    newdistx = cdistx+1;
    newdisty = cdisty+1;
    newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
    if(newdist < olddist-epsilon)
    {
    distx[i]=newdistx;
    disty[i]=newdisty;
    dist[i]=newdist;
    olddist=newdist;
    changed = 1;
    }

    c = i+offset_u;
    cdistx = distx[c];
    cdisty = disty[c];
    newdistx = cdistx;
    newdisty = cdisty+1;
    newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
    if(newdist < olddist-epsilon)
    {
    distx[i]=newdistx;
    disty[i]=newdisty;
    dist[i]=newdist;
    olddist=newdist;
    changed = 1;
    }

    c = i+offset_ur;
    cdistx = distx[c];
    cdisty = disty[c];
    newdistx = cdistx-1;
    newdisty = cdisty+1;
    newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
    if(newdist < olddist-epsilon)
    {
    distx[i]=newdistx;
    disty[i]=newdisty;
    dist[i]=newdist;
    changed = 1;
    }
    }

    /* Rightmost pixel of row is special, has no right neighbors */
    olddist = dist[i];
    if(olddist > 0) // If not already zero distance
    {
    c = i+offset_l;
    cdistx = distx[c];
    cdisty = disty[c];
    newdistx = cdistx+1;
    newdisty = cdisty;
    newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
    if(newdist < olddist-epsilon)
    {
    distx[i]=newdistx;
    disty[i]=newdisty;
    dist[i]=newdist;
    olddist=newdist;
    changed = 1;
    }

    c = i+offset_lu;
    cdistx = distx[c];
    cdisty = disty[c];
    newdistx = cdistx+1;
    newdisty = cdisty+1;
    newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
    if(newdist < olddist-epsilon)
    {
    distx[i]=newdistx;
    disty[i]=newdisty;
    dist[i]=newdist;
    olddist=newdist;
    changed = 1;
    }

    c = i+offset_u;
    cdistx = distx[c];
    cdisty = disty[c];
    newdistx = cdistx;
    newdisty = cdisty+1;
    newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
    if(newdist < olddist-epsilon)
    {
    distx[i]=newdistx;
    disty[i]=newdisty;
    dist[i]=newdist;
    changed = 1;
    }
    }

    /* Move index to second rightmost pixel of current row. */
    /* Rightmost pixel is skipped, it has no right neighbor. */
    i = y*w + w-2;

    /* scan left, propagate distance from right */
    for(x=w-2; x>=0; x--, i--)
    {
    olddist = dist[i];
    if(olddist <= 0) continue; // Already zero distance

    c = i+offset_r;
    cdistx = distx[c];
    cdisty = disty[c];
    newdistx = cdistx-1;
    newdisty = cdisty;
    newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
    if(newdist < olddist-epsilon)
    {
    distx[i]=newdistx;
    disty[i]=newdisty;
    dist[i]=newdist;
    changed = 1;
    }
    }
    }

    /* Scan rows in reverse order, except last row */
    for(y=h-2; y>=0; y--)
    {
    /* move index to rightmost pixel of current row */
    i = y*w + w-1;

    /* Scan left, propagate distances from below & right */

    /* Rightmost pixel is special, has no right neighbors */
    olddist = dist[i];
    if(olddist > 0) // If not already zero distance
    {
    c = i+offset_d;
    cdistx = distx[c];
    cdisty = disty[c];
    newdistx = cdistx;
    newdisty = cdisty-1;
    newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
    if(newdist < olddist-epsilon)
    {
    distx[i]=newdistx;
    disty[i]=newdisty;
    dist[i]=newdist;
    olddist=newdist;
    changed = 1;
    }

    c = i+offset_dl;
    cdistx = distx[c];
    cdisty = disty[c];
    newdistx = cdistx+1;
    newdisty = cdisty-1;
    newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
    if(newdist < olddist-epsilon)
    {
    distx[i]=newdistx;
    disty[i]=newdisty;
    dist[i]=newdist;
    changed = 1;
    }
    }
    i--;

    /* Middle pixels have all neighbors */
    for(x=w-2; x>0; x--, i--)
    {
    olddist = dist[i];
    if(olddist <= 0) continue; // Already zero distance

    c = i+offset_r;
    cdistx = distx[c];
    cdisty = disty[c];
    newdistx = cdistx-1;
    newdisty = cdisty;
    newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
    if(newdist < olddist-epsilon)
    {
    distx[i]=newdistx;
    disty[i]=newdisty;
    dist[i]=newdist;
    olddist=newdist;
    changed = 1;
    }

    c = i+offset_rd;
    cdistx = distx[c];
    cdisty = disty[c];
    newdistx = cdistx-1;
    newdisty = cdisty-1;
    newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
    if(newdist < olddist-epsilon)
    {
    distx[i]=newdistx;
    disty[i]=newdisty;
    dist[i]=newdist;
    olddist=newdist;
    changed = 1;
    }

    c = i+offset_d;
    cdistx = distx[c];
    cdisty = disty[c];
    newdistx = cdistx;
    newdisty = cdisty-1;
    newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
    if(newdist < olddist-epsilon)
    {
    distx[i]=newdistx;
    disty[i]=newdisty;
    dist[i]=newdist;
    olddist=newdist;
    changed = 1;
    }

    c = i+offset_dl;
    cdistx = distx[c];
    cdisty = disty[c];
    newdistx = cdistx+1;
    newdisty = cdisty-1;
    newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
    if(newdist < olddist-epsilon)
    {
    distx[i]=newdistx;
    disty[i]=newdisty;
    dist[i]=newdist;
    changed = 1;
    }
    }
    /* Leftmost pixel is special, has no left neighbors */
    olddist = dist[i];
    if(olddist > 0) // If not already zero distance
    {
    c = i+offset_r;
    cdistx = distx[c];
    cdisty = disty[c];
    newdistx = cdistx-1;
    newdisty = cdisty;
    newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
    if(newdist < olddist-epsilon)
    {
    distx[i]=newdistx;
    disty[i]=newdisty;
    dist[i]=newdist;
    olddist=newdist;
    changed = 1;
    }

    c = i+offset_rd;
    cdistx = distx[c];
    cdisty = disty[c];
    newdistx = cdistx-1;
    newdisty = cdisty-1;
    newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
    if(newdist < olddist-epsilon)
    {
    distx[i]=newdistx;
    disty[i]=newdisty;
    dist[i]=newdist;
    olddist=newdist;
    changed = 1;
    }

    c = i+offset_d;
    cdistx = distx[c];
    cdisty = disty[c];
    newdistx = cdistx;
    newdisty = cdisty-1;
    newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
    if(newdist < olddist-epsilon)
    {
    distx[i]=newdistx;
    disty[i]=newdisty;
    dist[i]=newdist;
    changed = 1;
    }
    }

    /* Move index to second leftmost pixel of current row. */
    /* Leftmost pixel is skipped, it has no left neighbor. */
    i = y*w + 1;
    for(x=1; x<w; x++, i++)
    {
    /* scan right, propagate distance from left */
    olddist = dist[i];
    if(olddist <= 0) continue; // Already zero distance

    c = i+offset_l;
    cdistx = distx[c];
    cdisty = disty[c];
    newdistx = cdistx+1;
    newdisty = cdisty;
    newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);
    if(newdist < olddist-epsilon)
    {
    distx[i]=newdistx;
    disty[i]=newdisty;
    dist[i]=newdist;
    changed = 1;
    }
    }
    }
    }
    while(changed); // Sweep until no more updates are made

    /* The transformation is completed. */

    }
    880 changes: 145 additions & 735 deletions imguiSdfGenerator.cpp
    Original file line number Diff line number Diff line change
    @@ -1,772 +1,182 @@

    // The methods ImGuiEdtaaHelper::make_distance_mapd(...) and ImGuiEdtaaHelper::make_distance_mapb(...)
    // are based on https://github.com/rougier/freetype-gl/blob/master/distance-field.c [Copyright 2011,2012 Nicolas P. Rougier. All rights reserved.]
    // freetype-gl license:
    /*
    /* =========================================================================
    * Freetype GL - A C OpenGL Freetype engine
    * Platform: Any
    * WWW: https://github.com/rougier/freetype-gl
    * -------------------------------------------------------------------------
    * Copyright 2011,2012 Nicolas P. Rougier. All rights reserved.
    *
    * Redistribution and use in source and binary forms, with or without
    * modification, are permitted provided that the following conditions are met:
    *
    * 1. Redistributions of source code must retain the above copyright notice,
    * this list of conditions and the following disclaimer.
    *
    * 2. Redistributions in binary form must reproduce the above copyright
    * notice, this list of conditions and the following disclaimer in the
    * documentation and/or other materials provided with the distribution.
    *
    * THIS SOFTWARE IS PROVIDED BY NICOLAS P. ROUGIER ''AS IS'' AND ANY EXPRESS OR
    * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
    * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
    * EVENT SHALL NICOLAS P. ROUGIER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
    * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
    * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    *
    * The views and conclusions contained in the software and documentation are
    * those of the authors and should not be interpreted as representing official
    * policies, either expressed or implied, of Nicolas P. Rougier.
    * =========================================================================
    */

    // "edtaa3func.c.inl" by Stefan Gustavson has its own (MIT) license at the top of it

    #include "imguiSdfGenerator.h"
    #include <math.h>

    #include "imgui.h"
    #define IMGUI_DEFINE_MATH_OPERATORS
    #define IMGUI_DEFINE_PLACEMENT_NEW
    #include "imgui_internal.h"


    // TODO: add support for -lfreetype if possible
    // TODO: Here we compile "stb_rect_pack.h" and "stb_truetype.h" twice in 2 static units (see imgui_draw.cpp)...
    #define STBRP_ASSERT(x) IM_ASSERT(x)
    #ifndef IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
    #define STBRP_STATIC
    #define STB_RECT_PACK_IMPLEMENTATION
    #endif
    #include "stb_rect_pack.h"

    #define STBTT_malloc(x,u) ((void)(u), ImGui::MemAlloc(x))
    #define STBTT_free(x,u) ((void)(u), ImGui::MemFree(x))
    #define STBTT_assert(x) IM_ASSERT(x)
    #ifndef IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
    #define STBTT_STATIC
    #define STB_TRUETYPE_IMPLEMENTATION
    #else
    #define STBTT_DEF extern
    #endif
    #include "stb_truetype.h"


    namespace ImGuiSdfGenerator {
    // This namespace should contain stuff that is independent
    // on both stb_truetype and freetype
    // except one method that should be used as binding

    struct SdfExtraParams {
    float sdExtentInPixels; // signed distant extent [e.g. 2 pxls means that the sd alpha extends two pixels on each side of the glyph contour]
    float sdCenterValue; // the alpha value on the glyph contour [usually 0.5]
    float sdFlatnessInPixels; // 1.f [0.25f]
    const ImFontConfig* cfg;
    SdfExtraParams(const ImFontConfig* _cfg,float _sdCenterValue=0.5f,float _sdFlatnessInPixels=1.f) : sdCenterValue(_sdCenterValue),sdFlatnessInPixels(_sdFlatnessInPixels),cfg(_cfg) {
    IM_ASSERT(cfg);
    IM_ASSERT(cfg->SizePixels>0);
    sdExtentInPixels=(float)cfg->SizePixels*0.125f;
    sdExtentInPixels*=float(cfg->OversampleH>cfg->OversampleV?cfg->OversampleH:cfg->OversampleV);
    IM_ASSERT(sdFlatnessInPixels>0);
    }
    };

    inline static float ImVec2Dot(const ImVec2& S1,const ImVec2& S2) {return (S1.x*S2.x+S1.y*S2.y);}
    inline static float ImVec2Cross(const ImVec2& P0,const ImVec2& P1) {return P0.x*P1.y - P1.x*P0.y;}
    inline static bool AreFuzzyEqual(float v0,float v1,float eps = float(0.0000001)) {return fabs(v0-v1)<eps;}
    inline static bool AreFuzzyEqual(const ImVec2& v0,const ImVec2& v1,float eps = float(0.0000001)) {return (AreFuzzyEqual(v0.x,v1.x,eps) && AreFuzzyEqual(v0.y,v1.y,eps));}

    inline static float GetSegmentLength(const ImVec2& p0,const ImVec2& p1) {return sqrt((p1.x-p0.x)*(p1.x-p0.x)+(p1.y-p0.y)*(p1.y-p0.y));}
    inline static float GetQuadraticBezierCurveLength(const ImVec2& p0,const ImVec2& p1,const ImVec2& p2) {
    // http://www.malczak.linuxpl.com/blog/quadratic-bezier-curve-length/
    // Good approximation, but might fail: it might return NaN or infinite [Test case: P0=(5.9732384830713272,4.372880969196558) C0=(5.6681537218391895,4.372880969196558) P1=(5.3791260533034801,4.372880969196558)]
    ImVec2 a(p0.x-float(2)*p1.x+p2.x,p0.y-float(2)*p1.y+p2.y);
    ImVec2 b(float(2)*p1.x-float(2)*p0.x,float(2)*p1.y-float(2)*p0.y);

    const float A = float(4)*(a.x*a.x+a.y*a.y);
    const float B = float(4)*(a.x*b.x+a.y*b.y);
    const float C = b.x*b.x+b.y*b.y;

    const float Sabc = float(2)*sqrt(A+B+C);
    const float A_2 = sqrt(A);
    const float A_32 = float(2)*A*A_2;
    const float C_2 = float(2)*sqrt(C);
    const float BA = B/A_2;

    return (A_32*Sabc+A_2*B*(Sabc-C_2) + (float(4)*C*A-B*B)*log((float(2)*A_2+BA+Sabc)/(BA+C_2)))/(float(4)*A_32);
    }
    inline static float GetQuadraticBezierCurveLengthAlternative(const ImVec2& p0,const ImVec2& p1,const ImVec2& p2,int num_segments=5) {
    // Approximated by flattening the curve into 'num_segments' intermediate points.
    if (num_segments<=0) num_segments = 5;

    ImVec4 W;
    float length=0,t,u;
    ImVec2 L = p0,tp;
    for (int i = 0; i < num_segments; i++) {
    t = (float)(i+1)/(float)(num_segments+1);u = float(1.0) - t;
    W.x=u*u;W.y=float(2)*u*t;W.z=t*t;W.w=float(0);

    tp.x = W.x*p0.x + W.y*p1.x + W.z*p2.x;
    tp.y = W.x*p0.y + W.y*p1.y + W.z*p2.y;

    length+=GetSegmentLength(L,tp);
    L=tp;
    }
    tp = p2;
    length+=GetSegmentLength(L,tp);

    return length;
    #ifdef __cplusplus
    extern "C" {
    #include "edtaa3func.c.inl"
    }
    inline static float GetCubicBezierCurveLength(const ImVec2& p0,const ImVec2& p1,const ImVec2& p2,const ImVec2& p3,int num_segments=5) {
    // Approximated by flattening the curve into 'num_segments' intermediate points.
    if (num_segments<=0) num_segments = 5;

    ImVec4 W;
    float length=0,t,u;
    ImVec2 L = p0,tp;
    for (int i = 0; i < num_segments; i++) {
    t = (float)(i+1)/(float)(num_segments+1);u = float(1.0) - t;
    W.x=u*u*u;W.y=float(3)*u*u*t;W.z=float(3)*u*t*t;W.w=t*t*t;

    tp.x = W.x*p0.x + W.y*p1.x + W.z*p2.x + W.w*p3.x;
    tp.y = W.x*p0.y + W.y*p1.y + W.z*p2.y + W.w*p3.y;

    length+=GetSegmentLength(L,tp);
    L=tp;
    #else //__cplusplus
    #include "edtaa3func.c.inl"
    #endif //__cplusplus

    namespace ImGuiEdtaaHelper {
    // The purpose of TmpData is to allow releasing the memory all togther
    // at the end of a multiple call to make_distance_mapb(...)/make_distance_mapd(...)
    // [However we don't use this feature here]
    struct TmpData {
    ImVector<double> v_data;

    ImVector<short> v_xdist;
    ImVector<short> v_ydist;
    ImVector<double> v_gx;
    ImVector<double> v_gy;
    ImVector<double> v_outside;
    ImVector<double> v_inside;

    void freeMemory() {
    TmpData tmp;
    v_data.swap(tmp.v_data);
    v_xdist.swap(tmp.v_xdist);
    v_ydist.swap(tmp.v_ydist);
    v_gx.swap(tmp.v_gx);
    v_outside.swap(tmp.v_outside);
    v_inside.swap(tmp.v_inside);
    }
    tp = p3;
    length+=GetSegmentLength(L,tp);
    };
    static TmpData tmpData;
    static void FreeTmpMemory() {tmpData.freeMemory();}

    return length;
    }
    // These two methods are adapted from: https://github.com/rougier/freetype-gl (Copyright 2011,2012 Nicolas P. Rougier),
    // so that the memory they use can optionally be freed once, after all the glyphs are processed.
    static double * make_distance_mapd( double *data, unsigned int width, unsigned int height, TmpData* pOptionalTmpData=NULL)
    {
    if (!pOptionalTmpData) pOptionalTmpData=&tmpData;

    inline static float GetSquaredDistancePointSegment(const ImVec2& P,const ImVec2& S0,const ImVec2& S1,float eps = float(0.0000001)) {
    // Based on: http://stackoverflow.com/questions/849211/shortest-distance-between-a-point-and-a-line-segment
    const float l2 = (S0.x-S1.x)*(S0.x-S1.x)+(S0.y-S1.y)*(S0.y-S1.y); // segment length squared
    if (l2 < eps) {
    // S1 == S2 case
    return (P.x-S0.x)*(P.x-S0.x)+(P.y-S0.y)*(P.y-S0.y);
    }
    ImVec2 T(S1.x-S0.x,S1.y-S0.y),PS1(P.x-S0.x,P.y-S0.y);
    const float tf = ImVec2Dot(PS1, T)/l2;
    const float minTf = float(1) < tf ? float(1) : tf;
    const float t = float(0) > minTf ? float(0) : minTf;
    // T = Projection on the segment
    T.x = S0.x + T.x*t;
    T.y = S0.y + T.y*t;
    return (P.x-T.x)*(P.x-T.x)+(P.y-T.y)*(P.y-T.y);
    }
    inline static float GetSquaredDistanceToQuadraticBezierCurve(const ImVec2& point,const ImVec2& p0,const ImVec2& p1,const ImVec2& p2,int num_segments=2,ImVec2* pChk0=NULL,ImVec2* pChk1=NULL,float eps = float(0.0000001)) {
    static bool firstRun = true;
    static const int max_num_intermediate_segments = 25; // >0
    static const int total_num_segment = (max_num_intermediate_segments+1)*max_num_intermediate_segments/2;
    static int start_num_segment_index[max_num_intermediate_segments+1];
    static ImVec4 weights[total_num_segment];
    const unsigned int area = width * height;
    IM_ASSERT(area>0);
    pOptionalTmpData->v_xdist.resize(area); short* xdist = &pOptionalTmpData->v_xdist[0];
    pOptionalTmpData->v_ydist.resize(area); short* ydist = &pOptionalTmpData->v_ydist[0];
    pOptionalTmpData->v_gx.resize(area); double* gx = &pOptionalTmpData->v_gx[0];
    pOptionalTmpData->v_gy.resize(area); double* gy = &pOptionalTmpData->v_gy[0];
    pOptionalTmpData->v_outside.resize(area); double* outside=&pOptionalTmpData->v_outside[0];
    pOptionalTmpData->v_inside.resize(area); double* inside= &pOptionalTmpData->v_inside[0];

    if (num_segments>max_num_intermediate_segments) num_segments = max_num_intermediate_segments;
    else if (num_segments<0) num_segments=0;
    double vmin = DBL_MAX;
    unsigned int i;

    if (firstRun) {
    // static init here
    firstRun = false;
    int cnt = 0;
    for (int seg = 0; seg <= max_num_intermediate_segments; seg++) {
    start_num_segment_index[seg] = cnt;
    for (int i = 1; i <= seg; i++) {
    float t = (float)i/(float)(seg+1);
    float u = float(1.0) - t;
    ImVec4& W = weights[cnt];
    W.x=u*u;
    W.y=float(2)*u*t;
    W.z=t*t;
    W.w=float(0); // optional
    ++cnt;
    }
    }
    // Compute outside = edtaa3(bitmap); % Transform background (0's)
    computegradient( data, width, height, gx, gy);
    edtaa3(data, gx, gy, width, height, xdist, ydist, outside);
    for( i=0; i<area; ++i) {
    if( outside[i] < 0.0 ) outside[i] = 0.0;
    }

    float minSquaredDistance=FLT_MAX,tmp; // FLT_MAX is probably in <limits.h>
    ImVec2 L = p0,tp;
    for (int i = start_num_segment_index[num_segments],isz=i+num_segments; i < isz; i++) {
    const ImVec4& W=weights[i];
    tp.x = W.x*p0.x + W.y*p1.x + W.z*p2.x;
    tp.y = W.x*p0.y + W.y*p1.y + W.z*p2.y;

    tmp = GetSquaredDistancePointSegment(point,L,tp,eps);
    if (minSquaredDistance>=tmp) {minSquaredDistance=tmp;if (pChk0) *pChk0=L;if (pChk1) *pChk1=tp;}
    L=tp;
    // Compute inside = edtaa3(1-bitmap); % Transform foreground (1's)
    memset( gx, 0, sizeof(double)*area );
    memset( gy, 0, sizeof(double)*area );
    for( i=0; i<area; ++i) data[i] = 1 - data[i];
    computegradient( data, width, height, gx, gy );
    edtaa3( data, gx, gy, width, height, xdist, ydist, inside );
    for( i=0; i<area; ++i ) {
    if( inside[i] < 0 ) inside[i] = 0.0;
    }
    tp = p2;
    tmp = GetSquaredDistancePointSegment(point,L,tp,eps);
    if (minSquaredDistance>tmp) {minSquaredDistance=tmp;if (pChk0) *pChk0=L;if (pChk1) *pChk1=tp;}

    return minSquaredDistance;
    }
    inline static float GetSquaredDistanceToCubicBezierCurve(const ImVec2& point,const ImVec2& p0,const ImVec2& p1,const ImVec2& p2,const ImVec2& p3,int num_segments=2,ImVec2* pChk0=NULL,ImVec2* pChk1=NULL,float eps = float(0.0000001)) {
    static bool firstRun = true;
    static const int max_num_intermediate_segments = 25; // >0
    static const int total_num_segment = (max_num_intermediate_segments+1)*max_num_intermediate_segments/2;
    static int start_num_segment_index[max_num_intermediate_segments+1];
    static ImVec4 weights[total_num_segment];

    if (num_segments>max_num_intermediate_segments) num_segments = max_num_intermediate_segments;
    else if (num_segments<0) num_segments=0;

    if (firstRun) {
    // static init here
    firstRun = false;
    int cnt = 0;
    for (int seg = 0; seg <= max_num_intermediate_segments; seg++) {
    start_num_segment_index[seg] = cnt;
    for (int i = 1; i <= seg; i++) {
    float t = (float)i/(float)(seg+1);
    float u = float(1.0) - t;
    ImVec4& W = weights[cnt];
    W.x=u*u*u;
    W.y=float(3)*u*u*t;
    W.z=float(3)*u*t*t;
    W.w=t*t*t;
    ++cnt;
    }
    }
    // distmap = outside - inside; % Bipolar distance field
    for( i=0; i<area; ++i) {
    outside[i] -= inside[i];
    if( outside[i] < vmin ) vmin = outside[i];
    }

    float minSquaredDistance=FLT_MAX,tmp; // FLT_MAX is probably in <limits.h>
    ImVec2 L = p0,tp;
    for (int i = start_num_segment_index[num_segments],isz=i+num_segments; i < isz; i++) {
    const ImVec4& W=weights[i];
    tp.x = W.x*p0.x + W.y*p1.x + W.z*p2.x + W.w*p3.x;
    tp.y = W.x*p0.y + W.y*p1.y + W.z*p2.y + W.w*p3.y;
    vmin = fabs(vmin);

    tmp = GetSquaredDistancePointSegment(point,L,tp,eps);
    if (minSquaredDistance>tmp) {minSquaredDistance=tmp;if (pChk0) *pChk0=L;if (pChk1) *pChk1=tp;}
    L=tp;
    for( i=0; i<area; ++i) {
    double v = outside[i];
    if ( v < -vmin) outside[i] = -vmin;
    else if( v > +vmin) outside[i] = +vmin;
    data[i] = (outside[i]+vmin)/(2*vmin);
    }
    tp = p3;
    tmp = GetSquaredDistancePointSegment(point,L,tp,eps);
    if (minSquaredDistance>tmp) {minSquaredDistance=tmp;if (pChk0) *pChk0=L;if (pChk1) *pChk1=tp;}

    return minSquaredDistance;
    return data;
    }


    // Usage of BezierChunk: use only the first 3 constructors and fill an array of BezierChunks, then call Init(...) on it and finally RasterizeGlyphShapeSDF(...)
    struct BezierChunk {
    enum Type {
    TYPE_SEGMENT=0,
    TYPE_QUADRATIC,
    TYPE_CUBIC
    };
    BezierChunk(const ImVec2& _P0,const ImVec2& _P1) : P0(_P0),P1(_P1),num_segments(-1),length(-1),type(TYPE_SEGMENT) {}
    BezierChunk(const ImVec2& _P0,const ImVec2& _C0,const ImVec2& _P1) : P0(_P0),C0(_C0),P1(_P1),num_segments(-1),length(-1),type(TYPE_QUADRATIC) {}
    BezierChunk(const ImVec2& _P0,const ImVec2& _C0,const ImVec2& _C1,const ImVec2& _P1) : P0(_P0),C0(_C0),C1(_C1),P1(_P1),num_segments(-1),length(-1),type(TYPE_CUBIC) {}


    ImVec2 P0,C0,C1,P1;
    mutable int num_segments; // number of internal point to approximate TYPE_QUADRATIC and TYPE_CUBIC curves into straight segments
    mutable float length;
    Type type;
    BezierChunk() : num_segments(-1),length(-1),type(TYPE_SEGMENT){}

    inline static void Init(BezierChunk* chunksIn,int numChunksIn,float scale_flatness_in_pixels=float(0.75)) {
    IM_ASSERT(chunksIn && numChunksIn>0);
    IM_ASSERT(scale_flatness_in_pixels>0);

    for (int i=0;i<numChunksIn;i++) {
    const BezierChunk& c = chunksIn[i];
    if (c.type==TYPE_SEGMENT) continue; //c.length = GetSegmentLength(c.P0,c.P1);
    else if (c.type==TYPE_QUADRATIC) {
    c.length = GetQuadraticBezierCurveLength(c.P0,c.C0,c.P1); // Good approximation, but might fail: c.length = NaN or infinite [Test case: P0=(5.9732384830713272,4.372880969196558) C0=(5.6681537218391895,4.372880969196558) P1=(5.3791260533034801,4.372880969196558)]
    if (!(c.length>=0) || c.length>100000000000)
    c.length = GetQuadraticBezierCurveLengthAlternative(c.P0,c.C0,c.P1); // Fallback
    IM_ASSERT(c.length>=0); //
    }
    else if (c.type==TYPE_CUBIC) c.length = GetCubicBezierCurveLength(c.P0,c.C0,c.C1,c.P1);
    else continue;
    c.num_segments = c.length/scale_flatness_in_pixels; // Num intermediate points used to flatten the curve into line chunks
    IM_ASSERT(c.num_segments>=0);
    //printf("length=%1.4f num_segments=%d\n",c.length,c.num_segments);
    }
    }
    inline static ImVec2 AssignPoint(float unscaledPointX,float unscaledPointY,float scale_x=float(1),float scale_y=float(1),const ImVec2& postScaleOffset=ImVec2(0,0)) {
    return ImVec2(
    unscaledPointX*scale_x-postScaleOffset.x//-0.25f
    ,
    (unscaledPointY*(-scale_y)-postScaleOffset.y)//-0.25f
    );
    }
    static void RasterizeGlyphShapeSDF(
    unsigned char* imageOutPxls,int imageOutW,int imageOutH,int imageOutStride,
    const BezierChunk* chunksIn,int numChunksIn,const int* numChunksPerContourIn,int numChunksPerContourInSize,
    const SdfExtraParams& sdExtraParams,float eps = float(0.0000001)
    )
    {
    IM_ASSERT(imageOutPxls && imageOutW>0 && imageOutH>0 && imageOutStride>0);
    IM_ASSERT(chunksIn && numChunksIn>0 && numChunksPerContourIn && numChunksPerContourInSize>0);
    //IM_ASSERT(scale!=0);

    typedef struct floatPixel_ {
    float d;bool isOutside;
    floatPixel_() : d(-1),isOutside(true) {}
    inline void process(float _d,bool _isOutside) {if (d<0 || _d<d) {d=_d;isOutside=_isOutside;}}
    } floatPixel;


    const float sdCenterValuePer255 = sdExtraParams.sdCenterValue*255.0f;
    const float oneMinussdCenterValuePer255 = 255.0f-sdCenterValuePer255;
    const float sdExtentInPixelsSquared = sdExtraParams.sdExtentInPixels*sdExtraParams.sdExtentInPixels;
    for (int y=0;y<imageOutH;y++) {
    unsigned char* ppxl = NULL; // previous pixel
    for (int x=0;x<imageOutW;x++) {
    floatPixel fpxl;
    ImVec2 P(round(x),round(y)),CHK0,CHK1;bool isOutside;
    int chunkIndex=0;float accurateDistanceSquared(-1.0);
    for (int contourIndex=0;contourIndex<numChunksPerContourInSize;contourIndex++) {
    const int numChunks = numChunksPerContourIn[contourIndex];
    for (int maxChunkIndexOfThisContour=chunkIndex+numChunks;chunkIndex<maxChunkIndexOfThisContour;chunkIndex++) {
    const BezierChunk& c = chunksIn[chunkIndex];

    if (c.type==TYPE_SEGMENT) accurateDistanceSquared = GetSquaredDistancePointSegment(P,c.P0,c.P1,eps);
    else if (c.type==TYPE_QUADRATIC) {
    IM_ASSERT(c.num_segments>=0); // 'chunksIn' must be inited before calling this method
    accurateDistanceSquared = GetSquaredDistanceToQuadraticBezierCurve(P,c.P0,c.C0,c.P1,c.num_segments,&CHK0,&CHK1,eps);
    }
    else if (c.type==TYPE_CUBIC) {
    IM_ASSERT(c.num_segments>=0); // 'chunksIn' must be inited before calling this method
    accurateDistanceSquared = GetSquaredDistanceToCubicBezierCurve(P,c.P0,c.C0,c.C1,c.P1,c.num_segments,&CHK0,&CHK1,eps);
    }
    else {
    IM_ASSERT(true); // Why here?
    continue;
    }
    if (accurateDistanceSquared>=sdExtentInPixelsSquared) continue;
    if (fpxl.d>=0 && accurateDistanceSquared>fpxl.d) continue;
    isOutside = true;
    if (c.type==TYPE_SEGMENT) {
    if (ImVec2Cross(ImVec2(c.P1.x-c.P0.x,c.P1.y-c.P0.y),ImVec2(P.x-c.P0.x,P.y-c.P0.y))>=float(0)) isOutside = !isOutside;
    }
    else {
    if (ImVec2Cross(ImVec2(CHK1.x-CHK0.x,CHK1.y-CHK0.y),ImVec2(P.x-CHK0.x,P.y-CHK0.y))>=float(0)) isOutside = !isOutside;
    }
    fpxl.process(accurateDistanceSquared,isOutside);
    }
    }
    //-------------------------------------------------------------
    // Assign pxl in [0,255] from fpxl based on 'sdExtentInPixels' and 'sdCenterValue'
    unsigned char& pxl = imageOutPxls[imageOutStride*y+x];
    //IM_ASSERT(fpxl.d>=0);
    if (fpxl.d<0) pxl = (!ppxl || (*ppxl<=sdCenterValuePer255)) ? 0 : 255;
    else if (fpxl.d>=sdExtentInPixelsSquared) pxl = fpxl.isOutside ? 0 : 255;
    else
    {
    fpxl.d = sqrt(fpxl.d); // non-squared distance
    if (fpxl.isOutside) {
    fpxl.d = (sdCenterValuePer255*(sdExtraParams.sdExtentInPixels-fpxl.d)/sdExtraParams.sdExtentInPixels); // value in [0,sdCenterValuePer255]
    IM_ASSERT(fpxl.d>=0 && fpxl.d<=sdCenterValuePer255);
    IM_ASSERT(fpxl.d>=0 && fpxl.d<=255);
    pxl = (unsigned char) fpxl.d;
    }
    else {// inside
    fpxl.d = (sdCenterValuePer255+oneMinussdCenterValuePer255*(sdExtraParams.sdExtentInPixels-fpxl.d)/sdExtraParams.sdExtentInPixels); // value in [sdCenterValuePer255,sdCenterValuePer255+oneMinussdCenterValuePer255]
    IM_ASSERT(fpxl.d>=sdCenterValuePer255 && fpxl.d<=sdCenterValuePer255+oneMinussdCenterValuePer255);
    IM_ASSERT(fpxl.d>=0 && fpxl.d<=255);
    pxl = (unsigned char) fpxl.d;
    }
    }
    ppxl = &pxl;
    //---------------------------------------------------------------
    }
    }
    }

    };


    typedef ImVector<BezierChunk> BezierChunkVector;
    typedef ImVector<int> IntVector;


    #ifdef __STB_INCLUDE_STB_TRUETYPE_H__
    STBTT_DEF bool stbtt_RasterizeSD(stbtt__bitmap *result,float flatness_in_pixels, stbtt_vertex *vertices, int num_vertices, float scale_x,float scale_y, int x_off, int y_off,const SdfExtraParams& sdExtraParams,void *userdata=NULL)
    static unsigned char * make_distance_mapb(const unsigned char *img,unsigned int width, unsigned int height, unsigned char* imgOutSameSize, TmpData* pOptionalTmpData=NULL)
    {
    const ImVec2 postScaleOffset(x_off,y_off);
    IM_ASSERT(imgOutSameSize);

    BezierChunkVector chunks;IntVector numChunksPerContour;
    if (!pOptionalTmpData) pOptionalTmpData=&tmpData;

    static const float eps = float(0.0000001);
    const unsigned int area = width * height;
    IM_ASSERT(area>0);
    pOptionalTmpData->v_data.resize(area);
    double* data = &pOptionalTmpData->v_data[0];

    // Filling chunks and numChunksPerContour:
    if (num_vertices<=0 || !vertices || scale_x==0 || scale_y==0) {
    chunks.resize(0);
    numChunksPerContour.resize(0);
    return false;
    }
    chunks.reserve(num_vertices);
    numChunksPerContour.reserve(4);
    int cnt = 0;BezierChunk c;BezierChunk* ppc=NULL;ImVec2 tmpP0;
    for (int i=0;i<num_vertices;i++) {
    const stbtt_vertex& v= vertices[i];
    if (v.type==STBTT_vmove) {
    if (cnt>0) {
    numChunksPerContour.push_back(cnt);
    cnt=0;ppc=NULL;
    }
    tmpP0 = BezierChunk::AssignPoint(v.x,v.y,scale_x,scale_y,postScaleOffset);
    continue;
    }
    else {
    // Assign P1 and P0
    c.P1 = BezierChunk::AssignPoint(v.x,v.y,scale_x,scale_y,postScaleOffset);
    if (ppc) c.P0 = ppc->P1;
    else {
    c.P0 = tmpP0;
    IM_ASSERT(cnt==0);
    IM_ASSERT(ppc==NULL);
    }
    if (AreFuzzyEqual(c.P0,c.P1,eps)) continue; // Checks if P0==P1 and skips the chunk
    }
    c.type = (v.type==STBTT_vcurve) ? BezierChunk::TYPE_QUADRATIC : BezierChunk::TYPE_SEGMENT;
    if (v.type==STBTT_vcurve) {
    c.C0 = BezierChunk::AssignPoint(v.cx,v.cy,scale_x,scale_y,postScaleOffset);
    if (AreFuzzyEqual(c.P0,c.P1,eps)) continue; // Checks if P0==P1 and skips the chunk
    if (AreFuzzyEqual(c.P0,c.C0,eps) || AreFuzzyEqual(c.C0,c.P1,eps)) c.type = BezierChunk::TYPE_SEGMENT; // Degrade to STBTT_vline
    }
    //-------------------------------------------------
    //-------------------------------------------------
    chunks.push_back(c);
    ppc = &chunks[chunks.size()-1];
    ++cnt;
    unsigned int i;

    IM_ASSERT(c.P0.x==ppc->P0.x && c.P0.y==ppc->P0.y && c.P1.x==ppc->P1.x && c.P1.y==ppc->P1.y && c.type==ppc->type);
    }
    if (cnt>0) {
    numChunksPerContour.push_back(cnt);
    cnt=0;ppc=NULL;
    }
    // find minimimum and maximum values
    double img_min = DBL_MAX,img_max = DBL_MIN,v;

    IM_ASSERT(chunks.size()>0 && numChunksPerContour.size()>0);
    BezierChunk::Init(&chunks[0],chunks.size(),flatness_in_pixels);

    BezierChunk::RasterizeGlyphShapeSDF(result->pixels,result->w,result->h,result->stride,
    &chunks[0],chunks.size(),&numChunksPerContour[0],numChunksPerContour.size(),
    sdExtraParams,eps
    );
    for( i=0; i<area; ++i) {
    data[i] = v = img[i];
    if (v > img_max) img_max = v;
    if (v < img_min) img_min = v;
    }

    return true;
    }
    #endif //__STB_INCLUDE_STB_TRUETYPE_H__
    // Map values from 0 - 255 to 0.0 - 1.0
    for( i=0; i<area; ++i) data[i] = (img[i]-img_min)/img_max;

    } // namespace ImGuiSdfGenerator
    data = make_distance_mapd(data, width, height);

    // map values from 0.0 - 1.0 to 0 - 255
    for( i=0; i<area; ++i) imgOutSameSize[i] = (unsigned char)(255*(1-data[i]));

    #ifdef __STB_INCLUDE_STB_TRUETYPE_H__
    extern "C" {
    // These methods are mostly juat adapted from the ones inside stb_truetype.h
    STBTT_DEF void stbtt_RasterizeSD(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off,const ImGuiSdfGenerator::SdfExtraParams& sdExtraParams, void *userdata)
    {
    IM_ASSERT(shift_x==0 && shift_y==0); // We don't use them
    ImGuiSdfGenerator::stbtt_RasterizeSD(result,flatness_in_pixels,vertices,num_verts,scale_x,scale_y,x_off,y_off,sdExtraParams,userdata);
    return imgOutSameSize;
    }
    STBTT_DEF void stbtt_MakeSDGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph,const ImGuiSdfGenerator::SdfExtraParams& sdExtraParams,void* userdata)
    {
    int ix0,iy0;
    stbtt_vertex *vertices;
    int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);

    stbtt__bitmap gbm;
    } //ImGuiEdtaaHelper

    stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0);
    gbm.pixels = output;
    gbm.w = out_w;
    gbm.h = out_h;
    gbm.stride = out_stride;

    if (gbm.w && gbm.h) stbtt_RasterizeSD(&gbm, sdExtraParams.sdFlatnessInPixels, vertices, num_verts, scale_x, scale_y,shift_x,shift_y, ix0,iy0, sdExtraParams,info->userdata);

    STBTT_free(vertices, info->userdata);
    namespace ImGui {
    void ProcessAlpha8ImageForSignedDistanceFontEffect(unsigned char* pixels,int w,int h) {
    IM_ASSERT(pixels && w>0 && h>0);
    ImVector<unsigned char> tmpData;tmpData.resize(w*h);
    ImGuiEdtaaHelper::make_distance_mapb(pixels,w,h,&tmpData[0]);
    ImGuiEdtaaHelper::FreeTmpMemory();
    memcpy(pixels,&tmpData[0],w*h);
    }
    // rects array must be big enough to accommodate all characters in the given ranges
    STBTT_DEF int stbtt_PackSDFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects,const ImGuiSdfGenerator::SdfExtraParams& sdExtraParams)
    {
    int i,j,k, return_value = 1;

    // save current values
    int old_h_over = spc->h_oversample;
    int old_v_over = spc->v_oversample;

    k = 0;
    for (i=0; i < num_ranges; ++i) {
    float fh = ranges[i].font_size;
    float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh);
    float recip_h,recip_v,sub_x,sub_y;
    spc->h_oversample = ranges[i].h_oversample;
    spc->v_oversample = ranges[i].v_oversample;
    recip_h = 1.0f / spc->h_oversample;
    recip_v = 1.0f / spc->v_oversample;
    sub_x = stbtt__oversample_shift(spc->h_oversample);
    sub_y = stbtt__oversample_shift(spc->v_oversample);

    for (j=0; j < ranges[i].num_chars; ++j) {
    stbrp_rect *r = &rects[k];
    if (r->was_packed) {
    stbtt_packedchar *bc = &ranges[i].chardata_for_range[j];
    int advance, lsb, x0,y0,x1,y1;
    int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j];
    int glyph = stbtt_FindGlyphIndex(info, codepoint);
    stbrp_coord pad = (stbrp_coord) spc->padding;

    // pad on left and top
    r->x += pad;
    r->y += pad;
    r->w -= pad;
    r->h -= pad;
    stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb);
    stbtt_GetGlyphBitmapBox(info, glyph,
    scale * spc->h_oversample,
    scale * spc->v_oversample,
    &x0,&y0,&x1,&y1);

    // new code -------------------------------------------------------------
    stbtt_MakeSDGlyphBitmapSubpixel(info,
    spc->pixels + r->x + r->y*spc->stride_in_bytes,
    r->w - spc->h_oversample+1,
    r->h - spc->v_oversample+1,
    spc->stride_in_bytes,
    scale * spc->h_oversample,
    scale * spc->v_oversample,
    0,0,
    glyph,
    sdExtraParams,
    NULL
    );
    //-------------------------------------------------------------------------

    if (spc->h_oversample > 1)
    stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes,
    r->w, r->h, spc->stride_in_bytes,
    spc->h_oversample);

    if (spc->v_oversample > 1)
    stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes,
    r->w, r->h, spc->stride_in_bytes,
    spc->v_oversample);

    bc->x0 = (stbtt_int16) r->x;
    bc->y0 = (stbtt_int16) r->y;
    bc->x1 = (stbtt_int16) (r->x + r->w);
    bc->y1 = (stbtt_int16) (r->y + r->h);
    bc->xadvance = scale * advance;
    bc->xoff = (float) x0 * recip_h + sub_x;
    bc->yoff = (float) y0 * recip_v + sub_y;
    bc->xoff2 = (x0 + r->w) * recip_h + sub_x;
    bc->yoff2 = (y0 + r->h) * recip_v + sub_y;
    } else {
    return_value = 0; // if any fail, report failure
    }

    ++k;
    }
    }

    // restore original values
    spc->h_oversample = old_h_over;
    spc->v_oversample = old_v_over;

    return return_value;
    }
    void PostBuildForSignedDistanceFontEffect(ImFontAtlas* atlas) {
    if (!atlas->TexPixelsAlpha8) atlas->GetTexDataAsAlpha8(&atlas->TexPixelsAlpha8,NULL,NULL);
    ProcessAlpha8ImageForSignedDistanceFontEffect(atlas->TexPixelsAlpha8,atlas->TexWidth,atlas->TexHeight);
    }
    #endif //__STB_INCLUDE_STB_TRUETYPE_H__


    // This is ImFontAtlas::Build() with just one line removed and two more added (but now the atlas is an argument...)
    bool BuildSignedDistanceFontAtlas(ImFontAtlas* atlas)
    {
    IM_ASSERT(atlas->ConfigData.Size > 0);

    atlas->TexID = NULL;
    atlas->TexWidth = atlas->TexHeight = 0;
    atlas->TexUvWhitePixel = ImVec2(0, 0);
    atlas->ClearTexData();

    struct ImFontTempBuildData
    {
    stbtt_fontinfo FontInfo;
    stbrp_rect* Rects;
    stbtt_pack_range* Ranges;
    int RangesCount;
    };
    ImFontTempBuildData* tmp_array = (ImFontTempBuildData*)ImGui::MemAlloc((size_t)atlas->ConfigData.Size * sizeof(ImFontTempBuildData));

    // Initialize font information early (so we can error without any cleanup) + count glyphs
    int total_glyph_count = 0;
    int total_glyph_range_count = 0;
    for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++)
    {
    ImFontConfig& cfg = atlas->ConfigData[input_i];
    ImFontTempBuildData& tmp = tmp_array[input_i];

    IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == atlas));
    const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)cfg.FontData, cfg.FontNo);
    IM_ASSERT(font_offset >= 0);
    if (!stbtt_InitFont(&tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset))
    return false;

    // Count glyphs
    if (!cfg.GlyphRanges)
    cfg.GlyphRanges = atlas->GetGlyphRangesDefault();
    for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2)
    {
    total_glyph_count += (in_range[1] - in_range[0]) + 1;
    total_glyph_range_count++;
    }
    }

    // Start packing. We need a known width for the skyline algorithm. Using a cheap heuristic here to decide of width. User can override TexDesiredWidth if they wish.
    // After packing is done, width shouldn't matter much, but some API/GPU have texture size limitations and increasing width can decrease height.
    atlas->TexWidth = (atlas->TexDesiredWidth > 0) ? atlas->TexDesiredWidth : (total_glyph_count > 4000) ? 4096 : (total_glyph_count > 2000) ? 2048 : (total_glyph_count > 1000) ? 1024 : 512;
    atlas->TexHeight = 0;
    const int max_tex_height = 1024*32;
    stbtt_pack_context spc;
    stbtt_PackBegin(&spc, NULL, atlas->TexWidth, max_tex_height, 0, 1, NULL);

    // Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values).
    ImVector<stbrp_rect> extra_rects;
    atlas->RenderCustomTexData(0, &extra_rects);
    stbtt_PackSetOversampling(&spc, 1, 1);
    stbrp_pack_rects((stbrp_context*)spc.pack_info, &extra_rects[0], extra_rects.Size);
    for (int i = 0; i < extra_rects.Size; i++)
    if (extra_rects[i].was_packed)
    atlas->TexHeight = ImMax(atlas->TexHeight, extra_rects[i].y + extra_rects[i].h);

    // Allocate packing character data and flag packed characters buffer as non-packed (x0=y0=x1=y1=0)
    int buf_packedchars_n = 0, buf_rects_n = 0, buf_ranges_n = 0;
    stbtt_packedchar* buf_packedchars = (stbtt_packedchar*)ImGui::MemAlloc(total_glyph_count * sizeof(stbtt_packedchar));
    stbrp_rect* buf_rects = (stbrp_rect*)ImGui::MemAlloc(total_glyph_count * sizeof(stbrp_rect));
    stbtt_pack_range* buf_ranges = (stbtt_pack_range*)ImGui::MemAlloc(total_glyph_range_count * sizeof(stbtt_pack_range));
    memset(buf_packedchars, 0, total_glyph_count * sizeof(stbtt_packedchar));
    memset(buf_rects, 0, total_glyph_count * sizeof(stbrp_rect)); // Unnecessary but let's clear this for the sake of sanity.
    memset(buf_ranges, 0, total_glyph_range_count * sizeof(stbtt_pack_range));

    // First font pass: pack all glyphs (no rendering at this point, we are working with rectangles in an infinitely tall texture at this point)
    for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++)
    {
    ImFontConfig& cfg = atlas->ConfigData[input_i];
    ImFontTempBuildData& tmp = tmp_array[input_i];

    // Setup ranges
    int glyph_count = 0;
    int glyph_ranges_count = 0;
    for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2)
    {
    glyph_count += (in_range[1] - in_range[0]) + 1;
    glyph_ranges_count++;
    }
    tmp.Ranges = buf_ranges + buf_ranges_n;
    tmp.RangesCount = glyph_ranges_count;
    buf_ranges_n += glyph_ranges_count;
    for (int i = 0; i < glyph_ranges_count; i++)
    {
    const ImWchar* in_range = &cfg.GlyphRanges[i * 2];
    stbtt_pack_range& range = tmp.Ranges[i];
    range.font_size = cfg.SizePixels;
    range.first_unicode_codepoint_in_range = in_range[0];
    range.num_chars = (in_range[1] - in_range[0]) + 1;
    range.chardata_for_range = buf_packedchars + buf_packedchars_n;
    buf_packedchars_n += range.num_chars;
    }

    // Pack
    tmp.Rects = buf_rects + buf_rects_n;
    buf_rects_n += glyph_count;
    stbtt_PackSetOversampling(&spc, cfg.OversampleH, cfg.OversampleV);
    int n = stbtt_PackFontRangesGatherRects(&spc, &tmp.FontInfo, tmp.Ranges, tmp.RangesCount, tmp.Rects);
    stbrp_pack_rects((stbrp_context*)spc.pack_info, tmp.Rects, n);

    // Extend texture height
    for (int i = 0; i < n; i++)
    if (tmp.Rects[i].was_packed)
    atlas->TexHeight = ImMax(atlas->TexHeight, tmp.Rects[i].y + tmp.Rects[i].h);
    }
    IM_ASSERT(buf_rects_n == total_glyph_count);
    IM_ASSERT(buf_packedchars_n == total_glyph_count);
    IM_ASSERT(buf_ranges_n == total_glyph_range_count);

    // Create texture
    atlas->TexHeight = ImUpperPowerOfTwo(atlas->TexHeight);
    atlas->TexPixelsAlpha8 = (unsigned char*)ImGui::MemAlloc(atlas->TexWidth * atlas->TexHeight);
    memset(atlas->TexPixelsAlpha8, 0, atlas->TexWidth * atlas->TexHeight);
    spc.pixels = atlas->TexPixelsAlpha8;
    spc.height = atlas->TexHeight;

    // Second pass: render characters
    for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++)
    {
    ImFontConfig& cfg = atlas->ConfigData[input_i];
    ImFontTempBuildData& tmp = tmp_array[input_i];
    stbtt_PackSetOversampling(&spc, cfg.OversampleH, cfg.OversampleV);
    //------------------------------------------------------------------
    // old code (1 line)
    //stbtt_PackFontRangesRenderIntoRects(&spc, &tmp.FontInfo, tmp.Ranges, tmp.RangesCount, tmp.Rects);

    // new code
    ImGuiSdfGenerator::SdfExtraParams sdExtraParams(&cfg);
    # ifdef __STB_INCLUDE_STB_TRUETYPE_H__
    stbtt_PackSDFontRangesRenderIntoRects(&spc, &tmp.FontInfo, tmp.Ranges, tmp.RangesCount, tmp.Rects, sdExtraParams);
    # else // __STB_INCLUDE_STB_TRUETYPE_H__
    # error only stb_truetype is supported ATM
    # endif //__STB_INCLUDE_STB_TRUETYPE_H__
    //------------------------------------------------------------------
    tmp.Rects = NULL;
    }

    // End packing
    stbtt_PackEnd(&spc);
    ImGui::MemFree(buf_rects);
    buf_rects = NULL;

    // Third pass: setup ImFont and glyphs for runtime
    for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++)
    {
    ImFontConfig& cfg = atlas->ConfigData[input_i];
    ImFontTempBuildData& tmp = tmp_array[input_i];
    ImFont* dst_font = cfg.DstFont;

    float font_scale = stbtt_ScaleForPixelHeight(&tmp.FontInfo, cfg.SizePixels);
    int unscaled_ascent, unscaled_descent, unscaled_line_gap;
    stbtt_GetFontVMetrics(&tmp.FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap);

    float ascent = unscaled_ascent * font_scale;
    float descent = unscaled_descent * font_scale;
    if (!cfg.MergeMode)
    {
    dst_font->ContainerAtlas = atlas;
    dst_font->ConfigData = &cfg;
    dst_font->ConfigDataCount = 0;
    dst_font->FontSize = cfg.SizePixels;
    dst_font->Ascent = ascent;
    dst_font->Descent = descent;
    dst_font->Glyphs.resize(0);
    }
    dst_font->ConfigDataCount++;
    float off_y = (cfg.MergeMode && cfg.MergeGlyphCenterV) ? (ascent - dst_font->Ascent) * 0.5f : 0.0f;

    dst_font->FallbackGlyph = NULL; // Always clear fallback so FindGlyph can return NULL. It will be set again in BuildLookupTable()
    for (int i = 0; i < tmp.RangesCount; i++)
    {
    stbtt_pack_range& range = tmp.Ranges[i];
    for (int char_idx = 0; char_idx < range.num_chars; char_idx += 1)
    {
    const stbtt_packedchar& pc = range.chardata_for_range[char_idx];
    if (!pc.x0 && !pc.x1 && !pc.y0 && !pc.y1)
    continue;

    const int codepoint = range.first_unicode_codepoint_in_range + char_idx;
    if (cfg.MergeMode && dst_font->FindGlyph((unsigned short)codepoint))
    continue;

    stbtt_aligned_quad q;
    float dummy_x = 0.0f, dummy_y = 0.0f;
    stbtt_GetPackedQuad(range.chardata_for_range, atlas->TexWidth, atlas->TexHeight, char_idx, &dummy_x, &dummy_y, &q, 0);

    dst_font->Glyphs.resize(dst_font->Glyphs.Size + 1);
    ImFont::Glyph& glyph = dst_font->Glyphs.back();
    glyph.Codepoint = (ImWchar)codepoint;
    glyph.X0 = q.x0; glyph.Y0 = q.y0; glyph.X1 = q.x1; glyph.Y1 = q.y1;
    glyph.U0 = q.s0; glyph.V0 = q.t0; glyph.U1 = q.s1; glyph.V1 = q.t1;
    glyph.Y0 += (float)(int)(dst_font->Ascent + off_y + 0.5f);
    glyph.Y1 += (float)(int)(dst_font->Ascent + off_y + 0.5f);
    glyph.XAdvance = (pc.xadvance + cfg.GlyphExtraSpacing.x); // Bake spacing into XAdvance
    if (cfg.PixelSnapH)
    glyph.XAdvance = (float)(int)(glyph.XAdvance + 0.5f);
    }
    }
    cfg.DstFont->BuildLookupTable();
    }

    // Cleanup temporaries
    ImGui::MemFree(buf_packedchars);
    ImGui::MemFree(buf_ranges);
    ImGui::MemFree(tmp_array);

    // Render into our custom data block
    atlas->RenderCustomTexData(1, &extra_rects);

    return true;
    }

    }
    29 changes: 17 additions & 12 deletions imguiSdfGenerator.h
    Original file line number Diff line number Diff line change
    @@ -2,15 +2,7 @@
    #define IMGUISDFGENERATOR_H_
    #pragma once

    // VERSION 0.1 - Tested on Dear ImGui Version 1.50 WIP on Linux
    /*
    This is the worst signed distance font generator that can be found on the web for sure! (*)
    It's released just as a mockup-placeholder so that we have a working example that can be
    gradually improved.
    (*) The quality is so bad that if you use sdf shaders on normal non-sdf fonts it
    usually looks better.
    */
    // VERSION 1.0

    // BASIC USAGE:
    /*
    @@ -27,9 +19,15 @@ bool ImGui_ImplGlfw_CreateFontsTexture()
    ImGuiIO& io = ImGui::GetIO();
    unsigned char* pixels;
    int width, height;
    // New code -------------------------------------------------
    if (io.Fonts->ConfigData.empty()) io.Fonts->AddFontDefault();
    io.Fonts->Build(); // (change this line if you use [imgui_freetype](https://github.com/Vuhdo/imgui) appropriately)
    # ifdef IMIMPL_BUILD_SDF
    BuildSignedDistanceFontAtlas(io.Fonts); // New
    ImGui::PostBuildForSignedDistanceFontEffect(io.Fonts);
    # endif //IMIMPL_BUILD_SDF
    // End new code ----------------------------------------------
    io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits for OpenGL3 demo because it is more likely to be compatible with user's existing shader.
    ...
    @@ -106,6 +104,13 @@ bool ImGui_ImplGlfw_CreateFontsTexture()
    */

    bool BuildSignedDistanceFontAtlas(struct ImFontAtlas* atlas);

    #endif //IMGUISDFGENERATOR_H_

    namespace ImGui {
    void PostBuildForSignedDistanceFontEffect(struct ImFontAtlas* atlas);

    // Optional generic method: Hp) stride == w
    void ProcessAlpha8ImageForSignedDistanceFontEffect(unsigned char* pixels,int w,int h);
    } // namespace ImGui

    #endif //IMGUISDFGENERATOR_H_
  3. @Flix01 Flix01 created this gist Oct 26, 2016.
    772 changes: 772 additions & 0 deletions imguiSdfGenerator.cpp
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,772 @@
    #include "imguiSdfGenerator.h"
    #include <math.h>

    #include "imgui.h"
    #define IMGUI_DEFINE_MATH_OPERATORS
    #define IMGUI_DEFINE_PLACEMENT_NEW
    #include "imgui_internal.h"


    // TODO: add support for -lfreetype if possible
    // TODO: Here we compile "stb_rect_pack.h" and "stb_truetype.h" twice in 2 static units (see imgui_draw.cpp)...
    #define STBRP_ASSERT(x) IM_ASSERT(x)
    #ifndef IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
    #define STBRP_STATIC
    #define STB_RECT_PACK_IMPLEMENTATION
    #endif
    #include "stb_rect_pack.h"

    #define STBTT_malloc(x,u) ((void)(u), ImGui::MemAlloc(x))
    #define STBTT_free(x,u) ((void)(u), ImGui::MemFree(x))
    #define STBTT_assert(x) IM_ASSERT(x)
    #ifndef IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
    #define STBTT_STATIC
    #define STB_TRUETYPE_IMPLEMENTATION
    #else
    #define STBTT_DEF extern
    #endif
    #include "stb_truetype.h"


    namespace ImGuiSdfGenerator {
    // This namespace should contain stuff that is independent
    // on both stb_truetype and freetype
    // except one method that should be used as binding

    struct SdfExtraParams {
    float sdExtentInPixels; // signed distant extent [e.g. 2 pxls means that the sd alpha extends two pixels on each side of the glyph contour]
    float sdCenterValue; // the alpha value on the glyph contour [usually 0.5]
    float sdFlatnessInPixels; // 1.f [0.25f]
    const ImFontConfig* cfg;
    SdfExtraParams(const ImFontConfig* _cfg,float _sdCenterValue=0.5f,float _sdFlatnessInPixels=1.f) : sdCenterValue(_sdCenterValue),sdFlatnessInPixels(_sdFlatnessInPixels),cfg(_cfg) {
    IM_ASSERT(cfg);
    IM_ASSERT(cfg->SizePixels>0);
    sdExtentInPixels=(float)cfg->SizePixels*0.125f;
    sdExtentInPixels*=float(cfg->OversampleH>cfg->OversampleV?cfg->OversampleH:cfg->OversampleV);
    IM_ASSERT(sdFlatnessInPixels>0);
    }
    };

    inline static float ImVec2Dot(const ImVec2& S1,const ImVec2& S2) {return (S1.x*S2.x+S1.y*S2.y);}
    inline static float ImVec2Cross(const ImVec2& P0,const ImVec2& P1) {return P0.x*P1.y - P1.x*P0.y;}
    inline static bool AreFuzzyEqual(float v0,float v1,float eps = float(0.0000001)) {return fabs(v0-v1)<eps;}
    inline static bool AreFuzzyEqual(const ImVec2& v0,const ImVec2& v1,float eps = float(0.0000001)) {return (AreFuzzyEqual(v0.x,v1.x,eps) && AreFuzzyEqual(v0.y,v1.y,eps));}

    inline static float GetSegmentLength(const ImVec2& p0,const ImVec2& p1) {return sqrt((p1.x-p0.x)*(p1.x-p0.x)+(p1.y-p0.y)*(p1.y-p0.y));}
    inline static float GetQuadraticBezierCurveLength(const ImVec2& p0,const ImVec2& p1,const ImVec2& p2) {
    // http://www.malczak.linuxpl.com/blog/quadratic-bezier-curve-length/
    // Good approximation, but might fail: it might return NaN or infinite [Test case: P0=(5.9732384830713272,4.372880969196558) C0=(5.6681537218391895,4.372880969196558) P1=(5.3791260533034801,4.372880969196558)]
    ImVec2 a(p0.x-float(2)*p1.x+p2.x,p0.y-float(2)*p1.y+p2.y);
    ImVec2 b(float(2)*p1.x-float(2)*p0.x,float(2)*p1.y-float(2)*p0.y);

    const float A = float(4)*(a.x*a.x+a.y*a.y);
    const float B = float(4)*(a.x*b.x+a.y*b.y);
    const float C = b.x*b.x+b.y*b.y;

    const float Sabc = float(2)*sqrt(A+B+C);
    const float A_2 = sqrt(A);
    const float A_32 = float(2)*A*A_2;
    const float C_2 = float(2)*sqrt(C);
    const float BA = B/A_2;

    return (A_32*Sabc+A_2*B*(Sabc-C_2) + (float(4)*C*A-B*B)*log((float(2)*A_2+BA+Sabc)/(BA+C_2)))/(float(4)*A_32);
    }
    inline static float GetQuadraticBezierCurveLengthAlternative(const ImVec2& p0,const ImVec2& p1,const ImVec2& p2,int num_segments=5) {
    // Approximated by flattening the curve into 'num_segments' intermediate points.
    if (num_segments<=0) num_segments = 5;

    ImVec4 W;
    float length=0,t,u;
    ImVec2 L = p0,tp;
    for (int i = 0; i < num_segments; i++) {
    t = (float)(i+1)/(float)(num_segments+1);u = float(1.0) - t;
    W.x=u*u;W.y=float(2)*u*t;W.z=t*t;W.w=float(0);

    tp.x = W.x*p0.x + W.y*p1.x + W.z*p2.x;
    tp.y = W.x*p0.y + W.y*p1.y + W.z*p2.y;

    length+=GetSegmentLength(L,tp);
    L=tp;
    }
    tp = p2;
    length+=GetSegmentLength(L,tp);

    return length;
    }
    inline static float GetCubicBezierCurveLength(const ImVec2& p0,const ImVec2& p1,const ImVec2& p2,const ImVec2& p3,int num_segments=5) {
    // Approximated by flattening the curve into 'num_segments' intermediate points.
    if (num_segments<=0) num_segments = 5;

    ImVec4 W;
    float length=0,t,u;
    ImVec2 L = p0,tp;
    for (int i = 0; i < num_segments; i++) {
    t = (float)(i+1)/(float)(num_segments+1);u = float(1.0) - t;
    W.x=u*u*u;W.y=float(3)*u*u*t;W.z=float(3)*u*t*t;W.w=t*t*t;

    tp.x = W.x*p0.x + W.y*p1.x + W.z*p2.x + W.w*p3.x;
    tp.y = W.x*p0.y + W.y*p1.y + W.z*p2.y + W.w*p3.y;

    length+=GetSegmentLength(L,tp);
    L=tp;
    }
    tp = p3;
    length+=GetSegmentLength(L,tp);

    return length;
    }

    inline static float GetSquaredDistancePointSegment(const ImVec2& P,const ImVec2& S0,const ImVec2& S1,float eps = float(0.0000001)) {
    // Based on: http://stackoverflow.com/questions/849211/shortest-distance-between-a-point-and-a-line-segment
    const float l2 = (S0.x-S1.x)*(S0.x-S1.x)+(S0.y-S1.y)*(S0.y-S1.y); // segment length squared
    if (l2 < eps) {
    // S1 == S2 case
    return (P.x-S0.x)*(P.x-S0.x)+(P.y-S0.y)*(P.y-S0.y);
    }
    ImVec2 T(S1.x-S0.x,S1.y-S0.y),PS1(P.x-S0.x,P.y-S0.y);
    const float tf = ImVec2Dot(PS1, T)/l2;
    const float minTf = float(1) < tf ? float(1) : tf;
    const float t = float(0) > minTf ? float(0) : minTf;
    // T = Projection on the segment
    T.x = S0.x + T.x*t;
    T.y = S0.y + T.y*t;
    return (P.x-T.x)*(P.x-T.x)+(P.y-T.y)*(P.y-T.y);
    }
    inline static float GetSquaredDistanceToQuadraticBezierCurve(const ImVec2& point,const ImVec2& p0,const ImVec2& p1,const ImVec2& p2,int num_segments=2,ImVec2* pChk0=NULL,ImVec2* pChk1=NULL,float eps = float(0.0000001)) {
    static bool firstRun = true;
    static const int max_num_intermediate_segments = 25; // >0
    static const int total_num_segment = (max_num_intermediate_segments+1)*max_num_intermediate_segments/2;
    static int start_num_segment_index[max_num_intermediate_segments+1];
    static ImVec4 weights[total_num_segment];

    if (num_segments>max_num_intermediate_segments) num_segments = max_num_intermediate_segments;
    else if (num_segments<0) num_segments=0;

    if (firstRun) {
    // static init here
    firstRun = false;
    int cnt = 0;
    for (int seg = 0; seg <= max_num_intermediate_segments; seg++) {
    start_num_segment_index[seg] = cnt;
    for (int i = 1; i <= seg; i++) {
    float t = (float)i/(float)(seg+1);
    float u = float(1.0) - t;
    ImVec4& W = weights[cnt];
    W.x=u*u;
    W.y=float(2)*u*t;
    W.z=t*t;
    W.w=float(0); // optional
    ++cnt;
    }
    }
    }

    float minSquaredDistance=FLT_MAX,tmp; // FLT_MAX is probably in <limits.h>
    ImVec2 L = p0,tp;
    for (int i = start_num_segment_index[num_segments],isz=i+num_segments; i < isz; i++) {
    const ImVec4& W=weights[i];
    tp.x = W.x*p0.x + W.y*p1.x + W.z*p2.x;
    tp.y = W.x*p0.y + W.y*p1.y + W.z*p2.y;

    tmp = GetSquaredDistancePointSegment(point,L,tp,eps);
    if (minSquaredDistance>=tmp) {minSquaredDistance=tmp;if (pChk0) *pChk0=L;if (pChk1) *pChk1=tp;}
    L=tp;
    }
    tp = p2;
    tmp = GetSquaredDistancePointSegment(point,L,tp,eps);
    if (minSquaredDistance>tmp) {minSquaredDistance=tmp;if (pChk0) *pChk0=L;if (pChk1) *pChk1=tp;}

    return minSquaredDistance;
    }
    inline static float GetSquaredDistanceToCubicBezierCurve(const ImVec2& point,const ImVec2& p0,const ImVec2& p1,const ImVec2& p2,const ImVec2& p3,int num_segments=2,ImVec2* pChk0=NULL,ImVec2* pChk1=NULL,float eps = float(0.0000001)) {
    static bool firstRun = true;
    static const int max_num_intermediate_segments = 25; // >0
    static const int total_num_segment = (max_num_intermediate_segments+1)*max_num_intermediate_segments/2;
    static int start_num_segment_index[max_num_intermediate_segments+1];
    static ImVec4 weights[total_num_segment];

    if (num_segments>max_num_intermediate_segments) num_segments = max_num_intermediate_segments;
    else if (num_segments<0) num_segments=0;

    if (firstRun) {
    // static init here
    firstRun = false;
    int cnt = 0;
    for (int seg = 0; seg <= max_num_intermediate_segments; seg++) {
    start_num_segment_index[seg] = cnt;
    for (int i = 1; i <= seg; i++) {
    float t = (float)i/(float)(seg+1);
    float u = float(1.0) - t;
    ImVec4& W = weights[cnt];
    W.x=u*u*u;
    W.y=float(3)*u*u*t;
    W.z=float(3)*u*t*t;
    W.w=t*t*t;
    ++cnt;
    }
    }
    }

    float minSquaredDistance=FLT_MAX,tmp; // FLT_MAX is probably in <limits.h>
    ImVec2 L = p0,tp;
    for (int i = start_num_segment_index[num_segments],isz=i+num_segments; i < isz; i++) {
    const ImVec4& W=weights[i];
    tp.x = W.x*p0.x + W.y*p1.x + W.z*p2.x + W.w*p3.x;
    tp.y = W.x*p0.y + W.y*p1.y + W.z*p2.y + W.w*p3.y;

    tmp = GetSquaredDistancePointSegment(point,L,tp,eps);
    if (minSquaredDistance>tmp) {minSquaredDistance=tmp;if (pChk0) *pChk0=L;if (pChk1) *pChk1=tp;}
    L=tp;
    }
    tp = p3;
    tmp = GetSquaredDistancePointSegment(point,L,tp,eps);
    if (minSquaredDistance>tmp) {minSquaredDistance=tmp;if (pChk0) *pChk0=L;if (pChk1) *pChk1=tp;}

    return minSquaredDistance;
    }


    // Usage of BezierChunk: use only the first 3 constructors and fill an array of BezierChunks, then call Init(...) on it and finally RasterizeGlyphShapeSDF(...)
    struct BezierChunk {
    enum Type {
    TYPE_SEGMENT=0,
    TYPE_QUADRATIC,
    TYPE_CUBIC
    };
    BezierChunk(const ImVec2& _P0,const ImVec2& _P1) : P0(_P0),P1(_P1),num_segments(-1),length(-1),type(TYPE_SEGMENT) {}
    BezierChunk(const ImVec2& _P0,const ImVec2& _C0,const ImVec2& _P1) : P0(_P0),C0(_C0),P1(_P1),num_segments(-1),length(-1),type(TYPE_QUADRATIC) {}
    BezierChunk(const ImVec2& _P0,const ImVec2& _C0,const ImVec2& _C1,const ImVec2& _P1) : P0(_P0),C0(_C0),C1(_C1),P1(_P1),num_segments(-1),length(-1),type(TYPE_CUBIC) {}


    ImVec2 P0,C0,C1,P1;
    mutable int num_segments; // number of internal point to approximate TYPE_QUADRATIC and TYPE_CUBIC curves into straight segments
    mutable float length;
    Type type;
    BezierChunk() : num_segments(-1),length(-1),type(TYPE_SEGMENT){}

    inline static void Init(BezierChunk* chunksIn,int numChunksIn,float scale_flatness_in_pixels=float(0.75)) {
    IM_ASSERT(chunksIn && numChunksIn>0);
    IM_ASSERT(scale_flatness_in_pixels>0);

    for (int i=0;i<numChunksIn;i++) {
    const BezierChunk& c = chunksIn[i];
    if (c.type==TYPE_SEGMENT) continue; //c.length = GetSegmentLength(c.P0,c.P1);
    else if (c.type==TYPE_QUADRATIC) {
    c.length = GetQuadraticBezierCurveLength(c.P0,c.C0,c.P1); // Good approximation, but might fail: c.length = NaN or infinite [Test case: P0=(5.9732384830713272,4.372880969196558) C0=(5.6681537218391895,4.372880969196558) P1=(5.3791260533034801,4.372880969196558)]
    if (!(c.length>=0) || c.length>100000000000)
    c.length = GetQuadraticBezierCurveLengthAlternative(c.P0,c.C0,c.P1); // Fallback
    IM_ASSERT(c.length>=0); //
    }
    else if (c.type==TYPE_CUBIC) c.length = GetCubicBezierCurveLength(c.P0,c.C0,c.C1,c.P1);
    else continue;
    c.num_segments = c.length/scale_flatness_in_pixels; // Num intermediate points used to flatten the curve into line chunks
    IM_ASSERT(c.num_segments>=0);
    //printf("length=%1.4f num_segments=%d\n",c.length,c.num_segments);
    }
    }
    inline static ImVec2 AssignPoint(float unscaledPointX,float unscaledPointY,float scale_x=float(1),float scale_y=float(1),const ImVec2& postScaleOffset=ImVec2(0,0)) {
    return ImVec2(
    unscaledPointX*scale_x-postScaleOffset.x//-0.25f
    ,
    (unscaledPointY*(-scale_y)-postScaleOffset.y)//-0.25f
    );
    }
    static void RasterizeGlyphShapeSDF(
    unsigned char* imageOutPxls,int imageOutW,int imageOutH,int imageOutStride,
    const BezierChunk* chunksIn,int numChunksIn,const int* numChunksPerContourIn,int numChunksPerContourInSize,
    const SdfExtraParams& sdExtraParams,float eps = float(0.0000001)
    )
    {
    IM_ASSERT(imageOutPxls && imageOutW>0 && imageOutH>0 && imageOutStride>0);
    IM_ASSERT(chunksIn && numChunksIn>0 && numChunksPerContourIn && numChunksPerContourInSize>0);
    //IM_ASSERT(scale!=0);

    typedef struct floatPixel_ {
    float d;bool isOutside;
    floatPixel_() : d(-1),isOutside(true) {}
    inline void process(float _d,bool _isOutside) {if (d<0 || _d<d) {d=_d;isOutside=_isOutside;}}
    } floatPixel;


    const float sdCenterValuePer255 = sdExtraParams.sdCenterValue*255.0f;
    const float oneMinussdCenterValuePer255 = 255.0f-sdCenterValuePer255;
    const float sdExtentInPixelsSquared = sdExtraParams.sdExtentInPixels*sdExtraParams.sdExtentInPixels;
    for (int y=0;y<imageOutH;y++) {
    unsigned char* ppxl = NULL; // previous pixel
    for (int x=0;x<imageOutW;x++) {
    floatPixel fpxl;
    ImVec2 P(round(x),round(y)),CHK0,CHK1;bool isOutside;
    int chunkIndex=0;float accurateDistanceSquared(-1.0);
    for (int contourIndex=0;contourIndex<numChunksPerContourInSize;contourIndex++) {
    const int numChunks = numChunksPerContourIn[contourIndex];
    for (int maxChunkIndexOfThisContour=chunkIndex+numChunks;chunkIndex<maxChunkIndexOfThisContour;chunkIndex++) {
    const BezierChunk& c = chunksIn[chunkIndex];

    if (c.type==TYPE_SEGMENT) accurateDistanceSquared = GetSquaredDistancePointSegment(P,c.P0,c.P1,eps);
    else if (c.type==TYPE_QUADRATIC) {
    IM_ASSERT(c.num_segments>=0); // 'chunksIn' must be inited before calling this method
    accurateDistanceSquared = GetSquaredDistanceToQuadraticBezierCurve(P,c.P0,c.C0,c.P1,c.num_segments,&CHK0,&CHK1,eps);
    }
    else if (c.type==TYPE_CUBIC) {
    IM_ASSERT(c.num_segments>=0); // 'chunksIn' must be inited before calling this method
    accurateDistanceSquared = GetSquaredDistanceToCubicBezierCurve(P,c.P0,c.C0,c.C1,c.P1,c.num_segments,&CHK0,&CHK1,eps);
    }
    else {
    IM_ASSERT(true); // Why here?
    continue;
    }
    if (accurateDistanceSquared>=sdExtentInPixelsSquared) continue;
    if (fpxl.d>=0 && accurateDistanceSquared>fpxl.d) continue;
    isOutside = true;
    if (c.type==TYPE_SEGMENT) {
    if (ImVec2Cross(ImVec2(c.P1.x-c.P0.x,c.P1.y-c.P0.y),ImVec2(P.x-c.P0.x,P.y-c.P0.y))>=float(0)) isOutside = !isOutside;
    }
    else {
    if (ImVec2Cross(ImVec2(CHK1.x-CHK0.x,CHK1.y-CHK0.y),ImVec2(P.x-CHK0.x,P.y-CHK0.y))>=float(0)) isOutside = !isOutside;
    }
    fpxl.process(accurateDistanceSquared,isOutside);
    }
    }
    //-------------------------------------------------------------
    // Assign pxl in [0,255] from fpxl based on 'sdExtentInPixels' and 'sdCenterValue'
    unsigned char& pxl = imageOutPxls[imageOutStride*y+x];
    //IM_ASSERT(fpxl.d>=0);
    if (fpxl.d<0) pxl = (!ppxl || (*ppxl<=sdCenterValuePer255)) ? 0 : 255;
    else if (fpxl.d>=sdExtentInPixelsSquared) pxl = fpxl.isOutside ? 0 : 255;
    else
    {
    fpxl.d = sqrt(fpxl.d); // non-squared distance
    if (fpxl.isOutside) {
    fpxl.d = (sdCenterValuePer255*(sdExtraParams.sdExtentInPixels-fpxl.d)/sdExtraParams.sdExtentInPixels); // value in [0,sdCenterValuePer255]
    IM_ASSERT(fpxl.d>=0 && fpxl.d<=sdCenterValuePer255);
    IM_ASSERT(fpxl.d>=0 && fpxl.d<=255);
    pxl = (unsigned char) fpxl.d;
    }
    else {// inside
    fpxl.d = (sdCenterValuePer255+oneMinussdCenterValuePer255*(sdExtraParams.sdExtentInPixels-fpxl.d)/sdExtraParams.sdExtentInPixels); // value in [sdCenterValuePer255,sdCenterValuePer255+oneMinussdCenterValuePer255]
    IM_ASSERT(fpxl.d>=sdCenterValuePer255 && fpxl.d<=sdCenterValuePer255+oneMinussdCenterValuePer255);
    IM_ASSERT(fpxl.d>=0 && fpxl.d<=255);
    pxl = (unsigned char) fpxl.d;
    }
    }
    ppxl = &pxl;
    //---------------------------------------------------------------
    }
    }
    }

    };


    typedef ImVector<BezierChunk> BezierChunkVector;
    typedef ImVector<int> IntVector;


    #ifdef __STB_INCLUDE_STB_TRUETYPE_H__
    STBTT_DEF bool stbtt_RasterizeSD(stbtt__bitmap *result,float flatness_in_pixels, stbtt_vertex *vertices, int num_vertices, float scale_x,float scale_y, int x_off, int y_off,const SdfExtraParams& sdExtraParams,void *userdata=NULL)
    {
    const ImVec2 postScaleOffset(x_off,y_off);

    BezierChunkVector chunks;IntVector numChunksPerContour;

    static const float eps = float(0.0000001);

    // Filling chunks and numChunksPerContour:
    if (num_vertices<=0 || !vertices || scale_x==0 || scale_y==0) {
    chunks.resize(0);
    numChunksPerContour.resize(0);
    return false;
    }
    chunks.reserve(num_vertices);
    numChunksPerContour.reserve(4);
    int cnt = 0;BezierChunk c;BezierChunk* ppc=NULL;ImVec2 tmpP0;
    for (int i=0;i<num_vertices;i++) {
    const stbtt_vertex& v= vertices[i];
    if (v.type==STBTT_vmove) {
    if (cnt>0) {
    numChunksPerContour.push_back(cnt);
    cnt=0;ppc=NULL;
    }
    tmpP0 = BezierChunk::AssignPoint(v.x,v.y,scale_x,scale_y,postScaleOffset);
    continue;
    }
    else {
    // Assign P1 and P0
    c.P1 = BezierChunk::AssignPoint(v.x,v.y,scale_x,scale_y,postScaleOffset);
    if (ppc) c.P0 = ppc->P1;
    else {
    c.P0 = tmpP0;
    IM_ASSERT(cnt==0);
    IM_ASSERT(ppc==NULL);
    }
    if (AreFuzzyEqual(c.P0,c.P1,eps)) continue; // Checks if P0==P1 and skips the chunk
    }
    c.type = (v.type==STBTT_vcurve) ? BezierChunk::TYPE_QUADRATIC : BezierChunk::TYPE_SEGMENT;
    if (v.type==STBTT_vcurve) {
    c.C0 = BezierChunk::AssignPoint(v.cx,v.cy,scale_x,scale_y,postScaleOffset);
    if (AreFuzzyEqual(c.P0,c.P1,eps)) continue; // Checks if P0==P1 and skips the chunk
    if (AreFuzzyEqual(c.P0,c.C0,eps) || AreFuzzyEqual(c.C0,c.P1,eps)) c.type = BezierChunk::TYPE_SEGMENT; // Degrade to STBTT_vline
    }
    //-------------------------------------------------
    //-------------------------------------------------
    chunks.push_back(c);
    ppc = &chunks[chunks.size()-1];
    ++cnt;

    IM_ASSERT(c.P0.x==ppc->P0.x && c.P0.y==ppc->P0.y && c.P1.x==ppc->P1.x && c.P1.y==ppc->P1.y && c.type==ppc->type);
    }
    if (cnt>0) {
    numChunksPerContour.push_back(cnt);
    cnt=0;ppc=NULL;
    }

    IM_ASSERT(chunks.size()>0 && numChunksPerContour.size()>0);
    BezierChunk::Init(&chunks[0],chunks.size(),flatness_in_pixels);

    BezierChunk::RasterizeGlyphShapeSDF(result->pixels,result->w,result->h,result->stride,
    &chunks[0],chunks.size(),&numChunksPerContour[0],numChunksPerContour.size(),
    sdExtraParams,eps
    );

    return true;
    }
    #endif //__STB_INCLUDE_STB_TRUETYPE_H__

    } // namespace ImGuiSdfGenerator


    #ifdef __STB_INCLUDE_STB_TRUETYPE_H__
    extern "C" {
    // These methods are mostly juat adapted from the ones inside stb_truetype.h
    STBTT_DEF void stbtt_RasterizeSD(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off,const ImGuiSdfGenerator::SdfExtraParams& sdExtraParams, void *userdata)
    {
    IM_ASSERT(shift_x==0 && shift_y==0); // We don't use them
    ImGuiSdfGenerator::stbtt_RasterizeSD(result,flatness_in_pixels,vertices,num_verts,scale_x,scale_y,x_off,y_off,sdExtraParams,userdata);
    }
    STBTT_DEF void stbtt_MakeSDGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph,const ImGuiSdfGenerator::SdfExtraParams& sdExtraParams,void* userdata)
    {
    int ix0,iy0;
    stbtt_vertex *vertices;
    int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);

    stbtt__bitmap gbm;

    stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0);
    gbm.pixels = output;
    gbm.w = out_w;
    gbm.h = out_h;
    gbm.stride = out_stride;

    if (gbm.w && gbm.h) stbtt_RasterizeSD(&gbm, sdExtraParams.sdFlatnessInPixels, vertices, num_verts, scale_x, scale_y,shift_x,shift_y, ix0,iy0, sdExtraParams,info->userdata);

    STBTT_free(vertices, info->userdata);
    }
    // rects array must be big enough to accommodate all characters in the given ranges
    STBTT_DEF int stbtt_PackSDFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects,const ImGuiSdfGenerator::SdfExtraParams& sdExtraParams)
    {
    int i,j,k, return_value = 1;

    // save current values
    int old_h_over = spc->h_oversample;
    int old_v_over = spc->v_oversample;

    k = 0;
    for (i=0; i < num_ranges; ++i) {
    float fh = ranges[i].font_size;
    float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh);
    float recip_h,recip_v,sub_x,sub_y;
    spc->h_oversample = ranges[i].h_oversample;
    spc->v_oversample = ranges[i].v_oversample;
    recip_h = 1.0f / spc->h_oversample;
    recip_v = 1.0f / spc->v_oversample;
    sub_x = stbtt__oversample_shift(spc->h_oversample);
    sub_y = stbtt__oversample_shift(spc->v_oversample);

    for (j=0; j < ranges[i].num_chars; ++j) {
    stbrp_rect *r = &rects[k];
    if (r->was_packed) {
    stbtt_packedchar *bc = &ranges[i].chardata_for_range[j];
    int advance, lsb, x0,y0,x1,y1;
    int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j];
    int glyph = stbtt_FindGlyphIndex(info, codepoint);
    stbrp_coord pad = (stbrp_coord) spc->padding;

    // pad on left and top
    r->x += pad;
    r->y += pad;
    r->w -= pad;
    r->h -= pad;
    stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb);
    stbtt_GetGlyphBitmapBox(info, glyph,
    scale * spc->h_oversample,
    scale * spc->v_oversample,
    &x0,&y0,&x1,&y1);

    // new code -------------------------------------------------------------
    stbtt_MakeSDGlyphBitmapSubpixel(info,
    spc->pixels + r->x + r->y*spc->stride_in_bytes,
    r->w - spc->h_oversample+1,
    r->h - spc->v_oversample+1,
    spc->stride_in_bytes,
    scale * spc->h_oversample,
    scale * spc->v_oversample,
    0,0,
    glyph,
    sdExtraParams,
    NULL
    );
    //-------------------------------------------------------------------------

    if (spc->h_oversample > 1)
    stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes,
    r->w, r->h, spc->stride_in_bytes,
    spc->h_oversample);

    if (spc->v_oversample > 1)
    stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes,
    r->w, r->h, spc->stride_in_bytes,
    spc->v_oversample);

    bc->x0 = (stbtt_int16) r->x;
    bc->y0 = (stbtt_int16) r->y;
    bc->x1 = (stbtt_int16) (r->x + r->w);
    bc->y1 = (stbtt_int16) (r->y + r->h);
    bc->xadvance = scale * advance;
    bc->xoff = (float) x0 * recip_h + sub_x;
    bc->yoff = (float) y0 * recip_v + sub_y;
    bc->xoff2 = (x0 + r->w) * recip_h + sub_x;
    bc->yoff2 = (y0 + r->h) * recip_v + sub_y;
    } else {
    return_value = 0; // if any fail, report failure
    }

    ++k;
    }
    }

    // restore original values
    spc->h_oversample = old_h_over;
    spc->v_oversample = old_v_over;

    return return_value;
    }
    }
    #endif //__STB_INCLUDE_STB_TRUETYPE_H__


    // This is ImFontAtlas::Build() with just one line removed and two more added (but now the atlas is an argument...)
    bool BuildSignedDistanceFontAtlas(ImFontAtlas* atlas)
    {
    IM_ASSERT(atlas->ConfigData.Size > 0);

    atlas->TexID = NULL;
    atlas->TexWidth = atlas->TexHeight = 0;
    atlas->TexUvWhitePixel = ImVec2(0, 0);
    atlas->ClearTexData();

    struct ImFontTempBuildData
    {
    stbtt_fontinfo FontInfo;
    stbrp_rect* Rects;
    stbtt_pack_range* Ranges;
    int RangesCount;
    };
    ImFontTempBuildData* tmp_array = (ImFontTempBuildData*)ImGui::MemAlloc((size_t)atlas->ConfigData.Size * sizeof(ImFontTempBuildData));

    // Initialize font information early (so we can error without any cleanup) + count glyphs
    int total_glyph_count = 0;
    int total_glyph_range_count = 0;
    for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++)
    {
    ImFontConfig& cfg = atlas->ConfigData[input_i];
    ImFontTempBuildData& tmp = tmp_array[input_i];

    IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == atlas));
    const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)cfg.FontData, cfg.FontNo);
    IM_ASSERT(font_offset >= 0);
    if (!stbtt_InitFont(&tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset))
    return false;

    // Count glyphs
    if (!cfg.GlyphRanges)
    cfg.GlyphRanges = atlas->GetGlyphRangesDefault();
    for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2)
    {
    total_glyph_count += (in_range[1] - in_range[0]) + 1;
    total_glyph_range_count++;
    }
    }

    // Start packing. We need a known width for the skyline algorithm. Using a cheap heuristic here to decide of width. User can override TexDesiredWidth if they wish.
    // After packing is done, width shouldn't matter much, but some API/GPU have texture size limitations and increasing width can decrease height.
    atlas->TexWidth = (atlas->TexDesiredWidth > 0) ? atlas->TexDesiredWidth : (total_glyph_count > 4000) ? 4096 : (total_glyph_count > 2000) ? 2048 : (total_glyph_count > 1000) ? 1024 : 512;
    atlas->TexHeight = 0;
    const int max_tex_height = 1024*32;
    stbtt_pack_context spc;
    stbtt_PackBegin(&spc, NULL, atlas->TexWidth, max_tex_height, 0, 1, NULL);

    // Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values).
    ImVector<stbrp_rect> extra_rects;
    atlas->RenderCustomTexData(0, &extra_rects);
    stbtt_PackSetOversampling(&spc, 1, 1);
    stbrp_pack_rects((stbrp_context*)spc.pack_info, &extra_rects[0], extra_rects.Size);
    for (int i = 0; i < extra_rects.Size; i++)
    if (extra_rects[i].was_packed)
    atlas->TexHeight = ImMax(atlas->TexHeight, extra_rects[i].y + extra_rects[i].h);

    // Allocate packing character data and flag packed characters buffer as non-packed (x0=y0=x1=y1=0)
    int buf_packedchars_n = 0, buf_rects_n = 0, buf_ranges_n = 0;
    stbtt_packedchar* buf_packedchars = (stbtt_packedchar*)ImGui::MemAlloc(total_glyph_count * sizeof(stbtt_packedchar));
    stbrp_rect* buf_rects = (stbrp_rect*)ImGui::MemAlloc(total_glyph_count * sizeof(stbrp_rect));
    stbtt_pack_range* buf_ranges = (stbtt_pack_range*)ImGui::MemAlloc(total_glyph_range_count * sizeof(stbtt_pack_range));
    memset(buf_packedchars, 0, total_glyph_count * sizeof(stbtt_packedchar));
    memset(buf_rects, 0, total_glyph_count * sizeof(stbrp_rect)); // Unnecessary but let's clear this for the sake of sanity.
    memset(buf_ranges, 0, total_glyph_range_count * sizeof(stbtt_pack_range));

    // First font pass: pack all glyphs (no rendering at this point, we are working with rectangles in an infinitely tall texture at this point)
    for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++)
    {
    ImFontConfig& cfg = atlas->ConfigData[input_i];
    ImFontTempBuildData& tmp = tmp_array[input_i];

    // Setup ranges
    int glyph_count = 0;
    int glyph_ranges_count = 0;
    for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2)
    {
    glyph_count += (in_range[1] - in_range[0]) + 1;
    glyph_ranges_count++;
    }
    tmp.Ranges = buf_ranges + buf_ranges_n;
    tmp.RangesCount = glyph_ranges_count;
    buf_ranges_n += glyph_ranges_count;
    for (int i = 0; i < glyph_ranges_count; i++)
    {
    const ImWchar* in_range = &cfg.GlyphRanges[i * 2];
    stbtt_pack_range& range = tmp.Ranges[i];
    range.font_size = cfg.SizePixels;
    range.first_unicode_codepoint_in_range = in_range[0];
    range.num_chars = (in_range[1] - in_range[0]) + 1;
    range.chardata_for_range = buf_packedchars + buf_packedchars_n;
    buf_packedchars_n += range.num_chars;
    }

    // Pack
    tmp.Rects = buf_rects + buf_rects_n;
    buf_rects_n += glyph_count;
    stbtt_PackSetOversampling(&spc, cfg.OversampleH, cfg.OversampleV);
    int n = stbtt_PackFontRangesGatherRects(&spc, &tmp.FontInfo, tmp.Ranges, tmp.RangesCount, tmp.Rects);
    stbrp_pack_rects((stbrp_context*)spc.pack_info, tmp.Rects, n);

    // Extend texture height
    for (int i = 0; i < n; i++)
    if (tmp.Rects[i].was_packed)
    atlas->TexHeight = ImMax(atlas->TexHeight, tmp.Rects[i].y + tmp.Rects[i].h);
    }
    IM_ASSERT(buf_rects_n == total_glyph_count);
    IM_ASSERT(buf_packedchars_n == total_glyph_count);
    IM_ASSERT(buf_ranges_n == total_glyph_range_count);

    // Create texture
    atlas->TexHeight = ImUpperPowerOfTwo(atlas->TexHeight);
    atlas->TexPixelsAlpha8 = (unsigned char*)ImGui::MemAlloc(atlas->TexWidth * atlas->TexHeight);
    memset(atlas->TexPixelsAlpha8, 0, atlas->TexWidth * atlas->TexHeight);
    spc.pixels = atlas->TexPixelsAlpha8;
    spc.height = atlas->TexHeight;

    // Second pass: render characters
    for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++)
    {
    ImFontConfig& cfg = atlas->ConfigData[input_i];
    ImFontTempBuildData& tmp = tmp_array[input_i];
    stbtt_PackSetOversampling(&spc, cfg.OversampleH, cfg.OversampleV);
    //------------------------------------------------------------------
    // old code (1 line)
    //stbtt_PackFontRangesRenderIntoRects(&spc, &tmp.FontInfo, tmp.Ranges, tmp.RangesCount, tmp.Rects);

    // new code
    ImGuiSdfGenerator::SdfExtraParams sdExtraParams(&cfg);
    # ifdef __STB_INCLUDE_STB_TRUETYPE_H__
    stbtt_PackSDFontRangesRenderIntoRects(&spc, &tmp.FontInfo, tmp.Ranges, tmp.RangesCount, tmp.Rects, sdExtraParams);
    # else // __STB_INCLUDE_STB_TRUETYPE_H__
    # error only stb_truetype is supported ATM
    # endif //__STB_INCLUDE_STB_TRUETYPE_H__
    //------------------------------------------------------------------
    tmp.Rects = NULL;
    }

    // End packing
    stbtt_PackEnd(&spc);
    ImGui::MemFree(buf_rects);
    buf_rects = NULL;

    // Third pass: setup ImFont and glyphs for runtime
    for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++)
    {
    ImFontConfig& cfg = atlas->ConfigData[input_i];
    ImFontTempBuildData& tmp = tmp_array[input_i];
    ImFont* dst_font = cfg.DstFont;

    float font_scale = stbtt_ScaleForPixelHeight(&tmp.FontInfo, cfg.SizePixels);
    int unscaled_ascent, unscaled_descent, unscaled_line_gap;
    stbtt_GetFontVMetrics(&tmp.FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap);

    float ascent = unscaled_ascent * font_scale;
    float descent = unscaled_descent * font_scale;
    if (!cfg.MergeMode)
    {
    dst_font->ContainerAtlas = atlas;
    dst_font->ConfigData = &cfg;
    dst_font->ConfigDataCount = 0;
    dst_font->FontSize = cfg.SizePixels;
    dst_font->Ascent = ascent;
    dst_font->Descent = descent;
    dst_font->Glyphs.resize(0);
    }
    dst_font->ConfigDataCount++;
    float off_y = (cfg.MergeMode && cfg.MergeGlyphCenterV) ? (ascent - dst_font->Ascent) * 0.5f : 0.0f;

    dst_font->FallbackGlyph = NULL; // Always clear fallback so FindGlyph can return NULL. It will be set again in BuildLookupTable()
    for (int i = 0; i < tmp.RangesCount; i++)
    {
    stbtt_pack_range& range = tmp.Ranges[i];
    for (int char_idx = 0; char_idx < range.num_chars; char_idx += 1)
    {
    const stbtt_packedchar& pc = range.chardata_for_range[char_idx];
    if (!pc.x0 && !pc.x1 && !pc.y0 && !pc.y1)
    continue;

    const int codepoint = range.first_unicode_codepoint_in_range + char_idx;
    if (cfg.MergeMode && dst_font->FindGlyph((unsigned short)codepoint))
    continue;

    stbtt_aligned_quad q;
    float dummy_x = 0.0f, dummy_y = 0.0f;
    stbtt_GetPackedQuad(range.chardata_for_range, atlas->TexWidth, atlas->TexHeight, char_idx, &dummy_x, &dummy_y, &q, 0);

    dst_font->Glyphs.resize(dst_font->Glyphs.Size + 1);
    ImFont::Glyph& glyph = dst_font->Glyphs.back();
    glyph.Codepoint = (ImWchar)codepoint;
    glyph.X0 = q.x0; glyph.Y0 = q.y0; glyph.X1 = q.x1; glyph.Y1 = q.y1;
    glyph.U0 = q.s0; glyph.V0 = q.t0; glyph.U1 = q.s1; glyph.V1 = q.t1;
    glyph.Y0 += (float)(int)(dst_font->Ascent + off_y + 0.5f);
    glyph.Y1 += (float)(int)(dst_font->Ascent + off_y + 0.5f);
    glyph.XAdvance = (pc.xadvance + cfg.GlyphExtraSpacing.x); // Bake spacing into XAdvance
    if (cfg.PixelSnapH)
    glyph.XAdvance = (float)(int)(glyph.XAdvance + 0.5f);
    }
    }
    cfg.DstFont->BuildLookupTable();
    }

    // Cleanup temporaries
    ImGui::MemFree(buf_packedchars);
    ImGui::MemFree(buf_ranges);
    ImGui::MemFree(tmp_array);

    // Render into our custom data block
    atlas->RenderCustomTexData(1, &extra_rects);

    return true;
    }

    111 changes: 111 additions & 0 deletions imguiSdfGenerator.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,111 @@
    #ifndef IMGUISDFGENERATOR_H_
    #define IMGUISDFGENERATOR_H_
    #pragma once

    // VERSION 0.1 - Tested on Dear ImGui Version 1.50 WIP on Linux
    /*
    This is the worst signed distance font generator that can be found on the web for sure! (*)
    It's released just as a mockup-placeholder so that we have a working example that can be
    gradually improved.
    (*) The quality is so bad that if you use sdf shaders on normal non-sdf fonts it
    usually looks better.
    */

    // BASIC USAGE:
    /*
    // We'll use examples/opengl3_example as a reference.
    // In imgui_impl_glfw.cpp (or similiar):
    #define IMIMPL_BUILD_SDF // Twickable
    #ifdef IMIMPL_BUILD_SDF
    #include <imguiSdfGenerator.h>
    #endif //IMIMPL_BUILD_SDF
    bool ImGui_ImplGlfw_CreateFontsTexture()
    {
    // Build texture atlas
    ImGuiIO& io = ImGui::GetIO();
    unsigned char* pixels;
    int width, height;
    # ifdef IMIMPL_BUILD_SDF
    BuildSignedDistanceFontAtlas(io.Fonts); // New
    # endif //IMIMPL_BUILD_SDF
    io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits for OpenGL3 demo because it is more likely to be compatible with user's existing shader.
    ...
    }
    // We need to replace the fragment shader code with one of these 2:
    #ifndef IMIMPL_BUILD_SDF
    const GLchar* fragment_shader[] = {
    "#version 100\n"
    "precision mediump float;\n"
    "uniform lowp sampler2D Texture;\n"
    "varying vec2 Frag_UV;\n"
    "varying vec4 Frag_Color;\n"
    "void main()\n"
    "{\n"
    " gl_FragColor = Frag_Color * texture2D( Texture, Frag_UV.st);\n"
    "}\n"
    };
    #else // IMIMPL_BUILD_SDF
    //#define USE_OUTLINE_SHADER // Tweakable
    # ifndef USE_OUTLINE_SHADER
    const char* fragment_shader[] = {
    "#version 100\n"
    "#extension GL_OES_standard_derivatives : enable\n" // fwidth
    "precision mediump float;\n"
    "uniform sampler2D Texture;\n"
    "varying vec2 Frag_UV;\n"
    "varying vec4 Frag_Color;\n"
    "void main(void) {\n"
    "vec4 texColor = texture2D(Texture, Frag_UV.st);\n"
    "float stepThreshold = 0.5;\n" // 0.5 -> threshold for glyph border
    "float width = fwidth(texColor.a);\n"
    "float alpha = smoothstep(stepThreshold - width, stepThreshold + width, texColor.a);\n"
    "gl_FragColor = vec4(Frag_Color.rgb*texColor.rgb,Frag_Color.a*alpha);\n"
    "}\n"
    };
    #else //USE_OUTLINE_SHADER
    const char* fragment_shader[] = {
    "#version 100\n"
    "#extension GL_OES_standard_derivatives : enable\n" // fwidth
    "precision mediump float;\n"
    "uniform sampler2D Texture;\n"
    "varying vec2 Frag_UV;\n"
    "varying vec4 Frag_Color;\n"
    "void main(void) {\n"
    "vec4 texColor = texture2D(Texture, Frag_UV.st);\n"
    "float width = fwidth(texColor.a);\n"
    "\n"
    "float outlineThreshold = 0.5;\n" // 0.5 -> threshold for glyph border
    "float alphaThreshold = 0.425;\n" // 0.2 -> threshold for glyph outline
    "float outlineDarkeningFactor = 0.3;\n" // 0.3
    "\n"
    "float inside = smoothstep(outlineThreshold - width, outlineThreshold + width, texColor.a) ;\n"
    "float glow = smoothstep (0.0 , 20.0 , texColor.a ) ;\n" // This can just be set to 1.0...
    "vec3 insidecolor = Frag_Color.rgb*texColor.rgb;\n"
    "vec3 outlinecolor = insidecolor.rgb*outlineDarkeningFactor;\n"
    "vec3 fragcolor = mix ( glow * outlinecolor , insidecolor , inside ) ;\n"
    "float alpha = smoothstep(alphaThreshold - width, alphaThreshold + width, texColor.a);\n"
    "\n"
    "gl_FragColor = vec4(fragcolor,Frag_Color.a*alpha);\n"
    "}\n"
    };
    #endif //USE_OUTLINE_SHADER
    #endif // IMIMPL_BUILD_SDF
    // Now it should work
    */

    // BEST SETTINGS I'VE FOUND SO FAR:
    /*
    ImGuiIO& io = ImGui::GetIO();
    ImFontConfig cfg;cfg.OversampleH=4;cfg.OversampleV=4;//cfg.PixelSnapH=true;//cfg.GlyphExtraSpacing=ImVec2(2,2);
    io.Fonts->AddFontFromFileTTF("/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-B.ttf", 13.0f,&cfg);
    */

    bool BuildSignedDistanceFontAtlas(struct ImFontAtlas* atlas);

    #endif //IMGUISDFGENERATOR_H_