2006-09-25 23:22:45 -07:00
/*
* Copyright © 2006 Novell , Inc .
*
* Permission to use , copy , modify , distribute , and sell this software
* and its documentation for any purpose is hereby granted without
* fee , provided that the above copyright notice appear in all copies
* and that both that copyright notice and this permission notice
* appear in supporting documentation , and that the name of
* Novell , Inc . not be used in advertising or publicity pertaining to
* distribution of the software without specific , written prior
* permission . Novell , Inc . makes no representations about the
* suitability of this software for any purpose . It is provided " as
* is " without express or implied warranty.
*
* NOVELL , INC . DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE , INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS , IN NO EVENT SHALL RED HAT , INC . BE LIABLE FOR ANY SPECIAL ,
* INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE , DATA OR PROFITS , WHETHER IN AN ACTION
* OF CONTRACT , NEGLIGENCE OR OTHER TORTIOUS ACTION , ARISING OUT OF OR
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE .
*
* Author : Robert O ' Callahan < rocallahan @ novell . com >
*/
# include "cairo-test.h"
# include <stddef.h>
# include <math.h>
static cairo_test_draw_function_t draw ;
cairo_test_t test = {
" get-path-extents " ,
" Test cairo_fill_extents and cairo_stroke_extents " ,
0 , 0 ,
draw
} ;
2007-11-16 22:43:43 +00:00
enum ExtentsType { FILL , STROKE , PATH } ;
2006-09-25 23:22:45 -07:00
2007-09-26 14:40:36 +01:00
enum Relation { EQUALS , APPROX_EQUALS , CONTAINS } ;
2006-09-25 23:22:45 -07:00
static cairo_bool_t
check_extents ( const char * message , cairo_t * cr , enum ExtentsType type ,
enum Relation relation ,
double x , double y , double width , double height )
{
double ext_x1 , ext_y1 , ext_x2 , ext_y2 ;
const char * type_string ;
const char * relation_string ;
switch ( type ) {
default :
case FILL :
type_string = " fill " ;
cairo_fill_extents ( cr , & ext_x1 , & ext_y1 , & ext_x2 , & ext_y2 ) ;
break ;
case STROKE :
type_string = " stroke " ;
cairo_stroke_extents ( cr , & ext_x1 , & ext_y1 , & ext_x2 , & ext_y2 ) ;
break ;
2007-11-16 22:43:43 +00:00
case PATH :
type_string = " path " ;
cairo_path_extents ( cr , & ext_x1 , & ext_y1 , & ext_x2 , & ext_y2 ) ;
break ;
2006-09-25 23:22:45 -07:00
}
2007-09-26 14:40:36 +01:00
2006-09-25 23:22:45 -07:00
/* let empty rects match */
if ( ( ext_x1 = = ext_x2 | | ext_y1 = = ext_y2 ) & & ( width = = 0 | | height = = 0 ) )
return 1 ;
switch ( relation ) {
default :
case EQUALS :
relation_string = " equal " ;
if ( ext_x1 = = x & & ext_y1 = = y & & ext_x2 = = x + width & & ext_y2 = = y + height )
return 1 ;
break ;
2007-09-26 14:40:36 +01:00
case APPROX_EQUALS :
relation_string = " approx. equal " ;
2008-02-07 17:09:41 -05:00
if ( floor ( ext_x1 + 0.5 ) = = x & & floor ( ext_y1 + 0.5 ) = = y & & floor ( ext_x2 + 0.5 ) = = x + width & & floor ( ext_y2 + 0.5 ) = = y + height )
2007-09-26 14:40:36 +01:00
return 1 ;
break ;
2006-09-25 23:22:45 -07:00
case CONTAINS :
relation_string = " contain " ;
if ( width = = 0 | | height = = 0 ) {
/* odd test that doesn't really test anything... */
return 1 ;
}
if ( ext_x1 < = x & & ext_y1 < = y & & ext_x2 > = x + width & & ext_y2 > = y + height )
return 1 ;
break ;
}
2008-02-25 19:36:22 -05:00
cairo_test_log ( " Error: %s; %s extents (%g, %g) x (%g, %g) should %s (%g, %g) x (%g, %g) \n " ,
2006-09-25 23:22:45 -07:00
message , type_string ,
ext_x1 , ext_y1 , ext_x2 - ext_x1 , ext_y2 - ext_y1 ,
relation_string ,
x , y , width , height ) ;
return 0 ;
}
static cairo_test_status_t
draw ( cairo_t * cr , int width , int height )
{
cairo_surface_t * surface ;
cairo_t * cr2 ;
const char * phase ;
2008-01-21 13:34:53 -08:00
const char string [ ] = " The quick brown fox jumps over the lazy dog. " ;
2008-01-17 13:51:21 +00:00
cairo_text_extents_t extents , scaled_font_extents ;
2007-09-27 14:49:30 +01:00
cairo_test_status_t ret = CAIRO_TEST_SUCCESS ;
2007-09-26 14:40:36 +01:00
2007-12-20 18:15:48 +00:00
surface = cairo_surface_create_similar ( cairo_get_group_target ( cr ) ,
2006-09-25 23:22:45 -07:00
CAIRO_CONTENT_COLOR , 100 , 100 ) ;
/* don't use cr accidentally */
cr = NULL ;
cr2 = cairo_create ( surface ) ;
cairo_surface_destroy ( surface ) ;
cairo_set_line_width ( cr2 , 10 ) ;
cairo_set_line_join ( cr2 , CAIRO_LINE_JOIN_MITER ) ;
cairo_set_miter_limit ( cr2 , 100 ) ;
phase = " No path " ;
if ( ! check_extents ( phase , cr2 , FILL , EQUALS , 0 , 0 , 0 , 0 ) | |
2007-11-16 22:43:43 +00:00
! check_extents ( phase , cr2 , STROKE , EQUALS , 0 , 0 , 0 , 0 ) | |
! check_extents ( phase , cr2 , PATH , EQUALS , 0 , 0 , 0 , 0 ) )
2007-09-27 14:49:30 +01:00
ret = CAIRO_TEST_FAILURE ;
2006-09-25 23:22:45 -07:00
2008-01-17 10:09:22 +00:00
cairo_save ( cr2 ) ;
cairo_new_path ( cr2 ) ;
cairo_move_to ( cr2 , 200 , 400 ) ;
cairo_rel_line_to ( cr2 , 0. , 0. ) ;
phase = " Degenerate line " ;
if ( ! check_extents ( phase , cr2 , FILL , EQUALS , 0 , 0 , 0 , 0 ) | |
2008-01-21 14:45:06 -08:00
! check_extents ( phase , cr2 , STROKE , EQUALS , 0 , 0 , 0 , 0 ) | |
! check_extents ( phase , cr2 , PATH , EQUALS , 200 , 400 , 0 , 0 ) )
2008-01-17 10:09:22 +00:00
ret = CAIRO_TEST_FAILURE ;
cairo_new_path ( cr2 ) ;
cairo_move_to ( cr2 , 200 , 400 ) ;
cairo_rel_curve_to ( cr2 , 0. , 0. , 0. , 0. , 0. , 0. ) ;
phase = " Degenerate curve " ;
if ( ! check_extents ( phase , cr2 , FILL , EQUALS , 0 , 0 , 0 , 0 ) | |
2008-01-21 14:45:06 -08:00
! check_extents ( phase , cr2 , STROKE , EQUALS , 0 , 0 , 0 , 0 ) | |
! check_extents ( phase , cr2 , PATH , EQUALS , 200 , 400 , 0 , 0 ) )
2008-01-17 10:09:22 +00:00
ret = CAIRO_TEST_FAILURE ;
cairo_new_path ( cr2 ) ;
cairo_arc ( cr2 , 200 , 400 , 0. , 0 , 2 * M_PI ) ;
phase = " Degenerate arc (R=0) " ;
if ( ! check_extents ( phase , cr2 , FILL , EQUALS , 0 , 0 , 0 , 0 ) | |
2008-01-21 14:45:06 -08:00
! check_extents ( phase , cr2 , STROKE , EQUALS , 0 , 0 , 0 , 0 ) | |
! check_extents ( phase , cr2 , PATH , EQUALS , 200 , 400 , 0 , 0 ) )
2008-01-17 10:09:22 +00:00
ret = CAIRO_TEST_FAILURE ;
cairo_new_path ( cr2 ) ;
cairo_arc ( cr2 , 200 , 400 , 10. , 0 , 0 ) ;
phase = " Degenerate arc (Θ=0) " ;
if ( ! check_extents ( phase , cr2 , FILL , EQUALS , 0 , 0 , 0 , 0 ) | |
2008-01-21 14:45:06 -08:00
! check_extents ( phase , cr2 , STROKE , EQUALS , 0 , 0 , 0 , 0 ) | |
! check_extents ( phase , cr2 , PATH , EQUALS , 200 , 400 , 0 , 0 ) )
2008-01-17 10:09:22 +00:00
ret = CAIRO_TEST_FAILURE ;
cairo_new_path ( cr2 ) ;
cairo_restore ( cr2 ) ;
2008-01-21 14:56:21 -08:00
/* Test that with CAIRO_LINE_CAP_ROUND, we get "dots" from
* cairo_move_to ; cairo_rel_line_to ( 0 , 0 ) */
cairo_save ( cr2 ) ;
cairo_set_line_cap ( cr2 , CAIRO_LINE_CAP_ROUND ) ;
cairo_set_line_width ( cr2 , 20 ) ;
cairo_move_to ( cr2 , 200 , 400 ) ;
cairo_rel_line_to ( cr2 , 0 , 0 ) ;
phase = " Single 'dot' " ;
if ( ! check_extents ( phase , cr2 , FILL , EQUALS , 0 , 0 , 0 , 0 ) | |
! check_extents ( phase , cr2 , STROKE , EQUALS , 190 , 390 , 20 , 20 ) | |
! check_extents ( phase , cr2 , PATH , EQUALS , 200 , 400 , 0 , 0 ) )
ret = CAIRO_TEST_FAILURE ;
/* Add another dot without starting a new path */
cairo_move_to ( cr2 , 100 , 500 ) ;
cairo_rel_line_to ( cr2 , 0 , 0 ) ;
phase = " Multiple 'dots' " ;
if ( ! check_extents ( phase , cr2 , FILL , EQUALS , 0 , 0 , 0 , 0 ) | |
! check_extents ( phase , cr2 , STROKE , EQUALS , 90 , 390 , 120 , 120 ) | |
! check_extents ( phase , cr2 , PATH , EQUALS , 100 , 400 , 100 , 100 ) )
ret = CAIRO_TEST_FAILURE ;
cairo_new_path ( cr2 ) ;
cairo_restore ( cr2 ) ;
2007-02-21 11:50:24 -05:00
/* http://bugs.freedesktop.org/show_bug.cgi?id=7965 */
phase = " A vertical, open path " ;
cairo_save ( cr2 ) ;
cairo_set_line_cap ( cr2 , CAIRO_LINE_CAP_ROUND ) ;
cairo_set_line_join ( cr2 , CAIRO_LINE_JOIN_ROUND ) ;
cairo_move_to ( cr2 , 0 , 180 ) ;
cairo_line_to ( cr2 , 750 , 180 ) ;
if ( ! check_extents ( phase , cr2 , FILL , EQUALS , 0 , 0 , 0 , 0 ) | |
2007-11-16 22:43:43 +00:00
! check_extents ( phase , cr2 , STROKE , EQUALS , - 5 , 175 , 760 , 10 ) | |
! check_extents ( phase , cr2 , PATH , EQUALS , 0 , 180 , 755 , 0 ) )
2007-09-27 14:49:30 +01:00
ret = CAIRO_TEST_FAILURE ;
2007-02-21 11:50:24 -05:00
cairo_new_path ( cr2 ) ;
cairo_restore ( cr2 ) ;
2006-09-25 23:22:45 -07:00
phase = " Simple rect " ;
cairo_save ( cr2 ) ;
cairo_rectangle ( cr2 , 10 , 10 , 80 , 80 ) ;
if ( ! check_extents ( phase , cr2 , FILL , EQUALS , 10 , 10 , 80 , 80 ) | |
2007-11-16 22:43:43 +00:00
! check_extents ( phase , cr2 , STROKE , EQUALS , 5 , 5 , 90 , 90 ) | |
! check_extents ( phase , cr2 , PATH , EQUALS , 10 , 10 , 80 , 80 ) )
2007-09-27 14:49:30 +01:00
ret = CAIRO_TEST_FAILURE ;
2006-09-25 23:22:45 -07:00
cairo_new_path ( cr2 ) ;
cairo_restore ( cr2 ) ;
phase = " Two rects " ;
cairo_save ( cr2 ) ;
cairo_rectangle ( cr2 , 10 , 10 , 10 , 10 ) ;
cairo_rectangle ( cr2 , 20 , 20 , 10 , 10 ) ;
if ( ! check_extents ( phase , cr2 , FILL , EQUALS , 10 , 10 , 20 , 20 ) | |
2007-11-16 22:43:43 +00:00
! check_extents ( phase , cr2 , STROKE , EQUALS , 5 , 5 , 30 , 30 ) | |
! check_extents ( phase , cr2 , PATH , EQUALS , 10 , 10 , 20 , 20 ) )
2007-09-27 14:49:30 +01:00
ret = CAIRO_TEST_FAILURE ;
2006-09-25 23:22:45 -07:00
cairo_new_path ( cr2 ) ;
cairo_restore ( cr2 ) ;
phase = " Triangle " ;
cairo_save ( cr2 ) ;
cairo_move_to ( cr2 , 10 , 10 ) ;
cairo_line_to ( cr2 , 90 , 90 ) ;
cairo_line_to ( cr2 , 90 , 10 ) ;
cairo_close_path ( cr2 ) ;
/* miter joins protrude 5*(1+sqrt(2)) above the top-left corner and to
the right of the bottom - right corner */
if ( ! check_extents ( phase , cr2 , FILL , EQUALS , 10 , 10 , 80 , 80 ) | |
2007-11-16 22:43:43 +00:00
! check_extents ( phase , cr2 , STROKE , CONTAINS , 0 , 5 , 95 , 95 ) | |
! check_extents ( phase , cr2 , PATH , CONTAINS , 10 , 10 , 80 , 80 ) )
2007-09-27 14:49:30 +01:00
ret = CAIRO_TEST_FAILURE ;
2006-09-25 23:22:45 -07:00
cairo_new_path ( cr2 ) ;
cairo_restore ( cr2 ) ;
2007-09-26 14:40:36 +01:00
2008-01-21 16:45:41 -08:00
cairo_save ( cr2 ) ;
cairo_set_line_width ( cr2 , 4 ) ;
cairo_rectangle ( cr2 , 10 , 10 , 30 , 30 ) ;
cairo_rectangle ( cr2 , 25 , 10 , 15 , 30 ) ;
cairo_set_fill_rule ( cr2 , CAIRO_FILL_RULE_EVEN_ODD ) ;
phase = " EVEN_ODD overlapping rectangles " ;
if ( ! check_extents ( phase , cr2 , FILL , EQUALS , 10 , 10 , 15 , 30 ) | |
! check_extents ( phase , cr2 , STROKE , EQUALS , 8 , 8 , 34 , 34 ) | |
! check_extents ( phase , cr2 , PATH , EQUALS , 10 , 10 , 30 , 30 ) )
ret = CAIRO_TEST_FAILURE ;
/* Test other fill rule with the same path. */
cairo_set_fill_rule ( cr2 , CAIRO_FILL_RULE_WINDING ) ;
phase = " WINDING overlapping rectangles " ;
if ( ! check_extents ( phase , cr2 , FILL , EQUALS , 10 , 10 , 30 , 30 ) | |
! check_extents ( phase , cr2 , STROKE , EQUALS , 8 , 8 , 34 , 34 ) | |
! check_extents ( phase , cr2 , PATH , EQUALS , 10 , 10 , 30 , 30 ) )
ret = CAIRO_TEST_FAILURE ;
/* Now, change the direction of the second rectangle and test both
* fill rules again . */
cairo_new_path ( cr2 ) ;
cairo_rectangle ( cr2 , 10 , 10 , 30 , 30 ) ;
cairo_rectangle ( cr2 , 25 , 40 , 15 , - 30 ) ;
cairo_set_fill_rule ( cr2 , CAIRO_FILL_RULE_EVEN_ODD ) ;
phase = " EVEN_ODD overlapping rectangles " ;
if ( ! check_extents ( phase , cr2 , FILL , EQUALS , 10 , 10 , 15 , 30 ) | |
! check_extents ( phase , cr2 , STROKE , EQUALS , 8 , 8 , 34 , 34 ) | |
! check_extents ( phase , cr2 , PATH , EQUALS , 10 , 10 , 30 , 30 ) )
ret = CAIRO_TEST_FAILURE ;
/* Test other fill rule with the same path. */
cairo_set_fill_rule ( cr2 , CAIRO_FILL_RULE_WINDING ) ;
phase = " WINDING overlapping rectangles " ;
if ( ! check_extents ( phase , cr2 , FILL , EQUALS , 10 , 10 , 15 , 30 ) | |
! check_extents ( phase , cr2 , STROKE , EQUALS , 8 , 8 , 34 , 34 ) | |
! check_extents ( phase , cr2 , PATH , EQUALS , 10 , 10 , 30 , 30 ) )
ret = CAIRO_TEST_FAILURE ;
cairo_new_path ( cr2 ) ;
cairo_restore ( cr2 ) ;
2007-10-27 17:29:54 +01:00
/* http://bugs.freedesktop.org/show_bug.cgi?id=7245 */
2007-09-26 14:40:36 +01:00
phase = " Arc " ;
cairo_save ( cr2 ) ;
cairo_arc ( cr2 , 250.0 , 250.0 , 157.0 , 5.147 , 3.432 ) ;
cairo_set_line_width ( cr2 , 154.0 ) ;
if ( ! check_extents ( phase , cr2 , STROKE , APPROX_EQUALS , 16 , 38 , 468 , 446 ) )
2007-09-27 14:49:30 +01:00
ret = CAIRO_TEST_FAILURE ;
cairo_new_path ( cr2 ) ;
cairo_restore ( cr2 ) ;
phase = " Text " ;
cairo_save ( cr2 ) ;
2008-01-21 13:33:46 -08:00
cairo_set_tolerance ( cr2 , 100.0 ) ;
2007-09-27 14:49:30 +01:00
cairo_select_font_face ( cr2 , " Bitstream Vera Sans " ,
CAIRO_FONT_SLANT_NORMAL ,
CAIRO_FONT_WEIGHT_NORMAL ) ;
cairo_set_font_size ( cr2 , 12 ) ;
2008-01-21 13:33:46 -08:00
cairo_text_extents ( cr2 , string , & extents ) ;
2008-01-17 13:51:21 +00:00
/* double check that the two methods of measuring the text agree... */
cairo_scaled_font_text_extents ( cairo_get_scaled_font ( cr2 ) ,
2008-01-21 13:33:46 -08:00
string ,
& scaled_font_extents ) ;
2008-01-17 13:51:21 +00:00
if ( memcmp ( & extents , & scaled_font_extents , sizeof ( extents ) ) ) {
cairo_test_log ( " Error: cairo_text_extents() does not match cairo_scaled_font_text_extents() - font extents (%f, %f) x (%f, %f) should be (%f, %f) x (%f, %f) \n " ,
scaled_font_extents . x_bearing ,
scaled_font_extents . y_bearing ,
scaled_font_extents . width ,
scaled_font_extents . height ,
extents . x_bearing ,
extents . y_bearing ,
extents . width ,
extents . height ) ;
ret = CAIRO_TEST_FAILURE ;
}
2007-09-27 14:49:30 +01:00
cairo_move_to ( cr2 , - extents . x_bearing , - extents . y_bearing ) ;
2008-01-21 13:33:46 -08:00
cairo_text_path ( cr2 , string ) ;
2007-09-27 14:49:30 +01:00
cairo_set_line_width ( cr2 , 2.0 ) ;
if ( ! check_extents ( phase , cr2 , FILL , EQUALS , 0 , 0 , extents . width , extents . height ) | |
2007-11-16 22:43:43 +00:00
! check_extents ( phase , cr2 , STROKE , EQUALS , - 1 , - 1 , extents . width + 2 , extents . height + 2 ) | |
! check_extents ( phase , cr2 , PATH , EQUALS , 0 , 0 , extents . width , extents . height ) )
2007-09-27 14:49:30 +01:00
ret = CAIRO_TEST_FAILURE ;
2007-09-26 14:40:36 +01:00
cairo_new_path ( cr2 ) ;
cairo_restore ( cr2 ) ;
2006-09-25 23:22:45 -07:00
phase = " User space, simple scale, getting extents with same transform " ;
cairo_save ( cr2 ) ;
cairo_scale ( cr2 , 2 , 2 ) ;
cairo_rectangle ( cr2 , 5 , 5 , 40 , 40 ) ;
if ( ! check_extents ( phase , cr2 , FILL , EQUALS , 5 , 5 , 40 , 40 ) | |
2007-11-16 22:43:43 +00:00
! check_extents ( phase , cr2 , STROKE , EQUALS , 0 , 0 , 50 , 50 ) | |
! check_extents ( phase , cr2 , PATH , EQUALS , 5 , 5 , 40 , 40 ) )
2007-09-27 14:49:30 +01:00
ret = CAIRO_TEST_FAILURE ;
2006-09-25 23:22:45 -07:00
cairo_new_path ( cr2 ) ;
cairo_restore ( cr2 ) ;
phase = " User space, simple scale, getting extents with no transform " ;
cairo_save ( cr2 ) ;
cairo_save ( cr2 ) ;
cairo_scale ( cr2 , 2 , 2 ) ;
cairo_rectangle ( cr2 , 5 , 5 , 40 , 40 ) ;
cairo_restore ( cr2 ) ;
if ( ! check_extents ( phase , cr2 , FILL , EQUALS , 10 , 10 , 80 , 80 ) | |
2007-11-16 22:43:43 +00:00
! check_extents ( phase , cr2 , STROKE , EQUALS , 5 , 5 , 90 , 90 ) | |
! check_extents ( phase , cr2 , PATH , EQUALS , 10 , 10 , 80 , 80 ) )
2007-09-27 14:49:30 +01:00
ret = CAIRO_TEST_FAILURE ;
2006-09-25 23:22:45 -07:00
cairo_new_path ( cr2 ) ;
cairo_restore ( cr2 ) ;
phase = " User space, rotation, getting extents with transform " ;
cairo_save ( cr2 ) ;
cairo_rectangle ( cr2 , - 50 , - 50 , 50 , 50 ) ;
cairo_rotate ( cr2 , - M_PI / 4 ) ;
/* the path in user space is now (nearly) the square rotated by
45 degrees about the origin . Thus its x1 and x2 are both nearly 0.
This should show any bugs where we just transform device - space
x1 , y1 and x2 , y2 to get the extents . */
/* The largest axis-aligned square inside the rotated path has
side lengths 50 * sqrt ( 2 ) , so a bit over 35 on either side of
the axes . With the stroke width added to the rotated path ,
the largest axis - aligned square is a bit over 38 on either side of
the axes . */
if ( ! check_extents ( phase , cr2 , FILL , CONTAINS , - 35 , - 35 , 35 , 35 ) | |
2007-11-16 22:43:43 +00:00
! check_extents ( phase , cr2 , STROKE , CONTAINS , - 38 , - 38 , 38 , 38 ) | |
! check_extents ( phase , cr2 , PATH , CONTAINS , - 35 , - 35 , 35 , 35 ) )
2007-09-27 14:49:30 +01:00
ret = CAIRO_TEST_FAILURE ;
2006-09-25 23:22:45 -07:00
cairo_new_path ( cr2 ) ;
cairo_restore ( cr2 ) ;
cairo_destroy ( cr2 ) ;
2007-09-27 14:49:30 +01:00
return ret ;
2006-09-25 23:22:45 -07:00
}
int
main ( void )
{
return cairo_test ( & test ) ;
}