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