bug 974575 - backport Behdad's patches for color bitmap glyph support from current cairo trunk. r=jrmuizel
authorJonathan Kew <jkew@mozilla.com>
Thu, 06 Mar 2014 12:29:22 +0000
changeset 189506 eeb213ed89b1561185bbbb7515d1a83f91188887
parent 189505 40658e50b8f961d4351411320e52e7c1060bfdd8
child 189507 0629688842561ff9bc68eea6eb6994748153b8b6
push id3503
push userraliiev@mozilla.com
push dateMon, 28 Apr 2014 18:51:11 +0000
treeherdermozilla-beta@c95ac01e332e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs974575
milestone30.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 974575 - backport Behdad's patches for color bitmap glyph support from current cairo trunk. r=jrmuizel * * * [ft] Fix resizing of bitmap fonts From b94a519aad3d5b50aa6de47ee16ee6a099de9791 Mon Sep 17 00:00:00 2001 Say, you were asking cairo for a font at 200px. For bitmap-only fonts, cairo was finding the closes strike size and using it. If the strike was at 20px, well, that's what you were getting. We now scale that 20px strike by a factor of 10 to get the correct size rendering. Note that by itself this patch doesn't change much on the Linux desktop. The reason is that the size you are interested in (eg. 200px) is lost by fontconfig. When you request a font at 200px, fontconfig returns a font pattern that says 20px, and so the next layers thing you want a font at 20px. To address that, one also needs a piece of fontconfig config that puts the 200 back into the pixelsize. Something like this: <match target="font"> <test name="scalable" mode="eq"> <bool>false</bool> </test> <edit name="pixelsize" mode="assign"> <times> <name>size</name> <name>dpi</name> <double>0.0138888888888</double> <!--1/72.--> </times> </edit> </match> I'm going to try to upstream this config so it will be enabled by default. The config can be a bit smarter. For example, if metricshinting is enabled and the size difference is small, we may as well not scale. The nice thing about this is that the configuration of whether and when to scale bitmaps will be done in fontconfig, not cairo / Qt / ... code. --- * * * [FT] Prefer downscaling bitmap glyphs to upscaling From a8f1b456db744e33a10b2301df03528787e5b1ca Mon Sep 17 00:00:00 2001 Say, you have bitmap strikes for sizes 50ppem and 100ppem. To render at 60ppem, it's much better to downscale the 100ppem bitmap than upscale 50ppem one. Prefer downscaling. --- * * * [ft] I meant fabs(), not abs() From 13bd8d09b44e50649f6fc4d58d036bc32c1d5c5b Mon Sep 17 00:00:00 2001 --- * * * [ft] Fix memory bug in copying bitmaps From a0f556f37fb7016aa304b7cf0e811c0d38f0b969 Mon Sep 17 00:00:00 2001 --- * * * [ft] Fix wrong assumptions From e738079302a968b7b1fb9101cd4d92a8887bedce Mon Sep 17 00:00:00 2001 If subpixel rendering is enabled, but FT returns a 8bit gray bitmap (perhaps because the font has 8bit embedded bitmaps) we were hitting the assertions because the assumptions made were wrong. Fix up. --- * * * Towards support loading color glyphs from FreeType From 2cc353c3dbe01b4d8f65d6de800f2b1d6004a1c2 Mon Sep 17 00:00:00 2001 See comments. --- * * * Support 2bit and 4bit embedded bitmaps From 9444ef09ccde2735258cc1bd2f1912119a32dd88 Mon Sep 17 00:00:00 2001 --- * * * [ft] Fix math From 7d26341072b13a78d4b3fe58779057ac020be487 Mon Sep 17 00:00:00 2001 --- * * * [ft] Add missing include From 0554d76402321b25cc952180e4d19436a9038d1a Mon Sep 17 00:00:00 2001 --- * * * [ft] Fix alignment From 34a747e7bdeba1cfe17318f80fbe6720d47bc023 Mon Sep 17 00:00:00 2001 --- * * * [ft] Ensure alignment of bitmaps received from FreeType From 46d9db96d460fea72f0420102e8a90c6a7231f79 Mon Sep 17 00:00:00 2001 ---
gfx/cairo/cairo/src/cairo-ft-font.c
--- a/gfx/cairo/cairo/src/cairo-ft-font.c
+++ b/gfx/cairo/cairo/src/cairo-ft-font.c
@@ -47,16 +47,17 @@
 #include <float.h>
 
 #include "cairo-fontconfig-private.h"
 
 #include <ft2build.h>
 #include FT_FREETYPE_H
 #include FT_OUTLINE_H
 #include FT_IMAGE_H
+#include FT_BITMAP_H
 #include FT_TRUETYPE_TABLES_H
 #if HAVE_FT_GLYPHSLOT_EMBOLDEN
 #include FT_SYNTHESIS_H
 #endif
 
 #if HAVE_FT_LIBRARY_SETLCDFILTER
 #include FT_LCD_FILTER_H
 #endif
@@ -697,17 +698,18 @@ void
     unscaled->lock_count--;
 
     CAIRO_MUTEX_UNLOCK (unscaled->mutex);
 }
 
 
 static cairo_status_t
 _compute_transform (cairo_ft_font_transform_t *sf,
-		    cairo_matrix_t      *scale)
+		    cairo_matrix_t      *scale,
+		    cairo_ft_unscaled_font_t *unscaled)
 {
     cairo_status_t status;
     double x_scale, y_scale;
     cairo_matrix_t normalized = *scale;
 
     /* The font matrix has x and y "scale" components which we extract and
      * use as character scale values. These influence the way freetype
      * chooses hints, as well as selecting different bitmaps in
@@ -725,16 +727,49 @@ static cairo_status_t
      * "A character width or height smaller than 1pt is set to 1pt;"
      * So, we cap them from below at 1.0 and let the FT transform
      * take care of sub-1.0 scaling. */
     if (x_scale < 1.0)
       x_scale = 1.0;
     if (y_scale < 1.0)
       y_scale = 1.0;
 
+    if (unscaled && (unscaled->face->face_flags & FT_FACE_FLAG_SCALABLE) == 0) {
+	double min_distance = DBL_MAX;
+	cairo_bool_t magnify = TRUE;
+	int i;
+	int best_i = 0;
+	double best_x_size = 0;
+	double best_y_size = 0;
+
+	for (i = 0; i < unscaled->face->num_fixed_sizes; i++) {
+	    double x_size = unscaled->face->available_sizes[i].y_ppem / 64.;
+	    double y_size = unscaled->face->available_sizes[i].y_ppem / 64.;
+	    double distance = y_size - y_scale;
+
+	    /*
+	     * distance is positive if current strike is larger than desired
+	     * size, and negative if smaller.
+	     *
+	     * We like to prefer down-scaling to upscaling.
+	     */
+
+	    if ((magnify && distance >= 0) || fabs (distance) <= min_distance) {
+		magnify = distance < 0;
+		min_distance = fabs (distance);
+		best_i = i;
+		best_x_size = x_size;
+		best_y_size = y_size;
+	    }
+	}
+
+	x_scale = best_x_size;
+	y_scale = best_y_size;
+    }
+
     sf->x_scale = x_scale;
     sf->y_scale = y_scale;
 
     cairo_matrix_scale (&normalized, 1.0 / x_scale, 1.0 / y_scale);
 
     _cairo_matrix_get_affine (&normalized,
 			      &sf->shape[0][0], &sf->shape[0][1],
 			      &sf->shape[1][0], &sf->shape[1][1],
@@ -749,30 +784,31 @@ static cairo_status_t
 static cairo_status_t
 _cairo_ft_unscaled_font_set_scale (cairo_ft_unscaled_font_t *unscaled,
 				   cairo_matrix_t	      *scale)
 {
     cairo_status_t status;
     cairo_ft_font_transform_t sf;
     FT_Matrix mat;
     FT_Error error;
+    double x_scale, y_scale;
 
     assert (unscaled->face != NULL);
 
     if (unscaled->have_scale &&
 	scale->xx == unscaled->current_scale.xx &&
 	scale->yx == unscaled->current_scale.yx &&
 	scale->xy == unscaled->current_scale.xy &&
 	scale->yy == unscaled->current_scale.yy)
 	return CAIRO_STATUS_SUCCESS;
 
     unscaled->have_scale = TRUE;
     unscaled->current_scale = *scale;
 
-    status = _compute_transform (&sf, scale);
+    status = _compute_transform (&sf, scale, unscaled);
     if (unlikely (status))
 	return status;
 
     unscaled->x_scale = sf.x_scale;
     unscaled->y_scale = sf.y_scale;
 
     mat.xx = DOUBLE_TO_16_16(sf.shape[0][0]);
     mat.yx = - DOUBLE_TO_16_16(sf.shape[0][1]);
@@ -787,56 +823,24 @@ static cairo_status_t
     unscaled->Current_Shape = mat;
     cairo_matrix_init (&unscaled->current_shape,
 		       sf.shape[0][0], sf.shape[0][1],
 		       sf.shape[1][0], sf.shape[1][1],
 		       0.0, 0.0);
 
     FT_Set_Transform(unscaled->face, &mat, NULL);
 
-    if ((unscaled->face->face_flags & FT_FACE_FLAG_SCALABLE) != 0) {
-	double x_scale = MIN(sf.x_scale, MAX_FONT_SIZE);
-	double y_scale = MIN(sf.y_scale, MAX_FONT_SIZE);
-	error = FT_Set_Char_Size (unscaled->face,
-				  x_scale * 64.0 + .5,
-				  y_scale * 64.0 + .5,
-				  0, 0);
-	if (error)
-	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
-    } else {
-	double min_distance = DBL_MAX;
-	int i;
-	int best_i = 0;
-
-	for (i = 0; i < unscaled->face->num_fixed_sizes; i++) {
-#if HAVE_FT_BITMAP_SIZE_Y_PPEM
-	    double size = unscaled->face->available_sizes[i].y_ppem / 64.;
-#else
-	    double size = unscaled->face->available_sizes[i].height;
-#endif
-	    double distance = fabs (size - sf.y_scale);
-
-	    if (distance <= min_distance) {
-		min_distance = distance;
-		best_i = i;
-	    }
-	}
-#if HAVE_FT_BITMAP_SIZE_Y_PPEM
-	error = FT_Set_Char_Size (unscaled->face,
-				  unscaled->face->available_sizes[best_i].x_ppem,
-				  unscaled->face->available_sizes[best_i].y_ppem,
-				  0, 0);
-	if (error)
-#endif
-	    error = FT_Set_Pixel_Sizes (unscaled->face,
-					unscaled->face->available_sizes[best_i].width,
-					unscaled->face->available_sizes[best_i].height);
-	if (error)
-	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
-    }
+    x_scale = MIN(sf.x_scale, MAX_FONT_SIZE);
+    y_scale = MIN(sf.y_scale, MAX_FONT_SIZE);
+    error = FT_Set_Char_Size (unscaled->face,
+			      x_scale * 64.0 + .5,
+			      y_scale * 64.0 + .5,
+			      0, 0);
+    if (error)
+      return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
     return CAIRO_STATUS_SUCCESS;
 }
 
 /* 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.
@@ -1109,24 +1113,26 @@ static void
     }
 }
 
 
 /* Fills in val->image with an image surface created from @bitmap
  */
 static cairo_status_t
 _get_bitmap_surface (FT_Bitmap		     *bitmap,
+		     FT_Library		      library,
 		     cairo_bool_t	      own_buffer,
 		     cairo_font_options_t    *font_options,
 		     cairo_image_surface_t  **surface)
 {
     int width, height, stride;
     unsigned char *data;
     int format = CAIRO_FORMAT_A8;
     cairo_image_surface_t *image;
+    cairo_bool_t component_alpha = FALSE;
 
     width = bitmap->width;
     height = bitmap->rows;
 
     if (width == 0 || height == 0) {
 	*surface = (cairo_image_surface_t *)
 	    cairo_image_surface_create_for_data (NULL, format, 0, 0, 0);
 	return (*surface)->base.status;
@@ -1147,19 +1153,17 @@ static cairo_status_t
 		memcpy (data, bitmap->buffer, stride * height);
 	    } else {
 		int i;
 		unsigned char *source, *dest;
 
 		source = bitmap->buffer;
 		dest = data;
 		for (i = height; i; i--) {
-		    memcpy (dest, source, bitmap->pitch);
-		    memset (dest + bitmap->pitch, '\0', stride - bitmap->pitch);
-
+		    memcpy (dest, source, stride);
 		    source += bitmap->pitch;
 		    dest += stride;
 		}
 	    }
 	}
 
 #ifndef WORDS_BIGENDIAN
 	{
@@ -1173,43 +1177,104 @@ static cairo_status_t
 	}
 #endif
 	format = CAIRO_FORMAT_A1;
 	break;
 
     case FT_PIXEL_MODE_LCD:
     case FT_PIXEL_MODE_LCD_V:
     case FT_PIXEL_MODE_GRAY:
-        if (font_options->antialias != CAIRO_ANTIALIAS_SUBPIXEL) {
+	if (font_options->antialias != CAIRO_ANTIALIAS_SUBPIXEL ||
+	    bitmap->pixel_mode == FT_PIXEL_MODE_GRAY)
+	{
 	    stride = bitmap->pitch;
+
+	    /* We don't support stride not multiple of 4. */
+	    if (stride & 3)
+	    {
+		assert (!own_buffer);
+		goto convert;
+	    }
+
 	    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;
+	    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;
-		stride = bitmap->pitch;
-		format = CAIRO_FORMAT_ARGB32;
+	    data = bitmap->buffer;
+	    stride = bitmap->pitch;
+	    format = CAIRO_FORMAT_ARGB32;
+	    component_alpha = TRUE;
 	}
 	break;
+#ifdef FT_LOAD_COLOR
+    case FT_PIXEL_MODE_BGRA:
+	stride = width * 4;
+	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_ARGB32;
+	break;
+#endif
     case FT_PIXEL_MODE_GRAY2:
     case FT_PIXEL_MODE_GRAY4:
+    convert:
+	if (!own_buffer && library)
+	{
+	    /* This is pretty much the only case that we can get in here. */
+	    /* Convert to 8bit grayscale. */
+
+	    FT_Bitmap  tmp;
+	    FT_Int     align;
+
+	    format = CAIRO_FORMAT_A8;
+
+	    align = cairo_format_stride_for_width (format, bitmap->width);
+
+	    FT_Bitmap_New( &tmp );
+
+	    if (FT_Bitmap_Convert( library, bitmap, &tmp, align ))
+		return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+	    FT_Bitmap_Done( library, bitmap );
+	    *bitmap = tmp;
+
+	    stride = bitmap->pitch;
+	    data = _cairo_malloc_ab (height, stride);
+	    if (!data)
+		return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+	    if (bitmap->num_grays != 256)
+	    {
+	      unsigned int x, y;
+	      unsigned int mul = 255 / (bitmap->num_grays - 1);
+	      FT_Byte *p = bitmap->buffer;
+	      for (y = 0; y < height; y++) {
+	        for (x = 0; x < width; x++)
+		  p[x] *= mul;
+		p += bitmap->pitch;
+	      }
+	    }
+
+	    memcpy (data, bitmap->buffer, stride * height);
+	    break;
+	}
 	/* These could be triggered by very rare types of TrueType fonts */
     default:
 	if (own_buffer)
 	    free (bitmap->buffer);
 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
     }
 
     /* XXX */
@@ -1217,17 +1282,17 @@ static cairo_status_t
 	cairo_image_surface_create_for_data (data,
 					     format,
 					     width, height, stride);
     if (image->base.status) {
 	free (data);
 	return (*surface)->base.status;
     }
 
-    if (format == CAIRO_FORMAT_ARGB32)
+    if (component_alpha)
 	pixman_image_set_component_alpha (image->pixman_image, TRUE);
 
     _cairo_image_surface_assume_ownership_of_data (image);
 
     _cairo_debug_check_image_surface_is_defined (&image->base);
 
     return CAIRO_STATUS_SUCCESS;
 }
@@ -1398,17 +1463,17 @@ static cairo_status_t
 		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);
+	status = _get_bitmap_surface (&bitmap, NULL, 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.
 	 */
@@ -1439,16 +1504,17 @@ static cairo_status_t
      */
     error = FT_Render_Glyph (glyphslot, FT_RENDER_MODE_NORMAL);
     /* XXX ignoring all other errors for now.  They are not fatal, typically
      * just a glyph-not-found. */
     if (error == FT_Err_Out_Of_Memory)
 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
     status = _get_bitmap_surface (&glyphslot->bitmap,
+				  glyphslot->library,
 				  FALSE, 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
@@ -1480,17 +1546,17 @@ static cairo_status_t
     cairo_status_t status;
     cairo_surface_pattern_t pattern;
 
     /* We want to compute a transform that takes the origin
      * (device_x_offset, device_y_offset) to 0,0, then applies
      * the "shape" portion of the font transform
      */
     original_to_transformed = *shape;
-    
+
     cairo_surface_get_device_offset (&(*surface)->base, &origin_x, &origin_y);
     orig_width = (*surface)->width;
     orig_height = (*surface)->height;
 
     cairo_matrix_translate (&original_to_transformed,
 			    -origin_x, -origin_y);
 
     /* Find the bounding box of the original bitmap under that
@@ -1530,17 +1596,21 @@ static cairo_status_t
     width  = x_max - x_min;
     height = y_max - y_min;
 
     transformed_to_original = original_to_transformed;
     status = cairo_matrix_invert (&transformed_to_original);
     if (unlikely (status))
 	return status;
 
-    image = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
+    if (cairo_image_surface_get_format (*surface) == CAIRO_FORMAT_ARGB32 &&
+        !pixman_image_get_component_alpha ((*surface)->pixman_image))
+      image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+    else
+      image = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
     if (unlikely (image->status))
 	return image->status;
 
     /* Draw the original bitmap transformed into the new bitmap
      */
     _cairo_pattern_init_for_surface (&pattern, &(*surface)->base);
     cairo_pattern_set_matrix (&pattern.base, &transformed_to_original);
 
@@ -2164,16 +2234,28 @@ static cairo_int_status_t
      * Don't pass FT_LOAD_VERTICAL_LAYOUT to FT_Load_Glyph here as
      * suggested by freetype people.
      */
     if (load_flags & FT_LOAD_VERTICAL_LAYOUT) {
 	load_flags &= ~FT_LOAD_VERTICAL_LAYOUT;
 	vertical_layout = TRUE;
     }
 
+#ifdef FT_LOAD_COLOR
+    /* Color-glyph support:
+     *
+     * This flags needs plumbing through fontconfig (does it?), and
+     * maybe we should cache color and grayscale bitmaps separately
+     * such that users of the font (ie. the surface) can choose which
+     * version to use based on target content type.
+     */
+
+    load_flags |= FT_LOAD_COLOR;
+#endif
+
     error = FT_Load_Glyph (scaled_font->unscaled->face,
 			   _cairo_scaled_glyph_index(scaled_glyph),
 			   load_flags);
     /* XXX ignoring all other errors for now.  They are not fatal, typically
      * just a glyph-not-found. */
     if (error == FT_Err_Out_Of_Memory) {
 	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 	goto FAIL;
@@ -2921,17 +3003,17 @@ static cairo_font_face_t *
     cairo_font_face_t *font_face;
 
     scale = *ctm;
     scale.x0 = scale.y0 = 0;
     cairo_matrix_multiply (&scale,
                            font_matrix,
                            &scale);
 
-    status = _compute_transform (&sf, &scale);
+    status = _compute_transform (&sf, &scale, NULL);
     if (unlikely (status))
 	return (cairo_font_face_t *)&_cairo_font_face_nil;
 
     pattern = FcPatternDuplicate (pattern);
     if (pattern == NULL)
 	return (cairo_font_face_t *)&_cairo_font_face_nil;
 
     if (! FcPatternAddDouble (pattern, FC_PIXEL_SIZE, sf.y_scale)) {