(root)/
freetype-2.13.2/
src/
base/
ftlcdfil.c
       1  /****************************************************************************
       2   *
       3   * ftlcdfil.c
       4   *
       5   *   FreeType API for color filtering of subpixel bitmap glyphs (body).
       6   *
       7   * Copyright (C) 2006-2023 by
       8   * David Turner, Robert Wilhelm, and Werner Lemberg.
       9   *
      10   * This file is part of the FreeType project, and may only be used,
      11   * modified, and distributed under the terms of the FreeType project
      12   * license, LICENSE.TXT.  By continuing to use, modify, or distribute
      13   * this file you indicate that you have read the license and
      14   * understand and accept it fully.
      15   *
      16   */
      17  
      18  
      19  #include <freetype/internal/ftdebug.h>
      20  
      21  #include <freetype/ftlcdfil.h>
      22  #include <freetype/ftimage.h>
      23  #include <freetype/internal/ftobjs.h>
      24  
      25  
      26  #ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
      27  
      28  /* define USE_LEGACY to implement the legacy filter */
      29  #define  USE_LEGACY
      30  
      31  #define FT_SHIFTCLAMP( x )  ( x >>= 8, (FT_Byte)( x > 255 ? 255 : x ) )
      32  
      33  
      34    /* add padding according to filter weights */
      35    FT_BASE_DEF( void )
      36    ft_lcd_padding( FT_BBox*        cbox,
      37                    FT_GlyphSlot    slot,
      38                    FT_Render_Mode  mode )
      39    {
      40      FT_Byte*                 lcd_weights;
      41      FT_Bitmap_LcdFilterFunc  lcd_filter_func;
      42  
      43  
      44      /* Per-face LCD filtering takes priority if set up. */
      45      if ( slot->face && slot->face->internal->lcd_filter_func )
      46      {
      47        lcd_weights     = slot->face->internal->lcd_weights;
      48        lcd_filter_func = slot->face->internal->lcd_filter_func;
      49      }
      50      else
      51      {
      52        lcd_weights     = slot->library->lcd_weights;
      53        lcd_filter_func = slot->library->lcd_filter_func;
      54      }
      55  
      56      if ( lcd_filter_func == ft_lcd_filter_fir )
      57      {
      58        if ( mode == FT_RENDER_MODE_LCD )
      59        {
      60          cbox->xMin -= lcd_weights[0] ? 43 :
      61                        lcd_weights[1] ? 22 : 0;
      62          cbox->xMax += lcd_weights[4] ? 43 :
      63                        lcd_weights[3] ? 22 : 0;
      64        }
      65        else if ( mode == FT_RENDER_MODE_LCD_V )
      66        {
      67          cbox->yMin -= lcd_weights[0] ? 43 :
      68                        lcd_weights[1] ? 22 : 0;
      69          cbox->yMax += lcd_weights[4] ? 43 :
      70                        lcd_weights[3] ? 22 : 0;
      71        }
      72      }
      73    }
      74  
      75  
      76    /* FIR filter used by the default and light filters */
      77    FT_BASE_DEF( void )
      78    ft_lcd_filter_fir( FT_Bitmap*           bitmap,
      79                       FT_LcdFiveTapFilter  weights )
      80    {
      81      FT_UInt   width  = (FT_UInt)bitmap->width;
      82      FT_UInt   height = (FT_UInt)bitmap->rows;
      83      FT_Int    pitch  = bitmap->pitch;
      84      FT_Byte*  origin = bitmap->buffer;
      85      FT_Byte   mode   = bitmap->pixel_mode;
      86  
      87  
      88      /* take care of bitmap flow */
      89      if ( pitch > 0 && height > 0 )
      90        origin += pitch * (FT_Int)( height - 1 );
      91  
      92      /* horizontal in-place FIR filter */
      93      if ( mode == FT_PIXEL_MODE_LCD && width >= 2 )
      94      {
      95        FT_Byte*  line = origin;
      96  
      97  
      98        /* `fir' must be at least 32 bit wide, since the sum of */
      99        /* the values in `weights' can exceed 0xFF              */
     100  
     101        for ( ; height > 0; height--, line -= pitch )
     102        {
     103          FT_UInt  fir[5];
     104          FT_UInt  val, xx;
     105  
     106  
     107          val    = line[0];
     108          fir[2] = weights[2] * val;
     109          fir[3] = weights[3] * val;
     110          fir[4] = weights[4] * val;
     111  
     112          val    = line[1];
     113          fir[1] = fir[2] + weights[1] * val;
     114          fir[2] = fir[3] + weights[2] * val;
     115          fir[3] = fir[4] + weights[3] * val;
     116          fir[4] =          weights[4] * val;
     117  
     118          for ( xx = 2; xx < width; xx++ )
     119          {
     120            val    = line[xx];
     121            fir[0] = fir[1] + weights[0] * val;
     122            fir[1] = fir[2] + weights[1] * val;
     123            fir[2] = fir[3] + weights[2] * val;
     124            fir[3] = fir[4] + weights[3] * val;
     125            fir[4] =          weights[4] * val;
     126  
     127            line[xx - 2] = FT_SHIFTCLAMP( fir[0] );
     128          }
     129  
     130          line[xx - 2] = FT_SHIFTCLAMP( fir[1] );
     131          line[xx - 1] = FT_SHIFTCLAMP( fir[2] );
     132        }
     133      }
     134  
     135      /* vertical in-place FIR filter */
     136      else if ( mode == FT_PIXEL_MODE_LCD_V && height >= 2 )
     137      {
     138        FT_Byte*  column = origin;
     139  
     140  
     141        for ( ; width > 0; width--, column++ )
     142        {
     143          FT_Byte*  col = column;
     144          FT_UInt   fir[5];
     145          FT_UInt   val, yy;
     146  
     147  
     148          val    = col[0];
     149          fir[2] = weights[2] * val;
     150          fir[3] = weights[3] * val;
     151          fir[4] = weights[4] * val;
     152          col   -= pitch;
     153  
     154          val    = col[0];
     155          fir[1] = fir[2] + weights[1] * val;
     156          fir[2] = fir[3] + weights[2] * val;
     157          fir[3] = fir[4] + weights[3] * val;
     158          fir[4] =          weights[4] * val;
     159          col   -= pitch;
     160  
     161          for ( yy = 2; yy < height; yy++, col -= pitch )
     162          {
     163            val    = col[0];
     164            fir[0] = fir[1] + weights[0] * val;
     165            fir[1] = fir[2] + weights[1] * val;
     166            fir[2] = fir[3] + weights[2] * val;
     167            fir[3] = fir[4] + weights[3] * val;
     168            fir[4] =          weights[4] * val;
     169  
     170            col[pitch * 2]  = FT_SHIFTCLAMP( fir[0] );
     171          }
     172  
     173          col[pitch * 2]  = FT_SHIFTCLAMP( fir[1] );
     174          col[pitch]      = FT_SHIFTCLAMP( fir[2] );
     175        }
     176      }
     177    }
     178  
     179  
     180  #ifdef USE_LEGACY
     181  
     182    /* intra-pixel filter used by the legacy filter */
     183    static void
     184    _ft_lcd_filter_legacy( FT_Bitmap*      bitmap,
     185                           FT_Byte*        weights )
     186    {
     187      FT_UInt   width  = (FT_UInt)bitmap->width;
     188      FT_UInt   height = (FT_UInt)bitmap->rows;
     189      FT_Int    pitch  = bitmap->pitch;
     190      FT_Byte*  origin = bitmap->buffer;
     191      FT_Byte   mode   = bitmap->pixel_mode;
     192  
     193      static const unsigned int  filters[3][3] =
     194      {
     195        { 65538 * 9/13, 65538 * 1/6, 65538 * 1/13 },
     196        { 65538 * 3/13, 65538 * 4/6, 65538 * 3/13 },
     197        { 65538 * 1/13, 65538 * 1/6, 65538 * 9/13 }
     198      };
     199  
     200      FT_UNUSED( weights );
     201  
     202  
     203      /* take care of bitmap flow */
     204      if ( pitch > 0 && height > 0 )
     205        origin += pitch * (FT_Int)( height - 1 );
     206  
     207      /* horizontal in-place intra-pixel filter */
     208      if ( mode == FT_PIXEL_MODE_LCD && width >= 3 )
     209      {
     210        FT_Byte*  line = origin;
     211  
     212  
     213        for ( ; height > 0; height--, line -= pitch )
     214        {
     215          FT_UInt  xx;
     216  
     217  
     218          for ( xx = 0; xx < width; xx += 3 )
     219          {
     220            FT_UInt  r, g, b;
     221            FT_UInt  p;
     222  
     223  
     224            p  = line[xx];
     225            r  = filters[0][0] * p;
     226            g  = filters[0][1] * p;
     227            b  = filters[0][2] * p;
     228  
     229            p  = line[xx + 1];
     230            r += filters[1][0] * p;
     231            g += filters[1][1] * p;
     232            b += filters[1][2] * p;
     233  
     234            p  = line[xx + 2];
     235            r += filters[2][0] * p;
     236            g += filters[2][1] * p;
     237            b += filters[2][2] * p;
     238  
     239            line[xx]     = (FT_Byte)( r / 65536 );
     240            line[xx + 1] = (FT_Byte)( g / 65536 );
     241            line[xx + 2] = (FT_Byte)( b / 65536 );
     242          }
     243        }
     244      }
     245      else if ( mode == FT_PIXEL_MODE_LCD_V && height >= 3 )
     246      {
     247        FT_Byte*  column = origin;
     248  
     249  
     250        for ( ; width > 0; width--, column++ )
     251        {
     252          FT_Byte*  col = column - 2 * pitch;
     253  
     254  
     255          for ( ; height > 0; height -= 3, col -= 3 * pitch )
     256          {
     257            FT_UInt  r, g, b;
     258            FT_UInt  p;
     259  
     260  
     261            p  = col[0];
     262            r  = filters[0][0] * p;
     263            g  = filters[0][1] * p;
     264            b  = filters[0][2] * p;
     265  
     266            p  = col[pitch];
     267            r += filters[1][0] * p;
     268            g += filters[1][1] * p;
     269            b += filters[1][2] * p;
     270  
     271            p  = col[pitch * 2];
     272            r += filters[2][0] * p;
     273            g += filters[2][1] * p;
     274            b += filters[2][2] * p;
     275  
     276            col[0]         = (FT_Byte)( r / 65536 );
     277            col[pitch]     = (FT_Byte)( g / 65536 );
     278            col[pitch * 2] = (FT_Byte)( b / 65536 );
     279          }
     280        }
     281      }
     282    }
     283  
     284  #endif /* USE_LEGACY */
     285  
     286  
     287    /* documentation in ftlcdfil.h */
     288  
     289    FT_EXPORT_DEF( FT_Error )
     290    FT_Library_SetLcdFilterWeights( FT_Library      library,
     291                                    unsigned char  *weights )
     292    {
     293      if ( !library )
     294        return FT_THROW( Invalid_Library_Handle );
     295  
     296      if ( !weights )
     297        return FT_THROW( Invalid_Argument );
     298  
     299      ft_memcpy( library->lcd_weights, weights, FT_LCD_FILTER_FIVE_TAPS );
     300      library->lcd_filter_func = ft_lcd_filter_fir;
     301  
     302      return FT_Err_Ok;
     303    }
     304  
     305  
     306    /* documentation in ftlcdfil.h */
     307  
     308    FT_EXPORT_DEF( FT_Error )
     309    FT_Library_SetLcdFilter( FT_Library    library,
     310                             FT_LcdFilter  filter )
     311    {
     312      static const FT_LcdFiveTapFilter  default_weights =
     313                     { 0x08, 0x4d, 0x56, 0x4d, 0x08 };
     314      static const FT_LcdFiveTapFilter  light_weights =
     315                     { 0x00, 0x55, 0x56, 0x55, 0x00 };
     316  
     317  
     318      if ( !library )
     319        return FT_THROW( Invalid_Library_Handle );
     320  
     321      switch ( filter )
     322      {
     323      case FT_LCD_FILTER_NONE:
     324        library->lcd_filter_func = NULL;
     325        break;
     326  
     327      case FT_LCD_FILTER_DEFAULT:
     328        ft_memcpy( library->lcd_weights,
     329                   default_weights,
     330                   FT_LCD_FILTER_FIVE_TAPS );
     331        library->lcd_filter_func = ft_lcd_filter_fir;
     332        break;
     333  
     334      case FT_LCD_FILTER_LIGHT:
     335        ft_memcpy( library->lcd_weights,
     336                   light_weights,
     337                   FT_LCD_FILTER_FIVE_TAPS );
     338        library->lcd_filter_func = ft_lcd_filter_fir;
     339        break;
     340  
     341  #ifdef USE_LEGACY
     342  
     343      case FT_LCD_FILTER_LEGACY:
     344      case FT_LCD_FILTER_LEGACY1:
     345        library->lcd_filter_func = _ft_lcd_filter_legacy;
     346        break;
     347  
     348  #endif
     349  
     350      default:
     351        return FT_THROW( Invalid_Argument );
     352      }
     353  
     354      return FT_Err_Ok;
     355    }
     356  
     357  
     358    FT_EXPORT_DEF( FT_Error )
     359    FT_Library_SetLcdGeometry( FT_Library  library,
     360                               FT_Vector   sub[3] )
     361    {
     362      FT_UNUSED( library );
     363      FT_UNUSED( sub );
     364  
     365      return FT_THROW( Unimplemented_Feature );
     366    }
     367  
     368  #else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
     369  
     370    /* add padding to accommodate outline shifts */
     371    FT_BASE_DEF( void )
     372    ft_lcd_padding( FT_BBox*        cbox,
     373                    FT_GlyphSlot    slot,
     374                    FT_Render_Mode  mode )
     375    {
     376      FT_Vector*  sub = slot->library->lcd_geometry;
     377  
     378      if ( mode == FT_RENDER_MODE_LCD )
     379      {
     380        cbox->xMin -= FT_MAX( FT_MAX( sub[0].x, sub[1].x ), sub[2].x );
     381        cbox->xMax -= FT_MIN( FT_MIN( sub[0].x, sub[1].x ), sub[2].x );
     382        cbox->yMin -= FT_MAX( FT_MAX( sub[0].y, sub[1].y ), sub[2].y );
     383        cbox->yMax -= FT_MIN( FT_MIN( sub[0].y, sub[1].y ), sub[2].y );
     384      }
     385      else if ( mode == FT_RENDER_MODE_LCD_V )
     386      {
     387        cbox->xMin -= FT_MAX( FT_MAX( sub[0].y, sub[1].y ), sub[2].y );
     388        cbox->xMax -= FT_MIN( FT_MIN( sub[0].y, sub[1].y ), sub[2].y );
     389        cbox->yMin += FT_MIN( FT_MIN( sub[0].x, sub[1].x ), sub[2].x );
     390        cbox->yMax += FT_MAX( FT_MAX( sub[0].x, sub[1].x ), sub[2].x );
     391      }
     392    }
     393  
     394  
     395    FT_EXPORT_DEF( FT_Error )
     396    FT_Library_SetLcdFilterWeights( FT_Library      library,
     397                                    unsigned char  *weights )
     398    {
     399      FT_UNUSED( library );
     400      FT_UNUSED( weights );
     401  
     402      return FT_THROW( Unimplemented_Feature );
     403    }
     404  
     405  
     406    FT_EXPORT_DEF( FT_Error )
     407    FT_Library_SetLcdFilter( FT_Library    library,
     408                             FT_LcdFilter  filter )
     409    {
     410      FT_UNUSED( library );
     411      FT_UNUSED( filter );
     412  
     413      return FT_THROW( Unimplemented_Feature );
     414    }
     415  
     416  
     417    /* documentation in ftlcdfil.h */
     418  
     419    FT_EXPORT_DEF( FT_Error )
     420    FT_Library_SetLcdGeometry( FT_Library  library,
     421                               FT_Vector   sub[3] )
     422    {
     423      if ( !library )
     424        return FT_THROW( Invalid_Library_Handle );
     425  
     426      if ( !sub )
     427        return FT_THROW( Invalid_Argument );
     428  
     429      ft_memcpy( library->lcd_geometry, sub, 3 * sizeof( FT_Vector ) );
     430  
     431      return FT_Err_Ok;
     432    }
     433  
     434  #endif /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
     435  
     436  
     437  /* END */