1 /* Lepton EDA library
2  * Copyright (C) 1998-2010 Ales Hvezda
3  * Copyright (C) 1998-2016 gEDA Contributors
4  * Copyright (C) 2017-2021 Lepton EDA Contributors
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 picture_object.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 <gio/gio.h>
34 
35 #include "liblepton_priv.h"
36 
37 
38 
39 /*! \brief Create picture LeptonObject from character string.
40  *  \par Function Description
41  *  Parses \a first_line and subsequent lines from \a tb, and returns
42  *  a newly-created picture #LeptonObject.
43  *
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  */
50 LeptonObject*
lepton_picture_object_read(const char * first_line,TextBuffer * tb,unsigned int release_ver,unsigned int fileformat_ver,GError ** err)51 lepton_picture_object_read (const char *first_line,
52                             TextBuffer *tb,
53                             unsigned int release_ver,
54                             unsigned int fileformat_ver,
55                             GError **err)
56 {
57   LeptonObject *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     g_message (_("Found a zero width/height picture "
78                  "[ %1$c %2$d %3$d %4$d %5$d ]"),
79                type, x1, y1, width, height);
80   }
81 
82   if ( (mirrored > 1) || (mirrored < 0)) {
83     g_message (_("Found a picture with a wrong 'mirrored' parameter: %1$d."),
84                mirrored);
85     g_message (_("Setting mirrored to 0."));
86     mirrored = 0;
87   }
88 
89   if ( (embedded > 1) || (embedded < 0)) {
90     g_message (_("Found a picture with a wrong 'embedded' parameter: %1$d."),
91                embedded);
92     g_message (_("Setting embedded to 0."));
93     embedded = 0;
94   }
95 
96   switch(angle) {
97 
98     case(0):
99     case(90):
100     case(180):
101     case(270):
102     break;
103 
104     default:
105       g_message (_("Found an unsupported picture angle [ %1$d ]"), angle);
106       g_message (_("Setting angle to 0."));
107       angle=0;
108       break;
109 
110   }
111 
112   filename = g_strdup(s_textbuffer_next_line(tb));
113   filename = lepton_str_remove_ending_newline (filename);
114 
115   /* Handle empty filenames */
116   if (strlen (filename) == 0) {
117     g_message (_("Found an image with no filename."));
118     g_free (filename);
119     filename = NULL;
120   }
121 
122   if (embedded == 1) {
123     GString *encoded_picture=g_string_new("");
124     char finished = 0;
125 
126     /* Read the encoded picture */
127     do {
128 
129       line = s_textbuffer_next_line(tb);
130       if (line == NULL) break;
131 
132       if (strcmp (line, ".\n") != 0) {
133         encoded_picture = g_string_append (encoded_picture, line);
134       } else {
135         finished = 1;
136       }
137     } while (finished == 0);
138 
139     /* Decode the picture */
140     if (encoded_picture != NULL) {
141       file_content = s_encoding_base64_decode(encoded_picture->str,
142                                               encoded_picture->len,
143                                               &file_length);
144       g_string_free (encoded_picture, TRUE);
145     }
146 
147     if (file_content == NULL) {
148       g_message (_("Failed to load image from embedded data [%1$s]: %2$s"),
149                  filename, _("Base64 decoding failed."));
150       g_message (_("Falling back to file loading. Picture unembedded."));
151       embedded = 0;
152     }
153   }
154 
155   /* create the picture */
156   /* The picture is described by its upper left and lower right corner */
157   new_obj = lepton_picture_object_new (file_content,
158                                        file_length,
159                                        filename,
160                                        x1,
161                                        y1+height,
162                                        x1+width,
163                                        y1,
164                                        angle,
165                                        mirrored,
166                                        embedded);
167   g_free (file_content);
168   g_free (filename);
169 
170   return new_obj;
171 }
172 
173 /*! \brief Create a character string representation of a picture LeptonObject.
174  *  \par Function Description
175  *  This function formats a string in the buffer <B>*buff</B> to describe
176  *  the picture object <B>*object</B>.
177  *
178  *  \param [in] object  Picture LeptonObject to create string from.
179  *  \return A pointer to the picture LeptonObject character string.
180  *
181  *  \note
182  *  Caller must g_free returned character string.
183  *
184  */
185 gchar*
lepton_picture_object_to_buffer(const LeptonObject * object)186 lepton_picture_object_to_buffer (const LeptonObject *object)
187 {
188   int width, height, x1, y1;
189   gchar *encoded_picture=NULL;
190   gchar *out=NULL;
191   guint encoded_picture_length;
192   const gchar *filename = NULL;
193   int lower_x, lower_y, upper_x, upper_y;
194 
195   lower_x = lepton_picture_object_get_lower_x (object);
196   lower_y = lepton_picture_object_get_lower_y (object);
197   upper_x = lepton_picture_object_get_upper_x (object);
198   upper_y = lepton_picture_object_get_upper_y (object);
199 
200   /* calculate the width and height of the box */
201   width  = abs (lower_x - upper_x);
202   height = abs (upper_y - lower_y);
203 
204   /* calculate the lower left corner of the box */
205   x1 = upper_x;
206   y1 = upper_y - height; /* move the origin to 0, 0*/
207 
208 #if DEBUG
209   printf("picture: %d %d %d %d\n", x1, y1, width, height);
210 #endif
211 
212   /* Encode the picture if it's embedded */
213   if (lepton_picture_object_get_embedded (object)) {
214     encoded_picture =
215       s_encoding_base64_encode( (char *)object->picture->file_content,
216                                 object->picture->file_length,
217                                 &encoded_picture_length,
218                                 TRUE);
219     if (encoded_picture == NULL) {
220       g_message (_("ERROR: unable to encode the picture."));
221     }
222   }
223 
224   /* Cope with null filename */
225   filename = lepton_picture_object_get_filename (object);
226   if (filename == NULL) filename = "";
227 
228   if (lepton_picture_object_get_embedded (object) &&
229       encoded_picture != NULL) {
230     out = g_strdup_printf("%c %d %d %d %d %d %d %d\n%s\n%s\n%s",
231                           lepton_object_get_type (object),
232                           x1, y1, width, height,
233                           lepton_picture_object_get_angle (object),
234                           lepton_picture_object_get_mirrored (object),
235                           TRUE,
236                           filename,
237                           encoded_picture,
238                           ".");
239   }
240   else {
241     out = g_strdup_printf("%c %d %d %d %d %d %d %d\n%s",
242                           lepton_object_get_type (object),
243                           x1, y1, width, height,
244                           lepton_picture_object_get_angle (object),
245                           lepton_picture_object_get_mirrored (object),
246                           FALSE,
247                           filename);
248   }
249   g_free(encoded_picture);
250 
251   return(out);
252 }
253 
254 
255 /*! \brief Create a picture object.
256  *  \par Function Description
257  *  This function creates a new object representing a picture.
258  *
259  *  The picture is described by its upper left corner (\a x1, \a y1)
260  *  and its lower right corner (\a x2, \a y2).  The \a type parameter
261  *  must be equal to #OBJ_PICTURE.
262  *
263  *  If \a file_content is non-NULL, it must be a pointer to a buffer
264  *  containing raw image data.  If loading data from \a file_content
265  *  is unsuccessful, and \a filename is non-NULL, an image will
266  *  attempt to be loaded from \a filename.  Otherwise, the picture
267  *  object will be initially empty.
268  *
269  *  \param [in]     file_content  Raw data of the image file, or NULL.
270  *  \param [in]     file_length   Length of raw data buffer
271  *  \param [in]     filename      File name backing this picture, or NULL.
272  *  \param [in]     x1            Upper x coordinate.
273  *  \param [in]     y1            Upper y coordinate.
274  *  \param [in]     x2            Lower x coordinate.
275  *  \param [in]     y2            Lower y coordinate.
276  *  \param [in]     angle         Picture rotation angle.
277  *  \param [in]     mirrored      Whether the image should be mirrored or not.
278  *  \param [in]     embedded      Whether the embedded flag should be set or not.
279  *  \return A pointer to a new picture #LeptonObject.
280  */
281 LeptonObject*
lepton_picture_object_new(const gchar * file_content,gsize file_length,const gchar * filename,int x1,int y1,int x2,int y2,int angle,gboolean mirrored,gboolean embedded)282 lepton_picture_object_new (const gchar *file_content,
283                            gsize file_length,
284                            const gchar *filename,
285                            int x1,
286                            int y1,
287                            int x2,
288                            int y2,
289                            int angle,
290                            gboolean mirrored,
291                            gboolean embedded)
292 {
293   LeptonObject *new_node;
294   LeptonPicture *picture;
295 
296   /* create the object */
297   new_node = lepton_object_new (OBJ_PICTURE, "picture");
298 
299   picture = lepton_picture_new ();
300   new_node->picture = picture;
301 
302   /* describe the picture with its upper left and lower right corner */
303   lepton_picture_object_set_upper_x (new_node, (x1 > x2) ? x2 : x1);
304   lepton_picture_object_set_upper_y (new_node, (y1 > y2) ? y1 : y2);
305   lepton_picture_object_set_lower_x (new_node, (x1 > x2) ? x1 : x2);
306   lepton_picture_object_set_lower_y (new_node, (y1 > y2) ? y2 : y1);
307 
308   picture->pixbuf = NULL;
309   picture->file_content = NULL;
310   picture->file_length = 0;
311 
312   lepton_picture_object_set_ratio (new_node, fabs ((double) (x1 - x2) / (y1 - y2)));
313   picture->filename = g_strdup (filename);
314   lepton_picture_object_set_angle (new_node, angle);
315   lepton_picture_object_set_mirrored (new_node, mirrored);
316   lepton_picture_object_set_embedded (new_node, embedded);
317 
318   if (file_content != NULL) {
319     GError *error = NULL;
320     if (!lepton_picture_object_set_from_buffer (new_node,
321                                                 filename,
322                                                 file_content,
323                                                 file_length,
324                                                 &error))
325     {
326       g_message (_("Failed to load buffer image [%1$s]: %2$s"),
327                  filename, error->message);
328       g_error_free (error);
329 
330       /* Force the data into the object anyway, so as to prevent data
331        * loss of embedded images. */
332       picture->file_content = (gchar*) g_memdup (file_content, file_length);
333       picture->file_length = file_length;
334     }
335   }
336   if (picture->pixbuf == NULL && filename != NULL) {
337     GError *error = NULL;
338     if (!lepton_picture_object_set_from_file (new_node, filename, &error))
339     {
340       g_message (_("Failed to load image from [%1$s]: %2$s"),
341                  filename, error->message);
342       g_error_free (error);
343       /* picture not found; try to open a fall back pixbuf */
344       picture->pixbuf = lepton_picture_get_fallback_pixbuf ();
345     }
346   }
347 
348   return new_node;
349 }
350 
351 /*! \brief Get picture bounding rectangle in WORLD coordinates.
352  *
353  *  On failure, this function sets the bounds to empty.
354  *
355  *  \param [in]  object    Picture LeptonObject to read coordinates from.
356  *  \param [out] bounds    The bounds of the picture
357  */
358 void
lepton_picture_object_calculate_bounds(const LeptonObject * object,LeptonBounds * bounds)359 lepton_picture_object_calculate_bounds (const LeptonObject *object,
360                                         LeptonBounds *bounds)
361 {
362   lepton_bounds_init (bounds);
363 
364   g_return_if_fail (lepton_object_is_picture (object));
365   g_return_if_fail (object->picture != NULL);
366 
367   lepton_bounds_init_with_points (bounds,
368                                   lepton_picture_object_get_lower_x (object),
369                                   lepton_picture_object_get_lower_y (object),
370                                   lepton_picture_object_get_upper_x (object),
371                                   lepton_picture_object_get_upper_y (object));
372 }
373 
374 /*! \brief get the position of the left bottom point
375  *  \par Function Description
376  *  This function gets the position of the bottom left point of a picture object.
377  *
378  *  \param [in] object   The object to get the position.
379  *  \param [out] x       pointer to the x-position
380  *  \param [out] y       pointer to the y-position
381  *  \return TRUE if successfully determined the position, FALSE otherwise
382  */
383 gboolean
lepton_picture_object_get_position(const LeptonObject * object,gint * x,gint * y)384 lepton_picture_object_get_position (const LeptonObject *object,
385                                     gint *x,
386                                     gint *y)
387 {
388   g_return_val_if_fail (lepton_object_is_picture (object), FALSE);
389   g_return_val_if_fail (object->picture != NULL, FALSE);
390 
391   if (x != NULL) {
392     *x = MIN (lepton_picture_object_get_lower_x (object),
393               lepton_picture_object_get_upper_x (object));
394   }
395 
396   if (y != NULL) {
397     *y = MIN (lepton_picture_object_get_lower_y (object),
398               lepton_picture_object_get_upper_y (object));
399   }
400 
401   return TRUE;
402 }
403 
404 
405 /*! \brief Get the width/height ratio of an image.
406  * \par Function Description
407 
408  * Returns the width/height ratio of picture \a object.  The
409  * function does not take into account the image rotation but
410  * simply returns the value of the 'ratio' field of the
411  * #LeptonPicture structure.
412  *
413  * \param object Picture #LeptonObject to inspect.
414  * \return Width/height ratio for the picture object.
415  */
416 double
lepton_picture_object_get_ratio(const LeptonObject * object)417 lepton_picture_object_get_ratio (const LeptonObject *object)
418 {
419   g_return_val_if_fail (lepton_object_is_picture (object), 0);
420   g_return_val_if_fail (object->picture != NULL, 0);
421 
422   return object->picture->ratio;
423 }
424 
425 /*! \brief Get the width/height ratio of an image.
426  * \par Function Description
427 
428  * Returns the width/height ratio of picture \a object, taking the
429  * image rotation into account.
430  *
431  * \param object    Picture #LeptonObject to inspect.
432  * \return width/height ratio for \a object.
433  */
434 double
lepton_picture_object_get_real_ratio(LeptonObject * object)435 lepton_picture_object_get_real_ratio (LeptonObject *object)
436 {
437   int angle;
438   double ratio;
439 
440   g_return_val_if_fail (lepton_object_is_picture (object), 1);
441   g_return_val_if_fail (object->picture != NULL, 1);
442 
443   angle = lepton_picture_object_get_angle (object);
444   ratio = lepton_picture_object_get_ratio (object);
445 
446   /* The effective ratio varies depending on the rotation of the
447    * image. */
448   switch (angle) {
449   case 0:
450   case 180:
451     return ratio;
452   case 90:
453   case 270:
454     return 1.0 / ratio;
455   default:
456     g_critical (_("Picture %1$p has invalid angle %2$i\n"), object, angle);
457   }
458   return 0;
459 }
460 
461 /*! \brief Set the width/height ratio of an image.
462  * \par Function Description
463 
464  * Sets the width/height ratio of picture \a object.
465  *
466  * \param object Picture #LeptonObject to amend.
467  * \param ratio  New width/height ratio for the picture.
468  */
469 void
lepton_picture_object_set_ratio(LeptonObject * object,double ratio)470 lepton_picture_object_set_ratio (LeptonObject *object,
471                                  double ratio)
472 {
473   g_return_if_fail (lepton_object_is_picture (object));
474   g_return_if_fail (object->picture != NULL);
475 
476   object->picture->ratio = ratio;
477 }
478 
479 /*! \brief Modify the description of a picture LeptonObject.
480  *  \par Function Description
481  *  This function modifies the coordinates of one of the four corner of
482  *  the picture. The new coordinates of the corner identified by
483  *  <B>whichone</B> are given by <B>x</B> and <B>y</B> in world unit.
484  *
485  *  The coordinates of the corner is modified in the world coordinate system.
486  *  Screen coordinates and boundings are then updated.
487  *
488  *  \param [in,out] object     Picture LeptonObject to modify.
489  *  \param [in]     x          New x coordinate.
490  *  \param [in]     y          New y coordinate.
491  *  \param [in]     whichone   Which picture parameter to modify.
492  *
493  *  <B>whichone</B> can have the following values:
494  *  <DL>
495  *    <DT>*</DT><DD>PICTURE_UPPER_LEFT
496  *    <DT>*</DT><DD>PICTURE_LOWER_LEFT
497  *    <DT>*</DT><DD>PICTURE_UPPER_RIGHT
498  *    <DT>*</DT><DD>PICTURE_LOWER_RIGHT
499  *  </DL>
500  */
501 void
lepton_picture_object_modify(LeptonObject * object,int x,int y,int whichone)502 lepton_picture_object_modify (LeptonObject *object,
503                               int x,
504                               int y,
505                               int whichone)
506 {
507   int tmp;
508   int lower_x, lower_y, upper_x, upper_y;
509   double ratio = lepton_picture_object_get_real_ratio (object);
510 
511   lepton_object_emit_pre_change_notify (object);
512 
513   /* change the position of the selected corner */
514   switch(whichone) {
515     case PICTURE_UPPER_LEFT:
516       lepton_picture_object_set_upper_x (object, x);
517       tmp = abs (lepton_picture_object_get_upper_x (object) -
518                  lepton_picture_object_get_lower_x (object)) / ratio;
519       if (y < lepton_picture_object_get_lower_y (object))
520       {
521         tmp = -tmp;
522       }
523       lepton_picture_object_set_upper_y (object,
524                                          lepton_picture_object_get_lower_y (object) + tmp);
525       break;
526 
527     case PICTURE_LOWER_LEFT:
528       lepton_picture_object_set_upper_x (object, x);
529       tmp = abs (lepton_picture_object_get_upper_x (object) -
530                  lepton_picture_object_get_lower_x (object)) / ratio;
531       if (y > lepton_picture_object_get_upper_y (object))
532       {
533         tmp = -tmp;
534       }
535       lepton_picture_object_set_lower_y (object,
536                                          lepton_picture_object_get_upper_y (object) - tmp);
537       break;
538 
539     case PICTURE_UPPER_RIGHT:
540       lepton_picture_object_set_lower_x (object, x);
541       tmp = abs (lepton_picture_object_get_upper_x (object) -
542                  lepton_picture_object_get_lower_x (object)) / ratio;
543       if (y < lepton_picture_object_get_lower_y (object))
544       {
545         tmp = -tmp;
546       }
547       lepton_picture_object_set_upper_y (object,
548                                          lepton_picture_object_get_lower_y (object) + tmp);
549       break;
550 
551     case PICTURE_LOWER_RIGHT:
552       lepton_picture_object_set_lower_x (object, x);
553       tmp = abs (lepton_picture_object_get_upper_x (object) -
554                  lepton_picture_object_get_lower_x (object)) / ratio;
555       if (y > lepton_picture_object_get_upper_y (object))
556       {
557         tmp = -tmp;
558       }
559       lepton_picture_object_set_lower_y (object,
560                                          lepton_picture_object_get_upper_y (object) - tmp);
561       break;
562 
563     default:
564       return;
565   }
566 
567   lower_x = lepton_picture_object_get_lower_x (object);
568   lower_y = lepton_picture_object_get_lower_y (object);
569   upper_x = lepton_picture_object_get_upper_x (object);
570   upper_y = lepton_picture_object_get_upper_y (object);
571 
572   /* need to update the upper left and lower right corners */
573   if (upper_x > lower_x)
574   {
575     tmp = upper_x;
576     lepton_picture_object_set_upper_x (object, lower_x);
577     lepton_picture_object_set_lower_x (object, tmp);
578   }
579 
580   if (upper_y < lower_y)
581   {
582     tmp = upper_y;
583     lepton_picture_object_set_upper_y (object, lower_y);
584     lepton_picture_object_set_lower_y (object, tmp);
585   }
586 
587   lepton_object_emit_change_notify (object);
588 }
589 
590 /*! \brief Rotate picture LeptonObject using WORLD coordinates.
591  *  \par Function Description
592  *  This function rotates the picture described by <B>*object</B> around
593  *  the (<B>world_centerx</B>, <B>world_centery</B>) point by <B>angle</B>
594  *  degrees.
595  *  The center of rotation is in world units.
596  *
597  *  \param [in]      world_centerx  Rotation center x coordinate in
598  *                                  WORLD units.
599  *  \param [in]      world_centery  Rotation center y coordinate in
600  *                                  WORLD units.
601  *  \param [in]      angle          Rotation angle in degrees (See note below).
602  *  \param [in,out]  object         Picture LeptonObject to rotate.
603  */
604 void
lepton_picture_object_rotate(int world_centerx,int world_centery,int angle,LeptonObject * object)605 lepton_picture_object_rotate (int world_centerx,
606                               int world_centery,
607                               int angle,
608                               LeptonObject *object)
609 {
610   int newx1, newy1;
611   int newx2, newy2;
612   int new_angle;
613 
614   g_return_if_fail (lepton_object_is_picture (object));
615   g_return_if_fail (object->picture != NULL);
616 
617   /* Only 90 degree multiple and positive angles are allowed. */
618   /* angle must be positive */
619   if(angle < 0) angle = -angle;
620   /* angle must be a 90 multiple or no rotation performed */
621   if((angle % 90) != 0) return;
622 
623   new_angle = (lepton_picture_object_get_angle (object) + angle) % 360;
624 
625   lepton_picture_object_set_angle (object, new_angle) ;
626 
627   /* The center of rotation (<B>world_centerx</B>, <B>world_centery</B>) is
628    * translated to the origin. The rotation of the upper left and lower
629    * right corner are then performed. Finally, the rotated picture is
630    * translated back to its previous location.
631    */
632   /* translate object to origin */
633   lepton_picture_object_translate (object, -world_centerx, -world_centery);
634 
635   /* rotate the upper left corner of the picture */
636   lepton_point_rotate_90 (lepton_picture_object_get_upper_x (object),
637                           lepton_picture_object_get_upper_y (object),
638                           angle,
639                           &newx1,
640                           &newy1);
641 
642   /* rotate the lower left corner of the picture */
643   lepton_point_rotate_90 (lepton_picture_object_get_lower_x (object),
644                           lepton_picture_object_get_lower_y (object),
645                           angle,
646                           &newx2,
647                           &newy2);
648 
649   /* reorder the corners after rotation */
650   lepton_picture_object_set_upper_x (object, MIN (newx1, newx2));
651   lepton_picture_object_set_upper_y (object, MAX (newy1, newy2));
652   lepton_picture_object_set_lower_x (object, MAX (newx1, newx2));
653   lepton_picture_object_set_lower_y (object, MIN (newy1, newy2));
654 
655   /* translate object back to normal position */
656   lepton_picture_object_translate (object, world_centerx, world_centery);
657 }
658 
659 /*! \brief Mirror a picture using WORLD coordinates.
660  *  \par Function Description
661  *  This function mirrors the picture from the point
662  *  (<B>world_centerx</B>,<B>world_centery</B>) in world unit.
663  *
664  *  The picture is first translated to the origin, then mirrored and
665  *  finally translated back at its previous position.
666  *
667  *  \param [in]     world_centerx  Origin x coordinate in WORLD units.
668  *  \param [in]     world_centery  Origin y coordinate in WORLD units.
669  *  \param [in,out] object         Picture LeptonObject to mirror.
670  */
671 void
lepton_picture_object_mirror(int world_centerx,int world_centery,LeptonObject * object)672 lepton_picture_object_mirror (int world_centerx,
673                               int world_centery,
674                               LeptonObject *object)
675 {
676   int newx1, newy1;
677   int newx2, newy2;
678 
679   g_return_if_fail (lepton_object_is_picture (object));
680   g_return_if_fail (object->picture != NULL);
681 
682   /* Set info in object. Sometimes it's necessary to change the
683    * rotation angle as well as the mirror flag. */
684   lepton_picture_object_set_mirrored (object,
685                                       !lepton_picture_object_get_mirrored (object));
686   switch (lepton_picture_object_get_angle (object)) {
687   case 90:
688     lepton_picture_object_set_angle (object, 270);
689     break;
690   case 270:
691     lepton_picture_object_set_angle (object, 90);
692     break;
693   }
694 
695   /* translate object to origin */
696   lepton_picture_object_translate (object, -world_centerx, -world_centery);
697 
698   /* mirror the corners */
699   newx1 = -lepton_picture_object_get_upper_x (object);
700   newy1 = lepton_picture_object_get_upper_y (object);
701   newx2 = -lepton_picture_object_get_lower_x (object);
702   newy2 = lepton_picture_object_get_lower_y (object);
703 
704   /* reorder the corners */
705   lepton_picture_object_set_upper_x (object, MIN (newx1, newx2));
706   lepton_picture_object_set_upper_y (object, MAX (newy1, newy2));
707   lepton_picture_object_set_lower_x (object, MAX (newx1, newx2));
708   lepton_picture_object_set_lower_y (object, MIN (newy1, newy2));
709 
710   /* translate back in position */
711   lepton_picture_object_translate (object, world_centerx, world_centery);
712 }
713 
714 /*! \brief Translate a picture position in WORLD coordinates by a delta.
715  *  \par Function Description
716  *  This function applies a translation of (<B>x1</B>,<B>y1</B>) to the picture
717  *  described by <B>*object</B>. <B>x1</B> and <B>y1</B> are in world units.
718  *
719  *  \param [in,out] object     Picture LeptonObject to translate.
720  *  \param [in]     dx         x distance to move.
721  *  \param [in]     dy         y distance to move.
722  */
723 void
lepton_picture_object_translate(LeptonObject * object,int dx,int dy)724 lepton_picture_object_translate (LeptonObject *object,
725                                  int dx,
726                                  int dy)
727 {
728   int lower_x, lower_y, upper_x, upper_y;
729 
730   g_return_if_fail (lepton_object_is_picture (object));
731   g_return_if_fail (object->picture != NULL);
732 
733   upper_x = lepton_picture_object_get_upper_x (object);
734   upper_y = lepton_picture_object_get_upper_y (object);
735   lower_x = lepton_picture_object_get_lower_x (object);
736   lower_y = lepton_picture_object_get_lower_y (object);
737 
738   /* Do world coords */
739   lepton_picture_object_set_upper_x (object, upper_x + dx);
740   lepton_picture_object_set_upper_y (object, upper_y + dy);
741   lepton_picture_object_set_lower_x (object, lower_x + dx);
742   lepton_picture_object_set_lower_y (object, lower_y + dy);
743 }
744 
745 /*! \brief Create a copy of a picture.
746  *  \par Function Description
747  *  This function creates a verbatim copy of the object pointed by
748  *  <B>o_current</B> describing a picture.
749  *
750  *  \param [in]  object     Picture LeptonObject to copy.
751  *  \return The new LeptonObject
752  */
753 LeptonObject*
lepton_picture_object_copy(LeptonObject * object)754 lepton_picture_object_copy (LeptonObject *object)
755 {
756   LeptonObject *new_node;
757   LeptonPicture *picture;
758 
759   /* create the object */
760   new_node = lepton_object_new (lepton_object_get_type (object), "picture");
761 
762   picture = (LeptonPicture*) g_malloc (sizeof (LeptonPicture));
763   new_node->picture = picture;
764 
765   lepton_object_set_color (new_node, lepton_object_get_color (object));
766   lepton_object_set_selectable (new_node, lepton_object_get_selectable (object));
767 
768   /* describe the picture with its upper left and lower right corner */
769   lepton_picture_object_set_upper_x (new_node, lepton_picture_object_get_upper_x (object));
770   lepton_picture_object_set_upper_y (new_node, lepton_picture_object_get_upper_y (object));
771   lepton_picture_object_set_lower_x (new_node, lepton_picture_object_get_lower_x (object));
772   lepton_picture_object_set_lower_y (new_node, lepton_picture_object_get_lower_y (object));
773 
774   if (object->picture->file_content != NULL) {
775     picture->file_content = (gchar*) g_memdup (object->picture->file_content,
776                                                object->picture->file_length);
777   } else {
778     picture->file_content = NULL;
779   }
780 
781   picture->file_length = object->picture->file_length;
782   picture->filename    = g_strdup (object->picture->filename);
783   lepton_picture_object_set_ratio (new_node, lepton_picture_object_get_ratio (object));
784   lepton_picture_object_set_angle (new_node, lepton_picture_object_get_angle (object));
785   lepton_picture_object_set_mirrored (new_node, lepton_picture_object_get_mirrored (object));
786   lepton_picture_object_set_embedded (new_node, lepton_picture_object_get_embedded (object));
787 
788   /* Get the picture data */
789   picture->pixbuf = lepton_picture_object_get_pixbuf (object);
790 
791   return new_node;
792 }
793 
794 /*! \brief Embed a picture object into its schematic.
795  *  \par Function Description
796  *  This functions embeds a picture object into its schematic if
797  *  it has valid image data associated with it.
798  *
799  *  \param object The picture #LeptonObject to embed.
800  */
801 void
lepton_picture_object_embed(LeptonObject * object)802 lepton_picture_object_embed (LeptonObject *object)
803 {
804   LeptonPage *page;
805   const gchar *filename;
806   gchar *basename;
807 
808   g_return_if_fail (lepton_object_is_picture (object));
809 
810   page = lepton_object_get_page (object);
811 
812   /* Return if the picture is already embedded. */
813   if (lepton_picture_object_get_embedded (object))
814     return;
815 
816   filename = lepton_picture_object_get_filename (object);
817 
818   if (object->picture->file_content == NULL)
819   {
820     /* Image has no data: signal an error. */
821     g_message (_("Picture [%1$s] has no image data."), filename);
822     g_message (_("Falling back to file loading. Picture is still unembedded."));
823   }
824   else
825   {
826     /* Set the embedded flag. */
827     lepton_picture_object_set_embedded (object, TRUE);
828 
829     basename = g_path_get_basename (filename);
830     g_message (_("Picture [%1$s] has been embedded."), basename);
831     g_free (basename);
832 
833     if (page != NULL)
834     {
835       lepton_page_set_changed (page, 1);
836     }
837   }
838 }
839 
840 
841 /*! \brief Unembed a picture object from its schematic.
842  *  \par Function Description
843  *  This function unembeds a picture object from its schematic
844  *  after checking that it is possible.  It verifies that the file
845  *  associated with the picture exists on disk and is usable.  If
846  *  so, the picture is reloaded and marked as not embedded.
847  *
848  *  \param [in] object The picture #LeptonObject to unembed.
849  */
850 void
lepton_picture_object_unembed(LeptonObject * object)851 lepton_picture_object_unembed (LeptonObject *object)
852 {
853   LeptonPage *page;
854   const gchar *filename;
855   gchar *basename;
856   GError *err = NULL;
857 
858   g_return_if_fail (lepton_object_is_picture (object));
859 
860   page = lepton_object_get_page (object);
861 
862   /* Return if the picture is already not embedded. */
863   if (!lepton_picture_object_get_embedded (object))
864     return;
865 
866   filename = lepton_picture_object_get_filename (object);
867 
868   /* Get picture data from file. */
869   lepton_picture_object_set_from_file (object, filename, &err);
870 
871   if (err != NULL)
872   {
873     /* Warn the user that the picture could not be loaded. */
874     g_message (_("Failed to load image from file [%1$s]: %2$s"),
875                filename, err->message);
876     g_message (_("Picture is still embedded."));
877     g_error_free (err);
878   }
879   else
880   {
881     /* Clear the embedded flag. */
882     lepton_picture_object_set_embedded (object, FALSE);
883 
884     basename = g_path_get_basename (filename);
885     g_message (_("Picture [%1$s] has been unembedded."), basename);
886     g_free (basename);
887 
888     if (page != NULL)
889     {
890       lepton_page_set_changed (page, 1);
891     }
892   }
893 }
894 
895 
896 /*! \brief Calculates the distance between the given point and the closest
897  * point in the picture.
898  *
899  *  Interrior points within the picture return a distance of zero.
900  *
901  *  \param [in] object         The picture LeptonObject.
902  *  \param [in] x              The x coordinate of the given point.
903  *  \param [in] y              The y coordinate of the given point.
904  *  \param [in] force_solid    If true, force treating the object as solid.
905  *  \param [in] include_hidden Take hidden text into account.
906  *  \return The shortest distance from the object to the point. With an
907  *  invalid parameter, this function returns G_MAXDOUBLE.
908  */
909 double
lepton_picture_object_shortest_distance(LeptonObject * object,int x,int y,int force_solid,gboolean include_hidden)910 lepton_picture_object_shortest_distance (LeptonObject *object,
911                                          int x,
912                                          int y,
913                                          int force_solid,
914                                          gboolean include_hidden)
915 {
916   double dx, dy;
917   double x1, y1, x2, y2;
918 
919   g_return_val_if_fail (lepton_object_is_picture (object), G_MAXDOUBLE);
920   g_return_val_if_fail (object->picture != NULL, G_MAXDOUBLE);
921 
922   x1 = (double) MIN (lepton_picture_object_get_upper_x (object),
923                      lepton_picture_object_get_lower_x (object));
924   y1 = (double) MIN (lepton_picture_object_get_upper_y (object),
925                      lepton_picture_object_get_lower_y (object));
926   x2 = (double) MAX (lepton_picture_object_get_upper_x (object),
927                      lepton_picture_object_get_lower_x (object));
928   y2 = (double) MAX (lepton_picture_object_get_upper_y (object),
929                      lepton_picture_object_get_lower_y (object));
930 
931   dx = MIN (((double)x) - x1, x2 - ((double)x));
932   dy = MIN (((double)y) - y1, y2 - ((double)y));
933 
934   dx = MIN (dx, 0);
935   dy = MIN (dy, 0);
936 
937   return hypot (dx, dy);
938 }
939 
940 /*! \brief Test whether a picture object is embedded.
941  * \par Function Description
942  * Returns TRUE if the picture \a object will have its data embedded
943  * in a schematic or symbol file; returns FALSE if its data will be
944  * obtained from a separate file.
945  *
946  * \param object    The picture #LeptonObject to inspect.
947  * \return TRUE if \a object is embedded.
948  */
949 gboolean
lepton_picture_object_get_embedded(const LeptonObject * object)950 lepton_picture_object_get_embedded (const LeptonObject *object)
951 {
952   g_return_val_if_fail (lepton_object_is_picture (object), FALSE);
953   g_return_val_if_fail (object->picture != NULL, FALSE);
954 
955   return object->picture->embedded;
956 }
957 
958 /*! \brief Set the \a embedded flag of a picture object.
959  *
960  * \param object   The picture #LeptonObject to amend.
961  * \param embedded If the picture object should be embedded.
962  */
963 void
lepton_picture_object_set_embedded(LeptonObject * object,gboolean embedded)964 lepton_picture_object_set_embedded (LeptonObject *object,
965                                     gboolean embedded)
966 {
967   g_return_if_fail (lepton_object_is_picture (object));
968   g_return_if_fail (object->picture != NULL);
969 
970   object->picture->embedded = embedded;
971 }
972 
973 
974 /*! \brief Get a pixel buffer for a picture object.
975  * \par Function Description
976  * Returns a #GdkPixbuf for the picture object \a object, or NULL if
977  * the picture could not be loaded.
978  *
979  * The returned value should have its reference count decremented with
980  * g_object_unref() when no longer needed.
981  *
982  * \param object    The picture #LeptonObject to inspect.
983  * \return A #GdkPixbuf for the picture.
984  */
985 GdkPixbuf *
lepton_picture_object_get_pixbuf(LeptonObject * object)986 lepton_picture_object_get_pixbuf (LeptonObject *object)
987 {
988   g_return_val_if_fail (lepton_object_is_picture (object), NULL);
989   g_return_val_if_fail (object->picture != NULL, NULL);
990 
991   if (object->picture->pixbuf != NULL) {
992     return GDK_PIXBUF (g_object_ref (object->picture->pixbuf));
993   } else {
994     return NULL;
995   }
996 }
997 
998 
999 /*! \brief Set a picture object's contents from a buffer.
1000  * \par Function Description
1001  * Sets the contents of the picture \a object by reading image data
1002  * from a buffer.  The buffer should be in on-disk format.
1003  *
1004  * \param object   The picture #LeptonObject to modify.
1005  * \param filename The new filename for the picture.
1006  * \param data     The new image data buffer.
1007  * \param len      The size of the data buffer.
1008  * \param error    Location to return error information.
1009  * \return TRUE on success, FALSE on failure.
1010  */
1011 gboolean
lepton_picture_object_set_from_buffer(LeptonObject * object,const gchar * filename,const gchar * data,size_t len,GError ** error)1012 lepton_picture_object_set_from_buffer (LeptonObject *object,
1013                                        const gchar *filename,
1014                                        const gchar *data,
1015                                        size_t len,
1016                                        GError **error)
1017 {
1018   GdkPixbuf *pixbuf;
1019   GInputStream *stream;
1020   gchar *tmp;
1021 
1022   g_return_val_if_fail (lepton_object_is_picture (object), FALSE);
1023   g_return_val_if_fail (object->picture != NULL, FALSE);
1024   g_return_val_if_fail (data != NULL, FALSE);
1025 
1026   /* Check that we can actually load the data before making any
1027    * changes to the object. */
1028   stream = G_INPUT_STREAM (g_memory_input_stream_new_from_data (data, len, NULL));
1029   pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, error);
1030   g_object_unref (stream);
1031   if (pixbuf == NULL) return FALSE;
1032 
1033   lepton_object_emit_pre_change_notify (object);
1034 
1035   if (object->picture->pixbuf != NULL) {
1036     g_object_unref (object->picture->pixbuf);
1037   }
1038   object->picture->pixbuf = pixbuf;
1039 
1040   lepton_picture_object_set_ratio (object,
1041                                    ((double) gdk_pixbuf_get_width(pixbuf) /
1042                                     gdk_pixbuf_get_height(pixbuf)));
1043 
1044   tmp = g_strdup (filename);
1045   g_free (object->picture->filename);
1046   object->picture->filename = tmp;
1047 
1048   gchar *buf = (gchar*) g_realloc (object->picture->file_content,
1049                                    len);
1050   /* It's possible that these buffers might overlap, because the
1051    * library user hates us. */
1052   memmove (buf, data, len);
1053   object->picture->file_content = buf;
1054   object->picture->file_length = len;
1055 
1056   lepton_object_emit_change_notify (object);
1057   return TRUE;
1058 }
1059 
1060 /*! \brief Set a picture object's contents from a file.
1061  * \par Function Description
1062  * Sets the contents of the picture \a object by reading image data
1063  * from a file.
1064  *
1065  * \param object   The picture #LeptonObject to modify.
1066  * \param filename The filename to load image data from.
1067  * \param error    Location to return error information.
1068  * \return TRUE on success, FALSE on failure.
1069  */
1070 gboolean
lepton_picture_object_set_from_file(LeptonObject * object,const gchar * filename,GError ** error)1071 lepton_picture_object_set_from_file (LeptonObject *object,
1072                                      const gchar *filename,
1073                                      GError **error)
1074 {
1075   gchar *buf;
1076   size_t len;
1077   gboolean status;
1078 
1079   g_return_val_if_fail (filename != NULL, FALSE);
1080 
1081   if (!g_file_get_contents (filename, &buf, &len, error)) {
1082     return FALSE;
1083   }
1084 
1085   status = lepton_picture_object_set_from_buffer (object,
1086                                                   filename,
1087                                                   buf,
1088                                                   len,
1089                                                   error);
1090   g_free (buf);
1091   return status;
1092 }
1093 
1094 /*! \brief Get a picture's corresponding filename.
1095  * \par Function Description
1096  * Returns the filename associated with the picture \a object.
1097  *
1098  * \param object   The picture #LeptonObject to inspect.
1099  * \return the filename associated with \a object.
1100  */
1101 const gchar *
lepton_picture_object_get_filename(const LeptonObject * object)1102 lepton_picture_object_get_filename (const LeptonObject *object)
1103 {
1104   g_return_val_if_fail (lepton_object_is_picture (object), NULL);
1105   g_return_val_if_fail (object->picture != NULL, NULL);
1106 
1107   return object->picture->filename;
1108 }
1109 
1110 /*! \brief Get the upper X coordinate of a picture.
1111  *
1112  *  \param [in] object Picture LeptonObject to get the upper X value of.
1113  *  \return upper_x The angle value.
1114  */
1115 int
lepton_picture_object_get_upper_x(const LeptonObject * object)1116 lepton_picture_object_get_upper_x (const LeptonObject *object)
1117 {
1118   g_return_val_if_fail (lepton_object_is_picture (object), 0);
1119   g_return_val_if_fail (object->picture != NULL, 0);
1120 
1121   return object->picture->upper_x;
1122 }
1123 
1124 
1125 /*! \brief Set the upper X coordinate of a picture.
1126  *
1127  *  \param [in] object Picture LeptonObject to set the upper X value of.
1128  *  \param [in] x  The new upper X value.
1129  */
1130 void
lepton_picture_object_set_upper_x(LeptonObject * object,int x)1131 lepton_picture_object_set_upper_x (LeptonObject *object,
1132                                    int x)
1133 {
1134   g_return_if_fail (lepton_object_is_picture (object));
1135   g_return_if_fail (object->picture != NULL);
1136 
1137   object->picture->upper_x = x;
1138 }
1139 
1140 
1141 /*! \brief Get the lower X coordinate of a picture.
1142  *
1143  *  \param [in] object Picture LeptonObject to get the lower X value of.
1144  *  \return lower_x The angle value.
1145  */
1146 int
lepton_picture_object_get_lower_x(const LeptonObject * object)1147 lepton_picture_object_get_lower_x (const LeptonObject *object)
1148 {
1149   g_return_val_if_fail (lepton_object_is_picture (object), 0);
1150   g_return_val_if_fail (object->picture != NULL, 0);
1151 
1152   return object->picture->lower_x;
1153 }
1154 
1155 
1156 /*! \brief Set the lower X coordinate of a picture.
1157  *
1158  *  \param [in] object Picture LeptonObject to set the lower X value of.
1159  *  \param [in] x  The new lower X value.
1160  */
1161 void
lepton_picture_object_set_lower_x(LeptonObject * object,int x)1162 lepton_picture_object_set_lower_x (LeptonObject *object,
1163                                    int x)
1164 {
1165   g_return_if_fail (lepton_object_is_picture (object));
1166   g_return_if_fail (object->picture != NULL);
1167 
1168   object->picture->lower_x = x;
1169 }
1170 
1171 
1172 /*! \brief Get the upper Y coordinate of a picture.
1173  *
1174  *  \param [in] object Picture LeptonObject to get the upper Y value of.
1175  *  \return upper_y The angle value.
1176  */
1177 int
lepton_picture_object_get_upper_y(const LeptonObject * object)1178 lepton_picture_object_get_upper_y (const LeptonObject *object)
1179 {
1180   g_return_val_if_fail (lepton_object_is_picture (object), 0);
1181   g_return_val_if_fail (object->picture != NULL, 0);
1182 
1183   return object->picture->upper_y;
1184 }
1185 
1186 
1187 /*! \brief Set the upper Y coordinate of a picture.
1188  *
1189  *  \param [in] object Picture LeptonObject to set the upper Y value of.
1190  *  \param [in] y  The new upper Y value.
1191  */
1192 void
lepton_picture_object_set_upper_y(LeptonObject * object,int y)1193 lepton_picture_object_set_upper_y (LeptonObject *object,
1194                                    int y)
1195 {
1196   g_return_if_fail (lepton_object_is_picture (object));
1197   g_return_if_fail (object->picture != NULL);
1198 
1199   object->picture->upper_y = y;
1200 }
1201 
1202 
1203 /*! \brief Get the lower Y coordinate of a picture.
1204  *
1205  *  \param [in] object Picture LeptonObject to get the lower Y value of.
1206  *  \return lower_y The angle value.
1207  */
1208 int
lepton_picture_object_get_lower_y(const LeptonObject * object)1209 lepton_picture_object_get_lower_y (const LeptonObject *object)
1210 {
1211   g_return_val_if_fail (lepton_object_is_picture (object), 0);
1212   g_return_val_if_fail (object->picture != NULL, 0);
1213 
1214   return object->picture->lower_y;
1215 }
1216 
1217 
1218 /*! \brief Set the lower Y coordinate of a picture.
1219  *
1220  *  \param [in] object Picture LeptonObject to set the lower Y value of.
1221  *  \param [in] y  The new lower Y value.
1222  */
1223 void
lepton_picture_object_set_lower_y(LeptonObject * object,int y)1224 lepton_picture_object_set_lower_y (LeptonObject *object,
1225                                    int y)
1226 {
1227   g_return_if_fail (lepton_object_is_picture (object));
1228   g_return_if_fail (object->picture != NULL);
1229 
1230   object->picture->lower_y = y;
1231 }
1232 
1233 
1234 /*! \brief Get the angle of a picture.
1235  *
1236  *  \param [in] object Picture LeptonObject to get the angle value of.
1237  *  \return angle  The angle value.
1238  */
1239 int
lepton_picture_object_get_angle(const LeptonObject * object)1240 lepton_picture_object_get_angle (const LeptonObject *object)
1241 {
1242   g_return_val_if_fail (lepton_object_is_picture (object), 0);
1243   g_return_val_if_fail (object->picture != NULL, 0);
1244 
1245   return object->picture->angle;
1246 }
1247 
1248 
1249 /*! \brief Set the angle of a picture.
1250  *
1251  *  \param [in] object Picture LeptonObject to set the angle value of.
1252  *  \param [in] angle  The angle value.
1253  */
1254 void
lepton_picture_object_set_angle(LeptonObject * object,int angle)1255 lepton_picture_object_set_angle (LeptonObject *object,
1256                                  int angle)
1257 {
1258   g_return_if_fail (lepton_object_is_picture (object));
1259   g_return_if_fail (object->picture != NULL);
1260   g_return_if_fail ((angle == 0)
1261                     || (angle == 90)
1262                     || (angle == 180)
1263                     || (angle == 270));
1264 
1265   object->picture->angle = angle;
1266 }
1267 
1268 
1269 /*! \brief Get the mirrored flag of a picture.
1270  *
1271  *  \param [in] object Picture LeptonObject to get the mirrored value of.
1272  *  \return mirrored  The mirrored flag value.
1273  */
1274 gboolean
lepton_picture_object_get_mirrored(const LeptonObject * object)1275 lepton_picture_object_get_mirrored (const LeptonObject *object)
1276 {
1277   g_return_val_if_fail (lepton_object_is_picture (object), 0);
1278   g_return_val_if_fail (object->picture != NULL, 0);
1279 
1280   return object->picture->mirrored;
1281 }
1282 
1283 
1284 /*! \brief Set the mirrored flag of a picture.
1285  *
1286  *  \param [in] object Picture LeptonObject to set the mirrored value of.
1287  *  \param [in] mirrored The mirrored flag value.
1288  */
1289 void
lepton_picture_object_set_mirrored(LeptonObject * object,gboolean mirrored)1290 lepton_picture_object_set_mirrored (LeptonObject *object,
1291                                     gboolean mirrored)
1292 {
1293   g_return_if_fail (lepton_object_is_picture (object));
1294   g_return_if_fail (object->picture != NULL);
1295   g_return_if_fail ((mirrored == 0) || (mirrored == 1));
1296 
1297   object->picture->mirrored = mirrored;
1298 }
1299