--- cairo-9999.new/src/cairo-ft-font.c 2010-08-06 21:51:21.603000022 +0300 +++ cairo-9999.new/src/cairo-ft-font.c 2010-08-06 21:52:00.926000031 +0300 @@ -57,6 +57,8 @@ #include FT_SYNTHESIS_H #endif +#define FIR_FILTER 1 + #if HAVE_FT_LIBRARY_SETLCDFILTER #include FT_LCD_FILTER_H #endif @@ -607,6 +609,8 @@ _cairo_ft_unscaled_font_destroy (void *a _cairo_ft_unscaled_font_fini (unscaled); } +static const int fir_filter[5] = { 0x1C, 0x38, 0x55, 0x38, 0x1C }; + static cairo_bool_t _has_unlocked_face (const void *entry) { @@ -1114,6 +1118,7 @@ _get_bitmap_surface (FT_Bitmap *bi unsigned char *data; int format = CAIRO_FORMAT_A8; cairo_image_surface_t *image; + cairo_bool_t subpixel = FALSE; width = bitmap->width; height = bitmap->rows; @@ -1183,7 +1188,220 @@ _get_bitmap_surface (FT_Bitmap *bi } format = CAIRO_FORMAT_A8; - } else { + } else +#ifdef FIR_FILTER + { + unsigned char* line; + unsigned char* bufBitmap; + int pitch; + unsigned char *data_rgba; + unsigned int width_rgba, stride_rgba; + int vmul = 1; + int hmul = 1; + + switch (font_options->subpixel_order) { + case CAIRO_SUBPIXEL_ORDER_DEFAULT: + case CAIRO_SUBPIXEL_ORDER_RGB: + case CAIRO_SUBPIXEL_ORDER_BGR: + default: + width /= 3; + hmul = 3; + break; + case CAIRO_SUBPIXEL_ORDER_VRGB: + case CAIRO_SUBPIXEL_ORDER_VBGR: + vmul = 3; + height /= 3; + break; + } + /* + * Filter the glyph to soften the color fringes + */ + width_rgba = width; + stride = bitmap->pitch; + stride_rgba = (width_rgba * 4 + 3) & ~3; + data_rgba = calloc (1, stride_rgba * height); + + /* perform in-place FIR filtering in either the horizontal or + * vertical direction. We're going to modify the RGB graymap, + * but that's ok, because we either own it, or its part of + * the FreeType glyph slot, which will not be used anymore. + */ + pitch = bitmap->pitch; + line = (unsigned char*)bitmap->buffer; + if ( pitch < 0 ) + line -= pitch*(height-1); + + bufBitmap = line; + + switch (font_options->subpixel_order) { + case CAIRO_SUBPIXEL_ORDER_DEFAULT: + case CAIRO_SUBPIXEL_ORDER_RGB: + case CAIRO_SUBPIXEL_ORDER_BGR: + { + int h; + + for ( h = height; h > 0; h--, line += pitch ) { + int pix[6] = { 0, 0, 0, 0, 0, 0 }; + unsigned char* p = line; + unsigned char* limit = line + width*3; + int nn, val, val2; + + val = p[0]; + for (nn = 0; nn < 3; nn++) + pix[2 + nn] += val * fir_filter[nn]; + + val = p[1]; + for (nn = 0; nn < 4; nn++) + pix[1 + nn] += val * fir_filter[nn]; + + p += 2; + + for ( ; p < limit; p++ ) { + val = p[0]; + for (nn = 0; nn < 5; nn++) + pix[nn] += val * fir_filter[nn]; + + val2 = pix[0] / 256; + val2 |= -(val2 >> 8); + p[-2] = (unsigned char)val2; + + for (nn = 0; nn < 5; nn++) + pix[nn] = pix[nn + 1]; + } + for (nn = 0; nn < 2; nn++ ) { + val2 = pix[nn] / 256; + val2 |= -(val2 >> 8); + p[nn - 2] = (unsigned char)val2; + } + } + } + break; + case CAIRO_SUBPIXEL_ORDER_VRGB: + case CAIRO_SUBPIXEL_ORDER_VBGR: + { + int w; + + for (w = 0; w < width; w++ ) { + int pix[6] = { 0, 0, 0, 0, 0, 0 }; + unsigned char* p = bufBitmap + w; + unsigned char* limit = bufBitmap + w + height*3*pitch; + int nn, val, val2; + + val = p[0]; + for (nn = 0; nn < 3; nn++) + pix[2 + nn] += val*fir_filter[nn]; + + val = p[pitch]; + for (nn = 0; nn < 4; nn++ ) + pix[1 + nn] += val * fir_filter[nn]; + + p += 2*pitch; + for ( ; p < limit; p += pitch ) { + val = p[0]; + for (nn = 0; nn < 5; nn++ ) + pix[nn] += val * fir_filter[nn]; + + val2 = pix[0] / 256; + val2 |= -(val2 >> 8); + p[-2 * pitch] = (unsigned char)val2; + + for (nn = 0; nn < 5; nn++) + pix[nn] = pix[nn+1]; + } + + for (nn = 0; nn < 2; nn++) { + val2 = pix[nn] / 256; + val2 |= -(val2 >> 8); + p[(nn - 2) * pitch] = (unsigned char)val2; + } + } + } + break; + default: /* shouldn't happen */ + break; + } + + /* now copy the resulting graymap into an ARGB32 image */ + { + unsigned char* in_line = bufBitmap; + unsigned char* out_line = data_rgba; + int h = height; + + switch (font_options->subpixel_order) { + case CAIRO_SUBPIXEL_ORDER_DEFAULT: + case CAIRO_SUBPIXEL_ORDER_RGB: + for ( ; h > 0; h--, in_line += pitch, out_line += stride_rgba) { + unsigned char* in = in_line; + int* out = (int*)out_line; + int w; + + for (w = width; w > 0; w--, in += 3, out += 1) { + int r = in[0]; + int g = in[1]; + int b = in[2]; + + out[0] = (g << 24) | (r << 16) | (g << 8) | b; + } + } + break; + case CAIRO_SUBPIXEL_ORDER_BGR: + for ( ; h > 0; h--, in_line += pitch, out_line += stride_rgba) { + unsigned char* in = in_line; + int* out = (int*)out_line; + int w; + + for (w = width; w > 0; w--, in += 3, out += 1) { + int r = in[2]; + int g = in[1]; + int b = in[0]; + + out[0] = (g << 24) | (r << 16) | (g << 8) | b; + } + } + break; + case CAIRO_SUBPIXEL_ORDER_VRGB: + for ( ; h > 0; h--, in_line += pitch*3, out_line += stride_rgba) { + unsigned char* in = in_line; + int* out = (int*)out_line; + int w; + + for (w = width; w > 0; w--, in += 1, out += 1) { + int r = in[0]; + int g = in[pitch]; + int b = in[pitch*2]; + + out[0] = (g << 24) | (r << 16) | (g << 8) | b; + } + } + break; + case CAIRO_SUBPIXEL_ORDER_VBGR: + for ( ; h > 0; h--, in_line += pitch*3, out_line += stride_rgba) { + unsigned char* in = in_line; + int* out = (int*)out_line; + int w; + + for (w = width; w > 0; w--, in += 1, out += 1) { + int r = in[2*pitch]; + int g = in[pitch]; + int b = in[0]; + + out[0] = (g << 24) | (r << 16) | (g << 8) | b; + } + } + break; + } + } + + if (own_buffer) + free (bitmap->buffer); + data = data_rgba; + stride = stride_rgba; + format = CAIRO_FORMAT_ARGB32; + subpixel = TRUE; + break; + } +#else /* !FIR_FILTER */ + { /* if we get there, the data from the source bitmap * really comes from _fill_xrender_bitmap, and is * made of 32-bit ARGB or ABGR values */ @@ -1194,6 +1412,7 @@ _get_bitmap_surface (FT_Bitmap *bi stride = bitmap->pitch; format = CAIRO_FORMAT_ARGB32; } +#endif /* !FIR_FILTER */ break; case FT_PIXEL_MODE_GRAY2: case FT_PIXEL_MODE_GRAY4: @@ -1244,59 +1463,16 @@ _render_glyph_outline (FT_Face cairo_font_options_t *font_options, cairo_image_surface_t **surface) { - int rgba = FC_RGBA_UNKNOWN; - int lcd_filter = FT_LCD_FILTER_LEGACY; - FT_GlyphSlot glyphslot = face->glyph; + FT_GlyphSlot glyphslot = face->glyph; FT_Outline *outline = &glyphslot->outline; FT_Bitmap bitmap; FT_BBox cbox; - unsigned int width, height; + FT_Matrix matrix; + int hmul = 1; + int vmul = 1; + unsigned int width, height, stride; + cairo_bool_t subpixel = FALSE; cairo_status_t status; - FT_Error fterror; - FT_Library library = glyphslot->library; - FT_Render_Mode render_mode = FT_RENDER_MODE_NORMAL; - - switch (font_options->antialias) { - case CAIRO_ANTIALIAS_NONE: - render_mode = FT_RENDER_MODE_MONO; - break; - - case CAIRO_ANTIALIAS_SUBPIXEL: - switch (font_options->subpixel_order) { - case CAIRO_SUBPIXEL_ORDER_DEFAULT: - case CAIRO_SUBPIXEL_ORDER_RGB: - case CAIRO_SUBPIXEL_ORDER_BGR: - render_mode = FT_RENDER_MODE_LCD; - break; - - case CAIRO_SUBPIXEL_ORDER_VRGB: - case CAIRO_SUBPIXEL_ORDER_VBGR: - render_mode = FT_RENDER_MODE_LCD_V; - break; - } - - switch (font_options->lcd_filter) { - case CAIRO_LCD_FILTER_NONE: - lcd_filter = FT_LCD_FILTER_NONE; - break; - case CAIRO_LCD_FILTER_DEFAULT: - case CAIRO_LCD_FILTER_INTRA_PIXEL: - lcd_filter = FT_LCD_FILTER_LEGACY; - break; - case CAIRO_LCD_FILTER_FIR3: - lcd_filter = FT_LCD_FILTER_LIGHT; - break; - case CAIRO_LCD_FILTER_FIR5: - lcd_filter = FT_LCD_FILTER_DEFAULT; - break; - } - - break; - - case CAIRO_ANTIALIAS_DEFAULT: - case CAIRO_ANTIALIAS_GRAY: - render_mode = FT_RENDER_MODE_NORMAL; - } FT_Outline_Get_CBox (outline, &cbox); @@ -1307,21 +1483,20 @@ _render_glyph_outline (FT_Face width = (unsigned int) ((cbox.xMax - cbox.xMin) >> 6); height = (unsigned int) ((cbox.yMax - cbox.yMin) >> 6); + stride = (width * hmul + 3) & ~3; if (width * height == 0) { cairo_format_t format; /* Looks like fb handles zero-sized images just fine */ - switch (render_mode) { - case FT_RENDER_MODE_MONO: + switch (font_options->antialias) { + case CAIRO_ANTIALIAS_NONE: format = CAIRO_FORMAT_A1; break; - case FT_RENDER_MODE_LCD: - case FT_RENDER_MODE_LCD_V: + case CAIRO_ANTIALIAS_SUBPIXEL: format= CAIRO_FORMAT_ARGB32; break; - case FT_RENDER_MODE_LIGHT: - case FT_RENDER_MODE_NORMAL: - case FT_RENDER_MODE_MAX: + case CAIRO_ANTIALIAS_DEFAULT: + case CAIRO_ANTIALIAS_GRAY: default: format = CAIRO_FORMAT_A8; break; @@ -1331,75 +1506,86 @@ _render_glyph_outline (FT_Face cairo_image_surface_create_for_data (NULL, format, 0, 0, 0); if ((*surface)->base.status) return (*surface)->base.status; - } else { + } else { - int bitmap_size; + matrix.xx = matrix.yy = 0x10000L; + matrix.xy = matrix.yx = 0; - switch (render_mode) { - case FT_RENDER_MODE_LCD: - if (font_options->subpixel_order == CAIRO_SUBPIXEL_ORDER_BGR) { - rgba = FC_RGBA_BGR; - } else { - rgba = FC_RGBA_RGB; - } - case FT_RENDER_MODE_LCD_V: - if (font_options->subpixel_order == CAIRO_SUBPIXEL_ORDER_VBGR) { - rgba = FC_RGBA_VBGR; - } else { - rgba = FC_RGBA_VRGB; - } - break; - case FT_RENDER_MODE_MONO: - case FT_RENDER_MODE_LIGHT: - case FT_RENDER_MODE_NORMAL: - case FT_RENDER_MODE_MAX: - default: + switch (font_options->antialias) { + case CAIRO_ANTIALIAS_NONE: + bitmap.pixel_mode = FT_PIXEL_MODE_MONO; + bitmap.num_grays = 1; + stride = ((width + 31) & -32) >> 3; + break; + case CAIRO_ANTIALIAS_DEFAULT: + case CAIRO_ANTIALIAS_GRAY: + bitmap.pixel_mode = FT_PIXEL_MODE_GRAY; + bitmap.num_grays = 256; + stride = (width + 3) & -4; break; - } - -#if HAVE_FT_LIBRARY_SETLCDFILTER - FT_Library_SetLcdFilter (library, lcd_filter); + case CAIRO_ANTIALIAS_SUBPIXEL: + switch (font_options->subpixel_order) { + case CAIRO_SUBPIXEL_ORDER_RGB: + case CAIRO_SUBPIXEL_ORDER_BGR: + case CAIRO_SUBPIXEL_ORDER_DEFAULT: + default: + matrix.xx *= 3; + hmul = 3; + subpixel = TRUE; +#ifdef FIR_FILTER + cbox.xMin -= 64; + cbox.xMax += 64; + width += 2; #endif - - fterror = FT_Render_Glyph (face->glyph, render_mode); - -#if HAVE_FT_LIBRARY_SETLCDFILTER - FT_Library_SetLcdFilter (library, FT_LCD_FILTER_NONE); + break; + case CAIRO_SUBPIXEL_ORDER_VRGB: + case CAIRO_SUBPIXEL_ORDER_VBGR: + matrix.yy *= 3; + vmul = 3; + subpixel = TRUE; +#ifdef FIR_FILTER + cbox.yMin -= 64; + cbox.yMax += 64; + height += 2; #endif + break; + } + FT_Outline_Transform (outline, &matrix); - if (fterror != 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + bitmap.pixel_mode = FT_PIXEL_MODE_GRAY; + bitmap.num_grays = 256; + stride = (width * hmul + 3) & -4; + } - bitmap_size = _compute_xrender_bitmap_size (&bitmap, - face->glyph, - render_mode); - if (bitmap_size < 0) + bitmap.pitch = stride; + bitmap.width = width * hmul; + bitmap.rows = height * vmul; + bitmap.buffer = calloc (stride, bitmap.rows); + if (bitmap.buffer == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - bitmap.buffer = calloc (1, bitmap_size); - if (bitmap.buffer == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + FT_Outline_Translate (outline, -cbox.xMin*hmul, -cbox.yMin*vmul); - _fill_xrender_bitmap (&bitmap, face->glyph, render_mode, - (rgba == FC_RGBA_BGR || rgba == FC_RGBA_VBGR)); + if (FT_Outline_Get_Bitmap (glyphslot->library, outline, &bitmap) != 0) { + free (bitmap.buffer); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } - /* Note: - * _get_bitmap_surface will free bitmap.buffer if there is an error - */ status = _get_bitmap_surface (&bitmap, TRUE, font_options, surface); - if (unlikely (status)) + if (status) return status; - - /* Note: the font's coordinate system is upside down from ours, so the - * Y coordinate of the control box needs to be negated. Moreover, device - * offsets are position of glyph origin relative to top left while xMin - * and yMax are offsets of top left relative to origin. Another negation. - */ - cairo_surface_set_device_offset (&(*surface)->base, - (double)-glyphslot->bitmap_left, - (double)+glyphslot->bitmap_top); } + /* + * Note: the font's coordinate system is upside down from ours, so the + * Y coordinate of the control box needs to be negated. Moreover, device + * offsets are position of glyph origin relative to top left while xMin + * and yMax are offsets of top left relative to origin. Another negation. + */ + cairo_surface_set_device_offset (&(*surface)->base, + floor (-(double) cbox.xMin / 64.0), + floor (+(double) cbox.yMax / 64.0)); + return CAIRO_STATUS_SUCCESS; }