freetype-commit
[Top][All Lists]
Advanced

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

[freetype2-demos] GSoC-2020-anuj b2d2eba 5/5: [ftsdf] Added function to


From: Anuj Verma
Subject: [freetype2-demos] GSoC-2020-anuj b2d2eba 5/5: [ftsdf] Added function to draw SDF to the display.
Date: Sat, 22 Aug 2020 01:37:49 -0400 (EDT)

branch: GSoC-2020-anuj
commit b2d2eba62aa9dd5461f5d7415a068a668a5b5b3f
Author: Anuj Verma <anujv@iitbhilai.ac.in>
Commit: Anuj Verma <anujv@iitbhilai.ac.in>

    [ftsdf] Added function to draw SDF to the display.
    
    * src/ftsdf.c (draw): The function draws the SDF data to the display 
buffer. It also
      takes care of filtering and certain property values.
    
    * src/ftsdf.c (clamp, smoothstep): A few helper functions for `draw'.
---
 src/ftsdf.c | 221 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 221 insertions(+)

diff --git a/src/ftsdf.c b/src/ftsdf.c
index 304a9f9..8cd43c3 100644
--- a/src/ftsdf.c
+++ b/src/ftsdf.c
@@ -340,4 +340,225 @@
     return ret;
   }
 
+  float
+  clamp(float x, float lower_limit, float upper_limit) {
+    /* The function clamps the value `x' between `lowerlimit' */
+    /* and `upperlimit'.                                      */
+    if (x < lower_limit)
+      x = lower_limit;
+    if (x > upper_limit)
+      x = upper_limit;
+    return x;
+  }
+
+  float
+  smoothstep(float edge0, float edge1, float x) {
+    /* The function interpolate the value of `x' between */
+    /* `edge0' and `edge1' smoothly using a polynomial   */
+    /* function.                                         */
+
+    /* This implementation is taken from Wikipedia.      */
+    /* https://en.wikipedia.org/wiki/Smoothstep          */
+
+    // 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);
+  }
+
+  static FT_Error
+  draw()
+  {
+    /* The function draws a SDF image to the display. */
+
+    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 the center of the display */
+    center.x = display->bitmap->width / 2;
+    center.y = display->bitmap->rows  / 2;
+
+    /* compute the draw reigon around the `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 the 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 will */
+    /* be 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 the sample region in case our draw region */
+    /* goes outiside the dislpay.                       */
+
+    /* 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 the pixels inside the draw region and copy the */
+    /* pixel 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;
+
+          /* 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 * bitmap->width + (int)bi_x;
+          indc[1] = ( (int)bi_y + 1 ) * bitmap->width + (int)bi_x;
+          indc[2] = (int)bi_y * bitmap->width + (int)bi_x + 1;
+          indc[3] = ( (int)bi_y + 1 ) * bitmap->width + (int)bi_x + 1;
+
+          dist[0] = (float)buffer[indc[0]] / 1024.0f;
+
+          if ( indc[1] >= bitmap->width * bitmap->rows )
+            dist[1] = -status.spread;
+          else
+            dist[1] = (float)buffer[indc[1]] / 1024.0f;
+
+          if ( indc[2] >= bitmap->width * bitmap->rows )
+            dist[2] = -status.spread;
+          else
+            dist[2] = (float)buffer[indc[2]] / 1024.0f;
+
+          if ( indc[3] >= bitmap->width * bitmap->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 the values outside the */
+          /* range defined by `status.width' and use the `status.edge' to */
+          /* make smooth anti-aliased edges.                              */
+
+          /* This is exactly similar to a 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]