libgeda: Remove some exit() calls and assertions.
[geda-gaf/peter-b.git] / libgeda / src / o_picture.c
blob82a5bac43c3d7f87642097fd3b79a6c0eb85c249
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)
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.
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.
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
21 /*! \file o_picture.c
22 * \brief functions for the picture object
25 #include <config.h>
27 #include <stdio.h>
28 #ifdef HAVE_STRING_H
29 #include <string.h>
30 #endif
31 #include <math.h>
33 #include <gdk-pixbuf/gdk-pixbuf.h>
34 #include <gio/gio.h>
36 #include "libgeda_priv.h"
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.
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.
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)
56 OBJECT *new_obj;
57 int x1, y1;
58 int width, height, angle;
59 int mirrored, embedded;
60 int num_conv;
61 gchar type;
62 gchar *line = NULL;
63 gchar *filename;
64 gchar *file_content = NULL;
65 guint file_length = 0;
67 num_conv = sscanf(first_line, "%c %d %d %d %d %d %d %d\n",
68 &type, &x1, &y1, &width, &height, &angle, &mirrored, &embedded);
70 if (num_conv != 8) {
71 s_log_message (_("Error reading picture definition line: %s.\n"),
72 first_line);
75 if (width == 0 || height == 0) {
76 s_log_message(_("Found a zero width/height picture [ %c %d %d %d %d ]\n"),
77 type, x1, y1, width, height);
80 if ( (mirrored > 1) || (mirrored < 0)) {
81 s_log_message(_("Found a picture with a wrong 'mirrored' parameter: %d.\n"),
82 mirrored);
83 s_log_message(_("Setting mirrored to 0\n"));
84 mirrored = 0;
87 if ( (embedded > 1) || (embedded < 0)) {
88 s_log_message(_("Found a picture with a wrong 'embedded' parameter: %d.\n"),
89 embedded);
90 s_log_message(_("Setting embedded to 0\n"));
91 embedded = 0;
94 switch(angle) {
96 case(0):
97 case(90):
98 case(180):
99 case(270):
100 break;
102 default:
103 s_log_message(_("Found an unsupported picture angle [ %d ]\n"), angle);
104 s_log_message(_("Setting angle to 0\n"));
105 angle=0;
106 break;
110 filename = g_strdup(s_textbuffer_next_line(tb));
111 filename = remove_last_nl(filename);
113 /* Handle empty filenames */
114 if (strlen (filename) == 0) {
115 s_log_message (_("Found an image with no filename."));
116 g_free (filename);
119 if (embedded == 1) {
120 GString *encoded_picture=g_string_new("");
121 char finished = 0;
123 /* Read the encoded picture */
124 do {
126 line = s_textbuffer_next_line(tb);
127 if (line == NULL) break;
129 if (g_strcasecmp(line, ".\n") != 0) {
130 encoded_picture = g_string_append (encoded_picture, line);
131 } else {
132 finished = 1;
134 } while (finished == 0);
136 /* Decode the picture */
137 file_content = s_encoding_base64_decode(encoded_picture->str,
138 encoded_picture->len,
139 &file_length);
140 if (encoded_picture != NULL) {
141 g_string_free (encoded_picture, TRUE);
144 if (file_content == NULL) {
145 s_log_message (_("Failed to load image from embedded data [%s]: %s\n"),
146 filename, _("Base64 decoding failed."));
147 s_log_message (_("Falling back to file loading. Picture unembedded.\n"));
148 embedded = 0;
152 /* create the picture */
153 /* The picture is described by its upper left and lower right corner */
154 new_obj = o_picture_new (toplevel, file_content, file_length, filename,
155 type,
156 x1, y1+height, x1+width, y1,
157 angle, mirrored, embedded);
159 g_free (file_content);
160 g_free (filename);
162 return new_obj;
166 /*! \brief Create a character string representation of a picture OBJECT.
167 * \par Function Description
168 * This function formats a string in the buffer <B>*buff</B> to describe
169 * the picture object <B>*object</B>.
171 * \param [in] toplevel a TOPLEVEL structure
172 * \param [in] object Picture OBJECT to create string from.
173 * \return A pointer to the picture OBJECT character string.
175 * \note
176 * Caller must g_free returned character string.
179 char *o_picture_save(TOPLEVEL *toplevel, OBJECT *object)
181 int width, height, x1, y1;
182 gchar *encoded_picture=NULL;
183 gchar *out=NULL;
184 guint encoded_picture_length;
185 const gchar *filename = NULL;
187 /* calculate the width and height of the box */
188 width = abs(object->picture->lower_x - object->picture->upper_x);
189 height = abs(object->picture->upper_y - object->picture->lower_y);
191 /* calculate the lower left corner of the box */
192 x1 = object->picture->upper_x;
193 y1 = object->picture->upper_y - height; /* move the origin to 0, 0*/
195 #if DEBUG
196 printf("picture: %d %d %d %d\n", x1, y1, width, height);
197 #endif
199 /* Encode the picture if it's embedded */
200 if (o_picture_is_embedded (toplevel, object)) {
201 encoded_picture =
202 s_encoding_base64_encode( (char *)object->picture->file_content,
203 object->picture->file_length,
204 &encoded_picture_length,
205 TRUE);
206 if (encoded_picture == NULL) {
207 s_log_message(_("ERROR: o_picture_save: unable to encode the picture.\n"));
211 /* Cope with null filename */
212 filename = o_picture_get_filename (toplevel, object);
213 if (filename == NULL) filename = "";
215 if (o_picture_is_embedded (toplevel, object) &&
216 encoded_picture != NULL) {
217 out = g_strdup_printf("%c %d %d %d %d %d %c %c\n%s\n%s\n%s",
218 object->type,
219 x1, y1, width, height,
220 object->picture->angle,
221 /* Convert the (0,1) chars to ASCII */
222 (object->picture->mirrored)+0x30,
223 '1',
224 filename,
225 encoded_picture,
226 ".");
228 else {
229 out = g_strdup_printf("%c %d %d %d %d %d %c %c\n%s",
230 object->type,
231 x1, y1, width, height,
232 object->picture->angle,
233 /* Convert the (0,1) chars to ASCII */
234 (object->picture->mirrored)+0x30,
235 '0',
236 filename);
238 g_free(encoded_picture);
240 return(out);
244 /*! \brief Create a picture object.
245 * \par Function Description
246 * This function creates a new object representing a picture.
248 * The picture is described by its upper left corner (\a x1, \a y1)
249 * and its lower right corner (\a x2, \ay2). The \a type parameter
250 * must be equal to #OBJ_PICTURE.
252 * If \a file_content is non-NULL, it must be a pointer to a buffer
253 * containing raw image data. If loading data from \a file_content
254 * is unsuccessful, and \a filename is non-NULL, an image will
255 * attempt to be loaded from \a filename. Otherwise, the picture
256 * object will be initially empty.
258 * \param [in] toplevel The TOPLEVEL object.
259 * \param [in] file_content Raw data of the image file, or NULL.
260 * \param [in] file_length Length of raw data buffer
261 * \param [in] filename File name backing this picture, or NULL.
262 * \param [in] type Must be OBJ_PICTURE.
263 * \param [in] x1 Upper x coordinate.
264 * \param [in] y1 Upper y coordinate.
265 * \param [in] x2 Lower x coordinate.
266 * \param [in] y2 Lower y coordinate.
267 * \param [in] angle Picture rotation angle.
268 * \param [in] mirrored Whether the image should be mirrored or not.
269 * \param [in] embedded Whether the embedded flag should be set or not.
270 * \return A pointer to a new picture #OBJECT.
272 OBJECT *o_picture_new (TOPLEVEL *toplevel,
273 const gchar *file_content, gsize file_length,
274 const gchar *filename,
275 char type, int x1, int y1, int x2, int y2, int angle,
276 int mirrored, int embedded)
278 OBJECT *new_node;
279 PICTURE *picture;
281 /* create the object */
282 new_node = s_basic_new_object(type, "picture");
284 picture = (PICTURE *) g_malloc0 (sizeof(PICTURE));
285 new_node->picture = picture;
287 /* describe the picture with its upper left and lower right corner */
288 picture->upper_x = (x1 > x2) ? x2 : x1;
289 picture->upper_y = (y1 > y2) ? y1 : y2;
290 picture->lower_x = (x1 > x2) ? x1 : x2;
291 picture->lower_y = (y1 > y2) ? y2 : y1;
293 picture->pixbuf = NULL;
294 picture->file_content = NULL;
295 picture->file_length = 0;
297 picture->ratio = abs ((double) (x1 - x2) / (y1 - y2));
298 picture->filename = g_strdup (filename);
299 picture->angle = angle;
300 picture->mirrored = mirrored;
301 picture->embedded = embedded;
303 if (file_content != NULL) {
304 GError *error = NULL;
305 if (!o_picture_set_from_buffer (toplevel, new_node, filename,
306 file_content, file_length, &error)) {
307 s_log_message (_("Failed to load buffer image [%s]: %s\n"),
308 filename, error->message);
309 g_error_free (error);
311 /* Force the data into the object anyway, so as to prevent data
312 * loss of embedded images. */
313 picture->file_content = g_memdup (file_content, file_length);
314 picture->file_length = file_length;
317 if (picture->pixbuf == NULL && filename != NULL) {
318 GError *error = NULL;
319 if (!o_picture_set_from_file (toplevel, new_node, filename, &error)) {
320 s_log_message (_("Failed to load image from [%s]: %s\n"),
321 filename, error->message);
322 g_error_free (error);
326 /* compute the bounding picture */
327 o_picture_recalc(toplevel, new_node);
329 return new_node;
332 /*! \brief Recalculate picture bounding box.
333 * \par Function Description
334 * This function recalculates the bounding box of the <B>o_current</B>
335 * parameter picture object.
337 * \param [in] toplevel The TOPLEVEL object.
338 * \param [in,out] o_current Picture OBJECT to be recalculated.
340 void o_picture_recalc(TOPLEVEL *toplevel, OBJECT *o_current)
342 int left, top, right, bottom;
344 if (o_current->picture == NULL) {
345 return;
348 /* update the bounding picture - world units */
349 world_get_picture_bounds(toplevel, o_current,
350 &left, &top, &right, &bottom);
351 o_current->w_left = left;
352 o_current->w_top = top;
353 o_current->w_right = right;
354 o_current->w_bottom = bottom;
355 o_current->w_bounds_valid = TRUE;
358 /*! \brief Get picture bounding rectangle in WORLD coordinates.
359 * \par Function Description
360 * This function sets the <B>left</B>, <B>top</B>, <B>right</B> and
361 * <B>bottom</B> parameters to the boundings of the picture object
362 * described in <B>*picture</B> in WORLD units.
364 * \param [in] toplevel The TOPLEVEL object.
365 * \param [in] object Picture OBJECT to read coordinates from.
366 * \param [out] left Left picture coordinate in WORLD units.
367 * \param [out] top Top picture coordinate in WORLD units.
368 * \param [out] right Right picture coordinate in WORLD units.
369 * \param [out] bottom Bottom picture coordinate in WORLD units.
371 void world_get_picture_bounds(TOPLEVEL *toplevel, OBJECT *object,
372 int *left, int *top, int *right, int *bottom)
374 *left = min(object->picture->upper_x, object->picture->lower_x);
375 *top = min(object->picture->upper_y, object->picture->lower_y);
376 *right = max(object->picture->upper_x, object->picture->lower_x);
377 *bottom = max(object->picture->upper_y, object->picture->lower_y);
381 /*! \brief get the position of the left bottom point
382 * \par Function Description
383 * This function gets the position of the bottom left point of a picture object.
385 * \param [in] toplevel The toplevel environment.
386 * \param [out] x pointer to the x-position
387 * \param [out] y pointer to the y-position
388 * \param [in] object The object to get the position.
389 * \return TRUE if successfully determined the position, FALSE otherwise
391 gboolean o_picture_get_position (TOPLEVEL *toplevel, gint *x, gint *y,
392 OBJECT *object)
394 *x = min(object->picture->lower_x, object->picture->upper_x);
395 *y = min(object->picture->lower_y, object->picture->upper_y);
396 return TRUE;
400 /*! \brief Get the width/height ratio of an image.
401 * \par Function Description
403 * Returns the width/height ratio of picture \a object, taking the
404 * image rotation into account.
406 * \param toplevel The current #TOPLEVEL.
407 * \param object Picture #OBJECT to inspect.
408 * \return width/height ratio for \a object.
410 double
411 o_picture_get_ratio (TOPLEVEL *toplevel, OBJECT *object)
413 g_return_val_if_fail (object != NULL, 1);
414 g_return_val_if_fail (object->picture != NULL, 1);
416 /* The effective ratio varies depending on the rotation of the
417 * image. */
418 switch (object->picture->angle) {
419 case 0:
420 case 180:
421 return object->picture->ratio;
422 case 90:
423 case 270:
424 return 1.0 / object->picture->ratio;
425 default:
426 g_critical (_("Picture %p has invalid angle %i\n"), object,
427 object->picture->angle);
429 return 0;
432 /*! \brief Modify the description of a picture OBJECT.
433 * \par Function Description
434 * This function modifies the coordinates of one of the four corner of
435 * the picture. The new coordinates of the corner identified by
436 * <B>whichone</B> are given by <B>x</B> and <B>y</B> in world unit.
438 * The coordinates of the corner is modified in the world coordinate system.
439 * Screen coordinates and boundings are then updated.
441 * \param [in] toplevel The TOPLEVEL object.
442 * \param [in,out] object Picture OBJECT to modify.
443 * \param [in] x New x coordinate.
444 * \param [in] y New y coordinate.
445 * \param [in] whichone Which picture parameter to modify.
447 * <B>whichone</B> can have the following values:
448 * <DL>
449 * <DT>*</DT><DD>PICTURE_UPPER_LEFT
450 * <DT>*</DT><DD>PICTURE_LOWER_LEFT
451 * <DT>*</DT><DD>PICTURE_UPPER_RIGHT
452 * <DT>*</DT><DD>PICTURE_LOWER_RIGHT
453 * </DL>
455 void o_picture_modify(TOPLEVEL *toplevel, OBJECT *object,
456 int x, int y, int whichone)
458 int tmp;
459 double ratio = o_picture_get_ratio (toplevel, object);
461 o_emit_pre_change_notify (toplevel, object);
463 /* change the position of the selected corner */
464 switch(whichone) {
465 case PICTURE_UPPER_LEFT:
466 object->picture->upper_x = x;
467 tmp = abs(object->picture->upper_x - object->picture->lower_x) / ratio;
468 if (y < object->picture->lower_y) {
469 tmp = -tmp;
471 object->picture->upper_y = object->picture->lower_y + tmp;
472 break;
474 case PICTURE_LOWER_LEFT:
475 object->picture->upper_x = x;
476 tmp = abs(object->picture->upper_x - object->picture->lower_x) / ratio;
477 if (y > object->picture->upper_y) {
478 tmp = -tmp;
480 object->picture->lower_y = object->picture->upper_y - tmp;
481 break;
483 case PICTURE_UPPER_RIGHT:
484 object->picture->lower_x = x;
485 tmp = abs(object->picture->upper_x - object->picture->lower_x) / ratio;
486 if (y < object->picture->lower_y) {
487 tmp = -tmp;
489 object->picture->upper_y = object->picture->lower_y + tmp;
490 break;
492 case PICTURE_LOWER_RIGHT:
493 object->picture->lower_x = x;
494 tmp = abs(object->picture->upper_x - object->picture->lower_x) / ratio;
495 if (y > object->picture->upper_y) {
496 tmp = -tmp;
498 object->picture->lower_y = object->picture->upper_y - tmp;
499 break;
501 default:
502 return;
505 /* need to update the upper left and lower right corners */
506 if(object->picture->upper_x > object->picture->lower_x) {
507 tmp = object->picture->upper_x;
508 object->picture->upper_x = object->picture->lower_x;
509 object->picture->lower_x = tmp;
512 if(object->picture->upper_y < object->picture->lower_y) {
513 tmp = object->picture->upper_y;
514 object->picture->upper_y = object->picture->lower_y;
515 object->picture->lower_y = tmp;
518 /* recalculate the screen coords and the boundings */
519 o_picture_recalc(toplevel, object);
520 o_emit_change_notify (toplevel, object);
523 /*! \brief Modify a picture object's coordinates.
524 * \par Function Description
525 * Modifies the coordinates of all four corners of a picture \a
526 * object. The picture is adjusted to fit the rectangle enclosed by
527 * the points (\a x1, \a y1) and (\a x2, \a y2), and scaled as large
528 * as possible to still fit within that rectangle.
530 * \param [in] toplevel current #TOPLEVEL.
531 * \param [in,out] object picture #OBJECT to be modified.
532 * \param [in] x1 x coordinate of first corner of box.
533 * \param [in] y1 y coordinate of first corner of box.
534 * \param [in] x2 x coordinate of second corner of box.
535 * \param [in] y2 y coordinate of second corner of box.
537 void
538 o_picture_modify_all (TOPLEVEL *toplevel, OBJECT *object,
539 int x1, int y1, int x2, int y2)
541 o_emit_pre_change_notify (toplevel, object);
543 /* Normalise the requested rectangle. */
544 object->picture->lower_x = (x1 > x2) ? x1 : x2;
545 object->picture->lower_y = (y1 > y2) ? y2 : y1;
546 object->picture->upper_x = (x1 > x2) ? x2 : x1;
547 object->picture->upper_y = (y1 > y2) ? y1 : y2;
549 /* recalculate the world coords and bounds */
550 o_box_recalc(toplevel, object);
551 o_emit_change_notify (toplevel, object);
554 /*! \brief Rotate picture OBJECT using WORLD coordinates.
555 * \par Function Description
556 * This function rotates the picture described by <B>*object</B> around
557 * the (<B>world_centerx</B>, <B>world_centery</B>) point by <B>angle</B>
558 * degrees.
559 * The center of rotation is in world units.
561 * \param [in] toplevel The TOPLEVEL object.
562 * \param [in] world_centerx Rotation center x coordinate in
563 * WORLD units.
564 * \param [in] world_centery Rotation center y coordinate in
565 * WORLD units.
566 * \param [in] angle Rotation angle in degrees (See note below).
567 * \param [in,out] object Picture OBJECT to rotate.
569 void o_picture_rotate_world(TOPLEVEL *toplevel,
570 int world_centerx, int world_centery, int angle,
571 OBJECT *object)
573 int newx1, newy1;
574 int newx2, newy2;
576 /* Only 90 degree multiple and positive angles are allowed. */
577 /* angle must be positive */
578 if(angle < 0) angle = -angle;
579 /* angle must be a 90 multiple or no rotation performed */
580 if((angle % 90) != 0) return;
582 object->picture->angle = ( object->picture->angle + angle ) % 360;
584 /* The center of rotation (<B>world_centerx</B>, <B>world_centery</B>) is
585 * translated to the origin. The rotation of the upper left and lower
586 * right corner are then performed. Finally, the rotated picture is
587 * translated back to its previous location.
589 /* translate object to origin */
590 object->picture->upper_x -= world_centerx;
591 object->picture->upper_y -= world_centery;
592 object->picture->lower_x -= world_centerx;
593 object->picture->lower_y -= world_centery;
595 /* rotate the upper left corner of the picture */
596 rotate_point_90(object->picture->upper_x, object->picture->upper_y, angle,
597 &newx1, &newy1);
599 /* rotate the lower left corner of the picture */
600 rotate_point_90(object->picture->lower_x, object->picture->lower_y, angle,
601 &newx2, &newy2);
603 /* reorder the corners after rotation */
604 object->picture->upper_x = min(newx1,newx2);
605 object->picture->upper_y = max(newy1,newy2);
606 object->picture->lower_x = max(newx1,newx2);
607 object->picture->lower_y = min(newy1,newy2);
609 /* translate object back to normal position */
610 object->picture->upper_x += world_centerx;
611 object->picture->upper_y += world_centery;
612 object->picture->lower_x += world_centerx;
613 object->picture->lower_y += world_centery;
615 /* recalc boundings and screen coords */
616 o_picture_recalc(toplevel, object);
620 /*! \brief Mirror a picture using WORLD coordinates.
621 * \par Function Description
622 * This function mirrors the picture from the point
623 * (<B>world_centerx</B>,<B>world_centery</B>) in world unit.
625 * The picture is first translated to the origin, then mirrored and
626 * finally translated back at its previous position.
628 * \param [in] toplevel The TOPLEVEL object.
629 * \param [in] world_centerx Origin x coordinate in WORLD units.
630 * \param [in] world_centery Origin y coordinate in WORLD units.
631 * \param [in,out] object Picture OBJECT to mirror.
633 void o_picture_mirror_world(TOPLEVEL *toplevel,
634 int world_centerx, int world_centery,
635 OBJECT *object)
637 int newx1, newy1;
638 int newx2, newy2;
640 /* Set info in object. Sometimes it's necessary to change the
641 * rotation angle as well as the mirror flag. */
642 object->picture->mirrored = !object->picture->mirrored;
643 switch (object->picture->angle) {
644 case 90:
645 object->picture->angle = 270;
646 break;
647 case 270:
648 object->picture->angle = 90;
649 break;
652 /* translate object to origin */
653 object->picture->upper_x -= world_centerx;
654 object->picture->upper_y -= world_centery;
655 object->picture->lower_x -= world_centerx;
656 object->picture->lower_y -= world_centery;
658 /* mirror the corners */
659 newx1 = -object->picture->upper_x;
660 newy1 = object->picture->upper_y;
661 newx2 = -object->picture->lower_x;
662 newy2 = object->picture->lower_y;
664 /* reorder the corners */
665 object->picture->upper_x = min(newx1,newx2);
666 object->picture->upper_y = max(newy1,newy2);
667 object->picture->lower_x = max(newx1,newx2);
668 object->picture->lower_y = min(newy1,newy2);
670 /* translate back in position */
671 object->picture->upper_x += world_centerx;
672 object->picture->upper_y += world_centery;
673 object->picture->lower_x += world_centerx;
674 object->picture->lower_y += world_centery;
676 /* recalc boundings and screen coords */
677 o_picture_recalc(toplevel, object);
681 /*! \brief Translate a picture position in WORLD coordinates by a delta.
682 * \par Function Description
683 * This function applies a translation of (<B>x1</B>,<B>y1</B>) to the picture
684 * described by <B>*object</B>. <B>x1</B> and <B>y1</B> are in world units.
686 * \param [in] toplevel The TOPLEVEL object.
687 * \param [in] dx x distance to move.
688 * \param [in] dy y distance to move.
689 * \param [in,out] object Picture OBJECT to translate.
691 void o_picture_translate_world(TOPLEVEL *toplevel,
692 int dx, int dy, OBJECT *object)
694 if (object == NULL) printf("btw NO!\n");
696 /* Do world coords */
697 object->picture->upper_x = object->picture->upper_x + dx;
698 object->picture->upper_y = object->picture->upper_y + dy;
699 object->picture->lower_x = object->picture->lower_x + dx;
700 object->picture->lower_y = object->picture->lower_y + dy;
702 /* recalc the screen coords and the bounding picture */
703 o_picture_recalc(toplevel, object);
706 /*! \brief Create a copy of a picture.
707 * \par Function Description
708 * This function creates a verbatim copy of the object pointed by
709 * <B>o_current</B> describing a picture.
711 * \param [in] toplevel The TOPLEVEL object.
712 * \param [in] object Picture OBJECT to copy.
713 * \return The new OBJECT
715 OBJECT *o_picture_copy(TOPLEVEL *toplevel, OBJECT *object)
717 OBJECT *new_node;
718 PICTURE *picture;
720 /* create the object */
721 new_node = s_basic_new_object(object->type, "picture");
723 picture = g_malloc(sizeof(PICTURE));
724 new_node->picture = picture;
726 new_node->color = object->color;
727 new_node->selectable = object->selectable;
729 /* describe the picture with its upper left and lower right corner */
730 picture->upper_x = object->picture->upper_x;
731 picture->upper_y = object->picture->upper_y;
732 picture->lower_x = object->picture->lower_x;
733 picture->lower_y = object->picture->lower_y;
735 if (object->picture->file_content != NULL) {
736 picture->file_content = g_memdup (object->picture->file_content,
737 object->picture->file_length);
738 } else {
739 picture->file_content = NULL;
742 picture->file_length = object->picture->file_length;
743 picture->filename = g_strdup (object->picture->filename);
744 picture->ratio = object->picture->ratio;
745 picture->angle = object->picture->angle;
746 picture->mirrored = object->picture->mirrored;
747 picture->embedded = object->picture->embedded;
749 /* Get the picture data */
750 picture->pixbuf = o_picture_get_pixbuf (toplevel, object);
752 /* compute the bounding picture */
753 o_picture_recalc(toplevel, new_node);
755 return new_node;
759 /*! \brief Get RGB data from image.
760 * \par Function Description
761 * This function returns the RGB data of the given image.
762 * Function taken from the DIA source code (http://www.gnome.org/projects/dia)
763 * and licensed under the GNU GPL version 2.
765 * \param [in] image GdkPixbuf image to read RGB data from.
766 * \return Array of rgb data from image.
768 * \note
769 * Caller must g_free returned guint8 array.
771 static guint8 *
772 o_picture_rgb_data(GdkPixbuf *image)
774 int width = gdk_pixbuf_get_width(image);
775 int height = gdk_pixbuf_get_height(image);
776 int rowstride = gdk_pixbuf_get_rowstride(image);
777 int size = height*rowstride;
778 guint8 *rgb_pixels = g_malloc(size);
780 if (gdk_pixbuf_get_has_alpha(image)) {
781 guint8 *pixels = gdk_pixbuf_get_pixels(image);
782 int i, j;
783 for (i = 0; i < height; i++) {
784 for (j = 0; j < width; j++) {
785 rgb_pixels[i*rowstride+j*3] = pixels[i*rowstride+j*4];
786 rgb_pixels[i*rowstride+j*3+1] = pixels[i*rowstride+j*4+1];
787 rgb_pixels[i*rowstride+j*3+2] = pixels[i*rowstride+j*4+2];
790 return rgb_pixels;
791 } else {
792 guint8 *pixels = gdk_pixbuf_get_pixels(image);
794 g_memmove(rgb_pixels, pixels, height*rowstride);
795 return rgb_pixels;
799 /*! \brief Get mask data from image.
800 * \par Function Description
801 * This function returns the mask data of the given image.
802 * Function taken from the DIA source code (http://www.gnome.org/projects/dia)
803 * and licensed under the GNU GPL version 2.
805 * \param [in] image GdkPixbuf image to get mask data from.
806 * \return Array of mask data from image.
808 * \note
809 * Caller must g_free returned guint8 array.
811 static guint8 *
812 o_picture_mask_data(GdkPixbuf *image)
814 guint8 *pixels;
815 guint8 *mask;
816 int i, size;
818 if (!gdk_pixbuf_get_has_alpha(image)) {
819 return NULL;
822 pixels = gdk_pixbuf_get_pixels(image);
824 size = gdk_pixbuf_get_width(image)*
825 gdk_pixbuf_get_height(image);
827 mask = g_malloc(size);
829 /* Pick every fourth byte (the alpha channel) into mask */
830 for (i = 0; i < size; i++)
831 mask[i] = pixels[i*4+3];
833 return mask;
836 /*! \brief Print picture to Postscript document.
837 * \par Function Description
838 * This function prints a picture object. The picture is defined by the
839 * coordinates of its upper left corner in (<B>x</B>,<B>y</B>) and its width
840 * and height given by the <B>width</B> and <B>height</B> parameters.
842 * If the picture object was unable to be loaded, prints a crossed
843 * box of the same dimensions.
845 * The Postscript document is defined by the file pointer <B>fp</B>.
846 * Function based on the DIA source code (http://www.gnome.org/projects/dia)
847 * and licensed under the GNU GPL version 2.
849 * All dimensions are in mils.
851 * \param [in] toplevel The TOPLEVEL object.
852 * \param [in] fp FILE pointer to Postscript document.
853 * \param [in] o_current Picture OBJECT to write to document.
854 * \param [in] origin_x Page x coordinate to place picture OBJECT.
855 * \param [in] origin_y Page y coordinate to place picture OBJECT.
857 void o_picture_print(TOPLEVEL *toplevel, FILE *fp, OBJECT *o_current,
858 int origin_x, int origin_y)
860 int x1, y1, x, y;
861 int height, width;
862 GdkPixbuf* image = o_picture_get_pixbuf (toplevel, o_current);
863 int img_width, img_height, img_rowstride;
864 guint8 *rgb_data;
865 guint8 *mask_data;
867 /* calculate the width and height of the box */
868 width = abs(o_current->picture->lower_x - o_current->picture->upper_x);
869 height = abs(o_current->picture->upper_y - o_current->picture->lower_y);
871 /* calculate the origin of the box */
872 x1 = o_current->picture->upper_x;
873 y1 = o_current->picture->upper_y;
875 /* If the image failed to load, try to get hold of the fallback
876 * pixbuf. */
877 if (image == NULL) image = o_picture_get_fallback_pixbuf (toplevel);
878 /* If the image failed to load, draw a box in the default color with a
879 * cross in it. */
880 if (image == NULL) {
881 int line_width = (toplevel->line_style == THICK) ? LINE_WIDTH : 2;
882 o_box_print_solid (toplevel, fp, x1, y1, width, height,
883 DEFAULT_COLOR, line_width, -1, -1, -1, -1);
884 o_line_print_solid (toplevel, fp, x1, y1, x1+width, y1+height,
885 DEFAULT_COLOR, line_width, -1, -1, -1, -1);
886 o_line_print_solid (toplevel, fp, x1+width, y1, x1, y1+height,
887 DEFAULT_COLOR, line_width, -1, -1, -1, -1);
888 return;
891 img_width = gdk_pixbuf_get_width(image);
892 img_rowstride = gdk_pixbuf_get_rowstride(image);
893 img_height = gdk_pixbuf_get_height(image);
895 rgb_data = o_picture_rgb_data(image);
896 mask_data = o_picture_mask_data(image);
898 fprintf(fp, "gsave\n");
900 /* color output only */
901 fprintf(fp, "/pix %i string def\n", img_width * 3);
902 fprintf(fp, "%i %i 8\n", img_width, img_height);
903 fprintf(fp, "%i %i translate\n", x1, y1);
904 fprintf(fp, "%i %i scale\n", width, height);
905 fprintf(fp, "[%i 0 0 -%i 0 0]\n", img_width, img_height);
907 fprintf(fp, "{currentfile pix readhexstring pop}\n");
908 fprintf(fp, "false 3 colorimage\n");
909 fprintf(fp, "\n");
911 if (mask_data) {
912 for (y = 0; y < img_height; y++) {
913 for (x = 0; x < img_width; x++) {
914 int i = y*img_rowstride+x*3;
915 int m = y*img_width+x;
916 fprintf(fp, "%02x", 255-(mask_data[m]*(255-rgb_data[i])/255));
917 fprintf(fp, "%02x", 255-(mask_data[m]*(255-rgb_data[i+1])/255));
918 fprintf(fp, "%02x", 255-(mask_data[m]*(255-rgb_data[i+2])/255));
920 fprintf(fp, "\n");
922 } else {
923 for (y = 0; y < img_height; y++) {
924 for (x = 0; x < img_width; x++) {
925 int i = y*img_rowstride+x*3;
926 fprintf(fp, "%02x", (int)(rgb_data[i]));
927 fprintf(fp, "%02x", (int)(rgb_data[i+1]));
928 fprintf(fp, "%02x", (int)(rgb_data[i+2]));
930 fprintf(fp, "\n");
933 fprintf(fp, "grestore\n");
934 fprintf(fp, "\n");
936 g_object_unref (image);
937 g_free(rgb_data);
938 g_free(mask_data);
942 /*! \brief Embed the image file associated with a picture
943 * \par Function Description
944 * Verify that a picture has valid data associated with it, and if so,
945 * mark it to be embedded.
947 * \param [in] toplevel The TOPLEVEL object.
948 * \param [in] object The picture OBJECT to embed
950 void o_picture_embed (TOPLEVEL *toplevel, OBJECT *object)
952 const gchar *filename = o_picture_get_filename (toplevel, object);
953 gchar *basename;
955 if (o_picture_is_embedded (toplevel, object)) return;
957 if (object->picture->file_content == NULL) {
958 s_log_message (_("Picture [%s] has no image data.\n"), filename);
959 s_log_message (_("Falling back to file loading. Picture is still unembedded.\n"));
960 object->picture->embedded = 0;
961 return;
964 object->picture->embedded = 1;
966 basename = g_path_get_basename (filename);
967 s_log_message (_("Picture [%s] has been embedded\n"), basename);
968 g_free (basename);
972 /*! \brief Unembed a picture, reloading the image from disk
973 * \par Function Description
974 * Verify that the file associated with \a object exists on disk and
975 * is usable, and if so, reload the picture and mark it as unembedded.
977 * \param [in] toplevel The TOPLEVEL object.
978 * \param [in] object The picture OBJECT to unembed
980 void o_picture_unembed (TOPLEVEL *toplevel, OBJECT *object)
982 GError *err = NULL;
983 const gchar *filename = o_picture_get_filename (toplevel, object);
984 gchar *basename;
986 if (!o_picture_is_embedded (toplevel, object)) return;
988 o_picture_set_from_file (toplevel, object, filename, &err);
990 if (err != NULL) {
991 s_log_message (_("Failed to load image from file [%s]: %s\n"),
992 filename, err->message);
993 s_log_message (_("Picture is still embedded.\n"));
994 g_error_free (err);
995 return;
998 object->picture->embedded = 0;
1000 basename = g_path_get_basename(filename);
1001 s_log_message (_("Picture [%s] has been unembedded\n"), basename);
1002 g_free(basename);
1005 /*! \brief Calculates the distance between the given point and the closest
1006 * point in the picture.
1008 * Interrior points within the picture return a distance of zero.
1010 * \param [in] object The picture OBJECT.
1011 * \param [in] x The x coordinate of the given point.
1012 * \param [in] y The y coordinate of the given point.
1013 * \param [in] force_solid If true, force treating the object as solid.
1014 * \return The shortest distance from the object to the point. With an
1015 * invalid parameter, this function returns G_MAXDOUBLE.
1017 double o_picture_shortest_distance (OBJECT *object, int x, int y,
1018 int force_solid)
1020 double dx, dy;
1021 double x1, y1, x2, y2;
1023 g_return_val_if_fail (object->picture != NULL, G_MAXDOUBLE);
1025 x1 = (double)min (object->picture->upper_x, object->picture->lower_x);
1026 y1 = (double)min (object->picture->upper_y, object->picture->lower_y);
1027 x2 = (double)max (object->picture->upper_x, object->picture->lower_x);
1028 y2 = (double)max (object->picture->upper_y, object->picture->lower_y);
1030 dx = min (((double)x) - x1, x2 - ((double)x));
1031 dy = min (((double)y) - y1, y2 - ((double)y));
1033 dx = min (dx, 0);
1034 dy = min (dy, 0);
1036 return sqrt ((dx * dx) + (dy * dy));
1039 /*! \brief Test whether a picture object is embedded.
1040 * \par Function Description
1041 * Returns TRUE if the picture \a object will have its data embedded
1042 * in a schematic or symbol file; returns FALSE if its data will be
1043 * obtained from a separate file.
1045 * \param toplevel The current #TOPLEVEL.
1046 * \param object The picture #OBJECT to inspect.
1047 * \return TRUE if \a object is embedded.
1049 gboolean
1050 o_picture_is_embedded (TOPLEVEL *toplevel, OBJECT *object)
1052 g_return_val_if_fail (object != NULL, FALSE);
1053 g_return_val_if_fail (object->picture != NULL, FALSE);
1055 return object->picture->embedded;
1058 /*! \brief Get a pixel buffer for a picture object.
1059 * \par Function Description
1060 * Returns a #GdkPixbuf for the picture object \a object, or NULL if
1061 * the picture could not be loaded.
1063 * The returned value should have its reference count decremented with
1064 * g_object_unref() when no longer needed.
1066 * \param toplevel The current #TOPLEVEL.
1067 * \param object The picture #OBJECT to inspect.
1068 * \return A #GdkPixbuf for the picture.
1070 GdkPixbuf *
1071 o_picture_get_pixbuf (TOPLEVEL *toplevel, OBJECT *object)
1073 g_return_val_if_fail (object != NULL, NULL);
1074 g_return_val_if_fail (object->picture != NULL, NULL);
1076 if (object->picture->pixbuf != NULL) {
1077 return g_object_ref (object->picture->pixbuf);
1078 } else {
1079 return NULL;
1083 /*! \brief Get the raw image data from a picture object.
1084 * \par Function Description
1085 * Returns the raw image file data underlying the picture \a object,
1086 * or NULL if the picture could not be loaded.
1088 * \param toplevel The current #TOPLEVEL.
1089 * \param object The picture #OBJECT to inspect.
1090 * \param len Location to store buffer length.
1091 * \return A read-only buffer of raw image data.
1093 const char *
1094 o_picture_get_data (TOPLEVEL *toplevel, OBJECT *object,
1095 size_t *len)
1097 g_return_val_if_fail (object != NULL, NULL);
1098 g_return_val_if_fail (object->picture != NULL, NULL);
1100 *len = object->picture->file_length;
1101 return object->picture->file_content;
1104 /*! \brief Set a picture object's contents from a buffer.
1105 * \par Function Description
1106 * Sets the contents of the picture \a object by reading image data
1107 * from a buffer. The buffer should be in on-disk format.
1109 * \param toplevel The current #TOPLEVEL.
1110 * \param object The picture #OBJECT to modify.
1111 * \param filename The new filename for the picture.
1112 * \param data The new image data buffer.
1113 * \param len The size of the data buffer.
1114 * \param error Location to return error information.
1115 * \return TRUE on success, FALSE on failure.
1117 gboolean
1118 o_picture_set_from_buffer (TOPLEVEL *toplevel, OBJECT *object,
1119 const gchar *filename,
1120 const gchar *data, size_t len,
1121 GError **error)
1123 GdkPixbuf *pixbuf;
1124 GInputStream *stream;
1125 gchar *tmp;
1127 g_return_val_if_fail (toplevel != NULL, FALSE);
1128 g_return_val_if_fail (object != NULL, FALSE);
1129 g_return_val_if_fail (object->picture != NULL, FALSE);
1130 g_return_val_if_fail (data != NULL, FALSE);
1132 /* Check that we can actually load the data before making any
1133 * changes to the object. */
1134 stream = G_INPUT_STREAM (g_memory_input_stream_new_from_data (data, len, NULL));
1135 pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, error);
1136 g_object_unref (stream);
1137 if (pixbuf == NULL) return FALSE;
1139 o_emit_pre_change_notify (toplevel, object);
1141 if (object->picture->pixbuf != NULL) {
1142 g_object_unref (object->picture->pixbuf);
1144 object->picture->pixbuf = pixbuf;
1146 object->picture->ratio = ((double) gdk_pixbuf_get_width(pixbuf) /
1147 gdk_pixbuf_get_height(pixbuf));
1149 tmp = g_strdup (filename);
1150 g_free (object->picture->filename);
1151 object->picture->filename = tmp;
1153 gchar *buf = g_realloc (object->picture->file_content,
1154 len);
1155 /* It's possible that these buffers might overlap, because the
1156 * library user hates us. */
1157 memmove (buf, data, len);
1158 object->picture->file_content = buf;
1159 object->picture->file_length = len;
1161 o_emit_change_notify (toplevel, object);
1162 return TRUE;
1165 /*! \brief Set a picture object's contents from a file.
1166 * \par Function Description
1167 * Sets the contents of the picture \a object by reading image data
1168 * from a file.
1170 * \param toplevel The current #TOPLEVEL.
1171 * \param object The picture #OBJECT to modify.
1172 * \param filename The filename to load image data from.
1173 * \param error Location to return error information.
1174 * \return TRUE on success, FALSE on failure.
1176 gboolean
1177 o_picture_set_from_file (TOPLEVEL *toplevel, OBJECT *object,
1178 const gchar *filename,
1179 GError **error)
1181 gchar *buf;
1182 size_t len;
1183 gboolean status;
1185 g_return_val_if_fail (filename != NULL, FALSE);
1187 if (!g_file_get_contents (filename, &buf, &len, error)) {
1188 return FALSE;
1191 status = o_picture_set_from_buffer (toplevel, object, filename,
1192 buf, len, error);
1193 g_free (buf);
1194 return status;
1197 /*! \brief Get a picture's corresponding filename.
1198 * \par Function Description
1199 * Returns the filename associated with the picture \a object.
1201 * \param toplevel The current #TOPLEVEL.
1202 * \param object The picture #OBJECT to inspect.
1203 * \return the filename associated with \a object.
1205 const gchar *
1206 o_picture_get_filename (TOPLEVEL *toplevel, OBJECT *object)
1208 g_return_val_if_fail (object != NULL, NULL);
1209 g_return_val_if_fail (object->picture != NULL, NULL);
1211 return object->picture->filename;
1214 /*! \brief Get fallback pixbuf for displaying pictures.
1215 * \par Function Description
1216 * Returns a pixbuf containing the fallback image to be used if a
1217 * picture object fails to load. The returned pixbuf should be freed
1218 * with g_object_unref() when no longer needed.
1220 * \return a #GdkPixbuf containing a warning image.
1222 GdkPixbuf *
1223 o_picture_get_fallback_pixbuf (TOPLEVEL *toplevel)
1225 static GdkPixbuf *pixbuf = NULL;
1226 static gboolean failed = FALSE;
1228 if (pixbuf == NULL && !failed) {
1229 gchar *filename;
1230 GError *error = NULL;
1232 filename = g_build_filename (toplevel->bitmap_directory,
1233 "gschem-warning.png", NULL);
1234 pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
1236 if (pixbuf == NULL) {
1237 g_warning ( _("Failed to load fallback image %s: %s.\n"),
1238 filename, error->message);
1239 g_error_free (error);
1240 failed = TRUE;
1242 g_free (filename);
1245 if (failed) return NULL;
1247 g_assert (GDK_IS_PIXBUF (pixbuf));
1248 return g_object_ref (pixbuf);