| /* |
| * Copyright (C) 2026 Behdad Esfahbod |
| * |
| * This is part of HarfBuzz, a text shaping library. |
| * |
| * Permission is hereby granted, without written agreement and without |
| * license or royalty fees, to use, copy, modify, and distribute this |
| * software and its documentation for any purpose, provided that the |
| * above copyright notice and the following two paragraphs appear in |
| * all copies of this software. |
| * |
| * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR |
| * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
| * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN |
| * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
| * DAMAGE. |
| * |
| * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, |
| * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
| * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS |
| * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO |
| * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
| * |
| * Author(s): Behdad Esfahbod |
| */ |
| |
| #include "hb-test.h" |
| #include "hb-gpu.h" |
| #include <hb-ot.h> |
| #include <cmath> |
| |
| #define FONT_FILE "fonts/Roboto-Regular.abc.ttf" |
| #define COLR_FONT_FILE "fonts/test_glyphs-glyf_colr_1.ttf" |
| |
| struct hb_gpu_test_texel_t |
| { |
| int16_t r, g, b, a; |
| }; |
| |
| static const hb_gpu_test_texel_t * |
| blob_as_texels (hb_blob_t *blob, |
| unsigned *texel_count) |
| { |
| unsigned length = hb_blob_get_length (blob); |
| g_assert_cmpuint (length % sizeof (hb_gpu_test_texel_t), ==, 0); |
| |
| *texel_count = length / sizeof (hb_gpu_test_texel_t); |
| /* hb_blob_get_data returns malloc'd memory which is suitably |
| * aligned for any standard type; the cast is safe. */ |
| #pragma GCC diagnostic push |
| #pragma GCC diagnostic ignored "-Wcast-align" |
| return (const hb_gpu_test_texel_t *) hb_blob_get_data (blob, nullptr); |
| #pragma GCC diagnostic pop |
| } |
| |
| static hb_bool_t |
| blob_has_texel (hb_blob_t *blob, |
| const hb_gpu_test_texel_t needle) |
| { |
| unsigned texel_count; |
| const hb_gpu_test_texel_t *texels = blob_as_texels (blob, &texel_count); |
| |
| for (unsigned i = 0; i < texel_count; i++) |
| if (texels[i].r == needle.r && |
| texels[i].g == needle.g && |
| texels[i].b == needle.b && |
| texels[i].a == needle.a) |
| return true; |
| |
| return false; |
| } |
| |
| |
| static void |
| test_create_destroy (void) |
| { |
| hb_gpu_draw_t *draw = hb_gpu_draw_create_or_fail (); |
| g_assert_nonnull (draw); |
| |
| hb_gpu_draw_t *ref = hb_gpu_draw_reference (draw); |
| g_assert_true (ref == draw); |
| hb_gpu_draw_destroy (ref); |
| |
| hb_gpu_draw_destroy (draw); |
| } |
| |
| static void |
| test_empty_encode (void) |
| { |
| hb_gpu_draw_t *draw = hb_gpu_draw_create_or_fail (); |
| g_assert_nonnull (draw); |
| |
| /* Encode with no curves should return empty blob and zero extents. */ |
| hb_glyph_extents_t ext; |
| hb_blob_t *blob = hb_gpu_draw_encode (draw, &ext); |
| g_assert_nonnull (blob); |
| g_assert_cmpuint (hb_blob_get_length (blob), ==, 0); |
| hb_blob_destroy (blob); |
| |
| g_assert_cmpint (ext.x_bearing, ==, 0); |
| g_assert_cmpint (ext.y_bearing, ==, 0); |
| g_assert_cmpint (ext.width, ==, 0); |
| g_assert_cmpint (ext.height, ==, 0); |
| |
| hb_gpu_draw_destroy (draw); |
| } |
| |
| static void |
| test_encode_glyph (void) |
| { |
| hb_face_t *face = hb_test_open_font_file (FONT_FILE); |
| g_assert_nonnull (face); |
| hb_font_t *font = hb_font_create (face); |
| |
| hb_gpu_draw_t *draw = hb_gpu_draw_create_or_fail (); |
| g_assert_nonnull (draw); |
| |
| /* Draw glyph for 'a' (glyph index from cmap). */ |
| hb_codepoint_t gid; |
| hb_bool_t found = hb_font_get_nominal_glyph (font, 'a', &gid); |
| g_assert_true (found); |
| |
| hb_gpu_draw_glyph (draw, font, gid); |
| |
| /* Encode should produce non-empty blob with non-zero extents. */ |
| hb_glyph_extents_t ext; |
| hb_blob_t *blob = hb_gpu_draw_encode (draw, &ext); |
| g_assert_nonnull (blob); |
| g_assert_cmpuint (hb_blob_get_length (blob), >, 0); |
| |
| /* Blob size should be a multiple of 8 (RGBA16I texels). */ |
| g_assert_cmpuint (hb_blob_get_length (blob) % 8, ==, 0); |
| |
| g_assert_cmpint (ext.width, >, 0); |
| g_assert_cmpint (ext.height, <, 0); /* height is negative (y-down) */ |
| |
| hb_blob_destroy (blob); |
| hb_gpu_draw_destroy (draw); |
| hb_font_destroy (font); |
| hb_face_destroy (face); |
| } |
| |
| static void |
| test_reset (void) |
| { |
| hb_face_t *face = hb_test_open_font_file (FONT_FILE); |
| hb_font_t *font = hb_font_create (face); |
| |
| hb_gpu_draw_t *draw = hb_gpu_draw_create_or_fail (); |
| g_assert_nonnull (draw); |
| |
| hb_codepoint_t gid; |
| hb_font_get_nominal_glyph (font, 'a', &gid); |
| |
| /* Draw, encode, reset, encode again — should work. */ |
| hb_gpu_draw_glyph (draw, font, gid); |
| hb_blob_t *blob1 = hb_gpu_draw_encode (draw, nullptr); |
| g_assert_cmpuint (hb_blob_get_length (blob1), >, 0); |
| hb_blob_destroy (blob1); |
| |
| hb_gpu_draw_reset (draw); |
| |
| /* After reset, encode should be empty. */ |
| hb_blob_t *blob2 = hb_gpu_draw_encode (draw, nullptr); |
| g_assert_cmpuint (hb_blob_get_length (blob2), ==, 0); |
| hb_blob_destroy (blob2); |
| |
| /* Draw again after reset. */ |
| hb_gpu_draw_glyph (draw, font, gid); |
| hb_blob_t *blob3 = hb_gpu_draw_encode (draw, nullptr); |
| g_assert_cmpuint (hb_blob_get_length (blob3), >, 0); |
| hb_blob_destroy (blob3); |
| |
| hb_gpu_draw_destroy (draw); |
| hb_font_destroy (font); |
| hb_face_destroy (face); |
| } |
| |
| static void |
| test_draw_funcs (void) |
| { |
| hb_draw_funcs_t *funcs = hb_gpu_draw_get_funcs (nullptr); |
| g_assert_nonnull (funcs); |
| |
| /* Should be the same instance each time. */ |
| g_assert_true (funcs == hb_gpu_draw_get_funcs (nullptr)); |
| } |
| |
| static void |
| test_shader_sources (void) |
| { |
| g_assert_nonnull (hb_gpu_shader_source (HB_GPU_SHADER_STAGE_FRAGMENT, HB_GPU_SHADER_LANG_GLSL)); |
| g_assert_nonnull (hb_gpu_shader_source (HB_GPU_SHADER_STAGE_VERTEX, HB_GPU_SHADER_LANG_GLSL)); |
| g_assert_nonnull (hb_gpu_draw_shader_source (HB_GPU_SHADER_STAGE_FRAGMENT, HB_GPU_SHADER_LANG_GLSL)); |
| g_assert_nonnull (hb_gpu_draw_shader_source (HB_GPU_SHADER_STAGE_VERTEX, HB_GPU_SHADER_LANG_GLSL)); |
| } |
| |
| static void |
| test_encode_quantizes_extents_outward (void) |
| { |
| hb_gpu_draw_t *draw = hb_gpu_draw_create_or_fail (); |
| g_assert_nonnull (draw); |
| |
| hb_draw_funcs_t *funcs = hb_gpu_draw_get_funcs (draw); |
| hb_draw_state_t st = HB_DRAW_STATE_DEFAULT; |
| hb_draw_move_to (funcs, draw, &st, 0.24f, 0.24f); |
| hb_draw_line_to (funcs, draw, &st, 0.76f, 0.24f); |
| hb_draw_line_to (funcs, draw, &st, 0.76f, 0.76f); |
| hb_draw_close_path (funcs, draw, &st); |
| |
| hb_blob_t *blob = hb_gpu_draw_encode (draw, nullptr); |
| g_assert_nonnull (blob); |
| |
| unsigned texel_count; |
| const hb_gpu_test_texel_t *texels = blob_as_texels (blob, &texel_count); |
| g_assert_cmpuint (texel_count, >, 0); |
| g_assert_cmpint (texels[0].r, ==, 0); |
| g_assert_cmpint (texels[0].g, ==, 0); |
| g_assert_cmpint (texels[0].b, ==, 4); |
| g_assert_cmpint (texels[0].a, ==, 4); |
| |
| hb_blob_destroy (blob); |
| hb_gpu_draw_destroy (draw); |
| } |
| |
| static void |
| test_encode_preserves_touching_contours (void) |
| { |
| hb_gpu_draw_t *draw = hb_gpu_draw_create_or_fail (); |
| g_assert_nonnull (draw); |
| |
| hb_draw_funcs_t *funcs = hb_gpu_draw_get_funcs (draw); |
| hb_draw_state_t st = HB_DRAW_STATE_DEFAULT; |
| |
| hb_draw_move_to (funcs, draw, &st, 0.f, 0.f); |
| hb_draw_line_to (funcs, draw, &st, 1.f, 0.f); |
| hb_draw_line_to (funcs, draw, &st, 0.f, 1.f); |
| hb_draw_close_path (funcs, draw, &st); |
| |
| hb_draw_move_to (funcs, draw, &st, 0.f, 0.f); |
| hb_draw_quadratic_to (funcs, draw, &st, -0.75f, 0.25f, -0.25f, -1.f); |
| hb_draw_close_path (funcs, draw, &st); |
| |
| hb_blob_t *blob = hb_gpu_draw_encode (draw, nullptr); |
| g_assert_nonnull (blob); |
| |
| g_assert_true (blob_has_texel (blob, {0, 0, -3, 1})); |
| |
| hb_blob_destroy (blob); |
| hb_gpu_draw_destroy (draw); |
| } |
| |
| static void |
| test_recycle_blob (void) |
| { |
| hb_gpu_draw_t *draw = hb_gpu_draw_create_or_fail (); |
| g_assert_nonnull (draw); |
| |
| hb_draw_funcs_t *funcs = hb_gpu_draw_get_funcs (draw); |
| hb_draw_state_t st = HB_DRAW_STATE_DEFAULT; |
| |
| hb_draw_move_to (funcs, draw, &st, 0.f, 0.f); |
| hb_draw_line_to (funcs, draw, &st, 1.f, 0.f); |
| hb_draw_line_to (funcs, draw, &st, 0.f, 1.f); |
| hb_draw_close_path (funcs, draw, &st); |
| |
| hb_blob_t *blob = hb_gpu_draw_encode (draw, nullptr); |
| g_assert_nonnull (blob); |
| unsigned first_length = hb_blob_get_length (blob); |
| g_assert_cmpuint (first_length, >, 0); |
| |
| hb_gpu_draw_recycle_blob (draw, blob); |
| |
| hb_gpu_draw_reset (draw); |
| |
| hb_draw_move_to (funcs, draw, &st, 0.f, 0.f); |
| hb_draw_line_to (funcs, draw, &st, 2.f, 0.f); |
| hb_draw_line_to (funcs, draw, &st, 2.f, 2.f); |
| hb_draw_line_to (funcs, draw, &st, 0.f, 2.f); |
| hb_draw_close_path (funcs, draw, &st); |
| hb_draw_move_to (funcs, draw, &st, 0.25f, 0.25f); |
| hb_draw_quadratic_to (funcs, draw, &st, 1.f, 1.75f, 1.75f, 0.25f); |
| hb_draw_quadratic_to (funcs, draw, &st, 1.f, -0.5f, 0.25f, 0.25f); |
| hb_draw_close_path (funcs, draw, &st); |
| |
| hb_blob_t *blob2 = hb_gpu_draw_encode (draw, nullptr); |
| g_assert_nonnull (blob2); |
| g_assert_cmpuint (hb_blob_get_length (blob2), >, first_length); |
| |
| hb_blob_destroy (blob2); |
| hb_gpu_draw_destroy (draw); |
| } |
| |
| static void |
| test_extents_saturate_overflow (void) |
| { |
| hb_gpu_draw_t *draw = hb_gpu_draw_create_or_fail (); |
| g_assert_nonnull (draw); |
| |
| hb_draw_funcs_t *funcs = hb_gpu_draw_get_funcs (draw); |
| hb_draw_state_t st = HB_DRAW_STATE_DEFAULT; |
| |
| hb_draw_move_to (funcs, draw, &st, -0x1p31f, 0.f); |
| hb_draw_line_to (funcs, draw, &st, 821.f, 0.f); |
| hb_draw_line_to (funcs, draw, &st, 821.f, 1.f); |
| hb_draw_close_path (funcs, draw, &st); |
| |
| hb_glyph_extents_t ext; |
| hb_blob_t *blob = hb_gpu_draw_encode (draw, &ext); |
| hb_blob_destroy (blob); |
| |
| g_assert_cmpint (ext.x_bearing, ==, G_MININT32); |
| g_assert_cmpint (ext.y_bearing, ==, 1); |
| g_assert_cmpint (ext.width, ==, G_MAXINT32); |
| g_assert_cmpint (ext.height, ==, -1); |
| |
| hb_gpu_draw_destroy (draw); |
| } |
| |
| /* ===== Paint encoder tests ===== */ |
| |
| static void |
| test_paint_create_destroy (void) |
| { |
| hb_gpu_paint_t *p = hb_gpu_paint_create_or_fail (); |
| g_assert_nonnull (p); |
| |
| hb_gpu_paint_t *ref = hb_gpu_paint_reference (p); |
| g_assert_true (ref == p); |
| hb_gpu_paint_destroy (ref); |
| |
| hb_gpu_paint_destroy (p); |
| } |
| |
| static void |
| test_paint_get_funcs (void) |
| { |
| hb_paint_funcs_t *funcs = hb_gpu_paint_get_funcs (nullptr); |
| g_assert_nonnull (funcs); |
| /* Same instance each call. */ |
| g_assert_true (funcs == hb_gpu_paint_get_funcs (nullptr)); |
| } |
| |
| static void |
| test_paint_shader_sources (void) |
| { |
| /* Fragment helpers exist for all four supported languages. */ |
| g_assert_nonnull (hb_gpu_paint_shader_source (HB_GPU_SHADER_STAGE_FRAGMENT, HB_GPU_SHADER_LANG_GLSL)); |
| g_assert_nonnull (hb_gpu_paint_shader_source (HB_GPU_SHADER_STAGE_FRAGMENT, HB_GPU_SHADER_LANG_MSL)); |
| g_assert_nonnull (hb_gpu_paint_shader_source (HB_GPU_SHADER_STAGE_FRAGMENT, HB_GPU_SHADER_LANG_HLSL)); |
| g_assert_nonnull (hb_gpu_paint_shader_source (HB_GPU_SHADER_STAGE_FRAGMENT, HB_GPU_SHADER_LANG_WGSL)); |
| } |
| |
| static void |
| test_paint_empty_encode (void) |
| { |
| /* Nothing painted -> empty blob (no ops to encode). Reserved for |
| * the "no ink" case; nullptr is only returned for unsupported |
| * features. */ |
| hb_gpu_paint_t *p = hb_gpu_paint_create_or_fail (); |
| g_assert_nonnull (p); |
| hb_blob_t *blob = hb_gpu_paint_encode (p, nullptr); |
| g_assert_nonnull (blob); |
| g_assert_cmpuint (hb_blob_get_length (blob), ==, 0); |
| hb_blob_destroy (blob); |
| hb_gpu_paint_destroy (p); |
| } |
| |
| static void |
| test_paint_encode_monochrome (void) |
| { |
| /* hb_gpu_paint_glyph synthesizes a single foreground-colored |
| * layer for non-color glyphs; the encoded blob should be |
| * non-empty and 8-byte (RGBA16I texel) aligned. */ |
| hb_face_t *face = hb_test_open_font_file (FONT_FILE); |
| g_assert_nonnull (face); |
| hb_font_t *font = hb_font_create (face); |
| |
| hb_gpu_paint_t *p = hb_gpu_paint_create_or_fail (); |
| g_assert_nonnull (p); |
| |
| hb_codepoint_t gid; |
| g_assert_true (hb_font_get_nominal_glyph (font, 'a', &gid)); |
| /* Use hb_gpu_paint_glyph (not _or_fail) so that non-color |
| * glyphs still produce a blob via the synthesize fallback. */ |
| hb_gpu_paint_glyph (p, font, gid); |
| |
| hb_glyph_extents_t ext; |
| hb_blob_t *blob = hb_gpu_paint_encode (p, &ext); |
| g_assert_nonnull (blob); |
| g_assert_cmpuint (hb_blob_get_length (blob), >, 0); |
| g_assert_cmpuint (hb_blob_get_length (blob) % 8, ==, 0); |
| g_assert_cmpint (ext.width, >, 0); |
| g_assert_cmpint (ext.height, <, 0); |
| |
| hb_blob_destroy (blob); |
| hb_gpu_paint_destroy (p); |
| hb_font_destroy (font); |
| hb_face_destroy (face); |
| } |
| |
| static void |
| test_paint_encode_color (void) |
| { |
| /* Real COLRv1 paint tree: scan for any color glyph that we |
| * support end-to-end (the test font deliberately exercises |
| * features we may flag as unsupported, so check several rather |
| * than asserting on the first). */ |
| hb_face_t *face = hb_test_open_font_file (COLR_FONT_FILE); |
| g_assert_nonnull (face); |
| hb_font_t *font = hb_font_create (face); |
| |
| hb_gpu_paint_t *p = hb_gpu_paint_create_or_fail (); |
| g_assert_nonnull (p); |
| |
| unsigned num = hb_face_get_glyph_count (face); |
| for (unsigned i = 0; i < num; i++) |
| { |
| if (!hb_ot_color_glyph_has_paint (face, i)) |
| continue; |
| /* paint_glyph's return is the supported flag; we don't gate |
| * on it -- encode handles both cases (returns empty blob for |
| * unsupported / empty, nullptr only on allocation failure). */ |
| hb_gpu_paint_glyph (p, font, i); |
| hb_blob_t *blob = hb_gpu_paint_encode (p, nullptr); |
| g_assert_nonnull (blob); |
| g_assert_cmpuint (hb_blob_get_length (blob) % 8, ==, 0); |
| hb_blob_destroy (blob); |
| } |
| /* Test exists to confirm we don't crash / corrupt while walking |
| * a real COLRv1 paint tree, not that our encoder covers every |
| * COLRv1 feature. */ |
| |
| hb_gpu_paint_destroy (p); |
| hb_font_destroy (font); |
| hb_face_destroy (face); |
| } |
| |
| static void |
| test_paint_auto_clear_on_encode (void) |
| { |
| /* hb_gpu_paint_encode auto-clears so the encoder can be reused |
| * straight away; user configuration (palette) is preserved. */ |
| hb_face_t *face = hb_test_open_font_file (FONT_FILE); |
| hb_font_t *font = hb_font_create (face); |
| |
| hb_gpu_paint_t *p = hb_gpu_paint_create_or_fail (); |
| hb_gpu_paint_set_palette (p, 3); |
| g_assert_cmpuint (hb_gpu_paint_get_palette (p), ==, 3); |
| |
| hb_codepoint_t gid; |
| hb_font_get_nominal_glyph (font, 'a', &gid); |
| |
| hb_gpu_paint_glyph (p, font, gid); |
| hb_blob_t *blob1 = hb_gpu_paint_encode (p, nullptr); |
| g_assert_nonnull (blob1); |
| hb_blob_destroy (blob1); |
| |
| /* Auto-clear: re-encoding without painting again returns the |
| * empty-blob singleton (nothing accumulated). */ |
| hb_blob_t *empty = hb_gpu_paint_encode (p, nullptr); |
| g_assert_nonnull (empty); |
| g_assert_cmpuint (hb_blob_get_length (empty), ==, 0); |
| hb_blob_destroy (empty); |
| |
| /* Palette config is preserved. */ |
| g_assert_cmpuint (hb_gpu_paint_get_palette (p), ==, 3); |
| |
| /* Paint another glyph -- works because state is clean and config |
| * is still in effect. */ |
| hb_font_get_nominal_glyph (font, 'b', &gid); |
| hb_gpu_paint_glyph (p, font, gid); |
| hb_blob_t *blob2 = hb_gpu_paint_encode (p, nullptr); |
| g_assert_nonnull (blob2); |
| hb_blob_destroy (blob2); |
| |
| hb_gpu_paint_destroy (p); |
| hb_font_destroy (font); |
| hb_face_destroy (face); |
| } |
| |
| static void |
| test_paint_reset (void) |
| { |
| /* paint_reset wipes user configuration too (vs. paint_clear which |
| * keeps it). */ |
| hb_gpu_paint_t *p = hb_gpu_paint_create_or_fail (); |
| hb_gpu_paint_set_palette (p, 5); |
| g_assert_cmpuint (hb_gpu_paint_get_palette (p), ==, 5); |
| hb_gpu_paint_reset (p); |
| g_assert_cmpuint (hb_gpu_paint_get_palette (p), ==, 0); |
| hb_gpu_paint_destroy (p); |
| } |
| |
| static void |
| test_paint_recycle_blob (void) |
| { |
| hb_face_t *face = hb_test_open_font_file (FONT_FILE); |
| hb_font_t *font = hb_font_create (face); |
| |
| hb_gpu_paint_t *p = hb_gpu_paint_create_or_fail (); |
| hb_codepoint_t gid; |
| hb_font_get_nominal_glyph (font, 'a', &gid); |
| |
| hb_gpu_paint_glyph (p, font, gid); |
| hb_blob_t *blob = hb_gpu_paint_encode (p, nullptr); |
| g_assert_nonnull (blob); |
| hb_gpu_paint_recycle_blob (p, blob); |
| |
| /* Encoder still works after recycling. */ |
| hb_gpu_paint_glyph (p, font, gid); |
| hb_blob_t *blob2 = hb_gpu_paint_encode (p, nullptr); |
| g_assert_nonnull (blob2); |
| hb_blob_destroy (blob2); |
| |
| hb_gpu_paint_destroy (p); |
| hb_font_destroy (font); |
| hb_face_destroy (face); |
| } |
| |
| |
| static void |
| test_shapes (void) |
| { |
| hb_gpu_draw_t *draw = hb_gpu_draw_create_or_fail (); |
| g_assert_nonnull (draw); |
| /* Use a reasonable scale so the coordinates below stay in |
| * the encoder's representable range. */ |
| hb_gpu_draw_set_scale (draw, 1000, 1000); |
| |
| hb_draw_funcs_t *dfuncs = hb_gpu_draw_get_funcs (draw); |
| hb_draw_state_t st = HB_DRAW_STATE_DEFAULT; |
| |
| /* Tapered line. */ |
| hb_draw_line (dfuncs, draw, &st, |
| 10.f, 10.f, 4.f, |
| 50.f, 30.f, 2.f, |
| HB_DRAW_LINE_CAP_BUTT); |
| hb_glyph_extents_t ext; |
| hb_blob_t *blob = hb_gpu_draw_encode (draw, &ext); |
| g_assert_nonnull (blob); |
| g_assert_cmpuint (hb_blob_get_length (blob), >, 0); |
| g_assert_cmpint (ext.width, >, 0); |
| hb_blob_destroy (blob); |
| |
| /* Degenerate line (zero length) is silently a no-op. */ |
| hb_draw_line (dfuncs, draw, &st, |
| 10.f, 10.f, 3.f, |
| 10.f, 10.f, 3.f, |
| HB_DRAW_LINE_CAP_BUTT); |
| blob = hb_gpu_draw_encode (draw, &ext); |
| g_assert_cmpuint (hb_blob_get_length (blob), ==, 0); |
| hb_blob_destroy (blob); |
| |
| /* NaN second width defaults to first; square cap extends each |
| * end by half the stroke width along the line direction. |
| * Horizontal line of length 40, stroke width 4: with butt |
| * caps the bbox width would be 40, with square caps it's |
| * 40 + 4 = 44. */ |
| hb_draw_line (dfuncs, draw, &st, |
| 10.f, 10.f, 4.f, |
| 50.f, 10.f, (float) std::nan (""), |
| HB_DRAW_LINE_CAP_SQUARE); |
| blob = hb_gpu_draw_encode (draw, &ext); |
| g_assert_nonnull (blob); |
| g_assert_cmpint (ext.width, ==, 44); |
| hb_blob_destroy (blob); |
| |
| /* Filled rect (stroke_width = NaN). */ |
| hb_draw_rectangle (dfuncs, draw, &st, |
| 0.f, 0.f, 20.f, 10.f, |
| (float) std::nan ("")); |
| blob = hb_gpu_draw_encode (draw, &ext); |
| g_assert_nonnull (blob); |
| g_assert_cmpuint (hb_blob_get_length (blob), >, 0); |
| g_assert_cmpint (ext.width, ==, 20); |
| hb_blob_destroy (blob); |
| |
| /* Stroked rect: outer edge extends stroke_width/2 beyond the |
| * nominal rect on each side. */ |
| hb_draw_rectangle (dfuncs, draw, &st, |
| 0.f, 0.f, 20.f, 10.f, 2.f); |
| blob = hb_gpu_draw_encode (draw, &ext); |
| g_assert_nonnull (blob); |
| g_assert_cmpuint (hb_blob_get_length (blob), >, 0); |
| g_assert_cmpint (ext.width, ==, 22); |
| hb_blob_destroy (blob); |
| |
| /* Filled circle. */ |
| hb_draw_circle (dfuncs, draw, &st, |
| 50.f, 50.f, 25.f, |
| (float) std::nan ("")); |
| blob = hb_gpu_draw_encode (draw, &ext); |
| g_assert_nonnull (blob); |
| g_assert_cmpuint (hb_blob_get_length (blob), >, 0); |
| hb_blob_destroy (blob); |
| |
| /* Stroked circle. */ |
| hb_draw_circle (dfuncs, draw, &st, |
| 50.f, 50.f, 25.f, 2.f); |
| blob = hb_gpu_draw_encode (draw, &ext); |
| g_assert_nonnull (blob); |
| g_assert_cmpuint (hb_blob_get_length (blob), >, 0); |
| hb_blob_destroy (blob); |
| |
| hb_gpu_draw_destroy (draw); |
| } |
| |
| |
| int |
| main (int argc, char **argv) |
| { |
| hb_test_init (&argc, &argv); |
| |
| hb_test_add (test_create_destroy); |
| hb_test_add (test_empty_encode); |
| hb_test_add (test_encode_glyph); |
| hb_test_add (test_reset); |
| hb_test_add (test_draw_funcs); |
| hb_test_add (test_shader_sources); |
| hb_test_add (test_encode_quantizes_extents_outward); |
| hb_test_add (test_encode_preserves_touching_contours); |
| hb_test_add (test_recycle_blob); |
| hb_test_add (test_extents_saturate_overflow); |
| hb_test_add (test_shapes); |
| |
| hb_test_add (test_paint_create_destroy); |
| hb_test_add (test_paint_get_funcs); |
| hb_test_add (test_paint_shader_sources); |
| hb_test_add (test_paint_empty_encode); |
| hb_test_add (test_paint_encode_monochrome); |
| hb_test_add (test_paint_encode_color); |
| hb_test_add (test_paint_auto_clear_on_encode); |
| hb_test_add (test_paint_reset); |
| hb_test_add (test_paint_recycle_blob); |
| |
| return hb_test_run (); |
| } |