Skip to content
Commits on Source (7)
......@@ -23,9 +23,9 @@
*/
/* cliptest:
* For debugging the rect_to_quad() and clip_quad() functions. An arbitrary
* quad (red) is transformed from global coordinate space to surface
* coordinate space and clipped to an axis-aligned rect (blue).
* For debugging the quad clipper. An arbitrary quad (red) is transformed
* from global coordinate space to surface coordinate space and clipped to
* an axis-aligned rect (blue).
*
* controls:
* surface rect position: mouse left drag, keys: w a s d
......@@ -111,8 +111,8 @@ weston_coord_global_to_surface(struct weston_view *view, struct weston_coord_glo
/* Keep this in sync with what is in gl-renderer.c! */
static void
rect_to_quad(pixman_box32_t *rect, struct weston_view *ev,
struct gl_quad *quad)
global_to_surface(pixman_box32_t *rect, struct weston_view *ev,
struct clipper_vertex polygon[4], bool *axis_aligned)
{
struct weston_coord_global rect_g[4] = {
{ .c = weston_coord(rect->x1, rect->y1) },
......@@ -123,32 +123,14 @@ rect_to_quad(pixman_box32_t *rect, struct weston_view *ev,
struct weston_coord rect_s;
int i;
/* Transform rect to surface space. */
quad->vertices.n = 4;
for (i = 0; i < quad->vertices.n; i++) {
for (i = 0; i < 4; i++) {
rect_s = weston_coord_global_to_surface(ev, rect_g[i]).c;
quad->vertices.pos[i].x = (float)rect_s.x;
quad->vertices.pos[i].y = (float)rect_s.y;
polygon[i].x = (float)rect_s.x;
polygon[i].y = (float)rect_s.y;
}
quad->axis_aligned = !ev->transform.enabled ||
*axis_aligned = !ev->transform.enabled ||
(ev->transform.matrix.type < WESTON_MATRIX_TRANSFORM_ROTATE);
/* Find axis-aligned bounding box. */
if (!quad->axis_aligned) {
quad->bbox.x1 = quad->bbox.x2 = quad->vertices.pos[0].x;
quad->bbox.y1 = quad->bbox.y2 = quad->vertices.pos[0].y;
for (i = 1; i < quad->vertices.n; i++) {
quad->bbox.x1 = MIN(quad->bbox.x1,
quad->vertices.pos[i].x);
quad->bbox.x2 = MAX(quad->bbox.x2,
quad->vertices.pos[i].x);
quad->bbox.y1 = MIN(quad->bbox.y1,
quad->vertices.pos[i].y);
quad->bbox.y2 = MAX(quad->bbox.y2,
quad->vertices.pos[i].y);
}
}
}
/* ---------------------- copied ends -----------------------*/
......@@ -199,7 +181,7 @@ struct cliptest {
};
static void
draw_polygon_closed(cairo_t *cr, struct clip_vertex *pos, int n)
draw_polygon_closed(cairo_t *cr, struct clipper_vertex *pos, int n)
{
int i;
......@@ -210,7 +192,7 @@ draw_polygon_closed(cairo_t *cr, struct clip_vertex *pos, int n)
}
static void
draw_polygon_labels(cairo_t *cr, struct clip_vertex *pos, int n)
draw_polygon_labels(cairo_t *cr, struct clipper_vertex *pos, int n)
{
char str[16];
int i;
......@@ -223,7 +205,7 @@ draw_polygon_labels(cairo_t *cr, struct clip_vertex *pos, int n)
}
static void
draw_coordinates(cairo_t *cr, double ox, double oy, struct clip_vertex *pos, int n)
draw_coordinates(cairo_t *cr, double ox, double oy, struct clipper_vertex *pos, int n)
{
char str[64];
int i;
......@@ -240,7 +222,7 @@ draw_coordinates(cairo_t *cr, double ox, double oy, struct clip_vertex *pos, int
static void
draw_box(cairo_t *cr, pixman_box32_t *box, struct weston_view *view)
{
struct clip_vertex pos[4];
struct clipper_vertex pos[4];
if (view) {
weston_view_from_global_float(view, box->x1, box->y1, &pos[0].x, &pos[0].y);
......@@ -259,7 +241,7 @@ draw_box(cairo_t *cr, pixman_box32_t *box, struct weston_view *view)
static void
draw_geometry(cairo_t *cr, struct weston_view *view,
struct clip_vertex *v, int n)
struct clipper_vertex *v, int n)
{
struct geometry *g = view->geometry;
float cx, cy;
......@@ -295,12 +277,15 @@ redraw_handler(struct widget *widget, void *data)
struct rectangle allocation;
cairo_t *cr;
cairo_surface_t *surface;
struct gl_quad quad;
struct clip_vertex v[8];
struct clipper_quad quad;
struct clipper_vertex transformed_v[4], v[8];
bool axis_aligned;
int n;
rect_to_quad(&g->quad, &cliptest->view, &quad);
n = clip_quad(&quad, &g->surf, v);
global_to_surface(&g->quad, &cliptest->view, transformed_v,
&axis_aligned);
clipper_quad_init(&quad, transformed_v, axis_aligned);
n = clipper_quad_clip_box32(&quad, &g->surf, v);
widget_get_allocation(cliptest->widget, &allocation);
......@@ -567,8 +552,9 @@ benchmark(void)
struct weston_surface surface;
struct weston_view view;
struct geometry geom;
struct gl_quad quad;
struct clip_vertex v[8];
struct clipper_quad quad;
struct clipper_vertex transformed_v[4], v[8];
bool axis_aligned;
int i;
double t;
const int N = 1000000;
......@@ -593,8 +579,10 @@ benchmark(void)
reset_timer();
for (i = 0; i < N; i++) {
geometry_set_phi(&geom, (float)i / 360.0f);
rect_to_quad(&geom.quad, &view, &quad);
clip_quad(&quad, &geom.surf, v);
global_to_surface(&geom.quad, &view, transformed_v,
&axis_aligned);
clipper_quad_init(&quad, transformed_v, axis_aligned);
clipper_quad_clip_box32(&quad, &geom.surf, v);
}
t = read_timer();
......
......@@ -472,8 +472,8 @@ timeline_submit_render_sync(struct gl_renderer *gr,
}
static void
rect_to_quad(pixman_box32_t *rect, struct weston_view *ev,
struct gl_quad *quad)
global_to_surface(pixman_box32_t *rect, struct weston_view *ev,
struct clipper_vertex polygon[4], bool *axis_aligned)
{
struct weston_coord_global rect_g[4] = {
{ .c = weston_coord(rect->x1, rect->y1) },
......@@ -484,32 +484,14 @@ rect_to_quad(pixman_box32_t *rect, struct weston_view *ev,
struct weston_coord rect_s;
int i;
/* Transform rect to surface space. */
quad->vertices.n = 4;
for (i = 0; i < quad->vertices.n; i++) {
for (i = 0; i < 4; i++) {
rect_s = weston_coord_global_to_surface(ev, rect_g[i]).c;
quad->vertices.pos[i].x = (float)rect_s.x;
quad->vertices.pos[i].y = (float)rect_s.y;
polygon[i].x = (float)rect_s.x;
polygon[i].y = (float)rect_s.y;
}
quad->axis_aligned = !ev->transform.enabled ||
*axis_aligned = !ev->transform.enabled ||
(ev->transform.matrix.type < WESTON_MATRIX_TRANSFORM_ROTATE);
/* Find axis-aligned bounding box. */
if (!quad->axis_aligned) {
quad->bbox.x1 = quad->bbox.x2 = quad->vertices.pos[0].x;
quad->bbox.y1 = quad->bbox.y2 = quad->vertices.pos[0].y;
for (i = 1; i < quad->vertices.n; i++) {
quad->bbox.x1 = MIN(quad->bbox.x1,
quad->vertices.pos[i].x);
quad->bbox.x2 = MAX(quad->bbox.x2,
quad->vertices.pos[i].x);
quad->bbox.y1 = MIN(quad->bbox.y1,
quad->vertices.pos[i].y);
quad->bbox.y2 = MAX(quad->bbox.y2,
quad->vertices.pos[i].y);
}
}
}
static bool
......@@ -568,13 +550,14 @@ texture_region(struct weston_paint_node *pnode,
struct weston_compositor *ec = pnode->surface->compositor;
struct weston_view *ev = pnode->view;
struct gl_renderer *gr = get_renderer(ec);
struct clip_vertex *v;
struct clipper_vertex *v;
unsigned int *vtxcnt, nvtx = 0;
pixman_box32_t *rects, *surf_rects;
pixman_box32_t *raw_rects;
int i, j, nrects, nsurf, raw_nrects;
bool used_band_compression;
struct gl_quad quad;
bool used_band_compression, axis_aligned;
struct clipper_vertex polygon[4];
struct clipper_quad quad;
raw_rects = pixman_region32_rectangles(region, &raw_nrects);
surf_rects = pixman_region32_rectangles(surf_region, &nsurf);
......@@ -594,7 +577,8 @@ texture_region(struct weston_paint_node *pnode,
vtxcnt = wl_array_add(&gr->vtxcnt, nrects * nsurf * sizeof *vtxcnt);
for (i = 0; i < nrects; i++) {
rect_to_quad(&rects[i], ev, &quad);
global_to_surface(&rects[i], ev, polygon, &axis_aligned);
clipper_quad_init(&quad, polygon, axis_aligned);
for (j = 0; j < nsurf; j++) {
int n;
......@@ -611,7 +595,7 @@ texture_region(struct weston_paint_node *pnode,
* To do this, we first calculate the (up to eight) points at the
* intersection of the edges of the quad and the surface rect.
*/
n = clip_quad(&quad, &surf_rects[j], v);
n = clipper_quad_clip_box32(&quad, &surf_rects[j], v);
if (n >= 3) {
v += n;
vtxcnt[nvtx++] = n;
......
......@@ -25,12 +25,19 @@
#include <assert.h>
#include <float.h>
#include <math.h>
#include <string.h>
#include "shared/helpers.h"
#include "vertex-clipping.h"
struct clip_context {
struct clipper_vertex prev;
struct clipper_vertex box[2];
struct clipper_vertex *vertices;
};
WESTON_EXPORT_FOR_TESTS float
float_difference(float a, float b)
clipper_float_difference(float a, float b)
{
/* https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ */
static const float max_diff = 4.0f * FLT_MIN;
......@@ -57,7 +64,7 @@ clip_intersect_y(float p1x, float p1y, float p2x, float p2y,
float x_arg)
{
float a;
float diff = float_difference(p1x, p2x);
float diff = clipper_float_difference(p1x, p2x);
/* Practically vertical line segment, yet the end points have already
* been determined to be on different sides of the line. Therefore
......@@ -79,7 +86,7 @@ clip_intersect_x(float p1x, float p1y, float p2x, float p2y,
float y_arg)
{
float a;
float diff = float_difference(p1y, p2y);
float diff = clipper_float_difference(p1y, p2y);
/* Practically horizontal line segment, yet the end points have already
* been determined to be on different sides of the line. Therefore
......@@ -111,25 +118,25 @@ clip_append_vertex(struct clip_context *ctx, float x, float y)
static enum path_transition
path_transition_left_edge(struct clip_context *ctx, float x, float y)
{
return ((ctx->prev.x >= ctx->clip.x1) << 1) | (x >= ctx->clip.x1);
return ((ctx->prev.x >= ctx->box[0].x) << 1) | (x >= ctx->box[0].x);
}
static enum path_transition
path_transition_right_edge(struct clip_context *ctx, float x, float y)
{
return ((ctx->prev.x < ctx->clip.x2) << 1) | (x < ctx->clip.x2);
return ((ctx->prev.x < ctx->box[1].x) << 1) | (x < ctx->box[1].x);
}
static enum path_transition
path_transition_top_edge(struct clip_context *ctx, float x, float y)
{
return ((ctx->prev.y >= ctx->clip.y1) << 1) | (y >= ctx->clip.y1);
return ((ctx->prev.y >= ctx->box[0].y) << 1) | (y >= ctx->box[0].y);
}
static enum path_transition
path_transition_bottom_edge(struct clip_context *ctx, float x, float y)
{
return ((ctx->prev.y < ctx->clip.y2) << 1) | (y < ctx->clip.y2);
return ((ctx->prev.y < ctx->box[1].y) << 1) | (y < ctx->box[1].y);
}
static void
......@@ -194,9 +201,14 @@ clip_polygon_topbottom(struct clip_context *ctx,
ctx->prev.y = y;
}
struct polygon8 {
struct clipper_vertex pos[8];
int n;
};
static void
clip_context_prepare(struct clip_context *ctx, const struct polygon8 *src,
struct clip_vertex *dst)
struct clipper_vertex *dst)
{
ctx->prev.x = src->pos[src->n - 1].x;
ctx->prev.y = src->pos[src->n - 1].y;
......@@ -205,7 +217,7 @@ clip_context_prepare(struct clip_context *ctx, const struct polygon8 *src,
static int
clip_polygon_left(struct clip_context *ctx, const struct polygon8 *src,
struct clip_vertex *dst)
struct clipper_vertex *dst)
{
enum path_transition trans;
int i;
......@@ -217,14 +229,14 @@ clip_polygon_left(struct clip_context *ctx, const struct polygon8 *src,
for (i = 0; i < src->n; i++) {
trans = path_transition_left_edge(ctx, src->pos[i].x, src->pos[i].y);
clip_polygon_leftright(ctx, trans, src->pos[i].x, src->pos[i].y,
ctx->clip.x1);
ctx->box[0].x);
}
return ctx->vertices - dst;
}
static int
clip_polygon_right(struct clip_context *ctx, const struct polygon8 *src,
struct clip_vertex *dst)
struct clipper_vertex *dst)
{
enum path_transition trans;
int i;
......@@ -236,14 +248,14 @@ clip_polygon_right(struct clip_context *ctx, const struct polygon8 *src,
for (i = 0; i < src->n; i++) {
trans = path_transition_right_edge(ctx, src->pos[i].x, src->pos[i].y);
clip_polygon_leftright(ctx, trans, src->pos[i].x, src->pos[i].y,
ctx->clip.x2);
ctx->box[1].x);
}
return ctx->vertices - dst;
}
static int
clip_polygon_top(struct clip_context *ctx, const struct polygon8 *src,
struct clip_vertex *dst)
struct clipper_vertex *dst)
{
enum path_transition trans;
int i;
......@@ -255,14 +267,14 @@ clip_polygon_top(struct clip_context *ctx, const struct polygon8 *src,
for (i = 0; i < src->n; i++) {
trans = path_transition_top_edge(ctx, src->pos[i].x, src->pos[i].y);
clip_polygon_topbottom(ctx, trans, src->pos[i].x, src->pos[i].y,
ctx->clip.y1);
ctx->box[0].y);
}
return ctx->vertices - dst;
}
static int
clip_polygon_bottom(struct clip_context *ctx, const struct polygon8 *src,
struct clip_vertex *dst)
struct clipper_vertex *dst)
{
enum path_transition trans;
int i;
......@@ -274,72 +286,94 @@ clip_polygon_bottom(struct clip_context *ctx, const struct polygon8 *src,
for (i = 0; i < src->n; i++) {
trans = path_transition_bottom_edge(ctx, src->pos[i].x, src->pos[i].y);
clip_polygon_topbottom(ctx, trans, src->pos[i].x, src->pos[i].y,
ctx->clip.y2);
ctx->box[1].y);
}
return ctx->vertices - dst;
}
/* General purpose polygon clipping algorithm based on Sutherland-Hodgman:
* https://www.codeguru.com/cplusplus/polygon-clipping/
*/
WESTON_EXPORT_FOR_TESTS int
clip_simple(struct clip_context *ctx,
struct polygon8 *surf,
struct clip_vertex *restrict vertices)
{
int i;
for (i = 0; i < surf->n; i++) {
vertices[i].x = CLIP(surf->pos[i].x, ctx->clip.x1, ctx->clip.x2);
vertices[i].y = CLIP(surf->pos[i].y, ctx->clip.y1, ctx->clip.y2);
}
return surf->n;
}
WESTON_EXPORT_FOR_TESTS int
clip_transformed(struct clip_context *ctx,
const struct polygon8 *surf,
struct clip_vertex *restrict vertices)
clipper_clip(const struct clipper_vertex *polygon,
size_t polygon_len,
const struct clipper_vertex box[2],
struct clipper_vertex *restrict vertices)
{
struct polygon8 p = *surf, tmp;
struct clip_context ctx;
struct polygon8 p, tmp;
int i, n;
tmp.n = clip_polygon_left(ctx, &p, tmp.pos);
p.n = clip_polygon_right(ctx, &tmp, p.pos);
tmp.n = clip_polygon_top(ctx, &p, tmp.pos);
p.n = clip_polygon_bottom(ctx, &tmp, p.pos);
if (polygon_len > 8)
return -1;
memcpy(ctx.box, box, 2 * sizeof *box);
memcpy(p.pos, polygon, polygon_len * sizeof *polygon);
p.n = polygon_len;
tmp.n = clip_polygon_left(&ctx, &p, tmp.pos);
p.n = clip_polygon_right(&ctx, &tmp, p.pos);
tmp.n = clip_polygon_top(&ctx, &p, tmp.pos);
p.n = clip_polygon_bottom(&ctx, &tmp, p.pos);
/* Get rid of duplicate vertices */
vertices[0] = p.pos[0];
n = 1;
for (i = 1; i < p.n; i++) {
if (float_difference(vertices[n - 1].x, p.pos[i].x) == 0.0f &&
float_difference(vertices[n - 1].y, p.pos[i].y) == 0.0f)
if (clipper_float_difference(vertices[n - 1].x, p.pos[i].x) == 0.0f &&
clipper_float_difference(vertices[n - 1].y, p.pos[i].y) == 0.0f)
continue;
vertices[n] = p.pos[i];
n++;
}
if (float_difference(vertices[n - 1].x, p.pos[0].x) == 0.0f &&
float_difference(vertices[n - 1].y, p.pos[0].y) == 0.0f)
if (clipper_float_difference(vertices[n - 1].x, p.pos[0].x) == 0.0f &&
clipper_float_difference(vertices[n - 1].y, p.pos[0].y) == 0.0f)
n--;
return n;
}
void
clipper_quad_init(struct clipper_quad *quad,
const struct clipper_vertex polygon[4],
bool axis_aligned)
{
int i;
memcpy(quad->polygon, polygon, 4 * sizeof *polygon);
quad->axis_aligned = axis_aligned;
if (axis_aligned)
return;
/* Find axis-aligned bounding box. */
quad->bbox[0].x = quad->bbox[1].x = polygon[0].x;
quad->bbox[0].y = quad->bbox[1].y = polygon[0].y;
for (i = 1; i < 4; i++) {
quad->bbox[0].x = MIN(quad->bbox[0].x, polygon[i].x);
quad->bbox[1].x = MAX(quad->bbox[1].x, polygon[i].x);
quad->bbox[0].y = MIN(quad->bbox[0].y, polygon[i].y);
quad->bbox[1].y = MAX(quad->bbox[1].y, polygon[i].y);
}
}
int
clip_quad(struct gl_quad *quad, pixman_box32_t *surf_rect,
struct clip_vertex *vertices)
clipper_quad_clip(struct clipper_quad *quad,
const struct clipper_vertex box[2],
struct clipper_vertex *restrict vertices)
{
struct clip_context ctx = {
.clip.x1 = surf_rect->x1,
.clip.y1 = surf_rect->y1,
.clip.x2 = surf_rect->x2,
.clip.y2 = surf_rect->y2,
};
int n;
int i, n;
/* Simple case: quad edges are parallel to surface rect edges, there
* will be either four or zero edges. We just need to clip the quad to
* the surface rect bounds and test for non-zero area:
/* Aligned case: quad edges are parallel to clipping box edges, there
* will be either four or zero edges. We just need to clamp the quad
* edges to the clipping box edges and test for non-zero area:
*/
if (quad->axis_aligned) {
clip_simple(&ctx, &quad->vertices, vertices);
for (i = 0; i < 4; i++) {
vertices[i].x = CLIP(quad->polygon[i].x,
box[0].x, box[1].x);
vertices[i].y = CLIP(quad->polygon[i].y,
box[0].y, box[1].y);
}
if ((vertices[0].x != vertices[1].x) &&
(vertices[0].y != vertices[2].y))
return 4;
......@@ -347,23 +381,32 @@ clip_quad(struct gl_quad *quad, pixman_box32_t *surf_rect,
return 0;
}
/* Transformed case: first, simple bounding box check to discard early a
* quad that does not intersect with the rect:
/* Unaligned case: first, simple bounding box check to discard early a
* quad that does not intersect with the clipping box:
*/
if ((quad->bbox.x1 >= ctx.clip.x2) || (quad->bbox.x2 <= ctx.clip.x1) ||
(quad->bbox.y1 >= ctx.clip.y2) || (quad->bbox.y2 <= ctx.clip.y1))
if ((quad->bbox[0].x >= box[1].x) || (quad->bbox[1].x <= box[0].x) ||
(quad->bbox[0].y >= box[1].y) || (quad->bbox[1].y <= box[0].y))
return 0;
/* Then, use a general polygon clipping algorithm to clip the quad with
* each side of the surface rect. The algorithm is Sutherland-Hodgman,
* as explained in
* https://www.codeguru.com/cplusplus/polygon-clipping/
* but without looking at any of that code.
/* Then use our general purpose clipping algorithm:
*/
n = clip_transformed(&ctx, &quad->vertices, vertices);
n = clipper_clip(quad->polygon, 4, box, vertices);
if (n < 3)
return 0;
return n;
}
int
clipper_quad_clip_box32(struct clipper_quad *quad,
const struct pixman_box32 *box,
struct clipper_vertex *restrict vertices)
{
struct clipper_vertex box_vertices[2] = {
{ box->x1, box->y1 },
{ box->x2, box->y2 }
};
return clipper_quad_clip(quad, box_vertices, vertices);
}
......@@ -25,58 +25,72 @@
#ifndef _WESTON_VERTEX_CLIPPING_H
#define _WESTON_VERTEX_CLIPPING_H
#include <stddef.h>
#include <stdbool.h>
#include <pixman.h>
struct clip_vertex {
struct clipper_vertex {
float x, y;
};
struct polygon8 {
struct clip_vertex pos[8];
int n;
};
struct gl_quad {
struct polygon8 vertices;
struct { float x1, y1, x2, y2; } bbox; /* Valid if !axis_aligned. */
struct clipper_quad {
struct clipper_vertex polygon[4];
struct clipper_vertex bbox[2]; /* Valid if !axis_aligned. */
bool axis_aligned;
};
struct clip_context {
struct clip_vertex prev;
struct {
float x1, y1;
float x2, y2;
} clip;
struct clip_vertex *vertices;
};
float
float_difference(float a, float b);
/*
* General purpose clipping function. Compute the boundary vertices of the
* intersection of a 'polygon' and a clipping 'box'. 'polygon' points to an
* array of 'polygon_len' vertices, less than or equal to 8, defining a convex
* polygon of any winding order. 'box' points to an array of 2 vertices where
* the values of the 1st vertex are less than or equal to the values of the 2nd
* vertex. Up to 16 resulting vertices, using 'polygon' winding order, are
* written to 'vertices'. The return value is the number of vertices created.
*/
int
clip_simple(struct clip_context *ctx,
struct polygon8 *surf,
struct clip_vertex *restrict vertices);
clipper_clip(const struct clipper_vertex *polygon,
size_t polygon_len,
const struct clipper_vertex box[2],
struct clipper_vertex *restrict vertices);
/*
* Initialize a 'quad' clipping context. 'polygon' points to an array of 4
* vertices defining a convex quadrilateral of any winding order. Call
* 'clipper_quad_clip()' to clip an initialized 'quad' to a clipping box.
* Clipping is faster if 'polygon' is an axis-aligned rectangle with edges
* parallel to the axes of the coordinate space. 'axis_aligned' indicates
* whether 'polygon' respects the conditions above.
*/
void
clipper_quad_init(struct clipper_quad *quad,
const struct clipper_vertex polygon[4],
bool axis_aligned);
/*
* Compute the boundary vertices of the intersection of a convex quadrilateral
* stored into a 'quad' clipping context and a clipping 'box'. 'box' points to
* an array of 2 vertices where the values of the 1st vertex are less than or
* equal to the values of the 2nd vertex. Either 0 or [3, 8] resulting vertices,
* with the same winding order than the 'polygon' passed to
* 'clipper_quad_init()', are written to 'vertices'. The return value is the
* number of vertices created.
*/
int
clip_transformed(struct clip_context *ctx,
const struct polygon8 *surf,
struct clip_vertex *restrict vertices);
clipper_quad_clip(struct clipper_quad *quad,
const struct clipper_vertex box[2],
struct clipper_vertex *restrict vertices);
/*
* Compute the boundary vertices of the intersection of an arbitrary
* quadrilateral 'quad' and the axis-aligned rectangle 'surf_rect'. The vertices
* are written to 'vertices', and the return value is the number of vertices.
* Vertices are produced in clockwise winding order. Guarantees to produce
* either zero vertices, or 3-8 vertices with non-zero polygon area.
* Utility function calling 'clipper_quad_clip()' but taking a pixman_box32
* pointer as clipping box.
*/
int
clip_quad(struct gl_quad *quad,
pixman_box32_t *surf_rect,
struct clip_vertex *vertices);
clipper_quad_clip_box32(struct clipper_quad *quad,
const struct pixman_box32 *box,
struct clipper_vertex *restrict vertices);
float
clipper_float_difference(float a, float b);
#endif
......@@ -25,12 +25,7 @@
#include "config.h"
#include <assert.h>
#include <string.h>
#include "weston-test-runner.h"
#include "shared/helpers.h"
#include "vertex-clipping.h"
#define BOUNDING_BOX_TOP_Y 100.0f
......@@ -48,230 +43,215 @@
#define OUTSIDE_Y1 (BOUNDING_BOX_BOTTOM_Y - 1.0f)
#define OUTSIDE_Y2 (BOUNDING_BOX_TOP_Y + 1.0f)
static void
populate_clip_context (struct clip_context *ctx)
{
ctx->clip.x1 = BOUNDING_BOX_LEFT_X;
ctx->clip.y1 = BOUNDING_BOX_BOTTOM_Y;
ctx->clip.x2 = BOUNDING_BOX_RIGHT_X;
ctx->clip.y2 = BOUNDING_BOX_TOP_Y;
}
static int
clip_polygon (struct clip_context *ctx,
struct polygon8 *polygon,
struct clip_vertex *pos)
{
populate_clip_context(ctx);
return clip_transformed(ctx, polygon, pos);
}
struct vertex_clip_test_data
{
struct polygon8 surface;
struct polygon8 expected;
struct vertex_clip_test_data {
struct clipper_vertex box[2];
struct clipper_vertex polygon[8];
struct clipper_vertex clipped[8];
int polygon_n;
int clipped_n;
};
const struct vertex_clip_test_data test_data[] =
{
const struct vertex_clip_test_data test_data[] = {
/* All inside */
{
{
.pos = {
{ .x = INSIDE_X1, .y = INSIDE_Y1 },
{ .x = INSIDE_X2, .y = INSIDE_Y1 },
{ .x = INSIDE_X2, .y = INSIDE_Y2 },
{ .x = INSIDE_X1, .y = INSIDE_Y2 },
},
.n = 4
.box = {
{ BOUNDING_BOX_LEFT_X, BOUNDING_BOX_BOTTOM_Y },
{ BOUNDING_BOX_RIGHT_X, BOUNDING_BOX_TOP_Y },
},
.polygon = {
{ INSIDE_X1, INSIDE_Y1 },
{ INSIDE_X2, INSIDE_Y1 },
{ INSIDE_X2, INSIDE_Y2 },
{ INSIDE_X1, INSIDE_Y2 },
},
{
.pos = {
{ .x = INSIDE_X1, .y = INSIDE_Y1 },
{ .x = INSIDE_X2, .y = INSIDE_Y1 },
{ .x = INSIDE_X2, .y = INSIDE_Y2 },
{ .x = INSIDE_X1, .y = INSIDE_Y2 },
},
.n = 4
}
.clipped = {
{ INSIDE_X1, INSIDE_Y1 },
{ INSIDE_X2, INSIDE_Y1 },
{ INSIDE_X2, INSIDE_Y2 },
{ INSIDE_X1, INSIDE_Y2 },
},
.polygon_n = 4,
.clipped_n = 4,
},
/* Top outside */
{
{
.pos = {
{ .x = INSIDE_X1, .y = INSIDE_Y1 },
{ .x = INSIDE_X2, .y = INSIDE_Y1 },
{ .x = INSIDE_X2, .y = OUTSIDE_Y2 },
{ .x = INSIDE_X1, .y = OUTSIDE_Y2 },
},
.n = 4
.box = {
{ BOUNDING_BOX_LEFT_X, BOUNDING_BOX_BOTTOM_Y },
{ BOUNDING_BOX_RIGHT_X, BOUNDING_BOX_TOP_Y },
},
.polygon = {
{ INSIDE_X1, INSIDE_Y1 },
{ INSIDE_X2, INSIDE_Y1 },
{ INSIDE_X2, OUTSIDE_Y2 },
{ INSIDE_X1, OUTSIDE_Y2 },
},
{
.pos = {
{ .x = INSIDE_X1, .y = BOUNDING_BOX_TOP_Y },
{ .x = INSIDE_X1, .y = INSIDE_Y1 },
{ .x = INSIDE_X2, .y = INSIDE_Y1 },
{ .x = INSIDE_X2, .y = BOUNDING_BOX_TOP_Y },
},
.n = 4
}
.clipped = {
{ INSIDE_X1, BOUNDING_BOX_TOP_Y },
{ INSIDE_X1, INSIDE_Y1 },
{ INSIDE_X2, INSIDE_Y1 },
{ INSIDE_X2, BOUNDING_BOX_TOP_Y },
},
.polygon_n = 4,
.clipped_n = 4,
},
/* Bottom outside */
{
{
.pos = {
{ .x = INSIDE_X1, .y = OUTSIDE_Y1 },
{ .x = INSIDE_X2, .y = OUTSIDE_Y1 },
{ .x = INSIDE_X2, .y = INSIDE_Y2 },
{ .x = INSIDE_X1, .y = INSIDE_Y2 },
},
.n = 4
.box = {
{ BOUNDING_BOX_LEFT_X, BOUNDING_BOX_BOTTOM_Y },
{ BOUNDING_BOX_RIGHT_X, BOUNDING_BOX_TOP_Y },
},
{
.pos = {
{ .x = INSIDE_X1, .y = BOUNDING_BOX_BOTTOM_Y },
{ .x = INSIDE_X2, .y = BOUNDING_BOX_BOTTOM_Y },
{ .x = INSIDE_X2, .y = INSIDE_Y2 },
{ .x = INSIDE_X1, .y = INSIDE_Y2 },
},
.n = 4
}
.polygon = {
{ INSIDE_X1, OUTSIDE_Y1 },
{ INSIDE_X2, OUTSIDE_Y1 },
{ INSIDE_X2, INSIDE_Y2 },
{ INSIDE_X1, INSIDE_Y2 },
},
.clipped = {
{ INSIDE_X1, BOUNDING_BOX_BOTTOM_Y },
{ INSIDE_X2, BOUNDING_BOX_BOTTOM_Y },
{ INSIDE_X2, INSIDE_Y2 },
{ INSIDE_X1, INSIDE_Y2 },
},
.polygon_n = 4,
.clipped_n = 4,
},
/* Left outside */
{
{
.pos = {
{ .x = OUTSIDE_X1, .y = INSIDE_Y1 },
{ .x = INSIDE_X2, .y = INSIDE_Y1 },
{ .x = INSIDE_X2, .y = INSIDE_Y2 },
{ .x = OUTSIDE_X1, .y = INSIDE_Y2 },
},
.n = 4
.box = {
{ BOUNDING_BOX_LEFT_X, BOUNDING_BOX_BOTTOM_Y },
{ BOUNDING_BOX_RIGHT_X, BOUNDING_BOX_TOP_Y }
},
.polygon = {
{ OUTSIDE_X1, INSIDE_Y1 },
{ INSIDE_X2, INSIDE_Y1 },
{ INSIDE_X2, INSIDE_Y2 },
{ OUTSIDE_X1, INSIDE_Y2 },
},
.clipped = {
{ BOUNDING_BOX_LEFT_X, INSIDE_Y1 },
{ INSIDE_X2, INSIDE_Y1 },
{ INSIDE_X2, INSIDE_Y2 },
{ BOUNDING_BOX_LEFT_X, INSIDE_Y2 },
},
{
.pos = {
{ .x = BOUNDING_BOX_LEFT_X, .y = INSIDE_Y1 },
{ .x = INSIDE_X2, .y = INSIDE_Y1 },
{ .x = INSIDE_X2, .y = INSIDE_Y2 },
{ .x = BOUNDING_BOX_LEFT_X, .y = INSIDE_Y2 },
},
.n = 4
}
.polygon_n = 4,
.clipped_n = 4,
},
/* Right outside */
{
{
.pos = {
{ .x = INSIDE_X1, .y = INSIDE_Y1 },
{ .x = OUTSIDE_X2, .y = INSIDE_Y1 },
{ .x = OUTSIDE_X2, .y = INSIDE_Y2 },
{ .x = INSIDE_X1, .y = INSIDE_Y2 },
},
.n = 4
.box = {
{ BOUNDING_BOX_LEFT_X, BOUNDING_BOX_BOTTOM_Y },
{ BOUNDING_BOX_RIGHT_X, BOUNDING_BOX_TOP_Y },
},
.polygon = {
{ INSIDE_X1, INSIDE_Y1 },
{ OUTSIDE_X2, INSIDE_Y1 },
{ OUTSIDE_X2, INSIDE_Y2 },
{ INSIDE_X1, INSIDE_Y2 },
},
.clipped = {
{ INSIDE_X1, INSIDE_Y1 },
{ BOUNDING_BOX_RIGHT_X, INSIDE_Y1 },
{ BOUNDING_BOX_RIGHT_X, INSIDE_Y2 },
{ INSIDE_X1, INSIDE_Y2 },
},
{
.pos = {
{ .x = INSIDE_X1, .y = INSIDE_Y1 },
{ .x = BOUNDING_BOX_RIGHT_X, .y = INSIDE_Y1 },
{ .x = BOUNDING_BOX_RIGHT_X, .y = INSIDE_Y2 },
{ .x = INSIDE_X1, .y = INSIDE_Y2 },
},
.n = 4
}
.polygon_n = 4,
.clipped_n = 4,
},
/* Diamond extending from bounding box edges, clip to bounding box */
/* Diamond extending from bounding box edges */
{
{
.pos = {
{ .x = BOUNDING_BOX_LEFT_X - 25, .y = BOUNDING_BOX_BOTTOM_Y + 25 },
{ .x = BOUNDING_BOX_LEFT_X + 25, .y = BOUNDING_BOX_TOP_Y + 25 },
{ .x = BOUNDING_BOX_RIGHT_X + 25, .y = BOUNDING_BOX_TOP_Y - 25 },
{ .x = BOUNDING_BOX_RIGHT_X - 25, .y = BOUNDING_BOX_BOTTOM_Y - 25 },
},
.n = 4
.box = {
{ BOUNDING_BOX_LEFT_X, BOUNDING_BOX_BOTTOM_Y },
{ BOUNDING_BOX_RIGHT_X, BOUNDING_BOX_TOP_Y },
},
.polygon = {
{ BOUNDING_BOX_LEFT_X - 25, BOUNDING_BOX_BOTTOM_Y + 25 },
{ BOUNDING_BOX_LEFT_X + 25, BOUNDING_BOX_TOP_Y + 25 },
{ BOUNDING_BOX_RIGHT_X + 25, BOUNDING_BOX_TOP_Y - 25 },
{ BOUNDING_BOX_RIGHT_X - 25, BOUNDING_BOX_BOTTOM_Y - 25 },
},
.clipped = {
{ BOUNDING_BOX_LEFT_X, BOUNDING_BOX_BOTTOM_Y },
{ BOUNDING_BOX_LEFT_X, BOUNDING_BOX_TOP_Y },
{ BOUNDING_BOX_RIGHT_X, BOUNDING_BOX_TOP_Y },
{ BOUNDING_BOX_RIGHT_X, BOUNDING_BOX_BOTTOM_Y },
},
{
.pos = {
{ .x = BOUNDING_BOX_LEFT_X, .y = BOUNDING_BOX_BOTTOM_Y },
{ .x = BOUNDING_BOX_LEFT_X, .y = BOUNDING_BOX_TOP_Y },
{ .x = BOUNDING_BOX_RIGHT_X, .y = BOUNDING_BOX_TOP_Y },
{ .x = BOUNDING_BOX_RIGHT_X, .y = BOUNDING_BOX_BOTTOM_Y },
},
.n = 4
}
.polygon_n = 4,
.clipped_n = 4,
},
/* Diamond inside of bounding box edges, clip t bounding box, 8 resulting vertices */
/* Diamond inside of bounding box edges */
{
{
.pos = {
{ .x = BOUNDING_BOX_LEFT_X - 12.5, .y = BOUNDING_BOX_BOTTOM_Y + 25 },
{ .x = BOUNDING_BOX_LEFT_X + 25, .y = BOUNDING_BOX_TOP_Y + 12.5 },
{ .x = BOUNDING_BOX_RIGHT_X + 12.5, .y = BOUNDING_BOX_TOP_Y - 25 },
{ .x = BOUNDING_BOX_RIGHT_X - 25, .y = BOUNDING_BOX_BOTTOM_Y - 12.5 },
},
.n = 4
.box = {
{ BOUNDING_BOX_LEFT_X, BOUNDING_BOX_BOTTOM_Y },
{ BOUNDING_BOX_RIGHT_X, BOUNDING_BOX_TOP_Y },
},
{
.pos = {
{ .x = BOUNDING_BOX_LEFT_X + 12.5, .y = BOUNDING_BOX_BOTTOM_Y },
{ .x = BOUNDING_BOX_LEFT_X, .y = BOUNDING_BOX_BOTTOM_Y + 12.5 },
{ .x = BOUNDING_BOX_LEFT_X, .y = BOUNDING_BOX_TOP_Y - 12.5 },
{ .x = BOUNDING_BOX_LEFT_X + 12.5, .y = BOUNDING_BOX_TOP_Y },
{ .x = BOUNDING_BOX_RIGHT_X - 12.5, .y = BOUNDING_BOX_TOP_Y },
{ .x = BOUNDING_BOX_RIGHT_X, .y = BOUNDING_BOX_TOP_Y - 12.5 },
{ .x = BOUNDING_BOX_RIGHT_X, .y = BOUNDING_BOX_BOTTOM_Y + 12.5},
{ .x = BOUNDING_BOX_RIGHT_X - 12.5, .y = BOUNDING_BOX_BOTTOM_Y },
},
.n = 8
}
}
.polygon = {
{ BOUNDING_BOX_LEFT_X - 12.5, BOUNDING_BOX_BOTTOM_Y + 25 },
{ BOUNDING_BOX_LEFT_X + 25, BOUNDING_BOX_TOP_Y + 12.5 },
{ BOUNDING_BOX_RIGHT_X + 12.5, BOUNDING_BOX_TOP_Y - 25 },
{ BOUNDING_BOX_RIGHT_X - 25, BOUNDING_BOX_BOTTOM_Y - 12.5 },
},
.clipped = {
{ BOUNDING_BOX_LEFT_X + 12.5, BOUNDING_BOX_BOTTOM_Y },
{ BOUNDING_BOX_LEFT_X, BOUNDING_BOX_BOTTOM_Y + 12.5 },
{ BOUNDING_BOX_LEFT_X, BOUNDING_BOX_TOP_Y - 12.5 },
{ BOUNDING_BOX_LEFT_X + 12.5, BOUNDING_BOX_TOP_Y },
{ BOUNDING_BOX_RIGHT_X - 12.5, BOUNDING_BOX_TOP_Y },
{ BOUNDING_BOX_RIGHT_X, BOUNDING_BOX_TOP_Y - 12.5 },
{ BOUNDING_BOX_RIGHT_X, BOUNDING_BOX_BOTTOM_Y + 12.5 },
{ BOUNDING_BOX_RIGHT_X - 12.5, BOUNDING_BOX_BOTTOM_Y },
},
.polygon_n = 4,
.clipped_n = 8,
},
};
/* clip_polygon modifies the source operand and the test data must
* be const, so we need to deep copy it */
static void
deep_copy_polygon8(const struct polygon8 *src, struct polygon8 *dst)
{
dst->n = src->n;
ARRAY_COPY(dst->pos, src->pos);
}
TEST_P(clip_polygon_n_vertices_emitted, test_data)
{
struct vertex_clip_test_data *tdata = data;
struct clip_context ctx;
struct polygon8 polygon;
struct clip_vertex vertices[8];
deep_copy_polygon8(&tdata->surface, &polygon);
int emitted = clip_polygon(&ctx, &polygon, vertices);
struct clipper_vertex clipped[8];
int clipped_n;
assert(emitted == tdata->expected.n);
clipped_n = clipper_clip(tdata->polygon, tdata->polygon_n, tdata->box,
clipped);
assert(clipped_n == tdata->clipped_n);
}
TEST_P(clip_polygon_expected_vertices, test_data)
{
struct vertex_clip_test_data *tdata = data;
struct clip_context ctx;
struct polygon8 polygon;
struct clip_vertex vertices[8];
deep_copy_polygon8(&tdata->surface, &polygon);
int emitted = clip_polygon(&ctx, &polygon, vertices);
int i = 0;
struct clipper_vertex clipped[8];
int clipped_n, i;
for (; i < emitted; ++i)
{
assert(vertices[i].x == tdata->expected.pos[i].x);
assert(vertices[i].y == tdata->expected.pos[i].y);
clipped_n = clipper_clip(tdata->polygon, tdata->polygon_n, tdata->box,
clipped);
for (i = 0; i < clipped_n; i++) {
assert(clipped[i].x == tdata->clipped[i].x);
assert(clipped[i].y == tdata->clipped[i].y);
}
}
TEST(clip_size_too_high)
{
struct clipper_vertex polygon[8] = {}, box[2] = {};
assert(clipper_clip(polygon, 9, box, NULL) == -1);
}
TEST(float_difference_different)
{
assert(float_difference(1.0f, 0.0f) == 1.0f);
assert(clipper_float_difference(1.0f, 0.0f) == 1.0f);
}
TEST(float_difference_same)
{
assert(float_difference(1.0f, 1.0f) == 0.0f);
assert(clipper_float_difference(1.0f, 1.0f) == 0.0f);
}