Commit 7b73cb0707586e7988efdd07a1dfe690c3036ca1

Alexei Podtelezhnikov 2018-05-24T22:38:24

[smooth] Formalize Harmony LCD rendering. This generalizes magic outline shifts that make Harmony LCD rendering work in terms of precise two-dimensional RGB subpixel positions. These coordinates are now set in time of the `smooth' module initialization and later used to shift a glyph outline for rendering. FT_RENDER_MODE_LCD and FT_RENDER_MODE_LCD_V use the same coordinates. The letter, however, rotates them before using. The LCD bitmap padding is also calculated using these coordinates. * include/freetype/internal/ftobjs.h (FT_LibraryRec): New array field `lcd_geometry'. * src/base/ftlcdfil.c (ft_lcd_padding): Reworked. * src/base/ftobjs.c (ft_glyphslot_preset_bitmap): Updated accordingly. * src/smooth/ftsmooth.c [!FT_CONFIG_OPTION_SUBPIXEL_RENDERING] (ft_smooth_init): Initialize `lcd_geometry'. (ft_smooth_render_generic): Formalize outline shifts.

diff --git a/ChangeLog b/ChangeLog
index 789ac94..7c19448 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,24 @@
+2018-05-24  Alexei Podtelezhnikov  <apodtele@gmail.com>
+
+	[smooth] Formalize Harmony LCD rendering.
+
+	This generalizes magic outline shifts that make Harmony LCD
+	rendering work in terms of precise two-dimensional RGB subpixel
+	positions. These coordinates are now set in time of the `smooth'
+	module initialization and later used to shift a glyph outline for
+	rendering. FT_RENDER_MODE_LCD and FT_RENDER_MODE_LCD_V use the same
+	coordinates. The letter, however, rotates them before using.
+	The LCD bitmap padding is also calculated using these coordinates.
+
+	* include/freetype/internal/ftobjs.h (FT_LibraryRec): New array field
+	`lcd_geometry'.
+	* src/base/ftlcdfil.c (ft_lcd_padding): Reworked.
+	* src/base/ftobjs.c (ft_glyphslot_preset_bitmap): Updated accordingly.
+
+	* src/smooth/ftsmooth.c [!FT_CONFIG_OPTION_SUBPIXEL_RENDERING]
+	(ft_smooth_init): Initialize `lcd_geometry'.
+	(ft_smooth_render_generic): Formalize outline shifts.
+
 2018-05-22  Werner Lemberg  <wl@gnu.org>
 
 	[truetype] Reject elements of composites with invalid glyph indices.
diff --git a/include/freetype/internal/ftobjs.h b/include/freetype/internal/ftobjs.h
index a8d987f..77f5dc4 100644
--- a/include/freetype/internal/ftobjs.h
+++ b/include/freetype/internal/ftobjs.h
@@ -270,11 +270,11 @@ FT_BEGIN_HEADER
   FT_CMap_Done( FT_CMap  cmap );
 
 
-  /* adds LCD padding to Min and Max boundaries */
+  /* add LCD padding to CBox */
   FT_BASE( void )
-  ft_lcd_padding( FT_Pos*       Min,
-                  FT_Pos*       Max,
-                  FT_GlyphSlot  slot );
+  ft_lcd_padding( FT_BBox*        cbox,
+                  FT_GlyphSlot    slot,
+                  FT_Render_Mode  mode );
 
 #ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
 
@@ -350,8 +350,8 @@ FT_BEGIN_HEADER
   /*                                                                       */
   /*    lcd_weights      ::                                                */
   /*    lcd_filter_func  ::                                                */
-  /*      If subpixel rendering is activated, the LCD filtering weights    */
-  /*      and callback function.                                           */
+  /*      These fields specify the LCD filtering weights and callback      */
+  /*      function for ClearType-style subpixel rendering.                 */
   /*                                                                       */
   /*    refcount ::                                                        */
   /*      A counter initialized to~1 at the time an @FT_Face structure is  */
@@ -868,11 +868,15 @@ FT_BEGIN_HEADER
   /*                        interpreter.  Currently, only the TrueType     */
   /*                        bytecode debugger uses this.                   */
   /*                                                                       */
-  /*    lcd_weights      :: If subpixel rendering is activated, the LCD    */
-  /*                        filter weights, if any.                        */
+  /*    lcd_weights      :: The LCD filter weights for ClearType-style     */
+  /*                        subpixel rendering.                            */
   /*                                                                       */
-  /*    lcd_filter_func  :: If subpixel rendering is activated, the LCD    */
-  /*                        filtering callback function.                   */
+  /*    lcd_filter_func  :: The LCD filtering callback function for        */
+  /*                        for ClearType-style subpixel rendering.        */
+  /*                                                                       */
+  /*    lcd_geometry     :: This array specifies LCD subpixel geometry     */
+  /*                        and controls Harmony LCD rendering technique,  */
+  /*                        alternative to ClearType.                      */
   /*                                                                       */
   /*    pic_container    :: Contains global structs and tables, instead    */
   /*                        of defining them globally.                     */
@@ -904,6 +908,8 @@ FT_BEGIN_HEADER
 #ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
     FT_LcdFiveTapFilter      lcd_weights;      /* filter weights, if any */
     FT_Bitmap_LcdFilterFunc  lcd_filter_func;  /* filtering callback     */
+#else
+    FT_Vector                lcd_geometry[3];  /* RGB subpixel positions */
 #endif
 
     FT_Int             refcount;
diff --git a/src/base/ftlcdfil.c b/src/base/ftlcdfil.c
index 8d314df..213f3f1 100644
--- a/src/base/ftlcdfil.c
+++ b/src/base/ftlcdfil.c
@@ -34,9 +34,9 @@
 
   /* add padding according to filter weights */
   FT_BASE_DEF (void)
-  ft_lcd_padding( FT_Pos*       Min,
-                  FT_Pos*       Max,
-                  FT_GlyphSlot  slot )
+  ft_lcd_padding( FT_BBox*        cbox,
+                  FT_GlyphSlot    slot,
+                  FT_Render_Mode  mode )
   {
     FT_Byte*                 lcd_weights;
     FT_Bitmap_LcdFilterFunc  lcd_filter_func;
@@ -56,10 +56,20 @@
 
     if ( lcd_filter_func == ft_lcd_filter_fir )
     {
-      *Min -= lcd_weights[0] ? 43 :
-              lcd_weights[1] ? 22 : 0;
-      *Max += lcd_weights[4] ? 43 :
-              lcd_weights[3] ? 22 : 0;
+      if ( mode == FT_RENDER_MODE_LCD )
+      {
+        cbox->xMin -= lcd_weights[0] ? 43 :
+                      lcd_weights[1] ? 22 : 0;
+        cbox->xMax += lcd_weights[4] ? 43 :
+                      lcd_weights[3] ? 22 : 0;
+      }
+      else if ( mode == FT_RENDER_MODE_LCD_V )
+      {
+        cbox->yMin -= lcd_weights[0] ? 43 :
+                      lcd_weights[1] ? 22 : 0;
+        cbox->yMax += lcd_weights[4] ? 43 :
+                      lcd_weights[3] ? 22 : 0;
+      }
     }
   }
 
@@ -343,16 +353,28 @@
 
 #else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
 
-  /* add padding according to accommodate outline shifts */
+  /* add padding to accommodate outline shifts */
   FT_BASE_DEF (void)
-  ft_lcd_padding( FT_Pos*       Min,
-                  FT_Pos*       Max,
-                  FT_GlyphSlot  slot )
+  ft_lcd_padding( FT_BBox*        cbox,
+                  FT_GlyphSlot    slot,
+                  FT_Render_Mode  mode )
   {
-    FT_UNUSED( slot );
+    FT_Vector*  sub = slot->library->lcd_geometry;
 
-    *Min -= 21;
-    *Max += 21;
+    if ( mode == FT_RENDER_MODE_LCD )
+    {
+      cbox->xMin -= FT_MAX( FT_MAX( sub[0].x, sub[1].x ), sub[2].x );
+      cbox->xMax -= FT_MIN( FT_MIN( sub[0].x, sub[1].x ), sub[2].x );
+      cbox->yMin -= FT_MAX( FT_MAX( sub[0].y, sub[1].y ), sub[2].y );
+      cbox->yMax -= FT_MIN( FT_MIN( sub[0].y, sub[1].y ), sub[2].y );
+    }
+    else if ( mode == FT_RENDER_MODE_LCD_V )
+    {
+      cbox->xMin -= FT_MAX( FT_MAX( sub[0].y, sub[1].y ), sub[2].y );
+      cbox->xMax -= FT_MIN( FT_MIN( sub[0].y, sub[1].y ), sub[2].y );
+      cbox->yMin += FT_MIN( FT_MIN( sub[0].x, sub[1].x ), sub[2].x );
+      cbox->yMax += FT_MAX( FT_MAX( sub[0].x, sub[1].x ), sub[2].x );
+    }
   }
 
 
diff --git a/src/base/ftobjs.c b/src/base/ftobjs.c
index d7768dd..2cd0c92 100644
--- a/src/base/ftobjs.c
+++ b/src/base/ftobjs.c
@@ -397,12 +397,12 @@
 
     case FT_RENDER_MODE_LCD:
       pixel_mode = FT_PIXEL_MODE_LCD;
-      ft_lcd_padding( &cbox.xMin, &cbox.xMax, slot );
+      ft_lcd_padding( &cbox, slot, mode );
       goto Round;
 
     case FT_RENDER_MODE_LCD_V:
       pixel_mode = FT_PIXEL_MODE_LCD_V;
-      ft_lcd_padding( &cbox.yMin, &cbox.yMax, slot );
+      ft_lcd_padding( &cbox, slot, mode );
       goto Round;
 
     case FT_RENDER_MODE_NORMAL:
diff --git a/src/smooth/ftsmooth.c b/src/smooth/ftsmooth.c
index 497926e..3813251 100644
--- a/src/smooth/ftsmooth.c
+++ b/src/smooth/ftsmooth.c
@@ -30,6 +30,22 @@
   static FT_Error
   ft_smooth_init( FT_Renderer  render )
   {
+
+#ifndef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
+
+    FT_Vector*  sub = render->root.library->lcd_geometry;
+
+
+    /* set up default subpixel geometry for striped RGB panels. */
+    sub[0].x = -21;
+    sub[0].y = 0;
+    sub[1].x = 0;
+    sub[1].y = 0;
+    sub[2].x = 21;
+    sub[2].y = 0;
+
+#endif
+
     render->clazz->raster_class->raster_reset( render->raster, NULL, 0 );
 
     return 0;
@@ -234,33 +250,33 @@
       unsigned int  width  = bitmap->width;
       int           pitch  = bitmap->pitch;
 
+      FT_Vector*  sub = slot->library->lcd_geometry;
 
-      /* Render 3 separate monochrome bitmaps, shifting the outline  */
-      /* by 1/3 pixel.                                               */
-      width /= 3;
 
-      bitmap->buffer += width;
+      /* Render 3 separate monochrome bitmaps, shifting the outline.  */
+      width /= 3;
 
+      FT_Outline_Translate( outline,           -sub[0].x,           -sub[0].y );
       error = render->raster_render( render->raster, &params );
       if ( error )
         goto Exit;
 
-      FT_Outline_Translate( outline, -21, 0 );
-      x_shift        -= 21;
       bitmap->buffer += width;
-
+      FT_Outline_Translate( outline, sub[0].x - sub[1].x, sub[0].y - sub[1].y );
       error = render->raster_render( render->raster, &params );
       if ( error )
         goto Exit;
 
-      FT_Outline_Translate( outline,  42, 0 );
-      x_shift        += 42;
-      bitmap->buffer -= 2 * width;
-
+      bitmap->buffer += width;
+      FT_Outline_Translate( outline, sub[1].x - sub[2].x, sub[1].y - sub[2].y );
       error = render->raster_render( render->raster, &params );
       if ( error )
         goto Exit;
 
+      x_shift        -= sub[2].x;
+      y_shift        -= sub[2].y;
+      bitmap->buffer -= 2 * width;
+
       /* XXX: Rearrange the bytes according to FT_PIXEL_MODE_LCD.    */
       /* XXX: It is more efficient to render every third byte above. */
 
@@ -285,34 +301,36 @@
     {
       int  pitch  = bitmap->pitch;
 
+      FT_Vector*  sub = slot->library->lcd_geometry;
 
-      /* Render 3 separate monochrome bitmaps, shifting the outline  */
-      /* by 1/3 pixel. Triple the pitch to render on each third row. */
+
+      /* Render 3 separate monochrome bitmaps, shifting the outline. */
+      /* Notice that the subpixel geometry vectors are rotated.      */
+      /* Triple the pitch to render on each third row.               */
       bitmap->pitch *= 3;
       bitmap->rows  /= 3;
 
-      bitmap->buffer += pitch;
-
+      FT_Outline_Translate( outline,           -sub[0].y, sub[0].x            );
       error = render->raster_render( render->raster, &params );
       if ( error )
         goto Exit;
 
-      FT_Outline_Translate( outline, 0,  21 );
-      y_shift        += 21;
       bitmap->buffer += pitch;
-
+      FT_Outline_Translate( outline, sub[0].y - sub[1].y, sub[1].x - sub[0].x );
       error = render->raster_render( render->raster, &params );
       if ( error )
         goto Exit;
 
-      FT_Outline_Translate( outline, 0, -42 );
-      y_shift        -= 42;
-      bitmap->buffer -= 2 * pitch;
-
+      bitmap->buffer += pitch;
+      FT_Outline_Translate( outline, sub[1].y - sub[2].y, sub[2].x - sub[1].x );
       error = render->raster_render( render->raster, &params );
       if ( error )
         goto Exit;
 
+      x_shift        -= sub[2].y;
+      y_shift        += sub[2].x;
+      bitmap->buffer -= 2 * pitch;
+
       bitmap->pitch /= 3;
       bitmap->rows  *= 3;
     }