[cairo] How to get extents of transformed graphics

Andreas Falkenhahn andreas at falkenhahn.com
Fri Sep 9 17:23:10 UTC 2022


On 09.09.2022 at 17:38 Bill Spitzak wrote:

> Not sure exactly what you are trying to do, but this is probably
> what you want (this also assumes x2>x1 and y2>y1):
> width = ceil(x2 * 4.6) - floor(x1 * 4.6);
> height = ceil(y2 * 4.6) - floor(y1 * 4.6);

Thanks for your answer! Unfortunately, the "C" shape still gets cut off so the surface height is still too small. 

> The reason for the ceil and floor is so the fractional-covered
> pixels are rounded out to the result. You have to do this after you
> convert to pixels which is what multiplying by 4.6 is doing (?).

No, 4.6 is the scaling factor on both axes. What I'm trying to do is this: Allocate an image surface so that a transformed path fits into it exactly. I'm transforming the "C" shape by applying an affine transformation with sx=4.6 and sy=4.6. Now I need to find out the size in pixels for this transformed path so that I can allocate an image surface for it.

Here's the test program. It draws the transformed "C" shape to an image surface and then saves this image surface to a PNG. As you can see in the output PNG, a row is missing at the bottom of the image.

#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

#include <cairo.h>

int main(int argc, char *argv[])
{
	cairo_t *cr;
	cairo_surface_t *surface;
	cairo_matrix_t cm;
	int k;
	double tx = 0, ty = 0;
	double x1, y1, x2, y2;
	
	surface = cairo_image_surface_create(CAIRO_FORMAT_A8, 1000, 1000);
	cr = cairo_create(surface);

	for(k = 0; k < 2; k++) {
	
		cairo_new_path(cr);
		cairo_move_to(cr, tx, ty);
		cairo_set_font_size(cr, 100);
		cairo_text_path(cr, "C");				
		
		if(!k) {	
		
			double width, height;
			
			cairo_fill_extents(cr, &x1, &y1, &x2, &y2);
			
			tx = -x1;
			ty = -y1;		
			
			width = ceil(x2 * 4.6) - floor(x1 * 4.6);
			height = ceil(y2 * 4.6) - floor(y1 * 4.6);
			
			cairo_destroy(cr);
			cairo_surface_destroy(surface);
			
			surface = cairo_image_surface_create(CAIRO_FORMAT_A8, width, height);
			cr = cairo_create(surface);			
				
			cairo_matrix_init(&cm, 4.6, 0, 0, 4.6, 0, 0);
			cairo_set_matrix(cr, &cm);			
		}
	}
	
	cairo_fill(cr);

	cairo_surface_flush(surface);
	cairo_surface_write_to_png(surface, "tmp.png");
	
	cairo_destroy(cr);
	cairo_surface_destroy(surface);
	
	return 0;
} 

> On Fri, Sep 9, 2022 at 7:16 AM Andreas Falkenhahn <andreas at falkenhahn.com> wrote:

> I can fix the issue by adding 2 to "height" like so:
>  
>          width = fabs(ceil(x2) - floor(x1));
>          height = fabs(ceil(y2) - floor(y1));
>  
>          width = ceil(width * 4.6);
>          height = ceil(height * 4.6) + 2;
>  
>  But this feels very kludgy and probably only works for my specific
> case and not for any arbitrary transformation values.
>  
>  Is there really no general solution to this problem? IMHO it's a
> rather common thing to do and I'm a bit puzzled why this should be so difficult to solve...
>  
>  On 08.09.2022 at 19:24 Andreas Falkenhahn wrote:
>  
 >> I've tried this:
>  
 >>         width = fabs(x2 - floor(x1));
 >>         height = fabs(y2 - floor(y1));
>  
 >>         width = ceil(width * 4.6);
 >>         height = ceil(height * 4.6);
>  
 >> Still cuts off a line of the "C" shape. I've also tried this:
>  
 >>         width = fabs(ceil(x2) - floor(x1));
 >>         height = fabs(ceil(y2) - floor(y1));
>  
 >>         width = ceil(width * 4.6);
 >>         height = ceil(height * 4.6);
>  
 >> No success either. A line is cut off.
>  
 >> Any more ideas? You can find my test program below...
>  
 >> On 08.09.2022 at 19:03 Behdad Esfahbod wrote:
>  
 >>> Try flooring x1,y1 before subtracting them from x2,y2 for computing the size of your surface.
>  
>  
 >>> behdad
 >>> http://behdad.org/
>  
 >>> On Thu, Sep 8, 2022 at 10:21 AM Andreas Falkenhahn <andreas at falkenhahn.com> wrote:
>  
 >>> Is there really nobody who knows how to solve this? There surely
 >>> must be a way to find out the *exact* extents of transformed
 >>> graphics? The idea is to find out the exact dimensions in order to
 >>> allocate a Cairo surface that *exactly* matches the size of the path.
 >>>  
 >>>  On 28.08.2022 at 21:47 Andreas Falkenhahn wrote:
 >>>  
  >>>> Hi,
 >>>  
  >>>> is there any way to get the *exact* extents of transformed
  >>>> graphics? AFAICS, cairo_fill_extents() is in user coordinates, i.e.
  >>>> it doesn't take any transformation settings into account. So I've
  >>>> tried to apply the transformation manually to what I get from
  >>>> cairo_fill_extents() but I don't seem to get it 100% right. It looks
  >>>> like one row is missing at the bottom. 
 >>>  
  >>>> This is what I've tried:
 >>>  
  >>>>         cairo_t *cr;
  >>>>         cairo_surface_t *surface;
  >>>>         cairo_matrix_t cm;
  >>>>         int k;
  >>>>         double tx = 0, ty = 0;
  >>>>         double x1, y1, x2, y2;
  >>>>         
  >>>>         surface = cairo_image_surface_create(CAIRO_FORMAT_A8, 1000, 1000);
  >>>>         cr = cairo_create(surface);
 >>>  
  >>>>         for(k = 0; k < 2; k++) {
  >>>>         
  >>>>                 cairo_new_path(cr);
  >>>>                 cairo_move_to(cr, tx, ty);
  >>>>                 cairo_set_font_size(cr, 100);
  >>>>                 cairo_text_path(cr, "C");                          
  >>>>                 
  >>>>                 if(!k) {        
  >>>>                 
  >>>>                         double width, height;
  >>>>                         
  >>>>                         cairo_fill_extents(cr, &x1, &y1, &x2, &y2);
  >>>>                         
  >>>>                         tx = -x1;
  >>>>                         ty = -y1;               
  >>>>                         
  >>>>                         width = fabs(x2 - x1);
  >>>>                         height = fabs(y2 - y1);
 >>>  
  >>>>                         width = ceil(width * 4.6);
  >>>>                         height = ceil(height * 4.6);
  >>>>                         
  >>>>                         cairo_destroy(cr);
  >>>>                         cairo_surface_destroy(surface);
  >>>>                         
  >>>>                         surface =
  >>>> cairo_image_surface_create(CAIRO_FORMAT_A8, width, height);
  >>>>                         cr = cairo_create(surface);                
  >>>>                                 
  >>>>                         cairo_matrix_init(&cm, 4.6, 0, 0, 4.6, 0, 0);
  >>>>                         cairo_set_matrix(cr, &cm);                 
  >>>>                 }
  >>>>         }
  >>>>         
  >>>>         cairo_fill(cr);
 >>>  
  >>>>         cairo_surface_flush(surface);
  >>>>         cairo_surface_write_to_png(surface, "tmp.png");
  >>>>         
  >>>>         cairo_destroy(cr);
  >>>>         cairo_surface_destroy(surface);
 >>>  
  >>>> I'm attaching the resulting image. As you can see, there's at least
  >>>> one row of the "C" shape missing at the bottom of the image.
 >>>  
  >>>> Any ideas how to get this right? Is this some sort of floating
  >>>> point inaccuracy and should I just add 1 to width/height to solve
  >>>> this or am I doing something wrong here and there is a better way?
 >>>  
 >>>  
 >>>  
 >>>  -- 
 >>>  Best regards,
 >>>   Andreas Falkenhahn                            mailto:andreas at falkenhahn.com
 >>>  
 >>>  
>  
>  
>  
>  
>  -- 
>  Best regards,
>   Andreas Falkenhahn                            mailto:andreas at falkenhahn.com
>  
>  


-- 
Best regards,
 Andreas Falkenhahn                            mailto:andreas at falkenhahn.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: test.png
Type: image/png
Size: 4452 bytes
Desc: not available
URL: <https://lists.cairographics.org/archives/cairo/attachments/20220909/80b3ae4e/attachment-0001.png>


More information about the cairo mailing list