diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c index 1ad7145..affc982 100644 --- a/src/cairo-ft-font.c +++ b/src/cairo-ft-font.c @@ -1048,12 +1048,13 @@ static cairo_status_t _get_bitmap_surface (FT_Bitmap *bitmap, cairo_bool_t own_buffer, cairo_font_options_t *font_options, - cairo_image_surface_t **surface) + cairo_image_surface_t **surface, + FT_Error lcd_error) { int width, height, stride; unsigned char *data; int format = CAIRO_FORMAT_A8; - cairo_image_surface_t *image; + cairo_bool_t subpixel = FALSE; width = bitmap->width; height = bitmap->rows; @@ -1110,29 +1111,146 @@ _get_bitmap_surface (FT_Bitmap *bitmap, case FT_PIXEL_MODE_LCD: case FT_PIXEL_MODE_LCD_V: case FT_PIXEL_MODE_GRAY: - if (font_options->antialias != CAIRO_ANTIALIAS_SUBPIXEL) { - stride = bitmap->pitch; - if (own_buffer) { - data = bitmap->buffer; + if (lcd_error == 0) { + + if (font_options->antialias != CAIRO_ANTIALIAS_SUBPIXEL) { + stride = bitmap->pitch; + if (own_buffer) { + data = bitmap->buffer; + } else { + data = _cairo_malloc_ab (height, stride); + if (!data) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (data, bitmap->buffer, stride * height); + } + format = CAIRO_FORMAT_A8; } else { - data = _cairo_malloc_ab (height, stride); - if (!data) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - memcpy (data, bitmap->buffer, stride * height); + /* 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 */ + assert (own_buffer != 0); + assert (bitmap->pixel_mode != FT_PIXEL_MODE_GRAY); + + data = bitmap->buffer; + stride = bitmap->pitch; + format = CAIRO_FORMAT_ARGB32; + subpixel = TRUE; } - format = CAIRO_FORMAT_A8; - } else { - /* 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 */ - assert (own_buffer != 0); - assert (bitmap->pixel_mode != FT_PIXEL_MODE_GRAY); - data = bitmap->buffer; + } else { + + const int filters[3][3] = { + /* red */ +#if 0 + { 65538*4/7,65538*2/7,65538*1/7 }, + /* green */ + { 65536*1/4, 65536*2/4, 65537*1/4 }, + /* blue */ + { 65538*1/7,65538*2/7,65538*4/7 }, +#endif + { 65538*9/13,65538*3/13,65538*1/13 }, + /* green */ + { 65538*1/6, 65538*4/6, 65538*1/6 }, + /* blue */ + { 65538*1/13,65538*3/13,65538*9/13 }, + }; + + int x, y; + unsigned char *in_line, *out_line, *in; + unsigned int *out; + unsigned int red, green, blue; + int rf, gf, bf; + int s; + int o, os; + 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 (stride_rgba, height); + if (data_rgba == NULL) { + if (own_buffer) + free (bitmap->buffer); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + os = 1; + switch (font_options->subpixel_order) { + case CAIRO_SUBPIXEL_ORDER_VRGB: + os = stride; + case CAIRO_SUBPIXEL_ORDER_DEFAULT: + case CAIRO_SUBPIXEL_ORDER_RGB: + default: + rf = 0; + gf = 1; + bf = 2; + break; + case CAIRO_SUBPIXEL_ORDER_VBGR: + os = stride; + case CAIRO_SUBPIXEL_ORDER_BGR: + bf = 0; + gf = 1; + rf = 2; + break; + } + in_line = bitmap->buffer; + out_line = data_rgba; + for (y = 0; y < height; y++) + { + in = in_line; + out = (unsigned int *) out_line; + in_line += stride * vmul; + out_line += stride_rgba; + for (x = 0; x < width * hmul; x += hmul) + { + red = green = blue = 0; + o = 0; + for (s = 0; s < 3; s++) + { + red += filters[rf][s]*in[x+o]; + green += filters[gf][s]*in[x+o]; + blue += filters[bf][s]*in[x+o]; + o += os; + } + red = red / 65536; + green = green / 65536; + blue = blue / 65536; + *out++ = (green << 24) | (red << 16) | (green << 8) | blue; + } + } + + /* Images here are stored in native format. The + * backend must convert to its own format as needed + */ + if (own_buffer) + free (bitmap->buffer); + data = data_rgba; + stride = stride_rgba; format = CAIRO_FORMAT_ARGB32; - } + subpixel = TRUE; + } + break; case FT_PIXEL_MODE_GRAY2: case FT_PIXEL_MODE_GRAY4: @@ -1143,22 +1261,20 @@ _get_bitmap_surface (FT_Bitmap *bitmap, return _cairo_error (CAIRO_STATUS_NO_MEMORY); } - /* XXX */ - *surface = image = (cairo_image_surface_t *) + *surface = (cairo_image_surface_t *) cairo_image_surface_create_for_data (data, format, width, height, stride); - if (image->base.status) { + if ((*surface)->base.status) { free (data); return (*surface)->base.status; } - if (font_options->antialias == CAIRO_ANTIALIAS_SUBPIXEL) - pixman_image_set_component_alpha (image->pixman_image, TRUE); + if (subpixel) + pixman_image_set_component_alpha ((*surface)->pixman_image, TRUE); - _cairo_image_surface_assume_ownership_of_data (image); + _cairo_image_surface_assume_ownership_of_data ((*surface)); - return CAIRO_STATUS_SUCCESS; } /* Converts an outline FT_GlyphSlot into an image @@ -1190,31 +1306,22 @@ _render_glyph_outline (FT_Face face, unsigned int width, height; cairo_status_t status; FT_Error fterror; + FT_Error lcd_error = FT_Err_Unimplemented_Feature; // Default this to off until we determine otherwise. 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; + lcd_error = 0; // If we're not antialiasing, then we don't need to worry about lcd filtering. 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; + lcd_error = 0; // If we're not lcd filtering, don't worry about it. break; case CAIRO_LCD_FILTER_DEFAULT: case CAIRO_LCD_FILTER_INTRA_PIXEL: @@ -1228,6 +1335,25 @@ _render_glyph_outline (FT_Face face, break; } +#if HAVE_FT_LIBRARY_SETLCDFILTER + lcd_error = FT_Library_SetLcdFilter (library, lcd_filter); +#endif + + if (lcd_error == 0) { + 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; + } + } + break; case CAIRO_ANTIALIAS_DEFAULT: @@ -1268,7 +1394,7 @@ _render_glyph_outline (FT_Face 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; @@ -1295,36 +1421,92 @@ _render_glyph_outline (FT_Face face, break; } -#if HAVE_FT_LIBRARY_SETLCDFILTER - FT_Library_SetLcdFilter (library, lcd_filter); -#endif + double device_offset_left, device_offset_top; + + if (lcd_error != 0) { + + FT_Matrix matrix; + int hmul = 1; + int vmul = 1; + unsigned int stride; + cairo_bool_t subpixel = FALSE; + + stride = (width * hmul + 3) & ~3; + + matrix.xx = matrix.yy = 0x10000L; + matrix.xy = matrix.yx = 0; + + 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; + break; + case CAIRO_SUBPIXEL_ORDER_VRGB: + case CAIRO_SUBPIXEL_ORDER_VBGR: + matrix.yy *= 3; + vmul = 3; + subpixel = TRUE; + break; + } + FT_Outline_Transform (outline, &matrix); + + bitmap.pixel_mode = FT_PIXEL_MODE_GRAY; + bitmap.num_grays = 256; + stride = (width * hmul + 3) & -4; + + 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); + + FT_Outline_Translate (outline, -cbox.xMin*hmul, -cbox.yMin*vmul); + + if (FT_Outline_Get_Bitmap (glyphslot->library, outline, &bitmap) != 0) { + free (bitmap.buffer); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + device_offset_left = floor (-(double) cbox.xMin / 64.0); + device_offset_top = floor (+(double) cbox.yMax / 64.0); + + } else { - fterror = FT_Render_Glyph (face->glyph, render_mode); + fterror = FT_Render_Glyph (face->glyph, render_mode); #if HAVE_FT_LIBRARY_SETLCDFILTER - FT_Library_SetLcdFilter (library, FT_LCD_FILTER_NONE); + FT_Library_SetLcdFilter (library, FT_LCD_FILTER_NONE); #endif - if (fterror != 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + if (fterror != 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); - bitmap_size = _compute_xrender_bitmap_size (&bitmap, + bitmap_size = _compute_xrender_bitmap_size (&bitmap, face->glyph, render_mode); - if (bitmap_size < 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + if (bitmap_size < 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); - bitmap.buffer = calloc (1, bitmap_size); - 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); - _fill_xrender_bitmap (&bitmap, face->glyph, render_mode, + _fill_xrender_bitmap (&bitmap, face->glyph, render_mode, (rgba == FC_RGBA_BGR || rgba == FC_RGBA_VBGR)); + device_offset_left = (double)-glyphslot->bitmap_left; + device_offset_top = (double)+glyphslot->bitmap_top; + } + /* Note: * _get_bitmap_surface will free bitmap.buffer if there is an error */ - status = _get_bitmap_surface (&bitmap, TRUE, font_options, surface); + status = _get_bitmap_surface (&bitmap, TRUE, font_options, surface, lcd_error); if (status) return status; @@ -1334,8 +1516,8 @@ _render_glyph_outline (FT_Face face, * 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); + device_offset_left, + device_offset_top); } return CAIRO_STATUS_SUCCESS; @@ -1364,7 +1546,7 @@ _render_glyph_bitmap (FT_Face face, if (error == FT_Err_Out_Of_Memory) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - status = _get_bitmap_surface (&glyphslot->bitmap, FALSE, font_options, surface); + status = _get_bitmap_surface (&glyphslot->bitmap, FALSE, font_options, surface, 0); if (status) return status;