3 * Copyright (C) 2001-2009 Jim Evins <evins@snaught.com>.
5 * This file is part of gLabels.
7 * gLabels is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
12 * gLabels is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with gLabels. If not, see <http://www.gnu.org/licenses/>.
23 #include "label-image.h"
25 #include <glib/gi18n.h>
28 #include <librsvg/rsvg.h>
29 #include <librsvg/rsvg-cairo.h>
31 #include "pixbuf-util.h"
32 #include "file-util.h"
33 #include "pixmaps/checkerboard.xpm"
38 #define MIN_IMAGE_SIZE 1.0
41 /*========================================================*/
43 /*========================================================*/
52 struct _glLabelImagePrivate {
59 RsvgHandle *svg_handle;
63 /*========================================================*/
64 /* Private globals. */
65 /*========================================================*/
67 static GdkPixbuf *default_pixbuf = NULL;
70 /*========================================================*/
71 /* Private function prototypes. */
72 /*========================================================*/
74 static void gl_label_image_finalize (GObject *object);
76 static void copy (glLabelObject *dst_object,
77 glLabelObject *src_object);
79 static void set_size (glLabelObject *object,
84 static void draw_object (glLabelObject *object,
87 glMergeRecord *record);
89 static void draw_shadow (glLabelObject *object,
92 glMergeRecord *record);
94 static gboolean object_at (glLabelObject *object,
100 /*****************************************************************************/
101 /* Boilerplate object stuff. */
102 /*****************************************************************************/
103 G_DEFINE_TYPE (glLabelImage, gl_label_image, GL_TYPE_LABEL_OBJECT)
107 gl_label_image_class_init (glLabelImageClass *class)
109 GObjectClass *object_class = G_OBJECT_CLASS (class);
110 glLabelObjectClass *label_object_class = GL_LABEL_OBJECT_CLASS (class);
113 gl_label_image_parent_class = g_type_class_peek_parent (class);
115 label_object_class->copy = copy;
116 label_object_class->set_size = set_size;
117 label_object_class->draw_object = draw_object;
118 label_object_class->draw_shadow = draw_shadow;
119 label_object_class->object_at = object_at;
121 object_class->finalize = gl_label_image_finalize;
123 if ( default_pixbuf == NULL ) {
124 pixbuf = gdk_pixbuf_new_from_xpm_data ((const char **)checkerboard_xpm);
126 gdk_pixbuf_scale_simple (pixbuf, 128, 128, GDK_INTERP_NEAREST);
127 g_object_unref (pixbuf);
133 gl_label_image_init (glLabelImage *this)
135 this->priv = g_new0 (glLabelImagePrivate, 1);
137 this->priv->filename = g_new0 (glTextNode, 1);
139 this->priv->type = FILE_TYPE_NONE;
140 this->priv->pixbuf = NULL;
141 this->priv->svg_handle = NULL;
146 gl_label_image_finalize (GObject *object)
148 glLabelObject *lobject = GL_LABEL_OBJECT (object);
149 glLabelImage *this = GL_LABEL_IMAGE (object);
153 g_return_if_fail (object && GL_IS_LABEL_IMAGE (object));
155 if (!this->priv->filename->field_flag) {
157 label = gl_label_object_get_parent (lobject);
159 switch ( this->priv->type )
162 case FILE_TYPE_PIXBUF:
163 cache = gl_label_get_pixbuf_cache (label);
164 gl_pixbuf_cache_remove_pixbuf (cache, this->priv->filename->data);
168 cache = gl_label_get_svg_cache (label);
169 gl_svg_cache_remove_svg (cache, this->priv->filename->data);
178 gl_text_node_free (&this->priv->filename);
181 G_OBJECT_CLASS (gl_label_image_parent_class)->finalize (object);
185 /*****************************************************************************/
186 /* NEW label "image" object. */
187 /*****************************************************************************/
189 gl_label_image_new (glLabel *label,
194 this = g_object_new (gl_label_image_get_type(), NULL);
200 gl_label_checkpoint (label, _("Create image object"));
203 gl_label_add_object (label, GL_LABEL_OBJECT (this));
204 gl_label_object_set_parent (GL_LABEL_OBJECT (this), label);
207 return G_OBJECT (this);
211 /*****************************************************************************/
212 /* Copy object contents. */
213 /*****************************************************************************/
215 copy (glLabelObject *dst_object,
216 glLabelObject *src_object)
218 glLabelImage *src_limage = (glLabelImage *)src_object;
219 glLabelImage *new_limage = (glLabelImage *)dst_object;
220 glTextNode *filename;
223 glLabel *src_label, *dst_label;
226 gl_debug (DEBUG_LABEL, "START");
228 g_return_if_fail (src_limage && GL_IS_LABEL_IMAGE (src_limage));
229 g_return_if_fail (new_limage && GL_IS_LABEL_IMAGE (new_limage));
231 filename = gl_label_image_get_filename (src_limage);
233 /* Make sure destination label has data suitably cached. */
234 if ( !filename->field_flag && (src_limage->priv->type != FILE_TYPE_NONE) )
236 src_label = gl_label_object_get_parent (src_object);
237 dst_label = gl_label_object_get_parent (dst_object);
239 switch ( src_limage->priv->type )
242 case FILE_TYPE_PIXBUF:
243 pixbuf = src_limage->priv->pixbuf;
244 if ( pixbuf != NULL ) {
245 cache = gl_label_get_pixbuf_cache (dst_label);
246 gl_pixbuf_cache_add_pixbuf (cache, filename->data, pixbuf);
251 cache = gl_label_get_svg_cache (src_label);
252 contents = gl_svg_cache_get_contents (cache, filename->data);
253 if ( contents != NULL ) {
254 cache = gl_label_get_svg_cache (dst_label);
255 gl_svg_cache_add_svg (cache, filename->data, contents);
266 gl_label_image_set_filename (new_limage, filename, FALSE);
267 gl_text_node_free (&filename);
269 gl_debug (DEBUG_LABEL, "END");
273 /*---------------------------------------------------------------------------*/
274 /* PRIVATE. Set size method. */
275 /*---------------------------------------------------------------------------*/
277 set_size (glLabelObject *object,
282 g_return_if_fail (object && GL_IS_LABEL_OBJECT (object));
284 if (w < MIN_IMAGE_SIZE)
289 if (h < MIN_IMAGE_SIZE)
294 GL_LABEL_OBJECT_CLASS (gl_label_image_parent_class)->set_size (object, w, h, checkpoint);
298 /*****************************************************************************/
299 /* Set object params. */
300 /*****************************************************************************/
302 gl_label_image_set_filename (glLabelImage *this,
303 glTextNode *filename,
306 glTextNode *old_filename;
308 GHashTable *pixbuf_cache;
309 GHashTable *svg_cache;
311 RsvgHandle *svg_handle;
312 RsvgDimensionData svg_dim;
313 gdouble image_w, image_h, aspect_ratio, w, h;
315 gl_debug (DEBUG_LABEL, "START");
317 g_return_if_fail (this && GL_IS_LABEL_IMAGE (this));
318 g_return_if_fail (filename != NULL);
320 old_filename = this->priv->filename;
322 /* If Unchanged don't do anything */
323 if ( gl_text_node_equal (filename, old_filename ) ) {
327 label = gl_label_object_get_parent (GL_LABEL_OBJECT (this));
328 pixbuf_cache = gl_label_get_pixbuf_cache (label);
329 svg_cache = gl_label_get_svg_cache (label);
333 gl_label_checkpoint (label, _("Set image"));
336 /* Set new filename. */
337 this->priv->filename = gl_text_node_dup(filename);
339 /* Remove reference to previous item. */
340 switch (this->priv->type)
343 case FILE_TYPE_PIXBUF:
344 if ( !old_filename->field_flag && (old_filename->data != NULL) ) {
345 gl_pixbuf_cache_remove_pixbuf (pixbuf_cache, old_filename->data);
350 if ( !old_filename->field_flag && (old_filename->data != NULL) ) {
351 gl_svg_cache_remove_svg (svg_cache, old_filename->data);
360 gl_text_node_free (&old_filename);
363 /* Now set the new file type and the pixbuf or svg_handle. */
364 if ( !filename->field_flag && (filename->data != NULL) )
367 if ( gl_file_util_is_extension (filename->data, ".svg") )
369 svg_handle = gl_svg_cache_get_handle (svg_cache, filename->data);
371 if (svg_handle != NULL)
373 this->priv->type = FILE_TYPE_SVG;
374 this->priv->pixbuf = NULL;
375 this->priv->svg_handle = svg_handle;
379 this->priv->type = FILE_TYPE_NONE;
380 this->priv->pixbuf = NULL;
381 this->priv->svg_handle = NULL;
388 pixbuf = gl_pixbuf_cache_get_pixbuf (pixbuf_cache, filename->data);
392 this->priv->type = FILE_TYPE_PIXBUF;
393 this->priv->pixbuf = pixbuf;
394 this->priv->svg_handle = NULL;
398 this->priv->type = FILE_TYPE_NONE;
399 this->priv->pixbuf = NULL;
400 this->priv->svg_handle = NULL;
407 this->priv->type = FILE_TYPE_NONE;
408 this->priv->pixbuf = NULL;
409 this->priv->svg_handle = NULL;
413 /* Treat current size as a bounding box, scale image to maintain aspect
414 * ratio while fitting it in this bounding box. */
415 switch (this->priv->type)
418 case FILE_TYPE_PIXBUF:
419 image_w = gdk_pixbuf_get_width (this->priv->pixbuf);
420 image_h = gdk_pixbuf_get_height (this->priv->pixbuf);
424 rsvg_handle_get_dimensions (this->priv->svg_handle, &svg_dim);
425 image_w = svg_dim.width;
426 image_h = svg_dim.height;
430 image_w = gdk_pixbuf_get_width (default_pixbuf);
431 image_h = gdk_pixbuf_get_height (default_pixbuf);
435 aspect_ratio = image_h / image_w;
436 gl_label_object_get_size (GL_LABEL_OBJECT(this), &w, &h);
437 if ( h > w*aspect_ratio ) {
438 h = w * aspect_ratio;
440 w = h / aspect_ratio;
442 gl_label_object_set_size (GL_LABEL_OBJECT(this), w, h, FALSE);
444 gl_label_object_emit_changed (GL_LABEL_OBJECT(this));
446 gl_debug (DEBUG_LABEL, "END");
451 gl_label_image_set_pixbuf (glLabelImage *this,
455 glTextNode *old_filename;
457 GHashTable *pixbuf_cache;
458 GHashTable *svg_cache;
461 gdouble image_w, image_h;
463 gl_debug (DEBUG_LABEL, "START");
465 g_return_if_fail (this && GL_IS_LABEL_IMAGE (this));
466 g_return_if_fail (pixbuf && GDK_IS_PIXBUF (pixbuf));
468 old_filename = this->priv->filename;
470 label = gl_label_object_get_parent (GL_LABEL_OBJECT (this));
474 gl_label_checkpoint (label, _("Set image"));
477 pixbuf_cache = gl_label_get_pixbuf_cache (label);
478 svg_cache = gl_label_get_svg_cache (label);
480 /* Remove reference to previous item. */
481 switch (this->priv->type)
484 case FILE_TYPE_PIXBUF:
485 if ( !old_filename->field_flag && (old_filename->data != NULL) ) {
486 gl_pixbuf_cache_remove_pixbuf (pixbuf_cache, old_filename->data);
491 if ( !old_filename->field_flag && (old_filename->data != NULL) ) {
492 gl_svg_cache_remove_svg (svg_cache, old_filename->data);
501 /* Set new filename. */
502 cs = g_compute_checksum_for_data (G_CHECKSUM_MD5,
503 gdk_pixbuf_get_pixels (pixbuf),
504 gdk_pixbuf_get_rowstride (pixbuf)*gdk_pixbuf_get_height (pixbuf));
505 name = g_strdup_printf ("%s.bitmap", cs);
506 this->priv->filename = gl_text_node_new_from_text(name);
507 gl_text_node_free (&old_filename);
509 this->priv->pixbuf = g_object_ref (pixbuf);
510 gl_pixbuf_cache_add_pixbuf (pixbuf_cache, name, pixbuf);
515 this->priv->type = FILE_TYPE_PIXBUF;
516 this->priv->svg_handle = NULL;
518 image_w = gdk_pixbuf_get_width (this->priv->pixbuf);
519 image_h = gdk_pixbuf_get_height (this->priv->pixbuf);
520 gl_label_object_set_size (GL_LABEL_OBJECT(this), image_w, image_h, FALSE);
522 gl_label_object_emit_changed (GL_LABEL_OBJECT(this));
524 gl_debug (DEBUG_LABEL, "END");
528 /*****************************************************************************/
529 /* Get object params. */
530 /*****************************************************************************/
532 gl_label_image_get_pixbuf (glLabelImage *this,
533 glMergeRecord *record)
535 g_return_val_if_fail (this && GL_IS_LABEL_IMAGE (this), NULL);
537 if ((record != NULL) && this->priv->filename->field_flag)
540 GdkPixbuf *pixbuf = NULL;
541 gchar *real_filename;
543 /* Indirect filename, re-evaluate for given record. */
545 real_filename = gl_merge_eval_key (record,
546 this->priv->filename->data);
548 if (real_filename != NULL)
550 pixbuf = gdk_pixbuf_new_from_file (real_filename, NULL);
555 if ( this->priv->type == FILE_TYPE_PIXBUF )
557 return g_object_ref (this->priv->pixbuf);
567 gl_label_image_get_svg_handle (glLabelImage *this,
568 glMergeRecord *record)
570 g_return_val_if_fail (this && GL_IS_LABEL_IMAGE (this), NULL);
572 if ((record != NULL) && this->priv->filename->field_flag)
575 RsvgHandle *svg_handle = NULL;
576 gchar *real_filename;
578 /* Indirect filename, re-evaluate for given record. */
580 real_filename = gl_merge_eval_key (record,
581 this->priv->filename->data);
583 if (real_filename != NULL)
585 if ( gl_file_util_is_extension (real_filename, ".svg") )
587 svg_handle = rsvg_handle_new_from_file (real_filename, NULL);
593 if ( this->priv->type == FILE_TYPE_SVG )
595 return g_object_ref (this->priv->svg_handle);
605 get_type (glLabelImage *this,
606 glMergeRecord *record)
608 g_return_val_if_fail (this && GL_IS_LABEL_IMAGE (this), FALSE);
610 if ((record != NULL) && this->priv->filename->field_flag)
612 gchar *real_filename;
614 real_filename = gl_merge_eval_key (record,
615 this->priv->filename->data);
617 if ( gl_file_util_is_extension (real_filename, ".svg") )
619 return FILE_TYPE_SVG;
623 /* Assume a pixbuf compat file. If not, queries for
624 pixbufs should return NULL and do the right thing. */
625 return FILE_TYPE_PIXBUF;
630 return (this->priv->type);
636 gl_label_image_get_filename (glLabelImage *this)
638 g_return_val_if_fail (this && GL_IS_LABEL_IMAGE (this), NULL);
640 return gl_text_node_dup (this->priv->filename);
645 gl_label_image_get_base_size (glLabelImage *this,
649 RsvgDimensionData svg_dim;
651 g_return_if_fail (this && GL_IS_LABEL_IMAGE (this));
652 g_return_if_fail (w != NULL);
653 g_return_if_fail (h != NULL);
655 switch (this->priv->type)
658 case FILE_TYPE_PIXBUF:
659 *w = gdk_pixbuf_get_width (this->priv->pixbuf);
660 *h = gdk_pixbuf_get_height (this->priv->pixbuf);
664 rsvg_handle_get_dimensions (this->priv->svg_handle, &svg_dim);
670 *w = gdk_pixbuf_get_width (default_pixbuf);
671 *h = gdk_pixbuf_get_height (default_pixbuf);
678 /*****************************************************************************/
679 /* Draw object method. */
680 /*****************************************************************************/
682 draw_object (glLabelObject *object,
684 gboolean screen_flag,
685 glMergeRecord *record)
687 glLabelImage *this = GL_LABEL_IMAGE (object);
689 gdouble image_w, image_h;
691 RsvgHandle *svg_handle;
692 RsvgDimensionData svg_dim;
694 gl_debug (DEBUG_LABEL, "START");
696 gl_label_object_get_size (object, &w, &h);
700 switch (get_type (this, record))
703 case FILE_TYPE_PIXBUF:
704 pixbuf = gl_label_image_get_pixbuf (this, record);
707 image_w = gdk_pixbuf_get_width (pixbuf);
708 image_h = gdk_pixbuf_get_height (pixbuf);
709 cairo_rectangle (cr, 0.0, 0.0, w, h);
710 cairo_scale (cr, w/image_w, h/image_h);
711 gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
713 g_object_unref (pixbuf);
718 svg_handle = gl_label_image_get_svg_handle (this, record);
721 rsvg_handle_get_dimensions (svg_handle, &svg_dim);
722 cairo_scale (cr, w/svg_dim.width, h/svg_dim.height);
723 rsvg_handle_render_cairo (svg_handle, cr);
728 cairo_rectangle (cr, 0.0, 0.0, w, h);
729 image_w = gdk_pixbuf_get_width (default_pixbuf);
730 image_h = gdk_pixbuf_get_height (default_pixbuf);
731 cairo_scale (cr, w/image_w, h/image_h);
732 gdk_cairo_set_source_pixbuf (cr, default_pixbuf, 0, 0);
740 gl_debug (DEBUG_LABEL, "END");
744 /*****************************************************************************/
745 /* Draw shadow method. */
746 /*****************************************************************************/
748 draw_shadow (glLabelObject *object,
750 gboolean screen_flag,
751 glMergeRecord *record)
753 glLabelImage *this = GL_LABEL_IMAGE (object);
756 GdkPixbuf *shadow_pixbuf;
757 gdouble image_w, image_h;
758 glColorNode *shadow_color_node;
760 gdouble shadow_opacity;
762 gl_debug (DEBUG_LABEL, "START");
764 gl_label_object_get_size (object, &w, &h);
766 shadow_color_node = gl_label_object_get_shadow_color (object);
767 shadow_color = gl_color_node_expand (shadow_color_node, record);
768 if (shadow_color_node->field_flag && screen_flag)
770 shadow_color = GL_COLOR_SHADOW_MERGE_DEFAULT;
772 shadow_opacity = gl_label_object_get_shadow_opacity (object);
776 switch (get_type (this, record))
779 case FILE_TYPE_PIXBUF:
780 pixbuf = gl_label_image_get_pixbuf (this, record);
783 image_w = gdk_pixbuf_get_width (pixbuf);
784 image_h = gdk_pixbuf_get_height (pixbuf);
786 shadow_pixbuf = gl_pixbuf_util_create_shadow_pixbuf (pixbuf,
787 shadow_color, shadow_opacity);
788 cairo_rectangle (cr, 0.0, 0.0, w, h);
789 cairo_scale (cr, w/image_w, h/image_h);
790 gdk_cairo_set_source_pixbuf (cr, (GdkPixbuf *)shadow_pixbuf, 0, 0);
793 g_object_unref (G_OBJECT (shadow_pixbuf));
794 g_object_unref (G_OBJECT (pixbuf));
799 /* FIXME: no shadow support, yet. */
803 shadow_color = gl_color_set_opacity (shadow_color, shadow_opacity);
805 cairo_rectangle (cr, 0.0, 0.0, w, h);
806 cairo_set_source_rgba (cr, GL_COLOR_RGBA_ARGS (shadow_color));
813 gl_debug (DEBUG_LABEL, "END");
817 /*****************************************************************************/
818 /* Is object at coordinates? */
819 /*****************************************************************************/
821 object_at (glLabelObject *object,
828 gl_label_object_get_size (object, &w, &h);
831 cairo_rectangle (cr, 0.0, 0.0, w, h);
833 if (cairo_in_fill (cr, x, y))
845 * Local Variables: -- emacs
847 * c-basic-offset: 8 -- emacs
848 * tab-width: 8 -- emacs
849 * indent-tabs-mode: nil -- emacs