1 /* gEDA - GPL Electronic Design Automation
2  * libgeda - gEDA's library
3  * Copyright (C) 1998-2010 Ales Hvezda
4  * Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details)
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 /*! \file o_picture.c
22  *  \brief functions for the picture object
23  */
24 
25 #include <config.h>
26 
27 #include <stdio.h>
28 #ifdef HAVE_STRING_H
29 #include <string.h>
30 #endif
31 #include <math.h>
32 
33 #include <gdk-pixbuf/gdk-pixbuf.h>
34 #include <gio/gio.h>
35 
36 #include "libgeda_priv.h"
37 
38 /*! \brief Create picture OBJECT from character string.
39  *  \par Function Description
40  *  Parses \a first_line and subsequent lines from \a tb, and returns
41  *  a newly-created picture #OBJECT.
42  *
43  *  \param [in]  toplevel       The TOPLEVEL object.
44  *  \param [in]  first_line      Character string with picture description.
45  *  \param [in]  tb              Text buffer to load embedded data from.
46  *  \param [in]  release_ver     libgeda release version number.
47  *  \param [in]  fileformat_ver  libgeda file format version number.
48  *  \return A pointer to the new picture object, or NULL on error.
49  */
o_picture_read(TOPLEVEL * toplevel,const char * first_line,TextBuffer * tb,unsigned int release_ver,unsigned int fileformat_ver,GError ** err)50 OBJECT *o_picture_read (TOPLEVEL *toplevel,
51 		       const char *first_line,
52 		       TextBuffer *tb,
53 		       unsigned int release_ver,
54                unsigned int fileformat_ver,
55                GError **err)
56 {
57   OBJECT *new_obj;
58   int x1, y1;
59   int width, height, angle;
60   int mirrored, embedded;
61   int num_conv;
62   gchar type;
63   const gchar *line = NULL;
64   gchar *filename;
65   gchar *file_content = NULL;
66   guint file_length = 0;
67 
68   num_conv = sscanf(first_line, "%c %d %d %d %d %d %d %d\n",
69 	 &type, &x1, &y1, &width, &height, &angle, &mirrored, &embedded);
70 
71   if (num_conv != 8) {
72     g_set_error(err, EDA_ERROR, EDA_ERROR_PARSE, _("Failed to parse picture definition"));
73     return NULL;
74   }
75 
76   if (width == 0 || height == 0) {
77     s_log_message(_("Found a zero width/height picture [ %c %d %d %d %d ]\n"),
78                   type, x1, y1, width, height);
79   }
80 
81   if ( (mirrored > 1) || (mirrored < 0)) {
82     s_log_message(_("Found a picture with a wrong 'mirrored' parameter: %d.\n"),
83 	    mirrored);
84     s_log_message(_("Setting mirrored to 0\n"));
85     mirrored = 0;
86   }
87 
88   if ( (embedded > 1) || (embedded < 0)) {
89     s_log_message(_("Found a picture with a wrong 'embedded' parameter: %d.\n"),
90 	    embedded);
91     s_log_message(_("Setting embedded to 0\n"));
92     embedded = 0;
93   }
94 
95   switch(angle) {
96 
97     case(0):
98     case(90):
99     case(180):
100     case(270):
101     break;
102 
103     default:
104       s_log_message(_("Found an unsupported picture angle [ %d ]\n"), angle);
105       s_log_message(_("Setting angle to 0\n"));
106       angle=0;
107       break;
108 
109   }
110 
111   filename = g_strdup(s_textbuffer_next_line(tb));
112   filename = remove_last_nl(filename);
113 
114   /* Handle empty filenames */
115   if (strlen (filename) == 0) {
116     s_log_message (_("Found an image with no filename."));
117     g_free (filename);
118     filename = NULL;
119   }
120 
121   if (embedded == 1) {
122     GString *encoded_picture=g_string_new("");
123     char finished = 0;
124 
125     /* Read the encoded picture */
126     do {
127 
128       line = s_textbuffer_next_line(tb);
129       if (line == NULL) break;
130 
131       if (g_strcasecmp(line, ".\n") != 0) {
132         encoded_picture = g_string_append (encoded_picture, line);
133       } else {
134         finished = 1;
135       }
136     } while (finished == 0);
137 
138     /* Decode the picture */
139     if (encoded_picture != NULL) {
140       file_content = s_encoding_base64_decode(encoded_picture->str,
141                                               encoded_picture->len,
142                                               &file_length);
143       g_string_free (encoded_picture, TRUE);
144     }
145 
146     if (file_content == NULL) {
147       s_log_message (_("Failed to load image from embedded data [%s]: %s\n"),
148                      filename, _("Base64 decoding failed."));
149       s_log_message (_("Falling back to file loading. Picture unembedded.\n"));
150       embedded = 0;
151     }
152   }
153 
154   /* create the picture */
155   /* The picture is described by its upper left and lower right corner */
156   new_obj = o_picture_new (toplevel, file_content, file_length, filename,
157                            type,
158                            x1, y1+height, x1+width, y1,
159                            angle, mirrored, embedded);
160 
161   g_free (file_content);
162   g_free (filename);
163 
164   return new_obj;
165 }
166 
167 /*! \brief Create a character string representation of a picture OBJECT.
168  *  \par Function Description
169  *  This function formats a string in the buffer <B>*buff</B> to describe
170  *  the picture object <B>*object</B>.
171  *
172  *  \param [in] toplevel  a TOPLEVEL structure
173  *  \param [in] object  Picture OBJECT to create string from.
174  *  \return A pointer to the picture OBJECT character string.
175  *
176  *  \note
177  *  Caller must g_free returned character string.
178  *
179  */
o_picture_save(TOPLEVEL * toplevel,OBJECT * object)180 char *o_picture_save(TOPLEVEL *toplevel, OBJECT *object)
181 {
182   int width, height, x1, y1;
183   gchar *encoded_picture=NULL;
184   gchar *out=NULL;
185   guint encoded_picture_length;
186   const gchar *filename = NULL;
187 
188   /* calculate the width and height of the box */
189   width  = abs(object->picture->lower_x - object->picture->upper_x);
190   height = abs(object->picture->upper_y - object->picture->lower_y);
191 
192   /* calculate the lower left corner of the box */
193   x1 = object->picture->upper_x;
194   y1 = object->picture->upper_y - height; /* move the origin to 0, 0*/
195 
196 #if DEBUG
197   printf("picture: %d %d %d %d\n", x1, y1, width, height);
198 #endif
199 
200   /* Encode the picture if it's embedded */
201   if (o_picture_is_embedded (toplevel, object)) {
202     encoded_picture =
203       s_encoding_base64_encode( (char *)object->picture->file_content,
204                                 object->picture->file_length,
205                                 &encoded_picture_length,
206                                 TRUE);
207     if (encoded_picture == NULL) {
208       s_log_message(_("ERROR: o_picture_save: unable to encode the picture.\n"));
209     }
210   }
211 
212   /* Cope with null filename */
213   filename = o_picture_get_filename (toplevel, object);
214   if (filename == NULL) filename = "";
215 
216   if (o_picture_is_embedded (toplevel, object) &&
217       encoded_picture != NULL) {
218     out = g_strdup_printf("%c %d %d %d %d %d %c %c\n%s\n%s\n%s",
219 			  object->type,
220 			  x1, y1, width, height,
221 			  object->picture->angle,
222 			  /* Convert the (0,1) chars to ASCII */
223 			  (object->picture->mirrored)+0x30,
224 			  '1',
225 			  filename,
226 			  encoded_picture,
227 			  ".");
228   }
229   else {
230     out = g_strdup_printf("%c %d %d %d %d %d %c %c\n%s",
231 			  object->type,
232 			  x1, y1, width, height,
233 			  object->picture->angle,
234 			  /* Convert the (0,1) chars to ASCII */
235 			  (object->picture->mirrored)+0x30,
236 			  '0',
237 			  filename);
238   }
239   g_free(encoded_picture);
240 
241   return(out);
242 }
243 
244 
245 /*! \brief Create a picture object.
246  *  \par Function Description
247  *  This function creates a new object representing a picture.
248  *
249  *  The picture is described by its upper left corner (\a x1, \a y1)
250  *  and its lower right corner (\a x2, \ay2).  The \a type parameter
251  *  must be equal to #OBJ_PICTURE.
252  *
253  *  If \a file_content is non-NULL, it must be a pointer to a buffer
254  *  containing raw image data.  If loading data from \a file_content
255  *  is unsuccessful, and \a filename is non-NULL, an image will
256  *  attempt to be loaded from \a filename.  Otherwise, the picture
257  *  object will be initially empty.
258  *
259  *  \param [in]     toplevel      The TOPLEVEL object.
260  *  \param [in]     file_content  Raw data of the image file, or NULL.
261  *  \param [in]     file_length   Length of raw data buffer
262  *  \param [in]     filename      File name backing this picture, or NULL.
263  *  \param [in]     type          Must be OBJ_PICTURE.
264  *  \param [in]     x1            Upper x coordinate.
265  *  \param [in]     y1            Upper y coordinate.
266  *  \param [in]     x2            Lower x coordinate.
267  *  \param [in]     y2            Lower y coordinate.
268  *  \param [in]     angle         Picture rotation angle.
269  *  \param [in]     mirrored      Whether the image should be mirrored or not.
270  *  \param [in]     embedded      Whether the embedded flag should be set or not.
271  *  \return A pointer to a new picture #OBJECT.
272  */
o_picture_new(TOPLEVEL * toplevel,const gchar * file_content,gsize file_length,const gchar * filename,char type,int x1,int y1,int x2,int y2,int angle,int mirrored,int embedded)273 OBJECT *o_picture_new (TOPLEVEL *toplevel,
274                        const gchar *file_content, gsize file_length,
275                        const gchar *filename,
276                        char type, int x1, int y1, int x2, int y2, int angle,
277                        int mirrored, int embedded)
278 {
279   OBJECT *new_node;
280   PICTURE *picture;
281 
282   /* create the object */
283   new_node = s_basic_new_object(type, "picture");
284 
285   picture = (PICTURE *) g_malloc0 (sizeof(PICTURE));
286   new_node->picture = picture;
287 
288   /* describe the picture with its upper left and lower right corner */
289   picture->upper_x = (x1 > x2) ? x2 : x1;
290   picture->upper_y = (y1 > y2) ? y1 : y2;
291   picture->lower_x = (x1 > x2) ? x1 : x2;
292   picture->lower_y = (y1 > y2) ? y2 : y1;
293 
294   picture->pixbuf = NULL;
295   picture->file_content = NULL;
296   picture->file_length = 0;
297 
298   picture->ratio = abs ((double) (x1 - x2) / (y1 - y2));
299   picture->filename = g_strdup (filename);
300   picture->angle = angle;
301   picture->mirrored = mirrored;
302   picture->embedded = embedded;
303 
304   if (file_content != NULL) {
305     GError *error = NULL;
306     if (!o_picture_set_from_buffer (toplevel, new_node, filename,
307                                     file_content, file_length, &error)) {
308       s_log_message (_("Failed to load buffer image [%s]: %s\n"),
309                      filename, error->message);
310       g_error_free (error);
311 
312       /* Force the data into the object anyway, so as to prevent data
313        * loss of embedded images. */
314       picture->file_content = g_memdup (file_content, file_length);
315       picture->file_length = file_length;
316     }
317   }
318   if (picture->pixbuf == NULL && filename != NULL) {
319     GError *error = NULL;
320     if (!o_picture_set_from_file (toplevel, new_node, filename, &error)) {
321       s_log_message (_("Failed to load image from [%s]: %s\n"),
322                      filename, error->message);
323       g_error_free (error);
324     }
325   }
326 
327   /* compute the bounding picture */
328   o_picture_recalc(toplevel, new_node);
329 
330   return new_node;
331 }
332 
333 /*! \brief Recalculate picture bounding box.
334  *  \par Function Description
335  *  This function recalculates the bounding box of the <B>o_current</B>
336  *  parameter picture object.
337  *
338  *  \param [in] toplevel      The TOPLEVEL object.
339  *  \param [in,out] o_current  Picture OBJECT to be recalculated.
340  */
o_picture_recalc(TOPLEVEL * toplevel,OBJECT * o_current)341 void o_picture_recalc(TOPLEVEL *toplevel, OBJECT *o_current)
342 {
343   int left, top, right, bottom;
344 
345   if (o_current->picture == NULL) {
346     return;
347   }
348 
349   /* update the bounding picture - world units */
350   world_get_picture_bounds(toplevel, o_current,
351 		     &left, &top, &right, &bottom);
352   o_current->w_left   = left;
353   o_current->w_top    = top;
354   o_current->w_right  = right;
355   o_current->w_bottom = bottom;
356   o_current->w_bounds_valid = TRUE;
357 }
358 
359 /*! \brief Get picture bounding rectangle in WORLD coordinates.
360  *  \par Function Description
361  *  This function sets the <B>left</B>, <B>top</B>, <B>right</B> and
362  *  <B>bottom</B> parameters to the boundings of the picture object
363  *  described in <B>*picture</B> in WORLD units.
364  *
365  *  \param [in]  toplevel  The TOPLEVEL object.
366  *  \param [in]  object     Picture OBJECT to read coordinates from.
367  *  \param [out] left       Left picture coordinate in WORLD units.
368  *  \param [out] top        Top picture coordinate in WORLD units.
369  *  \param [out] right      Right picture coordinate in WORLD units.
370  *  \param [out] bottom     Bottom picture coordinate in WORLD units.
371  */
world_get_picture_bounds(TOPLEVEL * toplevel,OBJECT * object,int * left,int * top,int * right,int * bottom)372 void world_get_picture_bounds(TOPLEVEL *toplevel, OBJECT *object,
373                               int *left, int *top, int *right, int *bottom)
374 {
375   *left   = min(object->picture->upper_x, object->picture->lower_x);
376   *top    = min(object->picture->upper_y, object->picture->lower_y);
377   *right  = max(object->picture->upper_x, object->picture->lower_x);
378   *bottom = max(object->picture->upper_y, object->picture->lower_y);
379 
380 }
381 
382 /*! \brief get the position of the left bottom point
383  *  \par Function Description
384  *  This function gets the position of the bottom left point of a picture object.
385  *
386  *  \param [in] toplevel The toplevel environment.
387  *  \param [out] x       pointer to the x-position
388  *  \param [out] y       pointer to the y-position
389  *  \param [in] object   The object to get the position.
390  *  \return TRUE if successfully determined the position, FALSE otherwise
391  */
o_picture_get_position(TOPLEVEL * toplevel,gint * x,gint * y,OBJECT * object)392 gboolean o_picture_get_position (TOPLEVEL *toplevel, gint *x, gint *y,
393                               OBJECT *object)
394 {
395   *x = min(object->picture->lower_x, object->picture->upper_x);
396   *y = min(object->picture->lower_y, object->picture->upper_y);
397   return TRUE;
398 }
399 
400 
401 /*! \brief Get the width/height ratio of an image.
402  * \par Function Description
403 
404  * Returns the width/height ratio of picture \a object, taking the
405  * image rotation into account.
406  *
407  * \param toplevel  The current #TOPLEVEL.
408  * \param object    Picture #OBJECT to inspect.
409  * \return width/height ratio for \a object.
410  */
411 double
o_picture_get_ratio(TOPLEVEL * toplevel,OBJECT * object)412 o_picture_get_ratio (TOPLEVEL *toplevel, OBJECT *object)
413 {
414   g_return_val_if_fail (object != NULL, 1);
415   g_return_val_if_fail (object->picture != NULL, 1);
416 
417   /* The effective ratio varies depending on the rotation of the
418    * image. */
419   switch (object->picture->angle) {
420   case 0:
421   case 180:
422     return object->picture->ratio;
423   case 90:
424   case 270:
425     return 1.0 / object->picture->ratio;
426   default:
427     g_critical (_("Picture %p has invalid angle %i\n"), object,
428                 object->picture->angle);
429   }
430   return 0;
431 }
432 
433 /*! \brief Modify the description of a picture OBJECT.
434  *  \par Function Description
435  *  This function modifies the coordinates of one of the four corner of
436  *  the picture. The new coordinates of the corner identified by
437  *  <B>whichone</B> are given by <B>x</B> and <B>y</B> in world unit.
438  *
439  *  The coordinates of the corner is modified in the world coordinate system.
440  *  Screen coordinates and boundings are then updated.
441  *
442  *  \param [in]     toplevel  The TOPLEVEL object.
443  *  \param [in,out] object     Picture OBJECT to modify.
444  *  \param [in]     x          New x coordinate.
445  *  \param [in]     y          New y coordinate.
446  *  \param [in]     whichone   Which picture parameter to modify.
447  *
448  *  <B>whichone</B> can have the following values:
449  *  <DL>
450  *    <DT>*</DT><DD>PICTURE_UPPER_LEFT
451  *    <DT>*</DT><DD>PICTURE_LOWER_LEFT
452  *    <DT>*</DT><DD>PICTURE_UPPER_RIGHT
453  *    <DT>*</DT><DD>PICTURE_LOWER_RIGHT
454  *  </DL>
455  */
o_picture_modify(TOPLEVEL * toplevel,OBJECT * object,int x,int y,int whichone)456 void o_picture_modify(TOPLEVEL *toplevel, OBJECT *object,
457 		      int x, int y, int whichone)
458 {
459   int tmp;
460   double ratio = o_picture_get_ratio (toplevel, object);
461 
462   o_emit_pre_change_notify (toplevel, object);
463 
464   /* change the position of the selected corner */
465   switch(whichone) {
466     case PICTURE_UPPER_LEFT:
467       object->picture->upper_x = x;
468       tmp = abs(object->picture->upper_x - object->picture->lower_x) / ratio;
469       if (y < object->picture->lower_y) {
470 	tmp = -tmp;
471       }
472       object->picture->upper_y = object->picture->lower_y + tmp;
473       break;
474 
475     case PICTURE_LOWER_LEFT:
476       object->picture->upper_x = x;
477       tmp = abs(object->picture->upper_x - object->picture->lower_x) / ratio;
478       if (y > object->picture->upper_y) {
479 	tmp = -tmp;
480       }
481       object->picture->lower_y = object->picture->upper_y - tmp;
482       break;
483 
484     case PICTURE_UPPER_RIGHT:
485       object->picture->lower_x = x;
486       tmp = abs(object->picture->upper_x - object->picture->lower_x) / ratio;
487       if (y < object->picture->lower_y) {
488 	tmp = -tmp;
489       }
490       object->picture->upper_y = object->picture->lower_y + tmp;
491       break;
492 
493     case PICTURE_LOWER_RIGHT:
494       object->picture->lower_x = x;
495       tmp = abs(object->picture->upper_x - object->picture->lower_x) / ratio;
496       if (y > object->picture->upper_y) {
497 	tmp = -tmp;
498       }
499       object->picture->lower_y = object->picture->upper_y - tmp;
500       break;
501 
502     default:
503       return;
504   }
505 
506   /* need to update the upper left and lower right corners */
507   if(object->picture->upper_x > object->picture->lower_x) {
508     tmp                      = object->picture->upper_x;
509     object->picture->upper_x = object->picture->lower_x;
510     object->picture->lower_x = tmp;
511   }
512 
513   if(object->picture->upper_y < object->picture->lower_y) {
514     tmp                      = object->picture->upper_y;
515     object->picture->upper_y = object->picture->lower_y;
516     object->picture->lower_y = tmp;
517   }
518 
519   /* recalculate the screen coords and the boundings */
520   o_picture_recalc(toplevel, object);
521   o_emit_change_notify (toplevel, object);
522 }
523 
524 /*! \brief Modify a picture object's coordinates.
525  * \par Function Description
526  * Modifies the coordinates of all four corners of a picture \a
527  * object.  The picture is adjusted to fit the rectangle enclosed by
528  * the points (\a x1, \a y1) and (\a x2, \a y2), and scaled as large
529  * as possible to still fit within that rectangle.
530  *
531  * \param [in]     toplevel current #TOPLEVEL.
532  * \param [in,out] object   picture #OBJECT to be modified.
533  * \param [in]     x1       x coordinate of first corner of box.
534  * \param [in]     y1       y coordinate of first corner of box.
535  * \param [in]     x2       x coordinate of second corner of box.
536  * \param [in]     y2       y coordinate of second corner of box.
537  */
538 void
o_picture_modify_all(TOPLEVEL * toplevel,OBJECT * object,int x1,int y1,int x2,int y2)539 o_picture_modify_all (TOPLEVEL *toplevel, OBJECT *object,
540                       int x1, int y1, int x2, int y2)
541 {
542   o_emit_pre_change_notify (toplevel, object);
543 
544   /* Normalise the requested rectangle. */
545   object->picture->lower_x = (x1 > x2) ? x1 : x2;
546   object->picture->lower_y = (y1 > y2) ? y2 : y1;
547   object->picture->upper_x = (x1 > x2) ? x2 : x1;
548   object->picture->upper_y = (y1 > y2) ? y1 : y2;
549 
550   /* recalculate the world coords and bounds */
551   o_box_recalc(toplevel, object);
552   o_emit_change_notify (toplevel, object);
553 }
554 
555 /*! \brief Rotate picture OBJECT using WORLD coordinates.
556  *  \par Function Description
557  *  This function rotates the picture described by <B>*object</B> around
558  *  the (<B>world_centerx</B>, <B>world_centery</B>) point by <B>angle</B>
559  *  degrees.
560  *  The center of rotation is in world units.
561  *
562  *  \param [in]      toplevel      The TOPLEVEL object.
563  *  \param [in]      world_centerx  Rotation center x coordinate in
564  *                                  WORLD units.
565  *  \param [in]      world_centery  Rotation center y coordinate in
566  *                                  WORLD units.
567  *  \param [in]      angle          Rotation angle in degrees (See note below).
568  *  \param [in,out]  object         Picture OBJECT to rotate.
569  */
o_picture_rotate_world(TOPLEVEL * toplevel,int world_centerx,int world_centery,int angle,OBJECT * object)570 void o_picture_rotate_world(TOPLEVEL *toplevel,
571 			    int world_centerx, int world_centery, int angle,
572 			    OBJECT *object)
573 {
574   int newx1, newy1;
575   int newx2, newy2;
576 
577   /* Only 90 degree multiple and positive angles are allowed. */
578   /* angle must be positive */
579   if(angle < 0) angle = -angle;
580   /* angle must be a 90 multiple or no rotation performed */
581   if((angle % 90) != 0) return;
582 
583   object->picture->angle = ( object->picture->angle + angle ) % 360;
584 
585   /* The center of rotation (<B>world_centerx</B>, <B>world_centery</B>) is
586    * translated to the origin. The rotation of the upper left and lower
587    * right corner are then performed. Finally, the rotated picture is
588    * translated back to its previous location.
589    */
590   /* translate object to origin */
591   object->picture->upper_x -= world_centerx;
592   object->picture->upper_y -= world_centery;
593   object->picture->lower_x -= world_centerx;
594   object->picture->lower_y -= world_centery;
595 
596   /* rotate the upper left corner of the picture */
597   rotate_point_90(object->picture->upper_x, object->picture->upper_y, angle,
598 		  &newx1, &newy1);
599 
600   /* rotate the lower left corner of the picture */
601   rotate_point_90(object->picture->lower_x, object->picture->lower_y, angle,
602 		  &newx2, &newy2);
603 
604   /* reorder the corners after rotation */
605   object->picture->upper_x = min(newx1,newx2);
606   object->picture->upper_y = max(newy1,newy2);
607   object->picture->lower_x = max(newx1,newx2);
608   object->picture->lower_y = min(newy1,newy2);
609 
610   /* translate object back to normal position */
611   object->picture->upper_x += world_centerx;
612   object->picture->upper_y += world_centery;
613   object->picture->lower_x += world_centerx;
614   object->picture->lower_y += world_centery;
615 
616   /* recalc boundings and screen coords */
617   o_picture_recalc(toplevel, object);
618 
619 }
620 
621 /*! \brief Mirror a picture using WORLD coordinates.
622  *  \par Function Description
623  *  This function mirrors the picture from the point
624  *  (<B>world_centerx</B>,<B>world_centery</B>) in world unit.
625  *
626  *  The picture is first translated to the origin, then mirrored and
627  *  finally translated back at its previous position.
628  *
629  *  \param [in]     toplevel      The TOPLEVEL object.
630  *  \param [in]     world_centerx  Origin x coordinate in WORLD units.
631  *  \param [in]     world_centery  Origin y coordinate in WORLD units.
632  *  \param [in,out] object         Picture OBJECT to mirror.
633  */
o_picture_mirror_world(TOPLEVEL * toplevel,int world_centerx,int world_centery,OBJECT * object)634 void o_picture_mirror_world(TOPLEVEL *toplevel,
635 			    int world_centerx, int world_centery,
636 			    OBJECT *object)
637 {
638   int newx1, newy1;
639   int newx2, newy2;
640 
641   /* Set info in object. Sometimes it's necessary to change the
642    * rotation angle as well as the mirror flag. */
643   object->picture->mirrored = !object->picture->mirrored;
644   switch (object->picture->angle) {
645   case 90:
646     object->picture->angle = 270;
647     break;
648   case 270:
649     object->picture->angle = 90;
650     break;
651   }
652 
653   /* translate object to origin */
654   object->picture->upper_x -= world_centerx;
655   object->picture->upper_y -= world_centery;
656   object->picture->lower_x -= world_centerx;
657   object->picture->lower_y -= world_centery;
658 
659   /* mirror the corners */
660   newx1 = -object->picture->upper_x;
661   newy1 = object->picture->upper_y;
662   newx2 = -object->picture->lower_x;
663   newy2 = object->picture->lower_y;
664 
665   /* reorder the corners */
666   object->picture->upper_x = min(newx1,newx2);
667   object->picture->upper_y = max(newy1,newy2);
668   object->picture->lower_x = max(newx1,newx2);
669   object->picture->lower_y = min(newy1,newy2);
670 
671   /* translate back in position */
672   object->picture->upper_x += world_centerx;
673   object->picture->upper_y += world_centery;
674   object->picture->lower_x += world_centerx;
675   object->picture->lower_y += world_centery;
676 
677   /* recalc boundings and screen coords */
678   o_picture_recalc(toplevel, object);
679 
680 }
681 
682 /*! \brief Translate a picture position in WORLD coordinates by a delta.
683  *  \par Function Description
684  *  This function applies a translation of (<B>x1</B>,<B>y1</B>) to the picture
685  *  described by <B>*object</B>. <B>x1</B> and <B>y1</B> are in world units.
686  *
687  *  \param [in]     toplevel  The TOPLEVEL object.
688  *  \param [in]     dx         x distance to move.
689  *  \param [in]     dy         y distance to move.
690  *  \param [in,out] object     Picture OBJECT to translate.
691  */
o_picture_translate_world(TOPLEVEL * toplevel,int dx,int dy,OBJECT * object)692 void o_picture_translate_world(TOPLEVEL *toplevel,
693 			       int dx, int dy, OBJECT *object)
694 {
695   /* Do world coords */
696   object->picture->upper_x = object->picture->upper_x + dx;
697   object->picture->upper_y = object->picture->upper_y + dy;
698   object->picture->lower_x = object->picture->lower_x + dx;
699   object->picture->lower_y = object->picture->lower_y + dy;
700 
701   /* recalc the screen coords and the bounding picture */
702   o_picture_recalc(toplevel, object);
703 }
704 
705 /*! \brief Create a copy of a picture.
706  *  \par Function Description
707  *  This function creates a verbatim copy of the object pointed by
708  *  <B>o_current</B> describing a picture.
709  *
710  *  \param [in]  toplevel   The TOPLEVEL object.
711  *  \param [in]  object     Picture OBJECT to copy.
712  *  \return The new OBJECT
713  */
o_picture_copy(TOPLEVEL * toplevel,OBJECT * object)714 OBJECT *o_picture_copy(TOPLEVEL *toplevel, OBJECT *object)
715 {
716   OBJECT *new_node;
717   PICTURE *picture;
718 
719   /* create the object */
720   new_node = s_basic_new_object(object->type, "picture");
721 
722   picture = g_malloc(sizeof(PICTURE));
723   new_node->picture = picture;
724 
725   new_node->color = object->color;
726   new_node->selectable = object->selectable;
727 
728   /* describe the picture with its upper left and lower right corner */
729   picture->upper_x = object->picture->upper_x;
730   picture->upper_y = object->picture->upper_y;
731   picture->lower_x = object->picture->lower_x;
732   picture->lower_y = object->picture->lower_y;
733 
734   if (object->picture->file_content != NULL) {
735     picture->file_content = g_memdup (object->picture->file_content,
736                                       object->picture->file_length);
737   } else {
738     picture->file_content = NULL;
739   }
740 
741   picture->file_length = object->picture->file_length;
742   picture->filename    = g_strdup (object->picture->filename);
743   picture->ratio       = object->picture->ratio;
744   picture->angle       = object->picture->angle;
745   picture->mirrored    = object->picture->mirrored;
746   picture->embedded    = object->picture->embedded;
747 
748   /* Get the picture data */
749   picture->pixbuf = o_picture_get_pixbuf (toplevel, object);
750 
751   /* compute the bounding picture */
752   o_picture_recalc(toplevel, new_node);
753 
754   return new_node;
755 }
756 
757 
758 /*! \brief Get RGB data from image.
759  *  \par Function Description
760  *  This function returns the RGB data of the given image.
761  *  Function taken from the DIA source code (http://www.gnome.org/projects/dia)
762  *  and licensed under the GNU GPL version 2.
763  *
764  *  \param [in] image  GdkPixbuf image to read RGB data from.
765  *  \return Array of rgb data from image.
766  *
767  *  \note
768  *  Caller must g_free returned guint8 array.
769  */
770 static guint8 *
o_picture_rgb_data(GdkPixbuf * image)771 o_picture_rgb_data(GdkPixbuf *image)
772 {
773   int width = gdk_pixbuf_get_width(image);
774   int height = gdk_pixbuf_get_height(image);
775   int rowstride = gdk_pixbuf_get_rowstride(image);
776   int size = height*rowstride;
777   guint8 *rgb_pixels = g_malloc(size);
778 
779   if (gdk_pixbuf_get_has_alpha(image)) {
780     guint8 *pixels = gdk_pixbuf_get_pixels(image);
781     int i, j;
782     for (i = 0; i < height; i++) {
783       for (j = 0; j < width; j++) {
784 	rgb_pixels[i*rowstride+j*3] = pixels[i*rowstride+j*4];
785 	rgb_pixels[i*rowstride+j*3+1] = pixels[i*rowstride+j*4+1];
786 	rgb_pixels[i*rowstride+j*3+2] = pixels[i*rowstride+j*4+2];
787       }
788     }
789     return rgb_pixels;
790   } else {
791     guint8 *pixels = gdk_pixbuf_get_pixels(image);
792 
793     g_memmove(rgb_pixels, pixels, height*rowstride);
794     return rgb_pixels;
795   }
796 }
797 
798 /*! \brief Get mask data from image.
799  *  \par Function Description
800  *  This function returns the mask data of the given image.
801  *  Function taken from the DIA source code (http://www.gnome.org/projects/dia)
802  *  and licensed under the GNU GPL version 2.
803  *
804  *  \param [in] image  GdkPixbuf image to get mask data from.
805  *  \return Array of mask data from image.
806  *
807  *  \note
808  *  Caller must g_free returned guint8 array.
809  */
810 static guint8 *
o_picture_mask_data(GdkPixbuf * image)811 o_picture_mask_data(GdkPixbuf *image)
812 {
813   guint8 *pixels;
814   guint8 *mask;
815   int i, size;
816 
817   if (!gdk_pixbuf_get_has_alpha(image)) {
818     return NULL;
819   }
820 
821   pixels = gdk_pixbuf_get_pixels(image);
822 
823   size = gdk_pixbuf_get_width(image)*
824     gdk_pixbuf_get_height(image);
825 
826   mask = g_malloc(size);
827 
828   /* Pick every fourth byte (the alpha channel) into mask */
829   for (i = 0; i < size; i++)
830     mask[i] = pixels[i*4+3];
831 
832   return mask;
833 }
834 
835 /*! \brief Print picture to Postscript document.
836  *  \par Function Description
837  *  This function prints a picture object. The picture is defined by the
838  *  coordinates of its upper left corner in (<B>x</B>,<B>y</B>) and its width
839  *  and height given by the <B>width</B> and <B>height</B> parameters.
840  *
841  *  If the picture object was unable to be loaded, prints a crossed
842  *  box of the same dimensions.
843  *
844  *  The Postscript document is defined by the file pointer <B>fp</B>.
845  *  Function based on the DIA source code (http://www.gnome.org/projects/dia)
846  *  and licensed under the GNU GPL version 2.
847  *
848  *  All dimensions are in mils.
849  *
850  *  \param [in] toplevel  The TOPLEVEL object.
851  *  \param [in] fp         FILE pointer to Postscript document.
852  *  \param [in] o_current  Picture OBJECT to write to document.
853  *  \param [in] origin_x   Page x coordinate to place picture OBJECT.
854  *  \param [in] origin_y   Page y coordinate to place picture OBJECT.
855  */
o_picture_print(TOPLEVEL * toplevel,FILE * fp,OBJECT * o_current,int origin_x,int origin_y)856 void o_picture_print(TOPLEVEL *toplevel, FILE *fp, OBJECT *o_current,
857 		     int origin_x, int origin_y)
858 {
859   int x1, y1, x, y;
860   int height, width;
861   GdkPixbuf* image = o_picture_get_pixbuf (toplevel, o_current);
862   int img_width, img_height, img_rowstride;
863   guint8 *rgb_data;
864   guint8 *mask_data;
865 
866   /* calculate the width and height of the box */
867   width  = abs(o_current->picture->lower_x - o_current->picture->upper_x);
868   height = abs(o_current->picture->upper_y - o_current->picture->lower_y);
869 
870   /* calculate the origin of the box */
871   x1 = o_current->picture->upper_x;
872   y1 = o_current->picture->upper_y;
873 
874   /* If the image failed to load, try to get hold of the fallback
875    * pixbuf. */
876   if (image == NULL) image = o_picture_get_fallback_pixbuf (toplevel);
877   /* If the image failed to load, draw a box in the default color with a
878    * cross in it. */
879   if (image == NULL) {
880     int line_width = (toplevel->line_style == THICK) ? LINE_WIDTH : 2;
881     o_box_print_solid (toplevel, fp, x1, y1, width, height,
882                        DEFAULT_COLOR, line_width, -1, -1, -1, -1);
883     o_line_print_solid (toplevel, fp, x1, y1, x1+width, y1-height,
884                         DEFAULT_COLOR, line_width, -1, -1, -1, -1);
885     o_line_print_solid (toplevel, fp, x1+width, y1, x1, y1-height,
886                         DEFAULT_COLOR, line_width, -1, -1, -1, -1);
887     return;
888   }
889 
890   img_width = gdk_pixbuf_get_width(image);
891   img_rowstride = gdk_pixbuf_get_rowstride(image);
892   img_height = gdk_pixbuf_get_height(image);
893 
894   rgb_data = o_picture_rgb_data(image);
895   mask_data = o_picture_mask_data(image);
896 
897   fprintf(fp, "gsave\n");
898 
899   /* color output only */
900   fprintf(fp, "/pix %i string def\n", img_width * 3);
901   fprintf(fp, "%i %i 8\n", img_width, img_height);
902   fprintf(fp, "%i %i translate\n", x1, y1);
903   fprintf(fp, "%i %i scale\n", width, height);
904   fprintf(fp, "[%i 0 0 -%i 0 0]\n", img_width, img_height);
905 
906   fprintf(fp, "{currentfile pix readhexstring pop}\n");
907   fprintf(fp, "false 3 colorimage\n");
908   fprintf(fp, "\n");
909 
910   if (mask_data) {
911     for (y = 0; y < img_height; y++) {
912       for (x = 0; x < img_width; x++) {
913 	int i = y*img_rowstride+x*3;
914 	int m = y*img_width+x;
915         fprintf(fp, "%02x", 255-(mask_data[m]*(255-rgb_data[i])/255));
916         fprintf(fp, "%02x", 255-(mask_data[m]*(255-rgb_data[i+1])/255));
917         fprintf(fp, "%02x", 255-(mask_data[m]*(255-rgb_data[i+2])/255));
918       }
919       fprintf(fp, "\n");
920     }
921   } else {
922     for (y = 0; y < img_height; y++) {
923       for (x = 0; x < img_width; x++) {
924 	int i = y*img_rowstride+x*3;
925         fprintf(fp, "%02x", (int)(rgb_data[i]));
926         fprintf(fp, "%02x", (int)(rgb_data[i+1]));
927         fprintf(fp, "%02x", (int)(rgb_data[i+2]));
928       }
929       fprintf(fp, "\n");
930     }
931   }
932   fprintf(fp, "grestore\n");
933   fprintf(fp, "\n");
934 
935   g_object_unref (image);
936   g_free(rgb_data);
937   g_free(mask_data);
938 }
939 
940 
941 /*! \brief Embed the image file associated with a picture
942  * \par Function Description
943  * Verify that a picture has valid data associated with it, and if so,
944  * mark it to be embedded.
945  *
946  *  \param [in]     toplevel     The TOPLEVEL object.
947  *  \param [in]     object       The picture OBJECT to embed
948  */
o_picture_embed(TOPLEVEL * toplevel,OBJECT * object)949 void o_picture_embed (TOPLEVEL *toplevel, OBJECT *object)
950 {
951   const gchar *filename = o_picture_get_filename (toplevel, object);
952   gchar *basename;
953 
954   if (o_picture_is_embedded (toplevel, object)) return;
955 
956   if (object->picture->file_content == NULL) {
957     s_log_message (_("Picture [%s] has no image data.\n"), filename);
958     s_log_message (_("Falling back to file loading. Picture is still unembedded.\n"));
959     object->picture->embedded = 0;
960     return;
961   }
962 
963   object->picture->embedded = 1;
964 
965   basename = g_path_get_basename (filename);
966   s_log_message (_("Picture [%s] has been embedded\n"), basename);
967   g_free (basename);
968 }
969 
970 
971 /*! \brief Unembed a picture, reloading the image from disk
972  * \par Function Description
973  * Verify that the file associated with \a object exists on disk and
974  * is usable, and if so, reload the picture and mark it as unembedded.
975  *
976  *  \param [in]     toplevel     The TOPLEVEL object.
977  *  \param [in]     object       The picture OBJECT to unembed
978  */
o_picture_unembed(TOPLEVEL * toplevel,OBJECT * object)979 void o_picture_unembed (TOPLEVEL *toplevel, OBJECT *object)
980 {
981   GError *err = NULL;
982   const gchar *filename = o_picture_get_filename (toplevel, object);
983   gchar *basename;
984 
985   if (!o_picture_is_embedded (toplevel, object)) return;
986 
987   o_picture_set_from_file (toplevel, object, filename, &err);
988 
989   if (err != NULL) {
990     s_log_message (_("Failed to load image from file [%s]: %s\n"),
991                    filename, err->message);
992     s_log_message (_("Picture is still embedded.\n"));
993     g_error_free (err);
994     return;
995   }
996 
997   object->picture->embedded = 0;
998 
999   basename = g_path_get_basename(filename);
1000   s_log_message (_("Picture [%s] has been unembedded\n"), basename);
1001   g_free(basename);
1002 }
1003 
1004 /*! \brief Calculates the distance between the given point and the closest
1005  * point in the picture.
1006  *
1007  *  Interrior points within the picture return a distance of zero.
1008  *
1009  *  \param [in] object       The picture OBJECT.
1010  *  \param [in] x            The x coordinate of the given point.
1011  *  \param [in] y            The y coordinate of the given point.
1012  *  \param [in] force_solid  If true, force treating the object as solid.
1013  *  \return The shortest distance from the object to the point. With an
1014  *  invalid parameter, this function returns G_MAXDOUBLE.
1015  */
o_picture_shortest_distance(OBJECT * object,int x,int y,int force_solid)1016 double o_picture_shortest_distance (OBJECT *object, int x, int y,
1017                                     int force_solid)
1018 {
1019   double dx, dy;
1020   double x1, y1, x2, y2;
1021 
1022   g_return_val_if_fail (object->picture != NULL, G_MAXDOUBLE);
1023 
1024   x1 = (double)min (object->picture->upper_x, object->picture->lower_x);
1025   y1 = (double)min (object->picture->upper_y, object->picture->lower_y);
1026   x2 = (double)max (object->picture->upper_x, object->picture->lower_x);
1027   y2 = (double)max (object->picture->upper_y, object->picture->lower_y);
1028 
1029   dx = min (((double)x) - x1, x2 - ((double)x));
1030   dy = min (((double)y) - y1, y2 - ((double)y));
1031 
1032   dx = min (dx, 0);
1033   dy = min (dy, 0);
1034 
1035   return sqrt ((dx * dx) + (dy * dy));
1036 }
1037 
1038 /*! \brief Test whether a picture object is embedded.
1039  * \par Function Description
1040  * Returns TRUE if the picture \a object will have its data embedded
1041  * in a schematic or symbol file; returns FALSE if its data will be
1042  * obtained from a separate file.
1043  *
1044  * \param toplevel  The current #TOPLEVEL.
1045  * \param object    The picture #OBJECT to inspect.
1046  * \return TRUE if \a object is embedded.
1047  */
1048 gboolean
o_picture_is_embedded(TOPLEVEL * toplevel,OBJECT * object)1049 o_picture_is_embedded (TOPLEVEL *toplevel, OBJECT *object)
1050 {
1051   g_return_val_if_fail (object != NULL, FALSE);
1052   g_return_val_if_fail (object->picture != NULL, FALSE);
1053 
1054   return object->picture->embedded;
1055 }
1056 
1057 /*! \brief Get a pixel buffer for a picture object.
1058  * \par Function Description
1059  * Returns a #GdkPixbuf for the picture object \a object, or NULL if
1060  * the picture could not be loaded.
1061  *
1062  * The returned value should have its reference count decremented with
1063  * g_object_unref() when no longer needed.
1064  *
1065  * \param toplevel  The current #TOPLEVEL.
1066  * \param object    The picture #OBJECT to inspect.
1067  * \return A #GdkPixbuf for the picture.
1068  */
1069 GdkPixbuf *
o_picture_get_pixbuf(TOPLEVEL * toplevel,OBJECT * object)1070 o_picture_get_pixbuf (TOPLEVEL *toplevel, OBJECT *object)
1071 {
1072   g_return_val_if_fail (object != NULL, NULL);
1073   g_return_val_if_fail (object->picture != NULL, NULL);
1074 
1075   if (object->picture->pixbuf != NULL) {
1076     return g_object_ref (object->picture->pixbuf);
1077   } else {
1078     return NULL;
1079   }
1080 }
1081 
1082 /*! \brief Get the raw image data from a picture object.
1083  * \par Function Description
1084  * Returns the raw image file data underlying the picture \a object,
1085  * or NULL if the picture could not be loaded.
1086  *
1087  * \param toplevel  The current #TOPLEVEL.
1088  * \param object    The picture #OBJECT to inspect.
1089  * \param len       Location to store buffer length.
1090  * \return A read-only buffer of raw image data.
1091  */
1092 const char *
o_picture_get_data(TOPLEVEL * toplevel,OBJECT * object,size_t * len)1093 o_picture_get_data (TOPLEVEL *toplevel, OBJECT *object,
1094                     size_t *len)
1095 {
1096   g_return_val_if_fail (object != NULL, NULL);
1097   g_return_val_if_fail (object->picture != NULL, NULL);
1098 
1099   *len = object->picture->file_length;
1100   return object->picture->file_content;
1101 }
1102 
1103 /*! \brief Set a picture object's contents from a buffer.
1104  * \par Function Description
1105  * Sets the contents of the picture \a object by reading image data
1106  * from a buffer.  The buffer should be in on-disk format.
1107  *
1108  * \param toplevel The current #TOPLEVEL.
1109  * \param object   The picture #OBJECT to modify.
1110  * \param filename The new filename for the picture.
1111  * \param data     The new image data buffer.
1112  * \param len      The size of the data buffer.
1113  * \param error    Location to return error information.
1114  * \return TRUE on success, FALSE on failure.
1115  */
1116 gboolean
o_picture_set_from_buffer(TOPLEVEL * toplevel,OBJECT * object,const gchar * filename,const gchar * data,size_t len,GError ** error)1117 o_picture_set_from_buffer (TOPLEVEL *toplevel, OBJECT *object,
1118                            const gchar *filename,
1119                            const gchar *data, size_t len,
1120                            GError **error)
1121 {
1122   GdkPixbuf *pixbuf;
1123   GInputStream *stream;
1124   gchar *tmp;
1125 
1126   g_return_val_if_fail (toplevel != NULL, FALSE);
1127   g_return_val_if_fail (object != NULL, FALSE);
1128   g_return_val_if_fail (object->picture != NULL, FALSE);
1129   g_return_val_if_fail (data != NULL, FALSE);
1130 
1131   /* Check that we can actually load the data before making any
1132    * changes to the object. */
1133   stream = G_INPUT_STREAM (g_memory_input_stream_new_from_data (data, len, NULL));
1134   pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, error);
1135   g_object_unref (stream);
1136   if (pixbuf == NULL) return FALSE;
1137 
1138   o_emit_pre_change_notify (toplevel, object);
1139 
1140   if (object->picture->pixbuf != NULL) {
1141     g_object_unref (object->picture->pixbuf);
1142   }
1143   object->picture->pixbuf = pixbuf;
1144 
1145   object->picture->ratio = ((double) gdk_pixbuf_get_width(pixbuf) /
1146                             gdk_pixbuf_get_height(pixbuf));
1147 
1148   tmp = g_strdup (filename);
1149   g_free (object->picture->filename);
1150   object->picture->filename = tmp;
1151 
1152   gchar *buf = g_realloc (object->picture->file_content,
1153                           len);
1154   /* It's possible that these buffers might overlap, because the
1155    * library user hates us. */
1156   memmove (buf, data, len);
1157   object->picture->file_content = buf;
1158   object->picture->file_length = len;
1159 
1160   o_emit_change_notify (toplevel, object);
1161   return TRUE;
1162 }
1163 
1164 /*! \brief Set a picture object's contents from a file.
1165  * \par Function Description
1166  * Sets the contents of the picture \a object by reading image data
1167  * from a file.
1168  *
1169  * \param toplevel The current #TOPLEVEL.
1170  * \param object   The picture #OBJECT to modify.
1171  * \param filename The filename to load image data from.
1172  * \param error    Location to return error information.
1173  * \return TRUE on success, FALSE on failure.
1174  */
1175 gboolean
o_picture_set_from_file(TOPLEVEL * toplevel,OBJECT * object,const gchar * filename,GError ** error)1176 o_picture_set_from_file (TOPLEVEL *toplevel, OBJECT *object,
1177                          const gchar *filename,
1178                          GError **error)
1179 {
1180   gchar *buf;
1181   size_t len;
1182   gboolean status;
1183 
1184   g_return_val_if_fail (filename != NULL, FALSE);
1185 
1186   if (!g_file_get_contents (filename, &buf, &len, error)) {
1187     return FALSE;
1188   }
1189 
1190   status = o_picture_set_from_buffer (toplevel, object, filename,
1191                                       buf, len, error);
1192   g_free (buf);
1193   return status;
1194 }
1195 
1196 /*! \brief Get a picture's corresponding filename.
1197  * \par Function Description
1198  * Returns the filename associated with the picture \a object.
1199  *
1200  * \param toplevel The current #TOPLEVEL.
1201  * \param object   The picture #OBJECT to inspect.
1202  * \return the filename associated with \a object.
1203  */
1204 const gchar *
o_picture_get_filename(TOPLEVEL * toplevel,OBJECT * object)1205 o_picture_get_filename (TOPLEVEL *toplevel, OBJECT *object)
1206 {
1207   g_return_val_if_fail (object != NULL, NULL);
1208   g_return_val_if_fail (object->picture != NULL, NULL);
1209 
1210   return object->picture->filename;
1211 }
1212 
1213 /*! \brief Get fallback pixbuf for displaying pictures.
1214  * \par Function Description
1215  * Returns a pixbuf containing the fallback image to be used if a
1216  * picture object fails to load.  The returned pixbuf should be freed
1217  * with g_object_unref() when no longer needed.
1218  *
1219  * \return a #GdkPixbuf containing a warning image.
1220  */
1221 GdkPixbuf *
o_picture_get_fallback_pixbuf(TOPLEVEL * toplevel)1222 o_picture_get_fallback_pixbuf (TOPLEVEL *toplevel)
1223 {
1224   static GdkPixbuf *pixbuf = NULL;
1225   static gboolean failed = FALSE;
1226 
1227   if (pixbuf == NULL && !failed) {
1228     gchar *filename;
1229     GError *error = NULL;
1230 
1231     filename = g_build_filename (toplevel->bitmap_directory,
1232                                  "gschem-warning.png", NULL);
1233     pixbuf = gdk_pixbuf_new_from_file (filename, &error);
1234 
1235     if (pixbuf == NULL) {
1236       g_warning ( _("Failed to load fallback image %s: %s.\n"),
1237                   filename, error->message);
1238       g_error_free (error);
1239       failed = TRUE;
1240     }
1241     g_free (filename);
1242   }
1243 
1244   if (failed) return NULL;
1245 
1246   g_assert (GDK_IS_PIXBUF (pixbuf));
1247   return g_object_ref (pixbuf);
1248 }
1249