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 ();
+}