I am writing an application using Cairo in C that does the following:
- Load background PNG (wheel)
- rotate wheel 90 degrees
- draw on wheel a set of numbers from other PNG files
- do this until all 6 parts of the wheel have the numbers drawn on the wheel
- save the PNG to a file ( results.png )
The problem I’m having is if I don’t rotate the wheel, the numbers are drawn properly as seen in this image, except the wheel isn’t rotated as needed.
However, if I attempt to rotate AND draw the numbers on the wheel I get a rotated wheel with no numbers as seen in this image.
I’ve tried various permutations of the code, but I can’t seem to make it work. I would appreciate some hints and / or sample code that shows what I’m doing wrong. I’ve checked the Cairo documentation to no avail.
The code can be found here.
And here:
#include <cairo.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
typedef struct
{
int numImages; /* Number of images in win amount string */
int indexes[7]; /* indexes into NumberImages[] */
}WinAmountData;
/***** Function Prototypes *****************/
int InitImages( void );
void DestroyNumberImages( void );
int ParseWinAmountString( char *string, WinAmountData *amtData );
int Rotate( cairo_t *cr , cairo_surface_t *image, double degrees );
/*******************************************/
typedef struct
{
int xOffset; /* pixel count offsete before next digit */
char fileName[20];
cairo_surface_t *image;
}ImageInfo;
ImageInfo NumberImages[] =
{
{ 8, "images/0.png", NULL },
{ 10, "images/1.png", NULL },
{ 10, "images/2.png", NULL },
{ 10, "images/3.png", NULL },
{ 10, "images/4.png", NULL },
{ 10, "images/5.png", NULL },
{ 10, "images/6.png", NULL },
{ 10, "images/7.png", NULL },
{ 10, "images/8.png", NULL },
{ 10, "images/9.png", NULL },
{ 7, "images/$.png", NULL },
{ 10, "images/euro.png", NULL },
{ 7, "images/pound.png", NULL },
{ 7, "images/yen.png", NULL }
};
enum { DOLLAR = 10, EURO, POUND, YEN };
#define FALSE 0
#define TRUE 1
int InitImages( void )
{
int i;
int ret = TRUE;
cairo_status_t imgStatus;
for( i = 0; i < ( sizeof( NumberImages ) / sizeof( ImageInfo ) ) && ret == TRUE; i++ )
{
NumberImages[i].image = cairo_image_surface_create_from_png( NumberImages[i].fileName );
imgStatus = cairo_surface_status(NumberImages[i].image);
ret = ( CAIRO_STATUS_SUCCESS == imgStatus );
}
return( ret );
}
void DestroyNumberImages( void )
{
int i;
for( i = 0; i < ( sizeof( NumberImages ) / sizeof( ImageInfo ) ); i++ )
{
cairo_surface_destroy(NumberImages[i].image);
}
return;
}
int ParseWinAmountString( char *string, WinAmountData *amtData )
{
int ret = TRUE;
int i = 0, len;
len = strlen( string );
if( (len > 0) && (len < 8) )
{
amtData->numImages = len;
for( i = 0; i < amtData->numImages && TRUE == ret; i++ )
{
if( string[i] >= '0' && string[i] <= '9' )
{
amtData->indexes[i] = string[i] - '0';
}
else
{
switch( string[i] )
{
case 'D':
amtData->indexes[i] = DOLLAR;
break;
case 'Y':
amtData->indexes[i] = YEN;
break;
case 'E':
amtData->indexes[i] = EURO;
break;
case 'P':
amtData->indexes[i] = POUND;
break;
default:
ret = FALSE;
break;
}
}
}
}
else
{
ret = FALSE;
}
return( ret );
}
double DegreesToRadians( double degrees )
{
return((double)((double)degrees * ( (double)M_PI/(double)180.0 )));
}
int Rotate( cairo_t *cr , cairo_surface_t *image, double degrees )
{
int ret = 0;
cairo_translate(cr, 90, 90);
cairo_rotate(cr, DegreesToRadians( degrees ));
cairo_set_source_surface(cr, image, -90, -90);
cairo_paint(cr);
return ( ret );
}
int main(int argc, char *argv[])
{
int i,x,y;
cairo_surface_t *imgWheelBg = NULL;
WinAmountData amtData;
if( argc == 2 )
{
printf("Parsing [%s]\n", argv[1]);
if ( ParseWinAmountString( argv[1], &amtData ) == TRUE )
{
printf("Amount indexes = [ ");
for( i = 0; i < amtData.numImages; i++ )
{
printf("%d ", amtData.indexes[i]);
}
printf("]\n");
}
else
{
printf("Failed to parse amount.\n");
return( 1 );
}
}
else
{
printf("Usage: %s <Amount String>\n", argv[0]);
return( 1 );
}
if( InitImages() == TRUE )
{
imgWheelBg = cairo_image_surface_create_from_png("images/blankwheel.png");
//Create the background image
cairo_surface_t *imgResult = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 180, 180);
//Create the cairo context
cairo_t *cr = cairo_create(imgResult);
//Paint empty wheel image
cairo_set_source_surface(cr, imgWheelBg, 0, 0);
cairo_paint(cr);
// At this point the wheel is painted ( blankwheel.png )
// vvvvvvvvvv THIS PART SEEMS TO BE CAUSING TROUBLES vvvvvvvvvv
// With this call the wheel DOES get rotated 90 degress. Confirmed
// by viewing the resulting PNG file.
// HOWEVER, after the Rotate() is called the numbers aren't put on the wheel.
// if I remove the Rotate() call, the wheel is drawn, not rotated, but the
// numbers are properly composited over the image.
//Rotate( cr, imgWheelBg, 90 );
// ^^^^^^^^^^^ THIS PART SEEMS TO BE CAUSING TROUBLES ^^^^^^^^^^^
/* Set drawing begin point in pixels */
x = 101;
y = 82;
/* Draw all characters in win amount string */
for( i = 0; i < amtData.numImages; i++ )
{
cairo_set_source_surface(cr, NumberImages[amtData.indexes[i]].image, x, y);
cairo_paint(cr);
x += NumberImages[i].xOffset;
}
//Destroy the cairo context and/or flush the destination image
cairo_surface_flush(imgResult);
cairo_destroy(cr);
//And write the results into a new file
cairo_surface_write_to_png(imgResult, "result.png");
// Destroy resources
cairo_surface_destroy(imgResult);
cairo_surface_destroy(imgWheelBg);
DestroyNumberImages();
printf("SUCCESS\n");
}
else
{
printf("FAILED Init Images\n");
}
return 0;
}
EDIT: The ultimate goal is to generate an image like this one programmatically and the animate it as needed in real time within a GTK application.
EDIT: With the comments from Mikhail Kozhevnikov and Uli Schlachter I was able to find a solution using this code.
Thank you very much!
I think the problem is that the transformation is applied to everything, including the number images, and they get drawn somewhere outside the picture because of this transformation. I’d suggest that the matrix be stored before applying the transformation and restored after the circle is drawn. Or would you like the numbers to rotate too?..