freetype-commit
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[freetype2-demos] sdf 3b0423b 5/9: [ftsdf] Draw SDF to display.


From: Werner LEMBERG
Subject: [freetype2-demos] sdf 3b0423b 5/9: [ftsdf] Draw SDF to display.
Date: Tue, 22 Dec 2020 12:07:36 -0500 (EST)

branch: sdf
commit 3b0423bcefebdaeefc320e03f4830103884ee2a4
Author: Anuj Verma <anujv@iitbhilai.ac.in>
Commit: Werner Lemberg <wl@gnu.org>

    [ftsdf] Draw SDF to display.
    
    * src/ftsdf.c (clamp, smoothstep, draw): New function.
---
 ChangeLog   |   6 ++
 src/ftsdf.c | 243 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 249 insertions(+)

diff --git a/ChangeLog b/ChangeLog
index 5d1cd55..5cdffbe 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
 2020-08-22  Anuj Verma  <anujv@iitbhilai.ac.in>
 
+       [ftsdf] Draw SDF to display.
+
+       * src/ftsdf.c (clamp, smoothstep, draw): New function.
+
+2020-08-22  Anuj Verma  <anujv@iitbhilai.ac.in>
+
        [ftsdf] Handle window events.
 
        * src/ftsdf.c (Process_Event): New function.
diff --git a/src/ftsdf.c b/src/ftsdf.c
index d428476..03e64fc 100644
--- a/src/ftsdf.c
+++ b/src/ftsdf.c
@@ -391,4 +391,247 @@
     return ret;
   }
 
+
+  /* Clamp value `x` between `lower_limit` and `upper_limit`. */
+  float
+  clamp( float  x,
+         float  lower_limit,
+         float  upper_limit )
+  {
+    if ( x < lower_limit )
+      x = lower_limit;
+    if ( x > upper_limit )
+      x = upper_limit;
+
+    return x;
+  }
+
+
+  /* Do smooth interpolation of value `x` between `edge0` and `edge1` */
+  /* using a polynomial function.                                     */
+  /*                                                                  */
+  /* This implementation is taken from Wikipedia.                     */
+  /*                                                                  */
+  /*   https://en.wikipedia.org/wiki/Smoothstep                       */
+  float
+  smoothstep( float  edge0,
+              float  edge1,
+              float  x )
+  {
+    /* scale, bias and saturate x to 0..1 range */
+    x = clamp( ( x - edge0 ) / ( edge1 - edge0 ), 0.0, 1.0 );
+
+    /* evaluate polynomial */
+    return x * x * ( 3 - 2 * x );
+  }
+
+
+  /* Draw an SDF image to the display. */
+  static FT_Error
+  draw( void )
+  {
+    FT_Bitmap*  bitmap = &status.face->glyph->bitmap;
+
+    Box  draw_region;
+    Box  sample_region;
+
+    Vec2       center;
+    FT_Short*  buffer;
+
+
+    if ( !bitmap || !bitmap->buffer )
+      return FT_Err_Invalid_Argument;
+
+    /* compute center of display */
+    center.x = display->bitmap->width / 2;
+    center.y = display->bitmap->rows  / 2;
+
+    /* compute draw region around `center` */
+    draw_region.xMin = center.x - ( bitmap->width * status.scale) / 2;
+    draw_region.xMax = center.x + ( bitmap->width * status.scale) / 2;
+    draw_region.yMin = center.y - ( bitmap->rows  * status.scale) / 2;
+    draw_region.yMax = center.y + ( bitmap->rows  * status.scale) / 2;
+
+    /* add position offset so that we can move the image */
+    draw_region.xMin += status.x_offset;
+    draw_region.xMax += status.x_offset;
+    draw_region.yMin += status.y_offset;
+    draw_region.yMax += status.y_offset;
+
+    /* Sample region is the region of the bitmap that gets */
+    /* sampled to the display buffer.                      */
+    sample_region.xMin = 0;
+    sample_region.xMax = bitmap->width * status.scale;
+    sample_region.yMin = 0;
+    sample_region.yMax = bitmap->rows * status.scale;
+
+    /* Adjust sample region in case our draw region */
+    /* goes outside of the display.                 */
+
+    /* adjust in -y */
+    if ( draw_region.yMin < 0 )
+    {
+      sample_region.yMax -= draw_region.yMin;
+      draw_region.yMin    = 0;
+    }
+
+    /* adjust in +y */
+    if ( draw_region.yMax > display->bitmap->rows )
+    {
+      sample_region.yMin += draw_region.yMax - display->bitmap->rows;
+      draw_region.yMax    = display->bitmap->rows;
+    }
+
+    /* adjust in -x */
+    if ( draw_region.xMin < 0 )
+    {
+      sample_region.xMin -= draw_region.xMin;
+      draw_region.xMin    = 0;
+    }
+
+    /* adjust in +x */
+    if ( draw_region.xMax > display->bitmap->width )
+    {
+      sample_region.xMax += draw_region.xMax - display->bitmap->width;
+      draw_region.xMax    = display->bitmap->width;
+    }
+
+    buffer = (FT_Short*)bitmap->buffer;
+
+    /* Finally loop over all pixels inside the draw region        */
+    /* and copy pixels from the sample region to the draw region. */
+    for ( FT_Int  j = draw_region.yMax - 1, y = sample_region.yMin;
+          j >= draw_region.yMin;
+          j--, y++ )
+    {
+      for ( FT_Int  i = draw_region.xMin, x = sample_region.xMin;
+            i < draw_region.xMax;
+            i++, x++ )
+      {
+        FT_UInt  display_index = j * display->bitmap->width + i;
+        float    min_dist;
+
+
+        if ( status.nearest_filtering )
+        {
+          FT_UInt   bitmap_index = ( y / status.scale ) * bitmap->width +
+                                     x / status.scale;
+          FT_Short  pixel_value  = buffer[bitmap_index];
+
+
+          /* If nearest filtering then simply take the value of the */
+          /* nearest sampling pixel.                                */
+          min_dist = (float)pixel_value / 1024.0f;
+        }
+        else
+        {
+          /* for simplicity use floats */
+          float  bi_x;
+          float  bi_y;
+
+          float  nbi_x;
+          float  nbi_y;
+
+          int    indc[4]; /* [0,0] [0,1] [1,0] [1,1] */
+          float  dist[4];
+
+          float  m1, m2;
+
+          int width = (int)bitmap->width;
+          int rows  = (int)bitmap->rows;
+
+
+          /* If bilinear filtering then compute the bilinear      */
+          /* interpolation of the current draw pixel using        */
+          /* the nearby sampling pixel values.                    */
+          /*                                                      */
+          /* Again the concept is taken from Wikipedia.           */
+          /*                                                      */
+          /* https://en.wikipedia.org/wiki/Bilinear_interpolation */
+
+          bi_x = (float)x / (float)status.scale;
+          bi_y = (float)y / (float)status.scale;
+
+          nbi_x = bi_x - (int)bi_x;
+          nbi_y = bi_y - (int)bi_y;
+
+          indc[0] = (int)bi_y * width + (int)bi_x;
+          indc[1] = ( (int)bi_y + 1 ) * width + (int)bi_x;
+          indc[2] = (int)bi_y * width + (int)bi_x + 1;
+          indc[3] = ( (int)bi_y + 1 ) * width + (int)bi_x + 1;
+
+          dist[0] = (float)buffer[indc[0]] / 1024.0f;
+
+          if ( indc[1] >= width * rows )
+            dist[1] = -status.spread;
+          else
+            dist[1] = (float)buffer[indc[1]] / 1024.0f;
+
+          if ( indc[2] >= width * rows )
+            dist[2] = -status.spread;
+          else
+            dist[2] = (float)buffer[indc[2]] / 1024.0f;
+
+          if ( indc[3] >= width * rows )
+            dist[3] = -status.spread;
+          else
+            dist[3] = (float)buffer[indc[3]] / 1024.0f;
+
+          m1 = dist[0] * ( 1.0f - nbi_y ) + dist[1] * nbi_y;
+          m2 = dist[2] * ( 1.0f - nbi_y ) + dist[3] * nbi_y;
+
+          /* This is our final display after bilinear interpolation. */
+          min_dist = ( 1.0f - nbi_x ) * m1 + nbi_x * m2;
+        }
+
+        if ( status.reconstruct )
+        {
+          float  alpha;
+
+
+          /* If we are reconstructing then discard all values outside of  */
+          /* the range defined by `status.width` and use `status.edge' to */
+          /* make smooth anti-aliased edges.                              */
+
+          /* This is similar to an OpenGL implementation to draw SDF. */
+          alpha  = 1.0f - smoothstep( status.width,
+                                      status.width + status.edge,
+                                      -min_dist );
+          alpha *= 255;
+
+          /* finally copy the target value to the display buffer */
+          display_index *= 3;
+          display->bitmap->buffer[display_index + 0] = (unsigned char)alpha;
+          display->bitmap->buffer[display_index + 1] = (unsigned char)alpha;
+          display->bitmap->buffer[display_index + 2] = (unsigned char)alpha;
+        }
+        else
+        {
+          float final_dist = min_dist;
+
+
+          /* If not reconstructing then normalize the values between */
+          /* [0, 255] and copy to the display buffer.                */
+
+          /* normalize using `status.spread` */
+          final_dist  = final_dist < 0 ? -final_dist : final_dist;
+          final_dist /= (float)status.spread;
+
+          /* invert the values */
+          final_dist  = 1.0f - final_dist;
+          final_dist *= 255;
+
+          /* finally copy the target value to the display buffer */
+          display_index *= 3;
+          display->bitmap->buffer[display_index + 0] = (unsigned 
char)final_dist;
+          display->bitmap->buffer[display_index + 1] = (unsigned 
char)final_dist;
+          display->bitmap->buffer[display_index + 2] = (unsigned 
char)final_dist;
+        }
+      }
+    }
+
+    return FT_Err_Ok;
+  }
+
+
 /* END */



reply via email to

[Prev in Thread] Current Thread [Next in Thread]