[paint] Add HB_PAINT_IMAGE_FORMAT_BGRA and use it in hb-ft

Now hb-ft can render color emoji as well.

Just left COLRv2.
This commit is contained in:
Behdad Esfahbod 2022-12-22 13:21:48 -07:00
parent 63db0d2aed
commit 381d410b1e
4 changed files with 125 additions and 14 deletions

View File

@ -244,6 +244,7 @@ hb_paint_color_func_t
hb_paint_funcs_set_color_func
HB_PAINT_IMAGE_FORMAT_PNG
HB_PAINT_IMAGE_FORMAT_SVG
HB_PAINT_IMAGE_FORMAT_BGRA
hb_paint_image_func_t
hb_paint_funcs_set_image_func
hb_color_line_t
@ -373,6 +374,7 @@ hb_font_get_glyph_v_origin
hb_font_get_glyph_origin_for_direction
hb_font_get_glyph_name
hb_font_get_glyph_shape
hb_font_draw_glyph
hb_font_paint_glyph
hb_font_get_nominal_glyph
hb_font_get_nominal_glyphs
@ -436,6 +438,8 @@ hb_font_get_glyph_name_func_t
hb_font_funcs_set_glyph_name_func
hb_font_get_glyph_shape_func_t
hb_font_funcs_set_glyph_shape_func
hb_font_draw_glyph_func_t
hb_font_funcs_set_draw_glyph_func
hb_font_paint_glyph_func_t
hb_font_funcs_set_paint_glyph_func
hb_font_get_nominal_glyph_func_t

View File

@ -830,7 +830,8 @@ hb_ft_paint_glyph (hb_font_t *font,
/* We release the lock before calling into callbacks, such that
* eg. draw API can call back into the face.*/
if (unlikely (FT_Load_Glyph (ft_face, gid, ft_font->load_flags)))
if (unlikely (FT_Load_Glyph (ft_face, gid,
ft_font->load_flags | FT_LOAD_COLOR)))
return;
if (ft_face->glyph->format == FT_GLYPH_FORMAT_OUTLINE)
@ -898,6 +899,42 @@ hb_ft_paint_glyph (hb_font_t *font,
return;
}
auto *glyph = ft_face->glyph;
if (glyph->format == FT_GLYPH_FORMAT_BITMAP)
{
auto &bitmap = glyph->bitmap;
if (bitmap.pixel_mode == FT_PIXEL_MODE_BGRA)
{
if (bitmap.pitch != (signed) bitmap.width * 4)
return;
ft_font->lock.unlock ();
hb_blob_t *blob = hb_blob_create ((const char *) bitmap.buffer,
bitmap.pitch * bitmap.rows,
HB_MEMORY_MODE_DUPLICATE,
nullptr, nullptr);
hb_glyph_extents_t extents;
if (!hb_font_get_glyph_extents (font, gid, &extents))
goto out;
paint_funcs->image (paint_data,
blob,
bitmap.width,
bitmap.rows,
HB_PAINT_IMAGE_FORMAT_BGRA,
font->slant_xy,
&extents);
out:
hb_blob_destroy (blob);
ft_font->lock.lock ();
}
return;
}
/* TODO Support image, COLRv0/1. */
}
#endif

View File

@ -227,17 +227,31 @@ typedef void (*hb_paint_color_func_t) (hb_paint_funcs_t *funcs,
/**
* HB_PAINT_IMAGE_FORMAT_PNG:
*
* Tag identifying png images in #hb_paint_image_func_t callbacks.
* Tag identifying PNG images in #hb_paint_image_func_t callbacks.
*
* Since: REPLACEME
*/
#define HB_PAINT_IMAGE_FORMAT_PNG HB_TAG('p','n','g',' ')
/**
* HB_PAINT_IMAGE_FORMAT_SVG:
*
* Tag identifying svg images in #hb_paint_image_func_t callbacks.
* Tag identifying SVG images in #hb_paint_image_func_t callbacks.
*
* Since: REPLACEME
*/
#define HB_PAINT_IMAGE_FORMAT_SVG HB_TAG('s','v','g',' ')
/**
* HB_PAINT_IMAGE_FORMAT_BGRA:
*
* Tag identifying raw pixel-data images in #hb_paint_image_func_t callbacks.
* The data is in BGRA pre-multiplied sRGBA color-space format.
*
* Since: REPLACEME
*/
#define HB_PAINT_IMAGE_FORMAT_BGRA HB_TAG('B','G','R','A')
/**
* hb_paint_image_func_t:
* @funcs: paint functions object

View File

@ -98,6 +98,14 @@ read_blob (void *closure,
}
#endif
static const cairo_user_data_key_t *_hb_cairo_surface_blob_user_data_key = {0};
static void
_hb_cairo_destroy_blob (void *p)
{
hb_blob_destroy ((hb_blob_t *) p);
}
void
hb_cairo_paint_glyph_image (cairo_t *cr,
hb_blob_t *blob,
@ -107,19 +115,68 @@ hb_cairo_paint_glyph_image (cairo_t *cr,
float slant,
hb_glyph_extents_t *extents)
{
#ifdef CAIRO_HAS_PNG_FUNCTIONS
if (format != HB_PAINT_IMAGE_FORMAT_PNG || !extents)
if (!extents) /* SVG currently. */
return;
read_blob_data_t r;
r.blob = blob;
r.offset = 0;
cairo_surface_t *surface = cairo_image_surface_create_from_png_stream (read_blob, &r);
cairo_surface_t *surface = NULL;
/* For PNG, width,height can be unreliable, as is the case for NotoColorEmoji :(.
* Just pull them out of the surface. */
width = cairo_image_surface_get_width (surface);
height = cairo_image_surface_get_width (surface);
#ifdef CAIRO_HAS_PNG_FUNCTIONS
if (format == HB_PAINT_IMAGE_FORMAT_PNG)
{
read_blob_data_t r;
r.blob = blob;
r.offset = 0;
surface = cairo_image_surface_create_from_png_stream (read_blob, &r);
/* For PNG, width,height can be unreliable, as is the case for NotoColorEmoji :(.
* Just pull them out of the surface. */
width = cairo_image_surface_get_width (surface);
height = cairo_image_surface_get_width (surface);
}
else
#endif
if (format == HB_PAINT_IMAGE_FORMAT_BGRA)
{
/* Byte-endian conversion. */
unsigned data_size = hb_blob_get_length (blob);
if (data_size < width * height * 4)
return;
unsigned char *data;
if (__BYTE_ORDER == __BIG_ENDIAN)
{
data = (unsigned char *) hb_blob_get_data_writable (blob, NULL);
if (!data)
return;
unsigned count = width * height * 4;
for (unsigned i = 0; i < count; i += 4)
{
unsigned char b;
b = data[i];
data[i] = data[i+3];
data[i+3] = b;
b = data[i+1];
data[i+1] = data[i+2];
data[i+2] = b;
}
}
else
data = (unsigned char *) hb_blob_get_data (blob, NULL);
surface = cairo_image_surface_create_for_data (data,
CAIRO_FORMAT_ARGB32,
width, height,
width * 4);
cairo_surface_set_user_data (surface,
_hb_cairo_surface_blob_user_data_key,
hb_blob_reference (blob),
_hb_cairo_destroy_blob);
}
if (!surface)
return;
cairo_pattern_t *pattern = cairo_pattern_create_for_surface (surface);
cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
@ -141,7 +198,6 @@ hb_cairo_paint_glyph_image (cairo_t *cr,
cairo_pattern_destroy (pattern);
cairo_surface_destroy (surface);
#endif
}
static void