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>
30 #include "pixbuf-util.h"
31 #include "file-util.h"
32 #include "pixmaps/checkerboard.xpm"
37 #define MIN_IMAGE_SIZE 1.0
40 /*========================================================*/
42 /*========================================================*/
51 struct _glLabelImagePrivate {
58 RsvgHandle *svg_handle;
62 /*========================================================*/
63 /* Private globals. */
64 /*========================================================*/
66 static GdkPixbuf *default_pixbuf = NULL;
69 /*========================================================*/
70 /* Private function prototypes. */
71 /*========================================================*/
73 static void gl_label_image_finalize (GObject *object);
75 static void copy (glLabelObject *dst_object,
76 glLabelObject *src_object);
78 static void set_size (glLabelObject *object,
83 static void draw_object (glLabelObject *object,
86 glMergeRecord *record);
88 static void draw_shadow (glLabelObject *object,
91 glMergeRecord *record);
93 static gboolean object_at (glLabelObject *object,
99 /*****************************************************************************/
100 /* Boilerplate object stuff. */
101 /*****************************************************************************/
102 G_DEFINE_TYPE (glLabelImage, gl_label_image, GL_TYPE_LABEL_OBJECT)
106 gl_label_image_class_init (glLabelImageClass *class)
108 GObjectClass *object_class = G_OBJECT_CLASS (class);
109 glLabelObjectClass *label_object_class = GL_LABEL_OBJECT_CLASS (class);
112 gl_label_image_parent_class = g_type_class_peek_parent (class);
114 label_object_class->copy = copy;
115 label_object_class->set_size = set_size;
116 label_object_class->draw_object = draw_object;
117 label_object_class->draw_shadow = draw_shadow;
118 label_object_class->object_at = object_at;
120 object_class->finalize = gl_label_image_finalize;
122 if ( default_pixbuf == NULL ) {
123 pixbuf = gdk_pixbuf_new_from_xpm_data ((const char **)checkerboard_xpm);
125 gdk_pixbuf_scale_simple (pixbuf, 128, 128, GDK_INTERP_NEAREST);
126 g_object_unref (pixbuf);
132 gl_label_image_init (glLabelImage *this)
134 this->priv = g_new0 (glLabelImagePrivate, 1);
136 this->priv->filename = g_new0 (glTextNode, 1);
138 this->priv->type = FILE_TYPE_NONE;
139 this->priv->pixbuf = NULL;
140 this->priv->svg_handle = NULL;
145 gl_label_image_finalize (GObject *object)
147 glLabelObject *lobject = GL_LABEL_OBJECT (object);
148 glLabelImage *this = GL_LABEL_IMAGE (object);
152 g_return_if_fail (object && GL_IS_LABEL_IMAGE (object));
154 if (!this->priv->filename->field_flag) {
156 label = gl_label_object_get_parent (lobject);
158 switch ( this->priv->type )
161 case FILE_TYPE_PIXBUF:
162 cache = gl_label_get_pixbuf_cache (label);
163 gl_pixbuf_cache_remove_pixbuf (cache, this->priv->filename->data);
167 cache = gl_label_get_svg_cache (label);
168 gl_svg_cache_remove_svg (cache, this->priv->filename->data);
177 gl_text_node_free (&this->priv->filename);
180 G_OBJECT_CLASS (gl_label_image_parent_class)->finalize (object);
184 /*****************************************************************************/
185 /* NEW label "image" object. */
186 /*****************************************************************************/
188 gl_label_image_new (glLabel *label,
193 this = g_object_new (gl_label_image_get_type(), NULL);
199 gl_label_checkpoint (label, _("Create image object"));
202 gl_label_add_object (label, GL_LABEL_OBJECT (this));
203 gl_label_object_set_parent (GL_LABEL_OBJECT (this), label);
206 return G_OBJECT (this);
210 /*****************************************************************************/
211 /* Copy object contents. */
212 /*****************************************************************************/
214 copy (glLabelObject *dst_object,
215 glLabelObject *src_object)
217 glLabelImage *src_limage = (glLabelImage *)src_object;
218 glLabelImage *new_limage = (glLabelImage *)dst_object;
219 glTextNode *filename;
222 glLabel *src_label, *dst_label;
225 gl_debug (DEBUG_LABEL, "START");
227 g_return_if_fail (src_limage && GL_IS_LABEL_IMAGE (src_limage));
228 g_return_if_fail (new_limage && GL_IS_LABEL_IMAGE (new_limage));
230 filename = gl_label_image_get_filename (src_limage);
232 /* Make sure destination label has data suitably cached. */
233 if ( !filename->field_flag && (src_limage->priv->type != FILE_TYPE_NONE) )
235 src_label = gl_label_object_get_parent (src_object);
236 dst_label = gl_label_object_get_parent (dst_object);
238 switch ( src_limage->priv->type )
241 case FILE_TYPE_PIXBUF:
242 pixbuf = src_limage->priv->pixbuf;
243 if ( pixbuf != NULL ) {
244 cache = gl_label_get_pixbuf_cache (dst_label);
245 gl_pixbuf_cache_add_pixbuf (cache, filename->data, pixbuf);
250 cache = gl_label_get_svg_cache (src_label);
251 contents = gl_svg_cache_get_contents (cache, filename->data);
252 if ( contents != NULL ) {
253 cache = gl_label_get_svg_cache (dst_label);
254 gl_svg_cache_add_svg (cache, filename->data, contents);
265 gl_label_image_set_filename (new_limage, filename, FALSE);
266 gl_text_node_free (&filename);
268 gl_debug (DEBUG_LABEL, "END");
272 /*---------------------------------------------------------------------------*/
273 /* PRIVATE. Set size method. */
274 /*---------------------------------------------------------------------------*/
276 set_size (glLabelObject *object,
281 g_return_if_fail (object && GL_IS_LABEL_OBJECT (object));
283 if (w < MIN_IMAGE_SIZE)
288 if (h < MIN_IMAGE_SIZE)
293 GL_LABEL_OBJECT_CLASS (gl_label_image_parent_class)->set_size (object, w, h, checkpoint);
297 /*****************************************************************************/
298 /* Set object params. */
299 /*****************************************************************************/
301 gl_label_image_set_filename (glLabelImage *this,
302 glTextNode *filename,
305 glTextNode *old_filename;
307 GHashTable *pixbuf_cache;
308 GHashTable *svg_cache;
310 RsvgHandle *svg_handle;
311 RsvgDimensionData svg_dim;
312 gdouble image_w, image_h, aspect_ratio, w, h;
314 gl_debug (DEBUG_LABEL, "START");
316 g_return_if_fail (this && GL_IS_LABEL_IMAGE (this));
317 g_return_if_fail (filename != NULL);
319 old_filename = this->priv->filename;
321 /* If Unchanged don't do anything */
322 if ( gl_text_node_equal (filename, old_filename ) ) {
326 label = gl_label_object_get_parent (GL_LABEL_OBJECT (this));
327 pixbuf_cache = gl_label_get_pixbuf_cache (label);
328 svg_cache = gl_label_get_svg_cache (label);
332 gl_label_checkpoint (label, _("Set image"));
335 /* Set new filename. */
336 this->priv->filename = gl_text_node_dup(filename);
338 /* Remove reference to previous item. */
339 switch (this->priv->type)
342 case FILE_TYPE_PIXBUF:
343 if ( !old_filename->field_flag && (old_filename->data != NULL) ) {
344 gl_pixbuf_cache_remove_pixbuf (pixbuf_cache, old_filename->data);
349 if ( !old_filename->field_flag && (old_filename->data != NULL) ) {
350 gl_svg_cache_remove_svg (svg_cache, old_filename->data);
359 gl_text_node_free (&old_filename);
362 /* Now set the new file type and the pixbuf or svg_handle. */
363 if ( !filename->field_flag && (filename->data != NULL) )
366 if ( gl_file_util_is_extension (filename->data, ".svg") )
368 svg_handle = gl_svg_cache_get_handle (svg_cache, filename->data);
370 if (svg_handle != NULL)
372 this->priv->type = FILE_TYPE_SVG;
373 this->priv->pixbuf = NULL;
374 this->priv->svg_handle = svg_handle;
378 this->priv->type = FILE_TYPE_NONE;
379 this->priv->pixbuf = NULL;
380 this->priv->svg_handle = NULL;
387 pixbuf = gl_pixbuf_cache_get_pixbuf (pixbuf_cache, filename->data);
391 this->priv->type = FILE_TYPE_PIXBUF;
392 this->priv->pixbuf = pixbuf;
393 this->priv->svg_handle = NULL;
397 this->priv->type = FILE_TYPE_NONE;
398 this->priv->pixbuf = NULL;
399 this->priv->svg_handle = NULL;
406 this->priv->type = FILE_TYPE_NONE;
407 this->priv->pixbuf = NULL;
408 this->priv->svg_handle = NULL;
412 /* Treat current size as a bounding box, scale image to maintain aspect
413 * ratio while fitting it in this bounding box. */
414 switch (this->priv->type)
417 case FILE_TYPE_PIXBUF:
418 image_w = gdk_pixbuf_get_width (this->priv->pixbuf);
419 image_h = gdk_pixbuf_get_height (this->priv->pixbuf);
423 rsvg_handle_get_dimensions (this->priv->svg_handle, &svg_dim);
424 image_w = svg_dim.width;
425 image_h = svg_dim.height;
429 image_w = gdk_pixbuf_get_width (default_pixbuf);
430 image_h = gdk_pixbuf_get_height (default_pixbuf);
434 aspect_ratio = image_h / image_w;
435 gl_label_object_get_size (GL_LABEL_OBJECT(this), &w, &h);
436 if ( h > w*aspect_ratio ) {
437 h = w * aspect_ratio;
439 w = h / aspect_ratio;
441 gl_label_object_set_size (GL_LABEL_OBJECT(this), w, h, FALSE);
443 gl_label_object_emit_changed (GL_LABEL_OBJECT(this));
445 gl_debug (DEBUG_LABEL, "END");
450 gl_label_image_set_pixbuf (glLabelImage *this,
454 glTextNode *old_filename;
456 GHashTable *pixbuf_cache;
457 GHashTable *svg_cache;
460 gdouble image_w, image_h;
462 gl_debug (DEBUG_LABEL, "START");
464 g_return_if_fail (this && GL_IS_LABEL_IMAGE (this));
465 g_return_if_fail (pixbuf && GDK_IS_PIXBUF (pixbuf));
467 old_filename = this->priv->filename;
469 label = gl_label_object_get_parent (GL_LABEL_OBJECT (this));
473 gl_label_checkpoint (label, _("Set image"));
476 pixbuf_cache = gl_label_get_pixbuf_cache (label);
477 svg_cache = gl_label_get_svg_cache (label);
479 /* Remove reference to previous item. */
480 switch (this->priv->type)
483 case FILE_TYPE_PIXBUF:
484 if ( !old_filename->field_flag && (old_filename->data != NULL) ) {
485 gl_pixbuf_cache_remove_pixbuf (pixbuf_cache, old_filename->data);
490 if ( !old_filename->field_flag && (old_filename->data != NULL) ) {
491 gl_svg_cache_remove_svg (svg_cache, old_filename->data);
500 /* Set new filename. */
501 cs = g_compute_checksum_for_data (G_CHECKSUM_MD5,
502 gdk_pixbuf_get_pixels (pixbuf),
503 gdk_pixbuf_get_rowstride (pixbuf)*gdk_pixbuf_get_height (pixbuf));
504 name = g_strdup_printf ("%s.bitmap", cs);
505 this->priv->filename = gl_text_node_new_from_text(name);
506 gl_text_node_free (&old_filename);
508 this->priv->pixbuf = g_object_ref (pixbuf);
509 gl_pixbuf_cache_add_pixbuf (pixbuf_cache, name, pixbuf);
514 this->priv->type = FILE_TYPE_PIXBUF;
515 this->priv->svg_handle = NULL;
517 image_w = gdk_pixbuf_get_width (this->priv->pixbuf);
518 image_h = gdk_pixbuf_get_height (this->priv->pixbuf);
519 gl_label_object_set_size (GL_LABEL_OBJECT(this), image_w, image_h, FALSE);
521 gl_label_object_emit_changed (GL_LABEL_OBJECT(this));
523 gl_debug (DEBUG_LABEL, "END");
527 /*****************************************************************************/
528 /* Get object params. */
529 /*****************************************************************************/
531 gl_label_image_get_pixbuf (glLabelImage *this,
532 glMergeRecord *record)
534 g_return_val_if_fail (this && GL_IS_LABEL_IMAGE (this), NULL);
536 if ((record != NULL) && this->priv->filename->field_flag)
539 GdkPixbuf *pixbuf = NULL;
540 gchar *real_filename;
542 /* Indirect filename, re-evaluate for given record. */
544 real_filename = gl_merge_eval_key (record,
545 this->priv->filename->data);
547 if (real_filename != NULL)
549 pixbuf = gdk_pixbuf_new_from_file (real_filename, NULL);
554 if ( this->priv->type == FILE_TYPE_PIXBUF )
556 return g_object_ref (this->priv->pixbuf);
566 gl_label_image_get_svg_handle (glLabelImage *this,
567 glMergeRecord *record)
569 g_return_val_if_fail (this && GL_IS_LABEL_IMAGE (this), NULL);
571 if ((record != NULL) && this->priv->filename->field_flag)
574 RsvgHandle *svg_handle = NULL;
575 gchar *real_filename;
577 /* Indirect filename, re-evaluate for given record. */
579 real_filename = gl_merge_eval_key (record,
580 this->priv->filename->data);
582 if (real_filename != NULL)
584 if ( gl_file_util_is_extension (real_filename, ".svg") )
586 svg_handle = rsvg_handle_new_from_file (real_filename, NULL);
592 if ( this->priv->type == FILE_TYPE_SVG )
594 return g_object_ref (this->priv->svg_handle);
604 get_type (glLabelImage *this,
605 glMergeRecord *record)
607 g_return_val_if_fail (this && GL_IS_LABEL_IMAGE (this), FALSE);
609 if ((record != NULL) && this->priv->filename->field_flag)
611 gchar *real_filename;
613 real_filename = gl_merge_eval_key (record,
614 this->priv->filename->data);
616 if ( gl_file_util_is_extension (real_filename, ".svg") )
618 return FILE_TYPE_SVG;
622 /* Assume a pixbuf compat file. If not, queries for
623 pixbufs should return NULL and do the right thing. */
624 return FILE_TYPE_PIXBUF;
629 return (this->priv->type);
635 gl_label_image_get_filename (glLabelImage *this)
637 g_return_val_if_fail (this && GL_IS_LABEL_IMAGE (this), NULL);
639 return gl_text_node_dup (this->priv->filename);
644 gl_label_image_get_base_size (glLabelImage *this,
648 RsvgDimensionData svg_dim;
650 g_return_if_fail (this && GL_IS_LABEL_IMAGE (this));
651 g_return_if_fail (w != NULL);
652 g_return_if_fail (h != NULL);
654 switch (this->priv->type)
657 case FILE_TYPE_PIXBUF:
658 *w = gdk_pixbuf_get_width (this->priv->pixbuf);
659 *h = gdk_pixbuf_get_height (this->priv->pixbuf);
663 rsvg_handle_get_dimensions (this->priv->svg_handle, &svg_dim);
669 *w = gdk_pixbuf_get_width (default_pixbuf);
670 *h = gdk_pixbuf_get_height (default_pixbuf);
677 /*****************************************************************************/
678 /* Draw object method. */
679 /*****************************************************************************/
681 draw_object (glLabelObject *object,
683 gboolean screen_flag,
684 glMergeRecord *record)
686 glLabelImage *this = GL_LABEL_IMAGE (object);
688 gdouble image_w, image_h;
690 RsvgHandle *svg_handle;
691 RsvgDimensionData svg_dim;
693 gl_debug (DEBUG_LABEL, "START");
695 gl_label_object_get_size (object, &w, &h);
699 switch (get_type (this, record))
702 case FILE_TYPE_PIXBUF:
703 pixbuf = gl_label_image_get_pixbuf (this, record);
706 image_w = gdk_pixbuf_get_width (pixbuf);
707 image_h = gdk_pixbuf_get_height (pixbuf);
708 cairo_rectangle (cr, 0.0, 0.0, w, h);
709 cairo_scale (cr, w/image_w, h/image_h);
710 gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
712 g_object_unref (pixbuf);
717 svg_handle = gl_label_image_get_svg_handle (this, record);
720 rsvg_handle_get_dimensions (svg_handle, &svg_dim);
721 cairo_scale (cr, w/svg_dim.width, h/svg_dim.height);
722 rsvg_handle_render_cairo (svg_handle, cr);
727 cairo_rectangle (cr, 0.0, 0.0, w, h);
728 image_w = gdk_pixbuf_get_width (default_pixbuf);
729 image_h = gdk_pixbuf_get_height (default_pixbuf);
730 cairo_scale (cr, w/image_w, h/image_h);
731 gdk_cairo_set_source_pixbuf (cr, default_pixbuf, 0, 0);
739 gl_debug (DEBUG_LABEL, "END");
743 /*****************************************************************************/
744 /* Draw shadow method. */
745 /*****************************************************************************/
747 draw_shadow (glLabelObject *object,
749 gboolean screen_flag,
750 glMergeRecord *record)
752 glLabelImage *this = GL_LABEL_IMAGE (object);
755 GdkPixbuf *shadow_pixbuf;
756 gdouble image_w, image_h;
757 glColorNode *shadow_color_node;
759 gdouble shadow_opacity;
761 gl_debug (DEBUG_LABEL, "START");
763 gl_label_object_get_size (object, &w, &h);
765 shadow_color_node = gl_label_object_get_shadow_color (object);
766 shadow_color = gl_color_node_expand (shadow_color_node, record);
767 if (shadow_color_node->field_flag && screen_flag)
769 shadow_color = GL_COLOR_SHADOW_MERGE_DEFAULT;
771 shadow_opacity = gl_label_object_get_shadow_opacity (object);
775 switch (get_type (this, record))
778 case FILE_TYPE_PIXBUF:
779 pixbuf = gl_label_image_get_pixbuf (this, record);
782 image_w = gdk_pixbuf_get_width (pixbuf);
783 image_h = gdk_pixbuf_get_height (pixbuf);
785 shadow_pixbuf = gl_pixbuf_util_create_shadow_pixbuf (pixbuf,
786 shadow_color, shadow_opacity);
787 cairo_rectangle (cr, 0.0, 0.0, w, h);
788 cairo_scale (cr, w/image_w, h/image_h);
789 gdk_cairo_set_source_pixbuf (cr, (GdkPixbuf *)shadow_pixbuf, 0, 0);
792 g_object_unref (G_OBJECT (shadow_pixbuf));
793 g_object_unref (G_OBJECT (pixbuf));
798 /* FIXME: no shadow support, yet. */
802 shadow_color = gl_color_set_opacity (shadow_color, shadow_opacity);
804 cairo_rectangle (cr, 0.0, 0.0, w, h);
805 cairo_set_source_rgba (cr, GL_COLOR_RGBA_ARGS (shadow_color));
812 gl_debug (DEBUG_LABEL, "END");
816 /*****************************************************************************/
817 /* Is object at coordinates? */
818 /*****************************************************************************/
820 object_at (glLabelObject *object,
827 gl_label_object_get_size (object, &w, &h);
830 cairo_rectangle (cr, 0.0, 0.0, w, h);
832 if (cairo_in_fill (cr, x, y))
844 * Local Variables: -- emacs
846 * c-basic-offset: 8 -- emacs
847 * tab-width: 8 -- emacs
848 * indent-tabs-mode: nil -- emacs