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