@ -81,294 +81,368 @@ weston_surface_to_global_float(struct weston_surface *surface,
/* ---------------------- copied begins -----------------------*/
# define max(a, b) (((a) > (b)) ? (a) : (b))
# define min(a, b) (((a) > (b)) ? (b) : (a))
# define clip(x, a, b) min(max(x, a), b)
# define sign(x) ((x) >= 0)
struct polygon8 {
GLfloat x [ 8 ] ;
GLfloat y [ 8 ] ;
int n ;
} ;
static int
calculate_edges ( struct weston_surface * es , pixman_box32_t * rect ,
pixman_box32_t * surf_rect , GLfloat * ex , GLfloat * ey )
struct clip_context {
struct {
GLfloat x ;
GLfloat y ;
} prev ;
struct {
GLfloat x1 , y1 ;
GLfloat x2 , y2 ;
} clip ;
struct {
GLfloat * x ;
GLfloat * y ;
} vertices ;
} ;
static GLfloat
float_difference ( GLfloat a , GLfloat b )
{
int i , n = 0 ;
GLfloat min_x , max_x , min_y , max_y ;
GLfloat x [ 4 ] = {
surf_rect - > x1 , surf_rect - > x2 , surf_rect - > x2 , surf_rect - > x1 ,
} ;
GLfloat y [ 4 ] = {
surf_rect - > y1 , surf_rect - > y1 , surf_rect - > y2 , surf_rect - > y2 ,
} ;
GLfloat cx1 = rect - > x1 ;
GLfloat cx2 = rect - > x2 ;
GLfloat cy1 = rect - > y1 ;
GLfloat cy2 = rect - > y2 ;
GLfloat dist_squared ( GLfloat x1 , GLfloat y1 , GLfloat x2 , GLfloat y2 )
{
GLfloat dx = ( x1 - x2 ) ;
GLfloat dy = ( y1 - y2 ) ;
return dx * dx + dy * dy ;
}
/* http://www.altdevblogaday.com/2012/02/22/comparing-floating-point-numbers-2012-edition/ */
static const GLfloat max_diff = 4.0f * FLT_MIN ;
static const GLfloat max_rel_diff = 4.0e-5 ;
GLfloat diff = a - b ;
GLfloat adiff = fabsf ( diff ) ;
void append_vertex ( GLfloat x , GLfloat y )
{
/* don't emit duplicate vertices: */
if ( ( n > 0 ) & & ( ex [ n - 1 ] = = x ) & & ( ey [ n - 1 ] = = y ) )
return ;
ex [ n ] = x ;
ey [ n ] = y ;
n + + ;
}
if ( adiff < = max_diff )
return 0.0f ;
/* transform surface to screen space: */
for ( i = 0 ; i < 4 ; i + + )
weston_surface_to_global_float ( es , x [ i ] , y [ i ] , & x [ i ] , & y [ i ] ) ;
a = fabsf ( a ) ;
b = fabsf ( b ) ;
if ( adiff < = ( a > b ? a : b ) * max_rel_diff )
return 0.0f ;
/* find bounding box: */
min_x = max_x = x [ 0 ] ;
min_y = max_y = y [ 0 ] ;
for ( i = 1 ; i < 4 ; i + + ) {
min_x = min ( min_x , x [ i ] ) ;
max_x = max ( max_x , x [ i ] ) ;
min_y = min ( min_y , y [ i ] ) ;
max_y = max ( max_y , y [ i ] ) ;
}
return diff ;
}
/* First, simple bounding box check to discard early transformed
* surface rects that do not intersect with the clip region :
/* A line segment (p1x, p1y)-(p2x, p2y) intersects the line x = x_arg.
* Compute the y coordinate of the intersection .
*/
if ( ( min_x > cx2 ) | | ( max_x < cx1 ) | |
( min_y > cy2 ) | | ( max_y < cy1 ) )
return 0 ;
static GLfloat
clip_intersect_y ( GLfloat p1x , GLfloat p1y , GLfloat p2x , GLfloat p2y ,
GLfloat x_arg )
{
GLfloat a ;
GLfloat diff = float_difference ( p1x , p2x ) ;
/* Simple case, bounding box edges are parallel to surface edges,
* there will be only four edges . We just need to clip the surface
* vertices to the clip rect bounds :
/* Practically vertical line segment, yet the end points have already
* been determined to be on different sides of the line . Therefore
* the line segment is part of the line and intersects everywhere .
* Return the end point , so we use the whole line segment .
*/
if ( ! es - > transform . enabled ) {
for ( i = 0 ; i < 4 ; i + + ) {
ex [ n ] = clip ( x [ i ] , cx1 , cx2 ) ;
ey [ n ] = clip ( y [ i ] , cy1 , cy2 ) ;
n + + ;
}
return 4 ;
}
if ( diff = = 0.0f )
return p2y ;
/* Hard case, transformation applied. We need to find the vertices
* of the shape that is the intersection of the clip rect and
* transformed surface . This can be anything from 3 to 8 sides .
*
* Observation : all the resulting vertices will be the intersection
* points of the transformed surface and the clip rect , plus the
* vertices of the clip rect which are enclosed by the transformed
* surface and the vertices of the transformed surface which are
* enclosed by the clip rect .
*
* Observation : there will be zero , one , or two resulting vertices
* for each edge of the src rect .
*
* Loop over four edges of the transformed rect :
*/
for ( i = 0 ; i < 4 ; i + + ) {
GLfloat x1 , y1 , x2 , y2 ;
int last_n = n ;
x1 = x [ i ] ;
y1 = y [ i ] ;
/* if this vertex is contained in the clip rect, use it as-is: */
if ( ( cx1 < = x1 ) & & ( x1 < = cx2 ) & &
( cy1 < = y1 ) & & ( y1 < = cy2 ) )
append_vertex ( x1 , y1 ) ;
/* for remaining, we consider the point as part of a line: */
x2 = x [ ( i + 1 ) % 4 ] ;
y2 = y [ ( i + 1 ) % 4 ] ;
if ( x1 = = x2 ) {
append_vertex ( clip ( x1 , cx1 , cx2 ) , clip ( y1 , cy1 , cy2 ) ) ;
append_vertex ( clip ( x2 , cx1 , cx2 ) , clip ( y2 , cy1 , cy2 ) ) ;
} else if ( y1 = = y2 ) {
append_vertex ( clip ( x1 , cx1 , cx2 ) , clip ( y1 , cy1 , cy2 ) ) ;
append_vertex ( clip ( x2 , cx1 , cx2 ) , clip ( y2 , cy1 , cy2 ) ) ;
} else {
GLfloat m , c , p ;
GLfloat tx [ 2 ] , ty [ 2 ] ;
int tn = 0 ;
a = ( x_arg - p2x ) / diff ;
return p2y + ( p1y - p2y ) * a ;
}
int intersect_horiz ( GLfloat y , GLfloat * p )
{
GLfloat x ;
/* A line segment (p1x, p1y)-(p2x, p2y) intersects the line y = y_arg.
* Compute the x coordinate of the intersection .
*/
static GLfloat
clip_intersect_x ( GLfloat p1x , GLfloat p1y , GLfloat p2x , GLfloat p2y ,
GLfloat y_arg )
{
GLfloat a ;
GLfloat diff = float_difference ( p1y , p2y ) ;
/* if y does not lie between y1 and y2, no
* intersection possible
/* Practically horizontal line segment, yet the end points have already
* been determined to be on different sides of the line . Therefore
* the line segment is part of the line and intersects everywhere .
* Return the end point , so we use the whole line segment .
*/
if ( sign ( y - y1 ) = = sign ( y - y2 ) )
return 0 ;
if ( diff = = 0.0f )
return p2x ;
x = ( y - c ) / m ;
a = ( y_arg - p2y ) / diff ;
return p2x + ( p1x - p2x ) * a ;
}
/* if x does not lie between cx1 and cx2, no
* intersection :
*/
if ( sign ( x - cx1 ) = = sign ( x - cx2 ) )
return 0 ;
enum path_transition {
PATH_TRANSITION_OUT_TO_OUT = 0 ,
PATH_TRANSITION_OUT_TO_IN = 1 ,
PATH_TRANSITION_IN_TO_OUT = 2 ,
PATH_TRANSITION_IN_TO_IN = 3 ,
} ;
* p = x ;
return 1 ;
}
static void
clip_append_vertex ( struct clip_context * ctx , GLfloat x , GLfloat y )
{
* ctx - > vertices . x + + = x ;
* ctx - > vertices . y + + = y ;
}
int intersect_vert ( GLfloat x , GLfloat * p )
{
GLfloat y ;
static enum path_transition
path_transition_left_edge ( struct clip_context * ctx , GLfloat x , GLfloat y )
{
return ( ( ctx - > prev . x > = ctx - > clip . x1 ) < < 1 ) | ( x > = ctx - > clip . x1 ) ;
}
if ( sign ( x - x1 ) = = sign ( x - x2 ) )
return 0 ;
static enum path_transition
path_transition_right_edge ( struct clip_context * ctx , GLfloat x , GLfloat y )
{
return ( ( ctx - > prev . x < ctx - > clip . x2 ) < < 1 ) | ( x < ctx - > clip . x2 ) ;
}
y = m * x + c ;
static enum path_transition
path_transition_top_edge ( struct clip_context * ctx , GLfloat x , GLfloat y )
{
return ( ( ctx - > prev . y > = ctx - > clip . y1 ) < < 1 ) | ( y > = ctx - > clip . y1 ) ;
}
if ( sign ( y - cy1 ) = = sign ( y - cy2 ) )
return 0 ;
static enum path_transition
path_transition_bottom_edge ( struct clip_context * ctx , GLfloat x , GLfloat y )
{
return ( ( ctx - > prev . y < ctx - > clip . y2 ) < < 1 ) | ( y < ctx - > clip . y2 ) ;
}
* p = y ;
return 1 ;
static void
clip_polygon_leftright ( struct clip_context * ctx ,
enum path_transition transition ,
GLfloat x , GLfloat y , GLfloat clip_x )
{
GLfloat yi ;
switch ( transition ) {
case PATH_TRANSITION_IN_TO_IN :
clip_append_vertex ( ctx , x , y ) ;
break ;
case PATH_TRANSITION_IN_TO_OUT :
yi = clip_intersect_y ( ctx - > prev . x , ctx - > prev . y , x , y , clip_x ) ;
clip_append_vertex ( ctx , clip_x , yi ) ;
break ;
case PATH_TRANSITION_OUT_TO_IN :
yi = clip_intersect_y ( ctx - > prev . x , ctx - > prev . y , x , y , clip_x ) ;
clip_append_vertex ( ctx , clip_x , yi ) ;
clip_append_vertex ( ctx , x , y ) ;
break ;
case PATH_TRANSITION_OUT_TO_OUT :
/* nothing */
break ;
default :
assert ( 0 & & " bad enum path_transition " ) ;
}
/* y = mx + c */
m = ( y2 - y1 ) / ( x2 - x1 ) ;
c = y1 - m * x1 ;
ctx - > prev . x = x ;
ctx - > prev . y = y ;
}
/* check for up to two intersections with the four edges
* of the clip rect . Note that we don ' t know the orientation
* of the transformed surface wrt . the clip rect . So if when
* there are two intersection points , we need to put the one
* closest to x1 , y1 first :
*/
static void
clip_polygon_topbottom ( struct clip_context * ctx ,
enum path_transition transition ,
GLfloat x , GLfloat y , GLfloat clip_y )
{
GLfloat xi ;
/* check top clip rect edge: */
if ( intersect_horiz ( cy1 , & p ) ) {
ty [ tn ] = cy1 ;
tx [ tn ] = p ;
tn + + ;
switch ( transition ) {
case PATH_TRANSITION_IN_TO_IN :
clip_append_vertex ( ctx , x , y ) ;
break ;
case PATH_TRANSITION_IN_TO_OUT :
xi = clip_intersect_x ( ctx - > prev . x , ctx - > prev . y , x , y , clip_y ) ;
clip_append_vertex ( ctx , xi , clip_y ) ;
break ;
case PATH_TRANSITION_OUT_TO_IN :
xi = clip_intersect_x ( ctx - > prev . x , ctx - > prev . y , x , y , clip_y ) ;
clip_append_vertex ( ctx , xi , clip_y ) ;
clip_append_vertex ( ctx , x , y ) ;
break ;
case PATH_TRANSITION_OUT_TO_OUT :
/* nothing */
break ;
default :
assert ( 0 & & " bad enum path_transition " ) ;
}
/* check right clip rect edge: */
if ( intersect_vert ( cx2 , & p ) ) {
ty [ tn ] = p ;
tx [ tn ] = cx2 ;
tn + + ;
if ( tn = = 2 )
goto edge_check_done ;
}
ctx - > prev . x = x ;
ctx - > prev . y = y ;
}
/* check bottom clip rect edge: */
if ( intersect_horiz ( cy2 , & p ) ) {
ty [ tn ] = cy2 ;
tx [ tn ] = p ;
tn + + ;
if ( tn = = 2 )
goto edge_check_done ;
}
static void
clip_context_prepare ( struct clip_context * ctx , const struct polygon8 * src ,
GLfloat * dst_x , GLfloat * dst_y )
{
ctx - > prev . x = src - > x [ src - > n - 1 ] ;
ctx - > prev . y = src - > y [ src - > n - 1 ] ;
ctx - > vertices . x = dst_x ;
ctx - > vertices . y = dst_y ;
}
/* check left clip rect edge: */
if ( intersect_vert ( cx1 , & p ) ) {
ty [ tn ] = p ;
tx [ tn ] = cx1 ;
tn + + ;
}
static int
clip_polygon_left ( struct clip_context * ctx , const struct polygon8 * src ,
GLfloat * dst_x , GLfloat * dst_y )
{
enum path_transition trans ;
int i ;
edge_check_done :
if ( tn = = 1 ) {
append_vertex ( tx [ 0 ] , ty [ 0 ] ) ;
} else if ( tn = = 2 ) {
if ( dist_squared ( x1 , y1 , tx [ 0 ] , ty [ 0 ] ) <
dist_squared ( x1 , y1 , tx [ 1 ] , ty [ 1 ] ) ) {
append_vertex ( tx [ 0 ] , ty [ 0 ] ) ;
append_vertex ( tx [ 1 ] , ty [ 1 ] ) ;
} else {
append_vertex ( tx [ 1 ] , ty [ 1 ] ) ;
append_vertex ( tx [ 0 ] , ty [ 0 ] ) ;
}
clip_context_prepare ( ctx , src , dst_x , dst_y ) ;
for ( i = 0 ; i < src - > n ; i + + ) {
trans = path_transition_left_edge ( ctx , src - > x [ i ] , src - > y [ i ] ) ;
clip_polygon_leftright ( ctx , trans , src - > x [ i ] , src - > y [ i ] ,
ctx - > clip . x1 ) ;
}
return ctx - > vertices . x - dst_x ;
}
if ( n = = last_n ) {
GLfloat best_x = 0 , best_y = 0 ;
uint32_t d , best_d = ( unsigned int ) - 1 ; /* distance squared */
uint32_t max_d = dist_squared ( x2 , y2 ,
x [ ( i + 2 ) % 4 ] , y [ ( i + 2 ) % 4 ] ) ;
/* if there are no vertices on this line, it could be that
* there is a vertex of the clip rect that is enclosed by
* the transformed surface . Find the vertex of the clip
* rect that is reached by the shortest line perpendicular
* to the current edge , if any .
*
* slope of perpendicular is 1 / m , so
*
* cy = - cx / m + c2
* c2 = cy + cx / m
*
*/
static int
clip_polygon_right ( struct clip_context * ctx , const struct polygon8 * src ,
GLfloat * dst_x , GLfloat * dst_y )
{
enum path_transition trans ;
int i ;
int perp_intersect ( GLfloat cx , GLfloat cy , uint32_t * d )
{
GLfloat c2 = cy + cx / m ;
GLfloat x = ( c2 - c ) / ( m + 1 / m ) ;
clip_context_prepare ( ctx , src , dst_x , dst_y ) ;
for ( i = 0 ; i < src - > n ; i + + ) {
trans = path_transition_right_edge ( ctx , src - > x [ i ] , src - > y [ i ] ) ;
clip_polygon_leftright ( ctx , trans , src - > x [ i ] , src - > y [ i ] ,
ctx - > clip . x2 ) ;
}
return ctx - > vertices . x - dst_x ;
}
/* if the x position of the intersection of the
* perpendicular with the transformed edge does
* not lie within the bounds of the edge , then
* no intersection :
*/
if ( sign ( x - x1 ) = = sign ( x - x2 ) )
return 0 ;
static int
clip_polygon_top ( struct clip_context * ctx , const struct polygon8 * src ,
GLfloat * dst_x , GLfloat * dst_y )
{
enum path_transition trans ;
int i ;
* d = dist_squared ( cx , cy , x , ( m * x ) + c ) ;
clip_context_prepare ( ctx , src , dst_x , dst_y ) ;
for ( i = 0 ; i < src - > n ; i + + ) {
trans = path_transition_top_edge ( ctx , src - > x [ i ] , src - > y [ i ] ) ;
clip_polygon_topbottom ( ctx , trans , src - > x [ i ] , src - > y [ i ] ,
ctx - > clip . y1 ) ;
}
return ctx - > vertices . x - dst_x ;
}
/* if intersection distance is further away than
* opposite edge of surface region , it is invalid :
*/
if ( * d > max_d )
return 0 ;
static int
clip_polygon_bottom ( struct clip_context * ctx , const struct polygon8 * src ,
GLfloat * dst_x , GLfloat * dst_y )
{
enum path_transition trans ;
int i ;
return 1 ;
clip_context_prepare ( ctx , src , dst_x , dst_y ) ;
for ( i = 0 ; i < src - > n ; i + + ) {
trans = path_transition_bottom_edge ( ctx , src - > x [ i ] , src - > y [ i ] ) ;
clip_polygon_topbottom ( ctx , trans , src - > x [ i ] , src - > y [ i ] ,
ctx - > clip . y2 ) ;
}
return ctx - > vertices . x - dst_x ;
}
if ( perp_intersect ( cx1 , cy1 , & d ) ) {
best_x = cx1 ;
best_y = cy1 ;
best_d = d ;
}
# define max(a, b) (((a) > (b)) ? (a) : (b))
# define min(a, b) (((a) > (b)) ? (b) : (a))
# define clip(x, a, b) min(max(x, a), b)
if ( perp_intersect ( cx1 , cy2 , & d ) & & ( d < best_d ) ) {
best_x = cx1 ;
best_y = cy2 ;
best_d = d ;
}
/*
* Compute the boundary vertices of the intersection of the global coordinate
* aligned rectangle ' rect ' , and an arbitrary quadrilateral produced from
* ' surf_rect ' when transformed from surface coordinates into global coordinates .
* The vertices are written to ' ex ' and ' ey ' , 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 .
*/
static int
calculate_edges ( struct weston_surface * es , pixman_box32_t * rect ,
pixman_box32_t * surf_rect , GLfloat * ex , GLfloat * ey )
{
struct polygon8 polygon ;
struct clip_context ctx ;
int i , n ;
GLfloat min_x , max_x , min_y , max_y ;
struct polygon8 surf = {
{ surf_rect - > x1 , surf_rect - > x2 , surf_rect - > x2 , surf_rect - > x1 } ,
{ surf_rect - > y1 , surf_rect - > y1 , surf_rect - > y2 , surf_rect - > y2 } ,
4
} ;
if ( perp_intersect ( cx2 , cy2 , & d ) & & ( d < best_d ) ) {
best_x = cx2 ;
best_y = cy2 ;
best_d = d ;
}
ctx . clip . x1 = rect - > x1 ;
ctx . clip . y1 = rect - > y1 ;
ctx . clip . x2 = rect - > x2 ;
ctx . clip . y2 = rect - > y2 ;
/* transform surface to screen space: */
for ( i = 0 ; i < surf . n ; i + + )
weston_surface_to_global_float ( es , surf . x [ i ] , surf . y [ i ] ,
& surf . x [ i ] , & surf . y [ i ] ) ;
if ( perp_intersect ( cx2 , cy1 , & d ) & & ( d < best_d ) ) {
best_x = cx2 ;
best_y = cy1 ;
best_d = d ;
/* find bounding box: */
min_x = max_x = surf . x [ 0 ] ;
min_y = max_y = surf . y [ 0 ] ;
for ( i = 1 ; i < surf . n ; i + + ) {
min_x = min ( min_x , surf . x [ i ] ) ;
max_x = max ( max_x , surf . x [ i ] ) ;
min_y = min ( min_y , surf . y [ i ] ) ;
max_y = max ( max_y , surf . y [ i ] ) ;
}
if ( best_d ! = ( unsigned int ) - 1 ) // XXX can this happen?
append_vertex ( best_x , best_y ) ;
/* First, simple bounding box check to discard early transformed
* surface rects that do not intersect with the clip region :
*/
if ( ( min_x > = ctx . clip . x2 ) | | ( max_x < = ctx . clip . x1 ) | |
( min_y > = ctx . clip . y2 ) | | ( max_y < = ctx . clip . y1 ) )
return 0 ;
/* Simple case, bounding box edges are parallel to surface edges,
* there will be only four edges . We just need to clip the surface
* vertices to the clip rect bounds :
*/
if ( ! es - > transform . enabled ) {
for ( i = 0 ; i < surf . n ; i + + ) {
ex [ i ] = clip ( surf . x [ i ] , ctx . clip . x1 , ctx . clip . x2 ) ;
ey [ i ] = clip ( surf . y [ i ] , ctx . clip . y1 , ctx . clip . y2 ) ;
}
return surf . n ;
}
/* Transformed case: use a general polygon clipping algorithm to
* clip the surface rectangle with each side of ' rect ' .
* The algorithm is Sutherland - Hodgman , as explained in
* http : //www.codeguru.com/cpp/misc/misc/graphics/article.php/c8965/Polygon-Clipping.htm
* but without looking at any of that code .
*/
polygon . n = clip_polygon_left ( & ctx , & surf , polygon . x , polygon . y ) ;
surf . n = clip_polygon_right ( & ctx , & polygon , surf . x , surf . y ) ;
polygon . n = clip_polygon_top ( & ctx , & surf , polygon . x , polygon . y ) ;
surf . n = clip_polygon_bottom ( & ctx , & polygon , surf . x , surf . y ) ;
/* Get rid of duplicate vertices */
ex [ 0 ] = surf . x [ 0 ] ;
ey [ 0 ] = surf . y [ 0 ] ;
n = 1 ;
for ( i = 1 ; i < surf . n ; i + + ) {
if ( float_difference ( ex [ n - 1 ] , surf . x [ i ] ) = = 0.0f & &
float_difference ( ey [ n - 1 ] , surf . y [ i ] ) = = 0.0f )
continue ;
ex [ n ] = surf . x [ i ] ;
ey [ n ] = surf . y [ i ] ;
n + + ;
}
if ( float_difference ( ex [ n - 1 ] , surf . x [ 0 ] ) = = 0.0f & &
float_difference ( ey [ n - 1 ] , surf . y [ 0 ] ) = = 0.0f )
n - - ;
if ( n < 3 )
return 0 ;
return n ;
}
/* ---------------------- copied ends -----------------------*/
static void