[smooth] Separate LCD paths from gray rendering.

This makes `ft_smooth_render' a lot smaller and easier to follow. It
also cleanly separates Harmony and ClearType-style LCD rendering
algorithms. Now I only wish to move LCD filtering and geometry from
FT_Library to FT_Renderer.

* src/smooth/ftsmooth.c (ft_smooth_render): Move LCD code from here...
(ft_smooth_raster_lcd, ft_smooth_raster_lcdv): ... to here.
[FT_CONFIG_OPTION_SUBPIXEL_RENDERING]: Reorganize #ifdef's.
diff --git a/ChangeLog b/ChangeLog
index 858c696..9af7080 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2020-07-03  Alexei Podtelezhnikov  <apodtele@gmail.com>
+
+	[smooth] Separate LCD paths from gray rendering.
+
+	This makes `ft_smooth_render' a lot smaller and easier to follow. It
+	also cleanly separates Harmony and ClearType-style LCD rendering
+	algorithms. Now I only wish to move LCD filtering and geometry from
+	FT_Library to FT_Renderer.
+
+	* src/smooth/ftsmooth.c (ft_smooth_render): Move LCD code from here...
+	(ft_smooth_raster_lcd, ft_smooth_raster_lcdv): ... to here.
+	[FT_CONFIG_OPTION_SUBPIXEL_RENDERING]: Reorganize #ifdef's.
+
 2020-06-20  Sebastian Rasmussen  <sebras@gmail.com>
 
 	[cff] Fix handling of `style_name == NULL' (#58630).
diff --git a/src/smooth/ftsmooth.c b/src/smooth/ftsmooth.c
index 95e14d3..945ff80 100644
--- a/src/smooth/ftsmooth.c
+++ b/src/smooth/ftsmooth.c
@@ -25,36 +25,6 @@
 #include "ftsmerrs.h"
 
 
-  /* initialize renderer -- init its raster */
-  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;
-
-#else   /* set up default LCD filtering */
-
-    FT_Library_SetLcdFilter( render->root.library, FT_LCD_FILTER_DEFAULT );
-
-#endif
-
-    render->clazz->raster_class->raster_reset( render->raster, NULL, 0 );
-
-    return 0;
-  }
-
-
   /* sets render-specific mode */
   static FT_Error
   ft_smooth_set_mode( FT_Renderer  render,
@@ -106,6 +76,255 @@
       FT_Outline_Get_CBox( &slot->outline, cbox );
   }
 
+#ifndef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
+
+  /* initialize renderer -- init its raster */
+  static FT_Error
+  ft_smooth_init( FT_Renderer  render )
+  {
+    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;
+
+    render->clazz->raster_class->raster_reset( render->raster, NULL, 0 );
+
+    return 0;
+  }
+
+
+  static FT_Error
+  ft_smooth_raster_lcd( FT_Renderer  render,
+                        FT_Outline*  outline,
+                        FT_Bitmap*   bitmap )
+  {
+    FT_Error      error = FT_Err_Ok;
+    unsigned int  width = bitmap->width / 3;
+    FT_Vector*    sub   = render->root.library->lcd_geometry;
+    FT_Pos        x, y;
+
+    FT_Raster_Params   params;
+
+
+    params.target = bitmap;
+    params.source = outline;
+    params.flags  = FT_RASTER_FLAG_AA;
+
+    /* Render 3 separate coverage bitmaps, shifting the outline.  */
+    FT_Outline_Translate( outline,
+                          -sub[0].x,
+                          -sub[0].y );
+    error = render->raster_render( render->raster, &params );
+    x = sub[0].x;
+    y = sub[0].y;
+    if ( error )
+      goto Exit;
+
+    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 );
+    x = sub[1].x;
+    y = sub[1].y;
+    bitmap->buffer -= width;
+    if ( error )
+      goto Exit;
+
+    bitmap->buffer += 2 * width;
+    FT_Outline_Translate( outline,
+                          sub[1].x - sub[2].x,
+                          sub[1].y - sub[2].y );
+    error = render->raster_render( render->raster, &params );
+    x = sub[2].x;
+    y = sub[2].y;
+    bitmap->buffer -= 2 * width;
+    if ( error )
+      goto Exit;
+
+    /* XXX: Rearrange the bytes according to FT_PIXEL_MODE_LCD.    */
+    /* XXX: It is more efficient to render every third byte above. */
+    {
+      FT_Memory  memory  = render->root.memory;
+      FT_Byte*   line;
+      FT_Byte*   temp = NULL;
+      FT_UInt    i, j;
+
+      unsigned int  height = bitmap->rows;
+      int           pitch  = bitmap->pitch;
+
+
+      if ( FT_ALLOC( temp, (FT_ULong)pitch ) )
+        goto Exit;
+
+      for ( i = 0; i < height; i++ )
+      {
+        line = bitmap->buffer + i * (FT_ULong)pitch;
+        for ( j = 0; j < width; j++ )
+        {
+          temp[3 * j    ] = line[j];
+          temp[3 * j + 1] = line[j + width];
+          temp[3 * j + 2] = line[j + width + width];
+        }
+        FT_MEM_COPY( line, temp, pitch );
+      }
+
+      FT_FREE( temp );
+    }
+
+  Exit:
+    FT_Outline_Translate( outline, x, y );
+
+    return error;
+  }
+
+
+  static FT_Error
+  ft_smooth_raster_lcdv( FT_Renderer  render,
+                         FT_Outline*  outline,
+                         FT_Bitmap*   bitmap )
+  {
+    FT_Error     error = FT_Err_Ok;
+    int          pitch = bitmap->pitch;
+    FT_Vector*   sub   = render->root.library->lcd_geometry;
+    FT_Pos       x, y;
+
+    FT_Raster_Params  params;
+
+
+    params.target = bitmap;
+    params.source = outline;
+    params.flags  = FT_RASTER_FLAG_AA;
+
+    /* Render 3 separate coverage 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;
+
+    FT_Outline_Translate( outline,
+                          -sub[0].y,
+                          sub[0].x );
+    error = render->raster_render( render->raster, &params );
+    x = sub[0].y;
+    y = -sub[0].x;
+    if ( error )
+      goto Exit;
+
+    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 );
+    x = sub[1].y;
+    y = -sub[1].x;
+    bitmap->buffer -= pitch;
+    if ( error )
+      goto Exit;
+
+    bitmap->buffer += 2 * pitch;
+    FT_Outline_Translate( outline,
+                          sub[1].y - sub[2].y,
+                          sub[2].x - sub[1].x );
+    error = render->raster_render( render->raster, &params );
+    x = sub[2].y;
+    y = -sub[2].x;
+    bitmap->buffer -= 2 * pitch;
+
+  Exit:
+    FT_Outline_Translate( outline, x, y );
+
+    bitmap->pitch /= 3;
+    bitmap->rows  *= 3;
+
+    return error;
+  }
+
+#else   /* FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
+
+  /* initialize renderer -- init its raster */
+  static FT_Error
+  ft_smooth_init( FT_Renderer  render )
+  {
+    /* set up default LCD filtering */
+    FT_Library_SetLcdFilter( render->root.library, FT_LCD_FILTER_DEFAULT );
+
+    render->clazz->raster_class->raster_reset( render->raster, NULL, 0 );
+
+    return 0;
+  }
+
+
+  static FT_Error
+  ft_smooth_raster_lcd( FT_Renderer  render,
+                        FT_Outline*  outline,
+                        FT_Bitmap*   bitmap )
+  {
+    FT_Error    error      = FT_Err_Ok;
+    FT_Vector*  points     = outline->points;
+    FT_Vector*  points_end = FT_OFFSET( points, outline->n_points );
+    FT_Vector*  vec;
+
+    FT_Raster_Params  params;
+
+
+    params.target = bitmap;
+    params.source = outline;
+    params.flags  = FT_RASTER_FLAG_AA;
+
+    /* implode outline */
+    for ( vec = points; vec < points_end; vec++ )
+      vec->x *= 3;
+
+    /* render outline into the bitmap */
+    error = render->raster_render( render->raster, &params );
+
+    /* deflate outline */
+    for ( vec = points; vec < points_end; vec++ )
+      vec->x /= 3;
+
+    return error;
+  }
+
+
+  static FT_Error
+  ft_smooth_raster_lcdv( FT_Renderer  render,
+                         FT_Outline*  outline,
+                         FT_Bitmap*   bitmap )
+  {
+    FT_Error    error      = FT_Err_Ok;
+    FT_Vector*  points     = outline->points;
+    FT_Vector*  points_end = FT_OFFSET( points, outline->n_points );
+    FT_Vector*  vec;
+
+    FT_Raster_Params  params;
+
+
+    params.target = bitmap;
+    params.source = outline;
+    params.flags  = FT_RASTER_FLAG_AA;
+
+    /* implode outline */
+    for ( vec = points; vec < points_end; vec++ )
+      vec->y *= 3;
+
+    /* render outline into the bitmap */
+    error = render->raster_render( render->raster, &params );
+
+    /* deflate outline */
+    for ( vec = points; vec < points_end; vec++ )
+      vec->y /= 3;
+
+    return error;
+  }
+
+#endif  /* FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
 
   /* convert a slot's glyph image into a bitmap */
   static FT_Error
@@ -120,10 +339,6 @@
     FT_Memory    memory  = render->root.memory;
     FT_Pos       x_shift = 0;
     FT_Pos       y_shift = 0;
-    FT_Int       hmul    = ( mode == FT_RENDER_MODE_LCD );
-    FT_Int       vmul    = ( mode == FT_RENDER_MODE_LCD_V );
-
-    FT_Raster_Params  params;
 
 
     /* check glyph image format */
@@ -136,7 +351,8 @@
     /* check mode */
     if ( mode != FT_RENDER_MODE_NORMAL &&
          mode != FT_RENDER_MODE_LIGHT  &&
-         !hmul && !vmul )
+         mode != FT_RENDER_MODE_LCD    &&
+         mode != FT_RENDER_MODE_LCD_V  )
     {
       error = FT_THROW( Cannot_Render_Glyph );
       goto Exit;
@@ -181,188 +397,52 @@
     if ( x_shift || y_shift )
       FT_Outline_Translate( outline, x_shift, y_shift );
 
-    /* set up parameters */
-    params.target = bitmap;
-    params.source = outline;
-    params.flags  = FT_RASTER_FLAG_AA;
+    if ( mode == FT_RENDER_MODE_NORMAL ||
+         mode == FT_RENDER_MODE_LIGHT  )
+    {
+      FT_Raster_Params  params;
+
+
+      params.target = bitmap;
+      params.source = outline;
+      params.flags  = FT_RASTER_FLAG_AA;
+
+      error = render->raster_render( render->raster, &params );
+    }
+    else
+    {
+      if ( mode == FT_RENDER_MODE_LCD )
+        error = ft_smooth_raster_lcd ( render, outline, bitmap );
+      else if ( mode == FT_RENDER_MODE_LCD_V )
+        error = ft_smooth_raster_lcdv( render, outline, bitmap );
 
 #ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
 
-    /* implode outline if needed */
-    {
-      FT_Vector*  points     = outline->points;
-      FT_Vector*  points_end = FT_OFFSET( points, outline->n_points );
-      FT_Vector*  vec;
-
-
-      if ( hmul )
-        for ( vec = points; vec < points_end; vec++ )
-          vec->x *= 3;
-
-      if ( vmul )
-        for ( vec = points; vec < points_end; vec++ )
-          vec->y *= 3;
-    }
-
-    /* render outline into the bitmap */
-    error = render->raster_render( render->raster, &params );
-
-    /* deflate outline if needed */
-    {
-      FT_Vector*  points     = outline->points;
-      FT_Vector*  points_end = FT_OFFSET( points, outline->n_points );
-      FT_Vector*  vec;
-
-
-      if ( hmul )
-        for ( vec = points; vec < points_end; vec++ )
-          vec->x /= 3;
-
-      if ( vmul )
-        for ( vec = points; vec < points_end; vec++ )
-          vec->y /= 3;
-    }
-
-    if ( error )
-      goto Exit;
-
-    /* finally apply filtering */
-    if ( hmul || vmul )
-    {
-      FT_Byte*                 lcd_weights;
-      FT_Bitmap_LcdFilterFunc  lcd_filter_func;
-
-
-      /* Per-face LCD filtering takes priority if set up. */
-      if ( slot->face && slot->face->internal->lcd_filter_func )
+      /* finally apply filtering */
       {
-        lcd_weights     = slot->face->internal->lcd_weights;
-        lcd_filter_func = slot->face->internal->lcd_filter_func;
-      }
-      else
-      {
-        lcd_weights     = slot->library->lcd_weights;
-        lcd_filter_func = slot->library->lcd_filter_func;
-      }
-
-      if ( lcd_filter_func )
-        lcd_filter_func( bitmap, lcd_weights );
-    }
-
-#else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
-
-    if ( hmul )  /* lcd */
-    {
-      FT_Byte*  line;
-      FT_Byte*  temp = NULL;
-      FT_UInt   i, j;
-
-      unsigned int  height = bitmap->rows;
-      unsigned int  width  = bitmap->width;
-      int           pitch  = bitmap->pitch;
-
-      FT_Vector*  sub = slot->library->lcd_geometry;
+        FT_Byte*                 lcd_weights;
+        FT_Bitmap_LcdFilterFunc  lcd_filter_func;
 
 
-      /* 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;
-
-      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 );
-      bitmap->buffer -= width;
-      if ( error )
-        goto Exit;
-
-      bitmap->buffer += 2 * width;
-      FT_Outline_Translate( outline,
-                            sub[1].x - sub[2].x,
-                            sub[1].y - sub[2].y );
-      error = render->raster_render( render->raster, &params );
-      bitmap->buffer -= 2 * width;
-      if ( error )
-        goto Exit;
-
-      x_shift -= sub[2].x;
-      y_shift -= sub[2].y;
-
-      /* XXX: Rearrange the bytes according to FT_PIXEL_MODE_LCD.    */
-      /* XXX: It is more efficient to render every third byte above. */
-
-      if ( FT_ALLOC( temp, (FT_ULong)pitch ) )
-        goto Exit;
-
-      for ( i = 0; i < height; i++ )
-      {
-        line = bitmap->buffer + i * (FT_ULong)pitch;
-        for ( j = 0; j < width; j++ )
+        /* Per-face LCD filtering takes priority if set up. */
+        if ( slot->face && slot->face->internal->lcd_filter_func )
         {
-          temp[3 * j    ] = line[j];
-          temp[3 * j + 1] = line[j + width];
-          temp[3 * j + 2] = line[j + width + width];
+          lcd_weights     = slot->face->internal->lcd_weights;
+          lcd_filter_func = slot->face->internal->lcd_filter_func;
         }
-        FT_MEM_COPY( line, temp, pitch );
+        else
+        {
+          lcd_weights     = slot->library->lcd_weights;
+          lcd_filter_func = slot->library->lcd_filter_func;
+        }
+
+        if ( lcd_filter_func )
+          lcd_filter_func( bitmap, lcd_weights );
       }
 
-      FT_FREE( temp );
+#endif /* FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
+
     }
-    else if ( vmul )  /* lcd_v */
-    {
-      int  pitch  = bitmap->pitch;
-
-      FT_Vector*  sub = slot->library->lcd_geometry;
-
-
-      /* 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;
-
-      FT_Outline_Translate( outline,
-                            -sub[0].y,
-                            sub[0].x );
-      error = render->raster_render( render->raster, &params );
-      if ( error )
-        goto Exit;
-
-      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 );
-      bitmap->buffer -= pitch;
-      if ( error )
-        goto Exit;
-
-      bitmap->buffer += 2 * pitch;
-      FT_Outline_Translate( outline,
-                            sub[1].y - sub[2].y,
-                            sub[2].x - sub[1].x );
-      error = render->raster_render( render->raster, &params );
-      bitmap->buffer -= 2 * pitch;
-      if ( error )
-        goto Exit;
-
-      x_shift -= sub[2].y;
-      y_shift += sub[2].x;
-
-      bitmap->pitch /= 3;
-      bitmap->rows  *= 3;
-    }
-    else  /* grayscale */
-      error = render->raster_render( render->raster, &params );
-
-#endif /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
 
   Exit:
     if ( !error )