diff --git a/docs/harfbuzz-sections.txt b/docs/harfbuzz-sections.txt index e63afdef0..d9ad80b3a 100644 --- a/docs/harfbuzz-sections.txt +++ b/docs/harfbuzz-sections.txt @@ -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 diff --git a/src/hb-ft.cc b/src/hb-ft.cc index e3e9eaec8..c495cb629 100644 --- a/src/hb-ft.cc +++ b/src/hb-ft.cc @@ -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 diff --git a/src/hb-paint.h b/src/hb-paint.h index c695891a0..ac9aaf020 100644 --- a/src/hb-paint.h +++ b/src/hb-paint.h @@ -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 diff --git a/util/hb-cairo-utils.c b/util/hb-cairo-utils.c index 0f03e40f3..614d96eda 100644 --- a/util/hb-cairo-utils.c +++ b/util/hb-cairo-utils.c @@ -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