2
* Copyright (c) 2009 Giuseppe Torelli <colossus73@gmail.com>
4
* This program is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation; either version 2 of the License,or
7
* (at your option) any later version.
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU Library General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program; if not,write to the Free Software
16
* Foundation,Inc.,59 Temple Place - Suite 330,Boston,MA 02111-1307,USA.
21
#include <glib/gstdio.h>
23
#define PLUGINS_INSTALLED 0
25
static gboolean img_plugin_is_loaded(img_window_struct *, GModule *);
27
GtkWidget *img_load_icon(gchar *filename, GtkIconSize size)
29
GtkWidget *file_image;
31
GdkPixbuf *file_pixbuf = NULL;
33
path = g_strconcat(DATADIR, "/imagination/pixmaps/",filename,NULL);
34
file_pixbuf = gdk_pixbuf_new_from_file(path,NULL);
37
if (file_pixbuf == NULL)
38
file_image = gtk_image_new_from_stock(GTK_STOCK_MISSING_IMAGE, size);
41
file_image = gtk_image_new_from_pixbuf(file_pixbuf);
42
g_object_unref (file_pixbuf);
47
gchar *img_convert_seconds_to_time(gint total_secs)
51
h = total_secs / 3600;
52
m = (total_secs % 3600) / 60;
53
s = total_secs - (h * 3600) - (m * 60);
54
return g_strdup_printf("%02d:%02d:%02d", h, m, s);
57
GtkWidget *_gtk_combo_box_new_text(gboolean pointer)
60
GtkCellRenderer *cell;
67
tree = gtk_tree_store_new (4, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_INT);
68
model = GTK_TREE_MODEL( tree );
70
combo_box = gtk_combo_box_new_with_model (model);
71
g_object_unref (G_OBJECT( model ));
72
cell = gtk_cell_renderer_pixbuf_new ();
73
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, FALSE);
74
gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), cell, "pixbuf", 0, NULL);
78
list = gtk_list_store_new (1, G_TYPE_STRING);
79
model = GTK_TREE_MODEL( list );
81
combo_box = gtk_combo_box_new_with_model (model);
82
g_object_unref (G_OBJECT( model ));
85
cell = gtk_cell_renderer_text_new ();
86
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, TRUE);
87
gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), cell, "text", pointer ? 1 : 0, NULL);
88
g_object_set(cell, "ypad", (guint)0, NULL);
92
void img_set_statusbar_message(img_window_struct *img_struct, gint selected)
94
gchar *message = NULL;
95
gchar *total_slides = NULL;
97
if (img_struct->slides_nr == 0)
99
message = g_strdup_printf(_("Welcome to Imagination - %d transitions loaded."),img_struct->nr_transitions_loaded);
100
gtk_statusbar_push(GTK_STATUSBAR(img_struct->statusbar),img_struct->context_id,message);
102
gtk_label_set_text(GTK_LABEL(img_struct->total_slide_number_label),NULL);
106
message = g_strdup_printf(_("%d slides selected"),selected);
107
gtk_statusbar_push(GTK_STATUSBAR(img_struct->statusbar),img_struct->context_id,message);
112
total_slides = g_strdup_printf("%d",img_struct->slides_nr);
113
gtk_label_set_text(GTK_LABEL(img_struct->total_slide_number_label),total_slides);
114
message = g_strdup_printf(ngettext("%d slide imported %s" ,"%d slides imported %s",img_struct->slides_nr),img_struct->slides_nr,_(" - Use the CTRL key to select/unselect or SHIFT for multiple select"));
115
gtk_statusbar_push(GTK_STATUSBAR(img_struct->statusbar),img_struct->context_id,message);
116
g_free(total_slides);
121
void img_load_available_transitions(img_window_struct *img)
124
const gchar *transition_name;
125
gchar *fname = NULL, *name, *filename;
126
gchar **trans, **bak;
129
GtkTreeIter piter, citer;
132
gchar *search_paths[3], **path;
133
void (*plugin_set_name)(gchar **, gchar ***);
135
model = GTK_TREE_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(img->transition_type)));
137
/* Fill the combo box with no transition */
138
gtk_tree_store_append(model, &piter, NULL);
139
gtk_tree_store_set(model, &piter, 0, NULL, 1, _("None"), 2, NULL, 3, -1, -1);
140
gtk_combo_box_set_active(GTK_COMBO_BOX(img->transition_type), 0);
142
/* Create NULL terminated array of paths that we'll be looking at */
143
#if PLUGINS_INSTALLED
144
search_paths[0] = g_strconcat(PACKAGE_LIB_DIR,"/imagination",NULL);
146
search_paths[0] = g_strdup("./transitions");
148
search_paths[1] = g_strconcat( g_get_home_dir(), "/.imagination/plugins", NULL );
149
search_paths[2] = NULL;
151
/* Search all paths listed in array */
152
for( path = search_paths; *path; path++ )
154
dir = g_dir_open( *path, 0, NULL );
163
transition_name = g_dir_read_name( dir );
164
if ( transition_name == NULL )
167
fname = g_build_filename( *path, transition_name, NULL );
168
module = g_module_open( fname, G_MODULE_BIND_LOCAL );
169
if( module && img_plugin_is_loaded(img, module) == FALSE )
171
/* Obtain the name from the plugin function */
172
g_module_symbol( module, "img_get_plugin_info",
173
(void *)&plugin_set_name);
174
plugin_set_name( &name, &trans );
176
/* Add group name to the store */
177
gtk_tree_store_append( model, &piter, NULL );
178
gtk_tree_store_set( model, &piter, 0, NULL, 1, name, 3, 0, -1 );
179
img->plugin_list = g_slist_append(img->plugin_list, module);
181
/* Add transitions */
182
for( bak = trans; *trans; trans += 3 )
184
#if PLUGINS_INSTALLED
186
g_strdup_printf( "%s/imagination/pixmaps/imagination-%d.png",
187
DATADIR, GPOINTER_TO_INT( trans[2] ) );
188
#else /* PLUGINS_INSTALLED */
190
g_strdup_printf( "./pixmaps/imagination-%d.png",
191
GPOINTER_TO_INT( trans[2] ) );
192
#endif /* ! PLUGINS_INSTALLED */
194
pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
196
/* Local plugins will fail to load images from system
197
* folder, so we'll try to load the from home folder. */
202
g_strdup_printf( "%s/.imagination/pixmaps/imagination-%d.png",
204
GPOINTER_TO_INT( trans[2] ) );
205
pixbuf = gdk_pixbuf_new_from_file( filename, NULL );
208
g_module_symbol( module, trans[1], &address );
209
gtk_tree_store_append( model, &citer, &piter );
210
gtk_tree_store_set( model, &citer, 0, pixbuf,
213
3, GPOINTER_TO_INT( trans[2] ),
215
img->nr_transitions_loaded++;
226
static gboolean img_plugin_is_loaded(img_window_struct *img, GModule *module)
228
return (g_slist_find(img->plugin_list,module) != NULL);
231
void img_show_file_chooser(SexyIconEntry *entry, SexyIconEntryPosition icon_pos,int button,img_window_struct *img)
233
GtkWidget *file_selector;
237
file_selector = gtk_file_chooser_dialog_new (_("Please choose the slideshow project filename"),
238
GTK_WINDOW (img->imagination_window),
239
GTK_FILE_CHOOSER_ACTION_SAVE,
246
gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER (file_selector),TRUE);
247
response = gtk_dialog_run (GTK_DIALOG(file_selector));
248
if (response == GTK_RESPONSE_ACCEPT)
250
dest_dir = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (file_selector));
251
gtk_entry_set_text(GTK_ENTRY(entry),dest_dir);
254
gtk_widget_destroy(file_selector);
258
img_create_new_slide( void )
260
slide_struct *slide = NULL;
262
slide = g_slice_new0( slide_struct );
269
slide->path = g_strdup( "0" );
270
slide->transition_id = -1;
271
slide->speed = NORMAL;
274
slide->cur_point = -1;
277
slide->anim_duration = 1;
278
slide->position = IMG_SUB_POS_MIDDLE_CENTER;
279
slide->placing = IMG_REL_PLACING_EXPORTED_VIDEO;
280
slide->font_desc = pango_font_description_from_string( "Sans 12" );
281
slide->font_color[0] = 0; /* R */
282
slide->font_color[1] = 0; /* G */
283
slide->font_color[2] = 0; /* B */
284
slide->font_color[3] = 1; /* A */
291
img_set_slide_file_info( slide_struct *slide,
292
const gchar *filename )
294
GdkPixbufFormat *format;
298
format = gdk_pixbuf_get_file_info( filename, &width, &height );
300
slide->o_filename = g_strdup( filename );
301
slide->r_filename = g_strdup( filename );
304
slide->resolution = g_strdup_printf( "%d x %d", width, height );
305
slide->type = gdk_pixbuf_format_get_name( format );
309
img_set_slide_gradient_info( slide_struct *slide,
311
gdouble *start_color,
313
gdouble *start_point,
314
gdouble *stop_point )
318
slide->gradient = gradient;
319
for( i = 0; i < 3; i++ )
321
slide->g_start_color[i] = start_color[i];
322
slide->g_stop_color[i] = stop_color[i];
324
for( i = 0; i < 2; i++ )
326
slide->g_start_point[i] = start_point[i];
327
slide->g_stop_point[i] = stop_point[i];
332
img_set_slide_still_info( slide_struct *slide,
334
img_window_struct *img )
336
if( slide->duration != duration )
338
slide->duration = duration;
340
if( ! img->total_dur_id )
342
g_idle_add( (GSourceFunc)img_set_total_slideshow_duration, img );
347
img_set_slide_transition_info( slide_struct *slide,
355
img_window_struct *img )
357
/* Set transition render. */
358
if( path && ( slide->transition_id != transition_id ) )
361
g_free( slide->path );
363
slide->path = g_strdup( path );
364
slide->transition_id = transition_id;
365
slide->render = render;
367
gtk_list_store_set( store, iter, 2, pix, -1 );
370
if( speed && ( slide->speed != speed ) )
372
slide->speed = speed;
374
if( ! img->total_dur_id )
376
g_idle_add( (GSourceFunc)img_set_total_slideshow_duration, img );
381
img_set_slide_ken_burns_info( slide_struct *slide,
390
if( slide->no_points )
392
g_list_free( slide->points );
393
slide->no_points = 0;
396
for( i = 0; i < length; i += 4 )
398
/* Create new point */
399
point = g_slice_new( ImgStopPoint );
400
point->time = (gint)( points[0 + i] + 0.5 );
401
point->offx = points[1 + i];
402
point->offy = points[2 + i];
403
point->zoom = points[3 + i];
405
/* Append it to the list */
406
slide->points = g_list_append( slide->points, point );
410
slide->cur_point = CLAMP( cur_point, -1, slide->no_points - 1 );
412
full = img_calc_slide_duration_points( slide->points,
415
slide->duration = full;
419
img_set_slide_text_info( slide_struct *slide,
422
const gchar *subtitle,
427
const gchar *font_desc,
429
img_window_struct *img )
431
/* Set the slide text info parameters */
436
if( slide->subtitle )
437
g_free( slide->subtitle );
438
slide->subtitle = g_strdup( subtitle );
440
flag = ( subtitle ? TRUE : FALSE );
441
gtk_list_store_set( store, iter, 3, flag, -1 );
444
if( ( anim_id > -1 ) && ( anim_id != slide->anim_id ) )
450
path = g_strdup_printf( "%d", anim_id );
451
model = gtk_combo_box_get_model( GTK_COMBO_BOX( img->sub_anim ) );
452
gtk_tree_model_get_iter_from_string( model, &iter, path );
455
slide->anim_id = anim_id;
456
gtk_tree_model_get( model, &iter, 1, &slide->anim, -1 );
459
img_sync_timings( slide, img );
462
if( ( anim_duration > 0 ) && ( anim_duration != slide->anim_duration ) )
464
slide->anim_duration = anim_duration;
466
/* Synchronize timings */
467
img_sync_timings( slide, img );
470
if( ( position > -1 ) && ( position != slide->position ) )
471
slide->position = position;
473
if( ( placing > -1 ) && ( placing != slide->placing ) )
474
slide->placing = placing;
478
if( slide->font_desc )
479
pango_font_description_free( slide->font_desc );
480
slide->font_desc = pango_font_description_from_string( font_desc );
485
slide->font_color[0] = font_color[0];
486
slide->font_color[1] = font_color[1];
487
slide->font_color[2] = font_color[2];
488
slide->font_color[3] = font_color[3];
493
img_free_slide_struct( slide_struct *entry )
497
if( entry->angle != ANGLE_0 )
498
g_unlink( entry->r_filename );
499
g_free(entry->o_filename);
500
g_free(entry->r_filename);
501
g_free(entry->resolution);
504
/* Free stop point list */
505
for( tmp = entry->points; tmp; tmp = g_list_next( tmp ) )
506
g_slice_free( ImgStopPoint, tmp->data );
507
g_list_free( entry->points );
509
g_slice_free( slide_struct, entry );
513
img_set_total_slideshow_duration( img_window_struct *img )
522
model = GTK_TREE_MODEL( img->thumbnail_model );
523
if( gtk_tree_model_get_iter_first( model, &iter ) )
527
gtk_tree_model_get( model, &iter, 1, &entry, -1 );
528
img->total_secs += entry->duration;
531
img->total_secs += entry->speed;
533
while( gtk_tree_model_iter_next( model, &iter ) );
535
/* Add time of last pseudo slide */
536
if( img->final_transition.render )
537
img->total_secs += img->final_transition.speed;
540
time = img_convert_seconds_to_time(img->total_secs);
541
gtk_label_set_text(GTK_LABEL (img->total_time_data),time);
544
/* This is here only to be able to add this to idle source. */
545
img->total_dur_id = 0;
550
img_calc_slide_duration_points( GList *list,
554
gint i, duration = 0;
557
/* If we have no points, return 0 */
561
/* Calculate length */
562
for( tmp = list, i = 0; i < length; tmp = g_list_next( tmp ), i++ )
564
point = (ImgStopPoint *)tmp->data;
565
duration += point->time;
574
* This function should be called for all image loading needs. It'll properly
575
* scale and trim loaded images, add borders if needed and return surface or
576
* pixbuf of requested size.
578
* If one of the size requests is 0, the other one will be calculated from
579
* first one with aspect ratio calculation. If both dimensions are 0, image
580
* will be loaded from disk at original size (this is mainly used for export,
581
* when we want to have images at their best quality).
583
* Return value: TRUE if image loading succeded, FALSE otherwise.
586
img_scale_image( const gchar *filename,
593
cairo_surface_t **surface )
595
GdkPixbuf *loader; /* Pixbuf used for loading */
596
gint i_width, i_height; /* Image dimensions */
597
gint offset_x, offset_y; /* Offset values for borders */
598
gdouble i_ratio; /* Export and image aspect ratios */
599
gdouble skew; /* Transformation between ratio and
601
gboolean transform = FALSE; /* Flag that controls scalling */
603
/* MAximal distortion values */
604
gdouble max_stretch = 0.1280;
605
gdouble max_crop = 0.8500;
607
/* Borderline skew values */
608
gdouble max_skew = ( 1 + max_stretch ) / max_crop;
609
gdouble min_skew = ( 1 - max_stretch ) * max_crop;
611
/* Obtain information about image being loaded */
612
if( ! gdk_pixbuf_get_file_info( filename, &i_width, &i_height ) )
615
/* How distorted images would be if we scaled them */
616
i_ratio = (gdouble)i_width / i_height;
617
skew = ratio / i_ratio;
619
/* Calculationg surface dimensions.
621
* In order to be as flexible as possible, this function can load images at
622
* various sizes, but at aspect ration that matches the aspect ratio of main
623
* preview area. How size is determined? If width argument is not -1, this
624
* is taken as a reference dimension from which height is calculated (if
625
* height argument also present, it's ignored). If width argument is -1,
626
* height is taken as a reference dimension. If both width and height are
627
* -1, surface dimensions are calculated to to fit original image.
631
/* Calculate height according to width */
632
height = width / ratio;
634
else if( height > 0 )
636
/* Calculate width from height */
637
width = height * ratio;
641
/* Load image at maximum quality
643
* If the user doesn't want to have distorted images, we create slightly
644
* bigger surface that will hold borders too.
646
* If images should be distorted, we first check if we're able to fit
647
* image without distorting it too much. If images would be largely
648
* distorted, we simply load them undistorted.
650
* If we came all the way to here, then we're able to distort image.
652
if( ( ! distort ) || /* Don't distort */
653
( skew > max_skew ) || /* Image is too wide */
654
( skew < min_skew ) ) /* Image is too tall */
656
/* User doesn't want images to be distorted or distortion would be
658
if( ratio < i_ratio )
660
/* Borders will be added on left and right */
662
height = width / ratio;
666
/* Borders will be added on top and bottom */
668
width = height * ratio;
673
/* User wants images to be distorted and we're able to do it
674
* without ruining images. */
675
if( ratio < i_ratio )
677
/* Image will be distorted horizontally */
679
width = height * ratio;
683
/* Image will be distorted vertically */
685
height = width / ratio;
690
/* Will image be disotrted?
693
* - user allows us to do it
694
* - skew is in sensible range
695
* - image is not smaller than exported wideo size
697
transform = distort && skew < max_skew && skew > min_skew &&
698
( i_width >= width || i_height >= height );
700
/* Load image into pixbuf at proper size */
705
/* Images will be loaded at slightly modified dimensions */
706
if( ratio < i_ratio )
708
/* Horizontal scaling */
709
lw = (gdouble)width / ( skew + 1 ) * 2;
714
/* Vertical scaling */
716
lh = (gdouble)height * ( skew + 1 ) / 2;
718
loader = gdk_pixbuf_new_from_file_at_scale( filename, lw, lh,
723
/* Simply load image into pixbuf at size */
724
loader = gdk_pixbuf_new_from_file_at_size( filename, width,
730
i_width = gdk_pixbuf_get_width( loader );
731
i_height = gdk_pixbuf_get_height( loader );
733
/* Calculate offsets */
734
offset_x = ( width - i_width ) / 2; /* CAN BE NEGATIVE!!! */
735
offset_y = ( height - i_height ) / 2; /* CAN BE NEGATIVE!!! */
739
* We can give two different output formats: cairo_surface_t and GdkPixbuf.
743
/* Create new pixbuf with loaded image */
744
GdkPixbuf *tmp_pix; /* Pixbuf used for loading */
745
guint32 tmp_color; /* Background color */
747
tmp_color = ( (gint)( color[0] * 0xff ) << 24 ) |
748
( (gint)( color[1] * 0xff ) << 16 ) |
749
( (gint)( color[2] * 0xff ) << 8 );
750
tmp_pix = gdk_pixbuf_new( GDK_COLORSPACE_RGB, FALSE, 8, width, height );
751
gdk_pixbuf_fill( tmp_pix, tmp_color );
752
gdk_pixbuf_composite( loader, tmp_pix,
753
MAX( 0, offset_x ), MAX( 0, offset_y ),
754
MIN( i_width, width ), MIN( i_height, height ),
755
offset_x, offset_y, 1, 1,
756
GDK_INTERP_BILINEAR, 255 );
762
/* Paint surface with loaded image
764
* If image cannot be scalled, transform is FALSE. In this case, just
765
* borders are added. If transform is not 0, than scale image before
767
cairo_t *cr; /* Cairo, used to transform image */
768
cairo_surface_t *tmp_surf; /* Surface to draw on */
770
/* Create image surface with proper dimensions */
771
tmp_surf = cairo_image_surface_create( CAIRO_FORMAT_RGB24,
774
cr = cairo_create( tmp_surf );
778
/* Fill with background color */
779
cairo_set_source_rgb( cr, color[0], color[1], color[2] );
784
gdk_cairo_set_source_pixbuf( cr, loader, offset_x, offset_y );
793
/* Free temporary pixbuf */
794
g_object_unref( G_OBJECT( loader ) );
800
img_set_project_mod_state( img_window_struct *img,
803
if( ( img->project_is_modified ? modified : ! modified ) )
806
img->project_is_modified = modified;
808
/* FIXME: Do any updates here (add "*" to window title, ...). */
812
img_sync_timings( slide_struct *slide,
813
img_window_struct *img )
815
/* If times are already synchronized, return */
816
if( slide->duration >= slide->anim_duration )
819
/* Do the right thing;) */
820
if( slide->no_points )
825
/* Calculate difference that we need to accomodate */
826
diff = slide->anim_duration - slide->duration;
828
/* Elongate last point */
829
point = (ImgStopPoint *)g_list_last( slide->points )->data;
832
/* Update Ken Burns display */
833
gtk_spin_button_set_value( GTK_SPIN_BUTTON( img->ken_duration ),
838
gtk_spin_button_set_value( GTK_SPIN_BUTTON( img->duration ),
839
slide->anim_duration );
842
void img_select_nth_slide(img_window_struct *img, gint slide_to_select)
846
gtk_icon_view_unselect_all(GTK_ICON_VIEW (img->active_icon));
847
path = gtk_tree_path_new_from_indices(slide_to_select, -1);
848
gtk_icon_view_set_cursor (GTK_ICON_VIEW (img->active_icon), path, NULL, FALSE);
849
gtk_icon_view_select_path (GTK_ICON_VIEW (img->active_icon), path);
850
gtk_icon_view_scroll_to_path (GTK_ICON_VIEW (img->active_icon), path, FALSE, 0, 0);
851
gtk_tree_path_free (path);
855
img_convert_surface_to_pixbuf( cairo_surface_t *surface )
858
gint w, h, ss, sp, row, col;
859
guchar *data_s, *data_p;
861
/* Information about surface */
862
w = cairo_image_surface_get_width( surface );
863
h = cairo_image_surface_get_height( surface );
864
ss = cairo_image_surface_get_stride( surface );
865
data_s = cairo_image_surface_get_data( surface );
867
/* Create new pixbuf according to upper specs */
868
pixbuf = gdk_pixbuf_new( GDK_COLORSPACE_RGB, FALSE, 8, w, h );
870
/* Get info about new pixbuf */
871
sp = gdk_pixbuf_get_rowstride( pixbuf );
872
data_p = gdk_pixbuf_get_pixels( pixbuf );
875
for( row = 0; row < h; row++ )
877
for( col = 0; col < w; col++ )
879
gint index_s, index_p;
881
index_s = row * ss + col * 4;
882
index_p = row * sp + col * 3;
884
data_p[index_p + 0] = data_s[index_s + 2];
885
data_p[index_p + 1] = data_s[index_s + 1];
886
data_p[index_p + 2] = data_s[index_s + 0];
894
img_scale_gradient( gint gradient,
902
cairo_surface_t **surface )
906
cairo_pattern_t *pat;
907
gdouble diffx, diffy, radius;
909
sf = cairo_image_surface_create( CAIRO_FORMAT_RGB24, width, height );
910
cr = cairo_create( sf );
914
case 0: /* Solid color */
915
cairo_set_source_rgb( cr, c_start[0], c_start[1], c_start[2] );
919
case 1: /* Linear gradient */
920
pat = cairo_pattern_create_linear( p_start[0] * width,
923
p_stop[1] * height );
924
cairo_pattern_add_color_stop_rgb( pat, 0, c_start[0],
925
c_start[1], c_start[2] );
926
cairo_pattern_add_color_stop_rgb( pat, 1, c_stop[0],
927
c_stop[1], c_stop[2] );
928
cairo_set_source( cr, pat );
930
cairo_pattern_destroy( pat );
933
case 2: /* Radial gradient */
934
diffx = ABS( p_start[0] - p_stop[0] ) * width;
935
diffy = ABS( p_start[1] - p_stop[1] ) * height;
936
radius = sqrt( pow( diffx, 2 ) + pow( diffy, 2 ) );
938
pat = cairo_pattern_create_radial( p_start[0] * width,
939
p_start[1] * height, 0,
941
p_start[1] * height, radius );
942
cairo_pattern_add_color_stop_rgb( pat, 0, c_start[0],
943
c_start[1], c_start[2] );
944
cairo_pattern_add_color_stop_rgb( pat, 1, c_stop[0],
945
c_stop[1], c_stop[2] );
946
cairo_set_source( cr, pat );
948
cairo_pattern_destroy( pat );
957
*pixbuf = img_convert_surface_to_pixbuf( sf );
958
cairo_surface_destroy( sf );