summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolaus L Helper <nlhepler@gmail.com>2010-06-17 08:56:30 +0100
committerChris Wilson <chris@chris-wilson.co.uk>2010-06-17 09:06:13 +0100
commit7a023a62f7517ad0d54f4d59c99909fadcc05e82 (patch)
tree0598a5746ed67cbfea33a79c402efa2eb3e4060e
parent36b4b0631cc220d01c411b596a4eef839338cd7c (diff)
ft,fc,xlib: LCD filtering patch.
This adds internal API to retrieve the LCD filtering parameters from fontconfig, or as set on the Screen, and feed them to FreeType when rendering the glyph. References: Bug 10301 - LCD filtering patch https://bugs.freedesktop.org/show_bug.cgi?id=10301 Tested-by: Brandon Wright <bearoso@gmail.com> Forward-ported-by: Robert Hooker <sarvatt@gmail.cm> ickle: The API is clearly not ready for public consumption, the enum are poorly named, however this stands by itself as enabling system wide properties.
-rw-r--r--src/cairo-font-options.c51
-rw-r--r--src/cairo-ft-font.c675
-rw-r--r--src/cairo-surface.c1
-rw-r--r--src/cairo-types-private.h25
-rw-r--r--src/cairo-xlib-screen.c28
-rw-r--r--src/cairoint.h7
6 files changed, 597 insertions, 190 deletions
diff --git a/src/cairo-font-options.c b/src/cairo-font-options.c
index 0348d28fb..971f7d50d 100644
--- a/src/cairo-font-options.c
+++ b/src/cairo-font-options.c
@@ -40,6 +40,7 @@
static const cairo_font_options_t _cairo_font_options_nil = {
CAIRO_ANTIALIAS_DEFAULT,
CAIRO_SUBPIXEL_ORDER_DEFAULT,
+ CAIRO_LCD_FILTER_DEFAULT,
CAIRO_HINT_STYLE_DEFAULT,
CAIRO_HINT_METRICS_DEFAULT
};
@@ -55,6 +56,7 @@ _cairo_font_options_init_default (cairo_font_options_t *options)
{
options->antialias = CAIRO_ANTIALIAS_DEFAULT;
options->subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT;
+ options->lcd_filter = CAIRO_LCD_FILTER_DEFAULT;
options->hint_style = CAIRO_HINT_STYLE_DEFAULT;
options->hint_metrics = CAIRO_HINT_METRICS_DEFAULT;
}
@@ -65,6 +67,7 @@ _cairo_font_options_init_copy (cairo_font_options_t *options,
{
options->antialias = other->antialias;
options->subpixel_order = other->subpixel_order;
+ options->lcd_filter = other->lcd_filter;
options->hint_style = other->hint_style;
options->hint_metrics = other->hint_metrics;
}
@@ -190,6 +193,8 @@ cairo_font_options_merge (cairo_font_options_t *options,
options->antialias = other->antialias;
if (other->subpixel_order != CAIRO_SUBPIXEL_ORDER_DEFAULT)
options->subpixel_order = other->subpixel_order;
+ if (other->lcd_filter != CAIRO_LCD_FILTER_DEFAULT)
+ options->lcd_filter = other->lcd_filter;
if (other->hint_style != CAIRO_HINT_STYLE_DEFAULT)
options->hint_style = other->hint_style;
if (other->hint_metrics != CAIRO_HINT_METRICS_DEFAULT)
@@ -222,6 +227,7 @@ cairo_font_options_equal (const cairo_font_options_t *options,
return (options->antialias == other->antialias &&
options->subpixel_order == other->subpixel_order &&
+ options->lcd_filter == other->lcd_filter &&
options->hint_style == other->hint_style &&
options->hint_metrics == other->hint_metrics);
}
@@ -247,7 +253,8 @@ cairo_font_options_hash (const cairo_font_options_t *options)
return ((options->antialias) |
(options->subpixel_order << 4) |
- (options->hint_style << 8) |
+ (options->lcd_filter << 8) |
+ (options->hint_style << 12) |
(options->hint_metrics << 16));
}
slim_hidden_def (cairo_font_options_hash);
@@ -329,6 +336,48 @@ cairo_font_options_get_subpixel_order (const cairo_font_options_t *options)
}
/**
+ * _cairo_font_options_set_lcd_filter:
+ * @options: a #cairo_font_options_t
+ * @lcd_filter: the new LCD filter
+ *
+ * Sets the LCD filter for the font options object. The LCD filter
+ * specifies how pixels are filtered when rendered with an antialiasing
+ * mode of %CAIRO_ANTIALIAS_SUBPIXEL. See the documentation for
+ * #cairo_lcd_filter_t for full details.
+ *
+ * Since: 1.8
+ **/
+void
+_cairo_font_options_set_lcd_filter (cairo_font_options_t *options,
+ cairo_lcd_filter_t lcd_filter)
+{
+ if (cairo_font_options_status (options))
+ return;
+
+ options->lcd_filter = lcd_filter;
+}
+
+/**
+ * _cairo_font_options_get_lcd_filter:
+ * @options: a #cairo_font_options_t
+ *
+ * Gets the LCD filter for the font options object.
+ * See the documentation for #cairo_lcd_filter_t for full details.
+ *
+ * Return value: the LCD filter for the font options object
+ *
+ * Since: 1.8
+ **/
+cairo_lcd_filter_t
+_cairo_font_options_get_lcd_filter (const cairo_font_options_t *options)
+{
+ if (cairo_font_options_status ((cairo_font_options_t *) options))
+ return CAIRO_LCD_FILTER_DEFAULT;
+
+ return options->lcd_filter;
+}
+
+/**
* cairo_font_options_set_hint_style:
* @options: a #cairo_font_options_t
* @hint_style: the new hint style
diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c
index ae0974098..d3b9660d4 100644
--- a/src/cairo-ft-font.c
+++ b/src/cairo-ft-font.c
@@ -60,6 +60,30 @@
#include FT_SYNTHESIS_H
#endif
+#if HAVE_FT_LIBRARY_SETLCDFILTER
+#include FT_LCD_FILTER_H
+#endif
+
+/* Fontconfig version older than 2.6 didn't have these options */
+#ifndef FC_LCD_FILTER
+#define FC_LCD_FILTER "lcdfilter"
+#endif
+/* Some Ubuntu versions defined FC_LCD_FILTER without defining the following */
+#ifndef FC_LCD_NONE
+#define FC_LCD_NONE 0
+#define FC_LCD_DEFAULT 1
+#define FC_LCD_LIGHT 2
+#define FC_LCD_LEGACY 3
+#endif
+
+/* FreeType version older than 2.3.5(?) didn't have these options */
+#ifndef FT_LCD_FILTER_NONE
+#define FT_LCD_FILTER_NONE 0
+#define FT_LCD_FILTER_DEFAULT 1
+#define FT_LCD_FILTER_LIGHT 2
+#define FT_LCD_FILTER_LEGACY 16
+#endif
+
#define DOUBLE_TO_26_6(d) ((FT_F26Dot6)((d) * 64.0))
#define DOUBLE_FROM_26_6(t) ((double)(t) / 64.0)
#define DOUBLE_TO_16_16(d) ((FT_Fixed)((d) * 65536.0))
@@ -177,6 +201,7 @@ typedef struct _cairo_ft_unscaled_font_map {
static cairo_ft_unscaled_font_map_t *cairo_ft_unscaled_font_map = NULL;
+
static void
_font_map_release_face_lock_held (cairo_ft_unscaled_font_map_t *font_map,
cairo_ft_unscaled_font_t *unscaled)
@@ -774,23 +799,286 @@ _cairo_ft_unscaled_font_set_scale (cairo_ft_unscaled_font_t *unscaled,
return CAIRO_STATUS_SUCCESS;
}
-/* Empirically-derived subpixel filtering values thanks to Keith
- * Packard and libXft. */
-static 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 },
+/* we sometimes need to convert the glyph bitmap in a FT_GlyphSlot
+ * into a different format. For example, we want to convert a
+ * FT_PIXEL_MODE_LCD or FT_PIXEL_MODE_LCD_V bitmap into a 32-bit
+ * ARGB or ABGR bitmap.
+ *
+ * this function prepares a target descriptor for this operation.
+ *
+ * input :: target bitmap descriptor. The function will set its
+ * 'width', 'rows' and 'pitch' fields, and only these
+ *
+ * slot :: the glyph slot containing the source bitmap. this
+ * function assumes that slot->format == FT_GLYPH_FORMAT_BITMAP
+ *
+ * mode :: the requested final rendering mode. supported values are
+ * MONO, NORMAL (i.e. gray), LCD and LCD_V
+ *
+ * the function returns the size in bytes of the corresponding buffer,
+ * it's up to the caller to allocate the corresponding memory block
+ * before calling _fill_xrender_bitmap
+ *
+ * it also returns -1 in case of error (e.g. incompatible arguments,
+ * like trying to convert a gray bitmap into a monochrome one)
+ */
+static int
+_compute_xrender_bitmap_size(FT_Bitmap *target,
+ FT_GlyphSlot slot,
+ FT_Render_Mode mode)
+{
+ FT_Bitmap *ftbit;
+ int width, height, pitch;
+
+ if (slot->format != FT_GLYPH_FORMAT_BITMAP)
+ return -1;
+
+ /* compute the size of the final bitmap */
+ ftbit = &slot->bitmap;
+
+ width = ftbit->width;
+ height = ftbit->rows;
+ pitch = (width + 3) & ~3;
+
+ switch (ftbit->pixel_mode) {
+ case FT_PIXEL_MODE_MONO:
+ if (mode == FT_RENDER_MODE_MONO) {
+ pitch = (((width + 31) & ~31) >> 3);
+ break;
+ }
+ /* fall-through */
+
+ case FT_PIXEL_MODE_GRAY:
+ if (mode == FT_RENDER_MODE_LCD ||
+ mode == FT_RENDER_MODE_LCD_V)
+ {
+ /* each pixel is replicated into a 32-bit ARGB value */
+ pitch = width * 4;
+ }
+ break;
+
+ case FT_PIXEL_MODE_LCD:
+ if (mode != FT_RENDER_MODE_LCD)
+ return -1;
+
+ /* horz pixel triplets are packed into 32-bit ARGB values */
+ width /= 3;
+ pitch = width * 4;
+ break;
+
+ case FT_PIXEL_MODE_LCD_V:
+ if (mode != FT_RENDER_MODE_LCD_V)
+ return -1;
+
+ /* vert pixel triplets are packed into 32-bit ARGB values */
+ height /= 3;
+ pitch = width * 4;
+ break;
+
+ default: /* unsupported source format */
+ return -1;
+ }
+
+ target->width = width;
+ target->rows = height;
+ target->pitch = pitch;
+ target->buffer = NULL;
+
+ return pitch * height;
+}
+
+/* this functions converts the glyph bitmap found in a FT_GlyphSlot
+ * into a different format (see _compute_xrender_bitmap_size)
+ *
+ * you should call this function after _compute_xrender_bitmap_size
+ *
+ * target :: target bitmap descriptor. Note that its 'buffer' pointer
+ * must point to memory allocated by the caller
+ *
+ * slot :: the glyph slot containing the source bitmap
+ *
+ * mode :: the requested final rendering mode
+ *
+ * bgr :: boolean, set if BGR or VBGR pixel ordering is needed
+ */
+static void
+_fill_xrender_bitmap(FT_Bitmap *target,
+ FT_GlyphSlot slot,
+ FT_Render_Mode mode,
+ int bgr)
+{
+ FT_Bitmap *ftbit = &slot->bitmap;
+ unsigned char *srcLine = ftbit->buffer;
+ unsigned char *dstLine = target->buffer;
+ int src_pitch = ftbit->pitch;
+ int width = target->width;
+ int height = target->rows;
+ int pitch = target->pitch;
+ int subpixel;
+ int h;
+
+ subpixel = (mode == FT_RENDER_MODE_LCD ||
+ mode == FT_RENDER_MODE_LCD_V);
+
+ if (src_pitch < 0)
+ srcLine -= src_pitch * (ftbit->rows - 1);
+
+ target->pixel_mode = ftbit->pixel_mode;
+
+ switch (ftbit->pixel_mode) {
+ case FT_PIXEL_MODE_MONO:
+ if (subpixel) {
+ /* convert mono to ARGB32 values */
+
+ for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) {
+ int x;
+
+ for (x = 0; x < width; x++) {
+ if (srcLine[(x >> 3)] & (0x80 >> (x & 7)))
+ ((unsigned int *) dstLine)[x] = 0xffffffffU;
+ }
+ }
+ target->pixel_mode = FT_PIXEL_MODE_LCD;
+
+ } else if (mode == FT_RENDER_MODE_NORMAL) {
+ /* convert mono to 8-bit gray */
+
+ for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) {
+ int x;
+
+ for (x = 0; x < width; x++) {
+ if (srcLine[(x >> 3)] & (0x80 >> (x & 7)))
+ dstLine[x] = 0xff;
+ }
+ }
+ target->pixel_mode = FT_PIXEL_MODE_GRAY;
+
+ } else {
+ /* copy mono to mono */
+
+ int bytes = (width + 7) >> 3;
+
+ for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch)
+ memcpy (dstLine, srcLine, bytes);
+ }
+ break;
+
+ case FT_PIXEL_MODE_GRAY:
+ if (subpixel) {
+ /* convert gray to ARGB32 values */
+
+ for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) {
+ int x;
+ unsigned int *dst = (unsigned int *) dstLine;
+
+ for (x = 0; x < width; x++) {
+ unsigned int pix = srcLine[x];
+
+ pix |= (pix << 8);
+ pix |= (pix << 16);
+
+ dst[x] = pix;
+ }
+ }
+ target->pixel_mode = FT_PIXEL_MODE_LCD;
+ } else {
+ /* copy gray into gray */
+
+ for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch)
+ memcpy (dstLine, srcLine, width);
+ }
+ break;
+
+ case FT_PIXEL_MODE_LCD:
+ if (!bgr) {
+ /* convert horizontal RGB into ARGB32 */
+
+ for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) {
+ int x;
+ unsigned char *src = srcLine;
+ unsigned int *dst = (unsigned int *) dstLine;
+
+ for (x = 0; x < width; x++, src += 3) {
+ unsigned int pix;
+
+ pix = ((unsigned int)src[0] << 16) |
+ ((unsigned int)src[1] << 8) |
+ ((unsigned int)src[2] ) |
+ ((unsigned int)src[1] << 24) ;
+
+ dst[x] = pix;
+ }
+ }
+ } else {
+ /* convert horizontal BGR into ARGB32 */
+
+ for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) {
+
+ int x;
+ unsigned char *src = srcLine;
+ unsigned int *dst = (unsigned int *) dstLine;
+
+ for (x = 0; x < width; x++, src += 3) {
+ unsigned int pix;
+
+ pix = ((unsigned int)src[2] << 16) |
+ ((unsigned int)src[1] << 8) |
+ ((unsigned int)src[0] ) |
+ ((unsigned int)src[1] << 24) ;
+
+ dst[x] = pix;
+ }
+ }
+ }
+ break;
+
+ default: /* FT_PIXEL_MODE_LCD_V */
+ /* convert vertical RGB into ARGB32 */
+ if (!bgr) {
+
+ for (h = height; h > 0; h--, srcLine += 3 * src_pitch, dstLine += pitch) {
+ int x;
+ unsigned char* src = srcLine;
+ unsigned int* dst = (unsigned int *) dstLine;
+
+ for (x = 0; x < width; x++, src += 1) {
+ unsigned int pix;
+#if 1
+ pix = ((unsigned int)src[0] << 16) |
+ ((unsigned int)src[src_pitch] << 8) |
+ ((unsigned int)src[src_pitch*2] ) |
+ 0xFF000000 ;
+#else
+ pix = ((unsigned int)src[0] << 16) |
+ ((unsigned int)src[src_pitch] << 8) |
+ ((unsigned int)src[src_pitch*2] ) |
+ ((unsigned int)src[src_pitch] << 24) ;
#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 },
-};
+ dst[x] = pix;
+ }
+ }
+ } else {
+
+ for (h = height; h > 0; h--, srcLine += 3*src_pitch, dstLine += pitch) {
+ int x;
+ unsigned char *src = srcLine;
+ unsigned int *dst = (unsigned int *) dstLine;
+
+ for (x = 0; x < width; x++, src += 1) {
+ unsigned int pix;
+
+ pix = ((unsigned int)src[src_pitch * 2] << 16) |
+ ((unsigned int)src[src_pitch] << 8) |
+ ((unsigned int)src[0] ) |
+ ((unsigned int)src[src_pitch] << 24) ;
+
+ dst[x] = pix;
+ }
+ }
+ }
+ }
+}
+
/* Fills in val->image with an image surface created from @bitmap
*/
@@ -803,6 +1091,7 @@ _get_bitmap_surface (FT_Bitmap *bitmap,
int width, height, stride;
unsigned char *data;
int format = CAIRO_FORMAT_A8;
+ cairo_image_surface_t *image;
width = bitmap->width;
height = bitmap->rows;
@@ -859,11 +1148,7 @@ _get_bitmap_surface (FT_Bitmap *bitmap,
case FT_PIXEL_MODE_LCD:
case FT_PIXEL_MODE_LCD_V:
case FT_PIXEL_MODE_GRAY:
- switch (font_options->antialias) {
- case CAIRO_ANTIALIAS_DEFAULT:
- case CAIRO_ANTIALIAS_GRAY:
- case CAIRO_ANTIALIAS_NONE:
- default:
+ if (font_options->antialias != CAIRO_ANTIALIAS_SUBPIXEL) {
stride = bitmap->pitch;
if (own_buffer) {
data = bitmap->buffer;
@@ -874,104 +1159,18 @@ _get_bitmap_surface (FT_Bitmap *bitmap,
memcpy (data, bitmap->buffer, stride * height);
}
- format = CAIRO_FORMAT_A8;
- break;
- case CAIRO_ANTIALIAS_SUBPIXEL: {
- 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 (unlikely (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
- */
+ 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);
- if (own_buffer)
- free (bitmap->buffer);
- data = data_rgba;
- stride = stride_rgba;
- format = CAIRO_FORMAT_ARGB32;
- break;
- }
+ data = bitmap->buffer;
+ stride = bitmap->pitch;
+ format = CAIRO_FORMAT_ARGB32;
}
break;
case FT_PIXEL_MODE_GRAY2:
@@ -983,21 +1182,22 @@ _get_bitmap_surface (FT_Bitmap *bitmap,
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
- *surface = (cairo_image_surface_t *)
+ /* XXX */
+ *surface = image = (cairo_image_surface_t *)
cairo_image_surface_create_for_data (data,
format,
width, height, stride);
- if ((*surface)->base.status) {
+ if (image->base.status) {
free (data);
return (*surface)->base.status;
}
if (format == CAIRO_FORMAT_ARGB32)
- pixman_image_set_component_alpha ((*surface)->pixman_image, TRUE);
+ pixman_image_set_component_alpha (image->pixman_image, TRUE);
- _cairo_image_surface_assume_ownership_of_data ((*surface));
+ _cairo_image_surface_assume_ownership_of_data (image);
- _cairo_debug_check_image_surface_is_defined (&(*surface)->base);
+ _cairo_debug_check_image_surface_is_defined (&image->base);
return CAIRO_STATUS_SUCCESS;
}
@@ -1022,16 +1222,59 @@ _render_glyph_outline (FT_Face 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_Outline *outline = &glyphslot->outline;
FT_Bitmap bitmap;
FT_BBox cbox;
- FT_Matrix matrix;
- int hmul = 1;
- int vmul = 1;
- unsigned int width, height, stride;
- cairo_bool_t subpixel = FALSE;
+ unsigned int width, height;
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);
@@ -1042,20 +1285,21 @@ _render_glyph_outline (FT_Face 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 (font_options->antialias) {
- case CAIRO_ANTIALIAS_NONE:
+ switch (render_mode) {
+ case FT_RENDER_MODE_MONO:
format = CAIRO_FORMAT_A1;
break;
- case CAIRO_ANTIALIAS_SUBPIXEL:
+ case FT_RENDER_MODE_LCD:
+ case FT_RENDER_MODE_LCD_V:
format= CAIRO_FORMAT_ARGB32;
break;
- case CAIRO_ANTIALIAS_DEFAULT:
- case CAIRO_ANTIALIAS_GRAY:
+ case FT_RENDER_MODE_LIGHT:
+ case FT_RENDER_MODE_NORMAL:
+ case FT_RENDER_MODE_MAX:
default:
format = CAIRO_FORMAT_A8;
break;
@@ -1065,75 +1309,74 @@ _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 {
- matrix.xx = matrix.yy = 0x10000L;
- matrix.xy = matrix.yx = 0;
+ int bitmap_size;
- switch (font_options->antialias) {
- case CAIRO_ANTIALIAS_NONE:
- bitmap.pixel_mode = FT_PIXEL_MODE_MONO;
- bitmap.num_grays = 1;
- stride = ((width + 31) & -32) >> 3;
+ 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 CAIRO_ANTIALIAS_DEFAULT:
- case CAIRO_ANTIALIAS_GRAY:
- bitmap.pixel_mode = FT_PIXEL_MODE_GRAY;
- bitmap.num_grays = 256;
- stride = (width + 3) & -4;
+ case FT_RENDER_MODE_MONO:
+ case FT_RENDER_MODE_LIGHT:
+ case FT_RENDER_MODE_NORMAL:
+ case FT_RENDER_MODE_MAX:
+ default:
break;
- 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;
- 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;
- }
+#if HAVE_FT_LIBRARY_SETLCDFILTER
+ FT_Library_SetLcdFilter (library, lcd_filter);
+#endif
- bitmap.pitch = stride;
- bitmap.width = width * hmul;
- bitmap.rows = height * vmul;
- bitmap.buffer = calloc (stride, bitmap.rows);
- if (unlikely (bitmap.buffer == NULL))
- return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ fterror = FT_Render_Glyph (face->glyph, render_mode);
+
+#if HAVE_FT_LIBRARY_SETLCDFILTER
+ FT_Library_SetLcdFilter (library, FT_LCD_FILTER_NONE);
+#endif
- FT_Outline_Translate (outline, -cbox.xMin*hmul, -cbox.yMin*vmul);
+ if (fterror != 0)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
- if (FT_Outline_Get_Bitmap (glyphslot->library, outline, &bitmap) != 0) {
- free (bitmap.buffer);
+ bitmap_size = _compute_xrender_bitmap_size (&bitmap,
+ face->glyph,
+ render_mode);
+ 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);
+
+ _fill_xrender_bitmap (&bitmap, face->glyph, render_mode,
+ (rgba == FC_RGBA_BGR || rgba == FC_RGBA_VBGR));
+
+ /* 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))
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,
- floor (-(double) cbox.xMin / 64.0),
- floor (+(double) cbox.yMax / 64.0));
+ /* 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);
+ }
return CAIRO_STATUS_SUCCESS;
}
@@ -1341,6 +1584,7 @@ _get_pattern_ft_options (FcPattern *pattern, cairo_ft_options_t *ret)
if (antialias) {
cairo_subpixel_order_t subpixel_order;
+ int lcd_filter;
/* disable hinting if requested */
if (FcPatternGetBool (pattern,
@@ -1376,6 +1620,25 @@ _get_pattern_ft_options (FcPattern *pattern, cairo_ft_options_t *ret)
ft_options.base.antialias = CAIRO_ANTIALIAS_SUBPIXEL;
}
+ if (FcPatternGetInteger (pattern,
+ FC_LCD_FILTER, 0, &lcd_filter) == FcResultMatch)
+ {
+ switch (lcd_filter) {
+ case FC_LCD_NONE:
+ ft_options.base.lcd_filter = CAIRO_LCD_FILTER_NONE;
+ break;
+ case FC_LCD_DEFAULT:
+ ft_options.base.lcd_filter = CAIRO_LCD_FILTER_FIR5;
+ break;
+ case FC_LCD_LIGHT:
+ ft_options.base.lcd_filter = CAIRO_LCD_FILTER_FIR3;
+ break;
+ case FC_LCD_LEGACY:
+ ft_options.base.lcd_filter = CAIRO_LCD_FILTER_INTRA_PIXEL;
+ break;
+ }
+ }
+
#ifdef FC_HINT_STYLE
if (FcPatternGetInteger (pattern,
FC_HINT_STYLE, 0, &hintstyle) != FcResultMatch)
@@ -1477,6 +1740,12 @@ _cairo_ft_options_merge (cairo_ft_options_t *options,
if (other->base.hint_style == CAIRO_HINT_STYLE_NONE)
options->base.hint_style = CAIRO_HINT_STYLE_NONE;
+ if (options->base.lcd_filter == CAIRO_LCD_FILTER_DEFAULT)
+ options->base.lcd_filter = other->base.lcd_filter;
+
+ if (other->base.lcd_filter == CAIRO_LCD_FILTER_NONE)
+ options->base.lcd_filter = CAIRO_LCD_FILTER_NONE;
+
if (options->base.antialias == CAIRO_ANTIALIAS_NONE) {
if (options->base.hint_style == CAIRO_HINT_STYLE_NONE)
load_flags |= FT_LOAD_NO_HINTING;
@@ -1500,11 +1769,11 @@ _cairo_ft_options_merge (cairo_ft_options_t *options,
case CAIRO_SUBPIXEL_ORDER_DEFAULT:
case CAIRO_SUBPIXEL_ORDER_RGB:
case CAIRO_SUBPIXEL_ORDER_BGR:
- load_target |= FT_LOAD_TARGET_LCD;
+ load_target = FT_LOAD_TARGET_LCD;
break;
case CAIRO_SUBPIXEL_ORDER_VRGB:
case CAIRO_SUBPIXEL_ORDER_VBGR:
- load_target |= FT_LOAD_TARGET_LCD_V;
+ load_target = FT_LOAD_TARGET_LCD_V;
break;
}
}
@@ -2503,6 +2772,34 @@ _cairo_ft_font_options_substitute (const cairo_font_options_t *options,
}
}
+ if (options->lcd_filter != CAIRO_LCD_FILTER_DEFAULT)
+ {
+ if (FcPatternGet (pattern, FC_LCD_FILTER, 0, &v) == FcResultNoMatch)
+ {
+ int lcd_filter;
+
+ switch (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;
+ default:
+ case CAIRO_LCD_FILTER_FIR5:
+ lcd_filter = FT_LCD_FILTER_DEFAULT;
+ break;
+ }
+
+ if (! FcPatternAddInteger (pattern, FC_LCD_FILTER, lcd_filter))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ }
+
if (options->hint_style != CAIRO_HINT_STYLE_DEFAULT)
{
if (FcPatternGet (pattern, FC_HINTING, 0, &v) == FcResultNoMatch)
diff --git a/src/cairo-surface.c b/src/cairo-surface.c
index 581e75561..8806f139e 100644
--- a/src/cairo-surface.c
+++ b/src/cairo-surface.c
@@ -74,6 +74,7 @@ const cairo_surface_t name = { \
{ NULL, NULL }, /* snapshot */ \
{ CAIRO_ANTIALIAS_DEFAULT, /* antialias */ \
CAIRO_SUBPIXEL_ORDER_DEFAULT, /* subpixel_order */ \
+ CAIRO_LCD_FILTER_DEFAULT, /* lcd_filter */ \
CAIRO_HINT_STYLE_DEFAULT, /* hint_style */ \
CAIRO_HINT_METRICS_DEFAULT /* hint_metrics */ \
} /* font_options */ \
diff --git a/src/cairo-types-private.h b/src/cairo-types-private.h
index 8b5a94b43..49fc7092a 100644
--- a/src/cairo-types-private.h
+++ b/src/cairo-types-private.h
@@ -129,9 +129,34 @@ struct _cairo_array {
cairo_bool_t is_snapshot;
};
+/**
+ * cairo_lcd_filter_t:
+ * @CAIRO_LCD_FILTER_DEFAULT: Use the default LCD filter for
+ * font backend and target device
+ * @CAIRO_LCD_FILTER_NONE: Do not perform LCD filtering
+ * @CAIRO_LCD_FILTER_INTRA_PIXEL: Intra-pixel filter
+ * @CAIRO_LCD_FILTER_FIR3: FIR filter with a 3x3 kernel
+ * @CAIRO_LCD_FILTER_FIR5: FIR filter with a 5x5 kernel
+ *
+ * The LCD filter specifies the low-pass filter applied to LCD-optimized
+ * bitmaps generated with an antialiasing mode of %CAIRO_ANTIALIAS_SUBPIXEL.
+ *
+ * Note: This API was temporarily made available in the public
+ * interface during the 1.7.x development series, but was made private
+ * before 1.8.
+ **/
+typedef enum _cairo_lcd_filter {
+ CAIRO_LCD_FILTER_DEFAULT,
+ CAIRO_LCD_FILTER_NONE,
+ CAIRO_LCD_FILTER_INTRA_PIXEL,
+ CAIRO_LCD_FILTER_FIR3,
+ CAIRO_LCD_FILTER_FIR5
+} cairo_lcd_filter_t;
+
struct _cairo_font_options {
cairo_antialias_t antialias;
cairo_subpixel_order_t subpixel_order;
+ cairo_lcd_filter_t lcd_filter;
cairo_hint_style_t hint_style;
cairo_hint_metrics_t hint_metrics;
};
diff --git a/src/cairo-xlib-screen.c b/src/cairo-xlib-screen.c
index b094eaf4c..db01a8433 100644
--- a/src/cairo-xlib-screen.c
+++ b/src/cairo-xlib-screen.c
@@ -152,13 +152,22 @@ _cairo_xlib_init_screen_font_options (Display *dpy,
cairo_bool_t xft_antialias;
int xft_hintstyle;
int xft_rgba;
+ int xft_lcdfilter;
cairo_antialias_t antialias;
cairo_subpixel_order_t subpixel_order;
+ cairo_lcd_filter_t lcd_filter;
cairo_hint_style_t hint_style;
if (!get_boolean_default (dpy, "antialias", &xft_antialias))
xft_antialias = TRUE;
+ if (!get_integer_default (dpy, "lcdfilter", &xft_lcdfilter)) {
+ /* -1 is an non-existant Fontconfig constant used to differentiate
+ * the case when no lcdfilter property is available.
+ */
+ xft_lcdfilter = -1;
+ }
+
if (!get_boolean_default (dpy, "hinting", &xft_hinting))
xft_hinting = TRUE;
@@ -241,6 +250,24 @@ _cairo_xlib_init_screen_font_options (Display *dpy,
subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT;
}
+ switch (xft_lcdfilter) {
+ case FC_LCD_NONE:
+ lcd_filter = CAIRO_LCD_FILTER_NONE;
+ break;
+ case FC_LCD_DEFAULT:
+ lcd_filter = CAIRO_LCD_FILTER_FIR5;
+ break;
+ case FC_LCD_LIGHT:
+ lcd_filter = CAIRO_LCD_FILTER_FIR3;
+ break;
+ case FC_LCD_LEGACY:
+ lcd_filter = CAIRO_LCD_FILTER_INTRA_PIXEL;
+ break;
+ default:
+ lcd_filter = CAIRO_LCD_FILTER_DEFAULT;
+ break;
+ }
+
if (xft_antialias) {
if (subpixel_order == CAIRO_SUBPIXEL_ORDER_DEFAULT)
antialias = CAIRO_ANTIALIAS_GRAY;
@@ -253,6 +280,7 @@ _cairo_xlib_init_screen_font_options (Display *dpy,
cairo_font_options_set_hint_style (&info->font_options, hint_style);
cairo_font_options_set_antialias (&info->font_options, antialias);
cairo_font_options_set_subpixel_order (&info->font_options, subpixel_order);
+ _cairo_font_options_set_lcd_filter (&info->font_options, lcd_filter);
cairo_font_options_set_hint_metrics (&info->font_options, CAIRO_HINT_METRICS_ON);
}
diff --git a/src/cairoint.h b/src/cairoint.h
index b3deee947..6ccc18f71 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1072,6 +1072,13 @@ cairo_private void
_cairo_font_options_init_copy (cairo_font_options_t *options,
const cairo_font_options_t *other);
+cairo_private void
+_cairo_font_options_set_lcd_filter (cairo_font_options_t *options,
+ cairo_lcd_filter_t lcd_filter);
+
+cairo_private cairo_lcd_filter_t
+_cairo_font_options_get_lcd_filter (const cairo_font_options_t *options);
+
/* cairo-hull.c */
cairo_private cairo_status_t
_cairo_hull_compute (cairo_pen_vertex_t *vertices, int *num_vertices);