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
22 * \brief functions for the picture object
33 #include <gdk-pixbuf/gdk-pixbuf.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
,
53 unsigned int release_ver
,
54 unsigned int fileformat_ver
)
58 int width
, height
, angle
;
59 int mirrored
, embedded
;
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
);
71 s_log_message (_("Error reading picture definition line: %s.\n"),
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"),
83 s_log_message(_("Setting mirrored to 0\n"));
87 if ( (embedded
> 1) || (embedded
< 0)) {
88 s_log_message(_("Found a picture with a wrong 'embedded' parameter: %d.\n"),
90 s_log_message(_("Setting embedded to 0\n"));
103 s_log_message(_("Found an unsupported picture angle [ %d ]\n"), angle
);
104 s_log_message(_("Setting angle to 0\n"));
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."));
120 GString
*encoded_picture
=g_string_new("");
123 /* Read the encoded picture */
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
);
134 } while (finished
== 0);
136 /* Decode the picture */
137 file_content
= s_encoding_base64_decode(encoded_picture
->str
,
138 encoded_picture
->len
,
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"));
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
,
156 x1
, y1
+height
, x1
+width
, y1
,
157 angle
, mirrored
, embedded
);
159 g_free (file_content
);
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.
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
;
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*/
196 printf("picture: %d %d %d %d\n", x1
, y1
, width
, height
);
199 /* Encode the picture if it's embedded */
200 if (o_picture_is_embedded (toplevel
, object
)) {
202 s_encoding_base64_encode( (char *)object
->picture
->file_content
,
203 object
->picture
->file_length
,
204 &encoded_picture_length
,
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",
219 x1
, y1
, width
, height
,
220 object
->picture
->angle
,
221 /* Convert the (0,1) chars to ASCII */
222 (object
->picture
->mirrored
)+0x30,
229 out
= g_strdup_printf("%c %d %d %d %d %d %c %c\n%s",
231 x1
, y1
, width
, height
,
232 object
->picture
->angle
,
233 /* Convert the (0,1) chars to ASCII */
234 (object
->picture
->mirrored
)+0x30,
238 g_free(encoded_picture
);
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
)
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
);
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
) {
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
,
394 *x
= min(object
->picture
->lower_x
, object
->picture
->upper_x
);
395 *y
= min(object
->picture
->lower_y
, object
->picture
->upper_y
);
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.
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
418 switch (object
->picture
->angle
) {
421 return object
->picture
->ratio
;
424 return 1.0 / object
->picture
->ratio
;
426 g_critical (_("Picture %p has invalid angle %i\n"), object
,
427 object
->picture
->angle
);
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:
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
455 void o_picture_modify(TOPLEVEL
*toplevel
, OBJECT
*object
,
456 int x
, int y
, int whichone
)
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 */
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
) {
471 object
->picture
->upper_y
= object
->picture
->lower_y
+ tmp
;
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
) {
480 object
->picture
->lower_y
= object
->picture
->upper_y
- tmp
;
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
) {
489 object
->picture
->upper_y
= object
->picture
->lower_y
+ tmp
;
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
) {
498 object
->picture
->lower_y
= object
->picture
->upper_y
- tmp
;
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.
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>
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
564 * \param [in] world_centery Rotation center y coordinate in
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
,
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
,
599 /* rotate the lower left corner of the picture */
600 rotate_point_90(object
->picture
->lower_x
, object
->picture
->lower_y
, angle
,
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
,
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
) {
645 object
->picture
->angle
= 270;
648 object
->picture
->angle
= 90;
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
)
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
);
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
);
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.
769 * Caller must g_free returned guint8 array.
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
);
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];
792 guint8
*pixels
= gdk_pixbuf_get_pixels(image
);
794 g_memmove(rgb_pixels
, pixels
, height
*rowstride
);
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.
809 * Caller must g_free returned guint8 array.
812 o_picture_mask_data(GdkPixbuf
*image
)
818 if (!gdk_pixbuf_get_has_alpha(image
)) {
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];
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
)
862 GdkPixbuf
* image
= o_picture_get_pixbuf (toplevel
, o_current
);
863 int img_width
, img_height
, img_rowstride
;
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
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
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);
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");
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));
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]));
933 fprintf(fp
, "grestore\n");
936 g_object_unref (image
);
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
);
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;
964 object
->picture
->embedded
= 1;
966 basename
= g_path_get_basename (filename
);
967 s_log_message (_("Picture [%s] has been embedded\n"), 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
)
983 const gchar
*filename
= o_picture_get_filename (toplevel
, object
);
986 if (!o_picture_is_embedded (toplevel
, object
)) return;
988 o_picture_set_from_file (toplevel
, object
, filename
, &err
);
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"));
998 object
->picture
->embedded
= 0;
1000 basename
= g_path_get_basename(filename
);
1001 s_log_message (_("Picture [%s] has been unembedded\n"), 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
,
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
));
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.
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.
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
);
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.
1094 o_picture_get_data (TOPLEVEL
*toplevel
, OBJECT
*object
,
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.
1118 o_picture_set_from_buffer (TOPLEVEL
*toplevel
, OBJECT
*object
,
1119 const gchar
*filename
,
1120 const gchar
*data
, size_t len
,
1124 GInputStream
*stream
;
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
,
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
);
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
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.
1177 o_picture_set_from_file (TOPLEVEL
*toplevel
, OBJECT
*object
,
1178 const gchar
*filename
,
1185 g_return_val_if_fail (filename
!= NULL
, FALSE
);
1187 if (!g_file_get_contents (filename
, &buf
, &len
, error
)) {
1191 status
= o_picture_set_from_buffer (toplevel
, object
, filename
,
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.
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.
1223 o_picture_get_fallback_pixbuf (TOPLEVEL
*toplevel
)
1225 static GdkPixbuf
*pixbuf
= NULL
;
1226 static gboolean failed
= FALSE
;
1228 if (pixbuf
== NULL
&& !failed
) {
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
);
1245 if (failed
) return NULL
;
1247 g_assert (GDK_IS_PIXBUF (pixbuf
));
1248 return g_object_ref (pixbuf
);