#pragma once #include "gdk/gdkdihedralprivate.h" #include #include #define GSK_RECT_INIT_CAIRO(cairo_rect) GRAPHENE_RECT_INIT((cairo_rect)->x, (cairo_rect)->y, (cairo_rect)->width, (cairo_rect)->height) static inline void gsk_rect_init (graphene_rect_t *r, float x, float y, float width, float height) { r->origin.x = x; r->origin.y = y; r->size.width = width; r->size.height = height; } static inline void gsk_rect_init_from_rect (graphene_rect_t *r, const graphene_rect_t *r1) { gsk_rect_init (r, r1->origin.x, r1->origin.y, r1->size.width, r1->size.height); } static inline void gsk_rect_init_offset (graphene_rect_t *r, const graphene_rect_t *src, const graphene_point_t *offset) { gsk_rect_init (r, src->origin.x + offset->x, src->origin.y + offset->y, src->size.width, src->size.height); } static inline gboolean G_GNUC_PURE gsk_rect_contains_rect (const graphene_rect_t *r1, const graphene_rect_t *r2) { return r2->origin.x >= r1->origin.x && (r2->origin.x + r2->size.width) <= (r1->origin.x + r1->size.width) && r2->origin.y >= r1->origin.y && (r2->origin.y + r2->size.height) <= (r1->origin.y + r1->size.height); } static inline gboolean G_GNUC_PURE gsk_rect_intersects (const graphene_rect_t *r1, const graphene_rect_t *r2) { float x1, y1, x2, y2; /* Assume both rects are already normalized, as they usually are */ x1 = MAX (r1->origin.x, r2->origin.x); y1 = MAX (r1->origin.y, r2->origin.y); x2 = MIN (r1->origin.x + r1->size.width, r2->origin.x + r2->size.width); y2 = MIN (r1->origin.y + r1->size.height, r2->origin.y + r2->size.height); if (x1 >= x2 || y1 >= y2) return FALSE; else return TRUE; } static inline gboolean gsk_rect_intersection (const graphene_rect_t *r1, const graphene_rect_t *r2, graphene_rect_t *res) { float x1, y1, x2, y2; /* Assume both rects are already normalized, as they usually are */ x1 = MAX (r1->origin.x, r2->origin.x); y1 = MAX (r1->origin.y, r2->origin.y); x2 = MIN (r1->origin.x + r1->size.width, r2->origin.x + r2->size.width); y2 = MIN (r1->origin.y + r1->size.height, r2->origin.y + r2->size.height); if (x1 >= x2 || y1 >= y2) { gsk_rect_init (res, 0.f, 0.f, 0.f, 0.f); return FALSE; } else { gsk_rect_init (res, x1, y1, x2 - x1, y2 - y1); return TRUE; } } /** * gsk_rect_coverage: * @r1: a valid rectangle * @r2: another valid rectangle * @res: The result, may be one of r1/r2 * * Computes the largest rectangle that is fully covered by * r1 and r2. * * Note that this is different from a union, which is the smallest * rectangle that covers the rectangles. * * The use case for this function is joining opaque rectangles. **/ static inline void gsk_rect_coverage (const graphene_rect_t *r1, const graphene_rect_t *r2, graphene_rect_t *res) { float x1min, y1min, x2min, y2min; float x1max, y1max, x2max, y2max; float size, size2; graphene_rect_t r; /* Assumes both rects are already normalized, as they usually are */ size = r1->size.width * r1->size.height; size2 = r2->size.width * r2->size.height; if (size >= size2) { r = *r1; } else { r = *r2; size = size2; } x1min = MIN (r1->origin.x, r2->origin.x); y1min = MIN (r1->origin.y, r2->origin.y); x1max = MAX (r1->origin.x, r2->origin.x); y1max = MAX (r1->origin.y, r2->origin.y); x2min = MIN (r1->origin.x + r1->size.width, r2->origin.x + r2->size.width); y2min = MIN (r1->origin.y + r1->size.height, r2->origin.y + r2->size.height); x2max = MAX (r1->origin.x + r1->size.width, r2->origin.x + r2->size.width); y2max = MAX (r1->origin.y + r1->size.height, r2->origin.y + r2->size.height); if (x2min >= x1max && y2min >= y1max) { float w, h; w = x2min - x1max; h = y2max - y1min; size2 = w * h; if (size2 > size) { r = GRAPHENE_RECT_INIT (x1max, y1min, w, h); size = size2; } w = x2max - x1min; h = y2min - y1max; size2 = w * h; if (size2 > size) { r = GRAPHENE_RECT_INIT (x1min, y1max, w, h); size = size2; } } *res = r; } /** * gsk_rect_snap_to_grid: * @src: rectangle to snap * @grid_scale: the scale of the grid * @grid_offset: the offset of the grid * @dest: target to snap to. Can be identical to source * * Snaps @src to the grid specified by the given scale * and offset. * Grid points to snap to will be at the given offset and * then spaced apart by the inverse of the given scale, * ie an offset of 0.5 and a scale of 3 will snap to * (..., 0.1667, 0.5, 0.8333, 1.1667, 1.5, ...). * * Snapping is done by growing the rectangle. * * Note that floating point rounding issues might result * in the snapping not being perfectly exact. **/ static inline void gsk_rect_snap_to_grid (const graphene_rect_t *src, const graphene_vec2_t *grid_scale, const graphene_point_t *grid_offset, graphene_rect_t *dest) { float x, y, xscale, yscale; xscale = graphene_vec2_get_x (grid_scale); yscale = graphene_vec2_get_y (grid_scale); x = floorf ((src->origin.x + grid_offset->x) * xscale); y = floorf ((src->origin.y + grid_offset->y) * yscale); *dest = GRAPHENE_RECT_INIT ( x / xscale - grid_offset->x, y / yscale - grid_offset->y, (ceilf ((src->origin.x + grid_offset->x + src->size.width) * xscale) - x) / xscale, (ceilf ((src->origin.y + grid_offset->y + src->size.height) * yscale) - y) / yscale); } static inline gboolean G_GNUC_PURE gsk_rect_is_empty (const graphene_rect_t *rect) { return rect->size.width == 0 || rect->size.height == 0; } static inline void gsk_rect_to_float (const graphene_rect_t *rect, float values[4]) { values[0] = rect->origin.x; values[1] = rect->origin.y; values[2] = rect->size.width; values[3] = rect->size.height; } static inline void gsk_rect_to_cairo_grow (const graphene_rect_t *graphene, cairo_rectangle_int_t *cairo) { cairo->x = floorf (graphene->origin.x); cairo->y = floorf (graphene->origin.y); cairo->width = ceilf (graphene->origin.x + graphene->size.width) - cairo->x; cairo->height = ceilf (graphene->origin.y + graphene->size.height) - cairo->y; } static inline void gsk_rect_to_cairo_shrink (const graphene_rect_t *graphene, cairo_rectangle_int_t *cairo) { cairo->x = ceilf (graphene->origin.x); cairo->y = ceilf (graphene->origin.y); cairo->width = floorf (graphene->origin.x + graphene->size.width) - cairo->x; cairo->height = floorf (graphene->origin.y + graphene->size.height) - cairo->y; } static inline gboolean gsk_rect_equal (const graphene_rect_t *r1, const graphene_rect_t *r2) { return r1->origin.x == r2->origin.x && r1->origin.y == r2->origin.y && r1->size.width == r2->size.width && r1->size.height == r2->size.height; } static inline void gsk_gpu_rect_to_float (const graphene_rect_t *rect, const graphene_point_t *offset, float values[4]) { values[0] = rect->origin.x + offset->x; values[1] = rect->origin.y + offset->y; values[2] = rect->size.width; values[3] = rect->size.height; } static inline void gsk_rect_round_larger (graphene_rect_t *rect) { float x = floor (rect->origin.x); float y = floor (rect->origin.y); *rect = GRAPHENE_RECT_INIT (x, y, ceil (rect->origin.x + rect->size.width) - x, ceil (rect->origin.y + rect->size.height) - y); } static inline void gsk_rect_scale (const graphene_rect_t *r, float sx, float sy, graphene_rect_t *res) { if (G_UNLIKELY (sx < 0 || sy < 0)) { graphene_rect_scale (r, sx, sy, res); return; } res->origin.x = r->origin.x * sx; res->origin.y = r->origin.y * sy; res->size.width = r->size.width * sx; res->size.height = r->size.height * sy; } static inline void gsk_rect_normalize (graphene_rect_t *r) { if (r->size.width < 0.f) { float size = fabsf (r->size.width); r->origin.x -= size; r->size.width = size; } if (r->size.height < 0.f) { float size = fabsf (r->size.height); r->origin.y -= size; r->size.height = size; } } static inline void gsk_rect_dihedral (const graphene_rect_t *src, GdkDihedral dihedral, graphene_rect_t *res) { float xx, xy, yx, yy; gdk_dihedral_get_mat2 (dihedral, &xx, &xy, &yx, &yy); graphene_rect_init (res, (xx * src->origin.x + xy * src->origin.y), (yx * src->origin.x + yy * src->origin.y), (xx * src->size.width + xy * src->size.height), (yx * src->size.width + yy * src->size.height)); gsk_rect_normalize (res); }