12c002e13a74ab89b2ad0305088ed18660d5952abWerner Lemberg/***************************************************************************/
22c002e13a74ab89b2ad0305088ed18660d5952abWerner Lemberg/*                                                                         */
32c002e13a74ab89b2ad0305088ed18660d5952abWerner Lemberg/*  ftlcdfil.c                                                             */
42c002e13a74ab89b2ad0305088ed18660d5952abWerner Lemberg/*                                                                         */
52c002e13a74ab89b2ad0305088ed18660d5952abWerner Lemberg/*    FreeType API for color filtering of subpixel bitmap glyphs (body).   */
62c002e13a74ab89b2ad0305088ed18660d5952abWerner Lemberg/*                                                                         */
70a0c22569deab933df21127e75db5c81f724f292Werner Lemberg/*  Copyright 2006-2018 by                                                 */
82c002e13a74ab89b2ad0305088ed18660d5952abWerner Lemberg/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
92c002e13a74ab89b2ad0305088ed18660d5952abWerner Lemberg/*                                                                         */
102c002e13a74ab89b2ad0305088ed18660d5952abWerner Lemberg/*  This file is part of the FreeType project, and may only be used,       */
112c002e13a74ab89b2ad0305088ed18660d5952abWerner Lemberg/*  modified, and distributed under the terms of the FreeType project      */
122c002e13a74ab89b2ad0305088ed18660d5952abWerner Lemberg/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
132c002e13a74ab89b2ad0305088ed18660d5952abWerner Lemberg/*  this file you indicate that you have read the license and              */
142c002e13a74ab89b2ad0305088ed18660d5952abWerner Lemberg/*  understand and accept it fully.                                        */
152c002e13a74ab89b2ad0305088ed18660d5952abWerner Lemberg/*                                                                         */
162c002e13a74ab89b2ad0305088ed18660d5952abWerner Lemberg/***************************************************************************/
172c002e13a74ab89b2ad0305088ed18660d5952abWerner Lemberg
182c002e13a74ab89b2ad0305088ed18660d5952abWerner Lemberg
193cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner#include <ft2build.h>
20059bc335ce42220b222763379e89d0cbf2b949ebWerner Lemberg#include FT_INTERNAL_DEBUG_H
21059bc335ce42220b222763379e89d0cbf2b949ebWerner Lemberg
223cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner#include FT_LCD_FILTER_H
233cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner#include FT_IMAGE_H
243cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner#include FT_INTERNAL_OBJECTS_H
253cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner
262c002e13a74ab89b2ad0305088ed18660d5952abWerner Lemberg
273cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
283cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner
2929c191cd530ef3351a8f15aa86d8811e820e9fa1David Turner/* define USE_LEGACY to implement the legacy filter */
308765c71b41625406fe87598645d842ca8451983cDavid Turner#define  USE_LEGACY
318765c71b41625406fe87598645d842ca8451983cDavid Turner
32c56d8851ea987023cc73981a70d261b3f6427545Alexei Podtelezhnikov (Алексей Подтележников)#define FT_SHIFTCLAMP( x )  ( x >>= 8, (FT_Byte)( x > 255 ? 255 : x ) )
33c56d8851ea987023cc73981a70d261b3f6427545Alexei Podtelezhnikov (Алексей Подтележников)
3461d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)
3561d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)  /* add padding according to filter weights */
3661d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)  FT_BASE_DEF (void)
3761d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)  ft_lcd_padding( FT_Pos*       Min,
3861d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)                  FT_Pos*       Max,
3961d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)                  FT_GlyphSlot  slot )
4061d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)  {
4161d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)    FT_Byte*                 lcd_weights;
4261d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)    FT_Bitmap_LcdFilterFunc  lcd_filter_func;
4361d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)
4461d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)
4561d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)    /* Per-face LCD filtering takes priority if set up. */
4661d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)    if ( slot->face && slot->face->internal->lcd_filter_func )
4761d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)    {
4861d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)      lcd_weights     = slot->face->internal->lcd_weights;
4961d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)      lcd_filter_func = slot->face->internal->lcd_filter_func;
5061d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)    }
5161d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)    else
5261d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)    {
5361d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)      lcd_weights     = slot->library->lcd_weights;
5461d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)      lcd_filter_func = slot->library->lcd_filter_func;
5561d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)    }
5661d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)
5761d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)    if ( lcd_filter_func == ft_lcd_filter_fir )
5861d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)    {
5961d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)      *Min -= lcd_weights[0] ? 43 :
6061d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)              lcd_weights[1] ? 22 : 0;
6161d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)      *Max += lcd_weights[4] ? 43 :
6261d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)              lcd_weights[3] ? 22 : 0;
6361d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)    }
6461d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)  }
6561d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)
6661d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)
678765c71b41625406fe87598645d842ca8451983cDavid Turner  /* FIR filter used by the default and light filters */
6802e80da6090c21d6e59ac955b7f56e1ad4a9850bAlexei Podtelezhnikov (Алексей Подтележников)  FT_BASE_DEF( void )
692e9519885b8e7e5fdc9d75b35992ef7bbf15e53aNikolaus Waxweiler  ft_lcd_filter_fir( FT_Bitmap*           bitmap,
702e9519885b8e7e5fdc9d75b35992ef7bbf15e53aNikolaus Waxweiler                     FT_Render_Mode       mode,
712e9519885b8e7e5fdc9d75b35992ef7bbf15e53aNikolaus Waxweiler                     FT_LcdFiveTapFilter  weights )
723cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner  {
737b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)    FT_UInt   width  = (FT_UInt)bitmap->width;
747b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)    FT_UInt   height = (FT_UInt)bitmap->rows;
757b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)    FT_Int    pitch  = bitmap->pitch;
767b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)    FT_Byte*  origin = bitmap->buffer;
772c002e13a74ab89b2ad0305088ed18660d5952abWerner Lemberg
783cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner
797b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)    /* take care of bitmap flow */
807b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)    if ( pitch > 0 )
817b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)      origin += pitch * (FT_Int)( height - 1 );
827b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)
833cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner    /* horizontal in-place FIR filter */
847b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)    if ( mode == FT_RENDER_MODE_LCD && width >= 2 )
853cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner    {
867b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)      FT_Byte*  line = origin;
873cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner
889154dab8155e7bb5865dd5041fc4ef7fc5450c98Werner Lemberg
897b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)      /* `fir' must be at least 32 bit wide, since the sum of */
907b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)      /* the values in `weights' can exceed 0xFF              */
916497b9c5a599071a631664a55cf32b7d96ce3474Werner Lemberg
927b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)      for ( ; height > 0; height--, line -= pitch )
933cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner      {
947b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)        FT_UInt  fir[5];
957b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)        FT_UInt  val, xx;
962c002e13a74ab89b2ad0305088ed18660d5952abWerner Lemberg
973cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner
987b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)        val    = line[0];
997b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)        fir[2] = weights[2] * val;
1007b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)        fir[3] = weights[3] * val;
1017b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)        fir[4] = weights[4] * val;
1023cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner
1037b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)        val    = line[1];
1047b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)        fir[1] = fir[2] + weights[1] * val;
1057b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)        fir[2] = fir[3] + weights[2] * val;
1067b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)        fir[3] = fir[4] + weights[3] * val;
1077b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)        fir[4] =          weights[4] * val;
1083cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner
1093cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner        for ( xx = 2; xx < width; xx++ )
1103cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner        {
1113cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner          val    = line[xx];
1127b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)          fir[0] = fir[1] + weights[0] * val;
1137b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)          fir[1] = fir[2] + weights[1] * val;
1147b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)          fir[2] = fir[3] + weights[2] * val;
1157b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)          fir[3] = fir[4] + weights[3] * val;
1167b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)          fir[4] =          weights[4] * val;
1177b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)
118c56d8851ea987023cc73981a70d261b3f6427545Alexei Podtelezhnikov (Алексей Подтележников)          line[xx - 2] = FT_SHIFTCLAMP( fir[0] );
1193cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner        }
1203cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner
121c56d8851ea987023cc73981a70d261b3f6427545Alexei Podtelezhnikov (Алексей Подтележников)        line[xx - 2] = FT_SHIFTCLAMP( fir[1] );
122c56d8851ea987023cc73981a70d261b3f6427545Alexei Podtelezhnikov (Алексей Подтележников)        line[xx - 1] = FT_SHIFTCLAMP( fir[2] );
1233cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner      }
1243cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner    }
1252c002e13a74ab89b2ad0305088ed18660d5952abWerner Lemberg
1263cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner    /* vertical in-place FIR filter */
1277b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)    else if ( mode == FT_RENDER_MODE_LCD_V && height >= 2 )
1283cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner    {
1297b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)      FT_Byte*  column = origin;
1303cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner
1319154dab8155e7bb5865dd5041fc4ef7fc5450c98Werner Lemberg
1323cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner      for ( ; width > 0; width--, column++ )
1333cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner      {
1343cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner        FT_Byte*  col = column;
1357b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)        FT_UInt   fir[5];
1367b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)        FT_UInt   val, yy;
1373cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner
1382c002e13a74ab89b2ad0305088ed18660d5952abWerner Lemberg
1397b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)        val    = col[0];
1407b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)        fir[2] = weights[2] * val;
1417b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)        fir[3] = weights[3] * val;
1427b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)        fir[4] = weights[4] * val;
1437b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)        col   -= pitch;
1443cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner
1457b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)        val    = col[0];
1467b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)        fir[1] = fir[2] + weights[1] * val;
1477b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)        fir[2] = fir[3] + weights[2] * val;
1487b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)        fir[3] = fir[4] + weights[3] * val;
1497b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)        fir[4] =          weights[4] * val;
1507b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)        col   -= pitch;
1513cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner
1527b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)        for ( yy = 2; yy < height; yy++, col -= pitch )
1533cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner        {
1543cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner          val    = col[0];
1557b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)          fir[0] = fir[1] + weights[0] * val;
1567b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)          fir[1] = fir[2] + weights[1] * val;
1577b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)          fir[2] = fir[3] + weights[2] * val;
1587b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)          fir[3] = fir[4] + weights[3] * val;
1597b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)          fir[4] =          weights[4] * val;
1607b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)
161c56d8851ea987023cc73981a70d261b3f6427545Alexei Podtelezhnikov (Алексей Подтележников)          col[pitch * 2]  = FT_SHIFTCLAMP( fir[0] );
1623cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner        }
1633cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner
164c56d8851ea987023cc73981a70d261b3f6427545Alexei Podtelezhnikov (Алексей Подтележников)        col[pitch * 2]  = FT_SHIFTCLAMP( fir[1] );
165c56d8851ea987023cc73981a70d261b3f6427545Alexei Podtelezhnikov (Алексей Подтележников)        col[pitch]      = FT_SHIFTCLAMP( fir[2] );
1663cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner      }
1673cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner    }
1683cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner  }
1693cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner
1703cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner
1718765c71b41625406fe87598645d842ca8451983cDavid Turner#ifdef USE_LEGACY
1720d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg
1734c93399155e88c415df05a46a14625ebf95c5ddcWerner Lemberg  /* intra-pixel filter used by the legacy filter */
1748765c71b41625406fe87598645d842ca8451983cDavid Turner  static void
1758765c71b41625406fe87598645d842ca8451983cDavid Turner  _ft_lcd_filter_legacy( FT_Bitmap*      bitmap,
1768765c71b41625406fe87598645d842ca8451983cDavid Turner                         FT_Render_Mode  mode,
1772e9519885b8e7e5fdc9d75b35992ef7bbf15e53aNikolaus Waxweiler                         FT_Byte*        weights )
1788765c71b41625406fe87598645d842ca8451983cDavid Turner  {
1797b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)    FT_UInt   width  = (FT_UInt)bitmap->width;
1807b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)    FT_UInt   height = (FT_UInt)bitmap->rows;
1817b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)    FT_Int    pitch  = bitmap->pitch;
1827b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)    FT_Byte*  origin = bitmap->buffer;
1838765c71b41625406fe87598645d842ca8451983cDavid Turner
18476abc75c2637cffc692fee6b16396c8f56091252Werner Lemberg    static const unsigned int  filters[3][3] =
1858765c71b41625406fe87598645d842ca8451983cDavid Turner    {
1860d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg      { 65538 * 9/13, 65538 * 1/6, 65538 * 1/13 },
1870d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg      { 65538 * 3/13, 65538 * 4/6, 65538 * 3/13 },
1880d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg      { 65538 * 1/13, 65538 * 1/6, 65538 * 9/13 }
1898765c71b41625406fe87598645d842ca8451983cDavid Turner    };
1908765c71b41625406fe87598645d842ca8451983cDavid Turner
1912e9519885b8e7e5fdc9d75b35992ef7bbf15e53aNikolaus Waxweiler    FT_UNUSED( weights );
1920d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg
1938765c71b41625406fe87598645d842ca8451983cDavid Turner
1947b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)    /* take care of bitmap flow */
1957b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)    if ( pitch > 0 )
1967b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)      origin += pitch * (FT_Int)( height - 1 );
1977b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)
1984c93399155e88c415df05a46a14625ebf95c5ddcWerner Lemberg    /* horizontal in-place intra-pixel filter */
1998765c71b41625406fe87598645d842ca8451983cDavid Turner    if ( mode == FT_RENDER_MODE_LCD && width >= 3 )
2008765c71b41625406fe87598645d842ca8451983cDavid Turner    {
2017b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)      FT_Byte*  line = origin;
2028765c71b41625406fe87598645d842ca8451983cDavid Turner
2038765c71b41625406fe87598645d842ca8451983cDavid Turner
2047b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)      for ( ; height > 0; height--, line -= pitch )
2058765c71b41625406fe87598645d842ca8451983cDavid Turner      {
2068765c71b41625406fe87598645d842ca8451983cDavid Turner        FT_UInt  xx;
2078765c71b41625406fe87598645d842ca8451983cDavid Turner
2080d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg
2098765c71b41625406fe87598645d842ca8451983cDavid Turner        for ( xx = 0; xx < width; xx += 3 )
2108765c71b41625406fe87598645d842ca8451983cDavid Turner        {
2117b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)          FT_UInt  r, g, b;
2128765c71b41625406fe87598645d842ca8451983cDavid Turner          FT_UInt  p;
2138765c71b41625406fe87598645d842ca8451983cDavid Turner
2140d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg
2158765c71b41625406fe87598645d842ca8451983cDavid Turner          p  = line[xx];
2167b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)          r  = filters[0][0] * p;
2177b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)          g  = filters[0][1] * p;
2187b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)          b  = filters[0][2] * p;
2190d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg
2200d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg          p  = line[xx + 1];
2210d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg          r += filters[1][0] * p;
2220d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg          g += filters[1][1] * p;
2230d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg          b += filters[1][2] * p;
2240d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg
2250d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg          p  = line[xx + 2];
2260d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg          r += filters[2][0] * p;
2270d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg          g += filters[2][1] * p;
2280d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg          b += filters[2][2] * p;
2290d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg
2300d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg          line[xx]     = (FT_Byte)( r / 65536 );
2310d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg          line[xx + 1] = (FT_Byte)( g / 65536 );
2320d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg          line[xx + 2] = (FT_Byte)( b / 65536 );
2338765c71b41625406fe87598645d842ca8451983cDavid Turner        }
2348765c71b41625406fe87598645d842ca8451983cDavid Turner      }
2358765c71b41625406fe87598645d842ca8451983cDavid Turner    }
2368765c71b41625406fe87598645d842ca8451983cDavid Turner    else if ( mode == FT_RENDER_MODE_LCD_V && height >= 3 )
2378765c71b41625406fe87598645d842ca8451983cDavid Turner    {
2387b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)      FT_Byte*  column = origin;
2398765c71b41625406fe87598645d842ca8451983cDavid Turner
2409154dab8155e7bb5865dd5041fc4ef7fc5450c98Werner Lemberg
2418765c71b41625406fe87598645d842ca8451983cDavid Turner      for ( ; width > 0; width--, column++ )
2428765c71b41625406fe87598645d842ca8451983cDavid Turner      {
2437b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)        FT_Byte*  col = column - 2 * pitch;
2440d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg
2458765c71b41625406fe87598645d842ca8451983cDavid Turner
2467b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)        for ( ; height > 0; height -= 3, col -= 3 * pitch )
2478765c71b41625406fe87598645d842ca8451983cDavid Turner        {
2487b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)          FT_UInt  r, g, b;
2498765c71b41625406fe87598645d842ca8451983cDavid Turner          FT_UInt  p;
2508765c71b41625406fe87598645d842ca8451983cDavid Turner
2510d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg
2528765c71b41625406fe87598645d842ca8451983cDavid Turner          p  = col[0];
2537b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)          r  = filters[0][0] * p;
2547b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)          g  = filters[0][1] * p;
2557b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)          b  = filters[0][2] * p;
2560d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg
2570d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg          p  = col[pitch];
2580d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg          r += filters[1][0] * p;
2590d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg          g += filters[1][1] * p;
2600d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg          b += filters[1][2] * p;
2610d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg
2620d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg          p  = col[pitch * 2];
2630d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg          r += filters[2][0] * p;
2640d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg          g += filters[2][1] * p;
2650d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg          b += filters[2][2] * p;
2660d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg
2670d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg          col[0]         = (FT_Byte)( r / 65536 );
2680d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg          col[pitch]     = (FT_Byte)( g / 65536 );
2697b7278334cf3da01bb955f8ec0a33259a03dbb38Alexei Podtelezhnikov (Алексей Подтележников)          col[pitch * 2] = (FT_Byte)( b / 65536 );
2708765c71b41625406fe87598645d842ca8451983cDavid Turner        }
2718765c71b41625406fe87598645d842ca8451983cDavid Turner      }
2728765c71b41625406fe87598645d842ca8451983cDavid Turner    }
2738765c71b41625406fe87598645d842ca8451983cDavid Turner  }
2740d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg
2758765c71b41625406fe87598645d842ca8451983cDavid Turner#endif /* USE_LEGACY */
2768765c71b41625406fe87598645d842ca8451983cDavid Turner
2770d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg
27852cd0fc8b9a76b4bee8d4fab7aa0409a85900970Werner Lemberg  FT_EXPORT_DEF( FT_Error )
279460d23f1689f14f5812bfe4b322fd3f2a449905eWerner Lemberg  FT_Library_SetLcdFilterWeights( FT_Library      library,
280460d23f1689f14f5812bfe4b322fd3f2a449905eWerner Lemberg                                  unsigned char  *weights )
281460d23f1689f14f5812bfe4b322fd3f2a449905eWerner Lemberg  {
282f4f9e6880036304912c9da39fa94a343131fdb82Werner Lemberg    if ( !library )
283f4f9e6880036304912c9da39fa94a343131fdb82Werner Lemberg      return FT_THROW( Invalid_Library_Handle );
284f4f9e6880036304912c9da39fa94a343131fdb82Werner Lemberg
285f4f9e6880036304912c9da39fa94a343131fdb82Werner Lemberg    if ( !weights )
286059bc335ce42220b222763379e89d0cbf2b949ebWerner Lemberg      return FT_THROW( Invalid_Argument );
287460d23f1689f14f5812bfe4b322fd3f2a449905eWerner Lemberg
2882e9519885b8e7e5fdc9d75b35992ef7bbf15e53aNikolaus Waxweiler    ft_memcpy( library->lcd_weights, weights, FT_LCD_FILTER_FIVE_TAPS );
2892e9519885b8e7e5fdc9d75b35992ef7bbf15e53aNikolaus Waxweiler    library->lcd_filter_func = ft_lcd_filter_fir;
290460d23f1689f14f5812bfe4b322fd3f2a449905eWerner Lemberg
291460d23f1689f14f5812bfe4b322fd3f2a449905eWerner Lemberg    return FT_Err_Ok;
292460d23f1689f14f5812bfe4b322fd3f2a449905eWerner Lemberg  }
293460d23f1689f14f5812bfe4b322fd3f2a449905eWerner Lemberg
294460d23f1689f14f5812bfe4b322fd3f2a449905eWerner Lemberg
295460d23f1689f14f5812bfe4b322fd3f2a449905eWerner Lemberg  FT_EXPORT_DEF( FT_Error )
296460d23f1689f14f5812bfe4b322fd3f2a449905eWerner Lemberg  FT_Library_SetLcdFilter( FT_Library    library,
297460d23f1689f14f5812bfe4b322fd3f2a449905eWerner Lemberg                           FT_LcdFilter  filter )
2983cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner  {
2992e9519885b8e7e5fdc9d75b35992ef7bbf15e53aNikolaus Waxweiler    static const FT_LcdFiveTapFilter  default_weights =
3002e9519885b8e7e5fdc9d75b35992ef7bbf15e53aNikolaus Waxweiler                   { 0x08, 0x4d, 0x56, 0x4d, 0x08 };
3012e9519885b8e7e5fdc9d75b35992ef7bbf15e53aNikolaus Waxweiler    static const FT_LcdFiveTapFilter  light_weights =
3022e9519885b8e7e5fdc9d75b35992ef7bbf15e53aNikolaus Waxweiler                   { 0x00, 0x55, 0x56, 0x55, 0x00 };
3030d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg
3043cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner
305460d23f1689f14f5812bfe4b322fd3f2a449905eWerner Lemberg    if ( !library )
306f4f9e6880036304912c9da39fa94a343131fdb82Werner Lemberg      return FT_THROW( Invalid_Library_Handle );
3073cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner
3088765c71b41625406fe87598645d842ca8451983cDavid Turner    switch ( filter )
3093cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner    {
3100d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg    case FT_LCD_FILTER_NONE:
3110d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg      library->lcd_filter_func = NULL;
3120d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg      break;
3130d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg
3140d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg    case FT_LCD_FILTER_DEFAULT:
3152e9519885b8e7e5fdc9d75b35992ef7bbf15e53aNikolaus Waxweiler      ft_memcpy( library->lcd_weights,
3162e9519885b8e7e5fdc9d75b35992ef7bbf15e53aNikolaus Waxweiler                 default_weights,
3172e9519885b8e7e5fdc9d75b35992ef7bbf15e53aNikolaus Waxweiler                 FT_LCD_FILTER_FIVE_TAPS );
3182e9519885b8e7e5fdc9d75b35992ef7bbf15e53aNikolaus Waxweiler      library->lcd_filter_func = ft_lcd_filter_fir;
3190d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg      break;
3200d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg
3210d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg    case FT_LCD_FILTER_LIGHT:
3222e9519885b8e7e5fdc9d75b35992ef7bbf15e53aNikolaus Waxweiler      ft_memcpy( library->lcd_weights,
3232e9519885b8e7e5fdc9d75b35992ef7bbf15e53aNikolaus Waxweiler                 light_weights,
3242e9519885b8e7e5fdc9d75b35992ef7bbf15e53aNikolaus Waxweiler                 FT_LCD_FILTER_FIVE_TAPS );
3252e9519885b8e7e5fdc9d75b35992ef7bbf15e53aNikolaus Waxweiler      library->lcd_filter_func = ft_lcd_filter_fir;
3260d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg      break;
3278765c71b41625406fe87598645d842ca8451983cDavid Turner
3288765c71b41625406fe87598645d842ca8451983cDavid Turner#ifdef USE_LEGACY
3290d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg
3300d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg    case FT_LCD_FILTER_LEGACY:
331b96af12eb646534ab4d112e25210bd88812ee420Werner Lemberg    case FT_LCD_FILTER_LEGACY1:
3320d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg      library->lcd_filter_func = _ft_lcd_filter_legacy;
3330d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg      break;
3340d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg
3358765c71b41625406fe87598645d842ca8451983cDavid Turner#endif
3360d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg
3370d0d78dadc9ae38aa0aafbddb5f08d381b7d27ccWerner Lemberg    default:
338059bc335ce42220b222763379e89d0cbf2b949ebWerner Lemberg      return FT_THROW( Invalid_Argument );
3393cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner    }
3403cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner
341460d23f1689f14f5812bfe4b322fd3f2a449905eWerner Lemberg    return FT_Err_Ok;
3423cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner  }
3433cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner
3442c002e13a74ab89b2ad0305088ed18660d5952abWerner Lemberg#else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
3453cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner
34661d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)  /* add padding according to accommodate outline shifts */
34761d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)  FT_BASE_DEF (void)
34861d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)  ft_lcd_padding( FT_Pos*       Min,
34961d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)                  FT_Pos*       Max,
35061d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)                  FT_GlyphSlot  slot )
3512e9519885b8e7e5fdc9d75b35992ef7bbf15e53aNikolaus Waxweiler  {
35261d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)    FT_UNUSED( slot );
35361d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)
35461d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)    *Min -= 21;
35561d1818b5ef4fd9b014c4577f07cf5580ba67eeeAlexei Podtelezhnikov (Алексей Подтележников)    *Max += 21;
3562e9519885b8e7e5fdc9d75b35992ef7bbf15e53aNikolaus Waxweiler  }
3572e9519885b8e7e5fdc9d75b35992ef7bbf15e53aNikolaus Waxweiler
3582e9519885b8e7e5fdc9d75b35992ef7bbf15e53aNikolaus Waxweiler
35952cd0fc8b9a76b4bee8d4fab7aa0409a85900970Werner Lemberg  FT_EXPORT_DEF( FT_Error )
360460d23f1689f14f5812bfe4b322fd3f2a449905eWerner Lemberg  FT_Library_SetLcdFilterWeights( FT_Library      library,
361460d23f1689f14f5812bfe4b322fd3f2a449905eWerner Lemberg                                  unsigned char  *weights )
362460d23f1689f14f5812bfe4b322fd3f2a449905eWerner Lemberg  {
363460d23f1689f14f5812bfe4b322fd3f2a449905eWerner Lemberg    FT_UNUSED( library );
364460d23f1689f14f5812bfe4b322fd3f2a449905eWerner Lemberg    FT_UNUSED( weights );
365460d23f1689f14f5812bfe4b322fd3f2a449905eWerner Lemberg
366059bc335ce42220b222763379e89d0cbf2b949ebWerner Lemberg    return FT_THROW( Unimplemented_Feature );
367460d23f1689f14f5812bfe4b322fd3f2a449905eWerner Lemberg  }
368460d23f1689f14f5812bfe4b322fd3f2a449905eWerner Lemberg
369460d23f1689f14f5812bfe4b322fd3f2a449905eWerner Lemberg
370460d23f1689f14f5812bfe4b322fd3f2a449905eWerner Lemberg  FT_EXPORT_DEF( FT_Error )
3718765c71b41625406fe87598645d842ca8451983cDavid Turner  FT_Library_SetLcdFilter( FT_Library    library,
3728765c71b41625406fe87598645d842ca8451983cDavid Turner                           FT_LcdFilter  filter )
3733cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner  {
3742c002e13a74ab89b2ad0305088ed18660d5952abWerner Lemberg    FT_UNUSED( library );
3758765c71b41625406fe87598645d842ca8451983cDavid Turner    FT_UNUSED( filter );
3763cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner
377059bc335ce42220b222763379e89d0cbf2b949ebWerner Lemberg    return FT_THROW( Unimplemented_Feature );
3783cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner  }
3793cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner
3802c002e13a74ab89b2ad0305088ed18660d5952abWerner Lemberg#endif /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
3812c002e13a74ab89b2ad0305088ed18660d5952abWerner Lemberg
3823cc7d630bdccc1f2ae4bbf97ba7fddcddeb470c5David Turner
3832c002e13a74ab89b2ad0305088ed18660d5952abWerner Lemberg/* END */
384