From 7c98910488b1c342d40b131b04ad0cde7aedc8ff Mon Sep 17 00:00:00 2001 From: Bilal Elmoussaoui Date: Thu, 21 Sep 2023 12:05:24 +0200 Subject: [PATCH] mtk: Add a Region type Currently, we use cairo_region_t despite it being a thing wrapper around pixman_region_32 In order to push for a cairo-less and wayland only build in the future, replace cairo_region_t with a thin wrapper that is almost a copy of the upstream cairo implementation Part-of: --- meson.build | 2 + mtk/meson.build | 1 + mtk/mtk/meson.build | 3 + mtk/mtk/mtk-region-private.h | 31 +++ mtk/mtk/mtk-region.c | 354 +++++++++++++++++++++++++++++++++++ mtk/mtk/mtk-region.h | 117 ++++++++++++ mtk/mtk/mtk.h | 1 + src/tests/mtk/meson.build | 7 + src/tests/mtk/region-tests.c | 90 +++++++++ 9 files changed, 606 insertions(+) create mode 100644 mtk/mtk/mtk-region-private.h create mode 100644 mtk/mtk/mtk-region.c create mode 100644 mtk/mtk/mtk-region.h create mode 100644 src/tests/mtk/region-tests.c diff --git a/meson.build b/meson.build index efeb4a4db..02df4302c 100644 --- a/meson.build +++ b/meson.build @@ -26,6 +26,7 @@ uprof_req = '>= 0.3' pango_req = '>= 1.46.0' cairo_req = '>= 1.10.0' pangocairo_req = '>= 1.20' +pixman_req = '>= 0.42' gsettings_desktop_schemas_req = '>= 40.alpha' json_glib_req = '>= 0.12.0' x11_req = '>= 1.7.0' @@ -111,6 +112,7 @@ pango_dep = dependency('pango', version: pango_req) cairo_dep = dependency('cairo', version: cairo_req) cairo_gobject_dep = dependency('cairo-gobject', version: cairo_req) pangocairo_dep = dependency('pangocairo', version: pangocairo_req) +pixman_dep = dependency('pixman-1', version: pixman_req) fribidi_dep = dependency('fribidi', version: fribidi_req) gsettings_desktop_schemas_dep = dependency('gsettings-desktop-schemas', version: gsettings_desktop_schemas_req) diff --git a/mtk/meson.build b/mtk/meson.build index 0de6b8a7b..1b2609c50 100644 --- a/mtk/meson.build +++ b/mtk/meson.build @@ -33,6 +33,7 @@ mtk_pkg_deps = [ gobject_dep, gio_dep, graphene_dep, + pixman_dep, ] if have_x11 diff --git a/mtk/mtk/meson.build b/mtk/mtk/meson.build index 8dca0558e..dd4d95259 100644 --- a/mtk/mtk/meson.build +++ b/mtk/mtk/meson.build @@ -4,10 +4,12 @@ mtk_headers = [ 'mtk.h', 'mtk-macros.h', 'mtk-rectangle.h', + 'mtk-region.h', ] mtk_sources = [ 'mtk-rectangle.c', + 'mtk-region.c', ] if have_x11 @@ -19,6 +21,7 @@ if have_x11 endif mtk_private_headers = [ + 'mtk-region-private.h', ] diff --git a/mtk/mtk/mtk-region-private.h b/mtk/mtk/mtk-region-private.h new file mode 100644 index 000000000..9ced6fad7 --- /dev/null +++ b/mtk/mtk/mtk-region-private.h @@ -0,0 +1,31 @@ +/* + * Mtk + * + * A low-level base library. + * + * Copyright (C) 2023 Red Hat + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#pragma once + +#include + +struct _MtkRegion +{ + gatomicrefcount ref_count; + + pixman_region32_t inner_region; +}; diff --git a/mtk/mtk/mtk-region.c b/mtk/mtk/mtk-region.c new file mode 100644 index 000000000..e01ed0c49 --- /dev/null +++ b/mtk/mtk/mtk-region.c @@ -0,0 +1,354 @@ +/* + * Mtk + * + * A low-level base library. + * + * Copyright (C) 2023 Red Hat + * + * The implementation is heavily inspired by cairo_region_t. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#include "config.h" +#include "mtk/mtk-region.h" +#include "mtk/mtk-region-private.h" + +/** + * mtk_region_ref: + * @region: A region + * + * Increases the reference count + * + * Returns: (transfer none): The region + */ +MtkRegion * +mtk_region_ref (MtkRegion *region) +{ + g_return_val_if_fail (region != NULL, NULL); + + g_atomic_ref_count_inc (®ion->ref_count); + return region; +} + +void +mtk_region_unref (MtkRegion *region) +{ + g_return_if_fail (region != NULL); + + if (g_atomic_ref_count_dec (®ion->ref_count)) + { + pixman_region32_fini (®ion->inner_region); + g_free (region); + } +} + +G_DEFINE_BOXED_TYPE (MtkRegion, mtk_region, + mtk_region_ref, mtk_region_unref); + +MtkRegion * +mtk_region_create (void) +{ + MtkRegion *region; + + region = g_new0 (MtkRegion, 1); + + g_atomic_ref_count_init (®ion->ref_count); + pixman_region32_init (®ion->inner_region); + + return region; +} + + +/** + * mtk_region_copy: + * @region: The region to copy + * + * Returns: (transfer full): A copy of the passed region + */ +MtkRegion * +mtk_region_copy (const MtkRegion *region) +{ + g_autoptr (MtkRegion) copy = NULL; + + g_return_val_if_fail (region != NULL, NULL); + + copy = mtk_region_create (); + + if (!pixman_region32_copy (©->inner_region, + ®ion->inner_region)) + return NULL; + + return g_steal_pointer (©); +} + +gboolean +mtk_region_equal (const MtkRegion *region, + const MtkRegion *other) +{ + if (region == other) + return TRUE; + + if (region == NULL || other == NULL) + return FALSE; + + return pixman_region32_equal (®ion->inner_region, + &other->inner_region); +} + +gboolean +mtk_region_is_empty (const MtkRegion *region) +{ + g_return_val_if_fail (region != NULL, TRUE); + + return !pixman_region32_not_empty (®ion->inner_region); +} + +MtkRectangle +mtk_region_get_extents (const MtkRegion *region) +{ + pixman_box32_t *extents; + + g_return_val_if_fail (region != NULL, MTK_RECTANGLE_INIT (0, 0, 0, 0)); + + extents = pixman_region32_extents (®ion->inner_region); + return MTK_RECTANGLE_INIT (extents->x1, + extents->y1, + extents->x2 - extents->x1, + extents->y2 - extents->y1); +} + +int +mtk_region_num_rectangles (const MtkRegion *region) +{ + g_return_val_if_fail (region != NULL, 0); + + return pixman_region32_n_rects (®ion->inner_region); +} + +void +mtk_region_translate (MtkRegion *region, + int dx, + int dy) +{ + g_return_if_fail (region != NULL); + + pixman_region32_translate (®ion->inner_region, dx, dy); +} + +gboolean +mtk_region_contains_point (MtkRegion *region, + int x, + int y) +{ + g_return_val_if_fail (region != NULL, FALSE); + + return pixman_region32_contains_point (®ion->inner_region, x, y, NULL); +} + +void +mtk_region_union (MtkRegion *region, + const MtkRegion *other) +{ + g_return_if_fail (region != NULL); + g_return_if_fail (other != NULL); + + pixman_region32_union (®ion->inner_region, + ®ion->inner_region, + &other->inner_region); +} + +void +mtk_region_union_rectangle (MtkRegion *region, + const MtkRectangle *rect) +{ + pixman_region32_t pixman_region; + + g_return_if_fail (region != NULL); + g_return_if_fail (rect != NULL); + + pixman_region32_init_rect (&pixman_region, + rect->x, rect->y, + rect->width, rect->height); + pixman_region32_union (®ion->inner_region, + ®ion->inner_region, + &pixman_region); + pixman_region32_fini (&pixman_region); +} + +void +mtk_region_subtract (MtkRegion *region, + const MtkRegion *other) +{ + g_return_if_fail (region != NULL); + g_return_if_fail (other != NULL); + + pixman_region32_subtract (®ion->inner_region, + ®ion->inner_region, + &other->inner_region); +} + +void +mtk_region_subtract_rectangle (MtkRegion *region, + const MtkRectangle *rect) +{ + g_return_if_fail (region != NULL); + g_return_if_fail (rect != NULL); + + pixman_region32_t pixman_region; + pixman_region32_init_rect (&pixman_region, + rect->x, rect->y, + rect->width, rect->height); + + pixman_region32_subtract (®ion->inner_region, + ®ion->inner_region, + &pixman_region); + pixman_region32_fini (&pixman_region); +} + +void +mtk_region_intersect (MtkRegion *region, + const MtkRegion *other) +{ + g_return_if_fail (region != NULL); + g_return_if_fail (other != NULL); + + pixman_region32_intersect (®ion->inner_region, + ®ion->inner_region, + &other->inner_region); +} + +void +mtk_region_intersect_rectangle (MtkRegion *region, + const MtkRectangle *rect) +{ + pixman_region32_t pixman_region; + + g_return_if_fail (region != NULL); + + pixman_region32_init_rect (&pixman_region, + rect->x, rect->y, + rect->width, rect->height); + + pixman_region32_intersect (®ion->inner_region, + ®ion->inner_region, + &pixman_region); + pixman_region32_fini (&pixman_region); +} + +MtkRectangle +mtk_region_get_rectangle (const MtkRegion *region, + int nth) +{ + pixman_box32_t *box; + + g_return_val_if_fail (region != NULL, MTK_RECTANGLE_INIT (0, 0, 0, 0)); + + box = pixman_region32_rectangles (®ion->inner_region, NULL) + nth; + return MTK_RECTANGLE_INIT (box->x1, box->y1, box->x2 - box->x1, box->y2 - box->y1); +} + +MtkRegion * +mtk_region_create_rectangle (const MtkRectangle *rect) +{ + MtkRegion *region; + g_return_val_if_fail (rect != NULL, NULL); + + region = g_new0 (MtkRegion, 1); + g_atomic_ref_count_init (®ion->ref_count); + pixman_region32_init_rect (®ion->inner_region, + rect->x, rect->y, + rect->width, rect->height); + return region; +} + +MtkRegion * +mtk_region_create_rectangles (const MtkRectangle *rects, + int n_rects) +{ + pixman_box32_t stack_boxes[512 * sizeof (int) / sizeof (pixman_box32_t)]; + pixman_box32_t *boxes = stack_boxes; + int i; + g_autoptr (MtkRegion) region = NULL; + + g_return_val_if_fail (rects != NULL, NULL); + g_return_val_if_fail (n_rects != 0, NULL); + + region = g_new0 (MtkRegion, 1); + g_atomic_ref_count_init (®ion->ref_count); + + if (n_rects == 1) + { + pixman_region32_init_rect (®ion->inner_region, + rects->x, rects->y, + rects->width, rects->height); + + return g_steal_pointer (®ion); + } + + if (n_rects > sizeof (stack_boxes) / sizeof (stack_boxes[0])) + { + boxes = g_new0 (pixman_box32_t, n_rects); + if (G_UNLIKELY (boxes == NULL)) + return NULL; + } + + for (i = 0; i < n_rects; i++) + { + boxes[i].x1 = rects[i].x; + boxes[i].y1 = rects[i].y; + boxes[i].x2 = rects[i].x + rects[i].width; + boxes[i].y2 = rects[i].y + rects[i].height; + } + + i = pixman_region32_init_rects (®ion->inner_region, + boxes, n_rects); + + if (boxes != stack_boxes) + free (boxes); + + if (G_UNLIKELY (i == 0)) + return NULL; + + return g_steal_pointer (®ion); +} + +MtkRegionOverlap +mtk_region_contains_rectangle (const MtkRegion *region, + const MtkRectangle *rect) +{ + pixman_box32_t box; + pixman_region_overlap_t overlap; + + g_return_val_if_fail (region != NULL, MTK_REGION_OVERLAP_OUT); + g_return_val_if_fail (rect != NULL, MTK_REGION_OVERLAP_OUT); + + box.x1 = rect->x; + box.y1 = rect->y; + box.x2 = rect->x + rect->width; + box.y2 = rect->y + rect->height; + + overlap = pixman_region32_contains_rectangle (®ion->inner_region, + &box); + switch (overlap) + { + default: + case PIXMAN_REGION_OUT: + return MTK_REGION_OVERLAP_OUT; + case PIXMAN_REGION_IN: + return MTK_REGION_OVERLAP_IN; + case PIXMAN_REGION_PART: + return MTK_REGION_OVERLAP_PART; + } +} diff --git a/mtk/mtk/mtk-region.h b/mtk/mtk/mtk-region.h new file mode 100644 index 000000000..f5ea9bbfa --- /dev/null +++ b/mtk/mtk/mtk-region.h @@ -0,0 +1,117 @@ +/* + * Mtk + * + * A low-level base library. + * + * Copyright (C) 2023 Red Hat + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#pragma once + +#include + +#include "mtk/mtk-rectangle.h" +#include "mtk/mtk-macros.h" + +#define MTK_TYPE_REGION (mtk_region_get_type ()) + +typedef enum _MtkRegionOverlap +{ + MTK_REGION_OVERLAP_OUT, + MTK_REGION_OVERLAP_IN, + MTK_REGION_OVERLAP_PART, +} MtkRegionOverlap; + +typedef struct _MtkRegion MtkRegion; + +MTK_EXPORT +GType mtk_region_get_type (void); + +MTK_EXPORT +MtkRegion * mtk_region_copy (const MtkRegion *region); + +MTK_EXPORT +MtkRegion * mtk_region_ref (MtkRegion *region); + +MTK_EXPORT +void mtk_region_unref (MtkRegion *region); + +MTK_EXPORT +MtkRegion * mtk_region_create (void); + +MTK_EXPORT +gboolean mtk_region_equal (const MtkRegion *region, + const MtkRegion *other); + +MTK_EXPORT +gboolean mtk_region_is_empty (const MtkRegion *region); + +MTK_EXPORT +MtkRectangle mtk_region_get_extents (const MtkRegion *region); + +MTK_EXPORT +int mtk_region_num_rectangles (const MtkRegion *region); + +MTK_EXPORT +void mtk_region_translate (MtkRegion *region, + int dx, + int dy); + +MTK_EXPORT +gboolean mtk_region_contains_point (MtkRegion *region, + int x, + int y); + +MTK_EXPORT +void mtk_region_union (MtkRegion *region, + const MtkRegion *other); + +MTK_EXPORT +void mtk_region_union_rectangle (MtkRegion *region, + const MtkRectangle *rect); + +MTK_EXPORT +void mtk_region_subtract_rectangle (MtkRegion *region, + const MtkRectangle *rect); + +MTK_EXPORT +void mtk_region_subtract (MtkRegion *region, + const MtkRegion *other); + +MTK_EXPORT +void mtk_region_intersect (MtkRegion *region, + const MtkRegion *other); + +MTK_EXPORT +void mtk_region_intersect_rectangle (MtkRegion *region, + const MtkRectangle *rect); + +MTK_EXPORT +MtkRectangle mtk_region_get_rectangle (const MtkRegion *region, + int nth); + +MTK_EXPORT +MtkRegion * mtk_region_create_rectangle (const MtkRectangle *rect); + +MTK_EXPORT +MtkRegion * mtk_region_create_rectangles (const MtkRectangle *rects, + int n_rects); + +MTK_EXPORT +MtkRegionOverlap mtk_region_contains_rectangle (const MtkRegion *region, + const MtkRectangle *rect); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (MtkRegion, mtk_region_unref) diff --git a/mtk/mtk/mtk.h b/mtk/mtk/mtk.h index 7645e3841..7bc7e0ecc 100644 --- a/mtk/mtk/mtk.h +++ b/mtk/mtk/mtk.h @@ -24,6 +24,7 @@ #define __MTK_H_INSIDE__ #include "mtk/mtk-rectangle.h" +#include "mtk/mtk-region.h" #include "mtk/mtk-macros.h" #undef __MTK_H_INSIDE__ diff --git a/src/tests/mtk/meson.build b/src/tests/mtk/meson.build index c4ac13507..e39d61331 100644 --- a/src/tests/mtk/meson.build +++ b/src/tests/mtk/meson.build @@ -6,4 +6,11 @@ test_cases += [ 'mtk/rectangle-tests.c', ], }, + { + 'name': 'mtk-region', + 'suite': 'unit', + 'sources': [ + 'mtk/region-tests.c', + ] + } ] diff --git a/src/tests/mtk/region-tests.c b/src/tests/mtk/region-tests.c new file mode 100644 index 000000000..7e41a53f0 --- /dev/null +++ b/src/tests/mtk/region-tests.c @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2023 Bilal Elmoussaoui + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include "config.h" +#include "mtk/mtk.h" + +#include + + +static void +test_contains_point (void) +{ + g_autoptr (MtkRegion) r1 = NULL; + + r1 = mtk_region_create_rectangle (mtk_rectangle_new (0, 0, 100, 100)); + + g_assert (!mtk_region_contains_point (r1, 200, 200)); + g_assert (mtk_region_contains_point (r1, 50, 50)); +} + +/* A re-implementation of a pixman translation test */ +#define LARGE 32000 +static void +test_translate (void) +{ + MtkRectangle rect = MTK_RECTANGLE_INIT (-LARGE, -LARGE, LARGE, LARGE); + g_autoptr (MtkRegion) r1, r2 = NULL; + + r1 = mtk_region_create_rectangles (&rect, 1); + g_assert_cmpint (mtk_region_num_rectangles (r1), ==, 1); + r2 = mtk_region_create_rectangle (&rect); + g_assert_cmpint (mtk_region_num_rectangles (r2), ==, 1); + + g_assert (mtk_region_equal (r1, r2)); + + mtk_region_translate (r1, -LARGE, LARGE); + mtk_region_translate (r1, LARGE, -LARGE); + + g_assert (mtk_region_equal (r1, r2)); +} + +static void +test_region (void) +{ + g_autoptr (MtkRegion) r1 = NULL; + + r1 = mtk_region_create (); + g_assert (mtk_region_is_empty (r1)); + + MtkRectangle rect = MTK_RECTANGLE_INIT (5, 5, 20, 20); + mtk_region_union_rectangle (r1, &rect); + + g_assert (!mtk_region_is_empty (r1)); + MtkRectangle extents = mtk_region_get_extents (r1); + g_assert (mtk_rectangle_equal (&extents, &rect)); + + mtk_region_translate (r1, 15, 20); + extents = mtk_region_get_extents (r1); + g_assert_cmpint (extents.x, ==, rect.x + 15); + g_assert_cmpint (extents.y, ==, rect.y + 20); + g_assert_cmpint (extents.width, ==, rect.width); + g_assert_cmpint (extents.height, ==, rect.height); +} + +int +main (int argc, + char **argv) +{ + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/mtk/region/region", test_region); + g_test_add_func ("/mtk/region/contains-point", test_contains_point); + g_test_add_func ("/mtk/region/translate", test_translate); + + return g_test_run (); +}